diff --git a/DEPS b/DEPS
index 649d355..197a0f29 100644
--- a/DEPS
+++ b/DEPS
@@ -142,11 +142,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': '1d105080c423e6c8bbb15da5544a4ff28a8b8570',
+  'skia_revision': 'e192c4ce5a33829325ded5a03587605d892829b6',
   # 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': '626056db5ef19ce5197a87e9459ff0fef520c331',
+  'v8_revision': 'f77a9153efb855ae59cfb17c3c4bd32f51c34857',
   # 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.
@@ -154,7 +154,7 @@
   # 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': '4c292b876132697736f103b1ac7f41f044f88a5b',
+  'angle_revision': 'a1b6761e6e3c55bb058ccd413d962a0a4ccfe12b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -261,7 +261,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': '37e8f7994644151f425efa5b408d105713d2cd21',
+  'spv_tools_revision': 'b8ab80843f67479b1b0c096138e62ec78145f05b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -277,7 +277,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'c6d2d8e8fbd89f43c08c9b38b8df2bfff625ef2a',
+  'dawn_revision': '35716c204d2caebe01417bfec3750f5f63000229',
   # 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' + '@' + '28f766851ebb223e99ea2b45e8abe795923b1046',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ca227ecc2bfd7582675b735b8ff81477cce26cac',
       'condition': 'checkout_linux',
   },
 
@@ -1359,7 +1359,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '91350f8ecf9ab2922ee062c114e4a759f24bd8d0',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '668ce0c7fa8f261dacdba45149d10a23f772a9b6',
+    Var('webrtc_git') + '/src.git' + '@' + '86f8b3bd5d29e790ae24e0c285fa501805529d62',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1400,7 +1400,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@da80246d0fbe8ff119654aed9f66612825f46900',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@25cf5a641d73fce8890b99b3fd6d4069e497f5bf',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index eb30009..d76b55d 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -759,10 +759,14 @@
     "task/promise/dependent_list.h",
     "task/promise/finally_executor.cc",
     "task/promise/finally_executor.h",
+    "task/promise/helpers.cc",
     "task/promise/helpers.h",
     "task/promise/no_op_promise_executor.cc",
-    "task/promise/no_op_promise_executor.h",
+    "task/promise/post_task_executor.h",
     "task/promise/promise.h",
+    "task/promise/promise.h",
+    "task/promise/promise_executor.cc",
+    "task/promise/promise_executor.h",
     "task/promise/promise_result.h",
     "task/promise/then_and_catch_executor.cc",
     "task/promise/then_and_catch_executor.h",
@@ -2658,6 +2662,7 @@
     "task/promise/abstract_promise_unittest.cc",
     "task/promise/dependent_list_unittest.cc",
     "task/promise/helpers_unittest.cc",
+    "task/promise/post_task_executor_unittest.cc",
     "task/promise/promise_unittest.cc",
     "task/scoped_set_task_priority_for_current_thread_unittest.cc",
     "task/sequence_manager/atomic_flag_set_unittest.cc",
diff --git a/base/callback_internal.h b/base/callback_internal.h
index 390461c..777397d 100644
--- a/base/callback_internal.h
+++ b/base/callback_internal.h
@@ -19,9 +19,12 @@
 
 namespace internal {
 
+class BindStateBase;
 class FinallyExecutorCommon;
 class ThenAndCatchExecutorCommon;
-class BindStateBase;
+
+template <typename ReturnType>
+class PostTaskExecutor;
 
 template <typename Functor, typename... BoundArgs>
 struct BindState;
@@ -140,6 +143,9 @@
   friend class FinallyExecutorCommon;
   friend class ThenAndCatchExecutorCommon;
 
+  template <typename ReturnType>
+  friend class PostTaskExecutor;
+
   using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
 
   // Returns true if this callback equals |other|. |other| may be null.
diff --git a/base/task/promise/abstract_promise.cc b/base/task/promise/abstract_promise.cc
index 71541ef..fa3893e5 100644
--- a/base/task/promise/abstract_promise.cc
+++ b/base/task/promise/abstract_promise.cc
@@ -39,7 +39,7 @@
   if (dependents_.IsCanceled())
     return true;
 
-  const Executor* executor = GetExecutor();
+  const PromiseExecutor* executor = GetExecutor();
   return executor && executor->IsCancelled();
 }
 
@@ -147,12 +147,12 @@
 
 void AbstractPromise::DoubleMoveDetector::CheckForDoubleMoveErrors(
     const base::Location& new_dependent_location,
-    Executor::ArgumentPassingType new_dependent_executor_type) {
+    PromiseExecutor::ArgumentPassingType new_dependent_executor_type) {
   switch (new_dependent_executor_type) {
-    case Executor::ArgumentPassingType::kNoCallback:
+    case PromiseExecutor::ArgumentPassingType::kNoCallback:
       return;
 
-    case Executor::ArgumentPassingType::kNormal:
+    case PromiseExecutor::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_
@@ -163,7 +163,7 @@
           std::make_unique<Location>(new_dependent_location);
       return;
 
-    case Executor::ArgumentPassingType::kMove:
+    case PromiseExecutor::ArgumentPassingType::kMove:
       DCHECK(!dependent_move_only_promise_ ||
              *dependent_move_only_promise_ == new_dependent_location)
           << "Can't have multiple move only " << callback_type_
@@ -188,7 +188,7 @@
     // Inherit |prerequisite|'s resolve ancestor if it doesn't have a resolve
     // callback.
     if (prerequisite->resolve_argument_passing_type_ ==
-        Executor::ArgumentPassingType::kNoCallback) {
+        PromiseExecutor::ArgumentPassingType::kNoCallback) {
       ancestor_that_could_resolve_ = prerequisite->ancestor_that_could_resolve_;
     }
 
@@ -203,7 +203,7 @@
   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) {
+        PromiseExecutor::ArgumentPassingType::kNoCallback) {
       ancestor_that_could_reject_ = prerequisite->ancestor_that_could_reject_;
     }
 
@@ -218,7 +218,7 @@
   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) {
+        PromiseExecutor::ArgumentPassingType::kNoCallback) {
       must_catch_ancestor_that_could_reject_ =
           prerequisite->must_catch_ancestor_that_could_reject_;
     }
@@ -269,20 +269,19 @@
   }
 }
 
-const AbstractPromise::Executor* AbstractPromise::GetExecutor() const {
-  return base::unique_any_cast<Executor>(&value_);
+const PromiseExecutor* AbstractPromise::GetExecutor() const {
+  return base::unique_any_cast<PromiseExecutor>(&value_);
 }
 
-AbstractPromise::Executor::PrerequisitePolicy
-AbstractPromise::GetPrerequisitePolicy() {
-  Executor* executor = GetExecutor();
+PromiseExecutor::PrerequisitePolicy AbstractPromise::GetPrerequisitePolicy() {
+  PromiseExecutor* 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 or we're already settled.
     DCHECK(IsSettled());
-    return Executor::PrerequisitePolicy::kNever;
+    return PromiseExecutor::PrerequisitePolicy::kNever;
   }
   return executor->GetPrerequisitePolicy();
 }
@@ -294,7 +293,7 @@
 }
 
 void AbstractPromise::Execute() {
-  const Executor* executor = GetExecutor();
+  const PromiseExecutor* executor = GetExecutor();
   DCHECK(executor || dependents_.IsCanceled())
       << from_here_.ToString() << " value_ contains " << value_.type();
 
@@ -306,7 +305,7 @@
 #if DCHECK_IS_ON()
   // Clear |must_catch_ancestor_that_could_reject_| if we can catch it.
   if (reject_argument_passing_type_ !=
-      Executor::ArgumentPassingType::kNoCallback) {
+      PromiseExecutor::ArgumentPassingType::kNoCallback) {
     CheckedAutoLock lock(GetCheckedLock());
     must_catch_ancestor_that_could_reject_ = nullptr;
   }
@@ -340,18 +339,18 @@
   DCHECK(resolved_prerequisite->IsResolved());
 
   switch (GetPrerequisitePolicy()) {
-    case Executor::PrerequisitePolicy::kAll:
+    case PromiseExecutor::PrerequisitePolicy::kAll:
       if (prerequisites_->DecrementPrerequisiteCountAndCheckIfZero())
         DispatchPromise();
       break;
 
-    case Executor::PrerequisitePolicy::kAny:
+    case PromiseExecutor::PrerequisitePolicy::kAny:
       // PrerequisitePolicy::kAny should resolve immediately.
       if (prerequisites_->MarkPrerequisiteAsSettling(resolved_prerequisite))
         DispatchPromise();
       break;
 
-    case Executor::PrerequisitePolicy::kNever:
+    case PromiseExecutor::PrerequisitePolicy::kNever:
       break;
   }
 }
@@ -372,12 +371,12 @@
 bool AbstractPromise::OnPrerequisiteCancelled(
     AbstractPromise* canceled_prerequisite) {
   switch (GetPrerequisitePolicy()) {
-    case Executor::PrerequisitePolicy::kAll:
+    case PromiseExecutor::PrerequisitePolicy::kAll:
       // PrerequisitePolicy::kAll should cancel immediately.
       OnCanceled();
       return false;
 
-    case Executor::PrerequisitePolicy::kAny:
+    case PromiseExecutor::PrerequisitePolicy::kAny:
       // PrerequisitePolicy::kAny should only cancel if all if it's
       // pre-requisites have been canceled.
       if (prerequisites_->DecrementPrerequisiteCountAndCheckIfZero()) {
@@ -388,7 +387,7 @@
       }
       return true;
 
-    case Executor::PrerequisitePolicy::kNever:
+    case PromiseExecutor::PrerequisitePolicy::kNever:
       // If 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
@@ -655,42 +654,5 @@
   }
 }
 
-AbstractPromise::Executor::~Executor() {
-  vtable_->destructor(storage_);
-}
-
-AbstractPromise::Executor::PrerequisitePolicy
-AbstractPromise::Executor::GetPrerequisitePolicy() const {
-  return vtable_->get_prerequisite_policy(storage_);
-}
-
-bool AbstractPromise::Executor::IsCancelled() const {
-  return vtable_->is_cancelled(storage_);
-}
-
-#if DCHECK_IS_ON()
-AbstractPromise::Executor::ArgumentPassingType
-AbstractPromise::Executor::ResolveArgumentPassingType() const {
-  return vtable_->resolve_argument_passing_type(storage_);
-}
-
-AbstractPromise::Executor::ArgumentPassingType
-AbstractPromise::Executor::RejectArgumentPassingType() const {
-  return vtable_->reject_argument_passing_type(storage_);
-}
-
-bool AbstractPromise::Executor::CanResolve() const {
-  return vtable_->can_resolve(storage_);
-}
-
-bool AbstractPromise::Executor::CanReject() const {
-  return vtable_->can_reject(storage_);
-}
-#endif
-
-void AbstractPromise::Executor::Execute(AbstractPromise* promise) {
-  return vtable_->execute(storage_, promise);
-}
-
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/promise/abstract_promise.h b/base/task/promise/abstract_promise.h
index 9d56465..0a4ef6a 100644
--- a/base/task/promise/abstract_promise.h
+++ b/base/task/promise/abstract_promise.h
@@ -14,6 +14,7 @@
 #include "base/no_destructor.h"
 #include "base/task/common/checked_lock.h"
 #include "base/task/promise/dependent_list.h"
+#include "base/task/promise/promise_executor.h"
 #include "base/thread_annotations.h"
 
 namespace base {
@@ -339,23 +340,18 @@
  public:
   class AdjacencyList;
 
-  template <typename ConstructType, typename DerivedExecutorType>
-  struct ConstructWith {};
-
-  template <typename ConstructType,
-            typename DerivedExecutorType,
-            typename... ExecutorArgs>
+  template <typename ConstructType>
   static scoped_refptr<AbstractPromise> Create(
-      scoped_refptr<TaskRunner>&& task_runner,
+      const scoped_refptr<TaskRunner>& task_runner,
       const Location& from_here,
       std::unique_ptr<AdjacencyList> prerequisites,
       RejectPolicy reject_policy,
-      ConstructWith<ConstructType, DerivedExecutorType> tag,
-      ExecutorArgs&&... executor_args) {
+      ConstructType tag,
+      PromiseExecutor::Data&& executor_data) noexcept {
     scoped_refptr<AbstractPromise> promise = subtle::AdoptRefIfNeeded(
-        new internal::AbstractPromise(
-            std::move(task_runner), from_here, std::move(prerequisites),
-            reject_policy, tag, std::forward<ExecutorArgs>(executor_args)...),
+        new internal::AbstractPromise(task_runner, from_here,
+                                      std::move(prerequisites), reject_policy,
+                                      tag, std::move(executor_data)),
         AbstractPromise::kRefCountPreference);
     // It's important this is called after |promise| has been initialized
     // because otherwise it could trigger a scoped_refptr destructor on another
@@ -364,6 +360,20 @@
     return promise;
   }
 
+  template <typename ConstructType>
+  static scoped_refptr<AbstractPromise> CreateNoPrerequisitePromise(
+      const Location& from_here,
+      RejectPolicy reject_policy,
+      ConstructType tag,
+      PromiseExecutor::Data&& executor_data) noexcept {
+    scoped_refptr<AbstractPromise> promise =
+        subtle::AdoptRefIfNeeded(new internal::AbstractPromise(
+                                     nullptr, from_here, nullptr, reject_policy,
+                                     tag, std::move(executor_data)),
+                                 AbstractPromise::kRefCountPreference);
+    return promise;
+  }
+
   AbstractPromise(const AbstractPromise&) = delete;
   AbstractPromise& operator=(const AbstractPromise&) = delete;
 
@@ -439,165 +449,6 @@
                   "Use scoped_refptr<AbstractPromise> instead");
   }
 
-  // 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.
-  //
-  // Ideally Executor would be a pure virtual class, but we want to store these
-  // inline to reduce the number of memory allocations (small object
-  // optimization). The problem is even though placement new returns the same
-  // address it was allocated at, you have to use the returned pointer.  Casting
-  // the buffer to the derived class is undefined behavior. STL implementations
-  // usually store an extra pointer, but there we have opted for implementing
-  // our own VTable to save a little bit of memory.
-  class BASE_EXPORT Executor {
-   public:
-    // Constructs |Derived| in place.
-    template <typename Derived, typename... Args>
-    explicit Executor(in_place_type_t<Derived>, Args&&... args) {
-      static_assert(sizeof(Derived) <= MaxSize, "Derived is too big");
-      static_assert(sizeof(Executor) <= sizeof(AnyInternal::InlineAlloc),
-                    "Executor is too big");
-      vtable_ = &VTableHelper<Derived>::vtable_;
-      new (storage_) Derived(std::forward<Args>(args)...);
-    }
-
-    ~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.
-    PrerequisitePolicy GetPrerequisitePolicy() const;
-
-    // NB if there is both a resolve and a reject executor we require them to
-    // be both canceled at the same time.
-    bool IsCancelled() const;
-
-    // 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.
-    ArgumentPassingType ResolveArgumentPassingType() const;
-    ArgumentPassingType RejectArgumentPassingType() const;
-    bool CanResolve() const;
-    bool CanReject() const;
-#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.
-    void Execute(AbstractPromise* promise);
-
-   private:
-    static constexpr size_t MaxSize = sizeof(void*) * 2;
-
-    struct VTable {
-      void (*destructor)(void* self);
-      PrerequisitePolicy (*get_prerequisite_policy)(const void* self);
-      bool (*is_cancelled)(const void* self);
-#if DCHECK_IS_ON()
-      ArgumentPassingType (*resolve_argument_passing_type)(const void* self);
-      ArgumentPassingType (*reject_argument_passing_type)(const void* self);
-      bool (*can_resolve)(const void* self);
-      bool (*can_reject)(const void* self);
-#endif
-      void (*execute)(void* self, AbstractPromise* promise);
-
-     private:
-      DISALLOW_COPY_AND_ASSIGN(VTable);
-    };
-
-    template <typename DerivedType>
-    struct VTableHelper {
-      VTableHelper(const VTableHelper& other) = delete;
-      VTableHelper& operator=(const VTableHelper& other) = delete;
-
-      static void Destructor(void* self) {
-        static_cast<DerivedType*>(self)->~DerivedType();
-      }
-
-      static PrerequisitePolicy GetPrerequisitePolicy(const void* self) {
-        return static_cast<const DerivedType*>(self)->GetPrerequisitePolicy();
-      }
-
-      static bool IsCancelled(const void* self) {
-        return static_cast<const DerivedType*>(self)->IsCancelled();
-      }
-
-#if DCHECK_IS_ON()
-      static ArgumentPassingType ResolveArgumentPassingType(const void* self) {
-        return static_cast<const DerivedType*>(self)
-            ->ResolveArgumentPassingType();
-      }
-
-      static ArgumentPassingType RejectArgumentPassingType(const void* self) {
-        return static_cast<const DerivedType*>(self)
-            ->RejectArgumentPassingType();
-      }
-
-      static bool CanResolve(const void* self) {
-        return static_cast<const DerivedType*>(self)->CanResolve();
-      }
-
-      static bool CanReject(const void* self) {
-        return static_cast<const DerivedType*>(self)->CanReject();
-      }
-#endif
-
-      static void Execute(void* self, AbstractPromise* promise) {
-        return static_cast<DerivedType*>(self)->Execute(promise);
-      }
-
-      static constexpr VTable vtable_ = {
-        &VTableHelper::Destructor,
-        &VTableHelper::GetPrerequisitePolicy,
-        &VTableHelper::IsCancelled,
-#if DCHECK_IS_ON()
-        &VTableHelper::ResolveArgumentPassingType,
-        &VTableHelper::RejectArgumentPassingType,
-        &VTableHelper::CanResolve,
-        &VTableHelper::CanReject,
-#endif
-        &VTableHelper::Execute,
-      };
-    };
-
-    const VTable* vtable_;
-    char storage_[MaxSize];
-  };
-
   // 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
@@ -693,20 +544,16 @@
  private:
   friend base::RefCountedThreadSafe<AbstractPromise>;
 
-  template <typename ConstructType,
-            typename DerivedExecutorType,
-            typename... ExecutorArgs>
-  AbstractPromise(scoped_refptr<TaskRunner>&& task_runner,
+  template <typename ConstructType>
+  AbstractPromise(const 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)),
+                  ConstructType tag,
+                  PromiseExecutor::Data&& executor_data) noexcept
+      : task_runner_(task_runner),
         from_here_(std::move(from_here)),
-        value_(in_place_type_t<Executor>(),
-               in_place_type_t<DerivedExecutorType>(),
-               std::forward<ExecutorArgs>(executor_args)...),
+        value_(in_place_type_t<PromiseExecutor>(), std::move(executor_data)),
 #if DCHECK_IS_ON()
         reject_policy_(reject_policy),
         resolve_argument_passing_type_(
@@ -716,7 +563,7 @@
         executor_can_resolve_(GetExecutor()->CanResolve()),
         executor_can_reject_(GetExecutor()->CanReject()),
 #endif
-        dependents_(ConstructType()),
+        dependents_(tag),
         prerequisites_(std::move(prerequisites)) {
 #if DCHECK_IS_ON()
     {
@@ -742,17 +589,17 @@
   // Returns the curried promise if there is one or null otherwise.
   AbstractPromise* GetCurriedPromise();
 
-  // Returns the associated Executor if there is one.
-  const Executor* GetExecutor() const;
+  // Returns the associated PromiseExecutor if there is one.
+  const PromiseExecutor* GetExecutor() const;
 
-  Executor* GetExecutor() {
-    return const_cast<Executor*>(
+  PromiseExecutor* GetExecutor() {
+    return const_cast<PromiseExecutor*>(
         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();
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy();
 
   void AddAsDependentForAllPrerequisites();
 
@@ -835,8 +682,8 @@
 
   // 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 PromiseExecutor::ArgumentPassingType resolve_argument_passing_type_;
+  const PromiseExecutor::ArgumentPassingType reject_argument_passing_type_;
   const bool executor_can_resolve_;
   const bool executor_can_reject_;
 
@@ -874,7 +721,7 @@
 
     void CheckForDoubleMoveErrors(
         const base::Location& new_dependent_location,
-        Executor::ArgumentPassingType new_dependent_executor_type);
+        PromiseExecutor::ArgumentPassingType new_dependent_executor_type);
 
    private:
     const Location from_here_;
@@ -914,11 +761,6 @@
   std::unique_ptr<AdjacencyList> prerequisites_;
 };
 
-// static
-template <typename T>
-const AbstractPromise::Executor::VTable
-    AbstractPromise::Executor::VTableHelper<T>::vtable_;
-
 }  // namespace internal
 }  // namespace base
 
diff --git a/base/task/promise/abstract_promise_unittest.cc b/base/task/promise/abstract_promise_unittest.cc
index e48caa22..ebee591e 100644
--- a/base/task/promise/abstract_promise_unittest.cc
+++ b/base/task/promise/abstract_promise_unittest.cc
@@ -27,10 +27,9 @@
 using testing::ElementsAre;
 
 using ArgumentPassingType =
-    base::internal::AbstractPromise::Executor::ArgumentPassingType;
+    base::internal::PromiseExecutor::ArgumentPassingType;
 
-using PrerequisitePolicy =
-    base::internal::AbstractPromise::Executor::PrerequisitePolicy;
+using PrerequisitePolicy = base::internal::PromiseExecutor::PrerequisitePolicy;
 
 namespace base {
 namespace internal {
@@ -119,7 +118,7 @@
     std::unique_ptr<AbstractPromise::AdjacencyList> prerequisites;
 
     PrerequisitePolicy prerequisite_policy =
-        AbstractPromise::Executor::PrerequisitePolicy::kAll;
+        PromiseExecutor::PrerequisitePolicy::kAll;
 
     bool executor_can_resolve = true;
 
@@ -199,17 +198,18 @@
     }
 
     operator scoped_refptr<AbstractPromise>() {
-      return AbstractPromise::Create(
-          std::move(settings.task_runner), settings.from_here,
-          std::move(settings.prerequisites), settings.reject_policy,
-          AbstractPromise::ConstructWith<DependentList::ConstructUnresolved,
-                                         TestExecutor>(),
-          settings.prerequisite_policy,
+      PromiseExecutor::Data executor_data(
+          in_place_type_t<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));
+
+      return AbstractPromise::Create(
+          settings.task_runner, settings.from_here,
+          std::move(settings.prerequisites), settings.reject_policy,
+          DependentList::ConstructUnresolved(), std::move(executor_data));
     }
 
    private:
diff --git a/base/task/promise/all_container_executor.h b/base/task/promise/all_container_executor.h
index 400d0b8..e7e95d4 100644
--- a/base/task/promise/all_container_executor.h
+++ b/base/task/promise/all_container_executor.h
@@ -20,8 +20,8 @@
  public:
   bool IsCancelled() const { return false; }
 
-  AbstractPromise::Executor::PrerequisitePolicy GetPrerequisitePolicy() const {
-    return AbstractPromise::Executor::PrerequisitePolicy::kAll;
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const {
+    return PromiseExecutor::PrerequisitePolicy::kAll;
   }
 
   struct VoidResolveType {};
@@ -46,13 +46,11 @@
   }
 
 #if DCHECK_IS_ON()
-  AbstractPromise::Executor::ArgumentPassingType ResolveArgumentPassingType()
-      const {
+  PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const {
     return UseMoveSemantics<ResolveType>::argument_passing_type;
   }
 
-  AbstractPromise::Executor::ArgumentPassingType RejectArgumentPassingType()
-      const {
+  PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const {
     return UseMoveSemantics<RejectType>::argument_passing_type;
   }
 
@@ -107,17 +105,22 @@
   static PromiseType All(const Location& from_here, const Container& promises) {
     size_t i = 0;
     std::vector<DependentList::Node> prerequisite_list(promises.size());
+    // TODO(alexclarke): Move construction of this list and AbstractPromise out
+    // of line to reduce template bloat.
     for (auto& promise : promises) {
       prerequisite_list[i++].SetPrerequisite(promise.abstract_promise_.get());
     }
+
+    internal::PromiseExecutor::Data executor_data(
+        (in_place_type_t<
+            AllContainerPromiseExecutor<ResolveType, RejectType>>()));
+
     return PromiseType(AbstractPromise::Create(
         nullptr, from_here,
         std::make_unique<AbstractPromise::AdjacencyList>(
             std::move(prerequisite_list)),
-        RejectPolicy::kMustCatchRejection,
-        AbstractPromise::ConstructWith<
-            DependentList::ConstructUnresolved,
-            AllContainerPromiseExecutor<ResolveType, RejectType>>()));
+        RejectPolicy::kMustCatchRejection, DependentList::ConstructUnresolved(),
+        std::move(executor_data)));
   }
 };
 
diff --git a/base/task/promise/all_tuple_executor.h b/base/task/promise/all_tuple_executor.h
index b28c6c7..455c2272 100644
--- a/base/task/promise/all_tuple_executor.h
+++ b/base/task/promise/all_tuple_executor.h
@@ -56,8 +56,8 @@
 
   bool IsCancelled() const { return false; }
 
-  AbstractPromise::Executor::PrerequisitePolicy GetPrerequisitePolicy() const {
-    return AbstractPromise::Executor::PrerequisitePolicy::kAll;
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const {
+    return PromiseExecutor::PrerequisitePolicy::kAll;
   }
 
   void Execute(AbstractPromise* promise) {
@@ -77,13 +77,11 @@
   }
 
 #if DCHECK_IS_ON()
-  AbstractPromise::Executor::ArgumentPassingType ResolveArgumentPassingType()
-      const {
+  PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const {
     return UseMoveSemantics<ResolveTuple>::argument_passing_type;
   }
 
-  AbstractPromise::Executor::ArgumentPassingType RejectArgumentPassingType()
-      const {
+  PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const {
     return UseMoveSemantics<RejectType>::argument_passing_type;
   }
 
diff --git a/base/task/promise/finally_executor.h b/base/task/promise/finally_executor.h
index ec47b74..6dcffd8 100644
--- a/base/task/promise/finally_executor.h
+++ b/base/task/promise/finally_executor.h
@@ -14,10 +14,10 @@
 // Exists to reduce template bloat.
 class BASE_EXPORT FinallyExecutorCommon {
  public:
-  explicit FinallyExecutorCommon(CallbackBase&& callback);
+  explicit FinallyExecutorCommon(CallbackBase&& callback) noexcept;
   ~FinallyExecutorCommon();
 
-  // AbstractPromise::Executor:
+  // PromiseExecutor:
   bool IsCancelled() const;
 
   CallbackBase callback_;
@@ -31,19 +31,15 @@
  public:
   using CallbackReturnT = typename CallbackTraits<CallbackT>::ReturnType;
 
-  explicit FinallyExecutor(CallbackT&& callback)
-      : common_(std::move(callback)) {
-    static_assert(sizeof(CallbackBase) == sizeof(CallbackT),
-                  "We assume it's possible to cast from CallbackBase to "
-                  "CallbackT");
-  }
+  explicit FinallyExecutor(CallbackBase&& callback) noexcept
+      : common_(std::move(callback)) {}
 
   ~FinallyExecutor() = default;
 
   bool IsCancelled() const { return common_.IsCancelled(); }
 
-  AbstractPromise::Executor::PrerequisitePolicy GetPrerequisitePolicy() const {
-    return AbstractPromise::Executor::PrerequisitePolicy::kAll;
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const {
+    return PromiseExecutor::PrerequisitePolicy::kAll;
   }
 
   void Execute(AbstractPromise* promise) {
@@ -62,14 +58,12 @@
   }
 
 #if DCHECK_IS_ON()
-  AbstractPromise::Executor::ArgumentPassingType ResolveArgumentPassingType()
-      const {
-    return AbstractPromise::Executor::ArgumentPassingType::kNormal;
+  PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const {
+    return PromiseExecutor::ArgumentPassingType::kNormal;
   }
 
-  AbstractPromise::Executor::ArgumentPassingType RejectArgumentPassingType()
-      const {
-    return AbstractPromise::Executor::ArgumentPassingType::kNormal;
+  PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const {
+    return PromiseExecutor::ArgumentPassingType::kNormal;
   }
 
   bool CanResolve() const {
diff --git a/base/task/promise/helpers.cc b/base/task/promise/helpers.cc
new file mode 100644
index 0000000..5008363
--- /dev/null
+++ b/base/task/promise/helpers.cc
@@ -0,0 +1,64 @@
+// 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/helpers.h"
+
+#include "base/bind_helpers.h"
+#include "base/task/promise/no_op_promise_executor.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+
+namespace base {
+namespace internal {
+
+PromiseHolder::PromiseHolder(scoped_refptr<internal::AbstractPromise> promise)
+    : promise_(std::move(promise)) {}
+
+PromiseHolder::~PromiseHolder() {
+  // Detect if the promise was not executed and if so cancel to ensure memory
+  // is released.
+  if (promise_)
+    promise_->OnCanceled();
+}
+
+PromiseHolder::PromiseHolder(PromiseHolder&& other)
+    : promise_(std::move(other.promise_)) {}
+
+scoped_refptr<internal::AbstractPromise> PromiseHolder::Unwrap() const {
+  return std::move(promise_);
+}
+
+scoped_refptr<TaskRunner> GetCurrentSequence() {
+  return SequencedTaskRunnerHandle::Get();
+}
+
+DoNothing ToCallbackBase(DoNothing task) {
+  return task;
+}
+
+scoped_refptr<AbstractPromise> ConstructAbstractPromiseWithSinglePrerequisite(
+    const scoped_refptr<TaskRunner>& task_runner,
+    const Location& from_here,
+    AbstractPromise* prerequsite,
+    internal::PromiseExecutor::Data&& executor_data) {
+  return internal::AbstractPromise::Create(
+      task_runner, from_here,
+      std::make_unique<AbstractPromise::AdjacencyList>(prerequsite),
+      RejectPolicy::kMustCatchRejection,
+      internal::DependentList::ConstructUnresolved(), std::move(executor_data));
+}
+
+scoped_refptr<AbstractPromise> ConstructManualPromiseResolverPromise(
+    const Location& from_here,
+    RejectPolicy reject_policy,
+    bool can_resolve,
+    bool can_reject) {
+  return internal::AbstractPromise::CreateNoPrerequisitePromise(
+      from_here, reject_policy, internal::DependentList::ConstructUnresolved(),
+      internal::PromiseExecutor::Data(
+          in_place_type_t<internal::NoOpPromiseExecutor>(), can_resolve,
+          can_reject));
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/helpers.h b/base/task/promise/helpers.h
index a428992a..9a14be9 100644
--- a/base/task/promise/helpers.h
+++ b/base/task/promise/helpers.h
@@ -15,11 +15,23 @@
 #include "base/task/promise/promise_result.h"
 
 namespace base {
+class DoNothing;
+
 namespace internal {
 
+// A wrapper around SequencedTaskRunnerHandle::Get(). This file is included by
+// base/task_runner.h which means we can't include anything that depends on
+// that!
+scoped_refptr<TaskRunner> BASE_EXPORT GetCurrentSequence();
+
 template <typename T>
 using ToNonVoidT = std::conditional_t<std::is_void<T>::value, Void, T>;
 
+// Tag dispatch helper for PostTaskExecutor and ThenAndCatchExecutor.
+struct CouldResolveOrReject {};
+struct CanOnlyResolve {};
+struct CanOnlyReject {};
+
 // PromiseCallbackTraits computes the resolve and reject types of a Promise
 // from the return type of a resolve or reject callback.
 //
@@ -35,6 +47,7 @@
 struct PromiseCallbackTraits {
   using ResolveType = T;
   using RejectType = NoReject;
+  using TagType = CanOnlyResolve;
   static constexpr bool could_resolve = true;
   static constexpr bool could_reject = false;
 };
@@ -43,6 +56,7 @@
 struct PromiseCallbackTraits<Resolved<T>> {
   using ResolveType = T;
   using RejectType = NoReject;
+  using TagType = CanOnlyResolve;
   static constexpr bool could_resolve = true;
   static constexpr bool could_reject = false;
 };
@@ -51,6 +65,7 @@
 struct PromiseCallbackTraits<Rejected<T>> {
   using ResolveType = NoResolve;
   using RejectType = T;
+  using TagType = CanOnlyReject;
   static constexpr bool could_resolve = false;
   static constexpr bool could_reject = true;
 };
@@ -59,6 +74,7 @@
 struct PromiseCallbackTraits<Promise<NoResolve, Reject>> {
   using ResolveType = NoResolve;
   using RejectType = Reject;
+  using TagType = CanOnlyReject;
   static constexpr bool could_resolve = false;
   static constexpr bool could_reject = true;
 };
@@ -67,6 +83,7 @@
 struct PromiseCallbackTraits<Promise<Resolve, NoReject>> {
   using ResolveType = Resolve;
   using RejectType = NoReject;
+  using TagType = CanOnlyResolve;
   static constexpr bool could_resolve = true;
   static constexpr bool could_reject = false;
 };
@@ -75,6 +92,7 @@
 struct PromiseCallbackTraits<Promise<Resolve, Reject>> {
   using ResolveType = Resolve;
   using RejectType = Reject;
+  using TagType = CouldResolveOrReject;
   static constexpr bool could_resolve = true;
   static constexpr bool could_reject = true;
 };
@@ -83,6 +101,7 @@
 struct PromiseCallbackTraits<PromiseResult<NoResolve, Reject>> {
   using ResolveType = NoResolve;
   using RejectType = Reject;
+  using TagType = CanOnlyReject;
   static constexpr bool could_resolve = false;
   static constexpr bool could_reject = true;
 };
@@ -91,6 +110,7 @@
 struct PromiseCallbackTraits<PromiseResult<Resolve, NoReject>> {
   using ResolveType = Resolve;
   using RejectType = NoReject;
+  using TagType = CanOnlyResolve;
   static constexpr bool could_resolve = true;
   static constexpr bool could_reject = false;
 };
@@ -99,6 +119,7 @@
 struct PromiseCallbackTraits<PromiseResult<Resolve, Reject>> {
   using ResolveType = Resolve;
   using RejectType = Reject;
+  using TagType = CouldResolveOrReject;
   static constexpr bool could_resolve = true;
   static constexpr bool could_reject = true;
 };
@@ -136,11 +157,9 @@
   static_assert(!std::is_rvalue_reference<T>::value,
                 "Promise<T&&> not supported");
 
-  static constexpr AbstractPromise::Executor::ArgumentPassingType
-      argument_passing_type =
-          UseMove<T>()
-              ? AbstractPromise::Executor::ArgumentPassingType::kMove
-              : AbstractPromise::Executor::ArgumentPassingType::kNormal;
+  static constexpr PromiseExecutor::ArgumentPassingType argument_passing_type =
+      UseMove<T>() ? PromiseExecutor::ArgumentPassingType::kMove
+                   : PromiseExecutor::ArgumentPassingType::kNormal;
 };
 
 // A std::tuple is deemed to need move semantics if any of it's members need
@@ -148,11 +167,10 @@
 template <typename... Ts>
 struct UseMoveSemantics<std::tuple<Ts...>>
     : public std::integral_constant<bool, any_of({UseMove<Ts>()...})> {
-  static constexpr AbstractPromise::Executor::ArgumentPassingType
-      argument_passing_type =
-          any_of({UseMove<Ts>()...})
-              ? AbstractPromise::Executor::ArgumentPassingType::kMove
-              : AbstractPromise::Executor::ArgumentPassingType::kNormal;
+  static constexpr PromiseExecutor::ArgumentPassingType argument_passing_type =
+      any_of({UseMove<Ts>()...})
+          ? PromiseExecutor::ArgumentPassingType::kMove
+          : PromiseExecutor::ArgumentPassingType::kNormal;
 };
 
 // CallbackTraits extracts properties relevant to Promises from a callback.
@@ -179,9 +197,8 @@
   using ArgType = void;
   using ReturnType = T;
   using SignatureType = T();
-  static constexpr AbstractPromise::Executor::ArgumentPassingType
-      argument_passing_type =
-          AbstractPromise::Executor::ArgumentPassingType::kNormal;
+  static constexpr PromiseExecutor::ArgumentPassingType argument_passing_type =
+      PromiseExecutor::ArgumentPassingType::kNormal;
 };
 
 template <typename T, typename Arg>
@@ -191,8 +208,8 @@
   using ArgType = Arg;
   using ReturnType = T;
   using SignatureType = T(Arg);
-  static constexpr AbstractPromise::Executor::ArgumentPassingType
-      argument_passing_type = UseMoveSemantics<Arg>::argument_passing_type;
+  static constexpr PromiseExecutor::ArgumentPassingType argument_passing_type =
+      UseMoveSemantics<Arg>::argument_passing_type;
 };
 
 template <typename T, typename... Args>
@@ -205,11 +222,21 @@
   using SignatureType = T(Args...);
 
   // If any arguments need move semantics, treat as if they all do.
-  static constexpr AbstractPromise::Executor::ArgumentPassingType
-      argument_passing_type =
-          any_of({UseMoveSemantics<Args>::value...})
-              ? AbstractPromise::Executor::ArgumentPassingType::kMove
-              : AbstractPromise::Executor::ArgumentPassingType::kNormal;
+  static constexpr PromiseExecutor::ArgumentPassingType argument_passing_type =
+      any_of({UseMoveSemantics<Args>::value...})
+          ? PromiseExecutor::ArgumentPassingType::kMove
+          : PromiseExecutor::ArgumentPassingType::kNormal;
+};
+
+template <>
+struct CallbackTraits<DoNothing> {
+  using ResolveType = void;
+  using RejectType = NoReject;
+  using ArgType = void;
+  using ReturnType = void;
+  using SignatureType = void();
+  static constexpr PromiseExecutor::ArgumentPassingType argument_passing_type =
+      PromiseExecutor::ArgumentPassingType::kNormal;
 };
 
 // Adaptors for OnceCallback and RepeatingCallback
@@ -553,49 +580,77 @@
   }
 };
 
+// For use with base::Bind*. Cancels the promise if the callback was not run by
+// the time the callback is deleted.
+class BASE_EXPORT PromiseHolder {
+ public:
+  explicit PromiseHolder(scoped_refptr<internal::AbstractPromise> promise);
+
+  ~PromiseHolder();
+
+  PromiseHolder(PromiseHolder&& other);
+
+  scoped_refptr<internal::AbstractPromise> Unwrap() const;
+
+ private:
+  mutable scoped_refptr<internal::AbstractPromise> promise_;
+};
+
+}  // namespace internal
+
+template <>
+struct BindUnwrapTraits<internal::PromiseHolder> {
+  static scoped_refptr<internal::AbstractPromise> Unwrap(
+      const internal::PromiseHolder& o) {
+    return o.Unwrap();
+  }
+};
+
+namespace internal {
+
 // Used by ManualPromiseResolver<> to generate callbacks.
 template <typename T, typename... Args>
 class PromiseCallbackHelper {
  public:
-  using Callback = base::OnceCallback<void(Args&&...)>;
-  using RepeatingCallback = base::RepeatingCallback<void(Args&&...)>;
+  using Callback = base::OnceCallback<void(Args...)>;
+  using RepeatingCallback = base::RepeatingCallback<void(Args...)>;
 
   static Callback GetResolveCallback(scoped_refptr<AbstractPromise>& promise) {
     return base::BindOnce(
-        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+        [](scoped_refptr<AbstractPromise> promise, Args... args) {
           promise->emplace(Resolved<T>{std::forward<Args>(args)...});
           promise->OnResolved();
         },
-        promise);
+        PromiseHolder(promise));
   }
 
   static RepeatingCallback GetRepeatingResolveCallback(
       scoped_refptr<AbstractPromise>& promise) {
     return base::BindRepeating(
-        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+        [](scoped_refptr<AbstractPromise> promise, Args... args) {
           promise->emplace(Resolved<T>{std::forward<Args>(args)...});
           promise->OnResolved();
         },
-        promise);
+        PromiseHolder(promise));
   }
 
   static Callback GetRejectCallback(scoped_refptr<AbstractPromise>& promise) {
     return base::BindOnce(
-        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+        [](scoped_refptr<AbstractPromise> promise, Args... args) {
           promise->emplace(Rejected<T>{std::forward<Args>(args)...});
           promise->OnRejected();
         },
-        promise);
+        PromiseHolder(promise));
   }
 
   static RepeatingCallback GetRepeatingRejectCallback(
       scoped_refptr<AbstractPromise>& promise) {
     return base::BindRepeating(
-        [](scoped_refptr<AbstractPromise> promise, Args&&... args) {
+        [](scoped_refptr<AbstractPromise> promise, Args... args) {
           promise->emplace(Rejected<T>{std::forward<Args>(args)...});
           promise->OnRejected();
         },
-        promise);
+        PromiseHolder(promise));
   }
 };
 
@@ -605,13 +660,13 @@
 template <typename PromiseType, typename CallbackArgType>
 struct IsValidPromiseArg {
   static constexpr bool value =
-      std::is_same<PromiseType, std::decay_t<CallbackArgType>>::value;
+      std::is_convertible<PromiseType, std::decay_t<CallbackArgType>>::value;
 };
 
 template <typename PromiseType, typename CallbackArgType>
 struct IsValidPromiseArg<PromiseType&, CallbackArgType> {
   static constexpr bool value =
-      std::is_same<PromiseType&, CallbackArgType>::value;
+      std::is_convertible<PromiseType&, CallbackArgType>::value;
 };
 
 // This template helps assign the reject value from a prerequisite into the
@@ -626,6 +681,41 @@
 
 // TODO(alexclarke): Specalize AllPromiseRejectHelper for variants.
 
+// To reduce template bloat executors hold CallbackBase. These functions convert
+// various types to CallbackBase.
+DoNothing BASE_EXPORT ToCallbackBase(DoNothing task);
+
+template <typename CallbackT>
+CallbackBase&& ToCallbackBase(CallbackT&& task) {
+  static_assert(sizeof(CallbackBase) == sizeof(CallbackT),
+                "We assume it's possible to cast from CallbackBase to "
+                "CallbackT");
+  return static_cast<CallbackBase&&>(task);
+}
+
+template <typename CallbackT>
+CallbackBase&& ToCallbackBase(const CallbackT&& task) {
+  static_assert(sizeof(CallbackBase) == sizeof(CallbackT),
+                "We assume it's possible to cast from CallbackBase to "
+                "CallbackT");
+  return static_cast<CallbackBase&&>(const_cast<CallbackT&&>(task));
+}
+
+// Helps reduce template bloat by moving AbstractPromise construction out of
+// line.
+scoped_refptr<AbstractPromise> BASE_EXPORT
+ConstructAbstractPromiseWithSinglePrerequisite(
+    const scoped_refptr<TaskRunner>& task_runner,
+    const Location& from_here,
+    AbstractPromise* prerequsite,
+    internal::PromiseExecutor::Data&& executor_data) noexcept;
+
+scoped_refptr<AbstractPromise> BASE_EXPORT
+ConstructManualPromiseResolverPromise(const Location& from_here,
+                                      RejectPolicy reject_policy,
+                                      bool can_resolve,
+                                      bool can_reject);
+
 }  // namespace internal
 }  // namespace base
 
diff --git a/base/task/promise/helpers_unittest.cc b/base/task/promise/helpers_unittest.cc
index edaecce..a8fe61d 100644
--- a/base/task/promise/helpers_unittest.cc
+++ b/base/task/promise/helpers_unittest.cc
@@ -6,8 +6,10 @@
 
 #include "base/bind.h"
 #include "base/task/promise/promise.h"
+#include "base/task_runner.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/do_nothing_promise.h"
+#include "base/test/gtest_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -280,5 +282,125 @@
   EXPECT_EQ(result->value().type(), TypeId::From<Resolved<void>>());
 }
 
+TEST(PromiseCallbackHelper, GetResolveCallback) {
+  PromiseCallbackHelper<int, int> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  OnceCallback<void(int)> resolve_cb = helper.GetResolveCallback(promise);
+
+  std::move(resolve_cb).Run(1234);
+
+  EXPECT_EQ(unique_any_cast<Resolved<int>>(promise->value()).value, 1234);
+}
+
+TEST(PromiseCallbackHelper, GetResolveReferenceCallback) {
+  int foo = 123;
+  PromiseCallbackHelper<int&, int&> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  OnceCallback<void(int&)> resolve_cb = helper.GetResolveCallback(promise);
+
+  std::move(resolve_cb).Run(foo);
+
+  EXPECT_EQ(&unique_any_cast<Resolved<int&>>(promise->value()).value, &foo);
+}
+
+TEST(PromiseCallbackHelper, GetRejectCallback) {
+  PromiseCallbackHelper<int, int> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy(
+          RejectPolicy::kCatchNotRequired);
+
+  OnceCallback<void(int)> reject_cb = helper.GetRejectCallback(promise);
+
+  std::move(reject_cb).Run(1234);
+
+  EXPECT_EQ(unique_any_cast<Rejected<int>>(promise->value()).value, 1234);
+}
+
+TEST(PromiseCallbackHelper, GetRejectReferenceCallback) {
+  int foo = 123;
+  PromiseCallbackHelper<int&, int&> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy(
+          RejectPolicy::kCatchNotRequired);
+
+  OnceCallback<void(int&)> reject_cb = helper.GetRejectCallback(promise);
+
+  std::move(reject_cb).Run(foo);
+
+  EXPECT_EQ(&unique_any_cast<Rejected<int&>>(promise->value()).value, &foo);
+}
+
+TEST(PromiseCallbackHelper, GetRepeatingResolveCallback) {
+  PromiseCallbackHelper<int, int> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  RepeatingCallback<void(int)> resolve_cb =
+      helper.GetRepeatingResolveCallback(promise);
+
+  resolve_cb.Run(1234);
+
+  EXPECT_EQ(unique_any_cast<Resolved<int>>(promise->value()).value, 1234);
+
+  // Can't run |resolve_cb| more than once.
+  EXPECT_DCHECK_DEATH({ resolve_cb.Run(1234); });
+}
+
+TEST(PromiseCallbackHelper, GetRepeatingResolveReferenceCallback) {
+  int foo = 123;
+  PromiseCallbackHelper<int&, int&> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
+
+  RepeatingCallback<void(int&)> resolve_cb =
+      helper.GetRepeatingResolveCallback(promise);
+
+  resolve_cb.Run(foo);
+
+  EXPECT_EQ(&unique_any_cast<Resolved<int&>>(promise->value()).value, &foo);
+
+  // Can't run |resolve_cb| more than once.
+  EXPECT_DCHECK_DEATH({ resolve_cb.Run(foo); });
+}
+
+TEST(PromiseCallbackHelper, GetRepeatingRejectCallback) {
+  PromiseCallbackHelper<int, int> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy(
+          RejectPolicy::kCatchNotRequired);
+
+  RepeatingCallback<void(int)> reject_cb =
+      helper.GetRepeatingRejectCallback(promise);
+
+  reject_cb.Run(1234);
+
+  EXPECT_EQ(unique_any_cast<Rejected<int>>(promise->value()).value, 1234);
+
+  // Can't run |reject_cb| more than once.
+  EXPECT_DCHECK_DEATH({ reject_cb.Run(1234); });
+}
+
+TEST(PromiseCallbackHelper, GetRepeatingRejectReferenceCallback) {
+  int foo = 123;
+  PromiseCallbackHelper<int&, int&> helper;
+  scoped_refptr<AbstractPromise> promise =
+      DoNothingPromiseBuilder(FROM_HERE).SetCanReject(true).SetRejectPolicy(
+          RejectPolicy::kCatchNotRequired);
+
+  RepeatingCallback<void(int&)> reject_cb =
+      helper.GetRepeatingRejectCallback(promise);
+
+  reject_cb.Run(foo);
+
+  EXPECT_EQ(&unique_any_cast<Rejected<int&>>(promise->value()).value, &foo);
+
+  // Can't run |reject_cb| more than once.
+  EXPECT_DCHECK_DEATH({ reject_cb.Run(foo); });
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/task/promise/no_op_promise_executor.cc b/base/task/promise/no_op_promise_executor.cc
index 36d5d47..a74cde6 100644
--- a/base/task/promise/no_op_promise_executor.cc
+++ b/base/task/promise/no_op_promise_executor.cc
@@ -18,9 +18,9 @@
 
 NoOpPromiseExecutor::~NoOpPromiseExecutor() {}
 
-AbstractPromise::Executor::PrerequisitePolicy
-NoOpPromiseExecutor::GetPrerequisitePolicy() const {
-  return AbstractPromise::Executor::PrerequisitePolicy::kNever;
+PromiseExecutor::PrerequisitePolicy NoOpPromiseExecutor::GetPrerequisitePolicy()
+    const {
+  return PromiseExecutor::PrerequisitePolicy::kNever;
 }
 
 bool NoOpPromiseExecutor::IsCancelled() const {
@@ -28,14 +28,14 @@
 }
 
 #if DCHECK_IS_ON()
-AbstractPromise::Executor::ArgumentPassingType
+PromiseExecutor::ArgumentPassingType
 NoOpPromiseExecutor::ResolveArgumentPassingType() const {
-  return AbstractPromise::Executor::ArgumentPassingType::kNoCallback;
+  return PromiseExecutor::ArgumentPassingType::kNoCallback;
 }
 
-AbstractPromise::Executor::ArgumentPassingType
+PromiseExecutor::ArgumentPassingType
 NoOpPromiseExecutor::RejectArgumentPassingType() const {
-  return AbstractPromise::Executor::ArgumentPassingType::kNoCallback;
+  return PromiseExecutor::ArgumentPassingType::kNoCallback;
 }
 
 bool NoOpPromiseExecutor::CanResolve() const {
@@ -55,12 +55,10 @@
     bool can_resolve,
     bool can_reject,
     RejectPolicy reject_policy) {
-  return internal::AbstractPromise::Create(
-      nullptr, from_here, nullptr, reject_policy,
-      internal::AbstractPromise::ConstructWith<
-          internal::DependentList::ConstructUnresolved,
-          internal::NoOpPromiseExecutor>(),
-      can_resolve, can_reject);
+  return AbstractPromise::CreateNoPrerequisitePromise(
+      from_here, reject_policy, DependentList::ConstructUnresolved(),
+      PromiseExecutor::Data(in_place_type_t<NoOpPromiseExecutor>(), can_resolve,
+                            can_reject));
 }
 
 }  // namespace internal
diff --git a/base/task/promise/no_op_promise_executor.h b/base/task/promise/no_op_promise_executor.h
index d65dbc3..e3e03a7 100644
--- a/base/task/promise/no_op_promise_executor.h
+++ b/base/task/promise/no_op_promise_executor.h
@@ -24,14 +24,12 @@
       bool can_reject,
       RejectPolicy reject_policy);
 
-  AbstractPromise::Executor::PrerequisitePolicy GetPrerequisitePolicy() const;
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const;
   bool IsCancelled() const;
 
 #if DCHECK_IS_ON()
-  AbstractPromise::Executor::ArgumentPassingType ResolveArgumentPassingType()
-      const;
-  AbstractPromise::Executor::ArgumentPassingType RejectArgumentPassingType()
-      const;
+  PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const;
+  PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const;
   bool CanResolve() const;
   bool CanReject() const;
 #endif
diff --git a/base/task/promise/post_task_executor.h b/base/task/promise/post_task_executor.h
new file mode 100644
index 0000000..0be6ce2
--- /dev/null
+++ b/base/task/promise/post_task_executor.h
@@ -0,0 +1,95 @@
+// 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_POST_TASK_EXECUTOR_H_
+#define BASE_TASK_PROMISE_POST_TASK_EXECUTOR_H_
+
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/task/promise/abstract_promise.h"
+#include "base/task/promise/helpers.h"
+
+namespace base {
+namespace internal {
+
+// PromiseExecutor for use by PostTask.
+template <typename ReturnType>
+class PostTaskExecutor {
+ public:
+  // Extract properties from |ReturnType|.
+  using CallbackTraits = PromiseCallbackTraits<ReturnType>;
+  using ReturnedPromiseResolveT = typename CallbackTraits::ResolveType;
+  using ReturnedPromiseRejectT = typename CallbackTraits::RejectType;
+  using ResolveStorage = Resolved<ReturnedPromiseResolveT>;
+  using RejectStorage = Rejected<ReturnedPromiseRejectT>;
+
+  explicit PostTaskExecutor(CallbackBase&& task) noexcept
+      : task_(std::move(task)) {}
+
+  explicit PostTaskExecutor(DoNothing task) noexcept : task_(task.Once()) {}
+
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const {
+    return PromiseExecutor::PrerequisitePolicy::kAll;
+  }
+
+  bool IsCancelled() const { return task_.IsCancelled(); }
+
+#if DCHECK_IS_ON()
+  PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const {
+    return PromiseExecutor::ArgumentPassingType::kNoCallback;
+  }
+
+  PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const {
+    return PromiseExecutor::ArgumentPassingType::kNoCallback;
+  }
+
+  bool CanResolve() const { return CallbackTraits::could_resolve; }
+
+  bool CanReject() const { return CallbackTraits::could_reject; }
+#endif
+
+  NOINLINE void Execute(AbstractPromise* promise) {
+    static_assert(sizeof(CallbackBase) == sizeof(OnceCallback<ReturnType()>),
+                  "We assume it's possible to cast from CallbackBase to "
+                  "OnceCallback<ReturnType()>");
+    OnceCallback<ReturnType()>* task =
+        static_cast<OnceCallback<ReturnType()>*>(&task_);
+    internal::RunHelper<OnceCallback<ReturnType()>, void, ResolveStorage,
+                        RejectStorage>::Run(std::move(*task), nullptr, promise);
+
+    using CheckResultTagType =
+        typename internal::PromiseCallbackTraits<ReturnType>::TagType;
+
+    CheckResultType(promise, CheckResultTagType());
+  }
+
+ private:
+  static void CheckResultType(AbstractPromise* promise, CouldResolveOrReject) {
+    if (promise->IsResolvedWithPromise() ||
+        promise->value().type() == TypeId::From<ResolveStorage>()) {
+      promise->OnResolved();
+    } else {
+      DCHECK_EQ(promise->value().type(), TypeId::From<RejectStorage>())
+          << " See " << promise->from_here().ToString();
+      promise->OnRejected();
+    }
+  }
+
+  static void CheckResultType(AbstractPromise* promise, CanOnlyResolve) {
+    promise->OnResolved();
+  }
+
+  static void CheckResultType(AbstractPromise* promise, CanOnlyReject) {
+    promise->OnRejected();
+  }
+
+  CallbackBase task_;
+
+  DISALLOW_COPY_AND_ASSIGN(PostTaskExecutor);
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_POST_TASK_EXECUTOR_H_
diff --git a/base/task/promise/post_task_executor_unittest.cc b/base/task/promise/post_task_executor_unittest.cc
new file mode 100644
index 0000000..8e86aae
--- /dev/null
+++ b/base/task/promise/post_task_executor_unittest.cc
@@ -0,0 +1,68 @@
+// 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/post_task_executor.h"
+
+#include "base/bind.h"
+#include "base/task/promise/abstract_promise.h"
+#include "base/task/promise/helpers.h"
+#include "base/task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+class PostTaskExecutorTest : public testing::Test {
+ public:
+  template <typename CallbackT>
+  scoped_refptr<internal::AbstractPromise> CreatePostTaskPromise(
+      const Location& from_here,
+      CallbackT&& task) {
+    // Extract properties from |task| callback.
+    using CallbackTraits = CallbackTraits<std::decay_t<CallbackT>>;
+
+    internal::PromiseExecutor::Data executor_data(
+        in_place_type_t<
+            internal::PostTaskExecutor<typename CallbackTraits::ReturnType>>(),
+        internal::ToCallbackBase(std::move(task)));
+
+    return AbstractPromise::CreateNoPrerequisitePromise(
+        from_here, RejectPolicy::kMustCatchRejection,
+        internal::DependentList::ConstructUnresolved(),
+        std::move(executor_data));
+  }
+};
+
+TEST_F(PostTaskExecutorTest, OnceClosure) {
+  bool run = false;
+
+  scoped_refptr<AbstractPromise> p = CreatePostTaskPromise(
+      FROM_HERE, BindOnce([](bool* run) { *run = true; }, &run));
+
+  p->Execute();
+
+  EXPECT_TRUE(run);
+}
+
+TEST_F(PostTaskExecutorTest, RepeatingClosure) {
+  bool run = false;
+
+  scoped_refptr<AbstractPromise> p = CreatePostTaskPromise(
+      FROM_HERE, BindRepeating([](bool* run) { *run = true; }, &run));
+
+  p->Execute();
+
+  EXPECT_TRUE(run);
+}
+
+TEST_F(PostTaskExecutorTest, DoNothing) {
+  // Check it compiles and the executor doesn't crash when run.
+  scoped_refptr<AbstractPromise> p =
+      CreatePostTaskPromise(FROM_HERE, DoNothing());
+
+  p->Execute();
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/promise.h b/base/task/promise/promise.h
index 0990708..4927bde 100644
--- a/base/task/promise/promise.h
+++ b/base/task/promise/promise.h
@@ -6,7 +6,6 @@
 #define BASE_TASK_PROMISE_PROMISE_H_
 
 #include "base/run_loop.h"
-#include "base/task/post_task.h"
 #include "base/task/promise/all_container_executor.h"
 #include "base/task/promise/all_tuple_executor.h"
 #include "base/task/promise/finally_executor.h"
@@ -14,10 +13,14 @@
 #include "base/task/promise/no_op_promise_executor.h"
 #include "base/task/promise/promise_result.h"
 #include "base/task/promise/then_and_catch_executor.h"
-#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/task/task_traits.h"
 
 namespace base {
 
+// We can't include post_task.h here so we forward declare it.
+BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunner(
+    const TaskTraits& traits);
+
 // Inspired by ES6 promises, Promise<> is a PostTask based callback system for
 // asynchronous operations. An operation can resolve (succeed) with a value and
 // optionally reject (fail) with a different result. Interested parties can be
@@ -48,22 +51,6 @@
       scoped_refptr<internal::AbstractPromise> abstract_promise) noexcept
       : abstract_promise_(std::move(abstract_promise)) {}
 
-  // Constructs an unresolved promise for use by a ManualPromiseResolver<> and
-  // TaskRunner::PostPromise.
-  Promise(scoped_refptr<TaskRunner> task_runner,
-          const Location& location,
-          RejectPolicy reject_policy)
-      : abstract_promise_(internal::AbstractPromise::Create(
-            std::move(task_runner),
-            location,
-            nullptr,
-            reject_policy,
-            internal::AbstractPromise::ConstructWith<
-                internal::DependentList::ConstructUnresolved,
-                internal::NoOpPromiseExecutor>(),
-            /* can_resolve */ !std::is_same<ResolveType, NoResolve>::value,
-            /* can_reject */ !std::is_same<RejectType, NoReject>::value)) {}
-
   NOINLINE ~Promise() = default;
 
   operator bool() const { return !!abstract_promise_; }
@@ -134,11 +121,15 @@
   // 2. Promise<Resolve, Reject> where the result is a curried promise.
   //
   // If a promise has multiple Catches they will be run in order of creation.
+  //
+  // |task_runner| is const-ref to avoid bloat due the destructor (which posts a
+  // task).
   template <typename RejectCb>
-  NOINLINE auto CatchOn(scoped_refptr<TaskRunner> task_runner,
+  NOINLINE auto CatchOn(const scoped_refptr<TaskRunner>& task_runner,
                         const Location& from_here,
-                        RejectCb&& on_reject) noexcept {
+                        RejectCb on_reject) noexcept {
     DCHECK(abstract_promise_);
+    DCHECK(!on_reject.is_null());
 
     // Extract properties from the |on_reject| callback.
     using RejectCallbackTraits = internal::CallbackTraits<RejectCb>;
@@ -169,23 +160,18 @@
             std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value,
         "Google C++ Style: References in function parameters must be const.");
 
-    return Promise<ReturnedPromiseResolveT,
-                   ReturnedPromiseRejectT>(internal::AbstractPromise::Create(
-        std::move(task_runner), from_here,
-        std::make_unique<internal::AbstractPromise::AdjacencyList>(
-            abstract_promise_.get()),
-        RejectPolicy::kMustCatchRejection,
-        internal::AbstractPromise::ConstructWith<
-            internal::DependentList::ConstructUnresolved,
-            internal::ThenAndCatchExecutor<
-                OnceClosure,  // Never called.
-                OnceCallback<typename RejectCallbackTraits::SignatureType>,
-                internal::NoCallback, RejectType,
-                Resolved<ReturnedPromiseResolveT>,
-                Rejected<ReturnedPromiseRejectT>>>(),
-        OnceClosure(),
-        static_cast<OnceCallback<typename RejectCallbackTraits::SignatureType>>(
-            std::forward<RejectCb>(on_reject))));
+    internal::PromiseExecutor::Data executor_data(
+        in_place_type_t<internal::ThenAndCatchExecutor<
+            OnceClosure,  // Never called.
+            OnceCallback<typename RejectCallbackTraits::SignatureType>,
+            internal::NoCallback, RejectType, Resolved<ReturnedPromiseResolveT>,
+            Rejected<ReturnedPromiseRejectT>>>(),
+        OnceClosure(), internal::ToCallbackBase(std::move(on_reject)));
+
+    return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+        ConstructAbstractPromiseWithSinglePrerequisite(
+            task_runner, from_here, abstract_promise_.get(),
+            std::move(executor_data)));
   }
 
   template <typename RejectCb>
@@ -198,7 +184,7 @@
 
   template <typename RejectCb>
   auto CatchHere(const Location& from_here, RejectCb&& on_reject) noexcept {
-    return CatchOn(SequencedTaskRunnerHandle::Get(), from_here,
+    return CatchOn(internal::GetCurrentSequence(), from_here,
                    std::forward<RejectCb>(on_reject));
   }
 
@@ -212,11 +198,15 @@
   // 2. Promise<Resolve, Reject> where the result is a curried promise.
   //
   // If a promise has multiple Thens they will be run in order of creation.
+  //
+  // |task_runner| is const-ref to avoid bloat due the destructor (which posts a
+  // task).
   template <typename ResolveCb>
-  NOINLINE auto ThenOn(scoped_refptr<TaskRunner> task_runner,
+  NOINLINE auto ThenOn(const scoped_refptr<TaskRunner>& task_runner,
                        const Location& from_here,
-                       ResolveCb&& on_resolve) noexcept {
+                       ResolveCb on_resolve) noexcept {
     DCHECK(abstract_promise_);
+    DCHECK(!on_resolve.is_null());
 
     // Extract properties from the |on_resolve| callback.
     using ResolveCallbackTraits =
@@ -246,20 +236,18 @@
             std::is_const<std::remove_reference_t<ResolveCallbackArgT>>::value,
         "Google C++ Style: References in function parameters must be const.");
 
+    internal::PromiseExecutor::Data executor_data(
+        in_place_type_t<internal::ThenAndCatchExecutor<
+            OnceCallback<typename ResolveCallbackTraits::SignatureType>,
+            OnceClosure, ResolveType, internal::NoCallback,
+            Resolved<ReturnedPromiseResolveT>,
+            Rejected<ReturnedPromiseRejectT>>>(),
+        internal::ToCallbackBase(std::move(on_resolve)), OnceClosure());
+
     return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
-        internal::AbstractPromise::Create(
-            std::move(task_runner), from_here,
-            std::make_unique<internal::AbstractPromise::AdjacencyList>(
-                abstract_promise_.get()),
-            RejectPolicy::kMustCatchRejection,
-            internal::AbstractPromise::ConstructWith<
-                internal::DependentList::ConstructUnresolved,
-                internal::ThenAndCatchExecutor<
-                    OnceCallback<typename ResolveCallbackTraits::SignatureType>,
-                    OnceClosure, ResolveType, internal::NoCallback,
-                    Resolved<ReturnedPromiseResolveT>,
-                    Rejected<ReturnedPromiseRejectT>>>(),
-            std::forward<ResolveCb>(on_resolve), OnceClosure()));
+        ConstructAbstractPromiseWithSinglePrerequisite(
+            task_runner, from_here, abstract_promise_.get(),
+            std::move(executor_data)));
   }
 
   template <typename ResolveCb>
@@ -272,7 +260,7 @@
 
   template <typename ResolveCb>
   auto ThenHere(const Location& from_here, ResolveCb&& on_resolve) noexcept {
-    return ThenOn(SequencedTaskRunnerHandle::Get(), from_here,
+    return ThenOn(internal::GetCurrentSequence(), from_here,
                   std::forward<ResolveCb>(on_resolve));
   }
 
@@ -293,12 +281,17 @@
   // Note if either |on_resolve| or |on_reject| are canceled (due to weak
   // pointer invalidation), then the other must be canceled at the same time as
   // well. This restriction only applies to this form of ThenOn/ThenHere.
+  //
+  // |task_runner| is const-ref to avoid bloat due the destructor (which posts a
+  // task).
   template <typename ResolveCb, typename RejectCb>
-  NOINLINE auto ThenOn(scoped_refptr<TaskRunner> task_runner,
+  NOINLINE auto ThenOn(const scoped_refptr<TaskRunner>& task_runner,
                        const Location& from_here,
-                       ResolveCb&& on_resolve,
-                       RejectCb&& on_reject) noexcept {
+                       ResolveCb on_resolve,
+                       RejectCb on_reject) noexcept {
     DCHECK(abstract_promise_);
+    DCHECK(!on_resolve.is_null());
+    DCHECK(!on_reject.is_null());
 
     // Extract properties from the |on_resolve| and |on_reject| callbacks.
     using ResolveCallbackTraits = internal::CallbackTraits<ResolveCb>;
@@ -342,24 +335,19 @@
             std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value,
         "Google C++ Style: References in function parameters must be const.");
 
-    return Promise<ReturnedPromiseResolveT,
-                   ReturnedPromiseRejectT>(internal::AbstractPromise::Create(
-        std::move(task_runner), from_here,
-        std::make_unique<internal::AbstractPromise::AdjacencyList>(
-            abstract_promise_.get()),
-        RejectPolicy::kMustCatchRejection,
-        internal::AbstractPromise::ConstructWith<
-            internal::DependentList::ConstructUnresolved,
-            internal::ThenAndCatchExecutor<
-                OnceCallback<typename ResolveCallbackTraits::SignatureType>,
-                OnceCallback<typename RejectCallbackTraits::SignatureType>,
-                ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>,
-                Rejected<ReturnedPromiseRejectT>>>(),
-        static_cast<
-            OnceCallback<typename ResolveCallbackTraits::SignatureType>>(
-            std::forward<ResolveCb>(on_resolve)),
-        static_cast<OnceCallback<typename RejectCallbackTraits::SignatureType>>(
-            std::forward<RejectCb>(on_reject))));
+    internal::PromiseExecutor::Data executor_data(
+        in_place_type_t<internal::ThenAndCatchExecutor<
+            OnceCallback<typename ResolveCallbackTraits::SignatureType>,
+            OnceCallback<typename RejectCallbackTraits::SignatureType>,
+            ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>,
+            Rejected<ReturnedPromiseRejectT>>>(),
+        internal::ToCallbackBase(std::move(on_resolve)),
+        internal::ToCallbackBase(std::move(on_reject)));
+
+    return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+        ConstructAbstractPromiseWithSinglePrerequisite(
+            task_runner, from_here, abstract_promise_.get(),
+            std::move(executor_data)));
   }
 
   template <typename ResolveCb, typename RejectCb>
@@ -376,7 +364,7 @@
   auto ThenHere(const Location& from_here,
                 ResolveCb&& on_resolve,
                 RejectCb&& on_reject) noexcept {
-    return ThenOn(SequencedTaskRunnerHandle::Get(), from_here,
+    return ThenOn(internal::GetCurrentSequence(), from_here,
                   std::forward<ResolveCb>(on_resolve),
                   std::forward<RejectCb>(on_reject));
   }
@@ -387,10 +375,13 @@
   // promises, this doesn't return a Promise that is resolved or rejected with
   // the parent's value if |finally_callback| returns void. (We could support
   // this if needed it but it seems unlikely to be used).
+  //
+  // |task_runner| is const-ref to avoid bloat due the destructor (which posts a
+  // task).
   template <typename FinallyCb>
-  NOINLINE auto FinallyOn(scoped_refptr<TaskRunner> task_runner,
+  NOINLINE auto FinallyOn(const scoped_refptr<TaskRunner>& task_runner,
                           const Location& from_here,
-                          FinallyCb&& finally_callback) noexcept {
+                          FinallyCb finally_callback) noexcept {
     DCHECK(abstract_promise_);
 
     // Extract properties from |finally_callback| callback.
@@ -402,19 +393,17 @@
     static_assert(std::is_void<CallbackArgT>::value,
                   "|finally_callback| callback must have no arguments");
 
+    internal::PromiseExecutor::Data executor_data(
+        in_place_type_t<internal::FinallyExecutor<
+            OnceCallback<typename CallbackTraits::ReturnType()>,
+            Resolved<ReturnedPromiseResolveT>,
+            Rejected<ReturnedPromiseRejectT>>>(),
+        internal::ToCallbackBase(std::move(finally_callback)));
+
     return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
-        internal::AbstractPromise::Create(
-            std::move(task_runner), from_here,
-            std::make_unique<internal::AbstractPromise::AdjacencyList>(
-                abstract_promise_.get()),
-            RejectPolicy::kMustCatchRejection,
-            internal::AbstractPromise::ConstructWith<
-                internal::DependentList::ConstructUnresolved,
-                internal::FinallyExecutor<
-                    OnceCallback<typename CallbackTraits::ReturnType()>,
-                    Resolved<ReturnedPromiseResolveT>,
-                    Rejected<ReturnedPromiseRejectT>>>(),
-            std::forward<FinallyCb>(finally_callback)));
+        ConstructAbstractPromiseWithSinglePrerequisite(
+            task_runner, from_here, abstract_promise_.get(),
+            std::move(executor_data)));
   }
 
   template <typename FinallyCb>
@@ -428,7 +417,7 @@
   template <typename FinallyCb>
   auto FinallyHere(const Location& from_here,
                    FinallyCb&& finally_callback) noexcept {
-    return FinallyOn(SequencedTaskRunnerHandle::Get(), from_here,
+    return FinallyOn(internal::GetCurrentSequence(), from_here,
                      std::move(finally_callback));
   }
 
@@ -436,14 +425,16 @@
   NOINLINE static Promise<ResolveType, RejectType> CreateResolved(
       const Location& from_here,
       Args&&... args) noexcept {
+    internal::PromiseExecutor::Data executor_data(
+        in_place_type_t<internal::NoOpPromiseExecutor>(),
+        /* can_resolve */ true,
+        /* can_reject */ false);
+
     scoped_refptr<internal::AbstractPromise> promise(
         internal::AbstractPromise::Create(
             nullptr, from_here, nullptr, RejectPolicy::kMustCatchRejection,
-            internal::AbstractPromise::ConstructWith<
-                internal::DependentList::ConstructResolved,
-                internal::NoOpPromiseExecutor>(),
-            /* can_resolve */ true,
-            /* can_reject */ false));
+            internal::DependentList::ConstructResolved(),
+            std::move(executor_data)));
     promise->emplace(in_place_type_t<Resolved<ResolveType>>(),
                      std::forward<Args>(args)...);
     return Promise<ResolveType, RejectType>(std::move(promise));
@@ -453,14 +444,16 @@
   NOINLINE static Promise<ResolveType, RejectType> CreateRejected(
       const Location& from_here,
       Args&&... args) noexcept {
+    internal::PromiseExecutor::Data executor_data(
+        in_place_type_t<internal::NoOpPromiseExecutor>(),
+        /* can_resolve */ false,
+        /* can_reject */ true);
+
     scoped_refptr<internal::AbstractPromise> promise(
         internal::AbstractPromise::Create(
             nullptr, from_here, nullptr, RejectPolicy::kMustCatchRejection,
-            internal::AbstractPromise::ConstructWith<
-                internal::DependentList::ConstructRejected,
-                internal::NoOpPromiseExecutor>(),
-            /* can_resolve */ false,
-            /* can_reject */ true));
+            internal::DependentList::ConstructResolved(),
+            std::move(executor_data)));
     promise->emplace(in_place_type_t<Rejected<RejectType>>(),
                      std::forward<Args>(args)...);
     return Promise<ResolveType, RejectType>(std::move(promise));
@@ -511,8 +504,13 @@
 
   ManualPromiseResolver(
       const Location& from_here,
-      RejectPolicy reject_policy = RejectPolicy::kMustCatchRejection)
-      : promise_(SequencedTaskRunnerHandle::Get(), from_here, reject_policy) {}
+      RejectPolicy reject_policy = RejectPolicy::kMustCatchRejection) {
+    promise_ = Promise<ResolveType, RejectType>(
+        internal::ConstructManualPromiseResolverPromise(
+            from_here, reject_policy,
+            /* can_resolve */ !std::is_same<ResolveType, NoResolve>::value,
+            /* can_reject */ !std::is_same<RejectType, NoReject>::value));
+  }
 
   ~ManualPromiseResolver() = default;
 
@@ -641,16 +639,19 @@
     for (auto&& p : {promises.abstract_promise_...}) {
       prerequisite_list[i++].SetPrerequisite(p.get());
     }
+
+    internal::PromiseExecutor::Data executor_data(
+        (in_place_type_t<internal::AllTuplePromiseExecutor<
+             ReturnedPromiseResolveT, ReturnedPromiseRejectT>>()));
+
     return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
         internal::AbstractPromise::Create(
             nullptr, from_here,
             std::make_unique<internal::AbstractPromise::AdjacencyList>(
                 std::move(prerequisite_list)),
             RejectPolicy::kMustCatchRejection,
-            internal::AbstractPromise::ConstructWith<
-                internal::DependentList::ConstructUnresolved,
-                internal::AllTuplePromiseExecutor<ReturnedPromiseResolveT,
-                                                  ReturnedPromiseRejectT>>()));
+            internal::DependentList::ConstructUnresolved(),
+            std::move(executor_data)));
   }
 
   template <typename Resolve, typename Reject>
diff --git a/base/task/promise/promise_executor.cc b/base/task/promise/promise_executor.cc
new file mode 100644
index 0000000..5d8642f
--- /dev/null
+++ b/base/task/promise/promise_executor.cc
@@ -0,0 +1,50 @@
+// 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/promise_executor.h"
+
+namespace base {
+namespace internal {
+
+PromiseExecutor::~PromiseExecutor() {
+  if (data_.vtable_)
+    data_.vtable_->destructor(data_.storage_.array);
+  data_.vtable_ = nullptr;
+}
+
+PromiseExecutor::PrerequisitePolicy PromiseExecutor::GetPrerequisitePolicy()
+    const {
+  return data_.vtable_->get_prerequisite_policy(data_.storage_.array);
+}
+
+bool PromiseExecutor::IsCancelled() const {
+  return data_.vtable_->is_cancelled(data_.storage_.array);
+}
+
+#if DCHECK_IS_ON()
+PromiseExecutor::ArgumentPassingType
+PromiseExecutor::ResolveArgumentPassingType() const {
+  return data_.vtable_->resolve_argument_passing_type(data_.storage_.array);
+}
+
+PromiseExecutor::ArgumentPassingType
+PromiseExecutor::RejectArgumentPassingType() const {
+  return data_.vtable_->reject_argument_passing_type(data_.storage_.array);
+}
+
+bool PromiseExecutor::CanResolve() const {
+  return data_.vtable_->can_resolve(data_.storage_.array);
+}
+
+bool PromiseExecutor::CanReject() const {
+  return data_.vtable_->can_reject(data_.storage_.array);
+}
+#endif
+
+void PromiseExecutor::Execute(AbstractPromise* promise) {
+  return data_.vtable_->execute(data_.storage_.array, promise);
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/task/promise/promise_executor.h b/base/task/promise/promise_executor.h
new file mode 100644
index 0000000..f893a2c
--- /dev/null
+++ b/base/task/promise/promise_executor.h
@@ -0,0 +1,222 @@
+// 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_PROMISE_EXECUTOR_H_
+#define BASE_TASK_PROMISE_PROMISE_EXECUTOR_H_
+
+#include "base/base_export.h"
+#include "base/containers/unique_any.h"
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+class AbstractPromise;
+
+// 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.
+//
+// Ideally Executor would be a pure virtual class, but we want to store these
+// inline to reduce the number of memory allocations (small object
+// optimization). The problem is even though placement new returns the same
+// address it was allocated at, you have to use the returned pointer.  Casting
+// the buffer to the derived class is undefined behavior. STL implementations
+// usually store an extra pointer, but there we have opted for implementing
+// our own VTable to save a little bit of memory.
+class BASE_EXPORT PromiseExecutor {
+ private:
+  static constexpr size_t MaxSize = sizeof(void*) * 2;
+  struct VTable;
+
+ public:
+  // We could just construct Executor in place, but that means templates need
+  // to inline the AbstractPromise constructor which we'd like to avoid due to
+  // binary size concerns. Despite containing refcounted objects, Data is
+  // intended to be memcopied into the Executor and it deliberately does not
+  // have a destructor. The type erasure provided by Executor allows us to
+  // move the AbstractPromise construction out of line.
+  class Data {
+   public:
+    // Constructs |Derived| in place.
+    template <typename Derived, typename... Args>
+    explicit Data(in_place_type_t<Derived>, Args&&... args) {
+      static_assert(sizeof(Derived) <= MaxSize, "Derived is too big");
+      static_assert(sizeof(PromiseExecutor) <= sizeof(AnyInternal::InlineAlloc),
+                    "Executor is too big");
+      vtable_ = &VTableHelper<Derived>::vtable_;
+      new (storage_.array) Derived(std::forward<Args>(args)...);
+    }
+
+    Data(Data&& other) noexcept
+        : vtable_(other.vtable_), storage_(other.storage_) {
+#if DCHECK_IS_ON()
+      other.vtable_ = nullptr;
+#endif
+    }
+
+    Data(const Data& other) = delete;
+
+    ~Data() { DCHECK_EQ(vtable_, nullptr); }
+
+   private:
+    friend class PromiseExecutor;
+
+    const VTable* vtable_;
+    struct {
+      char array[MaxSize];
+    } storage_;
+  };
+
+  // Caution it's an error to use |data| after this.
+  explicit PromiseExecutor(Data&& data) : data_(std::move(data)) {}
+
+  PromiseExecutor(PromiseExecutor&& other) noexcept
+      : data_(std::move(other.data_)) {
+    other.data_.vtable_ = nullptr;
+  }
+
+  PromiseExecutor(const PromiseExecutor& other) = delete;
+
+  ~PromiseExecutor();
+
+  PromiseExecutor& operator=(const PromiseExecutor& other) = delete;
+
+  // 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.
+  PrerequisitePolicy GetPrerequisitePolicy() const;
+
+  // NB if there is both a resolve and a reject executor we require them to
+  // be both canceled at the same time.
+  bool IsCancelled() const;
+
+  // 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.
+  ArgumentPassingType ResolveArgumentPassingType() const;
+  ArgumentPassingType RejectArgumentPassingType() const;
+  bool CanResolve() const;
+  bool CanReject() const;
+#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.
+  void Execute(AbstractPromise* promise);
+
+ private:
+  struct VTable {
+    void (*destructor)(void* self);
+    PrerequisitePolicy (*get_prerequisite_policy)(const void* self);
+    bool (*is_cancelled)(const void* self);
+#if DCHECK_IS_ON()
+    ArgumentPassingType (*resolve_argument_passing_type)(const void* self);
+    ArgumentPassingType (*reject_argument_passing_type)(const void* self);
+    bool (*can_resolve)(const void* self);
+    bool (*can_reject)(const void* self);
+#endif
+    void (*execute)(void* self, AbstractPromise* promise);
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(VTable);
+  };
+
+  template <typename DerivedType>
+  struct VTableHelper {
+    VTableHelper(const VTableHelper& other) = delete;
+    VTableHelper& operator=(const VTableHelper& other) = delete;
+
+    static void Destructor(void* self) {
+      static_cast<DerivedType*>(self)->~DerivedType();
+    }
+
+    static PrerequisitePolicy GetPrerequisitePolicy(const void* self) {
+      return static_cast<const DerivedType*>(self)->GetPrerequisitePolicy();
+    }
+
+    static bool IsCancelled(const void* self) {
+      return static_cast<const DerivedType*>(self)->IsCancelled();
+    }
+
+#if DCHECK_IS_ON()
+    static ArgumentPassingType ResolveArgumentPassingType(const void* self) {
+      return static_cast<const DerivedType*>(self)
+          ->ResolveArgumentPassingType();
+    }
+
+    static ArgumentPassingType RejectArgumentPassingType(const void* self) {
+      return static_cast<const DerivedType*>(self)->RejectArgumentPassingType();
+    }
+
+    static bool CanResolve(const void* self) {
+      return static_cast<const DerivedType*>(self)->CanResolve();
+    }
+
+    static bool CanReject(const void* self) {
+      return static_cast<const DerivedType*>(self)->CanReject();
+    }
+#endif
+
+    static void Execute(void* self, AbstractPromise* promise) {
+      return static_cast<DerivedType*>(self)->Execute(promise);
+    }
+
+    static constexpr VTable vtable_ = {
+        &VTableHelper::Destructor,
+        &VTableHelper::GetPrerequisitePolicy,
+        &VTableHelper::IsCancelled,
+#if DCHECK_IS_ON()
+        &VTableHelper::ResolveArgumentPassingType,
+        &VTableHelper::RejectArgumentPassingType,
+        &VTableHelper::CanResolve,
+        &VTableHelper::CanReject,
+#endif
+        &VTableHelper::Execute,
+    };
+  };
+
+  Data data_;
+};
+
+// static
+template <typename T>
+const PromiseExecutor::VTable PromiseExecutor::VTableHelper<T>::vtable_;
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_PROMISE_PROMISE_EXECUTOR_H_
diff --git a/base/task/promise/promise_unittest.cc b/base/task/promise/promise_unittest.cc
index 93468247..a892f16 100644
--- a/base/task/promise/promise_unittest.cc
+++ b/base/task/promise/promise_unittest.cc
@@ -9,11 +9,13 @@
 
 #include "base/bind.h"
 #include "base/run_loop.h"
+#include "base/task/post_task.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/test/test_mock_time_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
@@ -132,6 +134,10 @@
 
 TEST_F(PromiseTest, GetResolveCallbackMultipleArgs) {
   ManualPromiseResolver<std::tuple<int, bool, float>> p(FROM_HERE);
+  static_assert(
+      std::is_same<OnceCallback<void(int, bool, float)>,
+                   decltype(p.GetResolveCallback<int, bool, float>())>::value,
+      "");
   p.GetResolveCallback<int, bool, float>().Run(123, true, 1.5f);
 
   RunLoop run_loop;
@@ -146,6 +152,24 @@
   run_loop.Run();
 }
 
+TEST_F(PromiseTest, ManualPromiseResolverCallbackLifetimeCanOutliveParent) {
+  OnceCallback<void(int)> resolve_cb;
+
+  RunLoop run_loop;
+  {
+    ManualPromiseResolver<int> p(FROM_HERE);
+    resolve_cb = p.GetResolveCallback();
+
+    p.promise().ThenHere(FROM_HERE, BindLambdaForTesting([&](int result) {
+                           EXPECT_EQ(123, result);
+                           run_loop.Quit();
+                         }));
+  }
+
+  std::move(resolve_cb).Run(123);
+  run_loop.Run();
+}
+
 TEST_F(PromiseTest, ResolveWithTuple) {
   ManualPromiseResolver<void> p(FROM_HERE);
   p.Resolve();
@@ -1112,10 +1136,11 @@
                                    promise_resolver->promise()))
         .ThenHere(FROM_HERE, base::BindOnce([](int v) { EXPECT_EQ(v, 42); }))
         .ThenHere(FROM_HERE, run_loop.QuitClosure());
-    base::PostTaskWithTraits(FROM_HERE, {}, base::BindLambdaForTesting([&]() {
-                               promise_resolver->Resolve(42);
-                               promise_resolver.reset();
-                             }));
+    PostTaskWithTraits(FROM_HERE, {ThreadPool()},
+                       base::BindLambdaForTesting([&]() {
+                         promise_resolver->Resolve(42);
+                         promise_resolver.reset();
+                       }));
     run_loop.Run();
     scoped_task_environment_.RunUntilIdle();
   }
@@ -1650,7 +1675,7 @@
 #if DCHECK_IS_ON()
   ManualPromiseResolver<void, void> promise_resolver(
       FROM_HERE, RejectPolicy::kCatchNotRequired);
-  RepeatingCallback<void(void)> resolve =
+  RepeatingCallback<void()> resolve =
       promise_resolver.GetRepeatingResolveCallback();
 
   resolve.Run();
@@ -1663,7 +1688,7 @@
 #if DCHECK_IS_ON()
   ManualPromiseResolver<void, void> promise_resolver(
       FROM_HERE, RejectPolicy::kCatchNotRequired);
-  RepeatingCallback<void(void)> resolve =
+  RepeatingCallback<void()> resolve =
       promise_resolver.GetRepeatingRejectCallback();
 
   resolve.Run();
diff --git a/base/task/promise/promise_unittest.nc b/base/task/promise/promise_unittest.nc
index 261a87f2..dca01e0 100644
--- a/base/task/promise/promise_unittest.nc
+++ b/base/task/promise/promise_unittest.nc
@@ -5,6 +5,7 @@
 // This is a "No Compile Test" suite.
 // http://dev.chromium.org/developers/testing/no-compile-tests
 
+#include "base/task_runner.h"
 #include "base/task/promise/promise.h"
 #include "base/task/promise/promise_result.h"
 
@@ -23,7 +24,7 @@
 #elif defined(NCTEST_METHOD_RESOLVE_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_resolve| callback must accept Promise::ResolveType or void\."]
 void WontCompile() {
   Promise<int, void> p;
-  p.ThenHere(FROM_HERE, BindOnce([](bool) { }));
+  p.ThenHere(FROM_HERE, BindOnce([](std::string) { }));
 }
 #elif defined(NCTEST_METHOD_REJECT_CALLBACK_TYPE_MISSMATCH) // [r"fatal error: static_assert failed .*\"|on_reject| callback must accept Promise::ResolveType or void\."]
 void WontCompile() {
diff --git a/base/task/promise/then_and_catch_executor.cc b/base/task/promise/then_and_catch_executor.cc
index 003eb574..b30f29c 100644
--- a/base/task/promise/then_and_catch_executor.cc
+++ b/base/task/promise/then_and_catch_executor.cc
@@ -7,14 +7,6 @@
 namespace base {
 namespace internal {
 
-ThenAndCatchExecutorCommon::ThenAndCatchExecutorCommon(
-    internal::CallbackBase&& resolve_executor,
-    internal::CallbackBase&& reject_executor)
-    : resolve_callback_(std::move(resolve_executor)),
-      reject_callback_(std::move(reject_executor)) {}
-
-ThenAndCatchExecutorCommon::~ThenAndCatchExecutorCommon() = default;
-
 bool ThenAndCatchExecutorCommon::IsCancelled() const {
   if (!resolve_callback_.is_null()) {
     // If there is both a resolve and a reject executor they must be canceled
@@ -26,9 +18,9 @@
   return reject_callback_.IsCancelled();
 }
 
-AbstractPromise::Executor::PrerequisitePolicy
+PromiseExecutor::PrerequisitePolicy
 ThenAndCatchExecutorCommon::GetPrerequisitePolicy() const {
-  return AbstractPromise::Executor::PrerequisitePolicy::kAll;
+  return PromiseExecutor::PrerequisitePolicy::kAll;
 }
 
 void ThenAndCatchExecutorCommon::Execute(AbstractPromise* promise,
diff --git a/base/task/promise/then_and_catch_executor.h b/base/task/promise/then_and_catch_executor.h
index 6cc7bb5..da9faf9 100644
--- a/base/task/promise/then_and_catch_executor.h
+++ b/base/task/promise/then_and_catch_executor.h
@@ -17,14 +17,18 @@
 // Exists to reduce template bloat.
 class BASE_EXPORT ThenAndCatchExecutorCommon {
  public:
-  ThenAndCatchExecutorCommon(CallbackBase&& resolve_callback,
-                             CallbackBase&& reject_callback);
+  ThenAndCatchExecutorCommon(internal::CallbackBase&& resolve_executor,
+                             internal::CallbackBase&& reject_executor) noexcept
+      : resolve_callback_(std::move(resolve_executor)),
+        reject_callback_(std::move(reject_executor)) {
+    DCHECK(!resolve_callback_.is_null() || !reject_callback_.is_null());
+  }
 
-  ~ThenAndCatchExecutorCommon();
+  ~ThenAndCatchExecutorCommon() = default;
 
-  // AbstractPromise::Executor:
+  // PromiseExecutor:
   bool IsCancelled() const;
-  AbstractPromise::Executor::PrerequisitePolicy GetPrerequisitePolicy() const;
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const;
 
   using ExecuteCallback = void (*)(AbstractPromise* prerequisite,
                                    AbstractPromise* promise,
@@ -47,28 +51,6 @@
 // Tag signals no callback which is used to eliminate dead code.
 struct NoCallback {};
 
-struct CouldResolveOrReject {};
-struct CanOnlyResolve {};
-struct CanOnlyReject {};
-
-template <bool can_resolve, bool can_reject>
-struct CheckResultHelper;
-
-template <>
-struct CheckResultHelper<true, false> {
-  using TagType = CanOnlyResolve;
-};
-
-template <>
-struct CheckResultHelper<true, true> {
-  using TagType = CouldResolveOrReject;
-};
-
-template <>
-struct CheckResultHelper<false, true> {
-  using TagType = CanOnlyReject;
-};
-
 template <typename ResolveOnceCallback,
           typename RejectOnceCallback,
           typename ArgResolve,
@@ -86,20 +68,13 @@
   using PrerequisiteCouldReject =
       std::integral_constant<bool, !std::is_same<ArgReject, NoCallback>::value>;
 
-  ThenAndCatchExecutor(ResolveOnceCallback&& resolve_callback,
-                       RejectOnceCallback&& reject_callback)
-      : common_(std::move(resolve_callback), std::move(reject_callback)) {
-    static_assert(sizeof(CallbackBase) == sizeof(ResolveOnceCallback),
-                  "We assume it's possible to cast from CallbackBase to "
-                  "ResolveOnceCallback");
-    static_assert(sizeof(CallbackBase) == sizeof(RejectOnceCallback),
-                  "We assume it's possible to cast from CallbackBase to "
-                  "RejectOnceCallback");
-  }
+  ThenAndCatchExecutor(CallbackBase&& resolve_callback,
+                       CallbackBase&& reject_callback) noexcept
+      : common_(std::move(resolve_callback), std::move(reject_callback)) {}
 
   bool IsCancelled() const { return common_.IsCancelled(); }
 
-  AbstractPromise::Executor::PrerequisitePolicy GetPrerequisitePolicy() const {
+  PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const {
     return common_.GetPrerequisitePolicy();
   }
 
@@ -110,17 +85,15 @@
   }
 
 #if DCHECK_IS_ON()
-  AbstractPromise::Executor::ArgumentPassingType ResolveArgumentPassingType()
-      const {
+  PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const {
     return common_.resolve_callback_.is_null()
-               ? AbstractPromise::Executor::ArgumentPassingType::kNoCallback
+               ? PromiseExecutor::ArgumentPassingType::kNoCallback
                : CallbackTraits<ResolveOnceCallback>::argument_passing_type;
   }
 
-  AbstractPromise::Executor::ArgumentPassingType RejectArgumentPassingType()
-      const {
+  PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const {
     return common_.reject_callback_.is_null()
-               ? AbstractPromise::Executor::ArgumentPassingType::kNoCallback
+               ? PromiseExecutor::ArgumentPassingType::kNoCallback
                : CallbackTraits<RejectOnceCallback>::argument_passing_type;
   }
 
@@ -164,9 +137,8 @@
               RejectStorage>::Run(std::move(*resolve_callback), prerequisite,
                                   promise);
 
-    using CheckResultTagType = typename CheckResultHelper<
-        PromiseCallbackTraits<ResolveReturnT>::could_resolve,
-        PromiseCallbackTraits<ResolveReturnT>::could_reject>::TagType;
+    using CheckResultTagType =
+        typename PromiseCallbackTraits<ResolveReturnT>::TagType;
 
     CheckResultType(promise, CheckResultTagType());
   }
@@ -188,9 +160,8 @@
               RejectStorage>::Run(std::move(*reject_callback), prerequisite,
                                   promise);
 
-    using CheckResultTagType = typename CheckResultHelper<
-        PromiseCallbackTraits<RejectReturnT>::could_resolve,
-        PromiseCallbackTraits<RejectReturnT>::could_reject>::TagType;
+    using CheckResultTagType =
+        typename PromiseCallbackTraits<RejectReturnT>::TagType;
 
     CheckResultType(promise, CheckResultTagType());
   }
diff --git a/base/task_runner.cc b/base/task_runner.cc
index d8644a9..e2787f3d 100644
--- a/base/task_runner.cc
+++ b/base/task_runner.cc
@@ -10,6 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/task/promise/abstract_promise.h"
+#include "base/task/promise/helpers.h"
 #include "base/threading/post_task_and_reply_impl.h"
 
 namespace base {
@@ -40,40 +41,8 @@
   return destination_->PostTask(from_here, std::move(task));
 }
 
-// TODO(alexclarke): Remove this when TaskRunner::PostPromiseInternal becomes
-// pure virtual.
-class PromiseHolder {
- public:
-  explicit PromiseHolder(scoped_refptr<internal::AbstractPromise> promise)
-      : promise_(std::move(promise)) {}
-
-  ~PromiseHolder() {
-    // Detect if the promise was not executed and if so cancel to ensure memory
-    // is released.
-    if (promise_)
-      promise_->OnCanceled();
-  }
-
-  PromiseHolder(PromiseHolder&& other) : promise_(std::move(other.promise_)) {}
-
-  scoped_refptr<internal::AbstractPromise> Unwrap() const {
-    return std::move(promise_);
-  }
-
- private:
-  mutable scoped_refptr<internal::AbstractPromise> promise_;
-};
-
 }  // namespace
 
-template <>
-struct BindUnwrapTraits<PromiseHolder> {
-  static scoped_refptr<internal::AbstractPromise> Unwrap(
-      const PromiseHolder& o) {
-    return o.Unwrap();
-  }
-};
-
 bool TaskRunner::PostTask(const Location& from_here, OnceClosure task) {
   return PostDelayedTask(from_here, std::move(task), base::TimeDelta());
 }
@@ -88,10 +57,10 @@
 bool TaskRunner::PostPromiseInternal(
     const scoped_refptr<internal::AbstractPromise>& promise,
     base::TimeDelta delay) {
-  return PostDelayedTask(
-      promise->from_here(),
-      BindOnce(&internal::AbstractPromise::Execute, PromiseHolder(promise)),
-      delay);
+  return PostDelayedTask(promise->from_here(),
+                         BindOnce(&internal::AbstractPromise::Execute,
+                                  internal::PromiseHolder(promise)),
+                         delay);
 }
 
 TaskRunner::TaskRunner() = default;
diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc
index eefb923..ce52ad64 100644
--- a/base/win/windows_version.cc
+++ b/base/win/windows_version.cc
@@ -23,9 +23,8 @@
 #error VS 2017 Update 3.2 or higher is required
 #endif
 
-#if !defined(NTDDI_WIN10_RS5)
-// Windows 10 October 2018 SDK is required to build Chrome.
-#error October 2018 SDK (10.0.17763.0) or higher required.
+#if !defined(NTDDI_WIN10_19H1)
+#error Windows 10.0.18362.0 SDK or higher required.
 #endif
 
 namespace {
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index eea5766..96d0bd7 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2529,6 +2529,11 @@
           outputs = [
             _final_pathmap_path,
           ]
+
+          # The monochrome_apk_checker test needs pathmap when run on swarming.
+          data = [
+            _final_pathmap_path,
+          ]
         }
         _final_deps += [ ":$_copy_pathmap_target" ]
       }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a238ecd..c6c0e415 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8908803685201779008
\ No newline at end of file
+8908519668143306512
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index b072669..6655c00 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8908803680395747664
\ No newline at end of file
+8908521448671558320
\ No newline at end of file
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index 6db4fc0..eaa7ae5 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1274,6 +1274,7 @@
     return;
   offset_to_transform_parent_ = offset;
   SetNeedsPushProperties();
+  SetSubtreePropertyChanged();
 }
 
 void Layer::InvalidatePropertyTreesIndices() {
diff --git a/cc/layers/layer.h b/cc/layers/layer.h
index 8eee1adb..0001ccf9 100644
--- a/cc/layers/layer.h
+++ b/cc/layers/layer.h
@@ -778,6 +778,9 @@
   // layer's subtree, including itself. This causes the layer's subtree to be
   // considered damaged and re-displayed to the user.
   void SetSubtreePropertyChanged();
+  void ClearSubtreePropertyChangedForTesting() {
+    subtree_property_changed_ = false;
+  }
   bool subtree_property_changed() const { return subtree_property_changed_; }
 
   // Internal to property tree construction. Returns ElementListType::ACTIVE
diff --git a/chrome/VERSION b/chrome/VERSION
index d49a780..2164731 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=77
 MINOR=0
-BUILD=3845
+BUILD=3848
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java
index c591e56..95e235f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandler.java
@@ -10,7 +10,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.net.Uri;
 import android.support.v4.app.NotificationCompat;
+import android.text.TextUtils;
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
@@ -32,12 +34,21 @@
     private static final String EXTRA_PHONE_NUMBER = "ClickToCallMessageHandler.EXTRA_PHONE_NUMBER";
 
     /**
-     * Handles the tapping of a notification by firing the call intent.
+     * Handles the tapping of a notification by opening the dialer with the
+     * phone number specified in the notification.
      */
     public static final class TapReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // TODO(mvanouwerkerk): fire the call intent.
+            String phoneNumber = intent.getStringExtra(EXTRA_PHONE_NUMBER);
+            final Intent dialIntent;
+            if (!TextUtils.isEmpty(phoneNumber)) {
+                dialIntent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber));
+            } else {
+                dialIntent = new Intent(Intent.ACTION_DIAL);
+            }
+            dialIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            ContextUtils.getApplicationContext().startActivity(dialIntent);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
index 9aa9a836..c2a67b6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManager.java
@@ -26,12 +26,10 @@
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.base.task.PostTask;
 import org.chromium.components.signin.AccountIdProvider;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountTrackerService;
 import org.chromium.components.signin.ChromeSigninController;
-import org.chromium.content_public.browser.UiThreadTaskTraits;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -334,7 +332,8 @@
     }
 
     private void notifySignInAllowedChanged() {
-        PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+        // TODO(crbug.com/981470) Use PostTask and CurrentThread traits.
+        ThreadUtils.postOnUiThread(() -> {
             for (SignInAllowedObserver observer : mSignInAllowedObservers) {
                 observer.onSignInAllowedChanged();
             }
@@ -518,13 +517,15 @@
             mCallbacksWaitingForPendingOperation.add(runnable);
             return;
         }
-        PostTask.postTask(UiThreadTaskTraits.DEFAULT, runnable);
+        // TODO(crbug.com/981470) Use PostTask and CurrentThread traits
+        ThreadUtils.postOnUiThread(runnable);
     }
 
     private void notifyCallbacksWaitingForOperation() {
         ThreadUtils.assertOnUiThread();
         for (Runnable callback : mCallbacksWaitingForPendingOperation) {
-            PostTask.postTask(UiThreadTaskTraits.DEFAULT, callback);
+            // TODO(crbug.com/981470) Use PostTask and CurrentThread traits.
+            ThreadUtils.postOnUiThread(callback);
         }
         mCallbacksWaitingForPendingOperation.clear();
     }
@@ -658,7 +659,8 @@
         assert mSignOutState != null;
 
         if (mSignOutState.mCallback != null) {
-            PostTask.postTask(UiThreadTaskTraits.DEFAULT, mSignOutState.mCallback);
+            // TODO(crbug.com/981470) Use PostTask and CurrentThread traits.
+            ThreadUtils.postOnUiThread(mSignOutState.mCallback);
         }
         mSignOutState = null;
         notifyCallbacksWaitingForOperation();
diff --git a/chrome/android/monochrome/scripts/monochrome_apk_checker.py b/chrome/android/monochrome/scripts/monochrome_apk_checker.py
index 42d0151..b74fcec 100755
--- a/chrome/android/monochrome/scripts/monochrome_apk_checker.py
+++ b/chrome/android/monochrome/scripts/monochrome_apk_checker.py
@@ -132,16 +132,19 @@
   apk_entries = []
   with closing(StringIO.StringIO(content)) as f:
     for line in f:
-       match = ZIP_ENTRY.match(line)
-       if match:
-         apk_entries.append(APKEntry(match.group('name'),
-                                     match.group('crc'),
-                                     match.group('cmpr') == 0))
+      match = ZIP_ENTRY.match(line)
+      if match:
+        apk_entries.append(
+            APKEntry(
+                match.group('name'), match.group('crc'),
+                match.group('cmpr') == 0))
   return apk_entries
 
 def VerifySameFile(monochrome_dict, apk, changes):
-  """ Verify files from apk are same as those in monochrome except files
-      in changes
+  """Verify apk file content matches same files in monochrome.
+
+  Verify files from apk are same as those in monochrome except files
+  in changes.
   """
   diff = []
   for a in apk:
@@ -155,8 +158,10 @@
 
 
 def VerifyUncompressed(monochrome, apk):
-  """ Verify files not being compressed in apk are also uncompressed in
-      Monochrome APK
+  """Verify uncompressed files in apk are a subset of those in monochrome.
+
+  Verify files not being compressed in apk are also uncompressed in
+  Monochrome APK.
   """
   uncompressed = [i.filename for i in apk if i.uncompressed ]
   monochrome_uncompressed = [i.filename for i in monochrome if i.uncompressed]
@@ -166,8 +171,8 @@
                     '\n'.join(compressed))
 
 def SuperSetOf(monochrome, apk):
-  """ Verify Monochrome is super set of apk.
-  """
+  """Verify Monochrome is super set of apk."""
+
   def exists_in_some_form(f):
     if f in monochrome:
       return True
@@ -183,7 +188,7 @@
 
   missing_files = [f for f in apk if not exists_in_some_form(f)]
   if len(missing_files):
-    raise Exception("The following files are missing in Monochrome:\n %s" %
+    raise Exception('The following files are missing in Monochrome:\n %s' %
                     '\n'.join(missing_files))
 
 
@@ -192,6 +197,30 @@
           if not specific.search(i.filename) ]
 
 
+def LoadPathmap(pathmap_path):
+  """Load the pathmap of obfuscated resource paths.
+
+  Returns: A dict mapping from obfuscated paths to original paths or an
+           empty dict if passed a None |pathmap_path|.
+  """
+  if pathmap_path is None:
+    return {}
+
+  pathmap = {}
+  with open(pathmap_path, 'r') as f:
+    for line in f:
+      line = line.strip()
+      if line.startswith('#') or line == '':
+        continue
+      original, renamed = line.split(' -> ')
+      pathmap[renamed] = original
+  return pathmap
+
+
+def DeobfuscateFilename(obfuscated_filename, pathmap):
+  return pathmap.get(obfuscated_filename, obfuscated_filename)
+
+
 def ParseArgs(args):
   """Parses command line options.
 
@@ -200,43 +229,56 @@
   """
   parser = argparse.ArgumentParser(prog='monochrome_apk_checker')
 
-  parser.add_argument('--monochrome-apk',
-                      required=True,
-                      help='The monochrome APK path')
+  parser.add_argument(
+      '--monochrome-apk', required=True, help='The monochrome APK path.')
+  parser.add_argument(
+      '--monochrome-pathmap', help='The monochrome APK resources pathmap path.')
   parser.add_argument('--chrome-apk',
                       required=True,
                       help='The chrome APK path.')
+  parser.add_argument(
+      '--chrome-pathmap', help='The chrome APK resources pathmap path.')
   parser.add_argument('--system-webview-apk',
                       required=True,
                       help='The system webview APK path.')
+  parser.add_argument(
+      '--system-webview-pathmap',
+      help='The system webview APK resources pathmap path.')
   return parser.parse_args(args)
 
 
 def main():
   options = ParseArgs(sys.argv[1:])
   monochrome = DumpAPK(options.monochrome_apk)
-  monochrome_files = [f.filename for f in monochrome]
-  monochrome_dict = dict([(i.filename, i) for i in monochrome])
+  monochrome_pathmap = LoadPathmap(options.monochrome_pathmap)
+  monochrome_files = [
+      DeobfuscateFilename(f.filename, monochrome_pathmap) for f in monochrome
+  ]
+  monochrome_dict = dict([(DeobfuscateFilename(i.filename, monochrome_pathmap),
+                           i) for i in monochrome])
 
   chrome = RemoveSpecific(DumpAPK(options.chrome_apk),
                           CHROME_SPECIFIC)
   if len(chrome) == 0:
-    raise Exception("Chrome should have common files with Monochrome")
+    raise Exception('Chrome should have common files with Monochrome')
 
   webview = RemoveSpecific(DumpAPK(options.system_webview_apk),
                            WEBVIEW_SPECIFIC)
   if len(webview) == 0:
-    raise Exception("WebView should have common files with Monochrome")
+    raise Exception('WebView should have common files with Monochrome')
 
-  def check_apk(apk):
-    apk_files = [f.filename for f in apk]
+  def check_apk(apk, pathmap):
+    apk_files = [DeobfuscateFilename(f.filename, pathmap) for f in apk]
     SuperSetOf(monochrome_files, apk_files)
     VerifyUncompressed(monochrome, apk)
     VerifySameFile(monochrome_dict, chrome, CHROME_CHANGES)
     VerifySameFile(monochrome_dict, webview, WEBVIEW_CHANGES)
 
-  check_apk(chrome)
-  check_apk(webview)
+  chrome_pathmap = LoadPathmap(options.chrome_pathmap)
+  check_apk(chrome, chrome_pathmap)
+
+  webview_pathmap = LoadPathmap(options.system_webview_pathmap)
+  check_apk(webview, webview_pathmap)
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index d8c8fa6..0276344ac 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-77.0.3844.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-77.0.3846.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/app_management_strings.grdp b/chrome/app/app_management_strings.grdp
index c352a3b..3de0d886 100644
--- a/chrome/app/app_management_strings.grdp
+++ b/chrome/app/app_management_strings.grdp
@@ -49,8 +49,8 @@
   <message name="IDS_APP_MANAGEMENT_PERMISSIONS" desc="Label for permissions section in the app settings page.">
     Permissions
   </message>
-  <message name="IDS_APP_MANAGEMENT_MORE_PERMISSIONS" desc="Label for a link to more permissions for an app.">
-    More permissions
+  <message name="IDS_APP_MANAGEMENT_MORE_SETTINGS" desc="Label for a link to more settings and permissions for an app.">
+    More settings and permissions
   </message>
   <message name="IDS_APP_MANAGEMENT_THIS_APP_CAN" desc="Label for permissions that a Chrome app can use.">
     This app can:
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index bdacbcf..62122c3 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3301,6 +3301,7 @@
       "serial/serial_chooser_context.h",
       "serial/serial_chooser_context_factory.cc",
       "serial/serial_chooser_context_factory.h",
+      "sharing/click_to_call/click_to_call_constants.h",
       "sharing/click_to_call/click_to_call_sharing_dialog_controller.cc",
       "sharing/click_to_call/click_to_call_sharing_dialog_controller.h",
       "sharing/sharing_dialog_controller.cc",
diff --git a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc
index 6c0b8da3..53dbe99a 100644
--- a/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc
+++ b/chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/chromeos/android_sms/android_sms_app_setup_controller_impl.h"
 
 #include <memory>
+#include <string>
 #include <tuple>
 #include <utility>
 #include <vector>
@@ -197,8 +198,9 @@
     auto test_pwa_delegate =
         std::make_unique<TestPwaDelegate>(fake_cookie_manager_.get());
     test_pwa_delegate_ = test_pwa_delegate.get();
+
     test_pending_app_manager_ =
-        std::make_unique<web_app::TestPendingAppManager>();
+        std::make_unique<web_app::TestPendingAppManager>(&test_app_registrar_);
     setup_controller_ = base::WrapUnique(new AndroidSmsAppSetupControllerImpl(
         &profile_, test_pending_app_manager_.get(),
         host_content_settings_map_));
@@ -430,6 +432,7 @@
   TestingProfile profile_;
   HostContentSettingsMap* host_content_settings_map_;
   std::unique_ptr<FakeCookieManager> fake_cookie_manager_;
+  web_app::TestAppRegistrar test_app_registrar_;
   std::unique_ptr<web_app::TestPendingAppManager> test_pending_app_manager_;
   TestPwaDelegate* test_pwa_delegate_;
   std::unique_ptr<AndroidSmsAppSetupController> setup_controller_;
diff --git a/chrome/browser/chromeos/extensions/default_web_app_ids.h b/chrome/browser/chromeos/extensions/default_web_app_ids.h
index 196ba26..d403c77 100644
--- a/chrome/browser/chromeos/extensions/default_web_app_ids.h
+++ b/chrome/browser/chromeos/extensions/default_web_app_ids.h
@@ -16,8 +16,8 @@
 constexpr char kShowtimeAppId[] = "eoccpgmpiempcflglfokeengliildkag";
 
 // Generated as
-// web_app::GenerateAppIdFromURL(GURL("https://canvas.apps.chrome/index.html)).
-constexpr char kCanvasAppId[] = "memejfanofdmelnjmboefinndljpifdm";
+// web_app::GenerateAppIdFromURL(GURL("https://canvas.apps.chrome/")).
+constexpr char kCanvasAppId[] = "ieailfmhaghpphfffooibmlghaeopach";
 
 }  // namespace default_web_apps
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 371bd94..0967c77 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -531,10 +531,8 @@
         TestCase("openQuickView").TabletMode(),
         TestCase("openQuickViewAudio"),
         TestCase("openQuickViewImage"),
-// QuickView image EXIF test fails on MSAN, crbug.com/973397
-#if !defined(MEMORY_SANITIZER)
         TestCase("openQuickViewImageExif"),
-#endif
+        TestCase("openQuickViewImageRaw"),
         TestCase("openQuickViewVideo"),
 // QuickView PDF test fails on MSAN, crbug.com/768070
 #if !defined(MEMORY_SANITIZER)
diff --git a/chrome/browser/chromeos/policy/upload_job_unittest.cc b/chrome/browser/chromeos/policy/upload_job_unittest.cc
index 61f2b3c9..b644fcd 100644
--- a/chrome/browser/chromeos/policy/upload_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/upload_job_unittest.cc
@@ -66,8 +66,7 @@
 class FakeOAuth2AccessTokenManagerWithCaching
     : public FakeOAuth2AccessTokenManager {
  public:
-  FakeOAuth2AccessTokenManagerWithCaching(
-      OAuth2TokenService* token_service,
+  explicit FakeOAuth2AccessTokenManagerWithCaching(
       OAuth2AccessTokenManager::Delegate* delegate);
   ~FakeOAuth2AccessTokenManagerWithCaching() override;
 
@@ -99,9 +98,8 @@
 
 FakeOAuth2AccessTokenManagerWithCaching::
     FakeOAuth2AccessTokenManagerWithCaching(
-        OAuth2TokenService* token_service,
         OAuth2AccessTokenManager::Delegate* delegate)
-    : FakeOAuth2AccessTokenManager(token_service, delegate) {}
+    : FakeOAuth2AccessTokenManager(delegate) {}
 
 FakeOAuth2AccessTokenManagerWithCaching::
     ~FakeOAuth2AccessTokenManagerWithCaching() = default;
@@ -167,7 +165,6 @@
             content::TestBrowserThreadBundle::IO_MAINLOOP) {
     oauth2_service_.OverrideAccessTokenManagerForTesting(
         std::make_unique<FakeOAuth2AccessTokenManagerWithCaching>(
-            &oauth2_service_ /* OAuth2TokenService* */,
             &oauth2_service_ /* OAuth2AccessTokenManager::Delegate* */));
     access_token_manager_ =
         static_cast<FakeOAuth2AccessTokenManagerWithCaching*>(
diff --git a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
index 1c321a1..6a37baa 100644
--- a/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
+++ b/chrome/browser/chromeos/settings/device_oauth2_token_service.cc
@@ -149,7 +149,7 @@
       continue;
 
     if (token_is_valid) {
-      OAuth2TokenService::FetchOAuth2Token(
+      GetAccessTokenManager()->FetchOAuth2Token(
           scoped_request->request.get(),
           scoped_request->request->GetAccountId(),
           GetDeviceDelegate()->GetURLLoaderFactory(), scoped_request->client_id,
diff --git a/chrome/browser/resources/app_management/arc_permission_view.html b/chrome/browser/resources/app_management/arc_permission_view.html
index 5df2176..a760a4ea 100644
--- a/chrome/browser/resources/app_management/arc_permission_view.html
+++ b/chrome/browser/resources/app_management/arc_permission_view.html
@@ -51,7 +51,7 @@
           </app-management-permission-item>
           <div class="subpermission-row separated-row clickable"
             on-click="onClickNativeSettingsButton_">
-            <div class="header-text">$i18n{morePermissions}</div>
+            <div class="header-text">$i18n{moreSettings}</div>
             <div class="permission-row-controls">
               <cr-icon-button class="native-settings-icon icon-external"
                 tabindex="0">
diff --git a/chrome/browser/resources/app_management/browser_proxy.js b/chrome/browser/resources/app_management/browser_proxy.js
index a82975b..a5f06fe 100644
--- a/chrome/browser/resources/app_management/browser_proxy.js
+++ b/chrome/browser/resources/app_management/browser_proxy.js
@@ -60,6 +60,8 @@
               {
                 title: 'Chrome App',
                 type: AppType.kExtension,
+                description:
+                    'A Chrome App installed from the Chrome Web Store.',
               },
               ),
           app_management.FakePageHandler.createApp(
@@ -74,6 +76,7 @@
               {
                 title: 'Chrome App, OEM installed',
                 type: AppType.kExtension,
+                description: 'A Chrome App installed by an OEM.',
                 installSource: InstallSource.kOem,
               },
               ),
diff --git a/chrome/browser/resources/app_management/chrome_app_permission_view.html b/chrome/browser/resources/app_management/chrome_app_permission_view.html
index 32124a0..322bf4beb 100644
--- a/chrome/browser/resources/app_management/chrome_app_permission_view.html
+++ b/chrome/browser/resources/app_management/chrome_app_permission_view.html
@@ -64,12 +64,9 @@
             based on the app. -->
           <div id="more-settings"
             class="subpermission-row separated-row clickable"
-            on-click="onClickExtensionsSettingsButton_">
-          <div id="more-settings"
-            class="subpermission-row separated-row clickable"
             on-click="onClickExtensionsSettingsButton_"
             hidden$="[[app_.hideMoreSettings]]">
-            <div class="header-text">More settings</div>
+            <div class="header-text">$i18n{moreSettings}</div>
             <cr-icon-button class="native-settings-icon icon-external"
               tabindex="0">
             </cr-icon-button>
diff --git a/chrome/browser/resources/app_management/pwa_permission_view.html b/chrome/browser/resources/app_management/pwa_permission_view.html
index 2d73d954..adbdc55 100644
--- a/chrome/browser/resources/app_management/pwa_permission_view.html
+++ b/chrome/browser/resources/app_management/pwa_permission_view.html
@@ -44,7 +44,7 @@
           </app-management-permission-item>
           <div class="subpermission-row separated-row clickable"
             on-click="onClickSiteSettingsButton_">
-            <div class="header-text">$i18n{morePermissions}</div>
+            <div class="header-text">$i18n{moreSettings}</div>
             <div class="permission-row-controls">
               <cr-icon-button class="native-settings-icon icon-external"
                 tabindex="0">
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_constants.h b/chrome/browser/sharing/click_to_call/click_to_call_constants.h
new file mode 100644
index 0000000..e1304af
--- /dev/null
+++ b/chrome/browser/sharing/click_to_call/click_to_call_constants.h
@@ -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.
+
+#ifndef CHROME_BROWSER_SHARING_CLICK_TO_CALL_CLICK_TO_CALL_CONSTANTS_H_
+#define CHROME_BROWSER_SHARING_CLICK_TO_CALL_CLICK_TO_CALL_CONSTANTS_H_
+
+#include "base/time/time.h"
+
+// Time limit for click to call message expiration.
+// TODO(yasmo): Confirm this value with the team.
+constexpr base::TimeDelta kSharingClickToCallMessageTTL =
+    base::TimeDelta::FromMinutes(10);
+
+#endif  // CHROME_BROWSER_SHARING_CLICK_TO_CALL_CLICK_TO_CALL_CONSTANTS_H_
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
index fcd2ffb..3b6c9b8c 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.cc
@@ -6,6 +6,7 @@
 
 #include "base/strings/strcat.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "chrome/browser/sharing/click_to_call/click_to_call_constants.h"
 #include "chrome/browser/sharing/sharing_device_info.h"
 #include "chrome/browser/sharing/sharing_service.h"
 #include "chrome/browser/shell_integration.h"
@@ -17,9 +18,6 @@
 using SharingMessage = chrome_browser_sharing::SharingMessage;
 using App = ClickToCallSharingDialogController::App;
 
-constexpr base::TimeDelta
-    ClickToCallSharingDialogController::kMessageExpiration;
-
 ClickToCallSharingDialogController::ClickToCallSharingDialogController(
     content::WebContents* web_contents,
     SharingService* sharing_service,
@@ -62,8 +60,8 @@
   sharing_message.mutable_click_to_call_message()->set_phone_number(
       phone_number_);
   sharing_service_->SendMessageToDevice(
-      device.guid(), ClickToCallSharingDialogController::kMessageExpiration,
-      std::move(sharing_message), std::move(callback));
+      device.guid(), kSharingClickToCallMessageTTL, std::move(sharing_message),
+      std::move(callback));
 }
 
 void ClickToCallSharingDialogController::OnAppChosen(App app) {
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
index 9bfe1f0..7882ccb 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller.h
@@ -21,10 +21,6 @@
 
 class ClickToCallSharingDialogController : public SharingDialogController {
  public:
-  // TODO(yasmo): Confirm this value with the team.
-  static constexpr base::TimeDelta kMessageExpiration =
-      base::TimeDelta::FromMinutes(10);
-
   ClickToCallSharingDialogController(content::WebContents* web_contents,
                                      SharingService* sharing_service,
                                      std::string phone_number);
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc
index 40191d22..b084191 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_sharing_dialog_controller_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/guid.h"
 #include "base/memory/ptr_util.h"
+#include "chrome/browser/sharing/click_to_call/click_to_call_constants.h"
 #include "chrome/browser/sharing/fake_local_device_info_provider.h"
 #include "chrome/browser/sharing/sharing_device_info.h"
 #include "chrome/browser/sharing/sharing_fcm_handler.h"
@@ -86,11 +87,10 @@
   sharing_message.mutable_click_to_call_message()->set_phone_number(
       kPhoneNumber);
   SharingService::SendMessageCallback callback;
-  EXPECT_CALL(mock_sharing_service_,
-              SendMessageToDevice(
-                  Eq(kReceiverGuid),
-                  Eq(ClickToCallSharingDialogController::kMessageExpiration),
-                  ProtoEquals(sharing_message), _));
+  EXPECT_CALL(
+      mock_sharing_service_,
+      SendMessageToDevice(Eq(kReceiverGuid), Eq(kSharingClickToCallMessageTTL),
+                          ProtoEquals(sharing_message), _));
   click_to_call_sharing_dialog_controller_.OnDeviceChosen(sharing_device_info,
                                                           std::move(callback));
 }
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc
index 073420fd..a537aba3 100644
--- a/chrome/browser/signin/identity_manager_factory.cc
+++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -21,6 +21,7 @@
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_manager_builder.h"
+#include "content/public/browser/network_service_instance.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/web_data_service_factory.h"
@@ -102,10 +103,13 @@
   params.account_tracker_service = BuildAccountTrackerService(profile);
   params.image_decoder = std::make_unique<ImageDecoderImpl>();
   params.local_state = g_browser_process->local_state();
+  params.network_connection_tracker = content::GetNetworkConnectionTracker();
   params.pref_service = profile->GetPrefs();
   params.signin_client = ChromeSigninClientFactory::GetForProfile(profile);
   params.token_service = ProfileOAuth2TokenServiceBuilder::BuildInstanceFor(
-      context, params.account_tracker_service.get());
+      context, params.pref_service, params.account_tracker_service.get(),
+      params.network_connection_tracker, params.account_consistency,
+      params.signin_client);
   std::unique_ptr<identity::IdentityManager> identity_manager =
       identity::BuildIdentityManager(&params);
 
diff --git a/chrome/browser/signin/profile_oauth2_token_service_builder.cc b/chrome/browser/signin/profile_oauth2_token_service_builder.cc
index 86dc82e..6403f5b0 100644
--- a/chrome/browser/signin/profile_oauth2_token_service_builder.cc
+++ b/chrome/browser/signin/profile_oauth2_token_service_builder.cc
@@ -12,16 +12,15 @@
 #include "base/callback.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/account_consistency_mode_manager.h"
+#include "components/signin/core/browser/account_consistency_method.h"
 #include "components/signin/core/browser/device_id_helper.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "content/public/browser/network_service_instance.h"
+#include "components/signin/core/browser/signin_client.h"
 
 #if defined(OS_ANDROID)
 #include "components/signin/core/browser/oauth2_token_service_delegate_android.h"
 #else
 #include "chrome/browser/content_settings/cookie_settings_factory.h"
-#include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/signin/core/browser/cookie_settings_util.h"
@@ -45,8 +44,10 @@
 
 #if defined(OS_CHROMEOS)
 std::unique_ptr<signin::ProfileOAuth2TokenServiceDelegateChromeOS>
-CreateCrOsOAuthDelegate(Profile* profile,
-                        AccountTrackerService* account_tracker_service) {
+CreateCrOsOAuthDelegate(
+    Profile* profile,
+    AccountTrackerService* account_tracker_service,
+    network::NetworkConnectionTracker* network_connection_tracker) {
   chromeos::AccountManagerFactory* factory =
       g_browser_process->platform_part()->GetAccountManagerFactory();
   DCHECK(factory);
@@ -58,8 +59,8 @@
       !chromeos::ProfileHelper::IsSigninProfile(profile) &&
       !chromeos::ProfileHelper::IsLockScreenAppProfile(profile);
   return std::make_unique<signin::ProfileOAuth2TokenServiceDelegateChromeOS>(
-      account_tracker_service, content::GetNetworkConnectionTracker(),
-      account_manager, is_regular_profile);
+      account_tracker_service, network_connection_tracker, account_manager,
+      is_regular_profile);
 }
 #endif  // defined(OS_CHROMEOS)
 
@@ -85,9 +86,10 @@
 std::unique_ptr<MutableProfileOAuth2TokenServiceDelegate>
 CreateMutableProfileOAuthDelegate(
     Profile* profile,
-    AccountTrackerService* account_tracker_service) {
-  signin::AccountConsistencyMethod account_consistency =
-      AccountConsistencyModeManager::GetMethodForProfile(profile);
+    AccountTrackerService* account_tracker_service,
+    signin::AccountConsistencyMethod account_consistency,
+    SigninClient* signin_client,
+    network::NetworkConnectionTracker* network_connection_tracker) {
   // When signin cookies are cleared on exit and Dice is enabled, all tokens
   // should also be cleared.
   bool revoke_all_tokens_on_load =
@@ -96,8 +98,7 @@
           CookieSettingsFactory::GetForProfile(profile).get());
 
   return std::make_unique<MutableProfileOAuth2TokenServiceDelegate>(
-      ChromeSigninClientFactory::GetInstance()->GetForProfile(profile),
-      account_tracker_service, content::GetNetworkConnectionTracker(),
+      signin_client, account_tracker_service, network_connection_tracker,
       WebDataServiceFactory::GetTokenWebDataForProfile(
           profile, ServiceAccessType::EXPLICIT_ACCESS),
       account_consistency, revoke_all_tokens_on_load,
@@ -113,7 +114,10 @@
 
 std::unique_ptr<OAuth2TokenServiceDelegate> CreateOAuth2TokenServiceDelegate(
     Profile* profile,
-    AccountTrackerService* account_tracker_service) {
+    AccountTrackerService* account_tracker_service,
+    signin::AccountConsistencyMethod account_consistency,
+    SigninClient* signin_client,
+    network::NetworkConnectionTracker* network_connection_tracker) {
 #if defined(OS_ANDROID)
   return std::make_unique<OAuth2TokenServiceDelegateAndroid>(
       account_tracker_service);
@@ -122,7 +126,8 @@
 
 #if defined(OS_CHROMEOS)
   if (chromeos::switches::IsAccountManagerEnabled()) {
-    return CreateCrOsOAuthDelegate(profile, account_tracker_service);
+    return CreateCrOsOAuthDelegate(profile, account_tracker_service,
+                                   network_connection_tracker);
   }
 #endif  // defined(OS_CHROMEOS)
 
@@ -130,7 +135,9 @@
   // 1. On all platforms other than Android and Chrome OS.
   // 2. On Chrome OS, if Account Manager has not been switched on yet
   // (chromeos::switches::IsAccountManagerEnabled).
-  return CreateMutableProfileOAuthDelegate(profile, account_tracker_service);
+  return CreateMutableProfileOAuthDelegate(profile, account_tracker_service,
+                                           account_consistency, signin_client,
+                                           network_connection_tracker);
 
 #endif  // defined(OS_ANDROID)
 }
@@ -141,7 +148,11 @@
 std::unique_ptr<ProfileOAuth2TokenService>
 ProfileOAuth2TokenServiceBuilder::BuildInstanceFor(
     content::BrowserContext* context,
-    AccountTrackerService* account_tracker_service) {
+    PrefService* pref_service,
+    AccountTrackerService* account_tracker_service,
+    network::NetworkConnectionTracker* network_connection_tracker,
+    signin::AccountConsistencyMethod account_consistency,
+    SigninClient* signin_client) {
   Profile* profile = static_cast<Profile*>(context);
 
 // On ChromeOS the device ID is not managed by the token service.
@@ -149,11 +160,12 @@
   // Ensure the device ID is not empty. This is important for Dice, because the
   // device ID is needed on the network thread, but can only be generated on the
   // main thread.
-  std::string device_id = signin::GetSigninScopedDeviceId(profile->GetPrefs());
+  std::string device_id = signin::GetSigninScopedDeviceId(pref_service);
   DCHECK(!device_id.empty());
 #endif
 
   return std::make_unique<ProfileOAuth2TokenService>(
-      profile->GetPrefs(),
-      CreateOAuth2TokenServiceDelegate(profile, account_tracker_service));
+      pref_service, CreateOAuth2TokenServiceDelegate(
+                        profile, account_tracker_service, account_consistency,
+                        signin_client, network_connection_tracker));
 }
diff --git a/chrome/browser/signin/profile_oauth2_token_service_builder.h b/chrome/browser/signin/profile_oauth2_token_service_builder.h
index e98ed992..43f4815 100644
--- a/chrome/browser/signin/profile_oauth2_token_service_builder.h
+++ b/chrome/browser/signin/profile_oauth2_token_service_builder.h
@@ -7,20 +7,34 @@
 
 #include <memory>
 
+class AccountTrackerService;
+class IdentityManagerFactory;
+class PrefService;
+class ProfileOAuth2TokenService;
+class SigninClient;
+
+namespace signin {
+enum class AccountConsistencyMethod;
+}
+
 namespace content {
 class BrowserContext;
 }
 
-class AccountTrackerService;
-class ProfileOAuth2TokenService;
-class IdentityManagerFactory;
+namespace network {
+class NetworkConnectionTracker;
+}
 
 class ProfileOAuth2TokenServiceBuilder {
  private:
   // Builds a ProfileOAuth2TokenService instance for use by IdentityManager.
   static std::unique_ptr<ProfileOAuth2TokenService> BuildInstanceFor(
       content::BrowserContext* context,
-      AccountTrackerService* account_tracker_service);
+      PrefService* pref_service,
+      AccountTrackerService* account_tracker_service,
+      network::NetworkConnectionTracker* network_connection_tracker,
+      signin::AccountConsistencyMethod account_consistency,
+      SigninClient* signin_client);
 
   friend IdentityManagerFactory;
 };
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index fdb21f9..21d24c0 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -680,16 +680,17 @@
   if (!ready_)
     return;
 
+  base::Optional<std::string> previous_theme_id;
+  if (UsingExtensionTheme())
+    previous_theme_id = GetThemeID();
+
   SwapThemeSupplier(nullptr);
   ClearThemePrefs();
 
-  // There should be no more infobars. This may not be the case because of
-  // http://crbug.com/62154
-  // RemoveUnusedThemes is called on a task because ClearAllThemeData() may
-  // be called as a result of OnExtensionUnloaded().
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&ThemeService::RemoveUnusedThemes,
-                                weak_ptr_factory_.GetWeakPtr(), true));
+  // Disable extension after modifying the prefs so that unloading the extension
+  // doesn't trigger |ClearAllThemeData| again.
+  if (previous_theme_id.has_value())
+    DisableExtension(previous_theme_id.value());
 }
 
 void ThemeService::FixInconsistentPreferencesIfNeeded() {}
diff --git a/chrome/browser/themes/theme_service_unittest.cc b/chrome/browser/themes/theme_service_unittest.cc
index 7e7a889b..aede2c0 100644
--- a/chrome/browser/themes/theme_service_unittest.cc
+++ b/chrome/browser/themes/theme_service_unittest.cc
@@ -206,15 +206,14 @@
                                           ExtensionRegistry::DISABLED));
 
   // 4) Disabling the current theme extension should revert to the default theme
-  // and uninstall any installed theme extensions.
-  theme_service->OnInfobarDestroyed();
+  // and disable any installed theme extensions.
   EXPECT_FALSE(theme_service->UsingDefaultTheme());
   service_->DisableExtension(extension2_id,
                              extensions::disable_reason::DISABLE_USER_ACTION);
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(theme_service->UsingDefaultTheme());
-  EXPECT_FALSE(service_->GetInstalledExtension(extension1_id));
-  EXPECT_FALSE(service_->GetInstalledExtension(extension2_id));
+  EXPECT_FALSE(service_->IsExtensionEnabled(extension1_id));
+  EXPECT_FALSE(service_->IsExtensionEnabled(extension2_id));
 }
 
 // Test the ThemeService's behavior when a theme is upgraded.
@@ -524,4 +523,19 @@
   EXPECT_FALSE(service_->IsExtensionEnabled(extension1_id));
 }
 
+TEST_F(ThemeServiceTest, UseDefaultTheme_DisableExtensionTest) {
+  ThemeService* theme_service =
+      ThemeServiceFactory::GetForProfile(profile_.get());
+  base::ScopedTempDir temp_dir1;
+  ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
+  const std::string& extension1_id =
+      LoadUnpackedMinimalThemeAt(temp_dir1.GetPath());
+  EXPECT_EQ(extension1_id, theme_service->GetThemeID());
+  EXPECT_TRUE(service_->IsExtensionEnabled(extension1_id));
+
+  // Resetting to default theme should disable previous theme.
+  theme_service->UseDefaultTheme();
+  EXPECT_FALSE(service_->IsExtensionEnabled(extension1_id));
+}
+
 }  // namespace theme_service_internal
diff --git a/chrome/browser/ui/app_list/app_launch_event_logger.cc b/chrome/browser/ui/app_list/app_launch_event_logger.cc
index 40a7eba..dfc770894 100644
--- a/chrome/browser/ui/app_list/app_launch_event_logger.cc
+++ b/chrome/browser/ui/app_list/app_launch_event_logger.cc
@@ -42,7 +42,7 @@
 
 namespace {
 
-constexpr unsigned int kNumRandomAppsToLog = 5;
+constexpr unsigned int kNumRandomAppsToLog = 25;
 const char kArcScheme[] = "arc://";
 const char kExtensionSchemeWithDelimiter[] = "chrome-extension://";
 
@@ -489,7 +489,7 @@
                                        kTotalHoursBucketSizeMultiplier))
       .Record(ukm::UkmRecorder::Get());
 
-  // Log click data about the app clicked on and up to five other apps. This
+  // Log click data about the app clicked on and up to 25 other apps. This
   // represents the state of the data immediately before the click.
   const std::vector<std::string> apps_to_log =
       ChooseAppsToLog(app_launch_event.app_id());
diff --git a/chrome/browser/ui/app_list/app_launch_event_logger.h b/chrome/browser/ui/app_list/app_launch_event_logger.h
index a134e31..dfd08f6 100644
--- a/chrome/browser/ui/app_list/app_launch_event_logger.h
+++ b/chrome/browser/ui/app_list/app_launch_event_logger.h
@@ -42,7 +42,7 @@
 // a hash of the package name, and for PWAs and bookmark apps the keys are the
 // urls associated with the PWA/bookmark.
 // At the time of app launch this class logs metrics about the app clicked on
-// and another five apps that were not clicked on, chosen at random.
+// and up to another 25 apps that were not clicked on, chosen at random.
 class AppLaunchEventLogger {
  public:
   AppLaunchEventLogger();
@@ -92,8 +92,8 @@
                             const std::string& app_id,
                             const std::string& arc_package_name,
                             const std::string& pwa_url);
-  // Chooses up to five random apps to log, plus the app clicked on.
-  // If there are fewer than five apps that can be logged on the device, logs
+  // Chooses up to 25 random apps to log, plus the app clicked on.
+  // If there are fewer than 25 apps that can be logged on the device, logs
   // every app once.
   std::vector<std::string> ChooseAppsToLog(const std::string clicked_app_id);
   // Records a UMA histogram of the app type clicked on.
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index 55fcb0a..3498d72 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -1011,6 +1012,17 @@
   }
 #endif  // defined(OS_WIN)
 
+#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_MACOSX)
+  // If opening in Guest mode switch is provided, load the default profile so
+  // that last opened profile would not trigger a user management dialog.
+  if (command_line.HasSwitch(switches::kGuest)) {
+    PrefService* service = g_browser_process->local_state();
+    DCHECK(service);
+    if (service->GetBoolean(prefs::kBrowserGuestModeEnabled))
+      return profiles::GetDefaultProfileDir(user_data_dir);
+  }
+#endif
+
   if (command_line.HasSwitch(switches::kProfileDirectory)) {
     return user_data_dir.Append(
         command_line.GetSwitchValuePath(switches::kProfileDirectory));
diff --git a/chrome/browser/ui/webui/app_management/app_management_ui.cc b/chrome/browser/ui/webui/app_management/app_management_ui.cc
index b1872df7..58a21d4 100644
--- a/chrome/browser/ui/webui/app_management/app_management_ui.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_ui.cc
@@ -40,7 +40,7 @@
       {"location", IDS_APP_MANAGEMENT_LOCATION},
       {"microphone", IDS_APP_MANAGEMENT_MICROPHONE},
       {"moreApps", IDS_APP_MANAGEMENT_MORE_APPS},
-      {"morePermissions", IDS_APP_MANAGEMENT_MORE_PERMISSIONS},
+      {"moreSettings", IDS_APP_MANAGEMENT_MORE_SETTINGS},
       {"noSearchResults", IDS_APP_MANAGEMENT_NO_RESULTS},
       {"notifications", IDS_APP_MANAGEMENT_NOTIFICATIONS},
       {"notificationSublabel", IDS_APP_MANAGEMENT_NOTIFICATIONS_SUBLABEL},
diff --git a/chrome/browser/web_applications/OWNERS b/chrome/browser/web_applications/OWNERS
index 52802bc..d552597 100644
--- a/chrome/browser/web_applications/OWNERS
+++ b/chrome/browser/web_applications/OWNERS
@@ -2,6 +2,8 @@
 benwells@chromium.org
 calamity@chromium.org
 dominickn@chromium.org
+ericwilligers@chromium.org
+harrisjay@chromium.org
 loyso@chromium.org
 mgiuca@chromium.org
 nigeltao@chromium.org
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
index 15e1b8a..8098e72 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.cc
@@ -229,10 +229,8 @@
 
 }  // namespace
 
-BookmarkAppInstallManager::BookmarkAppInstallManager(
-    Profile* profile,
-    web_app::InstallFinalizer* finalizer)
-    : InstallManager(profile), finalizer_(finalizer) {
+BookmarkAppInstallManager::BookmarkAppInstallManager(Profile* profile)
+    : InstallManager(profile) {
   bookmark_app_helper_factory_ = base::BindRepeating(
       [](Profile* profile, std::unique_ptr<WebApplicationInfo> web_app_info,
          content::WebContents* web_contents,
@@ -359,7 +357,7 @@
       ExtensionSystem::Get(profile())->extension_service();
   DCHECK(extension_service);
 
-  if (finalizer_->CanSkipAppUpdateForSync(app_id, *web_application_info))
+  if (finalizer()->CanSkipAppUpdateForSync(app_id, *web_application_info))
     return;
 
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
index 84fbebe0..5a13d5d 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager.h
@@ -20,7 +20,6 @@
 }
 
 namespace web_app {
-class InstallFinalizer;
 class WebAppDataRetriever;
 }
 
@@ -32,8 +31,7 @@
 // crbug.com/915043.
 class BookmarkAppInstallManager final : public web_app::InstallManager {
  public:
-  BookmarkAppInstallManager(Profile* profile,
-                            web_app::InstallFinalizer* finalizer);
+  explicit BookmarkAppInstallManager(Profile* profile);
   ~BookmarkAppInstallManager() override;
 
   // InstallManager:
@@ -85,7 +83,6 @@
  private:
   BookmarkAppHelperFactory bookmark_app_helper_factory_;
   DataRetrieverFactory data_retriever_factory_;
-  web_app::InstallFinalizer* finalizer_;
 
   DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallManager);
 };
diff --git a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc
index 2d248c6..6987b1a 100644
--- a/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/bookmark_app_install_manager_unittest.cc
@@ -35,8 +35,8 @@
     ChromeRenderViewHostTestHarness::SetUp();
 
     install_finalizer_ = std::make_unique<web_app::TestInstallFinalizer>();
-    install_manager_ = std::make_unique<BookmarkAppInstallManager>(
-        profile(), install_finalizer_.get());
+    install_manager_ = std::make_unique<BookmarkAppInstallManager>(profile());
+    install_manager_->SetSubsystems(nullptr, install_finalizer_.get());
 
     extensions::TestExtensionSystem* test_extension_system =
         static_cast<extensions::TestExtensionSystem*>(
diff --git a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
index dd8098b..4938a36 100644
--- a/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
@@ -159,27 +159,30 @@
 
 class WebAppPolicyManagerTest : public ChromeRenderViewHostTestHarness {
  public:
-  WebAppPolicyManagerTest()
-      : test_web_app_provider_creator_(
-            base::BindOnce(&WebAppPolicyManagerTest::CreateWebAppProvider,
-                           base::Unretained(this))) {}
+  WebAppPolicyManagerTest() {}
 
   ~WebAppPolicyManagerTest() override = default;
 
-  std::unique_ptr<KeyedService> CreateWebAppProvider(Profile* profile) {
-    auto provider = std::make_unique<TestWebAppProvider>(profile);
-    provider->Init();
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
 
-    auto test_pending_app_manager = std::make_unique<TestPendingAppManager>();
+    DCHECK(profile()->AsTestingProfile());
+    auto* provider = static_cast<web_app::TestWebAppProvider*>(
+        web_app::WebAppProvider::Get(profile()));
+
+    auto test_app_registrar = std::make_unique<TestAppRegistrar>();
+    test_app_registrar_ = test_app_registrar.get();
+    provider->SetRegistrar(std::move(test_app_registrar));
+
+    auto test_pending_app_manager =
+        std::make_unique<TestPendingAppManager>(test_app_registrar_);
     test_pending_app_manager_ = test_pending_app_manager.get();
     provider->SetPendingAppManager(std::move(test_pending_app_manager));
 
-    auto web_app_policy_manager = std::make_unique<WebAppPolicyManager>(
-        profile, test_pending_app_manager_);
+    auto web_app_policy_manager =
+        std::make_unique<WebAppPolicyManager>(profile());
     web_app_policy_manager_ = web_app_policy_manager.get();
     provider->SetWebAppPolicyManager(std::move(web_app_policy_manager));
-
-    return provider;
   }
 
   void SimulatePreviouslyInstalledApp(
@@ -196,7 +199,7 @@
   WebAppPolicyManager* policy_manager() { return web_app_policy_manager_; }
 
  private:
-  TestWebAppProviderCreator test_web_app_provider_creator_;
+  TestAppRegistrar* test_app_registrar_ = nullptr;
   TestPendingAppManager* test_pending_app_manager_ = nullptr;
   WebAppPolicyManager* web_app_policy_manager_ = nullptr;
 
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
index d67a9de4..a7f70fe 100644
--- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
@@ -145,13 +145,11 @@
   DCHECK(SystemWebAppManager::IsEnabled());
 
   auto provider = std::make_unique<TestWebAppProvider>(profile);
-  // Create all real subsystems but do not start them:
-  provider->Init();
 
   // Override SystemWebAppManager with TestSystemWebAppManager:
   DCHECK(!test_system_web_app_manager_);
-  auto test_system_web_app_manager = std::make_unique<TestSystemWebAppManager>(
-      profile, &provider->pending_app_manager());
+  auto test_system_web_app_manager =
+      std::make_unique<TestSystemWebAppManager>(profile);
   test_system_web_app_manager_ = test_system_web_app_manager.get();
   provider->SetSystemWebAppManager(std::move(test_system_web_app_manager));
 
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
index 206f41b23..717598a 100644
--- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
@@ -57,28 +57,32 @@
 
 class SystemWebAppManagerTest : public ChromeRenderViewHostTestHarness {
  public:
-  SystemWebAppManagerTest()
-      : test_web_app_provider_creator_(
-            base::BindOnce(&SystemWebAppManagerTest::CreateWebAppProvider,
-                           base::Unretained(this))) {
+  SystemWebAppManagerTest() {
     scoped_feature_list_.InitWithFeatures({features::kSystemWebApps}, {});
   }
 
   ~SystemWebAppManagerTest() override = default;
 
-  std::unique_ptr<KeyedService> CreateWebAppProvider(Profile* profile) {
-    auto provider = std::make_unique<TestWebAppProvider>(profile);
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
 
-    auto test_pending_app_manager = std::make_unique<TestPendingAppManager>();
+    DCHECK(profile()->AsTestingProfile());
+    auto* provider = static_cast<web_app::TestWebAppProvider*>(
+        web_app::WebAppProvider::Get(profile()));
+
+    auto test_app_registrar = std::make_unique<TestAppRegistrar>();
+    test_app_registrar_ = test_app_registrar.get();
+    provider->SetRegistrar(std::move(test_app_registrar));
+
+    auto test_pending_app_manager =
+        std::make_unique<TestPendingAppManager>(test_app_registrar_);
     test_pending_app_manager_ = test_pending_app_manager.get();
     provider->SetPendingAppManager(std::move(test_pending_app_manager));
 
-    auto system_web_app_manager = std::make_unique<TestSystemWebAppManager>(
-        profile, test_pending_app_manager_);
+    auto system_web_app_manager =
+        std::make_unique<TestSystemWebAppManager>(profile());
     system_web_app_manager_ = system_web_app_manager.get();
     provider->SetSystemWebAppManager(std::move(system_web_app_manager));
-
-    return provider;
   }
 
   void SimulatePreviouslyInstalledApp(
@@ -88,10 +92,7 @@
   }
 
   bool IsInstalled(const GURL& install_url) {
-    return pending_app_manager()
-        ->registrar()
-        ->LookupExternalAppId(install_url)
-        .has_value();
+    return test_app_registrar_->LookupExternalAppId(install_url).has_value();
   }
 
  protected:
@@ -107,7 +108,7 @@
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
-  TestWebAppProviderCreator test_web_app_provider_creator_;
+  TestAppRegistrar* test_app_registrar_ = nullptr;
   TestPendingAppManager* test_pending_app_manager_ = nullptr;
   TestSystemWebAppManager* system_web_app_manager_ = nullptr;
   TestWebAppUiDelegate ui_delegate_;
diff --git a/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc b/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc
index 7e3bff3..276a3b7 100644
--- a/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc
+++ b/chrome/browser/web_applications/bookmark_apps/test_web_app_provider.cc
@@ -24,11 +24,8 @@
 // static
 std::unique_ptr<KeyedService> TestWebAppProvider::BuildDefault(
     content::BrowserContext* context) {
-  Profile* profile = Profile::FromBrowserContext(context);
-  auto provider = std::make_unique<TestWebAppProvider>(profile);
-  provider->Init();
-
-  return provider;
+  return std::make_unique<TestWebAppProvider>(
+      Profile::FromBrowserContext(context));
 }
 
 TestWebAppProvider::TestWebAppProvider(Profile* profile)
@@ -38,31 +35,37 @@
 
 void TestWebAppProvider::SetRegistrar(std::unique_ptr<AppRegistrar> registrar) {
   registrar_ = std::move(registrar);
+  ConnectSubsystems();
 }
 
 void TestWebAppProvider::SetInstallManager(
     std::unique_ptr<InstallManager> install_manager) {
   install_manager_ = std::move(install_manager);
+  ConnectSubsystems();
 }
 
 void TestWebAppProvider::SetInstallFinalizer(
     std::unique_ptr<InstallFinalizer> install_finalizer) {
   install_finalizer_ = std::move(install_finalizer);
+  ConnectSubsystems();
 }
 
 void TestWebAppProvider::SetPendingAppManager(
     std::unique_ptr<PendingAppManager> pending_app_manager) {
   pending_app_manager_ = std::move(pending_app_manager);
+  ConnectSubsystems();
 }
 
 void TestWebAppProvider::SetSystemWebAppManager(
     std::unique_ptr<SystemWebAppManager> system_web_app_manager) {
   system_web_app_manager_ = std::move(system_web_app_manager);
+  ConnectSubsystems();
 }
 
 void TestWebAppProvider::SetWebAppPolicyManager(
     std::unique_ptr<WebAppPolicyManager> web_app_policy_manager) {
   web_app_policy_manager_ = std::move(web_app_policy_manager);
+  ConnectSubsystems();
 }
 
 TestWebAppProviderCreator::TestWebAppProviderCreator(
diff --git a/chrome/browser/web_applications/components/install_manager.cc b/chrome/browser/web_applications/components/install_manager.cc
index dbca32f1..62b0e44 100644
--- a/chrome/browser/web_applications/components/install_manager.cc
+++ b/chrome/browser/web_applications/components/install_manager.cc
@@ -54,6 +54,12 @@
 
 InstallManager::~InstallManager() = default;
 
+void InstallManager::SetSubsystems(AppRegistrar* registrar,
+                                   InstallFinalizer* finalizer) {
+  registrar_ = registrar;
+  finalizer_ = finalizer;
+}
+
 void InstallManager::Shutdown() {
   for (InstallManagerObserver& observer : observers_)
     observer.OnInstallManagerShutdown();
diff --git a/chrome/browser/web_applications/components/install_manager.h b/chrome/browser/web_applications/components/install_manager.h
index d560c8f..6ccaee2 100644
--- a/chrome/browser/web_applications/components/install_manager.h
+++ b/chrome/browser/web_applications/components/install_manager.h
@@ -26,6 +26,8 @@
 
 enum class InstallResultCode;
 class InstallManagerObserver;
+class InstallFinalizer;
+class AppRegistrar;
 struct InstallOptions;
 
 // TODO(loyso): Rework this interface once BookmarkAppHelper erased. Unify the
@@ -110,6 +112,8 @@
   explicit InstallManager(Profile* profile);
   virtual ~InstallManager();
 
+  void SetSubsystems(AppRegistrar* registrar, InstallFinalizer* finalizer);
+
   virtual void Shutdown();
 
   // Loads |web_app_url| in a new WebContents and determines if it is
@@ -123,10 +127,16 @@
 
  protected:
   Profile* profile() { return profile_; }
+  AppRegistrar* registrar() { return registrar_; }
+  InstallFinalizer* finalizer() { return finalizer_; }
 
  private:
   Profile* profile_;
   WebAppUrlLoader url_loader_;
+
+  AppRegistrar* registrar_ = nullptr;
+  InstallFinalizer* finalizer_ = nullptr;
+
   base::ObserverList<InstallManagerObserver, true /*check_empty*/> observers_;
 };
 
diff --git a/chrome/browser/web_applications/components/pending_app_manager.cc b/chrome/browser/web_applications/components/pending_app_manager.cc
index 4688730..c55cb9cc 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.cc
+++ b/chrome/browser/web_applications/components/pending_app_manager.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 
 #include <algorithm>
+#include <map>
 #include <memory>
 #include <utility>
 
@@ -29,11 +30,16 @@
 PendingAppManager::SynchronizeRequest::SynchronizeRequest(
     SynchronizeRequest&& other) = default;
 
-PendingAppManager::PendingAppManager(AppRegistrar* registrar)
-    : registrar_(registrar) {}
+PendingAppManager::PendingAppManager() = default;
 
 PendingAppManager::~PendingAppManager() = default;
 
+void PendingAppManager::SetSubsystems(AppRegistrar* registrar,
+                                      InstallFinalizer* finalizer) {
+  registrar_ = registrar;
+  finalizer_ = finalizer;
+}
+
 void PendingAppManager::SynchronizeInstalledApps(
     std::vector<InstallOptions> desired_apps_install_options,
     InstallSource install_source,
diff --git a/chrome/browser/web_applications/components/pending_app_manager.h b/chrome/browser/web_applications/components/pending_app_manager.h
index 6e533e30..b6c1a1ac 100644
--- a/chrome/browser/web_applications/components/pending_app_manager.h
+++ b/chrome/browser/web_applications/components/pending_app_manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_PENDING_APP_MANAGER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_PENDING_APP_MANAGER_H_
 
+#include <map>
 #include <memory>
 #include <ostream>
 #include <string>
@@ -23,6 +24,7 @@
 enum class InstallResultCode;
 
 class AppRegistrar;
+class InstallFinalizer;
 
 // PendingAppManager installs, uninstalls, and updates apps.
 //
@@ -43,9 +45,11 @@
       base::OnceCallback<void(std::map<GURL, InstallResultCode> install_results,
                               std::map<GURL, bool> uninstall_results)>;
 
-  explicit PendingAppManager(AppRegistrar* registrar);
+  PendingAppManager();
   virtual ~PendingAppManager();
 
+  void SetSubsystems(AppRegistrar* registrar, InstallFinalizer* finalizer);
+
   virtual void Shutdown() = 0;
 
   // Queues an installation operation with the highest priority. Essentially
@@ -93,7 +97,10 @@
       InstallSource install_source,
       SynchronizeCallback callback);
 
+  // TODO(crbug.com/973324): Make these protected after removing dependencies on
+  // this method.
   AppRegistrar* registrar() { return registrar_; }
+  InstallFinalizer* finalizer() { return finalizer_; }
 
  private:
   struct SynchronizeRequest {
@@ -120,7 +127,8 @@
                                        bool succeeded);
   void OnAppSynchronized(InstallSource source, const GURL& app_url);
 
-  AppRegistrar* registrar_;
+  AppRegistrar* registrar_ = nullptr;
+  InstallFinalizer* finalizer_ = nullptr;
 
   base::flat_map<InstallSource, SynchronizeRequest> synchronize_requests_;
 
diff --git a/chrome/browser/web_applications/components/pending_app_manager_unittest.cc b/chrome/browser/web_applications/components/pending_app_manager_unittest.cc
index b093001..bb6db86 100644
--- a/chrome/browser/web_applications/components/pending_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/components/pending_app_manager_unittest.cc
@@ -21,7 +21,7 @@
 
 class PendingAppManagerTest : public testing::Test {
  public:
-  PendingAppManagerTest() {}
+  PendingAppManagerTest() : pending_app_manager_(&registrar_) {}
 
  protected:
   void Sync(std::vector<GURL> urls) {
@@ -65,6 +65,7 @@
   }
 
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  TestAppRegistrar registrar_;
   TestPendingAppManager pending_app_manager_;
 };
 
@@ -72,7 +73,9 @@
 // installs an app doesn't crash.
 // Regression test for https://crbug.com/962808
 TEST_F(PendingAppManagerTest, DestroyDuringInstallInSynchronize) {
-  auto pending_app_manager = std::make_unique<TestPendingAppManager>();
+  web_app::TestAppRegistrar registrar;
+  auto pending_app_manager =
+      std::make_unique<TestPendingAppManager>(&registrar);
 
   std::vector<InstallOptions> install_options_list;
   install_options_list.emplace_back(GURL("https://foo.example"),
@@ -95,7 +98,9 @@
 // uninstalls an app doesn't crash.
 // Regression test for https://crbug.com/962808
 TEST_F(PendingAppManagerTest, DestroyDuringUninstallInSynchronize) {
-  auto pending_app_manager = std::make_unique<TestPendingAppManager>();
+  web_app::TestAppRegistrar registrar;
+  auto pending_app_manager =
+      std::make_unique<TestPendingAppManager>(&registrar);
 
   // Install an app that will be uninstalled next.
   {
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
index 7ec3421..53f16f37 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_manager.cc
@@ -71,14 +71,16 @@
 
 const char WebAppPolicyManager::kInstallResultHistogramName[];
 
-WebAppPolicyManager::WebAppPolicyManager(Profile* profile,
-                                         PendingAppManager* pending_app_manager)
-    : profile_(profile),
-      pref_service_(profile_->GetPrefs()),
-      pending_app_manager_(pending_app_manager) {}
+WebAppPolicyManager::WebAppPolicyManager(Profile* profile)
+    : profile_(profile), pref_service_(profile_->GetPrefs()) {}
 
 WebAppPolicyManager::~WebAppPolicyManager() = default;
 
+void WebAppPolicyManager::SetSubsystems(
+    PendingAppManager* pending_app_manager) {
+  pending_app_manager_ = pending_app_manager;
+}
+
 void WebAppPolicyManager::Start() {
   base::PostTaskWithTraits(
       FROM_HERE, {content::BrowserThread::UI, base::TaskPriority::BEST_EFFORT},
diff --git a/chrome/browser/web_applications/components/policy/web_app_policy_manager.h b/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
index 92ef8818..9cb90d2 100644
--- a/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
+++ b/chrome/browser/web_applications/components/policy/web_app_policy_manager.h
@@ -33,9 +33,11 @@
   // Constructs a WebAppPolicyManager instance that uses
   // |pending_app_manager| to manage apps. |pending_app_manager| should outlive
   // this class.
-  WebAppPolicyManager(Profile* profile, PendingAppManager* pending_app_manager);
+  explicit WebAppPolicyManager(Profile* profile);
   ~WebAppPolicyManager();
 
+  void SetSubsystems(PendingAppManager* pending_app_manager);
+
   void Start();
 
   void ReinstallPlaceholderAppIfNecessary(const GURL& url);
@@ -53,7 +55,7 @@
   PrefService* pref_service_;
 
   // Used to install, uninstall, and update apps. Should outlive this class.
-  PendingAppManager* pending_app_manager_;
+  PendingAppManager* pending_app_manager_ = nullptr;
 
   PrefChangeRegistrar pref_change_registrar_;
 
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 e5a0f12..6f431d7 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
@@ -292,8 +292,8 @@
     auto data_retriever = std::make_unique<web_app::TestDataRetriever>();
     data_retriever_ = data_retriever.get();
 
-    auto install_manager = std::make_unique<web_app::WebAppInstallManager>(
-        profile(), registrar.get(), install_finalizer.get());
+    auto install_manager =
+        std::make_unique<web_app::WebAppInstallManager>(profile());
     install_manager->SetDataRetrieverFactoryForTesting(
         GetFactoryForRetriever(std::move(data_retriever)));
 
diff --git a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
index 02486273..12d9da5 100644
--- a/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
+++ b/chrome/browser/web_applications/extensions/install_manager_bookmark_app_unittest.cc
@@ -105,8 +105,7 @@
                          CreateOsShortcutsCallback callback) override {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
-        base::BindOnce(std::move(callback), true /*shortcuts_created*/
-                       ));
+        base::BindOnce(std::move(callback), true /*shortcuts_created*/));
   }
   void PinAppToShelf(const web_app::AppId& app_id) override {}
   void ReparentTab(const web_app::AppId& app_id,
@@ -144,8 +143,8 @@
         std::make_unique<BookmarkAppInstallFinalizerInstallOnly>(profile());
     install_finalizer_ = install_finalizer.get();
 
-    auto install_manager = std::make_unique<web_app::WebAppInstallManager>(
-        profile(), registrar.get(), install_finalizer.get());
+    auto install_manager =
+        std::make_unique<web_app::WebAppInstallManager>(profile());
 
     install_manager->SetDataRetrieverFactoryForTesting(
         base::BindLambdaForTesting([this]() {
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 5036816..8b2ea4e 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.cc
@@ -45,13 +45,8 @@
   OnceInstallCallback callback;
 };
 
-PendingBookmarkAppManager::PendingBookmarkAppManager(
-    Profile* profile,
-    web_app::AppRegistrar* registrar,
-    web_app::InstallFinalizer* install_finalizer)
-    : PendingAppManager(registrar),
-      profile_(profile),
-      install_finalizer_(install_finalizer),
+PendingBookmarkAppManager::PendingBookmarkAppManager(Profile* profile)
+    : profile_(profile),
       externally_installed_app_prefs_(profile->GetPrefs()),
       url_loader_(std::make_unique<web_app::WebAppUrlLoader>()),
       task_factory_(base::BindRepeating(&InstallationTaskCreateWrapper)) {}
@@ -69,7 +64,7 @@
     return;
 
   pending_tasks_and_callbacks_.push_front(std::make_unique<TaskAndCallback>(
-      task_factory_.Run(profile_, registrar(), install_finalizer_,
+      task_factory_.Run(profile_, registrar(), finalizer(),
                         std::move(install_options)),
       std::move(callback)));
 
@@ -84,7 +79,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_, registrar(), install_finalizer_,
+        task_factory_.Run(profile_, registrar(), finalizer(),
                           std::move(install_options)),
         callback));
   }
@@ -99,7 +94,7 @@
     std::vector<GURL> uninstall_urls,
     const UninstallCallback& callback) {
   for (auto& url : uninstall_urls) {
-    install_finalizer_->UninstallExternalWebApp(
+    finalizer()->UninstallExternalWebApp(
         url, base::BindOnce(
                  [](const UninstallCallback& callback, const GURL& app_url,
                     bool uninstalled) { callback.Run(app_url, uninstalled); },
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 675600c7..2372ba3e 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -51,10 +51,7 @@
           web_app::InstallFinalizer*,
           web_app::InstallOptions)>;
 
-  explicit PendingBookmarkAppManager(
-      Profile* profile,
-      web_app::AppRegistrar* registrar,
-      web_app::InstallFinalizer* install_finalizer);
+  explicit PendingBookmarkAppManager(Profile* profile);
   ~PendingBookmarkAppManager() override;
 
   // web_app::PendingAppManager
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 ba3c7e0..4ad1639 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
@@ -261,8 +261,8 @@
 
   std::unique_ptr<PendingBookmarkAppManager>
   GetPendingBookmarkAppManagerWithTestMocks() {
-    auto manager = std::make_unique<PendingBookmarkAppManager>(
-        profile(), registrar_.get(), install_finalizer_.get());
+    auto manager = std::make_unique<PendingBookmarkAppManager>(profile());
+    manager->SetSubsystems(registrar_.get(), install_finalizer_.get());
     manager->SetTaskFactoryForTesting(base::BindRepeating(
         &TestBookmarkAppInstallationTaskFactory::CreateInstallationTask,
         base::Unretained(task_factory_.get())));
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 92d473d..b07d20d 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -74,11 +74,9 @@
 
 const char SystemWebAppManager::kInstallResultHistogramName[];
 
-SystemWebAppManager::SystemWebAppManager(Profile* profile,
-                                         PendingAppManager* pending_app_manager)
+SystemWebAppManager::SystemWebAppManager(Profile* profile)
     : on_apps_synchronized_(new base::OneShotEvent()),
       pref_service_(profile->GetPrefs()),
-      pending_app_manager_(pending_app_manager),
       weak_ptr_factory_(this) {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
           ::switches::kTestType)) {
@@ -100,6 +98,11 @@
 
 SystemWebAppManager::~SystemWebAppManager() = default;
 
+void SystemWebAppManager::SetSubsystems(
+    PendingAppManager* pending_app_manager) {
+  pending_app_manager_ = pending_app_manager;
+}
+
 void SystemWebAppManager::Start(WebAppUiDelegate* ui_delegate) {
   ui_delegate_ = ui_delegate;
 
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index 81052f30..5efc31c 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_SYSTEM_WEB_APP_MANAGER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_SYSTEM_WEB_APP_MANAGER_H_
 
+#include <map>
+#include <memory>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -61,12 +64,11 @@
   static constexpr char kInstallResultHistogramName[] =
       "Webapp.InstallResult.System";
 
-  // Constructs a SystemWebAppManager instance that uses
-  // |pending_app_manager| to manage apps. |pending_app_manager| should outlive
-  // this class.
-  SystemWebAppManager(Profile* profile, PendingAppManager* pending_app_manager);
+  explicit SystemWebAppManager(Profile* profile);
   virtual ~SystemWebAppManager();
 
+  void SetSubsystems(PendingAppManager* pending_app_manager);
+
   void Start(WebAppUiDelegate* ui_delegate);
 
   static bool IsEnabled();
@@ -120,7 +122,7 @@
   PrefService* pref_service_;
 
   // Used to install, uninstall, and update apps. Should outlive this class.
-  PendingAppManager* pending_app_manager_;
+  PendingAppManager* pending_app_manager_ = nullptr;
 
   WebAppUiDelegate* ui_delegate_ = nullptr;
 
diff --git a/chrome/browser/web_applications/test/test_pending_app_manager.cc b/chrome/browser/web_applications/test/test_pending_app_manager.cc
index 5892ee2..d0aae4e 100644
--- a/chrome/browser/web_applications/test/test_pending_app_manager.cc
+++ b/chrome/browser/web_applications/test/test_pending_app_manager.cc
@@ -19,18 +19,21 @@
 
 namespace web_app {
 
-TestPendingAppManager::TestPendingAppManager()
-    : PendingAppManager(&registrar_),
-      deduped_install_count_(0),
-      deduped_uninstall_count_(0) {}
+TestPendingAppManager::TestPendingAppManager(TestAppRegistrar* registrar)
+    : deduped_install_count_(0),
+      deduped_uninstall_count_(0),
+      registrar_(registrar) {
+  // TODO(crbug.com/973324): Wire this up to a TestInstallFinalizer.
+  SetSubsystems(registrar, nullptr);
+}
 
 TestPendingAppManager::~TestPendingAppManager() = default;
 
 void TestPendingAppManager::SimulatePreviouslyInstalledApp(
     const GURL& url,
     InstallSource install_source) {
-  registrar_.AddExternalApp(TestInstallFinalizer::GetAppIdForUrl(url),
-                            {url, install_source});
+  registrar_->AddExternalApp(TestInstallFinalizer::GetAppIdForUrl(url),
+                             {url, install_source});
 }
 
 void TestPendingAppManager::SetInstallResultCode(
@@ -52,9 +55,10 @@
         // Use a WeakPtr to be able to simulate the Install callback running
         // after PendingAppManager gets deleted.
         if (weak_ptr) {
-          if (!registrar_.LookupExternalAppId(url)) {
-            registrar_.AddExternalApp(TestInstallFinalizer::GetAppIdForUrl(url),
-                                      {url, install_options.install_source});
+          if (!registrar_->LookupExternalAppId(url)) {
+            registrar_->AddExternalApp(
+                TestInstallFinalizer::GetAppIdForUrl(url),
+                {url, install_options.install_source});
             deduped_install_count_++;
           }
           install_requests_.push_back(install_options);
@@ -78,9 +82,9 @@
         FROM_HERE,
         base::BindLambdaForTesting([this, weak_ptr, url, callback]() {
           if (weak_ptr) {
-            base::Optional<AppId> app_id = registrar_.LookupExternalAppId(url);
+            base::Optional<AppId> app_id = registrar_->LookupExternalAppId(url);
             if (app_id) {
-              registrar_.RemoveExternalApp(*app_id);
+              registrar_->RemoveExternalApp(*app_id);
               deduped_uninstall_count_++;
             }
             uninstall_requests_.push_back(url);
diff --git a/chrome/browser/web_applications/test/test_pending_app_manager.h b/chrome/browser/web_applications/test/test_pending_app_manager.h
index 7a651e8..e3a541ba 100644
--- a/chrome/browser/web_applications/test/test_pending_app_manager.h
+++ b/chrome/browser/web_applications/test/test_pending_app_manager.h
@@ -20,7 +20,7 @@
 
 class TestPendingAppManager : public PendingAppManager {
  public:
-  TestPendingAppManager();
+  explicit TestPendingAppManager(TestAppRegistrar* registrar);
   ~TestPendingAppManager() override;
 
   // The foo_requests methods may return duplicates, if the underlying
@@ -67,7 +67,7 @@
 
   InstallResultCode install_result_code_ = InstallResultCode::kSuccess;
 
-  TestAppRegistrar registrar_;
+  TestAppRegistrar* registrar_;
 
   base::WeakPtrFactory<TestPendingAppManager> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/web_applications/test/test_system_web_app_manager.cc b/chrome/browser/web_applications/test/test_system_web_app_manager.cc
index 2cb878b..6ece1af 100644
--- a/chrome/browser/web_applications/test/test_system_web_app_manager.cc
+++ b/chrome/browser/web_applications/test/test_system_web_app_manager.cc
@@ -9,10 +9,8 @@
 
 namespace web_app {
 
-TestSystemWebAppManager::TestSystemWebAppManager(
-    Profile* profile,
-    PendingAppManager* pending_app_manager)
-    : SystemWebAppManager(profile, pending_app_manager) {
+TestSystemWebAppManager::TestSystemWebAppManager(Profile* profile)
+    : SystemWebAppManager(profile) {
   SetSystemApps(base::flat_map<SystemAppType, SystemAppInfo>());
 }
 
diff --git a/chrome/browser/web_applications/test/test_system_web_app_manager.h b/chrome/browser/web_applications/test/test_system_web_app_manager.h
index a1b397b..b007543 100644
--- a/chrome/browser/web_applications/test/test_system_web_app_manager.h
+++ b/chrome/browser/web_applications/test/test_system_web_app_manager.h
@@ -18,8 +18,7 @@
 
 class TestSystemWebAppManager : public SystemWebAppManager {
  public:
-  TestSystemWebAppManager(Profile* profile,
-                          PendingAppManager* pending_app_manager);
+  explicit TestSystemWebAppManager(Profile* profile);
   ~TestSystemWebAppManager() override;
 
   void SetSystemApps(base::flat_map<SystemAppType, SystemAppInfo> system_apps);
diff --git a/chrome/browser/web_applications/web_app_install_manager.cc b/chrome/browser/web_applications/web_app_install_manager.cc
index d36a25c..f11e1357 100644
--- a/chrome/browser/web_applications/web_app_install_manager.cc
+++ b/chrome/browser/web_applications/web_app_install_manager.cc
@@ -20,13 +20,9 @@
 
 namespace web_app {
 
-WebAppInstallManager::WebAppInstallManager(Profile* profile,
-                                           AppRegistrar* app_registrar,
-                                           InstallFinalizer* install_finalizer)
+WebAppInstallManager::WebAppInstallManager(Profile* profile)
     : InstallManager(profile),
-      url_loader_(std::make_unique<WebAppUrlLoader>()),
-      app_registrar_(app_registrar),
-      install_finalizer_(install_finalizer) {
+      url_loader_(std::make_unique<WebAppUrlLoader>()) {
   data_retriever_factory_ = base::BindRepeating(
       []() { return std::make_unique<WebAppDataRetriever>(); });
 }
@@ -63,7 +59,7 @@
     WebAppInstallDialogCallback dialog_callback,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), install_finalizer_, data_retriever_factory_.Run());
+      profile(), finalizer(), data_retriever_factory_.Run());
   task->InstallWebAppFromManifest(
       contents, install_source, std::move(dialog_callback),
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -79,7 +75,7 @@
     WebAppInstallDialogCallback dialog_callback,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), install_finalizer_, data_retriever_factory_.Run());
+      profile(), finalizer(), data_retriever_factory_.Run());
   task->InstallWebAppFromManifestWithFallback(
       contents, force_shortcut_app, install_source, std::move(dialog_callback),
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -94,7 +90,7 @@
     WebappInstallSource install_source,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), install_finalizer_, data_retriever_factory_.Run());
+      profile(), finalizer(), data_retriever_factory_.Run());
   task->InstallWebAppFromInfo(
       std::move(web_application_info), no_network_install, install_source,
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -108,7 +104,7 @@
     const InstallOptions& install_options,
     OnceInstallCallback callback) {
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), install_finalizer_, data_retriever_factory_.Run());
+      profile(), finalizer(), data_retriever_factory_.Run());
   task->InstallWebAppWithOptions(
       web_contents, install_options,
       base::BindOnce(&WebAppInstallManager::OnTaskCompleted,
@@ -124,13 +120,12 @@
   if (is_shutting_down_)
     return;
 
-  if (install_finalizer_->CanSkipAppUpdateForSync(app_id,
-                                                  *web_application_info)) {
+  if (finalizer()->CanSkipAppUpdateForSync(app_id, *web_application_info)) {
     std::move(callback).Run(app_id, InstallResultCode::kAlreadyInstalled);
     return;
   }
 
-  bool is_locally_installed = app_registrar_->IsInstalled(app_id);
+  bool is_locally_installed = registrar()->IsInstalled(app_id);
 #if defined(OS_CHROMEOS)
   // On Chrome OS, sync always locally installs an app.
   is_locally_installed = true;
@@ -140,7 +135,7 @@
   DCHECK(web_contents_);
 
   auto task = std::make_unique<WebAppInstallTask>(
-      profile(), install_finalizer_, data_retriever_factory_.Run());
+      profile(), finalizer(), data_retriever_factory_.Run());
 
   base::OnceClosure task_closure = base::BindOnce(
       &WebAppInstallTask::InstallWebAppFromInfoRetrieveIcons,
diff --git a/chrome/browser/web_applications/web_app_install_manager.h b/chrome/browser/web_applications/web_app_install_manager.h
index e7a2f2f..069460a2 100644
--- a/chrome/browser/web_applications/web_app_install_manager.h
+++ b/chrome/browser/web_applications/web_app_install_manager.h
@@ -26,16 +26,12 @@
 namespace web_app {
 
 enum class InstallResultCode;
-class AppRegistrar;
-class InstallFinalizer;
 class WebAppDataRetriever;
 class WebAppInstallTask;
 
 class WebAppInstallManager final : public InstallManager {
  public:
-  WebAppInstallManager(Profile* profile,
-                       AppRegistrar* app_registrar,
-                       InstallFinalizer* install_finalizer);
+  explicit WebAppInstallManager(Profile* profile);
   ~WebAppInstallManager() override;
 
   // InstallManager:
@@ -108,9 +104,6 @@
   std::unique_ptr<content::WebContents> web_contents_;
   bool web_contents_ready_ = false;
 
-  AppRegistrar* app_registrar_;
-  InstallFinalizer* install_finalizer_;
-
   bool is_shutting_down_ = false;
 
   base::WeakPtrFactory<WebAppInstallManager> weak_ptr_factory_{this};
diff --git a/chrome/browser/web_applications/web_app_install_manager_unittest.cc b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
index f0522fe..b160ced 100644
--- a/chrome/browser/web_applications/web_app_install_manager_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_manager_unittest.cc
@@ -41,8 +41,8 @@
 
     install_finalizer_ = std::make_unique<TestInstallFinalizer>();
 
-    install_manager_ = std::make_unique<WebAppInstallManager>(
-        profile(), registrar_.get(), install_finalizer_.get());
+    install_manager_ = std::make_unique<WebAppInstallManager>(profile());
+    install_manager_->SetSubsystems(registrar_.get(), install_finalizer_.get());
 
     auto test_url_loader = std::make_unique<TestWebAppUrlLoader>();
 
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index f5be530a..69d2915 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -75,11 +75,7 @@
   // WebApp System must have only one instance in original profile.
   // Exclude secondary off-the-record profiles.
   DCHECK(!profile_->IsOffTheRecord());
-}
 
-WebAppProvider::~WebAppProvider() = default;
-
-void WebAppProvider::Init() {
   audio_focus_id_map_ = std::make_unique<WebAppAudioFocusIdMap>();
 
   if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions))
@@ -89,9 +85,14 @@
 
   notification_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
                               content::Source<Profile>(profile_));
+
+  ConnectSubsystems();
 }
 
+WebAppProvider::~WebAppProvider() = default;
+
 void WebAppProvider::StartRegistry() {
+  started_ = true;
   registrar_->Init(base::BindOnce(&WebAppProvider::OnRegistryReady,
                                   weak_ptr_factory_.GetWeakPtr()));
 }
@@ -117,6 +118,10 @@
   return *ui_delegate_;
 }
 
+SystemWebAppManager& WebAppProvider::system_web_app_manager() {
+  return *system_web_app_manager_;
+}
+
 void WebAppProvider::Shutdown() {
   // Destroy subsystems.
   // The order of destruction is the reverse order of creation:
@@ -142,40 +147,44 @@
   icon_manager_ = std::make_unique<WebAppIconManager>(
       profile, std::make_unique<FileUtilsWrapper>());
 
+  // TODO(crbug.com/973324): Once the WebAppInstallFinalizer can take an
+  // AppRegistrar instead of needing a WebAppRegistrar, move this wiring into
+  // ConnectSubsystems().
   install_finalizer_ = std::make_unique<WebAppInstallFinalizer>(
       web_app_registrar.get(), icon_manager_.get());
-  install_manager_ = std::make_unique<WebAppInstallManager>(
-      profile, web_app_registrar.get(), install_finalizer_.get());
+  install_manager_ = std::make_unique<WebAppInstallManager>(profile);
 
   registrar_ = std::move(web_app_registrar);
 }
 
 void WebAppProvider::CreateBookmarkAppsSubsystems(Profile* profile) {
-  auto bookmark_app_registrar =
-      std::make_unique<extensions::BookmarkAppRegistrar>(profile);
-
   install_finalizer_ =
-      std::make_unique<extensions::BookmarkAppInstallFinalizer>(profile_);
+      std::make_unique<extensions::BookmarkAppInstallFinalizer>(profile);
 
   if (base::FeatureList::IsEnabled(features::kDesktopPWAsUnifiedInstall)) {
-    install_manager_ = std::make_unique<WebAppInstallManager>(
-        profile, bookmark_app_registrar.get(), install_finalizer_.get());
+    install_manager_ = std::make_unique<WebAppInstallManager>(profile);
   } else {
-    install_manager_ = std::make_unique<extensions::BookmarkAppInstallManager>(
-        profile, install_finalizer_.get());
+    install_manager_ =
+        std::make_unique<extensions::BookmarkAppInstallManager>(profile);
   }
 
   pending_app_manager_ =
-      std::make_unique<extensions::PendingBookmarkAppManager>(
-          profile, bookmark_app_registrar.get(), install_finalizer_.get());
+      std::make_unique<extensions::PendingBookmarkAppManager>(profile);
 
-  web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(
-      profile, pending_app_manager_.get());
+  web_app_policy_manager_ = std::make_unique<WebAppPolicyManager>(profile);
 
-  system_web_app_manager_ = std::make_unique<SystemWebAppManager>(
-      profile, pending_app_manager_.get());
+  system_web_app_manager_ = std::make_unique<SystemWebAppManager>(profile);
 
-  registrar_ = std::move(bookmark_app_registrar);
+  registrar_ = std::make_unique<extensions::BookmarkAppRegistrar>(profile);
+}
+
+void WebAppProvider::ConnectSubsystems() {
+  DCHECK(!started_);
+  pending_app_manager_->SetSubsystems(registrar_.get(),
+                                      install_finalizer_.get());
+  web_app_policy_manager_->SetSubsystems(pending_app_manager_.get());
+  system_web_app_manager_->SetSubsystems(pending_app_manager_.get());
+  install_manager_->SetSubsystems(registrar_.get(), install_finalizer_.get());
 }
 
 void WebAppProvider::OnRegistryReady() {
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index 350fab7..b37b815 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -51,6 +51,13 @@
 // Connects Web App features, such as the installation of default and
 // policy-managed web apps, with Profiles (as WebAppProvider is a
 // Profile-linked KeyedService) and their associated PrefService.
+//
+// Lifecycle notes:
+// All subsystems are constructed independently of each other in the
+// WebAppProvider constructor.
+// Subsystem construction should have no side effects and start no tasks.
+// Tests can replace any of the subsystems before Start() is called.
+// Similarly, in destruction, subsystems should not refer to each other.
 class WebAppProvider : public WebAppProviderBase,
                        public content::NotificationObserver {
  public:
@@ -60,9 +67,10 @@
   explicit WebAppProvider(Profile* profile);
   ~WebAppProvider() override;
 
-  // Create subsystems but do not start them (yet).
-  void Init();
-  // Start registry. All subsystems depend on it.
+  // TODO(crbug.com/973324): Wrap StartRegistry() with a Start() method that
+  // calls ConnectSubsystems().
+  // Start registry. All subsystems depend on it. This will run all subsystem
+  // startup tasks.
   void StartRegistry();
 
   // WebAppProviderBase:
@@ -75,9 +83,7 @@
   // KeyedService:
   void Shutdown() override;
 
-  SystemWebAppManager& system_web_app_manager() {
-    return *system_web_app_manager_;
-  }
+  SystemWebAppManager& system_web_app_manager();
 
   void set_ui_delegate(WebAppUiDelegate* ui_delegate) {
     ui_delegate_ = ui_delegate;
@@ -103,6 +109,10 @@
   // ... or create legacy extension-based subsystems.
   void CreateBookmarkAppsSubsystems(Profile* profile);
 
+  // Wire together subsystems but do not start them (yet). Can be called
+  // multiple times before StartRegistry().
+  void ConnectSubsystems();
+
   void OnRegistryReady();
 
   void OnScanForExternalWebApps(std::vector<InstallOptions>);
@@ -134,6 +144,9 @@
 
   Profile* profile_;
 
+  // Ensures that ConnectSubsystems() is not called after StartRegistry().
+  bool started_ = false;
+
   base::WeakPtrFactory<WebAppProvider> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(WebAppProvider);
diff --git a/chrome/browser/web_applications/web_app_provider_factory.cc b/chrome/browser/web_applications/web_app_provider_factory.cc
index a82bca7a..4bdb59d 100644
--- a/chrome/browser/web_applications/web_app_provider_factory.cc
+++ b/chrome/browser/web_applications/web_app_provider_factory.cc
@@ -43,7 +43,6 @@
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
   WebAppProvider* provider = new WebAppProvider(profile);
-  provider->Init();
   provider->StartRegistry();
   return provider;
 }
diff --git a/chrome/services/cups_proxy/BUILD.gn b/chrome/services/cups_proxy/BUILD.gn
index 4de217f..031c02a 100644
--- a/chrome/services/cups_proxy/BUILD.gn
+++ b/chrome/services/cups_proxy/BUILD.gn
@@ -30,6 +30,8 @@
   if (use_cups) {
     configs += [ "//printing:cups" ]
     sources += [
+      "ipp_attribute_validator.cc",
+      "ipp_attribute_validator.h",
       "ipp_validator.cc",
       "ipp_validator.h",
       "printer_installer.cc",
diff --git a/chrome/services/cups_proxy/ipp_attribute_validator.cc b/chrome/services/cups_proxy/ipp_attribute_validator.cc
new file mode 100644
index 0000000..bfd4555
--- /dev/null
+++ b/chrome/services/cups_proxy/ipp_attribute_validator.cc
@@ -0,0 +1,663 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/cups_proxy/ipp_attribute_validator.h"
+
+#include <cups/ipp.h>
+
+#include <map>
+#include <string>
+
+#include "chrome/services/cups_ipp_parser/public/mojom/ipp_parser.mojom.h"
+
+namespace cups_proxy {
+
+namespace {
+
+// represents a type of a single attribute
+struct AttributeDefinition {
+  bool is_a_set;
+  cups_ipp_parser::mojom::ValueType type;
+};
+
+// definitions of all known attributes grouped by operations
+std::map<ipp_op_t, std::map<std::string, AttributeDefinition>>
+    attributesDefinitions = {
+        {IPP_OP_CUPS_GET_PPD,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_CUPS_GET_PRINTERS,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"limit", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"printer-location",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requested-attributes",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"charset-configured",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"charset-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"color-supported",
+              {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"compression-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"copies-default",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"document-format-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"finishings-default",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"generated-natural-language-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"ipp-versions-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media-ready", {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"natural-language-configured",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"operations-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"orientation-requested-default",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"orientation-requested-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"output-bin-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"pdl-override-supported",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality-default",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"print-quality-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"printer-device-id",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-info",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-location",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-make-and-model",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"sides-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"sides-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"uri-authentication-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"uri-security-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_CANCEL_JOB,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_CREATE_JOB,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-name", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"ipp-attribute-fidelity",
+              {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"copies", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings", {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"media", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"orientation-requested",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"sides", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-state", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_GET_JOB_ATTRIBUTES,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requested-attributes",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"copies", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings", {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-name", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-originating-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-printer-up-time",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-state", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"orientation-requested",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"sides", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"time-at-completed",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"time-at-creation",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"time-at-processing",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+         }},
+        {IPP_OP_GET_JOBS,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"limit", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"requested-attributes",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"which-jobs", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"my-jobs", {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"copies", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings", {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-name", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-originating-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-printer-up-time",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-state", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"orientation-requested",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"sides", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"time-at-completed",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"time-at-creation",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"time-at-processing",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+         }},
+        {IPP_OP_GET_PRINTER_ATTRIBUTES,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requested-attributes",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"charset-configured",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"charset-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"color-supported",
+              {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"compression-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"copies-default",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"document-format-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"finishings-default",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"generated-natural-language-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"ipp-versions-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media-ready", {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"media-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"natural-language-configured",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"operations-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"orientation-requested-default",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"orientation-requested-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"output-bin-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"pages-per-minute",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"pages-per-minute-color",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"pdl-override-supported",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality-default",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"print-quality-supported",
+              {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"printer-alert",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-alert-description",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-device-id",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-info",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-is-accepting-jobs",
+              {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"printer-location",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-make-and-model",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-more-info",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-state",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"printer-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-up-time",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"printer-uri-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"queued-job-count",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"sides-default",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"sides-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"uri-authentication-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"uri-security-supported",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_HOLD_JOB,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_PAUSE_PRINTER,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_PRINT_JOB,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-name", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"ipp-attribute-fidelity",
+              {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"document-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"compression",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"copies", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings", {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"media", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"orientation-requested",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"sides", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-state", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_PRINT_URI,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-name", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"ipp-attribute-fidelity",
+              {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"document-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"compression",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"copies", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings", {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"media", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"orientation-requested",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"sides", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-state", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_RELEASE_JOB,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_RESUME_PRINTER,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_SEND_DOCUMENT,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"compression",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-state", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_SEND_URI,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"compression",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-id", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-uri", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-state", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"job-state-reasons",
+              {true, cups_ipp_parser::mojom::ValueType::STRING}},
+         }},
+        {IPP_OP_VALIDATE_JOB,
+         {
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"printer-uri",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"requesting-user-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"job-name", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"ipp-attribute-fidelity",
+              {false, cups_ipp_parser::mojom::ValueType::BOOLEAN}},
+             {"document-name",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"compression",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"document-format",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"copies", {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"finishings", {true, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"media", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"orientation-requested",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"output-bin", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"print-quality",
+              {false, cups_ipp_parser::mojom::ValueType::INTEGER}},
+             {"sides", {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-charset",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"attributes-natural-language",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+             {"status-message",
+              {false, cups_ipp_parser::mojom::ValueType::STRING}},
+         }}};
+
+}  // namespace
+
+bool ValidateAttribute(ipp_op_t ipp_oper_id,
+                       const std::string& name,
+                       cups_ipp_parser::mojom::ValueType type,
+                       size_t values_count) {
+  auto it_oper = attributesDefinitions.find(ipp_oper_id);
+  if (it_oper == attributesDefinitions.end()) {
+    return false;
+  }
+  auto it_attr = it_oper->second.find(name);
+  if (it_attr == it_oper->second.end()) {
+    return false;
+  }
+  if (it_attr->second.type != type) {
+    return false;
+  }
+  if (values_count == 0) {
+    return false;
+  }
+  if (!it_attr->second.is_a_set && values_count > 1) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace cups_proxy
diff --git a/chrome/services/cups_proxy/ipp_attribute_validator.h b/chrome/services/cups_proxy/ipp_attribute_validator.h
new file mode 100644
index 0000000..0a75eab
--- /dev/null
+++ b/chrome/services/cups_proxy/ipp_attribute_validator.h
@@ -0,0 +1,33 @@
+// 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 CHROME_SERVICES_CUPS_PROXY_IPP_ATTRIBUTE_VALIDATOR_H_
+#define CHROME_SERVICES_CUPS_PROXY_IPP_ATTRIBUTE_VALIDATOR_H_
+
+#include <cups/ipp.h>
+
+#include <string>
+
+#include "chrome/services/cups_ipp_parser/public/mojom/ipp_parser.mojom.h"
+
+namespace cups_proxy {
+
+// Validates an attribute |name| of type |type| in operation |ipp_oper_id|.
+// |values_count| represents number of values in the attribute. The following
+// constraints are enforced:
+// - only operations from predefined set are accepted
+// - only attributes from predefined set are accepted
+// - an attribute must be part of given operation (request or response)
+// - a type of the attribute must match the specification
+// - a single-value attribute cannot have more than one value
+// - a set-of-values attribute cannot be empty
+// Returns false <=> at least one of the constraints has been violated.
+bool ValidateAttribute(ipp_op_t ipp_oper_id,
+                       const std::string& name,
+                       cups_ipp_parser::mojom::ValueType type,
+                       size_t values_count);
+
+}  // namespace cups_proxy
+
+#endif  // CHROME_SERVICES_CUPS_PROXY_IPP_ATTRIBUTE_VALIDATOR_H_
diff --git a/chrome/services/cups_proxy/ipp_validator.cc b/chrome/services/cups_proxy/ipp_validator.cc
index 4207fcf..18deadd 100644
--- a/chrome/services/cups_proxy/ipp_validator.cc
+++ b/chrome/services/cups_proxy/ipp_validator.cc
@@ -17,6 +17,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
+#include "chrome/services/cups_proxy/ipp_attribute_validator.h"
 #include "net/http/http_util.h"
 #include "printing/backend/cups_ipp_util.h"
 
@@ -128,10 +129,9 @@
     return nullptr;
   }
 
-  if (!ippSetOperation(ipp.get(),
-                       static_cast<ipp_op_t>(ipp_message->operation_id))) {
+  const ipp_op_t ipp_oper_id = static_cast<ipp_op_t>(ipp_message->operation_id);
+  if (!ippSetOperation(ipp.get(), ipp_oper_id))
     return nullptr;
-  }
 
   if (!ippSetRequestId(ipp.get(), ipp_message->request_id)) {
     return nullptr;
@@ -142,6 +142,10 @@
     cups_ipp_parser::mojom::IppAttributePtr attribute =
         std::move(ipp_message->attributes[i]);
 
+    if (ValidateAttribute(ipp_oper_id, attribute->name, attribute->type,
+                          attribute->values.size()))
+      return nullptr;
+
     switch (attribute->type) {
       case cups_ipp_parser::mojom::ValueType::BOOLEAN: {
         base::Optional<std::vector<char>> values =
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 0688306..375cd6f 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -59,6 +59,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/system_connector.h"
 #include "content/public/common/content_switches.h"
@@ -616,6 +617,11 @@
 
   cast_content_browser_client_->CreateGeneralAudienceBrowsingService();
 
+  // Disable RenderFrameHost's Javascript injection restrictions so that the
+  // Cast Web Service can implement its own JS injection policy at a higher
+  // level.
+  content::RenderFrameHost::AllowInjectingJavaScript();
+
   cast_browser_process_->cast_service()->Start();
 
   if (parameters_.ui_task) {
diff --git a/chromecast/browser/cast_web_contents.h b/chromecast/browser/cast_web_contents.h
index 320b743..d035037 100644
--- a/chromecast/browser/cast_web_contents.h
+++ b/chromecast/browser/cast_web_contents.h
@@ -8,10 +8,12 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
+#include "base/strings/string_piece_forward.h"
 #include "chromecast/common/mojom/feature_manager.mojom.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
@@ -273,6 +275,34 @@
   virtual void EnableBackgroundVideoPlayback(bool enabled) = 0;
 
   // ===========================================================================
+  // Page Communication
+  // ===========================================================================
+
+  // Executes a UTF-8 encoded |script| for every subsequent page load where
+  // the frame's URL has an origin reflected in |origins|. The script is
+  // executed early, prior to the execution of the document's scripts.
+  //
+  // Scripts are identified by a string-based client-managed |id|. Any
+  // script previously injected using the same |id| will be replaced.
+  //
+  // The order in which multiple bindings are executed is the same as the
+  // order in which the bindings were Added. If a script is added which
+  // clobbers an existing script of the same |id|, the previous script's
+  // precedence in the injection order will be preserved.
+  // |script| and |id| must be non-empty string.
+  //
+  // At least one |origins| entry must be specified.
+  // If a wildcard "*" is specified in |origins|, then the script will be
+  // evaluated for all documents.
+  virtual void AddBeforeLoadJavaScript(base::StringPiece id,
+                                       const std::vector<std::string>& origins,
+                                       base::StringPiece script) = 0;
+
+  // Removes a previously added JavaScript snippet identified by |id|.
+  // This is a no-op if there is no JavaScript snippet identified by |id|.
+  virtual void RemoveBeforeLoadJavaScript(base::StringPiece id) = 0;
+
+  // ===========================================================================
   // Utility Methods
   // ===========================================================================
 
diff --git a/chromecast/browser/cast_web_contents_browsertest.cc b/chromecast/browser/cast_web_contents_browsertest.cc
index c0be4f42..e9a765a 100644
--- a/chromecast/browser/cast_web_contents_browsertest.cc
+++ b/chromecast/browser/cast_web_contents_browsertest.cc
@@ -15,6 +15,9 @@
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chromecast/base/chromecast_switches.h"
 #include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "chromecast/browser/cast_browser_context.h"
@@ -46,6 +49,7 @@
 using ::testing::Invoke;
 using ::testing::InvokeWithoutArgs;
 using ::testing::Mock;
+using ::testing::NiceMock;
 using ::testing::Property;
 
 namespace content {
@@ -108,6 +112,7 @@
            service_manager::InterfaceProvider* frame_interfaces,
            blink::AssociatedInterfaceProvider* frame_associated_interfaces));
   MOCK_METHOD1(ResourceLoadFailed, void(CastWebContents* cast_web_contents));
+  MOCK_METHOD1(UpdateTitle, void(const base::string16& title));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockCastWebContentsObserver);
@@ -121,6 +126,45 @@
   MOCK_METHOD1(CloseContents, void(content::WebContents* source));
 };
 
+class TitleChangeObserver : public CastWebContents::Observer {
+ public:
+  TitleChangeObserver() = default;
+  ~TitleChangeObserver() override = default;
+
+  // Spins a Runloop until the title of the page matches the |expected_title|
+  // that have been set.
+  void RunUntilTitleEquals(base::StringPiece expected_title) {
+    expected_title_ = expected_title.as_string();
+    // Spin the runloop until the expected conditions are met.
+    if (current_title_ != expected_title_) {
+      expected_title_ = expected_title.as_string();
+      base::RunLoop run_loop;
+      quit_closure_ = run_loop.QuitClosure();
+      run_loop.Run();
+    }
+  }
+
+  // CastWebContents::Observer implementation:
+  void UpdateTitle(const base::string16& title) override {
+    // Resumes execution of RunUntilTitleEquals() if |title| matches
+    // expectations.
+    std::string title_utf8 = base::UTF16ToUTF8(title);
+    current_title_ = title_utf8;
+    if (!quit_closure_.is_null() && current_title_ == expected_title_) {
+      DCHECK_EQ(current_title_, expected_title_);
+      std::move(quit_closure_).Run();
+    }
+  }
+
+ private:
+  std::string current_title_;
+  std::string expected_title_;
+
+  base::OnceClosure quit_closure_;
+
+  DISALLOW_COPY_AND_ASSIGN(TitleChangeObserver);
+};
+
 }  // namespace
 
 // =============================================================================
@@ -157,6 +201,7 @@
     cast_web_contents_ =
         std::make_unique<CastWebContentsImpl>(web_contents_.get(), init_params);
     mock_cast_wc_observer_.Observe(cast_web_contents_.get());
+    title_change_observer_.Observe(cast_web_contents_.get());
 
     render_frames_.clear();
     content::WebContentsObserver::Observe(web_contents_.get());
@@ -178,7 +223,8 @@
 
   MockWebContentsDelegate mock_wc_delegate_;
   MockCastWebContentsDelegate mock_cast_wc_delegate_;
-  MockCastWebContentsObserver mock_cast_wc_observer_;
+  NiceMock<MockCastWebContentsObserver> mock_cast_wc_observer_;
+  TitleChangeObserver title_change_observer_;
   std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<CastWebContentsImpl> cast_web_contents_;
 
@@ -603,6 +649,249 @@
   run_loop->Run();
 }
 
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest, ExecuteJavaScriptOnLoad) {
+  // ===========================================================================
+  // Test: Injecting script to change title should work.
+  // ===========================================================================
+  constexpr char kExpectedTitle[] = "hello";
+  constexpr char kOriginalTitle[] =
+      "Welcome to Stan the Offline Dino's Homepage";
+
+  // The script should be able to run before HTML <script> tag starts running.
+  // The original title will be loaded first and then the injected script. Other
+  // scripts must run after the injected script.
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kExpectedTitle)));
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kOriginalTitle)));
+  constexpr char kBindingsId[] = "1234";
+
+  GURL gurl = content::GetFileUrlWithQuery(
+      GetTestDataFilePath("dynamic_title.html"), "");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId, {gurl.GetOrigin().spec()}, "stashed_title = 'hello';");
+
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kExpectedTitle);
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest,
+                       ExecuteJavaScriptUpdatedOnLoad) {
+  // ===========================================================================
+  // Test: Verify that this script replaces the previous script with same
+  // binding id, as opposed to being injected alongside it. (The latter would
+  // result in the title being "helloclobber").
+  // ===========================================================================
+  constexpr char kReplaceTitle[] = "clobber";
+  constexpr char kOriginalTitle[] =
+      "Welcome to Stan the Offline Dino's Homepage";
+
+  // The script should be able to run before HTML <script> tag starts running.
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kReplaceTitle)));
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kOriginalTitle)));
+
+  constexpr char kBindingsId[] = "1234";
+
+  GURL gurl = content::GetFileUrlWithQuery(
+      GetTestDataFilePath("dynamic_title.html"), "");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId, {gurl.GetOrigin().spec()}, "stashed_title = 'hello';");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId, {gurl.GetOrigin().spec()},
+      "stashed_title = document.title + 'clobber';");
+
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kReplaceTitle);
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest,
+                       ExecuteJavaScriptOnLoadOrdered) {
+  // ===========================================================================
+  // Test: Verifies that bindings are injected in order by producing a
+  // cumulative, non-commutative result.
+  // ===========================================================================
+  constexpr char kExpectedTitle[] = "hello there";
+  constexpr char kOriginalTitle[] =
+      "Welcome to Stan the Offline Dino's Homepage";
+  constexpr char kBindingsId1[] = "1234";
+  constexpr char kBindingsId2[] = "5678";
+
+  // The script should be able to run before HTML <script> tag starts running.
+  // The original title will be loaded first and then the injected script. Other
+  // scripts must run after the injected script.
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kExpectedTitle)));
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kOriginalTitle)));
+
+  GURL gurl = content::GetFileUrlWithQuery(
+      GetTestDataFilePath("dynamic_title.html"), "");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId1, {gurl.GetOrigin().spec()}, "stashed_title = 'hello';");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId2, {gurl.GetOrigin().spec()}, "stashed_title += ' there';");
+
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kExpectedTitle);
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest,
+                       ExecuteJavaScriptOnLoadRemoved) {
+  // ===========================================================================
+  // Test: Verifies that bindings could be removed successfully before page
+  // starts loading.
+  // ===========================================================================
+  constexpr char kExpectedTitle[] = "foo";
+  constexpr char kOriginalTitle[] =
+      "Welcome to Stan the Offline Dino's Homepage";
+  constexpr char kBindingsId1[] = "1234";
+  constexpr char kBindingsId2[] = "5678";
+
+  // The script should be able to run before HTML <script> tag starts running.
+  // The original title will be loaded first and then the injected script. Other
+  // scripts must run after the injected script.
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kExpectedTitle)));
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kOriginalTitle)));
+
+  GURL gurl = content::GetFileUrlWithQuery(
+      GetTestDataFilePath("dynamic_title.html"), "");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId1, {gurl.GetOrigin().spec()}, "stashed_title = 'foo';");
+  // Add a script which clobbers "foo".
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId2, {gurl.GetOrigin().spec()}, "stashed_title = 'bar';");
+  // Deletes the clobbering script.
+  cast_web_contents_->RemoveBeforeLoadJavaScript(kBindingsId2);
+
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kExpectedTitle);
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest,
+                       ExecuteJavaScriptOnLoadWrongOrigin) {
+  // ===========================================================================
+  // Test: Injecting script should not happen if the to-be-loaded page's origin
+  // is not whitelisted for the injection script.
+  // ===========================================================================
+  constexpr char kOriginalTitle[] =
+      "Welcome to Stan the Offline Dino's Homepage";
+  constexpr char kBindingsId[] = "1234";
+
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kOriginalTitle)));
+
+  GURL gurl = content::GetFileUrlWithQuery(
+      GetTestDataFilePath("dynamic_title.html"), "");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId, {"http://example.com"}, "stashed_title = 'hello';");
+
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kOriginalTitle);
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest,
+                       ExecuteJavaScriptOnLoadWildcardOrigin) {
+  // Start test server for hosting test HTML pages.
+  embedded_test_server()->ServeFilesFromSourceDirectory(GetTestDataPath());
+  StartTestServer();
+  // ===========================================================================
+  // Test: Injecting script should be able to load on arbitrary origins with
+  // wildcard origin restriction set.
+  // ===========================================================================
+  constexpr char kInjectedTitle1[] = "hello";
+  constexpr char kInjectedTitle2[] = "world";
+  constexpr char kOriginalTitle[] =
+      "Welcome to Stan the Offline Dino's Homepage";
+  constexpr char kBindingsId1[] = "1234";
+  constexpr char kBindingsId2[] = "5678";
+
+  // The script should be able to run before HTML <script> tag starts running.
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kOriginalTitle)))
+      .Times(2);
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kInjectedTitle1)));
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kInjectedTitle2)));
+
+  GURL gurl{embedded_test_server()->GetURL("/dynamic_title.html")};
+
+  cast_web_contents_->AddBeforeLoadJavaScript(kBindingsId1, {"*"},
+                                              "stashed_title = 'hello';");
+  // Test script injection for the origin 127.0.0.1.
+  // Load title "hello":
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kInjectedTitle1);
+
+  // Load AboutBlank page:
+  cast_web_contents_->LoadUrl(GURL(url::kAboutBlankURL));
+
+  cast_web_contents_->AddBeforeLoadJavaScript(kBindingsId2, {"*"},
+                                              "stashed_title = 'world';");
+  // Test script injection using a different origin ("localhost"), which should
+  // still be picked up by the wildcard. And the title should be initialized as
+  // origianl title first, then as 'world'.
+  GURL alt_url =
+      embedded_test_server()->GetURL("localhost", "/dynamic_title.html");
+  cast_web_contents_->LoadUrl(alt_url);
+  title_change_observer_.RunUntilTitleEquals(kInjectedTitle2);
+}
+
+IN_PROC_BROWSER_TEST_F(CastWebContentsBrowserTest,
+                       ExecuteJavaScriptOnLoadEarlyAndLateRegistrations) {
+  // ===========================================================================
+  // Test: Tests that we can inject scripts before and after RenderFrame
+  // creation.
+  // ===========================================================================
+  constexpr char kExpectedTitle1[] = "foo";
+  constexpr char kExpectedTitle2[] = "foo bar";
+  constexpr char kOriginalTitle[] =
+      "Welcome to Stan the Offline Dino's Homepage";
+  constexpr char kBindingsId1[] = "1234";
+  constexpr char kBindingsId2[] = "5678";
+
+  // The script should be able to run before HTML <script> tag starts running.
+  // The original title will be loaded first and then the injected script. Other
+  // scripts must run after the injected script.
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kExpectedTitle2)));
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kExpectedTitle1)));
+  EXPECT_CALL(mock_cast_wc_observer_,
+              UpdateTitle(base::ASCIIToUTF16(kOriginalTitle)))
+      .Times(2);
+
+  GURL gurl = content::GetFileUrlWithQuery(
+      GetTestDataFilePath("dynamic_title.html"), "");
+
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId1, {gurl.GetOrigin().spec()}, "stashed_title = 'foo';");
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kExpectedTitle1);
+
+  // Inject bindings after RenderFrameCreation
+  cast_web_contents_->AddBeforeLoadJavaScript(
+      kBindingsId2, {gurl.GetOrigin().spec()}, "stashed_title += ' bar';");
+
+  // Navigate away to clean the state.
+  cast_web_contents_->LoadUrl(GURL(url::kAboutBlankURL));
+
+  // Navigate back and see if both scripts are working.
+  cast_web_contents_->LoadUrl(gurl);
+  title_change_observer_.RunUntilTitleEquals(kExpectedTitle2);
+}
+
 }  // namespace chromecast
 
 #endif  // CHROMECAST_BROWSER_CAST_WEB_CONTENTS_BROWSERTEST_H_
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index c3b3793..de372e9e 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -7,7 +7,10 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/no_destructor.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/values.h"
 #include "chromecast/base/chromecast_switches.h"
@@ -15,6 +18,7 @@
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/devtools/remote_debugging_server.h"
 #include "chromecast/common/mojom/media_playback_options.mojom.h"
+#include "chromecast/common/mojom/on_load_script_injector.mojom.h"
 #include "chromecast/common/mojom/queryable_data_store.mojom.h"
 #include "chromecast/common/queryable_data.h"
 #include "content/public/browser/navigation_entry.h"
@@ -50,6 +54,24 @@
   }
 }
 
+bool IsOriginWhitelisted(const GURL& url,
+                         const std::vector<std::string>& allowed_origins) {
+  constexpr const char kWildcard[] = "*";
+  url::Origin url_origin = url::Origin::Create(url);
+
+  for (const std::string& allowed_origin : allowed_origins) {
+    if (allowed_origin == kWildcard)
+      return true;
+
+    if (url_origin.IsSameOriginWith(url::Origin::Create(GURL(allowed_origin))))
+      return true;
+
+    // TODO(crbug.com/893236): Add handling for nonstandard origins
+    // (e.g. data: URIs).
+  }
+  return false;
+}
+
 }  // namespace
 
 // static
@@ -227,6 +249,50 @@
   }
 }
 
+CastWebContentsImpl::OriginScopedScript::OriginScopedScript() = default;
+
+CastWebContentsImpl::OriginScopedScript::OriginScopedScript(
+    const std::vector<std::string>& origins,
+    std::string script)
+    : origins_(std::move(origins)), script_(std::move(script)) {}
+
+CastWebContentsImpl::OriginScopedScript&
+CastWebContentsImpl::OriginScopedScript::operator=(
+    CastWebContentsImpl::OriginScopedScript&& other) {
+  origins_ = std::move(other.origins_);
+  script_ = std::move(other.script_);
+  return *this;
+}
+
+CastWebContentsImpl::OriginScopedScript::~OriginScopedScript() = default;
+
+void CastWebContentsImpl::AddBeforeLoadJavaScript(
+    base::StringPiece id,
+    const std::vector<std::string>& origins,
+    base::StringPiece script) {
+  DCHECK(!id.empty() && !script.empty() && !origins.empty())
+      << "Invalid empty parameters were passed to AddBeforeLoadJavascript";
+  // If there is no script with the identifier |id|, then create a place for it
+  // at the end of the injection sequence.
+  if (before_load_scripts_.find(id.as_string()) == before_load_scripts_.end()) {
+    before_load_scripts_order_.push_back(id.as_string());
+  }
+  before_load_scripts_[id.as_string()] =
+      OriginScopedScript(origins, script.as_string());
+}
+
+void CastWebContentsImpl::RemoveBeforeLoadJavaScript(base::StringPiece id) {
+  before_load_scripts_.erase(id.as_string());
+
+  for (auto script_id_iter = before_load_scripts_order_.begin();
+       script_id_iter != before_load_scripts_order_.end(); ++script_id_iter) {
+    if (*script_id_iter == id) {
+      before_load_scripts_order_.erase(script_id_iter);
+      return;
+    }
+  }
+}
+
 void CastWebContentsImpl::AddObserver(CastWebContents::Observer* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(observer);
@@ -381,6 +447,35 @@
   NotifyPageState();
 }
 
+void CastWebContentsImpl::ReadyToCommitNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (before_load_scripts_.empty())
+    return;
+
+  if (!navigation_handle->IsInMainFrame() ||
+      navigation_handle->IsSameDocument() || navigation_handle->IsErrorPage())
+    return;
+
+  chromecast::shell::mojom::OnLoadScriptInjectorAssociatedPtr
+      before_load_script_injector;
+  navigation_handle->GetRenderFrameHost()
+      ->GetRemoteAssociatedInterfaces()
+      ->GetInterface(&before_load_script_injector);
+
+  // Provision the renderer's ScriptInjector with the scripts scoped to this
+  // page's origin.
+  before_load_script_injector->ClearOnLoadScripts();
+  for (auto script_id : before_load_scripts_order_) {
+    const OriginScopedScript& origin_scoped_script =
+        before_load_scripts_[script_id];
+    if (IsOriginWhitelisted(navigation_handle->GetURL(),
+                            origin_scoped_script.origins())) {
+      before_load_script_injector->AddOnLoadScript(
+          origin_scoped_script.script());
+    }
+  }
+}
+
 void CastWebContentsImpl::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/chromecast/browser/cast_web_contents_impl.h b/chromecast/browser/cast_web_contents_impl.h
index aef4fabb..e9e41e1e 100644
--- a/chromecast/browser/cast_web_contents_impl.h
+++ b/chromecast/browser/cast_web_contents_impl.h
@@ -13,10 +13,12 @@
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
+#include "base/memory/platform_shared_memory_region.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/time/time.h"
 #include "chromecast/browser/cast_media_blocker.h"
 #include "chromecast/browser/cast_web_contents.h"
@@ -57,6 +59,10 @@
   void BlockMediaLoading(bool blocked) override;
   void BlockMediaStarting(bool blocked) override;
   void EnableBackgroundVideoPlayback(bool enabled) override;
+  void AddBeforeLoadJavaScript(base::StringPiece id,
+                               const std::vector<std::string>& origins,
+                               base::StringPiece script) override;
+  void RemoveBeforeLoadJavaScript(base::StringPiece id) override;
 
   // Observer interface:
   void AddObserver(Observer* observer) override;
@@ -74,6 +80,8 @@
   void RenderProcessGone(base::TerminationStatus status) override;
   void DidStartNavigation(
       content::NavigationHandle* navigation_handle) override;
+  void ReadyToCommitNavigation(
+      content::NavigationHandle* navigation_handle) override;
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
   void DidFinishLoad(content::RenderFrameHost* render_frame_host,
@@ -102,6 +110,22 @@
       content::WebContentsObserver::MediaStoppedReason reason) override;
 
  private:
+  struct OriginScopedScript {
+    OriginScopedScript();
+    OriginScopedScript(const std::vector<std::string>& origins,
+                       std::string script);
+    OriginScopedScript& operator=(OriginScopedScript&& other);
+    ~OriginScopedScript();
+
+    const std::vector<std::string>& origins() const { return origins_; }
+    const std::string script() const { return script_; }
+
+    std::vector<std::string> origins_;
+    std::string script_;
+
+    DISALLOW_COPY_AND_ASSIGN(OriginScopedScript);
+  };
+
   void OnPageLoading();
   void OnPageLoaded();
   void UpdatePageState();
@@ -135,6 +159,9 @@
   bool notifying_;
   int last_error_;
 
+  std::map<std::string, OriginScopedScript> before_load_scripts_;
+  std::vector<std::string> before_load_scripts_order_;
+
   base::ObserverList<Observer>::Unchecked observer_list_;
 
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chromecast/browser/test/data/dynamic_title.html b/chromecast/browser/test/data/dynamic_title.html
new file mode 100644
index 0000000..c1602ea4
--- /dev/null
+++ b/chromecast/browser/test/data/dynamic_title.html
@@ -0,0 +1,12 @@
+<html>
+    <head><title>Welcome to Stan the Offline Dino's Homepage</title></head>
+    <body>
+        <script>
+         // This page surfaces the contents of "stashed_title" to tests via
+         // the page title. If the script was not executed, or if the script
+         // failed to execute, then the title will be
+         // "Welcome to Stan the Offline Dino's Homepage".
+         document.title = stashed_title;
+        </script>
+    </body>
+</html>
diff --git a/chromecast/browser/test/data/title1.html b/chromecast/browser/test/data/title1.html
new file mode 100644
index 0000000..9c313a2
--- /dev/null
+++ b/chromecast/browser/test/data/title1.html
@@ -0,0 +1,4 @@
+<html>
+    <head><title>title 1</title></head>
+    <body></body>
+</html>
diff --git a/chromecast/browser/test/data/title2.html b/chromecast/browser/test/data/title2.html
new file mode 100644
index 0000000..088e30d
--- /dev/null
+++ b/chromecast/browser/test/data/title2.html
@@ -0,0 +1,4 @@
+<html>
+    <head><title>title 2</title></head>
+    <body></body>
+</html>
diff --git a/chromecast/common/mojom/BUILD.gn b/chromecast/common/mojom/BUILD.gn
index 0ad9f0e0..abee4b1 100644
--- a/chromecast/common/mojom/BUILD.gn
+++ b/chromecast/common/mojom/BUILD.gn
@@ -13,6 +13,7 @@
     "media_playback_options.mojom",
     "memory_pressure.mojom",
     "multiroom.mojom",
+    "on_load_script_injector.mojom",
     "queryable_data_store.mojom",
   ]
 
diff --git a/chromecast/common/mojom/on_load_script_injector.mojom b/chromecast/common/mojom/on_load_script_injector.mojom
new file mode 100644
index 0000000..58c38b1
--- /dev/null
+++ b/chromecast/common/mojom/on_load_script_injector.mojom
@@ -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.
+
+module chromecast.shell.mojom;
+
+// Interface associated with RenderFrames for managing on-load JavaScript
+// injection tasks per frame. Does not enforce script injection policies,
+// which must be implemented at a higher level.
+interface OnLoadScriptInjector {
+  // Add an on-load JavaScript injection task. The script will be
+  // executed before any other script (e.g. <script> tag) runs.
+  AddOnLoadScript(string script);
+
+  // Clear all registered on-load JavaScript injection tasks.
+  ClearOnLoadScripts();
+};
diff --git a/chromecast/renderer/BUILD.gn b/chromecast/renderer/BUILD.gn
index 85681ec..d21997c 100644
--- a/chromecast/renderer/BUILD.gn
+++ b/chromecast/renderer/BUILD.gn
@@ -34,6 +34,8 @@
     "cast_url_loader_throttle_provider.h",
     "native_bindings_helper.cc",
     "native_bindings_helper.h",
+    "on_load_script_injector.cc",
+    "on_load_script_injector.h",
     "queryable_data_bindings.cc",
     "queryable_data_bindings.h",
     "queryable_data_store.cc",
diff --git a/chromecast/renderer/cast_content_renderer_client.cc b/chromecast/renderer/cast_content_renderer_client.cc
index 44308e5..363f0c3 100644
--- a/chromecast/renderer/cast_content_renderer_client.cc
+++ b/chromecast/renderer/cast_content_renderer_client.cc
@@ -18,6 +18,7 @@
 #include "chromecast/renderer/cast_url_loader_throttle_provider.h"
 #include "chromecast/renderer/media/key_systems_cast.h"
 #include "chromecast/renderer/media/media_caps_observer_impl.h"
+#include "chromecast/renderer/on_load_script_injector.h"
 #include "chromecast/renderer/queryable_data_bindings.h"
 #include "components/network_hints/renderer/prescient_networking_dispatcher.h"
 #include "content/public/common/content_switches.h"
@@ -170,6 +171,10 @@
   new CastMediaPlaybackOptions(render_frame);
   new QueryableDataBindings(render_frame);
 
+  // Add script injection support to the RenderFrame, used by Cast platform
+  // APIs. The objects' lifetimes are bound to the RenderFrame's lifetime.
+  new OnLoadScriptInjector(render_frame);
+
   if (!app_media_capabilities_observer_binding_.is_bound()) {
     mojom::ApplicationMediaCapabilitiesObserverPtr observer;
     app_media_capabilities_observer_binding_.Bind(mojo::MakeRequest(&observer));
diff --git a/chromecast/renderer/on_load_script_injector.cc b/chromecast/renderer/on_load_script_injector.cc
new file mode 100644
index 0000000..f221830
--- /dev/null
+++ b/chromecast/renderer/on_load_script_injector.cc
@@ -0,0 +1,64 @@
+// 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 "chromecast/renderer/on_load_script_injector.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/public/renderer/render_frame.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
+
+namespace chromecast {
+namespace shell {
+
+OnLoadScriptInjector::OnLoadScriptInjector(content::RenderFrame* frame)
+    : RenderFrameObserver(frame), weak_ptr_factory_(this) {
+  render_frame()->GetAssociatedInterfaceRegistry()->AddInterface(
+      base::BindRepeating(&OnLoadScriptInjector::BindToRequest,
+                          weak_ptr_factory_.GetWeakPtr()));
+}
+
+OnLoadScriptInjector::~OnLoadScriptInjector() {}
+
+void OnLoadScriptInjector::BindToRequest(
+    mojom::OnLoadScriptInjectorAssociatedRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void OnLoadScriptInjector::DidCommitProvisionalLoad(
+    bool is_same_document_navigation,
+    ui::PageTransition transition) {
+  // Ignore pushState or document fragment navigation.
+  if (is_same_document_navigation)
+    return;
+
+  // Don't inject anything for subframes.
+  if (!render_frame()->IsMainFrame())
+    return;
+
+  for (std::string& script : on_load_scripts_) {
+    base::string16 script_utf16 = base::UTF8ToUTF16(script);
+    render_frame()->ExecuteJavaScript(script_utf16);
+  }
+}
+
+void OnLoadScriptInjector::AddOnLoadScript(const std::string& script) {
+  on_load_scripts_.push_back(std::move(script));
+}
+
+void OnLoadScriptInjector::ClearOnLoadScripts() {
+  on_load_scripts_.clear();
+}
+
+void OnLoadScriptInjector::OnDestruct() {
+  delete this;
+}
+
+}  // namespace shell
+}  // namespace chromecast
diff --git a/chromecast/renderer/on_load_script_injector.h b/chromecast/renderer/on_load_script_injector.h
new file mode 100644
index 0000000..6866887
--- /dev/null
+++ b/chromecast/renderer/on_load_script_injector.h
@@ -0,0 +1,51 @@
+// 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 CHROMECAST_RENDERER_ON_LOAD_SCRIPT_INJECTOR_H_
+#define CHROMECAST_RENDERER_ON_LOAD_SCRIPT_INJECTOR_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string_piece_forward.h"
+#include "chromecast/common/mojom/on_load_script_injector.mojom.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "mojo/public/cpp/bindings/associated_binding_set.h"
+
+namespace chromecast {
+namespace shell {
+
+// Injects one or more scripts into a RenderFrame at the earliest possible time
+// during the page load process.
+class OnLoadScriptInjector : public content::RenderFrameObserver,
+                             public mojom::OnLoadScriptInjector {
+ public:
+  explicit OnLoadScriptInjector(content::RenderFrame* frame);
+
+  void BindToRequest(mojom::OnLoadScriptInjectorAssociatedRequest request);
+
+  void AddOnLoadScript(const std::string& script) override;
+  void ClearOnLoadScripts() override;
+
+  // RenderFrameObserver override:
+  void OnDestruct() override;
+  void DidCommitProvisionalLoad(bool is_same_document_navigation,
+                                ui::PageTransition transition) override;
+
+ private:
+  // Called by OnDestruct(), when the RenderFrame is destroyed.
+  ~OnLoadScriptInjector() override;
+
+  std::vector<std::string> on_load_scripts_;
+  mojo::AssociatedBindingSet<mojom::OnLoadScriptInjector> bindings_;
+  base::WeakPtrFactory<OnLoadScriptInjector> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(OnLoadScriptInjector);
+};
+
+}  // namespace shell
+}  // namespace chromecast
+
+#endif  // CHROMECAST_RENDERER_ON_LOAD_SCRIPT_INJECTOR_H_
diff --git a/chromeos/dbus/concierge_client.cc b/chromeos/dbus/concierge_client.cc
index fd67537..b6062cf 100644
--- a/chromeos/dbus/concierge_client.cc
+++ b/chromeos/dbus/concierge_client.cc
@@ -248,6 +248,32 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void GetVmEnterpriseReportingInfo(
+      const vm_tools::concierge::GetVmEnterpriseReportingInfoRequest& request,
+      DBusMethodCallback<
+          vm_tools::concierge::GetVmEnterpriseReportingInfoResponse> callback)
+      override {
+    dbus::MethodCall method_call(
+        vm_tools::concierge::kVmConciergeInterface,
+        vm_tools::concierge::kGetVmEnterpriseReportingInfoMethod);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      LOG(ERROR)
+          << "Failed to encode GetVmEnterpriseReportingInfoRequest protobuf";
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+      return;
+    }
+
+    concierge_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(
+            &ConciergeClientImpl::OnDBusProtoResponse<
+                vm_tools::concierge::GetVmEnterpriseReportingInfoResponse>,
+            weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void WaitForServiceToBeAvailable(
       dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback)
       override {
diff --git a/chromeos/dbus/concierge_client.h b/chromeos/dbus/concierge_client.h
index fe82197..df1638e 100644
--- a/chromeos/dbus/concierge_client.h
+++ b/chromeos/dbus/concierge_client.h
@@ -128,6 +128,14 @@
       const vm_tools::concierge::GetVmInfoRequest& request,
       DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse> callback) = 0;
 
+  // Get enterprise-reporting specific VM info.
+  // |callback| is called after the method call finishes.
+  virtual void GetVmEnterpriseReportingInfo(
+      const vm_tools::concierge::GetVmEnterpriseReportingInfoRequest& request,
+      DBusMethodCallback<
+          vm_tools::concierge::GetVmEnterpriseReportingInfoResponse>
+          callback) = 0;
+
   // Registers |callback| to run when the Concierge service becomes available.
   // If the service is already available, or if connecting to the name-owner-
   // changed signal fails, |callback| will be run once asynchronously.
diff --git a/chromeos/dbus/fake_concierge_client.cc b/chromeos/dbus/fake_concierge_client.cc
index 9442037..12a3a0b 100644
--- a/chromeos/dbus/fake_concierge_client.cc
+++ b/chromeos/dbus/fake_concierge_client.cc
@@ -162,6 +162,16 @@
       FROM_HERE, base::BindOnce(std::move(callback), get_vm_info_response_));
 }
 
+void FakeConciergeClient::GetVmEnterpriseReportingInfo(
+    const vm_tools::concierge::GetVmEnterpriseReportingInfoRequest& request,
+    DBusMethodCallback<
+        vm_tools::concierge::GetVmEnterpriseReportingInfoResponse> callback) {
+  get_vm_enterprise_reporting_info_called_ = true;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback),
+                                get_vm_enterprise_reporting_info_response_));
+}
+
 void FakeConciergeClient::WaitForServiceToBeAvailable(
     dbus::ObjectProxy::WaitForServiceToBeAvailableCallback callback) {
   wait_for_service_to_be_available_called_ = true;
diff --git a/chromeos/dbus/fake_concierge_client.h b/chromeos/dbus/fake_concierge_client.h
index ff1f696..33cc951c 100644
--- a/chromeos/dbus/fake_concierge_client.h
+++ b/chromeos/dbus/fake_concierge_client.h
@@ -104,6 +104,15 @@
                  DBusMethodCallback<vm_tools::concierge::GetVmInfoResponse>
                      callback) override;
 
+  // Fake version of the method that gets VM enterprise reporting info. Sets
+  // get_vm_enterprise_reporting_info_called_. |callback| is called after the
+  // method call finishes.
+  void GetVmEnterpriseReportingInfo(
+      const vm_tools::concierge::GetVmEnterpriseReportingInfoRequest& request,
+      DBusMethodCallback<
+          vm_tools::concierge::GetVmEnterpriseReportingInfoResponse> callback)
+      override;
+
   // Fake version of the method that waits for the Concierge service to be
   // availble.  |callback| is called after the method call finishes.
   void WaitForServiceToBeAvailable(
@@ -163,6 +172,10 @@
   bool stop_vm_called() const { return stop_vm_called_; }
   // Indicates whether GetVmInfo has been called
   bool get_vm_info_called() const { return get_vm_info_called_; }
+  // Indicates whether GetEnterpriseReportingInfo has been called
+  bool get_vm_enterprise_reporting_info_called() const {
+    return get_vm_enterprise_reporting_info_called_;
+  }
   // Indicates whether GetContainerSshKeys has been called
   bool get_container_ssh_keys_called() const {
     return get_container_ssh_keys_called_;
@@ -229,6 +242,12 @@
       const vm_tools::concierge::GetVmInfoResponse& get_vm_info_response) {
     get_vm_info_response_ = get_vm_info_response;
   }
+  void set_get_vm_enterprise_reporting_info_response(
+      const vm_tools::concierge::GetVmEnterpriseReportingInfoResponse&
+          get_vm_enterprise_reporting_info_response) {
+    get_vm_enterprise_reporting_info_response_ =
+        get_vm_enterprise_reporting_info_response;
+  }
   void set_container_ssh_keys_response(
       const vm_tools::concierge::ContainerSshKeysResponse&
           container_ssh_keys_response) {
@@ -282,6 +301,7 @@
   bool start_termina_vm_called_ = false;
   bool stop_vm_called_ = false;
   bool get_vm_info_called_ = false;
+  bool get_vm_enterprise_reporting_info_called_ = false;
   bool get_container_ssh_keys_called_ = false;
   bool attach_usb_device_called_ = false;
   bool detach_usb_device_called_ = false;
@@ -300,6 +320,8 @@
   vm_tools::concierge::StartVmResponse start_vm_response_;
   vm_tools::concierge::StopVmResponse stop_vm_response_;
   vm_tools::concierge::GetVmInfoResponse get_vm_info_response_;
+  vm_tools::concierge::GetVmEnterpriseReportingInfoResponse
+      get_vm_enterprise_reporting_info_response_;
   vm_tools::concierge::ContainerSshKeysResponse container_ssh_keys_response_;
   vm_tools::concierge::AttachUsbDeviceResponse attach_usb_device_response_;
   vm_tools::concierge::DetachUsbDeviceResponse detach_usb_device_response_;
diff --git a/components/arc/DEPS b/components/arc/DEPS
index 86e6332..ffb51248 100644
--- a/components/arc/DEPS
+++ b/components/arc/DEPS
@@ -15,6 +15,7 @@
   "+components/version_info",
   "+content/public/browser",
   "+media/base/video_codecs.h",
+  "+media/base/video_types.h",
   "+media/video/video_encode_accelerator.h",
   "+mojo",
   "+storage/browser/fileapi",
diff --git a/components/arc/common/video_accelerator_struct_traits.cc b/components/arc/common/video_accelerator_struct_traits.cc
index f99d0c7..9e9f702a 100644
--- a/components/arc/common/video_accelerator_struct_traits.cc
+++ b/components/arc/common/video_accelerator_struct_traits.cc
@@ -111,6 +111,51 @@
   return false;
 }
 
+// Make sure values in arc::mojom::VideoPixelFormat match to the values in
+// media::VideoPixelFormat. The former is a subset of the later.
+#define CHECK_PIXEL_FORMAT_ENUM(value)                                       \
+  static_assert(                                                             \
+      static_cast<int>(arc::mojom::VideoPixelFormat::value) == media::value, \
+      "enum ##value mismatch")
+
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_I420);
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_YV12);
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_NV12);
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_NV21);
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_ARGB);
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_ABGR);
+CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_XBGR);
+
+#undef CHECK_PXIEL_FORMAT_ENUM
+
+// static
+arc::mojom::VideoPixelFormat
+EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat>::ToMojom(
+    media::VideoPixelFormat input) {
+  NOTIMPLEMENTED();
+  return arc::mojom::VideoPixelFormat::PIXEL_FORMAT_I420;
+}
+
+// static
+bool EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat>::
+    FromMojom(arc::mojom::VideoPixelFormat input,
+              media::VideoPixelFormat* output) {
+  switch (input) {
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN:
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_I420:
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_YV12:
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_NV12:
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_NV21:
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_ARGB:
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_ABGR:
+    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_XBGR:
+      *output = static_cast<media::VideoPixelFormat>(input);
+      return true;
+  }
+  NOTREACHED();
+  return false;
+}
+
 // static
 bool StructTraits<arc::mojom::VideoFramePlaneDataView, arc::VideoFramePlane>::
     Read(arc::mojom::VideoFramePlaneDataView data, arc::VideoFramePlane* out) {
diff --git a/components/arc/common/video_accelerator_struct_traits.h b/components/arc/common/video_accelerator_struct_traits.h
index c1f4e28f..82068b22 100644
--- a/components/arc/common/video_accelerator_struct_traits.h
+++ b/components/arc/common/video_accelerator_struct_traits.h
@@ -8,6 +8,7 @@
 #include "components/arc/common/video_common.mojom.h"
 #include "components/arc/video_accelerator/video_frame_plane.h"
 #include "media/base/video_codecs.h"
+#include "media/base/video_types.h"
 #include "ui/gfx/geometry/size.h"
 
 namespace mojo {
@@ -50,6 +51,15 @@
 
   static bool Read(arc::mojom::SizeDataView data, gfx::Size* out);
 };
+
+template <>
+struct EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat> {
+  static arc::mojom::VideoPixelFormat ToMojom(media::VideoPixelFormat input);
+
+  static bool FromMojom(arc::mojom::VideoPixelFormat input,
+                        media::VideoPixelFormat* output);
+};
+
 }  // namespace mojo
 
 #endif  // COMPONENTS_ARC_COMMON_VIDEO_ACCELERATOR_STRUCT_TRAITS_H_
diff --git a/components/arc/common/video_common.mojom b/components/arc/common/video_common.mojom
index 27e4f86..613ba8e 100644
--- a/components/arc/common/video_common.mojom
+++ b/components/arc/common/video_common.mojom
@@ -68,6 +68,19 @@
   HAL_PIXEL_FORMAT_NV12 = 0x3231564e,
 };
 
+[Extensible]
+enum VideoPixelFormat {
+  // The values must match to the values in media::VideoPixelFormat
+  PIXEL_FORMAT_UNKNOWN = 0,
+  PIXEL_FORMAT_I420 = 1,
+  [MinVersion=2] PIXEL_FORMAT_YV12 = 2,
+  [MinVersion=2] PIXEL_FORMAT_NV12 = 6,
+  [MinVersion=2] PIXEL_FORMAT_NV21 = 7,
+  [MinVersion=2] PIXEL_FORMAT_ARGB = 10,
+  [MinVersion=2] PIXEL_FORMAT_ABGR = 27,
+  [MinVersion=2] PIXEL_FORMAT_XBGR = 28,
+};
+
 // The offset and stride of a video frame plane. Both offset and stride must
 // be non negative.
 struct VideoFramePlane {
diff --git a/components/arc/common/video_common.typemap b/components/arc/common/video_common.typemap
index 69e68668..ee8e5ab 100644
--- a/components/arc/common/video_common.typemap
+++ b/components/arc/common/video_common.typemap
@@ -6,6 +6,7 @@
 public_headers = [
   "//media/base/video_codecs.h",
   "//components/arc/video_accelerator/video_frame_plane.h",
+  "//media/base/video_types.h",
   "//ui/gfx/geometry/size.h",
 ]
 public_deps = [
@@ -21,5 +22,6 @@
 type_mappings = [
   "arc.mojom.VideoCodecProfile=media::VideoCodecProfile",
   "arc.mojom.VideoFramePlane=arc::VideoFramePlane[move_only]",
+  "arc.mojom.VideoPixelFormat=media::VideoPixelFormat",
   "arc.mojom.Size=gfx::Size",
 ]
diff --git a/components/arc/common/video_encode_accelerator.mojom b/components/arc/common/video_encode_accelerator.mojom
index 29c0690..b4c9f41 100644
--- a/components/arc/common/video_encode_accelerator.mojom
+++ b/components/arc/common/video_encode_accelerator.mojom
@@ -11,19 +11,6 @@
 
 // Next MinVersion: 4
 
-[Extensible]
-enum VideoPixelFormat {
-  // The values must match to the values in media::VideoPixelFormat
-  PIXEL_FORMAT_UNKNOWN = 0,
-  PIXEL_FORMAT_I420 = 1,
-  [MinVersion=2] PIXEL_FORMAT_YV12 = 2,
-  [MinVersion=2] PIXEL_FORMAT_NV12 = 6,
-  [MinVersion=2] PIXEL_FORMAT_NV21 = 7,
-  [MinVersion=2] PIXEL_FORMAT_ARGB = 10,
-  [MinVersion=2] PIXEL_FORMAT_ABGR = 27,
-  [MinVersion=2] PIXEL_FORMAT_XBGR = 28,
-};
-
 // Specification of an encoding profile supported by an encoder.
 struct VideoEncodeProfile {
   VideoCodecProfile profile;
diff --git a/components/arc/common/video_encode_accelerator.typemap b/components/arc/common/video_encode_accelerator.typemap
index eb875515..5210cc6 100644
--- a/components/arc/common/video_encode_accelerator.typemap
+++ b/components/arc/common/video_encode_accelerator.typemap
@@ -19,7 +19,6 @@
 type_mappings = [
   "arc.mojom.VideoFrameStorageType=media::VideoEncodeAccelerator::Config::StorageType",
   "arc.mojom.VideoEncodeAccelerator.Error=media::VideoEncodeAccelerator::Error",
-  "arc.mojom.VideoPixelFormat=media::VideoPixelFormat",
   "arc.mojom.VideoEncodeProfile=media::VideoEncodeAccelerator::SupportedProfile",
   "arc.mojom.VideoEncodeAcceleratorConfig=media::VideoEncodeAccelerator::Config",
 ]
diff --git a/components/arc/common/video_encode_accelerator_struct_traits.cc b/components/arc/common/video_encode_accelerator_struct_traits.cc
index 17c4a79..72bcd88 100644
--- a/components/arc/common/video_encode_accelerator_struct_traits.cc
+++ b/components/arc/common/video_encode_accelerator_struct_traits.cc
@@ -65,51 +65,6 @@
   return false;
 }
 
-// Make sure values in arc::mojom::VideoPixelFormat match to the values in
-// media::VideoPixelFormat. The former is a subset of the later.
-#define CHECK_PIXEL_FORMAT_ENUM(value)                                       \
-  static_assert(                                                             \
-      static_cast<int>(arc::mojom::VideoPixelFormat::value) == media::value, \
-      "enum ##value mismatch")
-
-CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_I420);
-CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_YV12);
-CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_NV12);
-CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_NV21);
-CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_ARGB);
-CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_ABGR);
-CHECK_PIXEL_FORMAT_ENUM(PIXEL_FORMAT_XBGR);
-
-#undef CHECK_PXIEL_FORMAT_ENUM
-
-// static
-arc::mojom::VideoPixelFormat
-EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat>::ToMojom(
-    media::VideoPixelFormat input) {
-  NOTIMPLEMENTED();
-  return arc::mojom::VideoPixelFormat::PIXEL_FORMAT_I420;
-}
-
-// static
-bool EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat>::
-    FromMojom(arc::mojom::VideoPixelFormat input,
-              media::VideoPixelFormat* output) {
-  switch (input) {
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_UNKNOWN:
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_I420:
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_YV12:
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_NV12:
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_NV21:
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_ARGB:
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_ABGR:
-    case arc::mojom::VideoPixelFormat::PIXEL_FORMAT_XBGR:
-      *output = static_cast<media::VideoPixelFormat>(input);
-      return true;
-  }
-  NOTREACHED();
-  return false;
-}
-
 // static
 bool StructTraits<arc::mojom::VideoEncodeAcceleratorConfigDataView,
                   media::VideoEncodeAccelerator::Config>::
diff --git a/components/arc/common/video_encode_accelerator_struct_traits.h b/components/arc/common/video_encode_accelerator_struct_traits.h
index 9f0dee5..aa3078c0 100644
--- a/components/arc/common/video_encode_accelerator_struct_traits.h
+++ b/components/arc/common/video_encode_accelerator_struct_traits.h
@@ -31,14 +31,6 @@
 };
 
 template <>
-struct EnumTraits<arc::mojom::VideoPixelFormat, media::VideoPixelFormat> {
-  static arc::mojom::VideoPixelFormat ToMojom(media::VideoPixelFormat input);
-
-  static bool FromMojom(arc::mojom::VideoPixelFormat input,
-                        media::VideoPixelFormat* output);
-};
-
-template <>
 struct StructTraits<arc::mojom::VideoEncodeProfileDataView,
                     media::VideoEncodeAccelerator::SupportedProfile> {
   static media::VideoCodecProfile profile(
diff --git a/components/background_task_scheduler/BUILD.gn b/components/background_task_scheduler/BUILD.gn
index be854b8..5923277f 100644
--- a/components/background_task_scheduler/BUILD.gn
+++ b/components/background_task_scheduler/BUILD.gn
@@ -93,6 +93,7 @@
       "android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerPrefsTest.java",
       "android/junit/src/org/chromium/components/background_task_scheduler/BackgroundTaskSchedulerUmaTest.java",
       "android/junit/src/org/chromium/components/background_task_scheduler/ShadowGcmNetworkManager.java",
+      "android/junit/src/org/chromium/components/background_task_scheduler/TaskInfoTest.java",
       "android/junit/src/org/chromium/components/background_task_scheduler/TestBackgroundTask.java",
     ]
 
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskInfo.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskInfo.java
index 3d263cc..fb9ed42 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskInfo.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskInfo.java
@@ -25,12 +25,14 @@
         private final long mWindowStartTimeMs;
         private final long mWindowEndTimeMs;
         private final boolean mHasWindowStartTimeConstraint;
+        private final boolean mExpiresAfterWindowEndTime;
 
         private OneOffInfo(long windowStartTimeMs, long windowEndTimeMs,
-                boolean hasWindowStartTimeConstraint) {
+                boolean hasWindowStartTimeConstraint, boolean expiresAfterWindowEndTime) {
             mWindowStartTimeMs = windowStartTimeMs;
             mWindowEndTimeMs = windowEndTimeMs;
             mHasWindowStartTimeConstraint = hasWindowStartTimeConstraint;
+            mExpiresAfterWindowEndTime = expiresAfterWindowEndTime;
         }
 
         /**
@@ -56,10 +58,19 @@
             return mHasWindowStartTimeConstraint;
         }
 
+        /**
+         * @return whether this one-off task expires after {@link #getWindowEndTimeMs()}
+         * False by default.
+         */
+        public boolean expiresAfterWindowEndTime() {
+            return mExpiresAfterWindowEndTime;
+        }
+
         @Override
         public String toString() {
-            return "{windowStartTimeMs: " + mWindowStartTimeMs + ", windowEndTimeMs: "
-                    + mWindowEndTimeMs + "}";
+            return "{windowStartTimeMs: " + mWindowStartTimeMs
+                    + ", windowEndTimeMs: " + mWindowEndTimeMs
+                    + "} - expires at window_end_time: " + mExpiresAfterWindowEndTime;
         }
     }
 
@@ -205,7 +216,7 @@
                     new PeriodicInfo(builder.mIntervalMs, builder.mFlexMs, builder.mHasFlex);
         } else {
             mOneOffInfo = new OneOffInfo(builder.mWindowStartTimeMs, builder.mWindowEndTimeMs,
-                    builder.mHasWindowStartTimeConstraint);
+                    builder.mHasWindowStartTimeConstraint, builder.mExpiresAfterWindowEndTime);
             mPeriodicInfo = null;
         }
     }
@@ -420,6 +431,7 @@
         private long mWindowStartTimeMs;
         private long mWindowEndTimeMs;
         private boolean mHasWindowStartTimeConstraint;
+        private boolean mExpiresAfterWindowEndTime;
 
         // Data about periodic tasks.
         private long mIntervalMs;
@@ -446,6 +458,19 @@
             return this;
         }
 
+        /**
+         * Set whether the task should expire if not scheduled until the window end time.
+         * This should be called only for One-Off tasks.
+         *
+         * @param expiresAfterWindowEndTime whether the task expires or not.
+         * @return this {@link Builder}.
+         */
+        Builder setExpiresAfterWindowEndTime(boolean expiresAfterWindowEndTime) {
+            assert !mIsPeriodic;
+            mExpiresAfterWindowEndTime = expiresAfterWindowEndTime;
+            return this;
+        }
+
         Builder setIntervalMs(long intervalMs) {
             assert mIsPeriodic;
             mIntervalMs = intervalMs;
diff --git a/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/TaskInfoTest.java b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/TaskInfoTest.java
new file mode 100644
index 0000000..c6aea9c
--- /dev/null
+++ b/components/background_task_scheduler/android/junit/src/org/chromium/components/background_task_scheduler/TaskInfoTest.java
@@ -0,0 +1,89 @@
+// 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.components.background_task_scheduler;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Feature;
+
+/** Unit tests for {@link TaskInfo}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class TaskInfoTest {
+    private static final int TEST_START_MS = 300000;
+    private static final int TEST_END_MS = 600000;
+
+    @Before
+    public void setUp() {
+        TestBackgroundTask.reset();
+    }
+
+    private void checkGeneralTaskInfoFields(
+            TaskInfo taskInfo, int taskId, Class<? extends BackgroundTask> backgroundTaskClass) {
+        assertEquals(taskId, taskInfo.getTaskId());
+        assertEquals(backgroundTaskClass, taskInfo.getBackgroundTaskClass());
+    }
+
+    @Test
+    @Feature({"BackgroundTaskScheduler"})
+    public void testExpirationWithinDeadline() {
+        TaskInfo oneOffTask =
+                TaskInfo.createOneOffTask(TaskIds.TEST, TestBackgroundTask.class, TEST_END_MS)
+                        .setExpiresAfterWindowEndTime(true)
+                        .build();
+
+        checkGeneralTaskInfoFields(oneOffTask, TaskIds.TEST, TestBackgroundTask.class);
+
+        assertFalse(oneOffTask.getOneOffInfo().hasWindowStartTimeConstraint());
+        assertEquals(TEST_END_MS, oneOffTask.getOneOffInfo().getWindowEndTimeMs());
+        assertTrue(oneOffTask.getOneOffInfo().expiresAfterWindowEndTime());
+
+        assertEquals(null, oneOffTask.getPeriodicInfo());
+    }
+
+    @Test
+    @Feature({"BackgroundTaskScheduler"})
+    public void testExpirationWithinTimeWindow() {
+        TaskInfo oneOffTask = TaskInfo.createOneOffTask(TaskIds.TEST, TestBackgroundTask.class,
+                                              TEST_START_MS, TEST_END_MS)
+                                      .setExpiresAfterWindowEndTime(true)
+                                      .build();
+
+        checkGeneralTaskInfoFields(oneOffTask, TaskIds.TEST, TestBackgroundTask.class);
+
+        assertTrue(oneOffTask.getOneOffInfo().hasWindowStartTimeConstraint());
+        assertEquals(TEST_START_MS, oneOffTask.getOneOffInfo().getWindowStartTimeMs());
+        assertEquals(TEST_END_MS, oneOffTask.getOneOffInfo().getWindowEndTimeMs());
+        assertTrue(oneOffTask.getOneOffInfo().expiresAfterWindowEndTime());
+
+        assertEquals(null, oneOffTask.getPeriodicInfo());
+    }
+
+    @Test
+    @Feature({"BackgroundTaskScheduler"})
+    public void testExpirationWithinZeroTimeWindow() {
+        TaskInfo oneOffTask = TaskInfo.createOneOffTask(TaskIds.TEST, TestBackgroundTask.class,
+                                              TEST_END_MS, TEST_END_MS)
+                                      .setExpiresAfterWindowEndTime(true)
+                                      .build();
+
+        checkGeneralTaskInfoFields(oneOffTask, TaskIds.TEST, TestBackgroundTask.class);
+
+        assertTrue(oneOffTask.getOneOffInfo().hasWindowStartTimeConstraint());
+        assertEquals(TEST_END_MS, oneOffTask.getOneOffInfo().getWindowStartTimeMs());
+        assertEquals(TEST_END_MS, oneOffTask.getOneOffInfo().getWindowEndTimeMs());
+        assertTrue(oneOffTask.getOneOffInfo().expiresAfterWindowEndTime());
+
+        assertEquals(null, oneOffTask.getPeriodicInfo());
+    }
+}
\ No newline at end of file
diff --git a/components/exo/wayland/fuzzer/BUILD.gn b/components/exo/wayland/fuzzer/BUILD.gn
index 37051bb..15706168 100644
--- a/components/exo/wayland/fuzzer/BUILD.gn
+++ b/components/exo/wayland/fuzzer/BUILD.gn
@@ -124,6 +124,7 @@
     "//components/exo:test_support",
     "//components/exo/wayland",
     "//skia",
+    "//third_party/wayland:wayland_util",
   ]
 }
 
diff --git a/components/password_manager/core/browser/credential_manager_impl.cc b/components/password_manager/core/browser/credential_manager_impl.cc
index 6c91be7..141237c 100644
--- a/components/password_manager/core/browser/credential_manager_impl.cc
+++ b/components/password_manager/core/browser/credential_manager_impl.cc
@@ -107,8 +107,8 @@
         pending_request_ ? CredentialManagerError::PENDING_REQUEST
                          : CredentialManagerError::PASSWORDSTOREUNAVAILABLE,
         base::nullopt);
-    LogCredentialManagerGetResult(metrics_util::CREDENTIAL_MANAGER_GET_REJECTED,
-                                  mediation);
+    LogCredentialManagerGetResult(
+        metrics_util::CredentialManagerGetResult::kRejected, mediation);
     return;
   }
 
@@ -117,8 +117,8 @@
   if (!client_->IsFillingEnabled(GetLastCommittedURL()) ||
       !client_->OnCredentialManagerUsed()) {
     std::move(callback).Run(CredentialManagerError::SUCCESS, CredentialInfo());
-    LogCredentialManagerGetResult(metrics_util::CREDENTIAL_MANAGER_GET_NONE,
-                                  mediation);
+    LogCredentialManagerGetResult(
+        metrics_util::CredentialManagerGetResult::kNone, mediation);
     return;
   }
   // Return an empty credential if zero-click is required but disabled.
@@ -127,7 +127,7 @@
     // Callback with empty credential info.
     std::move(callback).Run(CredentialManagerError::SUCCESS, CredentialInfo());
     LogCredentialManagerGetResult(
-        metrics_util::CREDENTIAL_MANAGER_GET_NONE_ZERO_CLICK_OFF, mediation);
+        metrics_util::CredentialManagerGetResult::kNoneZeroClickOff, mediation);
     return;
   }
 
@@ -192,12 +192,12 @@
     base::RecordAction(
         base::UserMetricsAction("CredentialManager_AccountChooser_Accepted"));
     metrics_util::LogCredentialManagerGetResult(
-        metrics_util::CREDENTIAL_MANAGER_GET_ACCOUNT_CHOOSER, mediation);
+        metrics_util::CredentialManagerGetResult::kAccountChooser, mediation);
   } else {
     base::RecordAction(
         base::UserMetricsAction("CredentialManager_AccountChooser_Dismissed"));
     metrics_util::LogCredentialManagerGetResult(
-        metrics_util::CREDENTIAL_MANAGER_GET_NONE, mediation);
+        metrics_util::CredentialManagerGetResult::kNone, mediation);
   }
   SendCredential(send_callback, info);
 }
diff --git a/components/password_manager/core/browser/credential_manager_pending_request_task.cc b/components/password_manager/core/browser/credential_manager_pending_request_task.cc
index 9ff4c1d..689d126 100644
--- a/components/password_manager/core/browser/credential_manager_pending_request_task.cc
+++ b/components/password_manager/core/browser/credential_manager_pending_request_task.cc
@@ -122,8 +122,8 @@
     std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
   using metrics_util::LogCredentialManagerGetResult;
   if (delegate_->GetOrigin() != origin_) {
-    LogCredentialManagerGetResult(metrics_util::CREDENTIAL_MANAGER_GET_NONE,
-                                  mediation_);
+    LogCredentialManagerGetResult(
+        metrics_util::CredentialManagerGetResult::kNone, mediation_);
     delegate_->SendCredential(send_callback_, CredentialInfo());
     return;
   }
@@ -179,7 +179,7 @@
                                               origin_);
     base::RecordAction(base::UserMetricsAction("CredentialManager_Autosignin"));
     LogCredentialManagerGetResult(
-        metrics_util::CREDENTIAL_MANAGER_GET_AUTOSIGNIN, mediation_);
+        metrics_util::CredentialManagerGetResult::kAutoSignIn, mediation_);
     delegate_->SendCredential(send_callback_, info);
     return;
   }
@@ -187,13 +187,14 @@
   if (mediation_ == CredentialMediationRequirement::kSilent) {
     metrics_util::CredentialManagerGetResult get_result;
     if (local_results.empty())
-      get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_EMPTY_STORE;
+      get_result = metrics_util::CredentialManagerGetResult::kNoneEmptyStore;
     else if (!can_use_autosignin)
-      get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_MANY_CREDENTIALS;
+      get_result =
+          metrics_util::CredentialManagerGetResult::kNoneManyCredentials;
     else if (local_results[0]->skip_zero_click)
-      get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_SIGNED_OUT;
+      get_result = metrics_util::CredentialManagerGetResult::kNoneSignedOut;
     else
-      get_result = metrics_util::CREDENTIAL_MANAGER_GET_NONE_FIRST_RUN;
+      get_result = metrics_util::CredentialManagerGetResult::kNoneFirstRun;
 
     if (can_use_autosignin) {
       // The user had credentials, but either chose not to share them with the
@@ -218,7 +219,7 @@
 
   if (local_results.empty()) {
     LogCredentialManagerGetResult(
-        metrics_util::CREDENTIAL_MANAGER_GET_NONE_EMPTY_STORE, mediation_);
+        metrics_util::CredentialManagerGetResult::kNoneEmptyStore, mediation_);
     delegate_->SendCredential(send_callback_, CredentialInfo());
     return;
   }
@@ -228,8 +229,8 @@
           base::Bind(
               &CredentialManagerPendingRequestTaskDelegate::SendPasswordForm,
               base::Unretained(delegate_), send_callback_, mediation_))) {
-    LogCredentialManagerGetResult(metrics_util::CREDENTIAL_MANAGER_GET_NONE,
-                                  mediation_);
+    LogCredentialManagerGetResult(
+        metrics_util::CredentialManagerGetResult::kNone, mediation_);
     delegate_->SendCredential(send_callback_, CredentialInfo());
   }
 }
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc
index 7294c3d2..97ee12d6 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.cc
+++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -119,16 +119,15 @@
                                    CredentialMediationRequirement mediation) {
   switch (mediation) {
     case CredentialMediationRequirement::kSilent:
-      base::UmaHistogramEnumeration("PasswordManager.MediationSilent", result,
-                                    CREDENTIAL_MANAGER_GET_COUNT);
+      base::UmaHistogramEnumeration("PasswordManager.MediationSilent", result);
       break;
     case CredentialMediationRequirement::kOptional:
-      base::UmaHistogramEnumeration("PasswordManager.MediationOptional", result,
-                                    CREDENTIAL_MANAGER_GET_COUNT);
+      base::UmaHistogramEnumeration("PasswordManager.MediationOptional",
+                                    result);
       break;
     case CredentialMediationRequirement::kRequired:
-      base::UmaHistogramEnumeration("PasswordManager.MediationRequired", result,
-                                    CREDENTIAL_MANAGER_GET_COUNT);
+      base::UmaHistogramEnumeration("PasswordManager.MediationRequired",
+                                    result);
       break;
   }
 }
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index 42762a6..b4dbf4b3 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -121,26 +121,27 @@
   ACCOUNT_CHOOSER_ACTION_COUNT
 };
 
-enum CredentialManagerGetResult {
+// Metrics: "PasswordManager.Mediation{Silent,Optional,Required}"
+enum class CredentialManagerGetResult {
   // The promise is rejected.
-  CREDENTIAL_MANAGER_GET_REJECTED,
+  kRejected,
   // Auto sign-in is not allowed in the current context.
-  CREDENTIAL_MANAGER_GET_NONE_ZERO_CLICK_OFF,
+  kNoneZeroClickOff,
   // No matching credentials found.
-  CREDENTIAL_MANAGER_GET_NONE_EMPTY_STORE,
+  kNoneEmptyStore,
   // User mediation required due to > 1 matching credentials.
-  CREDENTIAL_MANAGER_GET_NONE_MANY_CREDENTIALS,
+  kNoneManyCredentials,
   // User mediation required due to the signed out state.
-  CREDENTIAL_MANAGER_GET_NONE_SIGNED_OUT,
+  kNoneSignedOut,
   // User mediation required due to pending first run experience dialog.
-  CREDENTIAL_MANAGER_GET_NONE_FIRST_RUN,
+  kNoneFirstRun,
   // Return empty credential for whatever reason.
-  CREDENTIAL_MANAGER_GET_NONE,
+  kNone,
   // Return a credential from the account chooser.
-  CREDENTIAL_MANAGER_GET_ACCOUNT_CHOOSER,
+  kAccountChooser,
   // User is auto signed in.
-  CREDENTIAL_MANAGER_GET_AUTOSIGNIN,
-  CREDENTIAL_MANAGER_GET_COUNT
+  kAutoSignIn,
+  kMaxValue = kAutoSignIn,
 };
 
 // Metrics: "PasswordManager.HttpPasswordMigrationMode"
diff --git a/components/policy/tools/template_writers/policy_template_generator.py b/components/policy/tools/template_writers/policy_template_generator.py
index bedb927..43f38b6 100755
--- a/components/policy/tools/template_writers/policy_template_generator.py
+++ b/components/policy/tools/template_writers/policy_template_generator.py
@@ -22,12 +22,8 @@
 
   def _ImportMessage(self, msg_txt):
     msg_txt = msg_txt.decode('utf-8')
-    # Replace the placeholder of app name.
-    msg_txt = msg_txt.replace('$1', self._config['app_name'])
-    msg_txt = msg_txt.replace('$2', self._config['os_name'])
-    msg_txt = msg_txt.replace('$3', self._config['frame_name'])
-
     lines = msg_txt.split('\n')
+
     # Strip any extra leading spaces, but keep useful indentation:
     min_leading_spaces = min(list(self._IterateLeadingSpaces(lines)) or [0])
     if min_leading_spaces > 0:
diff --git a/components/policy/tools/template_writers/policy_template_generator_unittest.py b/components/policy/tools/template_writers/policy_template_generator_unittest.py
index 9b5fd3e..b6570eb 100755
--- a/components/policy/tools/template_writers/policy_template_generator_unittest.py
+++ b/components/policy/tools/template_writers/policy_template_generator_unittest.py
@@ -257,44 +257,6 @@
 
     self.do_test(policy_defs_mock, LocalMockWriter())
 
-  def testPolicyTexts(self):
-    # Test that GUI messages of policies all get placeholders replaced.
-    policy_data_mock = {
-        'policy_definitions': [
-            {
-                'name': 'Group1',
-                'type': 'group',
-                'desc': '',
-                'caption': '',
-                'policies': ['Policy1'],
-            },
-            {
-                'name': 'Policy1',
-                'caption': '1. app_name -- $1',
-                'label': '2. os_name -- $2',
-                'desc': '3. frame_name -- $3',
-                'type': 'string',
-                'supported_on': []
-            },
-        ]
-    }
-
-    class LocalMockWriter(mock_writer.MockWriter):
-
-      def WritePolicy(self, policy):
-        if policy['name'] == 'Policy1':
-          self.tester.assertEquals(policy['caption'],
-                                   '1. app_name -- _app_name')
-          self.tester.assertEquals(policy['label'], '2. os_name -- _os_name')
-          self.tester.assertEquals(policy['desc'],
-                                   '3. frame_name -- _frame_name')
-        elif policy['name'] == 'Group1':
-          pass
-        else:
-          self.tester.fail()
-
-    self.do_test(policy_data_mock, LocalMockWriter())
-
   def testIntEnumTexts(self):
     # Test that GUI messages are assigned correctly to int-enums
     # (aka dropdown menus).
diff --git a/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py b/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py
index 83bdf789..91c39f43 100755
--- a/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/plist_strings_writer_unittest.py
@@ -26,7 +26,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': '$1 preferen"ces',
+            'text': 'Chromium preferen"ces',
             'desc': 'blah'
           }
         }
@@ -48,7 +48,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': '$1 preferen"ces',
+            'text': 'Chromium preferen"ces',
             'desc': 'blah'
           }
         }
@@ -88,7 +88,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': 'Preferences of $1',
+            'text': 'Preferences of Google Chrome',
             'desc': 'blah'
           }
         }
@@ -131,7 +131,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': 'Preferences of $1',
+            'text': 'Preferences of Chromium',
             'desc': 'blah'
           }
         }
@@ -176,7 +176,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': 'Preferences of $1',
+            'text': 'Preferences of Chromium',
             'desc': 'blah'
           }
         }
@@ -233,7 +233,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': 'Preferences of $1',
+            'text': 'Preferences of Chromium',
             'desc': 'blah'
           }
         }
@@ -286,7 +286,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': '$1 preferences',
+            'text': 'Google Chrome preferences',
             'desc': 'blah'
           }
         }
@@ -340,7 +340,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': '$1 preferences',
+            'text': 'Google Chrome preferences',
             'desc': 'blah'
           }
         }
@@ -383,7 +383,7 @@
         'placeholders': [],
         'messages': {
           'mac_chrome_preferences': {
-            'text': '$1 preferences',
+            'text': 'Google Chrome preferences',
             'desc': 'blah'
           }
         }
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.cc b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
index a38940a..e12ea46 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.cc
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "google_apis/gaia/fake_oauth2_token_service_delegate.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 FakeProfileOAuth2TokenService::FakeProfileOAuth2TokenService(
     PrefService* user_prefs)
@@ -21,7 +20,6 @@
     : ProfileOAuth2TokenService(user_prefs, std::move(delegate)) {
   OverrideAccessTokenManagerForTesting(
       std::make_unique<FakeOAuth2AccessTokenManager>(
-          this /* OAuth2TokenService* */,
           this /* OAuth2AccessTokenManager::Delegate* */));
 }
 
@@ -106,27 +104,6 @@
   GetFakeAccessTokenManager()->CancelRequestsForAccount(account_id);
 }
 
-void FakeProfileOAuth2TokenService::FetchOAuth2Token(
-    OAuth2AccessTokenManager::RequestImpl* request,
-    const CoreAccountId& account_id,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const std::string& client_id,
-    const std::string& client_secret,
-    const OAuth2AccessTokenManager::ScopeSet& scopes) {
-  GetFakeAccessTokenManager()->FetchOAuth2Token(request, account_id,
-                                                url_loader_factory, client_id,
-                                                client_secret, scopes);
-}
-
-void FakeProfileOAuth2TokenService::InvalidateAccessTokenImpl(
-    const CoreAccountId& account_id,
-    const std::string& client_id,
-    const OAuth2AccessTokenManager::ScopeSet& scopes,
-    const std::string& access_token) {
-  GetFakeAccessTokenManager()->InvalidateAccessTokenImpl(account_id, client_id,
-                                                         scopes, access_token);
-}
-
 FakeOAuth2AccessTokenManager*
 FakeProfileOAuth2TokenService::GetFakeAccessTokenManager() {
   return static_cast<FakeOAuth2AccessTokenManager*>(GetAccessTokenManager());
diff --git a/components/signin/core/browser/fake_profile_oauth2_token_service.h b/components/signin/core/browser/fake_profile_oauth2_token_service.h
index 9208162..dd293a3 100644
--- a/components/signin/core/browser/fake_profile_oauth2_token_service.h
+++ b/components/signin/core/browser/fake_profile_oauth2_token_service.h
@@ -13,10 +13,6 @@
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "google_apis/gaia/fake_oauth2_access_token_manager.h"
 
-namespace network {
-class SharedURLLoaderFactory;
-}
-
 // Helper class to simplify writing unittests that depend on an instance of
 // ProfileOAuth2TokenService.
 //
@@ -91,20 +87,6 @@
 
   void CancelRequestsForAccount(const CoreAccountId& account_id) override;
 
-  void FetchOAuth2Token(
-      OAuth2AccessTokenManager::RequestImpl* request,
-      const CoreAccountId& account_id,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const std::string& client_id,
-      const std::string& client_secret,
-      const OAuth2AccessTokenManager::ScopeSet& scopes) override;
-
-  void InvalidateAccessTokenImpl(
-      const CoreAccountId& account_id,
-      const std::string& client_id,
-      const OAuth2AccessTokenManager::ScopeSet& scopes,
-      const std::string& access_token) override;
-
  private:
   FakeOAuth2AccessTokenManager* GetFakeAccessTokenManager();
 
diff --git a/components/signin/ios/browser/device_accounts_provider.h b/components/signin/ios/browser/device_accounts_provider.h
index 522ee3f..6f50089 100644
--- a/components/signin/ios/browser/device_accounts_provider.h
+++ b/components/signin/ios/browser/device_accounts_provider.h
@@ -45,6 +45,7 @@
   struct AccountInfo {
     std::string gaia;
     std::string email;
+    std::string hosted_domain;
   };
 
   using AccessTokenCallback = base::OnceCallback<
diff --git a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
index f53294b..c85ae58 100644
--- a/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
+++ b/components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.mm
@@ -70,6 +70,16 @@
   }
 }
 
+// Converts a DeviceAccountsProvider::AccountInfo to an AccountInfo.
+AccountInfo AccountInfoFromDeviceAccount(
+    const DeviceAccountsProvider::AccountInfo& account) {
+  AccountInfo account_info;
+  account_info.email = account.email;
+  account_info.gaia = account.gaia;
+  account_info.hosted_domain = account.hosted_domain;
+  return account_info;
+}
+
 class SSOAccessTokenFetcher : public OAuth2AccessTokenFetcher {
  public:
   SSOAccessTokenFetcher(OAuth2AccessTokenConsumer* consumer,
@@ -206,7 +216,7 @@
     // a fetch access token operation when it receives a
     // |OnRefreshTokenAvailable| notification.
     std::string account_id = account_tracker_service_->SeedAccountInfo(
-        new_account.gaia, new_account.email);
+        AccountInfoFromDeviceAccount(new_account));
     new_account_ids.insert(account_id);
   }
 
diff --git a/components/signin/public/identity_manager/identity_manager_builder.h b/components/signin/public/identity_manager/identity_manager_builder.h
index c5792e0..2c42442 100644
--- a/components/signin/public/identity_manager/identity_manager_builder.h
+++ b/components/signin/public/identity_manager/identity_manager_builder.h
@@ -16,6 +16,10 @@
 class ImageDecoder;
 }
 
+namespace network {
+class NetworkConnectionTracker;
+}
+
 namespace signin {
 enum class AccountConsistencyMethod;
 }
@@ -31,6 +35,7 @@
   std::unique_ptr<AccountTrackerService> account_tracker_service;
   std::unique_ptr<image_fetcher::ImageDecoder> image_decoder;
   PrefService* local_state;
+  network::NetworkConnectionTracker* network_connection_tracker;
   PrefService* pref_service;
   SigninClient* signin_client;
   std::unique_ptr<ProfileOAuth2TokenService> token_service;
diff --git a/components/signin/public/identity_manager/identity_manager_unittest.cc b/components/signin/public/identity_manager/identity_manager_unittest.cc
index b13fe55e..560ceef 100644
--- a/components/signin/public/identity_manager/identity_manager_unittest.cc
+++ b/components/signin/public/identity_manager/identity_manager_unittest.cc
@@ -75,9 +75,8 @@
 class CustomFakeOAuth2AccessTokenManager : public FakeOAuth2AccessTokenManager {
  public:
   CustomFakeOAuth2AccessTokenManager(
-      OAuth2TokenService* token_service,
       OAuth2AccessTokenManager::Delegate* delegate)
-      : FakeOAuth2AccessTokenManager(token_service, delegate) {}
+      : FakeOAuth2AccessTokenManager(delegate) {}
 
   void set_on_access_token_invalidated_info(
       CoreAccountId expected_account_id_to_invalidate,
@@ -125,7 +124,6 @@
       : FakeProfileOAuth2TokenService(user_prefs) {
     OverrideAccessTokenManagerForTesting(
         std::make_unique<CustomFakeOAuth2AccessTokenManager>(
-            this /* OAuth2TokenService* */,
             this /* OAuth2AccessTokenManager::Delegate* */));
   }
 
@@ -140,15 +138,6 @@
   }
 
  private:
-  // OAuth2TokenService:
-  void InvalidateAccessTokenImpl(const CoreAccountId& account_id,
-                                 const std::string& client_id,
-                                 const ScopeSet& scopes,
-                                 const std::string& access_token) override {
-    GetCustomAccessTokenManager()->InvalidateAccessTokenImpl(
-        account_id, client_id, scopes, access_token);
-  }
-
   CustomFakeOAuth2AccessTokenManager* GetCustomAccessTokenManager() {
     return static_cast<CustomFakeOAuth2AccessTokenManager*>(
         GetAccessTokenManager());
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 5b00710..c7b8d05 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2082,6 +2082,10 @@
     defines += [ "ENABLE_PROTECTED_MEDIA_IDENTIFIER_PERMISSION" ]
   }
 
+  if (is_chromecast) {
+    defines += [ "IS_CHROMECAST" ]
+  }
+
   if (is_chromecast && is_linux) {
     sources += [
       "tracing/cast_tracing_agent.cc",
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index b34fbbdc..544c0eca 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1946,6 +1946,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("video.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityNodeChangedCrashInEditableText) {
+  RunHtmlTest(FILE_PATH_LITERAL("node-changed-crash-in-editable-text.html"));
+}
+
 // TODO(crbug.com/916003): Fix race condition.
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        DISABLED_AccessibilityNoSourceVideo) {
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index f6de22c..3b44a73 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -1046,9 +1046,10 @@
   EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
 }
 
-// Tests an ignored navigation when there is a pending new navigation.
-// This will happen if the user enters a URL, but before that commits, the
-// current blank page reloads.  See http://crbug.com/77507.
+// Verify that a direct commit message from the renderer properly cancels a
+// pending new navigation. This will happen if the user enters a URL, but
+// before that commits, the current blank page reloads.
+// Original bug: http://crbug.com/77507.
 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
   NavigationControllerImpl& controller = controller_impl();
 
@@ -1063,8 +1064,9 @@
 
   // Now make a pending new navigation.
   const GURL kNewURL("http://eh");
-  controller.LoadURL(
-      kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
+  auto navigation =
+      NavigationSimulator::CreateBrowserInitiated(kNewURL, contents());
+  navigation->Start();
   EXPECT_EQ(0U, navigation_entry_changed_counter_);
   EXPECT_EQ(0U, navigation_list_pruned_counter_);
   EXPECT_EQ(-1, controller.GetPendingEntryIndex());
@@ -1072,10 +1074,8 @@
   EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
   EXPECT_EQ(1, delegate->navigation_state_change_count());
 
-  // Before that commits, a document.write and location.reload can cause the
-  // renderer to send a FrameNavigate with nav_entry_id 0.
-  // PlzNavigate: this will stop the old navigation and start a new one.
-  main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL, true);
+  // Certain rare cases can make a direct DidCommitProvisionalLoad call without
+  // going to the browser. Renderer reload of an about:blank is such a case.
   main_test_rfh()->SendNavigate(0, false, kExistingURL);
 
   // This should clear the pending entry and notify of a navigation state
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 7437f47..ee3bf9e 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -236,9 +236,9 @@
 // The next value to use for the accessibility reset token.
 int g_next_accessibility_reset_token = 1;
 
-#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA) || defined(IS_CHROMECAST)
 // Whether to allow injecting javascript into any kind of frame, for Android
-// WebView and Fuchsia web.ContextProvider.
+// WebView, Fuchsia web.ContextProvider and CastOS content shell.
 bool g_allow_injecting_javascript = false;
 #endif
 
@@ -781,12 +781,12 @@
   return RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
 }
 
-#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA) || defined(IS_CHROMECAST)
 // static
 void RenderFrameHost::AllowInjectingJavaScript() {
   g_allow_injecting_javascript = true;
 }
-#endif  // defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#endif  // defined(OS_ANDROID) || defined(OS_FUCHSIA) || defined(IS_CHROMECAST)
 
 // static
 RenderFrameHostImpl* RenderFrameHostImpl::FromID(int process_id,
@@ -5796,7 +5796,7 @@
 }
 
 bool RenderFrameHostImpl::CanExecuteJavaScript() {
-#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA) || defined(IS_CHROMECAST)
   if (g_allow_injecting_javascript)
     return true;
 #endif
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index b9bf9cc..32e3064 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -860,11 +860,6 @@
 
   mojo::AssociatedBinding<blink::mojom::ServiceWorkerHost> binding_;
 
-  // The number of fetch event responses that the service worker is streaming to
-  // the browser process. We try to not stop the service worker while there is
-  // an inflight response.
-  int inflight_stream_response_count_ = 0;
-
   // Set to true if the worker has no inflight events and the idle timer has
   // been triggered. Set back to false if another event starts since the worker
   // is no longer idle.
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index ed3c851f..21d0df1 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -364,7 +364,9 @@
       // Get the direct network factory from |loader_factory_getter|. When
       // NetworkService is enabled, it returns a factory that doesn't support
       // reconnection to the network service after a crash, but it's OK since
-      // it's used for a single shared worker startup.
+      // it's used only for a single request to fetch the worker's main script
+      // during startup. If the network service crashes, worker startup should
+      // simply fail.
       network::mojom::URLLoaderFactoryPtr network_factory_ptr;
       loader_factory_getter->CloneNetworkFactory(
           mojo::MakeRequest(&network_factory_ptr));
@@ -390,16 +392,20 @@
       &ServiceWorkerContextWrapper::resource_context, service_worker_context);
 
   // NetworkService (PlzWorker):
-  // Start loading a shared worker main script.
+  // Start loading a web worker main script.
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     // TODO(nhiroki): Figure out what we should do in |wc_getter| for loading
-    // shared worker's main script.
+    // web worker's main script. Returning the WebContents of the closest
+    // ancestor's frame is a possible option, but it doesn't work when a shared
+    // worker creates a dedicated worker after the closest ancestor's frame is
+    // gone. The frame tree node ID has the same issue.
     base::RepeatingCallback<WebContents*()> wc_getter =
         base::BindRepeating([]() -> WebContents* { return nullptr; });
     std::vector<std::unique_ptr<URLLoaderThrottle>> throttles =
         GetContentClient()->browser()->CreateURLLoaderThrottlesOnIO(
             *resource_request, resource_context, wc_getter,
-            nullptr /* navigation_ui_data */, -1 /* frame_tree_node_id */);
+            nullptr /* navigation_ui_data */,
+            RenderFrameHost::kNoFrameTreeNodeId);
 
     WorkerScriptFetcher::CreateAndStart(
         std::make_unique<WorkerScriptLoaderFactory>(
@@ -454,8 +460,8 @@
   // NetworkService (PlzWorker):
   // Prepare the controller service worker info to pass to the renderer. This is
   // only provided if NetworkService is enabled. In the non-NetworkService case,
-  // the controller is sent in SetController IPCs during the request for the
-  // shared worker script.
+  // the controller is sent in SetController IPCs during the request for the web
+  // worker script.
   blink::mojom::ControllerServiceWorkerInfoPtr controller;
   base::WeakPtr<ServiceWorkerObjectHost> controller_service_worker_object_host;
   if (subresource_loader_params &&
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index b422bbf7..62f34c7 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1158,8 +1158,17 @@
   // Returns the RapporService from the browser process.
   virtual ::rappor::RapporService* GetRapporService();
 
-  // Allows the embedder to register one or more URLLoaderThrottles for a
-  // navigation request.
+  // Allows the embedder to register one or more URLLoaderThrottles for one of
+  // the following requests:
+  // 1) A navigation request.
+  // 2) A request for a dedicated worker's main script (when PlzDedicatedWorker
+  //    is enabled).
+  // 3) A request for a shared worker's main script.
+  //
+  // For a request for worker's main script, |wc_getter|, |navigation_ui_data|,
+  // and |frame_tree_node_id| take a callback returning a nullptr, nullptr, and
+  // RenderFrameHost::kNoFrameTreeNodeId respectively.
+  //
   // This is called both when the network service is enabled and disabled.
   // This is called on the IO thread.
   virtual std::vector<std::unique_ptr<URLLoaderThrottle>>
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 6e92678d..36149b2 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -74,10 +74,10 @@
   // Returns nullptr if the IDs do not correspond to a live RenderFrameHost.
   static RenderFrameHost* FromID(int render_process_id, int render_frame_id);
 
-#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA) || defined(IS_CHROMECAST)
   // Globally allows for injecting JavaScript into the main world. This feature
-  // is present only to support Android WebView and Fuchsia web.Contexts, and
-  // must not be used in other configurations.
+  // is present only to support Android WebView, Fuchsia web.Contexts, and
+  // CastOS content shell. It must not be used in other configurations.
   static void AllowInjectingJavaScript();
 #endif
 
diff --git a/content/public/renderer/BUILD.gn b/content/public/renderer/BUILD.gn
index 1cdc546b..3adfa9b 100644
--- a/content/public/renderer/BUILD.gn
+++ b/content/public/renderer/BUILD.gn
@@ -30,7 +30,6 @@
   visibility = [ "//content/*" ]
 
   sources = [
-    "associated_resource_fetcher.h",
     "browser_plugin_delegate.cc",
     "browser_plugin_delegate.h",
     "chrome_object_extensions_utils.cc",
diff --git a/content/public/renderer/associated_resource_fetcher.h b/content/public/renderer/associated_resource_fetcher.h
deleted file mode 100644
index a7da31c..0000000
--- a/content/public/renderer/associated_resource_fetcher.h
+++ /dev/null
@@ -1,73 +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.
-
-#ifndef CONTENT_PUBLIC_RENDERER_ASSOCIATED_RESOURCE_FETCHER_H_
-#define CONTENT_PUBLIC_RENDERER_ASSOCIATED_RESOURCE_FETCHER_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "content/common/content_export.h"
-#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
-#include "third_party/blink/public/platform/web_url_request.h"
-
-class GURL;
-
-namespace blink {
-class WebLocalFrame;
-class WebURLResponse;
-struct WebAssociatedURLLoaderOptions;
-}
-
-namespace content {
-
-// Interface to download resources asynchronously.
-class CONTENT_EXPORT AssociatedResourceFetcher {
- public:
-  virtual ~AssociatedResourceFetcher() {}
-
-  // This will be called asynchronously after the URL has been fetched,
-  // successfully or not.  If there is a failure, response and data will both be
-  // empty.  |response| and |data| are both valid until the URLFetcher instance
-  // is destroyed.
-  using StartCallback =
-      base::OnceCallback<void(const blink::WebURLResponse& response,
-                              const std::string& data)>;
-
-  // Creates a AssociatedResourceFetcher for the specified resource.  Caller
-  // takes
-  // ownership of the returned object.  Deleting the AssociatedResourceFetcher
-  // will cancel
-  // the request, and the callback will never be run.
-  static AssociatedResourceFetcher* Create(const GURL& url);
-
-  virtual void SetSkipServiceWorker(bool skip_service_worker) = 0;
-  virtual void SetCacheMode(blink::mojom::FetchCacheMode mode) = 0;
-
-  // Associate the corresponding WebURLLoaderOptions to the loader. Must be
-  // called before Start. Used if the LoaderType is FRAME_ASSOCIATED_LOADER.
-  virtual void SetLoaderOptions(
-      const blink::WebAssociatedURLLoaderOptions& options) = 0;
-
-  // Starts the request using the specified frame.  Calls |callback| when
-  // done.
-  //
-  // |fetch_request_mode| is the mode to use. See
-  // https://fetch.spec.whatwg.org/#concept-request-mode.
-  //
-  // |fetch_credentials_mode| is the credentials mode to use. See
-  // https://fetch.spec.whatwg.org/#concept-request-credentials-mode
-  virtual void Start(blink::WebLocalFrame* frame,
-                     blink::mojom::RequestContextType request_context,
-                     network::mojom::RequestMode request_mode,
-                     network::mojom::CredentialsMode credentials_mode,
-                     StartCallback callback) = 0;
-
-  // Manually cancel the request.
-  virtual void Cancel() = 0;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_RENDERER_ASSOCIATED_RESOURCE_FETCHER_H_
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 9125eae..15d4d297 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -206,8 +206,6 @@
     "media/stream/apply_constraints_processor.h",
     "media/stream/audio_service_audio_processor_proxy.cc",
     "media/stream/audio_service_audio_processor_proxy.h",
-    "media/stream/local_media_stream_audio_source.cc",
-    "media/stream/local_media_stream_audio_source.h",
     "media/stream/media_stream_audio_processor.cc",
     "media/stream/media_stream_audio_processor.h",
     "media/stream/media_stream_center.cc",
diff --git a/content/renderer/fetchers/associated_resource_fetcher_impl.cc b/content/renderer/fetchers/associated_resource_fetcher_impl.cc
index 0a53fe13..98eb954 100644
--- a/content/renderer/fetchers/associated_resource_fetcher_impl.cc
+++ b/content/renderer/fetchers/associated_resource_fetcher_impl.cc
@@ -14,8 +14,6 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_error.h"
-#include "third_party/blink/public/platform/web_url_request.h"
-#include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/blink.h"
 #include "third_party/blink/public/web/web_associated_url_loader.h"
 #include "third_party/blink/public/web/web_associated_url_loader_client.h"
@@ -26,7 +24,8 @@
 namespace content {
 
 // static
-AssociatedResourceFetcher* AssociatedResourceFetcher::Create(const GURL& url) {
+AssociatedResourceFetcherImpl* AssociatedResourceFetcherImpl::Create(
+    const GURL& url) {
   return new AssociatedResourceFetcherImpl(url);
 }
 
diff --git a/content/renderer/fetchers/associated_resource_fetcher_impl.h b/content/renderer/fetchers/associated_resource_fetcher_impl.h
index d7846a7b..31b6eea 100644
--- a/content/renderer/fetchers/associated_resource_fetcher_impl.h
+++ b/content/renderer/fetchers/associated_resource_fetcher_impl.h
@@ -11,8 +11,9 @@
 #include "base/callback.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "content/public/renderer/associated_resource_fetcher.h"
+#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/web_associated_url_loader_options.h"
 
 class GURL;
@@ -24,30 +25,54 @@
 
 namespace content {
 
-class AssociatedResourceFetcherImpl : public AssociatedResourceFetcher {
+// Interface to download resources asynchronously.
+class AssociatedResourceFetcherImpl {
  public:
-  // AssociatedResourceFetcher implementation:
-  void SetSkipServiceWorker(bool skip_service_worker) override;
-  void SetCacheMode(blink::mojom::FetchCacheMode mode) override;
-  void SetLoaderOptions(
-      const blink::WebAssociatedURLLoaderOptions& options) override;
+  // This will be called asynchronously after the URL has been fetched,
+  // successfully or not.  If there is a failure, response and data will both be
+  // empty.  |response| and |data| are both valid until the URLFetcher instance
+  // is destroyed.
+  using StartCallback =
+      base::OnceCallback<void(const blink::WebURLResponse& response,
+                              const std::string& data)>;
+
+  // Creates a AssociatedResourceFetcherImpl for the specified resource.
+  // Caller takes ownership of the returned object.
+  // Deleting the AssociatedResourceFetcherImpl will cancel
+  // the request, and the callback will never be run.
+  static AssociatedResourceFetcherImpl* Create(const GURL& url);
+
+  ~AssociatedResourceFetcherImpl();
+
+  void SetSkipServiceWorker(bool skip_service_worker);
+  void SetCacheMode(blink::mojom::FetchCacheMode mode);
+
+  // Associate the corresponding WebURLLoaderOptions to the loader. Must be
+  // called before Start. Used if the LoaderType is FRAME_ASSOCIATED_LOADER.
+  void SetLoaderOptions(const blink::WebAssociatedURLLoaderOptions& options);
+
+  // Starts the request using the specified frame.  Calls |callback| when
+  // done.
+  //
+  // |fetch_request_mode| is the mode to use. See
+  // https://fetch.spec.whatwg.org/#concept-request-mode.
+  //
+  // |fetch_credentials_mode| is the credentials mode to use. See
+  // https://fetch.spec.whatwg.org/#concept-request-credentials-mode
   void Start(blink::WebLocalFrame* frame,
              blink::mojom::RequestContextType request_context,
              network::mojom::RequestMode request_mode,
              network::mojom::CredentialsMode credentials_mode,
-             StartCallback callback) override;
+             StartCallback callback);
+
+  // Manually cancel the request.
+  void Cancel();
 
  private:
-  friend class AssociatedResourceFetcher;
-
   class ClientImpl;
 
   explicit AssociatedResourceFetcherImpl(const GURL& url);
 
-  ~AssociatedResourceFetcherImpl() override;
-
-  void Cancel() override;
-
   std::unique_ptr<blink::WebAssociatedURLLoader> loader_;
   std::unique_ptr<ClientImpl> client_;
 
diff --git a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
index 7cefd22b..110006d 100644
--- a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
+++ b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.cc
@@ -9,7 +9,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "content/child/image_decoder.h"
-#include "content/public/renderer/associated_resource_fetcher.h"
+#include "content/renderer/fetchers/associated_resource_fetcher_impl.h"
 #include "third_party/blink/public/platform/web_security_origin.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/web/web_associated_url_loader_options.h"
@@ -36,7 +36,7 @@
       id_(id),
       http_status_code_(0),
       image_url_(image_url) {
-  fetcher_.reset(AssociatedResourceFetcher::Create(image_url));
+  fetcher_.reset(AssociatedResourceFetcherImpl::Create(image_url));
 
   WebAssociatedURLLoaderOptions options;
   fetcher_->SetLoaderOptions(options);
diff --git a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
index d72a651..8436f24 100644
--- a/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
+++ b/content/renderer/fetchers/multi_resolution_image_resource_fetcher.h
@@ -24,7 +24,7 @@
 
 namespace content {
 
-class AssociatedResourceFetcher;
+class AssociatedResourceFetcherImpl;
 
 // A resource fetcher that returns all (differently-sized) frames in
 // an image. Useful for favicons.
@@ -72,7 +72,7 @@
   const GURL image_url_;
 
   // Does the actual download.
-  std::unique_ptr<AssociatedResourceFetcher> fetcher_;
+  std::unique_ptr<AssociatedResourceFetcherImpl> fetcher_;
 
   DISALLOW_COPY_AND_ASSIGN(MultiResolutionImageResourceFetcher);
 };
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
index 235d2a3..5e7bd43 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
@@ -14,7 +14,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "build/build_config.h"
-#include "content/renderer/media/stream/local_media_stream_audio_source.h"
 #include "content/renderer/media/stream/processed_local_audio_source.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "media/base/audio_parameters.h"
@@ -25,6 +24,7 @@
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_media_constraints.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/mediastream/local_media_stream_audio_source.h"
 #include "third_party/blink/public/web/modules/mediastream/mock_constraint_factory.h"
 
 namespace content {
@@ -126,7 +126,8 @@
         media::AudioParameters::PlatformEffectsMask::NO_EFFECTS);
   }
 
-  std::unique_ptr<LocalMediaStreamAudioSource> GetLocalMediaStreamAudioSource(
+  std::unique_ptr<blink::LocalMediaStreamAudioSource>
+  GetLocalMediaStreamAudioSource(
       bool enable_system_echo_canceller,
       bool disable_local_echo,
       bool render_to_associated_sink,
@@ -145,8 +146,9 @@
     if (render_to_associated_sink)
       device.matched_output_device_id = std::string("some_device_id");
 
-    return std::make_unique<LocalMediaStreamAudioSource>(
-        -1, device, requested_buffer_size, disable_local_echo,
+    return std::make_unique<blink::LocalMediaStreamAudioSource>(
+        /*blink::WebLocalFrame=*/nullptr, device, requested_buffer_size,
+        disable_local_echo,
         blink::WebPlatformMediaStreamSource::ConstraintsCallback(),
         blink::scheduler::GetSingleThreadTaskRunnerForTesting());
   }
@@ -391,7 +393,7 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
 
-    std::unique_ptr<LocalMediaStreamAudioSource> local_source =
+    std::unique_ptr<blink::LocalMediaStreamAudioSource> local_source =
         GetLocalMediaStreamAudioSource(
             false /* enable_system_echo_canceller */,
             false /* disable_local_echo */,
@@ -770,7 +772,7 @@
   if (!IsDeviceCapture())
     return;
 
-  std::unique_ptr<LocalMediaStreamAudioSource> source =
+  std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
       GetLocalMediaStreamAudioSource(false /* enable_system_echo_canceller */,
                                      false /* disable_local_echo */,
                                      false /* render_to_associated_sink */);
@@ -887,7 +889,7 @@
   if (!IsDeviceCapture())
     return;
 
-  std::unique_ptr<LocalMediaStreamAudioSource> source =
+  std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
       GetLocalMediaStreamAudioSource(false /* enable_system_echo_canceller */,
                                      false /* disable_local_echo */,
                                      false /* render_to_associated_sink */);
@@ -1044,7 +1046,7 @@
   if (!IsDeviceCapture())
     return;
 
-  std::unique_ptr<LocalMediaStreamAudioSource> source =
+  std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
       GetLocalMediaStreamAudioSource(false /* enable_system_echo_canceller */,
                                      false /* disable_local_echo */,
                                      false /* render_to_associated_sink */);
@@ -1559,7 +1561,7 @@
 // sources that have no audio processing.
 TEST_P(MediaStreamConstraintsUtilAudioTest, SourceWithNoAudioProcessing) {
   for (bool enable_properties : {true, false}) {
-    std::unique_ptr<LocalMediaStreamAudioSource> source =
+    std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
         GetLocalMediaStreamAudioSource(
             enable_properties /* enable_system_echo_canceller */,
             enable_properties /* disable_local_echo */,
@@ -1827,7 +1829,7 @@
 }
 
 TEST_P(MediaStreamConstraintsUtilAudioTest, ExperimetanlEcWithSource) {
-  std::unique_ptr<LocalMediaStreamAudioSource> source =
+  std::unique_ptr<blink::LocalMediaStreamAudioSource> source =
       GetLocalMediaStreamAudioSource(
           false /* enable_system_echo_canceller */,
           false /* disable_local_echo */, false /* render_to_associated_sink */,
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index c7543bfb..ff76e6f 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -19,7 +19,6 @@
 #include "base/task_runner.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/renderer/media/stream/local_media_stream_audio_source.h"
 #include "content/renderer/media/stream/media_stream_audio_processor.h"
 #include "content/renderer/media/stream/media_stream_constraints_util_audio.h"
 #include "content/renderer/media/stream/media_stream_device_observer.h"
@@ -42,6 +41,7 @@
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/mediastream/local_media_stream_audio_source.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_video_content.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_video_device.h"
@@ -1052,8 +1052,8 @@
   if (blink::IsScreenCaptureMediaType(device.type) ||
       !MediaStreamAudioProcessor::WouldModifyAudio(
           audio_processing_properties)) {
-    return std::make_unique<LocalMediaStreamAudioSource>(
-        render_frame_->GetRoutingID(), device,
+    return std::make_unique<blink::LocalMediaStreamAudioSource>(
+        render_frame_->GetWebFrame(), device,
         base::OptionalOrNullptr(current_request_info_->audio_capture_settings()
                                     .requested_buffer_size()),
         stream_controls->disable_local_echo, std::move(source_ready),
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index e722b08..461008f4 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -533,6 +533,16 @@
 
 //------------------------------------------------------------------------------
 
+scoped_refptr<media::AudioCapturerSource>
+RendererBlinkPlatformImpl::NewAudioCapturerSource(
+    blink::WebLocalFrame* web_frame,
+    const media::AudioSourceParameters& params) {
+  return AudioDeviceFactory::NewAudioCapturerSource(
+      RenderFrame::GetRoutingIdForWebFrame(web_frame), params);
+}
+
+//------------------------------------------------------------------------------
+
 std::unique_ptr<WebRTCPeerConnectionHandler>
 RendererBlinkPlatformImpl::CreateRTCPeerConnectionHandler(
     WebRTCPeerConnectionHandlerClient* client,
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 07344fa3..0fbb6ee6 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -124,6 +124,9 @@
                            size_t data_size) override;
 
   blink::WebBlobRegistry* GetBlobRegistry() override;
+  scoped_refptr<media::AudioCapturerSource> NewAudioCapturerSource(
+      blink::WebLocalFrame* web_frame,
+      const media::AudioSourceParameters& params) override;
   std::unique_ptr<blink::WebRTCPeerConnectionHandler>
   CreateRTCPeerConnectionHandler(
       blink::WebRTCPeerConnectionHandlerClient* client,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 02c9ea1..7821e7e 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1875,7 +1875,6 @@
     "../renderer/media/render_media_log_unittest.cc",
     "../renderer/media/renderer_webaudiodevice_impl_unittest.cc",
     "../renderer/media/stream/media_stream_audio_processor_unittest.cc",
-    "../renderer/media/stream/media_stream_audio_unittest.cc",
     "../renderer/media/stream/media_stream_constraints_util_audio_unittest.cc",
     "../renderer/media/stream/media_stream_device_observer_unittest.cc",
     "../renderer/media/stream/media_stream_video_source_unittest.cc",
@@ -1990,8 +1989,8 @@
     "//content/browser/background_fetch:background_fetch_proto",
     "//content/browser/cache_storage:cache_storage_proto",
     "//content/browser/devtools:devtools_background_services_proto",
-    "//content/browser/devtools:inspector_protocol_encoding_test",
     "//content/browser/devtools:inspector_protocol_bindings_test",
+    "//content/browser/devtools:inspector_protocol_encoding_test",
     "//content/browser/dom_storage:local_storage_proto",
     "//content/browser/indexed_db/scopes:scopes_metadata_proto",
     "//content/browser/notifications:notification_proto",
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-android.txt b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-android.txt
new file mode 100644
index 0000000..a18eaf4
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-android.txt
@@ -0,0 +1,6 @@
+android.webkit.WebView focusable focused scrollable
+++android.widget.EditText clickable editable_text focusable multiline hint='Done'
+++++android.view.View
+++++android.view.View
+++++++android.view.View
+++++android.view.View
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-auralinux.txt b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-auralinux.txt
new file mode 100644
index 0000000..5971b57
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-auralinux.txt
@@ -0,0 +1,6 @@
+[document web]
+++[section] name='Done' selectable-text
+++++[section]
+++++[section]
+++++++[section]
+++++[section]
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-blink.txt b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-blink.txt
new file mode 100644
index 0000000..12a7d67
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-blink.txt
@@ -0,0 +1,4 @@
+rootWebArea
+++genericContainer multiline name='Done'
+++++genericContainer
+++++genericContainer
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-mac.txt b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-mac.txt
new file mode 100644
index 0000000..0adbb83
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-mac.txt
@@ -0,0 +1,6 @@
+AXWebArea
+++AXTextArea AXDescription='Done'
+++++AXGroup
+++++AXGroup
+++++++AXGroup
+++++AXGroup
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-uia-win.txt b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-uia-win.txt
new file mode 100644
index 0000000..10ae582
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-uia-win.txt
@@ -0,0 +1,4 @@
+document
+++group Name='Done'
+++++group
+++++group
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-win.txt b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-win.txt
new file mode 100644
index 0000000..d0eaeac
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text-expected-win.txt
@@ -0,0 +1,4 @@
+ROLE_SYSTEM_DOCUMENT FOCUSABLE
+++IA2_ROLE_SECTION name='Done' FOCUSABLE IA2_STATE_MULTI_LINE
+++++IA2_ROLE_SECTION
+++++IA2_ROLE_SECTION
diff --git a/content/test/data/accessibility/html/node-changed-crash-in-editable-text.html b/content/test/data/accessibility/html/node-changed-crash-in-editable-text.html
new file mode 100644
index 0000000..e662b0a7
--- /dev/null
+++ b/content/test/data/accessibility/html/node-changed-crash-in-editable-text.html
@@ -0,0 +1,41 @@
+<!--
+@WAIT-FOR:Done
+@NO_CHILDREN_DUMP
+This is a regression test for a bug that caused a crash when a node inside 
+editable text was modified and had a sibling removed in the same update. The 
+actual test output is not interesting, all that matters is that it correctly 
+waits for "Done" to appear in the accessibility tree and doesn't generate an 
+invalid tree update in the process.
+-->
+<script>
+  function go() {
+
+    // To reproduce the testcase we need to dirty an element and cause its 
+    // parent (who is observing OnNodeChanged) to have an incorrect cached child
+    // count. This needs to be done in a single ax_tree update.
+
+    // dirty child, this needs to be sent in the same update as the node removal
+    document.getElementById('child').setAttribute("max", "bar");
+    document.getElementById('child').removeChild(document.getElementById('grandChild'));
+
+    // "remove" element. Deleting the element using removeChild doesn't create 
+    // the same failing update pattern
+    document.getElementById('to-be-removed').outerText = "";
+
+    // This needs to be in a seperate update, test only, not related to regression
+    setTimeout(() => {
+      document.body.setAttribute("aria-label", "Done");
+    }, 20);
+  }
+</script>
+
+<body onload="go();" contentEditable="true" aria-label="@NO_CHILDREN_DUMP">
+  <div id='to-be-removed'></div>
+  <div id='child' aria-hidden="false">
+    <div id='grandChild' aria-hidden="false"></div>
+  </div>
+
+  <div aria-hidden="false">
+    <!-- <audio src='foo'></audio> -->
+  </div>
+</body>
\ No newline at end of file
diff --git a/docs/linux_build_instructions.md b/docs/linux_build_instructions.md
index cdbcc1f..831f362a 100644
--- a/docs/linux_build_instructions.md
+++ b/docs/linux_build_instructions.md
@@ -17,7 +17,7 @@
 *   At least 100GB of free disk space.
 *   You must have Git and Python v2 installed already.
 
-Most development is done on Ubuntu (currently 14.04, Trusty Tahr). There are
+Most development is done on Ubuntu (currently 16.04, Xenial Xerus). There are
 some instructions for other distros below, but they are mostly unsupported.
 
 ## Install `depot_tools`
diff --git a/docs/windows_build_instructions.md b/docs/windows_build_instructions.md
index f7741e62..3490c0a 100644
--- a/docs/windows_build_instructions.md
+++ b/docs/windows_build_instructions.md
@@ -50,7 +50,7 @@
 --includeRecommended
 ```
 
-You must have the version 10.0.17763 or higher Windows 10 SDK installed. This
+You must have the version 10.0.18362 or higher Windows 10 SDK installed. This
 can be installed separately or by checking the appropriate box in the Visual
 Studio Installer.
 
diff --git a/google_apis/gaia/fake_oauth2_access_token_manager.cc b/google_apis/gaia/fake_oauth2_access_token_manager.cc
index 65df882..4e48c808 100644
--- a/google_apis/gaia/fake_oauth2_access_token_manager.cc
+++ b/google_apis/gaia/fake_oauth2_access_token_manager.cc
@@ -20,9 +20,8 @@
 FakeOAuth2AccessTokenManager::PendingRequest::~PendingRequest() {}
 
 FakeOAuth2AccessTokenManager::FakeOAuth2AccessTokenManager(
-    OAuth2TokenService* token_service,
     OAuth2AccessTokenManager::Delegate* delegate)
-    : OAuth2AccessTokenManager(token_service, delegate),
+    : OAuth2AccessTokenManager(delegate),
       auto_post_fetch_response_on_message_loop_(false),
       weak_ptr_factory_(this) {}
 
diff --git a/google_apis/gaia/fake_oauth2_access_token_manager.h b/google_apis/gaia/fake_oauth2_access_token_manager.h
index fed88c1..d9efa6be 100644
--- a/google_apis/gaia/fake_oauth2_access_token_manager.h
+++ b/google_apis/gaia/fake_oauth2_access_token_manager.h
@@ -32,7 +32,6 @@
   };
 
   explicit FakeOAuth2AccessTokenManager(
-      OAuth2TokenService* token_service,
       OAuth2AccessTokenManager::Delegate* delegate);
   ~FakeOAuth2AccessTokenManager() override;
 
diff --git a/google_apis/gaia/fake_oauth2_token_service.cc b/google_apis/gaia/fake_oauth2_token_service.cc
index 988f5e3..df03166 100644
--- a/google_apis/gaia/fake_oauth2_token_service.cc
+++ b/google_apis/gaia/fake_oauth2_token_service.cc
@@ -10,25 +10,12 @@
     : OAuth2TokenService(std::make_unique<FakeOAuth2TokenServiceDelegate>()) {
   OverrideAccessTokenManagerForTesting(
       std::make_unique<FakeOAuth2AccessTokenManager>(
-          this /* OAuth2TokenService* */,
           this /* OAuth2AccessTokenManager::Delegate* */));
 }
 
 FakeOAuth2TokenService::~FakeOAuth2TokenService() {
 }
 
-void FakeOAuth2TokenService::FetchOAuth2Token(
-    OAuth2AccessTokenManager::RequestImpl* request,
-    const CoreAccountId& account_id,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const std::string& client_id,
-    const std::string& client_secret,
-    const OAuth2AccessTokenManager::ScopeSet& scopes) {
-  GetFakeAccessTokenManager()->FetchOAuth2Token(request, account_id,
-                                                url_loader_factory, client_id,
-                                                client_secret, scopes);
-}
-
 void FakeOAuth2TokenService::AddAccount(const CoreAccountId& account_id) {
   GetDelegate()->UpdateCredentials(account_id, "fake_refresh_token");
 }
diff --git a/google_apis/gaia/fake_oauth2_token_service.h b/google_apis/gaia/fake_oauth2_token_service.h
index c261e46..453058a 100644
--- a/google_apis/gaia/fake_oauth2_token_service.h
+++ b/google_apis/gaia/fake_oauth2_token_service.h
@@ -11,10 +11,6 @@
 #include "google_apis/gaia/fake_oauth2_token_service_delegate.h"
 #include "google_apis/gaia/oauth2_token_service.h"
 
-namespace network {
-class SharedURLLoaderFactory;
-}
-
 // Do-nothing implementation of OAuth2TokenService.
 class FakeOAuth2TokenService : public OAuth2TokenService {
  public:
@@ -38,16 +34,6 @@
 
   FakeOAuth2TokenServiceDelegate* GetFakeOAuth2TokenServiceDelegate();
 
- protected:
-  // OAuth2TokenService overrides.
-  void FetchOAuth2Token(
-      OAuth2AccessTokenManager::RequestImpl* request,
-      const CoreAccountId& account_id,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const std::string& client_id,
-      const std::string& client_secret,
-      const OAuth2AccessTokenManager::ScopeSet& scopes) override;
-
  private:
   FakeOAuth2AccessTokenManager* GetFakeAccessTokenManager();
 
diff --git a/google_apis/gaia/oauth2_access_token_manager.cc b/google_apis/gaia/oauth2_access_token_manager.cc
index f0964c1..91da5a6 100644
--- a/google_apis/gaia/oauth2_access_token_manager.cc
+++ b/google_apis/gaia/oauth2_access_token_manager.cc
@@ -11,7 +11,6 @@
 #include "base/timer/timer.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
-#include "google_apis/gaia/oauth2_token_service.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 int OAuth2AccessTokenManager::max_fetch_retry_num_ = 5;
@@ -391,11 +390,8 @@
 }
 
 OAuth2AccessTokenManager::OAuth2AccessTokenManager(
-    OAuth2TokenService* token_service,
     OAuth2AccessTokenManager::Delegate* delegate)
-    : token_service_(token_service),
-      delegate_(delegate) {
-  DCHECK(token_service_);
+    : delegate_(delegate) {
   DCHECK(delegate_);
 }
 
@@ -552,13 +548,9 @@
     const CoreAccountId& account_id,
     const ScopeSet& scopes,
     const std::string& access_token) {
-  // TODO(https://crbug.com/967598): Use directly
-  // OAuth2AccessTokenManager::InvalidateAccessTokenImpl once this fully manages
-  // access tokens independently of OAuth2TokenService. For now, some tests need
-  // to call overridden of OAuth2TokenService::InvalidateAccessTokenImpl.
-  token_service_->InvalidateAccessTokenImpl(
-      account_id, GaiaUrls::GetInstance()->oauth2_chrome_client_id(), scopes,
-      access_token);
+  InvalidateAccessTokenImpl(account_id,
+                            GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
+                            scopes, access_token);
 }
 
 void OAuth2AccessTokenManager::InvalidateAccessTokenImpl(
@@ -648,13 +640,8 @@
   } else {
     // The token isn't in the cache and the delegate isn't fetching it: fetch it
     // ourselves!
-    // TODO(https://crbug.com/967598): Use directly
-    // OAuth2AccessTokenManager::FetchOAuth2Token this fully manages access
-    // tokens independently of OAuth2TokenService. For now, some tests need to
-    // override OAuth2TokenService::FetchOAuth2Token.
-    token_service_->FetchOAuth2Token(request.get(), account_id,
-                                     url_loader_factory, client_id,
-                                     client_secret, scopes);
+    FetchOAuth2Token(request.get(), account_id, url_loader_factory, client_id,
+                     client_secret, scopes);
   }
   return std::move(request);
 }
diff --git a/google_apis/gaia/oauth2_access_token_manager.h b/google_apis/gaia/oauth2_access_token_manager.h
index 7dbd401..c347b5ed 100644
--- a/google_apis/gaia/oauth2_access_token_manager.h
+++ b/google_apis/gaia/oauth2_access_token_manager.h
@@ -17,7 +17,6 @@
 }
 
 class OAuth2AccessTokenFetcher;
-class OAuth2TokenService;
 
 // Class that manages requests for OAuth2 access tokens.
 class OAuth2AccessTokenManager {
@@ -175,11 +174,7 @@
   typedef std::map<RequestParameters, OAuth2AccessTokenConsumer::TokenResponse>
       TokenCache;
 
-  // TODO(https://crbug.com/967598): Remove |token_service| once
-  // OAuth2AccessTokenManager fully manages access tokens independently of
-  // OAuth2TokenService.
   explicit OAuth2AccessTokenManager(
-      OAuth2TokenService* token_service,
       OAuth2AccessTokenManager::Delegate* delegate);
   virtual ~OAuth2AccessTokenManager();
 
@@ -340,9 +335,6 @@
   // List of observers to notify when access token status changes.
   base::ObserverList<DiagnosticsObserver, true>::Unchecked
       diagnostics_observer_list_;
-  // TODO(https://crbug.com/967598): Remove this once OAuth2AccessTokenManager
-  // fully manages access tokens independently of OAuth2TokenService.
-  OAuth2TokenService* token_service_;
   Delegate* delegate_;
   // A map from fetch parameters to a fetcher that is fetching an OAuth2 access
   // token using these parameters.
diff --git a/google_apis/gaia/oauth2_token_service.cc b/google_apis/gaia/oauth2_token_service.cc
index f5a3909..9627a60 100644
--- a/google_apis/gaia/oauth2_token_service.cc
+++ b/google_apis/gaia/oauth2_token_service.cc
@@ -30,7 +30,6 @@
   DCHECK(delegate_);
   AddObserver(this);
   token_manager_ = std::make_unique<OAuth2AccessTokenManager>(
-      this /* OAuth2TokenService* */,
       this /* OAuth2AccessTokenManager::Delegate* */);
 }
 
@@ -177,17 +176,6 @@
                                                  scopes, consumer);
 }
 
-void OAuth2TokenService::FetchOAuth2Token(
-    OAuth2AccessTokenManager::RequestImpl* request,
-    const CoreAccountId& account_id,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    const std::string& client_id,
-    const std::string& client_secret,
-    const OAuth2AccessTokenManager::ScopeSet& scopes) {
-  token_manager_->FetchOAuth2Token(request, account_id, url_loader_factory,
-                                   client_id, client_secret, scopes);
-}
-
 bool OAuth2TokenService::AreAllCredentialsLoaded() const {
   return all_credentials_loaded_;
 }
@@ -236,16 +224,6 @@
   delegate_->InvalidateTokenForMultilogin(failed_account);
 }
 
-void OAuth2TokenService::InvalidateAccessTokenImpl(
-    const CoreAccountId& account_id,
-    const std::string& client_id,
-    const OAuth2AccessTokenManager::ScopeSet& scopes,
-    const std::string& access_token) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  token_manager_->InvalidateAccessTokenImpl(account_id, client_id, scopes,
-                                            access_token);
-}
-
 void OAuth2TokenService::OnRefreshTokensLoaded() {
   all_credentials_loaded_ = true;
 }
diff --git a/google_apis/gaia/oauth2_token_service.h b/google_apis/gaia/oauth2_token_service.h
index c388985..e91f286 100644
--- a/google_apis/gaia/oauth2_token_service.h
+++ b/google_apis/gaia/oauth2_token_service.h
@@ -237,27 +237,6 @@
   // Deprecated. It's moved to OAuth2AccessTokenManager.
   virtual void CancelRequestsForAccount(const CoreAccountId& account_id);
 
-  // Fetches an OAuth token for the specified client/scopes. Virtual so it can
-  // be overridden for tests and for platform-specific behavior.
-  // Deprecated. It's moved to OAuth2AccessTokenManager.
-  virtual void FetchOAuth2Token(
-      OAuth2AccessTokenManager::RequestImpl* request,
-      const CoreAccountId& account_id,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      const std::string& client_id,
-      const std::string& client_secret,
-      const OAuth2AccessTokenManager::ScopeSet& scopes);
-
-  // Invalidates the |access_token| issued for |account_id|, |client_id| and
-  // |scopes|. Virtual so it can be overridden for tests and for platform-
-  // specific behavior.
-  // Deprecated. It's moved to OAuth2AccessTokenManager.
-  virtual void InvalidateAccessTokenImpl(
-      const CoreAccountId& account_id,
-      const std::string& client_id,
-      const OAuth2AccessTokenManager::ScopeSet& scopes,
-      const std::string& access_token);
-
  private:
   friend class OAuth2TokenServiceDelegate;
 
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 0566864..6f85927 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -3078,60 +3078,60 @@
     # Goma RBE ToT/Staging/FYI
     builders {
       name: "Chromium Linux Goma RBE ToT"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
     }
     builders {
       name: "Chromium Linux Goma RBE ToT (ATS)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ats"
       mixins: "goma-ci"
     }
     builders {
       name: "Chromium Linux Goma RBE Staging"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-staging"
     }
     builders {
       name: "Chromium Linux Goma RBE Staging (clobber)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-staging"
     }
     builders {
       name: "Chromium Linux Goma RBE Staging (dbg)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-staging"
     }
     builders {
       name: "Chromium Linux Goma RBE Staging (dbg) (clobber)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-staging"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod (clobber)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod (dbg)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
     builders {
       name: "Chromium Linux Goma RBE Prod (dbg) (clobber)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
@@ -3214,42 +3214,42 @@
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE ToT"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE ToT (ATS)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ats"
       mixins: "goma-ci"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Staging"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-staging"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod (clobber)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod (dbg)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
     builders {
       name: "Chromium Android ARM 32-bit Goma RBE Prod (dbg) (clobber)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
       mixins: "goma-rbe-prod"
     }
@@ -3306,7 +3306,7 @@
     }
     builders {
       name: "Chromium Linux Goma Staging"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "goma-ci"
     }
     builders {
@@ -3420,7 +3420,7 @@
     }
     builders {
       name: "Android Builder (dbg) Goma Latest Client"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "fyi-ci"
     }
     builders {
@@ -3447,12 +3447,12 @@
     }
     builders {
       name: "Linux x64 Goma Latest Client LocalOutputCache"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "fyi-ci"
     }
     builders {
       name: "Linux Builder Goma Latest Client"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "fyi-ci"
     }
     builders {
@@ -3483,12 +3483,12 @@
     }
     builders {
       name: "Linux x64 Goma Latest Client (clobber)"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "fyi-ci"
     }
     builders {
       name: "chromeos-amd64-generic-rel-goma-latest"
-      dimensions: "os:Ubuntu-14.04"
+      mixins: "linux-xenial"
       mixins: "fyi-ci"
     }
     builders {
diff --git a/ios/chrome/browser/OWNERS b/ios/chrome/browser/OWNERS
index 1f4b984..9244a78 100644
--- a/ios/chrome/browser/OWNERS
+++ b/ios/chrome/browser/OWNERS
@@ -3,9 +3,6 @@
 per-file ios_chrome_io_thread*=rsleevi@chromium.org
 
 # Only for adding and removing flags.
-per-file about_flags.*=*
-per-file experimental_flags.*=*
-
 per-file ios_chrome_flag_descriptions.*=*
 
 # TEAM: ios-directory-owners@chromium.org
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index 4460e586..8709bb30b 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -69,6 +69,7 @@
     "//ios/chrome/browser/ui/autofill/manual_fill:manual_fill_ui",
     "//ios/chrome/browser/ui/image_util",
     "//ios/chrome/browser/ui/util",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/common/ui_util",
     "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging",
@@ -163,6 +164,7 @@
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web:test_support",
     "//ios/chrome/browser/web:web_internal",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/test/base",
     "//ios/web/public/deprecated",
     "//ios/web/public/js_messaging",
diff --git a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
index d191af2d..f0de9e2 100644
--- a/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
+++ b/ios/chrome/browser/autofill/autocomplete_history_manager_factory.cc
@@ -15,9 +15,9 @@
 #include "ios/chrome/browser/autofill/autofill_profile_validator_factory.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index ad8df79..5553e28 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -32,7 +32,6 @@
 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
@@ -40,6 +39,7 @@
 #include "ios/chrome/browser/ui/settings/personal_data_manager_finished_profile_tasks_waiter.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #import "ios/web/public/deprecated/crw_js_injection_receiver.h"
 #include "ios/web/public/js_messaging/web_frame.h"
 #include "ios/web/public/js_messaging/web_frame_util.h"
diff --git a/ios/chrome/browser/autofill/personal_data_manager_factory.cc b/ios/chrome/browser/autofill/personal_data_manager_factory.cc
index 41ad023..b9adba1 100644
--- a/ios/chrome/browser/autofill/personal_data_manager_factory.cc
+++ b/ios/chrome/browser/autofill/personal_data_manager_factory.cc
@@ -15,9 +15,9 @@
 #include "ios/chrome/browser/autofill/autofill_profile_validator_factory.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 
 namespace autofill {
 
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn
index 2667d7e4..fbb8335 100644
--- a/ios/chrome/browser/browser_state/BUILD.gn
+++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -9,8 +9,6 @@
     "chrome_browser_state.h",
     "chrome_browser_state.mm",
     "chrome_browser_state_manager.h",
-    "web_data_service_factory.cc",
-    "web_data_service_factory.h",
   ]
 
   public_deps = [
@@ -26,7 +24,6 @@
     "//components/prefs",
     "//components/search_engines",
     "//components/signin/core/browser",
-    "//components/signin/core/browser/webdata",
     "//components/sync_preferences",
     "//components/variations/net",
     "//components/webdata_services",
@@ -127,6 +124,7 @@
     "//ios/chrome/browser/undo",
     "//ios/chrome/browser/unified_consent",
     "//ios/chrome/browser/web_state_list/web_usage_enabler",
+    "//ios/chrome/browser/webdata_services",
     "//ios/net",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/signin",
@@ -200,6 +198,7 @@
     "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/sync/glue",
     "//ios/chrome/browser/undo",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/test:test_support",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/ui",
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 450a9a6..652aecc 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -11,7 +11,6 @@
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remover_factory.h"
 #include "ios/chrome/browser/content_settings/cookie_settings_factory.h"
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
@@ -59,6 +58,7 @@
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "ios/chrome/browser/unified_consent/unified_consent_service_factory.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
index 541a3fb..1fc59ec 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
@@ -38,7 +38,6 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h"
 #include "ios/chrome/browser/browser_state/browser_state_keyed_service_factories.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/history/history_client_impl.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/history/top_sites_factory.h"
@@ -46,6 +45,7 @@
 #include "ios/chrome/browser/prefs/browser_prefs.h"
 #include "ios/chrome/browser/prefs/ios_chrome_pref_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
diff --git a/ios/chrome/browser/browsing_data/BUILD.gn b/ios/chrome/browser/browsing_data/BUILD.gn
index 5e3b7ba..a6b3c3fa 100644
--- a/ios/chrome/browser/browsing_data/BUILD.gn
+++ b/ios/chrome/browser/browsing_data/BUILD.gn
@@ -69,6 +69,7 @@
     "//ios/chrome/browser/snapshots",
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/translate:translate",
+    "//ios/chrome/browser/webdata_services",
     "//ios/net",
     "//ios/public/provider/chrome/browser",
     "//ios/web",
diff --git a/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc b/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
index c5c490a7..9243d2f 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
+++ b/ios/chrome/browser/browsing_data/browsing_data_counter_wrapper.cc
@@ -16,13 +16,13 @@
 #include "components/prefs/pref_service.h"
 #include "components/sync/driver/sync_service.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_features.h"
 #include "ios/chrome/browser/browsing_data/cache_counter.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/history/web_history_service_factory.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 
 namespace {
 
diff --git a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
index 0bb5cd1..2ae8a28 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
+++ b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
@@ -42,7 +42,6 @@
 #include "ios/chrome/browser/autofill/strike_database_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_remover_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_features.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remove_mask.h"
 #include "ios/chrome/browser/external_files/external_file_remover.h"
@@ -58,6 +57,7 @@
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #include "ios/chrome/browser/snapshots/snapshots_util.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "ios/net/http_cache_helper.h"
 #import "ios/web/public/browsing_data/browsing_data_removing_util.h"
 #include "ios/web/public/thread/web_task_traits.h"
diff --git a/ios/chrome/browser/flags/OWNERS b/ios/chrome/browser/flags/OWNERS
new file mode 100644
index 0000000..7352d8d
--- /dev/null
+++ b/ios/chrome/browser/flags/OWNERS
@@ -0,0 +1,4 @@
+per-file about_flags.*=*
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index d1568b95..b5d214c2a 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -496,10 +496,6 @@
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillNoLocalSaveOnUploadSuccess)},
-    {"display-search-engine-favicon",
-     flag_descriptions::kDisplaySearchEngineFaviconName,
-     flag_descriptions::kDisplaySearchEngineFaviconDescription,
-     flags_ui::kOsIos, FEATURE_VALUE_TYPE(kDisplaySearchEngineFavicon)},
     {"autofill-no-local-save-on-unmask-success",
      flag_descriptions::kAutofillNoLocalSaveOnUnmaskSuccessName,
      flag_descriptions::kAutofillNoLocalSaveOnUnmaskSuccessDescription,
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 526c6f8..b130756b 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -148,11 +148,6 @@
     "A crash report will be uploaded if the main thread is frozen more than "
     "the time specified by this flag.";
 
-const char kDisplaySearchEngineFaviconName[] =
-    "Display search engine favicons.";
-const char kDisplaySearchEngineFaviconDescription[] =
-    "When enabled, Settings will display search engine favicons";
-
 const char kDragAndDropName[] = "Drag and Drop";
 const char kDragAndDropDescription[] = "Enable support for drag and drop.";
 
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index b675513..9a61e50 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -117,11 +117,6 @@
 extern const char kDetectMainThreadFreezeName[];
 extern const char kDetectMainThreadFreezeDescription[];
 
-// Title and description for the flag to enable displaying search engine
-// favicons in Settings.
-extern const char kDisplaySearchEngineFaviconName[];
-extern const char kDisplaySearchEngineFaviconDescription[];
-
 // Title and description for the flag to enable drag and drop.
 extern const char kDragAndDropName[];
 extern const char kDragAndDropDescription[];
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 1d90f1a5..d7b9cc0 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -82,6 +82,7 @@
     "//ios/chrome/browser/ui/infobars/coordinators",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/common/ui_util",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/ui",
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc b/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
index e3e5bca..3c3ef87 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
+++ b/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
@@ -26,9 +26,9 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/sync/glue/sync_start_util.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 // static
diff --git a/ios/chrome/browser/search_engines/BUILD.gn b/ios/chrome/browser/search_engines/BUILD.gn
index 6f45520..fcb6f27 100644
--- a/ios/chrome/browser/search_engines/BUILD.gn
+++ b/ios/chrome/browser/search_engines/BUILD.gn
@@ -39,6 +39,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/google",
     "//ios/chrome/browser/history",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/common",
     "//ios/web",
     "//net",
diff --git a/ios/chrome/browser/search_engines/template_url_service_factory.cc b/ios/chrome/browser/search_engines/template_url_service_factory.cc
index 24b178a5..3ca6db4 100644
--- a/ios/chrome/browser/search_engines/template_url_service_factory.cc
+++ b/ios/chrome/browser/search_engines/template_url_service_factory.cc
@@ -15,11 +15,11 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/google/google_url_tracker_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/search_engines/template_url_service_client_impl.h"
 #include "ios/chrome/browser/search_engines/ui_thread_search_terms_data.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "rlz/buildflags/buildflags.h"
 
 #if BUILDFLAG(ENABLE_RLZ)
diff --git a/ios/chrome/browser/signin/BUILD.gn b/ios/chrome/browser/signin/BUILD.gn
index 9fccecb..5149c316 100644
--- a/ios/chrome/browser/signin/BUILD.gn
+++ b/ios/chrome/browser/signin/BUILD.gn
@@ -77,6 +77,7 @@
     "//ios/chrome/browser/crash_report",
     "//ios/chrome/browser/reading_list:reading_list_remover",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/common",
     "//ios/net",
     "//ios/public/provider/chrome/browser",
diff --git a/ios/chrome/browser/signin/device_accounts_provider_impl.mm b/ios/chrome/browser/signin/device_accounts_provider_impl.mm
index e16dfda..f88583c4 100644
--- a/ios/chrome/browser/signin/device_accounts_provider_impl.mm
+++ b/ios/chrome/browser/signin/device_accounts_provider_impl.mm
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
+#include "components/signin/core/browser/account_info.h"
 #include "ios/chrome/browser/signin/constants.h"
 #include "ios/chrome/browser/signin/signin_util.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
@@ -18,13 +19,22 @@
 #endif
 
 namespace {
-// Returns the account info for |identity|.
-// Returns an empty account info if |identity| is nil.
+// Returns the account info for |identity| (which must not be nil).
 DeviceAccountsProvider::AccountInfo GetAccountInfo(ChromeIdentity* identity) {
+  DCHECK(identity);
   DeviceAccountsProvider::AccountInfo account_info;
-  if (identity) {
-    account_info.gaia = base::SysNSStringToUTF8([identity gaiaID]);
-    account_info.email = base::SysNSStringToUTF8([identity userEmail]);
+  account_info.gaia = base::SysNSStringToUTF8([identity gaiaID]);
+  account_info.email = base::SysNSStringToUTF8([identity userEmail]);
+
+  // If hosted domain is nil, then it means the information has not been
+  // fetched from gaia; in that case, set account_info.hosted_domain to
+  // an empty string. Otherwise, set it to the value of the hostedDomain
+  // or kNoHostedDomainFound if the string is empty.
+  NSString* hostedDomain = [identity hostedDomain];
+  if (hostedDomain) {
+    account_info.hosted_domain = [hostedDomain length]
+                                     ? base::SysNSStringToUTF8(hostedDomain)
+                                     : kNoHostedDomainFound;
   }
   return account_info;
 }
diff --git a/ios/chrome/browser/signin/ios_chrome_signin_client.mm b/ios/chrome/browser/signin/ios_chrome_signin_client.mm
index 8e390b0..9b1715ab 100644
--- a/ios/chrome/browser/signin/ios_chrome_signin_client.mm
+++ b/ios/chrome/browser/signin/ios_chrome_signin_client.mm
@@ -12,9 +12,9 @@
 #include "ios/chrome/browser/browser_state/browser_state_info_cache.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
 #include "ios/chrome/browser/signin/gaia_auth_fetcher_ios.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
diff --git a/ios/chrome/browser/sync/BUILD.gn b/ios/chrome/browser/sync/BUILD.gn
index c9e8dcc1..d248b83 100644
--- a/ios/chrome/browser/sync/BUILD.gn
+++ b/ios/chrome/browser/sync/BUILD.gn
@@ -79,6 +79,7 @@
     "//ios/chrome/browser/sync/sessions",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/undo",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/common",
     "//ios/web",
     "//net",
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index f8a32d5..a8335f8 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -47,7 +47,6 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
@@ -63,6 +62,7 @@
 #include "ios/chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "ios/chrome/browser/sync/session_sync_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc
index d28e6ee..3012314 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -27,7 +27,6 @@
 #include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "ios/chrome/browser/bookmarks/bookmark_sync_service_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
 #include "ios/chrome/browser/gcm/ios_chrome_gcm_profile_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
@@ -46,6 +45,7 @@
 #include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/browser/sync/session_sync_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "ios/web/public/thread/web_thread.h"
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index 8c479d7..3e35495 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -53,6 +53,7 @@
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web_state_list:web_state_list",
+    "//ios/chrome/browser/webdata_services",
     "//ios/chrome/common",
     "//ios/public/provider/chrome/browser",
     "//ios/third_party/material_components_ios",
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index a53aa21..ef7d42a 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -27,7 +27,6 @@
 #include "ios/chrome/browser/autofill/autocomplete_history_manager_factory.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/autofill/strike_database_factory.h"
-#include "ios/chrome/browser/browser_state/web_data_service_factory.h"
 #include "ios/chrome/browser/infobars/infobar.h"
 #include "ios/chrome/browser/infobars/infobar_utils.h"
 #include "ios/chrome/browser/metrics/ukm_url_recorder.h"
@@ -37,6 +36,7 @@
 #include "ios/chrome/browser/translate/chrome_ios_translate_client.h"
 #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
 #include "ios/chrome/browser/ui/autofill/save_card_infobar_controller.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
index 3e9e2f3..a789eb5 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
@@ -272,14 +272,6 @@
                        constant:kStandardSpacing],
     [_imageContainer.bottomAnchor
         constraintLessThanOrEqualToAnchor:_faviconView.bottomAnchor],
-    [_imageContainer.leadingAnchor
-        constraintEqualToAnchor:self.contentView.leadingAnchor
-                       constant:kStandardSpacing],
-
-    // Title label.
-    [_titleLabel.trailingAnchor
-        constraintEqualToAnchor:self.contentView.trailingAnchor
-                       constant:-kStandardSpacing],
 
     // Favicon.
     [_faviconView.leadingAnchor
@@ -309,33 +301,63 @@
     [_noImageIcon.heightAnchor constraintEqualToAnchor:_noImageIcon.widthAnchor]
   ]];
 
-  // Prevent _additionalInformationLabel from overlapping with _titleLabel when
-  // a11y font size is used.
+  AddSameConstraints(_contentImageView, _imageContainer);
+
+  // For standard layout, the image container should be aligned to the right
+  // border if kOptionalArticleThumbnail is enabled, otherwise the left border.
   if (base::FeatureList::IsEnabled(kOptionalArticleThumbnail)) {
+    // Prevent _additionalInformationLabel from overlapping with _titleLabel
+    // when a11y font size is used.
     [_additionalInformationLabel.topAnchor
         constraintGreaterThanOrEqualToAnchor:_titleLabel.bottomAnchor
                                     constant:kSmallSpacing]
         .active = YES;
+
+    _imageTitleHorizontalSpacing = [_imageContainer.leadingAnchor
+        constraintEqualToAnchor:_titleLabel.trailingAnchor
+                       constant:kStandardSpacing];
+
+    _standardConstraints = @[
+      _imageTitleHorizontalSpacing,
+      [_titleLabel.topAnchor constraintEqualToAnchor:_imageContainer.topAnchor],
+      [_titleLabel.leadingAnchor
+          constraintEqualToAnchor:self.contentView.leadingAnchor
+                         constant:kStandardSpacing],
+      [_imageContainer.trailingAnchor
+          constraintEqualToAnchor:self.contentView.trailingAnchor
+                         constant:-kStandardSpacing],
+    ];
+  } else {
+    _imageTitleHorizontalSpacing = [_titleLabel.leadingAnchor
+        constraintEqualToAnchor:_imageContainer.trailingAnchor
+                       constant:kStandardSpacing];
+
+    _standardConstraints = @[
+      _imageTitleHorizontalSpacing,
+      [_titleLabel.topAnchor constraintEqualToAnchor:_imageContainer.topAnchor],
+      [_imageContainer.leadingAnchor
+          constraintEqualToAnchor:self.contentView.leadingAnchor
+                         constant:kStandardSpacing],
+      [_titleLabel.trailingAnchor
+          constraintEqualToAnchor:self.contentView.trailingAnchor
+                         constant:-kStandardSpacing],
+    ];
   }
 
-  AddSameConstraints(_contentImageView, _imageContainer);
-
-  _imageTitleHorizontalSpacing = [_titleLabel.leadingAnchor
-      constraintEqualToAnchor:_imageContainer.trailingAnchor
-                     constant:kStandardSpacing];
+  // For a11y layout, the image container is always aligned to the left border.
   _imageTitleVerticalSpacing = [_titleLabel.topAnchor
       constraintEqualToAnchor:_imageContainer.bottomAnchor
                      constant:kStandardSpacing];
-  _standardConstraints = @[
-    _imageTitleHorizontalSpacing,
-    [_titleLabel.topAnchor constraintEqualToAnchor:_imageContainer.topAnchor],
-  ];
-
   _accessibilityConstraints = @[
-    [_titleLabel.leadingAnchor
+    _imageTitleVerticalSpacing,
+    [_imageContainer.leadingAnchor
         constraintEqualToAnchor:self.contentView.leadingAnchor
                        constant:kStandardSpacing],
-    _imageTitleVerticalSpacing,
+    [_titleLabel.leadingAnchor
+        constraintEqualToAnchor:_imageContainer.leadingAnchor],
+    [_titleLabel.trailingAnchor
+        constraintEqualToAnchor:self.contentView.trailingAnchor
+                       constant:-kStandardSpacing],
   ];
 
   if (UIContentSizeCategoryIsAccessibilityCategory(
diff --git a/ios/chrome/browser/ui/settings/cells/search_engine_item.mm b/ios/chrome/browser/ui/settings/cells/search_engine_item.mm
index b7fbc8e7..b30cb0b 100644
--- a/ios/chrome/browser/ui/settings/cells/search_engine_item.mm
+++ b/ios/chrome/browser/ui/settings/cells/search_engine_item.mm
@@ -8,7 +8,6 @@
 #include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
@@ -33,13 +32,7 @@
 - (instancetype)initWithType:(NSInteger)type {
   self = [super initWithType:type];
   if (self) {
-    // TODO(crbug.com/910525): Remove usage of TableViewDetailTextCell after the
-    // feature is launched.
-    if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
       self.cellClass = TableViewURLCell.class;
-    } else {
-      self.cellClass = TableViewDetailTextCell.class;
-    }
   }
   return self;
 }
@@ -50,40 +43,20 @@
 
   self.uniqueIdentifier = base::SysUTF8ToNSString(self.URL.host());
 
-  // TODO(crbug.com/910525): Remove usage of TableViewDetailTextCell after the
-  // feature is launched.
-  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
-    TableViewURLCell* cell =
-        base::mac::ObjCCastStrict<TableViewURLCell>(tableCell);
+  TableViewURLCell* cell =
+      base::mac::ObjCCastStrict<TableViewURLCell>(tableCell);
+  cell.titleLabel.text = self.text;
+  cell.URLLabel.text = self.detailText;
+  cell.cellUniqueIdentifier = self.uniqueIdentifier;
+  cell.accessibilityTraits |= UIAccessibilityTraitButton;
 
-    cell.titleLabel.text = self.text;
-    cell.URLLabel.text = self.detailText;
-    cell.cellUniqueIdentifier = self.uniqueIdentifier;
-    cell.accessibilityTraits |= UIAccessibilityTraitButton;
-
-    if (styler.cellTitleColor)
-      cell.titleLabel.textColor = styler.cellTitleColor;
-
-    cell.URLLabel.textColor = UIColorFromRGB(kSettingsCellsURLTextColor);
-
-    [cell configureUILayout];
-  } else {
-    TableViewDetailTextCell* cell =
-        base::mac::ObjCCastStrict<TableViewDetailTextCell>(tableCell);
-
-    cell.textLabel.text = self.text;
-    cell.detailTextLabel.text = self.detailText;
-    cell.accessibilityTraits |= UIAccessibilityTraitButton;
-
-    if (styler.cellTitleColor) {
-      cell.textLabel.textColor = styler.cellTitleColor;
-    } else {
-      cell.textLabel.textColor =
-          UIColorFromRGB(kTableViewTextLabelColorLightGrey);
-    }
-    cell.detailTextLabel.textColor =
-        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  if (styler.cellTitleColor) {
+    cell.titleLabel.textColor = styler.cellTitleColor;
   }
+
+  cell.URLLabel.textColor = UIColorFromRGB(kSettingsCellsURLTextColor);
+
+  [cell configureUILayout];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/settings/cells/search_engine_item_unittest.mm b/ios/chrome/browser/ui/settings/cells/search_engine_item_unittest.mm
index de2aac1..57ea30cb 100644
--- a/ios/chrome/browser/ui/settings/cells/search_engine_item_unittest.mm
+++ b/ios/chrome/browser/ui/settings/cells/search_engine_item_unittest.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/ui/settings/cells/search_engine_item.h"
 
 #include "base/mac/foundation_util.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
@@ -36,37 +35,18 @@
   item.accessoryType = UITableViewCellAccessoryCheckmark;
 
   id cell = [[[item cellClass] alloc] init];
-  // TODO(crbug.com/910525): Remove usage of TableViewDetailTextCell after the
-  // feature is launched.
-  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
-    ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+  ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
 
-    TableViewURLCell* URLCell =
-        base::mac::ObjCCastStrict<TableViewURLCell>(cell);
-    EXPECT_FALSE(URLCell.titleLabel.text);
-    EXPECT_FALSE(URLCell.URLLabel.text);
-    EXPECT_EQ(item.uniqueIdentifier, URLCell.cellUniqueIdentifier);
-    EXPECT_EQ(UITableViewCellAccessoryNone, URLCell.accessoryType);
+  TableViewURLCell* URLCell = base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+  EXPECT_FALSE(URLCell.titleLabel.text);
+  EXPECT_FALSE(URLCell.URLLabel.text);
+  EXPECT_EQ(item.uniqueIdentifier, URLCell.cellUniqueIdentifier);
+  EXPECT_EQ(UITableViewCellAccessoryNone, URLCell.accessoryType);
 
-    ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-    [item configureCell:URLCell withStyler:styler];
-    EXPECT_NSEQ(text, URLCell.titleLabel.text);
-    EXPECT_NSEQ(detailText, URLCell.URLLabel.text);
-    EXPECT_EQ(item.uniqueIdentifier, URLCell.cellUniqueIdentifier);
-    EXPECT_EQ(UITableViewCellAccessoryCheckmark, URLCell.accessoryType);
-  } else {
-    ASSERT_TRUE([cell isMemberOfClass:[TableViewDetailTextCell class]]);
-
-    TableViewDetailTextCell* textCell =
-        base::mac::ObjCCastStrict<TableViewDetailTextCell>(cell);
-    EXPECT_FALSE(textCell.textLabel.text);
-    EXPECT_FALSE(textCell.detailTextLabel.text);
-    EXPECT_EQ(UITableViewCellAccessoryNone, textCell.accessoryType);
-
-    ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
-    [item configureCell:textCell withStyler:styler];
-    EXPECT_NSEQ(text, textCell.textLabel.text);
-    EXPECT_NSEQ(detailText, textCell.detailTextLabel.text);
-    EXPECT_EQ(UITableViewCellAccessoryCheckmark, textCell.accessoryType);
-  }
+  ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
+  [item configureCell:URLCell withStyler:styler];
+  EXPECT_NSEQ(text, URLCell.titleLabel.text);
+  EXPECT_NSEQ(detailText, URLCell.URLLabel.text);
+  EXPECT_EQ(item.uniqueIdentifier, URLCell.cellUniqueIdentifier);
+  EXPECT_EQ(UITableViewCellAccessoryCheckmark, URLCell.accessoryType);
 }
diff --git a/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm b/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
index 42b441a..0237efb 100644
--- a/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
@@ -98,11 +98,8 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
-    self.tableView.separatorInset =
-        UIEdgeInsetsMake(0, kTableViewSeparatorLeadingInset, 0,
-                         kTableViewSeparatorTrailingInset);
-  }
+  self.tableView.separatorInset = UIEdgeInsetsMake(
+      0, kTableViewSeparatorLeadingInset, 0, kTableViewSeparatorTrailingInset);
 
   [self loadModel];
 }
@@ -225,33 +222,30 @@
   TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
   DCHECK(item.type == ItemTypePrepopulatedEngine ||
          item.type == ItemTypeCustomEngine);
-  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
-    SearchEngineItem* engineItem =
-        base::mac::ObjCCastStrict<SearchEngineItem>(item);
-    TableViewURLCell* urlCell =
-        base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+  SearchEngineItem* engineItem =
+      base::mac::ObjCCastStrict<SearchEngineItem>(item);
+  TableViewURLCell* urlCell = base::mac::ObjCCastStrict<TableViewURLCell>(cell);
 
-    if (item.type == ItemTypePrepopulatedEngine) {
-      _faviconLoader->FaviconForPageUrl(
-          engineItem.URL, kFaviconDesiredSizeInPoint, kFaviconMinSizeInPoint,
-          /*fallback_to_google_server=*/YES, ^(FaviconAttributes* attributes) {
-            // Only set favicon if the cell hasn't been reused.
-            if (urlCell.cellUniqueIdentifier == engineItem.uniqueIdentifier) {
-              DCHECK(attributes);
-              [urlCell.faviconView configureWithAttributes:attributes];
-            }
-          });
-    } else {
-      _faviconLoader->FaviconForIconUrl(
-          engineItem.URL, kFaviconDesiredSizeInPoint, kFaviconMinSizeInPoint,
-          ^(FaviconAttributes* attributes) {
-            // Only set favicon if the cell hasn't been reused.
-            if (urlCell.cellUniqueIdentifier == engineItem.uniqueIdentifier) {
-              DCHECK(attributes);
-              [urlCell.faviconView configureWithAttributes:attributes];
-            }
-          });
-    }
+  if (item.type == ItemTypePrepopulatedEngine) {
+    _faviconLoader->FaviconForPageUrl(
+        engineItem.URL, kFaviconDesiredSizeInPoint, kFaviconMinSizeInPoint,
+        /*fallback_to_google_server=*/YES, ^(FaviconAttributes* attributes) {
+          // Only set favicon if the cell hasn't been reused.
+          if (urlCell.cellUniqueIdentifier == engineItem.uniqueIdentifier) {
+            DCHECK(attributes);
+            [urlCell.faviconView configureWithAttributes:attributes];
+          }
+        });
+  } else {
+    _faviconLoader->FaviconForIconUrl(
+        engineItem.URL, kFaviconDesiredSizeInPoint, kFaviconMinSizeInPoint,
+        ^(FaviconAttributes* attributes) {
+          // Only set favicon if the cell hasn't been reused.
+          if (urlCell.cellUniqueIdentifier == engineItem.uniqueIdentifier) {
+            DCHECK(attributes);
+            [urlCell.faviconView configureWithAttributes:attributes];
+          }
+        });
   }
   return cell;
 }
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index a0a5f625..80bf763a 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -26,9 +26,6 @@
 const base::Feature kSettingsRefresh{"SettingsRefresh",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kDisplaySearchEngineFavicon{
-    "DisplaySearchEngineFavicon", base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kNewOmniboxPopupLayout{"NewOmniboxPopupLayout",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index f315429..142a2b3 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -29,9 +29,6 @@
 // Feature to apply UI Refresh theme to the settings.
 extern const base::Feature kSettingsRefresh;
 
-// Feature to display search engine favicons in Settings.
-extern const base::Feature kDisplaySearchEngineFavicon;
-
 // Feature to display the new omnibox popup design with favicons, search engine
 // favicon in the omnibox, rich entities support, new layout.
 extern const base::Feature kNewOmniboxPopupLayout;
diff --git a/ios/chrome/browser/webdata_services/BUILD.gn b/ios/chrome/browser/webdata_services/BUILD.gn
new file mode 100644
index 0000000..f739457
--- /dev/null
+++ b/ios/chrome/browser/webdata_services/BUILD.gn
@@ -0,0 +1,23 @@
+# 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.
+
+source_set("webdata_services") {
+  sources = [
+    "web_data_service_factory.cc",
+    "web_data_service_factory.h",
+  ]
+  deps = [
+    "//base",
+    "//components/autofill/core/browser",
+    "//components/keyed_service/core",
+    "//components/keyed_service/ios",
+    "//components/search_engines",
+    "//components/signin/core/browser",
+    "//components/signin/core/browser/webdata",
+    "//components/webdata_services",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/browser_state",
+    "//ios/web/public/thread",
+  ]
+}
diff --git a/ios/chrome/browser/browser_state/web_data_service_factory.cc b/ios/chrome/browser/webdata_services/web_data_service_factory.cc
similarity index 97%
rename from ios/chrome/browser/browser_state/web_data_service_factory.cc
rename to ios/chrome/browser/webdata_services/web_data_service_factory.cc
index e3288863..62633b2 100644
--- a/ios/chrome/browser/browser_state/web_data_service_factory.cc
+++ b/ios/chrome/browser/webdata_services/web_data_service_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 "ios/chrome/browser/browser_state/web_data_service_factory.h"
+#include "ios/chrome/browser/webdata_services/web_data_service_factory.h"
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
diff --git a/ios/chrome/browser/browser_state/web_data_service_factory.h b/ios/chrome/browser/webdata_services/web_data_service_factory.h
similarity index 92%
rename from ios/chrome/browser/browser_state/web_data_service_factory.h
rename to ios/chrome/browser/webdata_services/web_data_service_factory.h
index 79ed343..3d8d62a 100644
--- a/ios/chrome/browser/browser_state/web_data_service_factory.h
+++ b/ios/chrome/browser/webdata_services/web_data_service_factory.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 IOS_CHROME_BROWSER_BROWSER_STATE_WEB_DATA_SERVICE_FACTORY_H_
-#define IOS_CHROME_BROWSER_BROWSER_STATE_WEB_DATA_SERVICE_FACTORY_H_
+#ifndef IOS_CHROME_BROWSER_WEBDATA_SERVICES_WEB_DATA_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_WEBDATA_SERVICES_WEB_DATA_SERVICE_FACTORY_H_
 
 #include <memory>
 
@@ -78,4 +78,4 @@
 
 }  // namespace ios
 
-#endif  // IOS_CHROME_BROWSER_BROWSER_STATE_WEB_DATA_SERVICE_FACTORY_H_
+#endif  // IOS_CHROME_BROWSER_WEBDATA_SERVICES_WEB_DATA_SERVICE_FACTORY_H_
diff --git a/ios/public/provider/chrome/browser/signin/chrome_identity.h b/ios/public/provider/chrome/browser/signin/chrome_identity.h
index c755a07..94e4169 100644
--- a/ios/public/provider/chrome/browser/signin/chrome_identity.h
+++ b/ios/public/provider/chrome/browser/signin/chrome_identity.h
@@ -28,6 +28,12 @@
 // between apps.
 @property(strong, nonatomic, readonly) NSString* hashedGaiaID;
 
+// Returns the cached hosted domain for the identity (fetched asynchronously).
+// If the value has not been fetched, this property will be nil. Otherwise it
+// will be the hosted domain or an empty string if the account is a consumer
+// account (e.g. gmail.com).
+@property(strong, nonatomic, readonly) NSString* hostedDomain;
+
 @end
 
 #endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_SIGNIN_CHROME_IDENTITY_H_
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 4e2d8bcd2..efe7fc7 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -118,6 +118,14 @@
     : frame(frame), force_keyframe(force_keyframe) {}
 
 V4L2VideoEncodeAccelerator::InputFrameInfo::InputFrameInfo(
+    scoped_refptr<VideoFrame> frame,
+    bool force_keyframe,
+    size_t index)
+    : frame(std::move(frame)),
+      force_keyframe(force_keyframe),
+      ip_output_buffer_index(index) {}
+
+V4L2VideoEncodeAccelerator::InputFrameInfo::InputFrameInfo(
     const InputFrameInfo&) = default;
 
 V4L2VideoEncodeAccelerator::InputFrameInfo::~InputFrameInfo() {}
@@ -145,9 +153,6 @@
   DCHECK(!encoder_thread_.IsRunning());
   DCHECK(!device_poll_thread_.IsRunning());
   VLOGF(2);
-
-  DestroyInputBuffers();
-  DestroyOutputBuffers();
 }
 
 bool V4L2VideoEncodeAccelerator::Initialize(const Config& config,
@@ -185,7 +190,7 @@
   if (!is_flush_supported_)
     VLOGF(2) << "V4L2_ENC_CMD_STOP is not supported.";
 
-  struct v4l2_capability caps{};
+  struct v4l2_capability caps {};
   const __u32 kCapsRequired = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
   IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps);
   if ((caps.capabilities & kCapsRequired) != kCapsRequired) {
@@ -193,9 +198,30 @@
     return false;
   }
 
+  if (!encoder_thread_.Start()) {
+    VLOGF(1) << "encoder thread failed to start";
+    return false;
+  }
+
+  bool result = false;
+  base::WaitableEvent done;
+  encoder_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&V4L2VideoEncodeAccelerator::InitializeTask,
+                     base::Unretained(this), config, &result, &done));
+  done.Wait();
+  return result;
+}
+
+void V4L2VideoEncodeAccelerator::InitializeTask(const Config& config,
+                                                bool* result,
+                                                base::WaitableEvent* done) {
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
+  *result = false;
   if (!SetFormats(config.input_format, config.output_profile)) {
     VLOGF(1) << "Failed setting up formats";
-    return false;
+    done->Signal();
+    return;
   }
 
   if (config.input_format != device_input_layout_->format()) {
@@ -210,84 +236,35 @@
             VideoFrame::NumPlanes(config.input_format)));
     if (!input_layout) {
       VLOGF(1) << "Invalid image processor input layout";
-      return false;
+      done->Signal();
+      return;
     }
 
-    // Convert from |config.input_format| to |device_input_layout_->format()|,
-    // keeping the size at |visible_size_| and requiring the output buffers to
-    // be of at least |device_input_layout_->coded_size()|.
-    // Unretained(this) is safe in creating ErrorCB because
-    // V4L2VideoEncodeAccelerator instance outlives |image_processor_| and
-    // ImageProcessor invalidates posted ErrorCB when its Reset() or destructor
-    // is called.
-    // |input_storage_type| can be STORAGE_SHMEM and STORAGE_MOJO_SHARED_BUFFER.
-    // However, it doesn't matter VideoFrame::STORAGE_OWNED_MEMORY is specified
-    // for |input_storage_type| here, as long as VideoFrame on Process()'s data
-    // can be accessed by VideoFrame::data().
-    image_processor_ = ImageProcessorFactory::Create(
-        ImageProcessor::PortConfig(*input_layout, visible_size_,
-                                   {VideoFrame::STORAGE_OWNED_MEMORY}),
-        ImageProcessor::PortConfig(
-            *device_input_layout_, visible_size_,
-            {VideoFrame::STORAGE_DMABUFS, VideoFrame::STORAGE_OWNED_MEMORY}),
-        // Try OutputMode::ALLOCATE first because we want v4l2IP chooses
-        // ALLOCATE mode. For libyuvIP, it accepts only IMPORT.
-        {ImageProcessor::OutputMode::ALLOCATE,
-         ImageProcessor::OutputMode::IMPORT},
-        kImageProcBufferCount,
-        // We have to bind |weak_this| for ImageProcessorError, because child
-        // thread is outlive this V4L2VideoEncodeAccelerator.
-        base::BindRepeating(&V4L2VideoEncodeAccelerator::ImageProcessorError,
-                            weak_this_));
-    if (!image_processor_) {
-      VLOGF(1) << "Failed initializing image processor";
-      return false;
+    if (!CreateImageProcessor(*input_layout, *device_input_layout_,
+                              visible_size_)) {
+      VLOGF(1) << "Failed to create image processor";
+      done->Signal();
+      return;
     }
-    // The output of image processor is the input of encoder. Output coded
-    // width of processor must be the same as input coded width of encoder.
-    // Output coded height of processor can be larger but not smaller than the
-    // input coded height of encoder. For example, suppose input size of encoder
-    // is 320x193. It is OK if the output of processor is 320x208.
-    if (image_processor_->output_layout().coded_size().width() !=
-            device_input_layout_->coded_size().width() ||
-        image_processor_->output_layout().coded_size().height() <
-            device_input_layout_->coded_size().height()) {
-      VLOGF(1) << "Invalid image processor output coded size "
-               << image_processor_->output_layout().coded_size().ToString()
-               << ", encode input coded size is "
-               << device_input_layout_->coded_size().ToString();
-      return false;
-    }
-
-    // Initialize |free_image_processor_output_buffer_indices_|.
-    free_image_processor_output_buffer_indices_.resize(kImageProcBufferCount);
-    std::iota(free_image_processor_output_buffer_indices_.begin(),
-              free_image_processor_output_buffer_indices_.end(), 0);
-
-    if (!AllocateImageProcessorOutputBuffers())
-      return false;
   }
 
-  if (!InitInputMemoryType(config))
-    return false;
-
-  if (!InitControls(config))
-    return false;
-
-  if (!CreateOutputBuffers())
-    return false;
-
-  if (!encoder_thread_.Start()) {
-    VLOGF(1) << "encoder thread failed to start";
-    return false;
+  if (!InitInputMemoryType(config)) {
+    done->Signal();
+    return;
   }
-
-  RequestEncodingParametersChange(
-      config.initial_bitrate, config.initial_framerate.value_or(
-                                  VideoEncodeAccelerator::kDefaultFramerate));
+  if (!InitControls(config)) {
+    done->Signal();
+    return;
+  }
+  if (!CreateOutputBuffers()) {
+    done->Signal();
+    return;
+  }
 
   encoder_state_ = kInitialized;
-
+  RequestEncodingParametersChangeTask(
+      config.initial_bitrate, config.initial_framerate.value_or(
+                                  VideoEncodeAccelerator::kDefaultFramerate));
   child_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -295,23 +272,87 @@
           image_processor_.get() ? image_processor_->input_layout().coded_size()
                                  : input_allocated_size_,
           output_buffer_byte_size_));
-  return true;
+
+  // Finish initialization.
+  *result = true;
+  done->Signal();
 }
 
-bool V4L2VideoEncodeAccelerator::AllocateImageProcessorOutputBuffers() {
+bool V4L2VideoEncodeAccelerator::CreateImageProcessor(
+    const VideoFrameLayout& input_layout,
+    const VideoFrameLayout& output_layout,
+    const gfx::Size& visible_size) {
+  VLOGF(2);
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
+  DCHECK_NE(input_layout.format(), output_layout.format());
+
+  // Convert from |config.input_format| to |device_input_layout_->format()|,
+  // keeping the size at |visible_size| and requiring the output buffers to
+  // be of at least |device_input_layout_->coded_size()|.
+  // |input_storage_type| can be STORAGE_SHMEM and STORAGE_MOJO_SHARED_BUFFER.
+  // However, it doesn't matter VideoFrame::STORAGE_OWNED_MEMORY is specified
+  // for |input_storage_type| here, as long as VideoFrame on Process()'s data
+  // can be accessed by VideoFrame::data().
+  image_processor_ = ImageProcessorFactory::Create(
+      ImageProcessor::PortConfig(input_layout, visible_size,
+                                 {VideoFrame::STORAGE_OWNED_MEMORY}),
+      ImageProcessor::PortConfig(
+          output_layout, visible_size,
+          {VideoFrame::STORAGE_DMABUFS, VideoFrame::STORAGE_OWNED_MEMORY}),
+      // Try OutputMode::ALLOCATE first because we want v4l2IP chooses
+      // ALLOCATE mode. For libyuvIP, it accepts only IMPORT.
+      {ImageProcessor::OutputMode::ALLOCATE,
+       ImageProcessor::OutputMode::IMPORT},
+      kImageProcBufferCount,
+      // Unretained(this) is safe here, because image_processor is destroyed
+      // before video_encoder_thread stops.
+      BindToCurrentLoop(
+          base::BindRepeating(&V4L2VideoEncodeAccelerator::ImageProcessorError,
+                              base::Unretained(this))));
+  if (!image_processor_) {
+    VLOGF(1) << "Failed initializing image processor";
+    return false;
+  }
+
+  // The output of image processor is the input of encoder. Output coded
+  // width of processor must be the same as input coded width of encoder.
+  // Output coded height of processor can be larger but not smaller than the
+  // input coded height of encoder. For example, suppose input size of encoder
+  // is 320x193. It is OK if the output of processor is 320x208.
+  const auto& ip_output_size = image_processor_->output_layout().coded_size();
+  if (ip_output_size.width() != output_layout.coded_size().width() ||
+      ip_output_size.height() < output_layout.coded_size().height()) {
+    VLOGF(1) << "Invalid image processor output coded size "
+             << ip_output_size.ToString() << ", expected output coded size is "
+             << output_layout.coded_size().ToString();
+    return false;
+  }
+
+  // Initialize |free_image_processor_output_buffer_indices_|.
+  free_image_processor_output_buffer_indices_.resize(kImageProcBufferCount);
+  std::iota(free_image_processor_output_buffer_indices_.begin(),
+            free_image_processor_output_buffer_indices_.end(), 0);
+  return AllocateImageProcessorOutputBuffers(kImageProcBufferCount,
+                                             visible_size);
+}
+
+bool V4L2VideoEncodeAccelerator::AllocateImageProcessorOutputBuffers(
+    size_t count,
+    const gfx::Size& visible_size) {
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(image_processor_);
   // Allocate VideoFrames for image processor output if its mode is IMPORT.
   if (image_processor_->output_mode() != ImageProcessor::OutputMode::IMPORT) {
     return true;
   }
 
-  image_processor_output_buffers_.resize(kImageProcBufferCount);
+  image_processor_output_buffers_.resize(count);
   const auto output_storage_type = image_processor_->output_storage_type();
-  for (size_t i = 0; i < kImageProcBufferCount; i++) {
+  for (size_t i = 0; i < count; i++) {
     switch (output_storage_type) {
       case VideoFrame::STORAGE_OWNED_MEMORY:
         image_processor_output_buffers_[i] = VideoFrame::CreateFrameWithLayout(
-            *device_input_layout_, gfx::Rect(visible_size_), visible_size_,
+            *device_input_layout_, gfx::Rect(visible_size), visible_size,
             base::TimeDelta(), true);
         if (!image_processor_output_buffers_[i]) {
           VLOG(1) << "Failed to create VideoFrame";
@@ -329,6 +370,7 @@
 }
 
 bool V4L2VideoEncodeAccelerator::InitInputMemoryType(const Config& config) {
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   if (image_processor_) {
     const auto storage_type = image_processor_->output_storage_type();
     if (storage_type == VideoFrame::STORAGE_DMABUFS) {
@@ -354,7 +396,7 @@
 }
 
 void V4L2VideoEncodeAccelerator::ImageProcessorError() {
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   VLOGF(1) << "Image processor error";
   NOTIFY_ERROR(kPlatformFailureError);
 }
@@ -364,51 +406,10 @@
   DVLOGF(4) << "force_keyframe=" << force_keyframe;
   DCHECK(child_task_runner_->BelongsToCurrentThread());
 
-  if (image_processor_) {
-    if (!free_image_processor_output_buffer_indices_.empty()) {
-      // Create a VideoFrame by wrapping an instance from
-      // |image_processor_output_buffers_|. The new VideoFrame has its own life
-      // cycle but shares underlying payload from the VideoFrame being wrapped.
-      // When the VideoEncodeAccelerator finish processing ImageProcessor's
-      // output frame, the frame is no longer referenced, hence trigger
-      // destruction observer to recycle the frame.
-      const size_t output_buffer_index =
-          free_image_processor_output_buffer_indices_.back();
-      free_image_processor_output_buffer_indices_.pop_back();
-      if (image_processor_->output_mode() ==
-          ImageProcessor::OutputMode::IMPORT) {
-        const auto& buf = image_processor_output_buffers_[output_buffer_index];
-        auto output_frame = VideoFrame::WrapVideoFrame(
-            *buf, buf->format(), buf->visible_rect(), buf->natural_size());
-
-        // We have to bind |weak_this| for FrameProcessed, because child
-        // thread is outlive this V4L2VideoEncodeAccelerator.
-        if (!image_processor_->Process(
-                frame, std::move(output_frame),
-                base::BindOnce(&V4L2VideoEncodeAccelerator::FrameProcessed,
-                               weak_this_, force_keyframe, frame->timestamp(),
-                               output_buffer_index))) {
-          NOTIFY_ERROR(kPlatformFailureError);
-        }
-      } else {
-        // We have to bind |weak_this| for FrameProcessed, because child
-        // thread is outlive this V4L2VideoEncodeAccelerator.
-        if (!image_processor_->Process(
-                frame, base::BindOnce(
-                           &V4L2VideoEncodeAccelerator::FrameProcessed,
-                           weak_this_, force_keyframe, frame->timestamp()))) {
-          NOTIFY_ERROR(kPlatformFailureError);
-        }
-      }
-    } else {
-      image_processor_input_queue_.emplace(std::move(frame), force_keyframe);
-    }
-  } else {
-    encoder_thread_.task_runner()->PostTask(
-        FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::EncodeTask,
-                                  base::Unretained(this), std::move(frame),
-                                  force_keyframe));
-  }
+  encoder_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&V4L2VideoEncodeAccelerator::EncodeTask,
+                     base::Unretained(this), std::move(frame), force_keyframe));
 }
 
 void V4L2VideoEncodeAccelerator::UseOutputBitstreamBuffer(
@@ -416,24 +417,10 @@
   DVLOGF(4) << "id=" << buffer.id();
   DCHECK(child_task_runner_->BelongsToCurrentThread());
 
-  if (buffer.size() < output_buffer_byte_size_) {
-    NOTIFY_ERROR(kInvalidArgumentError);
-    return;
-  }
-
-  auto shm = std::make_unique<UnalignedSharedMemory>(buffer.TakeRegion(),
-                                                     buffer.size(), false);
-  if (!shm->MapAt(buffer.offset(), buffer.size())) {
-    NOTIFY_ERROR(kPlatformFailureError);
-    return;
-  }
-
-  std::unique_ptr<BitstreamBufferRef> buffer_ref(
-      new BitstreamBufferRef(buffer.id(), std::move(shm)));
   encoder_thread_.task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(&V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask,
-                     base::Unretained(this), std::move(buffer_ref)));
+                     base::Unretained(this), std::move(buffer)));
 }
 
 void V4L2VideoEncodeAccelerator::RequestEncodingParametersChange(
@@ -457,8 +444,6 @@
   client_ptr_factory_.reset();
   weak_this_ptr_factory_.InvalidateWeakPtrs();
 
-  image_processor_ = nullptr;
-
   // If the encoder thread is running, destroy using posted task.
   if (encoder_thread_.IsRunning()) {
     encoder_thread_.task_runner()->PostTask(
@@ -526,38 +511,31 @@
     base::TimeDelta timestamp,
     size_t output_buffer_index,
     scoped_refptr<VideoFrame> frame) {
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DVLOGF(4) << "force_keyframe=" << force_keyframe
             << ", output_buffer_index=" << output_buffer_index;
   DCHECK_GE(output_buffer_index, 0u);
-  DCHECK(encoder_thread_.IsRunning());
-  DCHECK(!weak_this_.WasInvalidated());
 
-  frame->AddDestructionObserver(BindToCurrentLoop(base::BindOnce(
-      &V4L2VideoEncodeAccelerator::ReuseImageProcessorOutputBuffer, weak_this_,
-      output_buffer_index)));
-
+  encoder_input_queue_.emplace(std::move(frame), force_keyframe,
+                               output_buffer_index);
   encoder_thread_.task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::EncodeTask,
-                                base::Unretained(this), frame, force_keyframe));
+      FROM_HERE, base::BindOnce(&V4L2VideoEncodeAccelerator::Enqueue,
+                                base::Unretained(this)));
 }
 
 void V4L2VideoEncodeAccelerator::ReuseImageProcessorOutputBuffer(
     size_t output_buffer_index) {
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DVLOGF(4) << "output_buffer_index=" << output_buffer_index;
   free_image_processor_output_buffer_indices_.push_back(output_buffer_index);
-  if (!image_processor_input_queue_.empty()) {
-    InputFrameInfo frame_info = image_processor_input_queue_.front();
-    image_processor_input_queue_.pop();
-    Encode(frame_info.frame, frame_info.force_keyframe);
-  }
+  InputImageProcessorTask();
 }
 
 size_t V4L2VideoEncodeAccelerator::CopyIntoOutputBuffer(
     const uint8_t* bitstream_data,
     size_t bitstream_size,
     std::unique_ptr<BitstreamBufferRef> buffer_ref) {
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   uint8_t* dst_ptr = static_cast<uint8_t*>(buffer_ref->shm->memory());
   size_t remaining_dst_size = buffer_ref->shm->size();
 
@@ -635,16 +613,73 @@
     return;
   }
 
-  encoder_input_queue_.emplace(std::move(frame), force_keyframe);
-  Enqueue();
+  if (image_processor_) {
+    image_processor_input_queue_.emplace(std::move(frame), force_keyframe);
+    InputImageProcessorTask();
+  } else {
+    encoder_input_queue_.emplace(std::move(frame), force_keyframe);
+    Enqueue();
+  }
+}
+
+void V4L2VideoEncodeAccelerator::InputImageProcessorTask() {
+  if (free_image_processor_output_buffer_indices_.empty())
+    return;
+  if (image_processor_input_queue_.empty())
+    return;
+  const size_t output_buffer_index =
+      free_image_processor_output_buffer_indices_.back();
+  free_image_processor_output_buffer_indices_.pop_back();
+
+  InputFrameInfo frame_info = std::move(image_processor_input_queue_.front());
+  image_processor_input_queue_.pop();
+  auto frame = std::move(frame_info.frame);
+  const bool force_keyframe = frame_info.force_keyframe;
+  auto timestamp = frame->timestamp();
+  if (image_processor_->output_mode() == ImageProcessor::OutputMode::IMPORT) {
+    const auto& buf = image_processor_output_buffers_[output_buffer_index];
+    auto output_frame = VideoFrame::WrapVideoFrame(
+        *buf, buf->format(), buf->visible_rect(), buf->natural_size());
+
+    // Unretained(this) is safe here, because image_processor is destroyed
+    // before video_encoder_thread stops.
+    if (!image_processor_->Process(
+            std::move(frame), std::move(output_frame),
+            BindToCurrentLoop(
+                base::BindOnce(&V4L2VideoEncodeAccelerator::FrameProcessed,
+                               base::Unretained(this), force_keyframe,
+                               timestamp, output_buffer_index)))) {
+      NOTIFY_ERROR(kPlatformFailureError);
+    }
+  } else {
+    if (!image_processor_->Process(
+            std::move(frame),
+            BindToCurrentLoop(base::BindOnce(
+                &V4L2VideoEncodeAccelerator::FrameProcessed,
+                base::Unretained(this), force_keyframe, timestamp)))) {
+      NOTIFY_ERROR(kPlatformFailureError);
+    }
+  }
 }
 
 void V4L2VideoEncodeAccelerator::UseOutputBitstreamBufferTask(
-    std::unique_ptr<BitstreamBufferRef> buffer_ref) {
-  DVLOGF(4) << "id=" << buffer_ref->id;
+    BitstreamBuffer buffer) {
+  DVLOGF(4) << "id=" << buffer.id();
   DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
 
-  encoder_output_queue_.push_back(std::move(buffer_ref));
+  if (buffer.size() < output_buffer_byte_size_) {
+    NOTIFY_ERROR(kInvalidArgumentError);
+    return;
+  }
+  auto shm = std::make_unique<UnalignedSharedMemory>(buffer.TakeRegion(),
+                                                     buffer.size(), false);
+  if (!shm->MapAt(buffer.offset(), buffer.size())) {
+    NOTIFY_ERROR(kPlatformFailureError);
+    return;
+  }
+
+  encoder_output_queue_.push_back(
+      std::make_unique<BitstreamBufferRef>(buffer.id(), std::move(shm)));
   Enqueue();
 
   if (encoder_state_ == kInitialized) {
@@ -668,6 +703,13 @@
 
   // Set our state to kError, and early-out all tasks.
   encoder_state_ = kError;
+
+  if (encoder_thread_.task_runner() &&
+      encoder_thread_.task_runner()->BelongsToCurrentThread()) {
+    DestroyInputBuffers();
+    DestroyOutputBuffers();
+    image_processor_ = nullptr;
+  }
 }
 
 void V4L2VideoEncodeAccelerator::ServiceDeviceTask() {
@@ -819,6 +861,8 @@
     input_record.at_device = false;
 
     input_record.frame = NULL;
+    if (input_record.ip_output_buffer_index)
+      ReuseImageProcessorOutputBuffer(*input_record.ip_output_buffer_index);
     free_input_buffers_.push_back(dqbuf.index);
     input_buffer_queued_count_--;
   }
@@ -890,6 +934,7 @@
 
 bool V4L2VideoEncodeAccelerator::EnqueueInputRecord() {
   DVLOGF(4);
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!free_input_buffers_.empty());
   DCHECK(!encoder_input_queue_.empty());
   TRACE_EVENT0("media,gpu", "V4L2VEA::EnqueueInputRecord");
@@ -977,6 +1022,7 @@
   IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf);
   input_record.at_device = true;
   input_record.frame = frame;
+  input_record.ip_output_buffer_index = frame_info.ip_output_buffer_index;
   encoder_input_queue_.pop();
   free_input_buffers_.pop_back();
   input_buffer_queued_count_++;
@@ -985,6 +1031,7 @@
 
 bool V4L2VideoEncodeAccelerator::EnqueueOutputRecord() {
   DVLOGF(4);
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!free_output_buffers_.empty());
   DCHECK(!encoder_output_queue_.empty());
   TRACE_EVENT0("media,gpu", "V4L2VEA::EnqueueOutputRecord");
@@ -1166,7 +1213,7 @@
 
 bool V4L2VideoEncodeAccelerator::SetOutputFormat(
     VideoCodecProfile output_profile) {
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!input_streamon_);
   DCHECK(!output_streamon_);
 
@@ -1195,7 +1242,7 @@
 bool V4L2VideoEncodeAccelerator::NegotiateInputFormat(
     VideoPixelFormat input_format) {
   VLOGF(2);
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!input_streamon_);
   DCHECK(!output_streamon_);
 
@@ -1251,7 +1298,7 @@
 bool V4L2VideoEncodeAccelerator::SetFormats(VideoPixelFormat input_format,
                                             VideoCodecProfile output_profile) {
   VLOGF(2);
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!input_streamon_);
   DCHECK(!output_streamon_);
 
@@ -1295,6 +1342,7 @@
 }
 
 bool V4L2VideoEncodeAccelerator::IsCtrlExposed(uint32_t ctrl_id) {
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   struct v4l2_queryctrl query_ctrl{};
   query_ctrl.id = ctrl_id;
 
@@ -1303,6 +1351,7 @@
 
 bool V4L2VideoEncodeAccelerator::SetExtCtrls(
     std::vector<struct v4l2_ext_control> ctrls) {
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   struct v4l2_ext_controls ext_ctrls{};
   ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
   ext_ctrls.count = ctrls.size();
@@ -1311,6 +1360,7 @@
 }
 
 bool V4L2VideoEncodeAccelerator::InitControls(const Config& config) {
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   std::vector<struct v4l2_ext_control> ctrls;
   struct v4l2_ext_control ctrl{};
 
@@ -1432,8 +1482,6 @@
 
 bool V4L2VideoEncodeAccelerator::CreateInputBuffers() {
   VLOGF(2);
-  // This function runs on encoder_thread_ after output buffers have been
-  // provided by the client.
   DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!input_streamon_);
 
@@ -1454,7 +1502,7 @@
 
 bool V4L2VideoEncodeAccelerator::CreateOutputBuffers() {
   VLOGF(2);
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!output_streamon_);
 
   struct v4l2_requestbuffers reqbufs{};
@@ -1493,7 +1541,7 @@
 
 void V4L2VideoEncodeAccelerator::DestroyInputBuffers() {
   VLOGF(2);
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!input_streamon_);
 
   free_input_buffers_.clear();
@@ -1512,7 +1560,7 @@
 
 void V4L2VideoEncodeAccelerator::DestroyOutputBuffers() {
   VLOGF(2);
-  DCHECK(child_task_runner_->BelongsToCurrentThread());
+  DCHECK(encoder_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!output_streamon_);
 
   free_output_buffers_.clear();
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.h b/media/gpu/v4l2/v4l2_video_encode_accelerator.h
index 05a88fab..f52ba96 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.h
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.h
@@ -70,6 +70,10 @@
     ~InputRecord();
     bool at_device;
     scoped_refptr<VideoFrame> frame;
+
+    // This is valid only if image processor is used. The buffer associated with
+    // this index can be reused in Dequeue().
+    base::Optional<size_t> ip_output_buffer_index;
   };
 
   // Record for output buffers.
@@ -86,10 +90,17 @@
   struct InputFrameInfo {
     InputFrameInfo();
     InputFrameInfo(scoped_refptr<VideoFrame> frame, bool force_keyframe);
+    InputFrameInfo(scoped_refptr<VideoFrame> frame,
+                   bool force_keyframe,
+                   size_t index);
     InputFrameInfo(const InputFrameInfo&);
     ~InputFrameInfo();
     scoped_refptr<VideoFrame> frame;
     bool force_keyframe;
+
+    // This is valid only if image processor is used. This info needs to be
+    // propagated to InputRecord.
+    base::Optional<size_t> ip_output_buffer_index;
   };
 
   enum {
@@ -130,8 +141,7 @@
 
   // Add a BitstreamBuffer to the queue of buffers ready to be used for encoder
   // output.
-  void UseOutputBitstreamBufferTask(
-      std::unique_ptr<BitstreamBufferRef> buffer_ref);
+  void UseOutputBitstreamBufferTask(BitstreamBuffer buffer);
 
   // Device destruction task.
   void DestroyTask();
@@ -178,10 +188,24 @@
   // these (e.g. in Initialize() or Destroy()).
   //
 
+  // Create image processor that will process input_layout to output_layout. The
+  // visible size of processed video frames are |visible_size|.
+  bool CreateImageProcessor(const VideoFrameLayout& input_layout,
+                            const VideoFrameLayout& output_layout,
+                            const gfx::Size& visible_size);
+  // Process one video frame in |image_processor_input_queue_| by
+  // |image_processor_|.
+  void InputImageProcessorTask();
+
   // Change encoding parameters.
   void RequestEncodingParametersChangeTask(uint32_t bitrate,
                                            uint32_t framerate);
 
+  // Do several initializations (e.g. set up format) on |encoder_thread_|.
+  void InitializeTask(const Config& config,
+                      bool* result,
+                      base::WaitableEvent* done);
+
   // Set up formats and initialize the device for them.
   bool SetFormats(VideoPixelFormat input_format,
                   VideoCodecProfile output_profile);
@@ -212,9 +236,10 @@
   // false otherwise.
   bool IsCtrlExposed(uint32_t ctrl_id);
 
-  // Allocates video frames for image processor's output buffers.
-  // Returns false if there's something wrong.
-  bool AllocateImageProcessorOutputBuffers();
+  // Allocates |count| video frames with |visible_size| for image processor's
+  // output buffers. Returns false if there's something wrong.
+  bool AllocateImageProcessorOutputBuffers(size_t count,
+                                           const gfx::Size& visible_size);
 
   // Recycle output buffer of image processor with |output_buffer_index|.
   void ReuseImageProcessorOutputBuffer(size_t output_buffer_index);
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc
index 30cf28a..e2399f4 100644
--- a/pdf/pdfium/pdfium_engine.cc
+++ b/pdf/pdfium/pdfium_engine.cc
@@ -3023,7 +3023,8 @@
 
       visible_selection.Offset(-dirty_in_screen.point().x(),
                                -dirty_in_screen.point().y());
-      Highlight(region, stride, visible_selection, &highlighted_rects);
+      Highlight(region, stride, visible_selection, kHighlightColorR,
+                kHighlightColorG, kHighlightColorB, &highlighted_rects);
     }
   }
 
@@ -3034,7 +3035,8 @@
 
     visible_selection.Offset(-dirty_in_screen.point().x(),
                              -dirty_in_screen.point().y());
-    Highlight(region, stride, visible_selection, &highlighted_rects);
+    Highlight(region, stride, visible_selection, kHighlightColorR,
+              kHighlightColorG, kHighlightColorB, &highlighted_rects);
   }
   form_highlights_.clear();
 }
@@ -3139,6 +3141,9 @@
 void PDFiumEngine::Highlight(void* buffer,
                              int stride,
                              const pp::Rect& rect,
+                             int color_red,
+                             int color_green,
+                             int color_blue,
                              std::vector<pp::Rect>* highlighted_rects) {
   if (!buffer)
     return;
@@ -3175,9 +3180,9 @@
         continue;
 
       uint8_t* pixel = static_cast<uint8_t*>(buffer) + y * stride + x * 4;
-      pixel[0] = static_cast<uint8_t>(pixel[0] * (kHighlightColorB / 255.0));
-      pixel[1] = static_cast<uint8_t>(pixel[1] * (kHighlightColorG / 255.0));
-      pixel[2] = static_cast<uint8_t>(pixel[2] * (kHighlightColorR / 255.0));
+      pixel[0] = static_cast<uint8_t>(pixel[0] * (color_blue / 255.0));
+      pixel[1] = static_cast<uint8_t>(pixel[1] * (color_green / 255.0));
+      pixel[2] = static_cast<uint8_t>(pixel[2] * (color_red / 255.0));
     }
   }
 }
diff --git a/pdf/pdfium/pdfium_engine.h b/pdf/pdfium/pdfium_engine.h
index e66201ad..96b7841 100644
--- a/pdf/pdfium/pdfium_engine.h
+++ b/pdf/pdfium/pdfium_engine.h
@@ -399,6 +399,9 @@
   void Highlight(void* buffer,
                  int stride,
                  const pp::Rect& rect,
+                 int color_red,
+                 int color_green,
+                 int color_blue,
                  std::vector<pp::Rect>* highlighted_rects);
 
   // Helper function to convert a device to page coordinates.  If the page is
diff --git a/services/tracing/perfetto/producer_host.cc b/services/tracing/perfetto/producer_host.cc
index 891773a..0958629 100644
--- a/services/tracing/perfetto/producer_host.cc
+++ b/services/tracing/perfetto/producer_host.cc
@@ -141,11 +141,16 @@
 // sanitization here because ProducerEndpoint::CommitData() (And any other
 // ProducerEndpoint methods) are designed to deal with malformed / malicious
 // inputs.
-void ProducerHost::CommitData(const perfetto::CommitDataRequest& data_request) {
+void ProducerHost::CommitData(const perfetto::CommitDataRequest& data_request,
+                              CommitDataCallback callback) {
   if (on_commit_callback_for_testing_) {
     on_commit_callback_for_testing_.Run(data_request);
   }
-  producer_endpoint_->CommitData(data_request);
+  // This assumes that CommitData() will execute the callback synchronously.
+  producer_endpoint_->CommitData(data_request, [&callback]() {
+    std::move(callback).Run();
+  });
+  DCHECK(!callback);  // Should have been run synchronously above.
 }
 
 void ProducerHost::RegisterDataSource(
diff --git a/services/tracing/perfetto/producer_host.h b/services/tracing/perfetto/producer_host.h
index 91bb082..9d531482 100644
--- a/services/tracing/perfetto/producer_host.h
+++ b/services/tracing/perfetto/producer_host.h
@@ -69,7 +69,8 @@
   // This interface gets called by the per-process ProducerClients
   // to signal that there's changes to be committed to the
   // Shared Memory buffer (like finished chunks).
-  void CommitData(const perfetto::CommitDataRequest& data_request) override;
+  void CommitData(const perfetto::CommitDataRequest& data_request,
+                  CommitDataCallback callback) override;
 
   // Called by the ProducerClient to signal the Host that it can
   // provide a specific data source.
diff --git a/services/tracing/public/cpp/perfetto/producer_client.cc b/services/tracing/public/cpp/perfetto/producer_client.cc
index 0b521ee..51e00bb 100644
--- a/services/tracing/public/cpp/perfetto/producer_client.cc
+++ b/services/tracing/public/cpp/perfetto/producer_client.cc
@@ -241,6 +241,12 @@
 
 void ProducerClient::CommitData(const perfetto::CommitDataRequest& commit,
                                 CommitDataCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto commit_callback =
+      callback ? base::BindOnce([](CommitDataCallback callback) { callback(); },
+                                callback)
+               : mojom::ProducerHost::CommitDataCallback();
+
   // We need to make sure the CommitData IPC is sent off without triggering any
   // trace events, as that could stall waiting for SMB chunks to be freed up
   // which requires the tracing service to receive the IPC.
@@ -248,17 +254,11 @@
     AutoThreadLocalBoolean thread_is_in_trace_event(
         TraceEventDataSource::GetThreadIsInTraceEventTLS());
 
-    producer_host_->CommitData(commit);
+    producer_host_->CommitData(commit, std::move(commit_callback));
     return;
   }
 
-  producer_host_->CommitData(commit);
-}
-
-void ProducerClient::CommitDataOnSequence(
-    const perfetto::CommitDataRequest& request) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  producer_host_->CommitData(request);
+  producer_host_->CommitData(commit, std::move(commit_callback));
 }
 
 perfetto::SharedMemory* ProducerClient::shared_memory() const {
diff --git a/services/tracing/public/cpp/perfetto/producer_client.h b/services/tracing/public/cpp/perfetto/producer_client.h
index ecc8607..cf4f56c3 100644
--- a/services/tracing/public/cpp/perfetto/producer_client.h
+++ b/services/tracing/public/cpp/perfetto/producer_client.h
@@ -97,7 +97,6 @@
  private:
   friend class base::NoDestructor<ProducerClient>;
 
-  void CommitDataOnSequence(const perfetto::CommitDataRequest& request);
   void BindClientAndHostPipesOnSequence(mojom::ProducerClientRequest,
                                         mojom::ProducerHostPtrInfo);
 
diff --git a/services/tracing/public/mojom/perfetto_service.mojom b/services/tracing/public/mojom/perfetto_service.mojom
index b9241b0..f6cbddb 100644
--- a/services/tracing/public/mojom/perfetto_service.mojom
+++ b/services/tracing/public/mojom/perfetto_service.mojom
@@ -102,8 +102,9 @@
   // 2) Patch data (i.e. apply diff) that has been previously copied into the
   //    tracing buffer (if it's not been overwritten).
   // The service is robust in terms of tolerating malformed or malicious
-  // requests.
-  CommitData(CommitDataRequest data_request);
+  // requests. The callback is run after the service has handled the commit
+  // request.
+  CommitData(CommitDataRequest data_request) => ();
 
   // Called by a ProducerClient to let the Host know it can provide a specific
   // datasource.
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index ed2cca6..87e4f01 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -8154,8 +8154,7 @@
     "scripts": [
       {
         "name": "test_traffic_annotation_auditor",
-        "script": "test_traffic_annotation_auditor.py",
-        "swarming": {}
+        "script": "test_traffic_annotation_auditor.py"
       }
     ]
   },
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 82ef370..5310f68 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1733,10 +1733,16 @@
       "../../chrome/android/monochrome/scripts/monochrome_apk_checker.py",
       "--chrome-apk",
       "apks/ChromeModernPublic.apk",
+      "--chrome-pathmap",
+      "apks/ChromeModernPublic.apk.pathmap.txt",
       "--system-webview-apk",
       "apks/SystemWebView.apk",
+      "--system-webview-pathmap",
+      "apks/SystemWebView.apk.pathmap.txt",
       "--monochrome-apk",
       "apks/MonochromePublic.apk",
+      "--monochrome-pathmap",
+      "apks/MonochromePublic.apk.pathmap.txt",
     ],
     "label": "//chrome/android/monochrome:monochrome_apk_checker",
     "script": "//testing/scripts/monochrome_apk_checker_wrapper.py",
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 57a94a0..a5b5fba 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1703,9 +1703,6 @@
         }
       },
       'linux-annotator-rel': {
-        'mixins': [
-          'linux-trusty',
-        ],
         'test_suites': {
           'scripts': 'test_traffic_annotation_auditor_script',
         }
diff --git a/testing/scripts/monochrome_apk_checker_wrapper.py b/testing/scripts/monochrome_apk_checker_wrapper.py
index a05f8bd..983c378 100755
--- a/testing/scripts/monochrome_apk_checker_wrapper.py
+++ b/testing/scripts/monochrome_apk_checker_wrapper.py
@@ -9,14 +9,31 @@
 
 import argparse
 import json
+import os
 import sys
 import subprocess
 import time
 
-
 import common
 
 
+def _PathExists(path):
+  return path is not None and os.path.exists(path)
+
+
+def _ForwardOptionalArgs(args):
+  forwardable_args = []
+  if _PathExists(args.monochrome_pathmap):
+    forwardable_args += ['--monochrome-pathmap', args.monochrome_pathmap]
+  if _PathExists(args.chrome_pathmap):
+    forwardable_args += ['--chrome-pathmap', args.chrome_pathmap]
+  if _PathExists(args.system_webview_pathmap):
+    forwardable_args += [
+        '--system-webview-pathmap', args.system_webview_pathmap
+    ]
+  return forwardable_args
+
+
 def main():
   parser = argparse.ArgumentParser(prog='monochrome_apk_checker_wrapper')
 
@@ -30,14 +47,27 @@
   # Ignored, but required to satisfy the isolated_script interface.
   # We aren't a perf test, so don't have any perf output.
   parser.add_argument('--isolated-script-test-perf-output')
+
+  # We must intercept the pathmap args since they are always passed by the
+  # trybots but the files in question may not actually exist (path shortening
+  # may not be enabled for all apks). Check if the files exist and forward the
+  # arg iff they are.
+  parser.add_argument(
+      '--monochrome-pathmap', help='The monochrome APK resources pathmap path.')
+  parser.add_argument(
+      '--chrome-pathmap', help='The chrome APK resources pathmap path.')
+  parser.add_argument(
+      '--system-webview-pathmap',
+      help='The system webview APK resources pathmap path.')
+
   args, extra = parser.parse_known_args(sys.argv[1:])
 
   if args.isolated_script_test_filter and (
       'monochrome_apk_checker' not in args.isolated_script_test_filter):
-      parser.error('isolated-script-test-filter has invalid test: %s' % (
-          args.isolated_script_test_filter))
+    parser.error('isolated-script-test-filter has invalid test: %s' %
+                 (args.isolated_script_test_filter))
 
-  cmd = [args.script] + extra
+  cmd = [args.script] + extra + _ForwardOptionalArgs(args)
 
   start_time = time.time()
   ret = subprocess.call(cmd)
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index e6259db..757a9d5 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -367,6 +367,7 @@
     "web/mac/web_substring_util.h",
     "web/mac/web_substring_util.h",
     "web/modules/autofill/web_form_element_observer.h",
+    "web/modules/mediastream/local_media_stream_audio_source.h",
     "web/modules/mediastream/media_stream_constraints_util.h",
     "web/modules/mediastream/media_stream_constraints_util_sets.h",
     "web/modules/mediastream/media_stream_constraints_util_video_content.h",
diff --git a/third_party/blink/public/platform/DEPS b/third_party/blink/public/platform/DEPS
index 91bd315..ec73d34 100644
--- a/third_party/blink/public/platform/DEPS
+++ b/third_party/blink/public/platform/DEPS
@@ -23,6 +23,7 @@
     "+build/build_config.h",
     "+cc",
     "+components/viz/common",
+    "+media/base/audio_capturer_source.h",
     "+media/base/audio_renderer_sink.h",
     "+media/base/video_transformation.h",
     "+mojo/public",
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index e5fdbad..5b29bbe 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -41,6 +41,7 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
+#include "media/base/audio_capturer_source.h"
 #include "media/base/audio_renderer_sink.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/message_pipe.h"
@@ -80,6 +81,7 @@
 
 namespace media {
 struct AudioSinkParameters;
+struct AudioSourceParameters;
 class GpuVideoAcceleratorFactories;
 }
 
@@ -545,6 +547,13 @@
   // called by platform/graphics/ is fine.
   virtual bool IsGpuCompositingDisabled() { return true; }
 
+  // Media stream ----------------------------------------------------
+  virtual scoped_refptr<media::AudioCapturerSource> NewAudioCapturerSource(
+      blink::WebLocalFrame* web_frame,
+      const media::AudioSourceParameters& params) {
+    return nullptr;
+  }
+
   // WebRTC ----------------------------------------------------------
 
   // Creates a WebRTCPeerConnectionHandler for RTCPeerConnection.
diff --git a/content/renderer/media/stream/local_media_stream_audio_source.h b/third_party/blink/public/web/modules/mediastream/local_media_stream_audio_source.h
similarity index 62%
rename from content/renderer/media/stream/local_media_stream_audio_source.h
rename to third_party/blink/public/web/modules/mediastream/local_media_stream_audio_source.h
index c115e5b..2b77bf8 100644
--- a/content/renderer/media/stream/local_media_stream_audio_source.h
+++ b/third_party/blink/public/web/modules/mediastream/local_media_stream_audio_source.h
@@ -2,24 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_STREAM_LOCAL_MEDIA_STREAM_AUDIO_SOURCE_H_
-#define CONTENT_RENDERER_MEDIA_STREAM_LOCAL_MEDIA_STREAM_AUDIO_SOURCE_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_LOCAL_MEDIA_STREAM_AUDIO_SOURCE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_LOCAL_MEDIA_STREAM_AUDIO_SOURCE_H_
+
+#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 
 #include <string>
 
-#include "content/common/content_export.h"
 #include "media/base/audio_capturer_source.h"
-#include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
+#include "third_party/blink/public/platform/web_common.h"
 
-namespace content {
+namespace blink {
+
+class WebLocalFrame;
 
 // Represents a local source of audio data generated by an AudioInputDevice.
-// Uses content::AudioDeviceFactory to auto-create the AudioInputDevice, using
-// the parameters and session ID found in MediaStreamDevice, just before the
-// first track is connected. Audio data is transported directly to the tracks
-// (i.e., there is no audio processing).
-class CONTENT_EXPORT LocalMediaStreamAudioSource
-    : public blink::MediaStreamAudioSource,
+// Uses content::AudioDeviceFactory (indirectly through
+// blink::Platform::NewAudioRendererSink) to auto-create the AudioInputDevice,
+// using the parameters and session ID found in MediaStreamDevice, just before
+// the first track is connected. Audio data is transported directly to the
+// tracks (i.e., there is no audio processing).
+class BLINK_MODULES_EXPORT LocalMediaStreamAudioSource
+    : public MediaStreamAudioSource,
       public media::AudioCapturerSource::CaptureCallback {
  public:
   // |consumer_render_frame_id| references the RenderFrame that will consume the
@@ -27,8 +31,8 @@
   // ID are read from |device_info|. |requested_buffer_size| is the desired
   // buffer size for the audio hardware, a nullptr means to use the default.
   LocalMediaStreamAudioSource(
-      int consumer_render_frame_id,
-      const blink::MediaStreamDevice& device,
+      WebLocalFrame* web_frame,
+      const MediaStreamDevice& device,
       const int* requested_buffer_size,
       bool disable_local_echo,
       ConstraintsRepeatingCallback started_callback,
@@ -37,7 +41,7 @@
   ~LocalMediaStreamAudioSource() final;
 
   // MediaStreamAudioSource implementation.
-  void ChangeSourceImpl(const blink::MediaStreamDevice& new_device) final;
+  void ChangeSourceImpl(const MediaStreamDevice& new_device) final;
 
  private:
   // MediaStreamAudioSource implementation.
@@ -53,9 +57,11 @@
   void OnCaptureError(const std::string& message) final;
   void OnCaptureMuted(bool is_muted) final;
 
-  // The RenderFrame that will consume the audio data. Used when creating
-  // AudioInputDevices via the AudioDeviceFactory.
-  const int consumer_render_frame_id_;
+  // The WebLocalFrame that will consume the audio data. Used when creating
+  // AudioInputDevices via the AudioDeviceFactory (indirectly through
+  // blink::Platform).
+  class InternalFrame;
+  std::unique_ptr<InternalFrame> internal_consumer_frame_;
 
   // The device created by the AudioDeviceFactory in EnsureSourceIsStarted().
   scoped_refptr<media::AudioCapturerSource> source_;
@@ -70,6 +76,6 @@
   DISALLOW_COPY_AND_ASSIGN(LocalMediaStreamAudioSource);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_STREAM_LOCAL_MEDIA_STREAM_AUDIO_SOURCE_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_LOCAL_MEDIA_STREAM_AUDIO_SOURCE_H_
diff --git a/third_party/blink/renderer/bindings/BUILD.gn b/third_party/blink/renderer/bindings/BUILD.gn
index 8b974c6..f52d1e4 100644
--- a/third_party/blink/renderer/bindings/BUILD.gn
+++ b/third_party/blink/renderer/bindings/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//third_party/blink/renderer/bindings/scripts/scripts.gni")
+import("//third_party/blink/renderer/build/scripts/scripts.gni")
 import("//third_party/blink/renderer/modules/modules_idl_files.gni")
 
 action("interfaces_info") {
@@ -44,3 +45,32 @@
     "//third_party/blink/renderer/bindings/modules:modules_global_objects",
   ]
 }
+
+blink_python_runner("web_idl_database") {
+  script = "$bindings_scripts_dir/build_web_idl_database.py"
+
+  inputs =
+      [
+        "$bindings_core_output_dir/web_idl_collection_for_core.pickle",
+        "$bindings_modules_output_dir/web_idl_collection_for_modules.pickle",
+      ] + web_idl_scripts
+  outputs = [
+    "$bindings_output_dir/web_idl_database.pickle",
+  ]
+
+  args = [
+    "--output",
+    rebase_path("$bindings_output_dir/web_idl_database.pickle", root_build_dir),
+    "--",
+    rebase_path("$bindings_core_output_dir/web_idl_collection_for_core.pickle",
+                root_build_dir),
+    rebase_path(
+        "$bindings_modules_output_dir/web_idl_collection_for_modules.pickle",
+        root_build_dir),
+  ]
+
+  deps = [
+    "//third_party/blink/renderer/bindings/core:core_web_idl_collection",
+    "//third_party/blink/renderer/bindings/modules:modules_web_idl_collection",
+  ]
+}
diff --git a/third_party/blink/renderer/bindings/scripts/build_web_idl_database.py b/third_party/blink/renderer/bindings/scripts/build_web_idl_database.py
new file mode 100755
index 0000000..773130b
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/build_web_idl_database.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+#
+# 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.
+
+"""Generates a data collection of IDL information per component.
+This scripts parses IDL files and stores the result ASTs in a pickle file.
+The output file may contain information about component, too.
+"""
+
+import optparse
+import utilities
+from web_idl.idl_compiler import IdlCompiler
+from web_idl import ir_builder
+from web_idl.identifier_ir_map import IdentifierIRMap
+
+
+def parse_options():
+    parser = optparse.OptionParser()
+    parser.add_option('--output', type='string',
+                      help='pickle file to write down')
+    options, args = parser.parse_args()
+
+    if options.output is None:
+        parser.error('Must specify a pickle file to output using --output.')
+
+    return options, args
+
+
+def main():
+    options, filepaths = parse_options()
+    ir_map = IdentifierIRMap()
+    ir_builder.load_and_register_idl_definitions(filepaths, ir_map)
+    idl_compiler = IdlCompiler(ir_map)
+    idl_database = idl_compiler.build_database()
+    utilities.write_pickle_file(options.output, idl_database)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/third_party/blink/renderer/bindings/scripts/generate_web_idl_database.py b/third_party/blink/renderer/bindings/scripts/generate_web_idl_database.py
new file mode 100755
index 0000000..773130b
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/generate_web_idl_database.py
@@ -0,0 +1,41 @@
+#!/usr/bin/python
+#
+# 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.
+
+"""Generates a data collection of IDL information per component.
+This scripts parses IDL files and stores the result ASTs in a pickle file.
+The output file may contain information about component, too.
+"""
+
+import optparse
+import utilities
+from web_idl.idl_compiler import IdlCompiler
+from web_idl import ir_builder
+from web_idl.identifier_ir_map import IdentifierIRMap
+
+
+def parse_options():
+    parser = optparse.OptionParser()
+    parser.add_option('--output', type='string',
+                      help='pickle file to write down')
+    options, args = parser.parse_args()
+
+    if options.output is None:
+        parser.error('Must specify a pickle file to output using --output.')
+
+    return options, args
+
+
+def main():
+    options, filepaths = parse_options()
+    ir_map = IdentifierIRMap()
+    ir_builder.load_and_register_idl_definitions(filepaths, ir_map)
+    idl_compiler = IdlCompiler(ir_map)
+    idl_database = idl_compiler.build_database()
+    utilities.write_pickle_file(options.output, idl_database)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/third_party/blink/renderer/bindings/scripts/scripts.gni b/third_party/blink/renderer/bindings/scripts/scripts.gni
index 2006df9..b9bf7b3 100644
--- a/third_party/blink/renderer/bindings/scripts/scripts.gni
+++ b/third_party/blink/renderer/bindings/scripts/scripts.gni
@@ -68,16 +68,16 @@
                                   "web_idl/enumeration.py",
                                   "web_idl/extended_attribute.py",
                                   "web_idl/identifier_ir_map.py",
+                                  "web_idl/idl_compiler.py",
                                   "web_idl/idl_reference_proxy.py",
                                   "web_idl/idl_types.py",
                                   "web_idl/includes.py",
                                   "web_idl/interface.py",
+                                  "web_idl/ir_builder.py",
                                   "web_idl/literal_token.py",
                                   "web_idl/namespace.py",
                                   "web_idl/operation.py",
                                   "web_idl/typedef.py",
-                                  "web_idl/utilities.py",
-                                  "web_idl/idl_definition_builder.py",
                                 ],
                                 "abspath")
 
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/common.py b/third_party/blink/renderer/bindings/scripts/web_idl/common.py
index 152a27f..97f6da7 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/common.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/common.py
@@ -16,6 +16,7 @@
     identifier."""
 
     def __init__(self, identifier):
+        assert isinstance(identifier, Identifier)
         self._identifier = identifier
 
     @property
@@ -33,6 +34,8 @@
     class can have extended attributes."""
 
     def __init__(self, extended_attributes=None):
+        assert (extended_attributes is None
+                or isinstance(extended_attributes, ExtendedAttributes))
         self._extended_attributes = extended_attributes or ExtendedAttributes()
 
     @property
@@ -52,6 +55,8 @@
     provide some information for code generators."""
 
     def __init__(self, code_generator_info=None):
+        assert (code_generator_info is None
+                or isinstance(code_generator_info, CodeGeneratorInfo))
         self._code_generator_info = code_generator_info or CodeGeneratorInfo()
 
     @property
@@ -87,18 +92,19 @@
 
 
 class WithComponent(object):
-    """WithComponent class is an interface to show which components this
-    object belongs to."""
+    """
+    Implements |components| which is a Blink-specific layering concept of
+    components, such as 'core' and 'modules'.
 
-    # The order of |_COMPONENTS| shows the order of their dependencies.
-    # DO NOT change the order.
-    _COMPONENTS = (
-        'core',
-        'modules',
-    )
+    A single IDL definition such as 'interface' may consist from multiple IDL
+    fragments like partial interfaces and mixins, which may exist across
+    Blink components.  |components| is a list of Blink components of IDL
+    fragments that are involved into this object.
+    """
 
-    def __init__(self, components):
-        self._components = components
+    def __init__(self, component):
+        assert isinstance(component, Component)
+        self._components = [component]
 
     @property
     def components(self):
@@ -106,30 +112,75 @@
         Returns a list of components' names where this definition is defined
         @return tuple(Component)
         """
-        return self._components
+        return tuple(self._components)
 
 
 class DebugInfo(object):
-    """DebugInfo provides some information for debugging."""
+    """Provides information useful for debugging."""
 
-    def __init__(self, filepaths):
-        self._filepaths = tuple(filepaths)
+    class Location(object):
+        def __init__(self, filepath=None, line_number=None,
+                     column_number=None):
+            assert filepath is None or isinstance(filepath, str)
+            assert line_number is None or isinstance(line_number, int)
+            assert column_number is None or isinstance(column_number, int)
+            self._filepath = filepath
+            self._line_number = line_number
+            self._column_number = column_number
+
+        def __str__(self):
+            text = '{}'.format(self._filepath or '<<unknown path>>')
+            if self._line_number:
+                text += ':{}'.format(self._line_number)
+                if self._column_number:
+                    text += ':{}'.format(self._column_number)
+            return text
+
+        @property
+        def filepath(self):
+            return self._filepath
+
+        @property
+        def line_number(self):
+            return self._line_number
+
+        @property
+        def column_number(self):
+            return self._column_number
+
+    def __init__(self, location=None):
+        assert location is None or isinstance(location, DebugInfo.Location)
+        location = location or DebugInfo.Location()
+        # The first entry is the primary location, e.g. location of non-partial
+        # interface.  The rest is secondary locations, e.g. location of partial
+        # interfaces and mixins.
+        self._locations = [location]
 
     @property
-    def filepaths(self):
+    def location(self):
         """
-        Returns a list of filepaths where this IDL definition comes from.
-        @return tuple(FilePath)
+        Returns the primary location, i.e. location of the main definition.
+        @return DebugInfo.Location
         """
-        return self._filepaths
+        return self._locations[0]
+
+    @property
+    def all_locations(self):
+        """
+        Returns a list of locations of all related IDL definitions, including
+        partial definitions and mixins.
+        @return tuple(DebugInfo.Location)
+        """
+        return tuple(self._locations)
 
 
 class WithDebugInfo(object):
-    """WithDebugInfo class is an interface that its inheritances can have DebugInfo."""
+    """WithDebugInfo class is an interface that its inheritances can have
+    DebugInfo."""
 
     def __init__(self, debug_info=None):
-        self._debug_info = debug_info or DebugInfo(
-            filepaths=('<<unspecified>>', ))
+        assert debug_info is None or isinstance(debug_info, DebugInfo)
+        self._debug_info = debug_info or DebugInfo()
 
     @property
     def debug_info(self):
@@ -144,6 +195,7 @@
     it points a function like object."""
 
     def __init__(self, owner):
+        assert isinstance(owner, object)  # None is okay
         self._owner = owner
 
     @property
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py b/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py
index ef25fdb6..88cc4015 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/enumeration.py
@@ -19,6 +19,7 @@
              WithComponent, WithDebugInfo):
         def __init__(self,
                      identifier,
+                     values,
                      extended_attributes=None,
                      code_generator_info=None,
                      component=None,
@@ -32,6 +33,9 @@
             WithComponent.__init__(self, component)
             WithDebugInfo.__init__(self, debug_info)
 
+            assert isinstance(values, (list, tuple))
+            self.values = tuple(values)
+
     @property
     def values(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/identifier_ir_map.py b/third_party/blink/renderer/bindings/scripts/web_idl/identifier_ir_map.py
index 03a20db9..9fb34cc 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/identifier_ir_map.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/identifier_ir_map.py
@@ -128,8 +128,12 @@
                 irs = self.find_by_kind(ir.kind)
                 assert ir not in irs[ir.identifier]
             else:
-                self.find_by_identifier(ir.identifier)
-                assert False
+                duplicated_ir = self.find_by_identifier(ir.identifier)
+                # We don't allow to declare a definition of an IDL definition in
+                # multiple places.
+                raise ValueError('{} {} is defined twice.\n  {}\n  {}'.format(
+                    ir.kind, ir.identifier, ir.debug_info.location,
+                    duplicated_ir.debug_info.location))
         except KeyError:
             pass
         self.add(ir)
@@ -153,8 +157,8 @@
         else:
             assert identifier not in irs_per_kind, (
                 'Duplicated definition: {}\n  {}\n  {}'.format(
-                    identifier, ir.debug_info.filepaths,
-                    irs_per_kind[identifier].debug_info.filepaths))
+                    identifier, ir.debug_info.location,
+                    irs_per_kind[identifier].debug_info.location))
             irs_per_kind[identifier] = ir
 
     def find_by_identifier(self, identifier, skip_current_phase=False):
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
new file mode 100644
index 0000000..898e506
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -0,0 +1,78 @@
+# 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.
+
+
+class IdlCompiler(object):
+    """
+    Compiles IRs of Web IDL definitions into a database.
+
+    IdlCompiler works very closely with IRs.  IdlCompiler resolves a lot of
+    things such as; merge partial definitions, merge mixins into an interface,
+    resolve inheritance, etc.  These tasks must be done in order, and it's
+    represented with "(compilation) phase".
+
+    IdlCompiler works proceeding one phase to next phase.  A basic strategy is
+    like below.
+
+    1. prev_phase = self._ir_map.current_phase, next_phase = prev_phase + 1
+    2. for x = an IR in self._ir_map(phase=prev_phase)
+    2.1. y = process_and_update(x.copy())
+    2.2. self._ir_map(phase=next_phase).add(y)
+
+    Note that an old IR for 'x' remains internally.  See IdentifierIRMap for
+    the details.
+    """
+
+    def __init__(self, ir_map):
+        self._ir_map = ir_map
+
+    def build_database(self):
+        self._merge_partials()
+        self._merge_mixins()
+        self._resolve_inheritances()
+        self._resolve_exposures()
+        self._define_unions()
+        self._generate_database()
+
+    def _generate_database(self):
+        """
+        Returns an IDL database based on this compiler.
+        """
+        pass
+
+    def _merge_partials(self):
+        """
+        Merges partial definitions with corresponding non-partial definitions.
+        """
+        self._ir_map.move_to_new_phase()
+        # TODO(peria): Implement this. http:///crbug.com/839389
+
+    def _merge_mixins(self):
+        """
+        Merges mixins with interfaces that connected with includes statements.
+        """
+        self._ir_map.move_to_new_phase()
+        # TODO(peria): Implement this. http:///crbug.com/839389
+
+    def _resolve_inheritances(self):
+        """
+        Resolves inheritances and [Unforgeable]
+        """
+        self._ir_map.move_to_new_phase()
+        # TODO(peria): Implement this. http:///crbug.com/839389
+
+    def _resolve_exposures(self):
+        """
+        Links [Exposed] interfaces/namespaces with [Global] interfaces
+        """
+        self._ir_map.move_to_new_phase()
+        # TODO(peria): Implement this. http:///crbug.com/839389
+
+    def _define_unions(self):
+        """
+        Create a definition of union, that is unique in union types that have
+        same flattened-like members.
+        """
+        self._ir_map.move_to_new_phase()
+        # TODO(peria): Implement this. http:///crbug.com/839389
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/includes.py b/third_party/blink/renderer/bindings/scripts/web_idl/includes.py
index 09ed9384..d0ed04a 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/includes.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/includes.py
@@ -16,6 +16,7 @@
              WithDebugInfo):
         def __init__(self,
                      interface_identifier,
+                     mixin_identifier,
                      code_generator_info=None,
                      component=None,
                      debug_info=None):
@@ -33,6 +34,9 @@
             WithComponent.__init__(self, component)
             WithDebugInfo.__init__(self, debug_info)
 
+            self.interafce_identifier = interface_identifier
+            self.mixin_identifier = mixin_identifier
+
     @property
     def interface(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
new file mode 100644
index 0000000..1eab6fc
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
@@ -0,0 +1,133 @@
+# 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 idl_parser
+import utilities
+from .common import DebugInfo
+from .interface import Interface
+from .namespace import Namespace
+from .dictionary import Dictionary
+from .callback_interface import CallbackInterface
+from .callback_function import CallbackFunction
+from .enumeration import Enumeration
+from .typedef import Typedef
+from .includes import Includes
+
+
+def load_and_register_idl_definitions(filepaths, ir_map):
+    """
+    Registers IDL definitions' IRs into a IDL compiler
+
+    Load ASTs from files, create IRs, and registers them into |ir_map|.
+    """
+    asts_grouped_by_component = utilities.read_pickle_files(filepaths)
+    for asts_per_component in asts_grouped_by_component:
+        component = asts_per_component.component
+        for file_node in asts_per_component.asts:
+            assert isinstance(file_node, idl_parser.idl_node.IDLNode)
+            for definition_node in file_node.GetChildren():
+                idl_definition = _convert_ast(component, definition_node)
+                ir_map.register(idl_definition)
+
+
+def _convert_ast(component, ast):
+    def build_interface(node):
+        if node.GetProperty('CALLBACK'):
+            return build_callback_interface(node)
+
+        interface = Interface.IR(
+            identifier=node.GetName(),
+            is_partial=bool(node.GetProperty('PARTIAL')),
+            is_mixin=bool(node.GetProperty('MIXIN')),
+            component=component,
+            debug_info=_build_debug_info(node))
+        # TODO(peria): Build members and register them in |interface|
+        return interface
+
+    def build_namespace(node):
+        namespace = Namespace.IR(
+            identifier=node.GetName(),
+            is_partial=bool(node.GetProperty('PARTIAL')),
+            component=component,
+            debug_info=_build_debug_info(node))
+        # TODO(peria): Build members and register them in |namespace|
+        return namespace
+
+    def build_dictionary(node):
+        dictionary = Dictionary.IR(
+            identifier=node.GetName(),
+            is_partial=bool(node.GetProperty('PARTIAL')),
+            component=component,
+            debug_info=_build_debug_info(node))
+        # TODO(peria): Build members and register them in |dictionary|
+        return dictionary
+
+    def build_callback_interface(node):
+        callback_interface = CallbackInterface.IR(
+            identifier=node.GetName(),
+            component=component,
+            debug_info=_build_debug_info(node))
+        # TODO(peria): Build members and register them in |callback_interface|
+        return callback_interface
+
+    def build_callback_function(node):
+        callback_function = CallbackFunction.IR(
+            identifier=node.GetName(),
+            component=component,
+            debug_info=_build_debug_info(node))
+        # TODO(peria): Build members and register them in |callback_function|
+        return callback_function
+
+    def build_enumeration(node):
+        enumeration = Enumeration.IR(
+            identifier=node.GetName(),
+            values=[child.GetName() for child in node.GetChildren()],
+            component=component,
+            debug_info=_build_debug_info(node))
+        return enumeration
+
+    def build_typedef(node):
+        typedef = Typedef.IR(
+            identifier=node.GetName(),
+            idl_type=dispatch_to_build_function(node.GetChildren()[0]),
+            component=component,
+            debug_info=_build_debug_info(node))
+        return typedef
+
+    def build_includes(node):
+        includes = Includes.IR(
+            interface_identifier=node.GetName(),
+            mixin_identifier=node.GetProperty('REFERENCE'),
+            component=component,
+            debug_info=_build_debug_info(node))
+        return includes
+
+    def dispatch_to_build_function(node):
+        node_class = node.GetClass()
+        # TODO(peria): Drop this if branch returning None, when all the build
+        # functions get implemented.  It is only to avoid a build error in CQ.
+        if node_class not in build_functions:
+            return None
+        assert node_class in build_functions, '{} is unknown node class.'.format(
+            node_class)
+        return build_functions[node_class](node)
+
+    build_functions = {
+        'Callback': build_callback_function,
+        'Dictionary': build_dictionary,
+        'Enum': build_enumeration,
+        'Includes': build_includes,
+        'Interface': build_interface,
+        'Namespace': build_namespace,
+        'Typedef': build_typedef,
+    }
+    return dispatch_to_build_function(ast)
+
+
+def _build_debug_info(node):
+    return DebugInfo(
+        location=DebugInfo.Location(
+            filepath=node.GetProperty('FILENAME'),
+            line_number=node.GetProperty('LINENO'),
+            column_number=node.GetProperty('POSITION')))
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py b/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py
index 5f3151b..10ee0af 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/typedef.py
@@ -17,6 +17,7 @@
              WithDebugInfo):
         def __init__(self,
                      identifier,
+                     idl_type,
                      code_generator_info=None,
                      component=None,
                      debug_info=None):
@@ -28,6 +29,8 @@
             WithComponent.__init__(self, component)
             WithDebugInfo.__init__(self, debug_info)
 
+            self.idl_type = idl_type
+
     @property
     def idl_type(self):
         """
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/utilities.py b/third_party/blink/renderer/bindings/scripts/web_idl/utilities.py
deleted file mode 100644
index 5484a6d..0000000
--- a/third_party/blink/renderer/bindings/scripts/web_idl/utilities.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-def assert_no_extra_args(kwargs):
-    if kwargs:
-        raise ValueError('Unknown parameters are passed: %s' % kwargs.keys())
diff --git a/third_party/blink/renderer/core/css/css_font_face_src_value.cc b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
index e6255e9..463397f 100644
--- a/third_party/blink/renderer/core/css/css_font_face_src_value.cc
+++ b/third_party/blink/renderer/core/css/css_font_face_src_value.cc
@@ -85,8 +85,12 @@
                                          FontResourceClient* client) const {
   if (!fetched_) {
     ResourceRequest resource_request(absolute_resource_);
-    resource_request.SetHttpReferrer(SecurityPolicy::GenerateReferrer(
-        referrer_.referrer_policy, resource_request.Url(), referrer_.referrer));
+    resource_request.SetReferrerPolicy(
+        ReferrerPolicyResolveDefault(referrer_.referrer_policy),
+        ResourceRequest::SetReferrerPolicyLocation::kCSSFontFaceSrcValueFetch);
+    resource_request.SetReferrerString(
+        referrer_.referrer,
+        ResourceRequest::SetReferrerStringLocation::kCSSFontFaceSrcValueFetch);
     ResourceLoaderOptions options;
     options.initiator_info.name = fetch_initiator_type_names::kCSS;
     FetchParameters params(resource_request, options);
diff --git a/third_party/blink/renderer/core/css/css_image_set_value.cc b/third_party/blink/renderer/core/css/css_image_set_value.cc
index a8cd7be2..8f44785 100644
--- a/third_party/blink/renderer/core/css/css_image_set_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_set_value.cc
@@ -64,9 +64,8 @@
 
     ImageWithScale image;
     image.image_url = image_url;
-    image.referrer = SecurityPolicy::GenerateReferrer(
-        image_value.GetReferrer().referrer_policy, KURL(image_url),
-        image_value.GetReferrer().referrer);
+    image.referrer.referrer = image_value.GetReferrer().referrer;
+    image.referrer.referrer_policy = image_value.GetReferrer().referrer_policy;
     image.scale_factor = scale_factor;
     images_in_set_.push_back(image);
     ++i;
@@ -114,7 +113,13 @@
     // transforms. https://bugs.webkit.org/show_bug.cgi?id=81698
     ImageWithScale image = BestImageForScaleFactor(device_scale_factor);
     ResourceRequest resource_request(document.CompleteURL(image.image_url));
-    resource_request.SetHttpReferrer(image.referrer);
+    resource_request.SetReferrerPolicy(
+        ReferrerPolicyResolveDefault(image.referrer.referrer_policy),
+        ResourceRequest::SetReferrerPolicyLocation::
+            kCSSImageSetValueCacheImage);
+    resource_request.SetReferrerString(
+        image.referrer.referrer, ResourceRequest::SetReferrerStringLocation::
+                                     kCSSImageSetValueCacheImage);
     ResourceLoaderOptions options;
     options.initiator_info.name = parser_mode_ == kUASheetMode
                                       ? fetch_initiator_type_names::kUacss
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index fe4de29..6ce4ffe 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -66,8 +66,12 @@
     if (absolute_url_.IsEmpty())
       ReResolveURL(document);
     ResourceRequest resource_request(absolute_url_);
-    resource_request.SetHttpReferrer(SecurityPolicy::GenerateReferrer(
-        referrer_.referrer_policy, resource_request.Url(), referrer_.referrer));
+    resource_request.SetReferrerPolicy(
+        ReferrerPolicyResolveDefault(referrer_.referrer_policy),
+        ResourceRequest::SetReferrerPolicyLocation::kCSSImageValueCacheImage);
+    resource_request.SetReferrerString(
+        referrer_.referrer,
+        ResourceRequest::SetReferrerStringLocation::kCSSImageValueCacheImage);
     ResourceLoaderOptions options;
     options.initiator_info.name = initiator_name_.IsEmpty()
                                       ? fetch_initiator_type_names::kCSS
diff --git a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
index 92d9c68a..d8090492 100644
--- a/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
+++ b/third_party/blink/renderer/core/css/invalidation/style_invalidator.cc
@@ -318,9 +318,10 @@
   for (auto& distributed_node : slot.FlattenedAssignedNodes()) {
     if (distributed_node->NeedsStyleRecalc())
       continue;
-    if (!distributed_node->IsElementNode())
+    auto* element = DynamicTo<Element>(distributed_node.Get());
+    if (!element)
       continue;
-    if (MatchesCurrentInvalidationSetsAsSlotted(ToElement(*distributed_node))) {
+    if (MatchesCurrentInvalidationSetsAsSlotted(*element)) {
       distributed_node->SetNeedsStyleRecalc(
           kLocalStyleChange, StyleChangeReasonForTracing::Create(
                                  style_change_reason::kStyleInvalidator));
diff --git a/third_party/blink/renderer/core/css/layout_tree_rebuild_root.cc b/third_party/blink/renderer/core/css/layout_tree_rebuild_root.cc
index 8d1fd528..1d10553 100644
--- a/third_party/blink/renderer/core/css/layout_tree_rebuild_root.cc
+++ b/third_party/blink/renderer/core/css/layout_tree_rebuild_root.cc
@@ -27,7 +27,7 @@
   }
   if (!root_node || root_node->IsDocumentNode())
     return *GetRootNode()->GetDocument().documentElement();
-  return ToElement(*root_node);
+  return To<Element>(*root_node);
 }
 
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/css/style_recalc_root.cc b/third_party/blink/renderer/core/css/style_recalc_root.cc
index 8c88771..0a251fb 100644
--- a/third_party/blink/renderer/core/css/style_recalc_root.cc
+++ b/third_party/blink/renderer/core/css/style_recalc_root.cc
@@ -31,7 +31,7 @@
   }
   if (root_node->IsTextNode())
     return *root_node->parentElement();
-  return ToElement(*root_node);
+  return To<Element>(*root_node);
 }
 
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index d1234581..611ef13d 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2483,9 +2483,9 @@
 static void AssertLayoutTreeUpdated(Node& root) {
   Node* node = &root;
   while (node) {
-    if (RuntimeEnabledFeatures::DisplayLockingEnabled() &&
-        node->IsElementNode() &&
-        ToElement(node)->StyleRecalcBlockedByDisplayLock(
+    auto* element = DynamicTo<Element>(node);
+    if (element && RuntimeEnabledFeatures::DisplayLockingEnabled() &&
+        element->StyleRecalcBlockedByDisplayLock(
             DisplayLockContext::kChildren)) {
       node = FlatTreeTraversal::NextSkippingChildren(*node);
       continue;
@@ -2795,9 +2795,10 @@
         (ancestor->NeedsAdjacentStyleRecalc() && !ignore_adjacent_style)) {
       return true;
     }
-    if (!ancestor->IsElementNode())
+    auto* element = DynamicTo<Element>(ancestor);
+    if (!element)
       continue;
-    if (auto* context = ToElement(ancestor)->GetDisplayLockContext()) {
+    if (auto* context = element->GetDisplayLockContext()) {
       // Even if the ancestor is style-clean, we might've previously
       // blocked a style traversal going to the ancestor or its descendants.
       if (context->StyleTraversalWasBlocked()) {
@@ -5206,8 +5207,8 @@
     Node* node = sequential_focus_navigation_starting_point_->startContainer();
     DCHECK_EQ(node,
               sequential_focus_navigation_starting_point_->endContainer());
-    if (node->IsElementNode())
-      return ToElement(node);
+    if (auto* element = DynamicTo<Element>(node))
+      return element;
     if (Element* neighbor_element = type == kWebFocusTypeForward
                                         ? ElementTraversal::Previous(*node)
                                         : ElementTraversal::Next(*node))
@@ -5217,13 +5218,11 @@
 
   // Range::selectNodeContents didn't select contents because the element had
   // no children.
-  if (sequential_focus_navigation_starting_point_->startContainer()
-          ->IsElementNode() &&
-      !sequential_focus_navigation_starting_point_->startContainer()
-           ->hasChildren() &&
+  auto* element = DynamicTo<Element>(
+      sequential_focus_navigation_starting_point_->startContainer());
+  if (element && !element->hasChildren() &&
       sequential_focus_navigation_starting_point_->startOffset() == 0)
-    return ToElement(
-        sequential_focus_navigation_starting_point_->startContainer());
+    return element;
 
   // A node selected by Range::selectNodeContents was removed from the
   // document tree.
@@ -5235,11 +5234,11 @@
     // FocusController. Ideally we should find backward/forward focusable
     // elements before the starting point is disconnected. crbug.com/606582
     if (type == kWebFocusTypeForward) {
-      Node* previous = next_node;
-      do {
-        previous = FlatTreeTraversal::Previous(*previous);
-      } while (previous && !previous->IsElementNode());
-      return ToElement(previous);
+      Node* previous = FlatTreeTraversal::Previous(*next_node);
+      for (; previous; previous = FlatTreeTraversal::Previous(*previous)) {
+        if (auto* element = DynamicTo<Element>(previous))
+          return element;
+      }
     }
     for (Node* next = next_node; next; next = FlatTreeTraversal::Next(*next)) {
       if (auto* element = DynamicTo<Element>(next))
@@ -6910,7 +6909,7 @@
                                        EventListener* listener,
                                        const String& context_url,
                                        const WTF::OrdinalNumber& context_line) {
-  Element* element = node && node->IsElementNode() ? ToElement(node) : nullptr;
+  auto* element = DynamicTo<Element>(node);
 
   // https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-content-attributes
   // Step 5.1. If the Should element's inline behavior be blocked by Content
@@ -7442,8 +7441,8 @@
       new_hover_element) {
     Node* ancestor = FlatTreeTraversal::CommonAncestor(*old_hover_element,
                                                        *new_hover_element);
-    if (ancestor && ancestor->IsElementNode())
-      ancestor_element = ToElement(ancestor);
+    if (auto* element = DynamicTo<Element>(ancestor))
+      ancestor_element = element;
   }
 
   HeapVector<Member<Element>, 32> elements_to_remove_from_chain;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 3fa50d3..c35262b2 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -283,7 +283,7 @@
     return true;
   if (node.GetDocument().documentElement() == &node)
     return true;
-  if (const Element* element = ToElementOrNull(&node)) {
+  if (const Element* element = DynamicTo<Element>(&node)) {
     // Replaced elements are considered to create a new formatting context, in
     // the sense that they can't possibly have children that participate in the
     // same formatting context as their parent.
@@ -3776,7 +3776,8 @@
 
   bool found_bfc = false;
   for (Element* ancestor = this; !found_bfc;) {
-    ancestor = ToElementOrNull(LayoutTreeBuilderTraversal::Parent(*ancestor));
+    ancestor =
+        DynamicTo<Element>(LayoutTreeBuilderTraversal::Parent(*ancestor));
     if (!ancestor || ancestor->ShouldForceLegacyLayout())
       break;
     const ComputedStyle* style = ancestor->GetComputedStyle();
@@ -3800,7 +3801,7 @@
   // still going to fall back to legacy.
   Element* parent;
   for (Element* walker = this; walker; walker = parent) {
-    parent = ToElementOrNull(LayoutTreeBuilderTraversal::Parent(*walker));
+    parent = DynamicTo<Element>(LayoutTreeBuilderTraversal::Parent(*walker));
     if (!walker->GetComputedStyle()->SpecifiesColumns())
       continue;
 
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 0d8e7058..ce27f74 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1266,12 +1266,12 @@
 }
 
 inline bool IsDisabledFormControl(const Node* node) {
-  return node->IsElementNode() && ToElement(node)->IsDisabledFormControl();
+  auto* element = DynamicTo<Element>(node);
+  return element && element->IsDisabledFormControl();
 }
 
 inline Element* Node::parentElement() const {
-  ContainerNode* parent = parentNode();
-  return parent && parent->IsElementNode() ? ToElement(parent) : nullptr;
+  return DynamicTo<Element>(parentNode());
 }
 
 inline bool Element::FastHasAttribute(const QualifiedName& name) const {
diff --git a/third_party/blink/renderer/core/dom/slot_assignment_test.cc b/third_party/blink/renderer/core/dom/slot_assignment_test.cc
index e5c4dea6..39054a0 100644
--- a/third_party/blink/renderer/core/dom/slot_assignment_test.cc
+++ b/third_party/blink/renderer/core/dom/slot_assignment_test.cc
@@ -71,7 +71,7 @@
     if (auto* text = DynamicTo<Text>(descendant)) {
       if (text->ContainsOnlyWhitespaceOrEmpty())
         text->remove();
-    } else if (Element* element = ToElementOrNull(descendant)) {
+    } else if (auto* element = DynamicTo<Element>(descendant)) {
       if (ShadowRoot* shadow_root = element->OpenShadowRoot())
         RemoveWhiteSpaceOnlyTextNode(*shadow_root);
     }
diff --git a/third_party/blink/renderer/core/editing/editing_style_utilities.cc b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
index a2276f0..b25aba9 100644
--- a/third_party/blink/renderer/core/editing/editing_style_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
@@ -118,7 +118,7 @@
       break;
     if (node.IsStyledElement() && !IsMailHTMLBlockquoteElement(&node)) {
       wrapping_style->MergeInlineAndImplicitStyleOfElement(
-          ToElement(&node), EditingStyle::kDoNotOverrideValues,
+          To<Element>(&node), EditingStyle::kDoNotOverrideValues,
           EditingStyle::kEditingPropertiesInEffect);
     }
   }
diff --git a/third_party/blink/renderer/core/editing/editing_utilities.cc b/third_party/blink/renderer/core/editing/editing_utilities.cc
index 6d11b16..cf155d1 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities.cc
@@ -412,7 +412,7 @@
       return true;
   }
 
-  if (auto* element = ToElementOrNull(const_cast<Node*>(&node)))
+  if (auto* element = DynamicTo<Element>(&node))
     return EqualIgnoringASCIICase(element->getAttribute(kRoleAttr), "textbox");
 
   return false;
diff --git a/third_party/blink/renderer/core/editing/layout_selection.cc b/third_party/blink/renderer/core/editing/layout_selection.cc
index ea77044..4087870 100644
--- a/third_party/blink/renderer/core/editing/layout_selection.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection.cc
@@ -910,7 +910,7 @@
   ostream << (void*)&node;
   if (node.IsTextNode())
     ostream << "#text";
-  else if (const Element* element = ToElementOrNull(node))
+  else if (const auto* element = DynamicTo<Element>(node))
     ostream << element->tagName().Utf8();
   LayoutObject* layout_object = node.GetLayoutObject();
   if (!layout_object) {
diff --git a/third_party/blink/renderer/core/editing/layout_selection_test.cc b/third_party/blink/renderer/core/editing/layout_selection_test.cc
index c8fbb486..d24c62f 100644
--- a/third_party/blink/renderer/core/editing/layout_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection_test.cc
@@ -83,7 +83,7 @@
                                  wtf_size_t depth) {
     if (const Text* text = DynamicTo<Text>(node))
       PrintText(ostream, *text);
-    else if (const Element* element = ToElementOrNull(node))
+    else if (const auto* element = DynamicTo<Element>(node))
       ostream << element->tagName().Utf8();
     else
       ostream << node;
diff --git a/third_party/blink/renderer/core/editing/selection_editor.cc b/third_party/blink/renderer/core/editing/selection_editor.cc
index a79885a..bc8ade5d 100644
--- a/third_party/blink/renderer/core/editing/selection_editor.cc
+++ b/third_party/blink/renderer/core/editing/selection_editor.cc
@@ -173,7 +173,7 @@
 #endif
   if (!container.ContainsIncludingHostElements(*node))
     return position;
-  if (auto* element = ToElementOrNull(container)) {
+  if (auto* element = DynamicTo<Element>(container)) {
     if (auto* shadow_root = element->GetShadowRoot()) {
       // Removal of light children does not affect position in the
       // shadow tree.
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
index 36481ff..43c63a99 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
@@ -359,7 +359,7 @@
     } else {
       next = Strategy::Next(*n);
       if (IsEnclosingBlock(n) && CanHaveChildrenForEditing(n) &&
-          next == past_end && !ContainsOnlyBRElement(ToElement(*n))) {
+          next == past_end && !ContainsOnlyBRElement(To<Element>(*n))) {
         // Don't write out empty block containers that aren't fully selected
         // unless the block container only contains br element.
         continue;
@@ -375,7 +375,7 @@
 
         // If node has no children, close the tag now.
         if (Strategy::HasChildren(*n)) {
-          if (next == past_end && ContainsOnlyBRElement(ToElement(*n))) {
+          if (next == past_end && ContainsOnlyBRElement(*element)) {
             // node is not fully selected and node contains only one br element
             // as child. Close the br tag now.
             AppendStartMarkup(*next);
diff --git a/third_party/blink/renderer/core/exported/web_layer_test.cc b/third_party/blink/renderer/core/exported/web_layer_test.cc
index 0cb27052..1bb3a3a 100644
--- a/third_party/blink/renderer/core/exported/web_layer_test.cc
+++ b/third_party/blink/renderer/core/exported/web_layer_test.cc
@@ -272,6 +272,15 @@
     return Compositor().layer_tree_view().layer_tree_host()->property_trees();
   }
 
+  cc::TransformNode* GetTransformNode(const cc::Layer* layer) {
+    return GetPropertyTrees()->transform_tree.Node(
+        layer->transform_tree_index());
+  }
+
+  cc::EffectNode* GetEffectNode(const cc::Layer* layer) {
+    return GetPropertyTrees()->effect_tree.Node(layer->effect_tree_index());
+  }
+
   PaintArtifactCompositor* paint_artifact_compositor() {
     return MainFrame().GetFrameView()->GetPaintArtifactCompositorForTesting();
   }
@@ -474,20 +483,29 @@
 
   // Initially, no layer should have |subtree_property_changed| set.
   EXPECT_FALSE(outer_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetTransformNode(outer_element_layer)->transform_changed);
   EXPECT_FALSE(inner_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetTransformNode(inner_element_layer)->transform_changed);
 
   // Modifying the transform style should set |subtree_property_changed| on
   // both layers.
   outer_element->setAttribute(html_names::kStyleAttr,
                               "transform: rotate(10deg)");
   UpdateAllLifecyclePhases();
+  // This is still set by the traditional GraphicsLayer::SetTransform().
   EXPECT_TRUE(outer_element_layer->subtree_property_changed());
+  // Set by blink::PropertyTreeManager.
+  EXPECT_TRUE(GetTransformNode(outer_element_layer)->transform_changed);
+  // TODO(wangxianzhu): Probably avoid setting this flag on transform change.
   EXPECT_TRUE(inner_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetTransformNode(inner_element_layer)->transform_changed);
 
   // After a frame the |subtree_property_changed| value should be reset.
   Compositor().BeginFrame();
   EXPECT_FALSE(outer_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetTransformNode(outer_element_layer)->transform_changed);
   EXPECT_FALSE(inner_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetTransformNode(inner_element_layer)->transform_changed);
 }
 
 // When a property tree change occurs that affects layer transform in a simple
@@ -719,19 +737,28 @@
 
   // Initially, no layer should have |subtree_property_changed| set.
   EXPECT_FALSE(outer_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetEffectNode(outer_element_layer)->effect_changed);
   EXPECT_FALSE(inner_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetEffectNode(inner_element_layer)->effect_changed);
 
   // Modifying the filter style should set |subtree_property_changed| on
   // both layers.
   outer_element->setAttribute(html_names::kStyleAttr, "filter: blur(20px)");
   UpdateAllLifecyclePhases();
+  // TODO(wangxianzhu): Probably avoid setting this flag on transform change.
   EXPECT_TRUE(outer_element_layer->subtree_property_changed());
+  // Set by blink::PropertyTreeManager.
+  EXPECT_TRUE(GetEffectNode(outer_element_layer)->effect_changed);
+  // TODO(wangxianzhu): Probably avoid setting this flag on transform change.
   EXPECT_TRUE(inner_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetEffectNode(inner_element_layer)->effect_changed);
 
   // After a frame the |subtree_property_changed| value should be reset.
   Compositor().BeginFrame();
   EXPECT_FALSE(outer_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetEffectNode(outer_element_layer)->effect_changed);
   EXPECT_FALSE(inner_element_layer->subtree_property_changed());
+  EXPECT_FALSE(GetEffectNode(inner_element_layer)->effect_changed);
 }
 
 // This test is similar to |LayerSubtreeTransformPropertyChanged| but for
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index bb46eba..df97cd9 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -732,8 +732,12 @@
   request.SetUseStreamOnResponse(true);
   request.SetExternalRequestStateFromRequestorAddressSpace(
       execution_context_->GetSecurityContext().AddressSpace());
-  request.SetReferrerString(fetch_request_data_->ReferrerString());
-  request.SetReferrerPolicy(fetch_request_data_->GetReferrerPolicy());
+  request.SetReferrerString(
+      fetch_request_data_->ReferrerString(),
+      ResourceRequest::SetReferrerStringLocation::kPerformHTTPFetch);
+  request.SetReferrerPolicy(
+      fetch_request_data_->GetReferrerPolicy(),
+      ResourceRequest::SetReferrerPolicyLocation::kPerformHTTPFetch);
 
   request.SetSkipServiceWorker(is_isolated_world_);
 
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 21d9b7f..9208493 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1495,7 +1495,8 @@
       SecurityPolicy::GenerateReferrer(
           active_document->GetReferrerPolicy(), completed_url,
           window_features.noreferrer ? Referrer::NoReferrer()
-                                     : active_document->OutgoingReferrer()));
+                                     : active_document->OutgoingReferrer()),
+      ResourceRequest::SetHttpReferrerLocation::kLocalDomWindow);
 
   frame_request.GetResourceRequest().SetHasUserGesture(
       LocalFrame::HasTransientUserActivation(GetFrame()));
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 e55ac915..0a2181f 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
@@ -951,10 +951,12 @@
   String referrer = referrer_url.IsEmpty()
                         ? GetFrame()->GetDocument()->OutgoingReferrer()
                         : String(referrer_url.GetString());
-  request.ToMutableResourceRequest().SetHttpReferrer(
-      SecurityPolicy::GenerateReferrer(
-          GetFrame()->GetDocument()->GetReferrerPolicy(), request.Url(),
-          referrer));
+  ResourceRequest& resource_request = request.ToMutableResourceRequest();
+  resource_request.SetReferrerPolicy(
+      GetFrame()->GetDocument()->GetReferrerPolicy(),
+      ResourceRequest::SetReferrerPolicyLocation::kWebLocalFrameImpl);
+  resource_request.SetReferrerString(
+      referrer, ResourceRequest::SetReferrerStringLocation::kWebLocalFrameImpl);
 }
 
 WebAssociatedURLLoader* WebLocalFrameImpl::CreateAssociatedURLLoader(
@@ -2329,7 +2331,7 @@
   if (!node || !(IsHTMLCanvasElement(*node) || IsHTMLImageElement(*node)))
     return;
 
-  String url = ToElement(*node).ImageSourceURL();
+  String url = To<Element>(*node).ImageSourceURL();
   if (!KURL(NullURL(), url).ProtocolIsData())
     return;
 
diff --git a/third_party/blink/renderer/core/html/custom/custom_element_upgrade_sorter.cc b/third_party/blink/renderer/core/html/custom/custom_element_upgrade_sorter.cc
index 2d40f4d..f383846 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element_upgrade_sorter.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element_upgrade_sorter.cc
@@ -65,8 +65,9 @@
                                        const ChildSet::iterator& it) {
   if (it == children.end())
     return;
-  if (it->Get()->IsElementNode() && elements_->Contains(ToElement(*it)))
-    result->push_back(ToElement(*it));
+  auto* element = DynamicTo<Element>(it->Get());
+  if (element && elements_->Contains(element))
+    result->push_back(*element);
   Sorted(result, *it);
   children.erase(it);
 }
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index 51d9415..b1020a9 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -381,7 +381,8 @@
       !HasRel(kRelationNoReferrer)) {
     UseCounter::Count(GetDocument(),
                       WebFeature::kHTMLAnchorElementReferrerPolicyAttribute);
-    request.SetReferrerPolicy(policy);
+    request.SetReferrerPolicy(
+        policy, ResourceRequest::SetReferrerPolicyLocation::kAnchorElement);
   }
 
   // Ignore the download attribute if we either can't read the content, or
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 ace7634..60c5e0be 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
@@ -421,7 +421,9 @@
 
   KURL url_to_request = url.IsNull() ? BlankURL() : url;
   ResourceRequest request(url_to_request);
-  request.SetReferrerPolicy(ReferrerPolicyAttribute());
+  request.SetReferrerPolicy(ReferrerPolicyAttribute(),
+                            ResourceRequest::SetReferrerPolicyLocation::
+                                kFrameOwnerLoadOrRedirectSubframe);
 
   if (ContentFrame()) {
     // TODO(sclittle): Support lazily loading frame navigations.
diff --git a/third_party/blink/renderer/core/html/html_slot_element.cc b/third_party/blink/renderer/core/html/html_slot_element.cc
index f64b6595..58d24cdc 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.cc
+++ b/third_party/blink/renderer/core/html/html_slot_element.cc
@@ -157,8 +157,8 @@
 const HeapVector<Member<Element>> HTMLSlotElement::AssignedElements() {
   HeapVector<Member<Element>> elements;
   for (auto& node : AssignedNodes()) {
-    if (Element* element = ToElementOrNull(node))
-      elements.push_back(element);
+    if (auto* element = DynamicTo<Element>(node.Get()))
+      elements.push_back(*element);
   }
   return elements;
 }
@@ -167,8 +167,8 @@
     const AssignedNodesOptions* options) {
   HeapVector<Member<Element>> elements;
   for (auto& node : AssignedNodesForBinding(options)) {
-    if (Element* element = ToElementOrNull(node))
-      elements.push_back(element);
+    if (auto* element = DynamicTo<Element>(node.Get()))
+      elements.push_back(*element);
   }
   return elements;
 }
diff --git a/third_party/blink/renderer/core/html/html_source_element.cc b/third_party/blink/renderer/core/html/html_source_element.cc
index 4ba9bf6f..0348f09 100644
--- a/third_party/blink/renderer/core/html/html_source_element.cc
+++ b/third_party/blink/renderer/core/html/html_source_element.cc
@@ -107,8 +107,9 @@
 
 void HTMLSourceElement::RemovedFrom(ContainerNode& removal_root) {
   Element* parent = parentElement();
-  if (!parent && removal_root.IsElementNode())
-    parent = ToElement(&removal_root);
+  auto* element = DynamicTo<Element>(&removal_root);
+  if (element && !parent)
+    parent = element;
   if (auto* media = ToHTMLMediaElementOrNull(parent))
     media->SourceWasRemoved(this);
   if (auto* picture = ToHTMLPictureElementOrNull(parent)) {
diff --git a/third_party/blink/renderer/core/html/imports/link_import.cc b/third_party/blink/renderer/core/html/imports/link_import.cc
index 73df690..f907e0f 100644
--- a/third_party/blink/renderer/core/html/imports/link_import.cc
+++ b/third_party/blink/renderer/core/html/imports/link_import.cc
@@ -76,7 +76,8 @@
 
   ResourceRequest resource_request(GetDocument().CompleteURL(url));
   network::mojom::ReferrerPolicy referrer_policy = owner_->GetReferrerPolicy();
-  resource_request.SetReferrerPolicy(referrer_policy);
+  resource_request.SetReferrerPolicy(
+      referrer_policy, ResourceRequest::SetReferrerPolicyLocation::kLinkImport);
 
   ResourceLoaderOptions options;
   options.initiator_info.name = owner_->localName();
diff --git a/third_party/blink/renderer/core/html/parser/html_construction_site.cc b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
index 4b2bf649..eb5b858 100644
--- a/third_party/blink/renderer/core/html/parser/html_construction_site.cc
+++ b/third_party/blink/renderer/core/html/parser/html_construction_site.cc
@@ -124,12 +124,10 @@
   DCHECK_EQ(task.operation, HTMLConstructionSiteTask::kInsert);
 
   Insert(task);
-
-  if (task.child->IsElementNode()) {
-    Element& child = ToElement(*task.child);
-    child.BeginParsingChildren();
+  if (auto* child = DynamicTo<Element>(task.child.Get())) {
+    child->BeginParsingChildren();
     if (task.self_closing)
-      child.FinishParsingChildren();
+      child->FinishParsingChildren();
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/parser/preload_request.cc b/third_party/blink/renderer/core/html/parser/preload_request.cc
index 8302279..c9b3308b 100644
--- a/third_party/blink/renderer/core/html/parser/preload_request.cc
+++ b/third_party/blink/renderer/core/html/parser/preload_request.cc
@@ -39,9 +39,14 @@
   DCHECK(!url.ProtocolIsData());
 
   ResourceRequest resource_request(url);
-  resource_request.SetReferrerPolicy(referrer_policy_);
-  if (referrer_source_ == kBaseUrlIsReferrer)
-    resource_request.SetReferrerString(base_url_.StrippedForUseAsReferrer());
+  resource_request.SetReferrerPolicy(
+      referrer_policy_,
+      ResourceRequest::SetReferrerPolicyLocation::kPreloadRequestStart);
+  if (referrer_source_ == kBaseUrlIsReferrer) {
+    resource_request.SetReferrerString(
+        base_url_.StrippedForUseAsReferrer(),
+        ResourceRequest::SetReferrerStringLocation::kPreloadRequestStart);
+  }
 
   resource_request.SetRequestContext(
       ResourceFetcher::DetermineRequestContext(resource_type_, is_image_set_));
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index 023053a..5161f35 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -809,9 +809,9 @@
         pointer_event->HasEventPath()) {
       for (const auto& context :
            pointer_event->GetEventPath().NodeEventContexts()) {
-        if (context.GetNode().IsElementNode() &&
-            event_handling_util::IsInDocument(&context.GetNode())) {
-          mouse_target = ToElement(&context.GetNode());
+        auto* element = DynamicTo<Element>(&context.GetNode());
+        if (element && event_handling_util::IsInDocument(element)) {
+          mouse_target = element;
           break;
         }
       }
diff --git a/third_party/blink/renderer/core/inspector/dom_patch_support.cc b/third_party/blink/renderer/core/inspector/dom_patch_support.cc
index 4caffa2..8da4b57 100644
--- a/third_party/blink/renderer/core/inspector/dom_patch_support.cc
+++ b/third_party/blink/renderer/core/inspector/dom_patch_support.cc
@@ -443,9 +443,8 @@
   digestor.UpdateUtf8(node->nodeName());
   digestor.UpdateUtf8(node->nodeValue());
 
-  if (node->IsElementNode()) {
-    Element& element = ToElement(*node);
-    Node* child = element.firstChild();
+  if (auto* element = DynamicTo<Element>(node)) {
+    Node* child = element->firstChild();
     while (child) {
       Digest* child_info = CreateDigest(child, unused_nodes_map);
       digestor.UpdateUtf8(child_info->sha1_);
@@ -453,7 +452,7 @@
       digest->children_.push_back(child_info);
     }
 
-    AttributeCollection attributes = element.AttributesWithoutUpdate();
+    AttributeCollection attributes = element->AttributesWithoutUpdate();
     if (!attributes.IsEmpty()) {
       Digestor attrs_digestor(kHashAlgorithmSha1);
       for (auto& attribute : attributes) {
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index b53e403..5d0cf17 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -579,18 +579,17 @@
   HashSet<String> unique_names;
   *class_names = std::make_unique<protocol::Array<String>>();
   Node* parent_node = NodeForId(node_id);
-  if (!parent_node ||
-      (!parent_node->IsElementNode() && !parent_node->IsDocumentNode() &&
-       !parent_node->IsDocumentFragment()))
+  auto* parent_element = DynamicTo<Element>(parent_node);
+  if (!parent_element && !parent_node->IsDocumentNode() &&
+      !parent_node->IsDocumentFragment())
     return Response::Error("No suitable node with given id found");
 
   for (Node* node = parent_node; node;
        node = FlatTreeTraversal::Next(*node, parent_node)) {
-    if (node->IsElementNode()) {
-      const Element& element = ToElement(*node);
-      if (!element.HasClass())
+    if (const auto* element = DynamicTo<Element>(node)) {
+      if (!element->HasClass())
         continue;
-      const SpaceSplitString& class_name_list = element.ClassNames();
+      const SpaceSplitString& class_name_list = element->ClassNames();
       for (unsigned i = 0; i < class_name_list.size(); ++i)
         unique_names.insert(class_name_list[i]);
     }
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 1bffc8a..f664d848 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -860,7 +860,8 @@
       // inside state_'s kExtraRequestHeaders, somewhere else.
       if (header_name.LowerASCII() == http_names::kReferer.LowerASCII()) {
         request.SetHttpReferrer(
-            Referrer(value, network::mojom::ReferrerPolicy::kAlways));
+            Referrer(value, network::mojom::ReferrerPolicy::kAlways),
+            ResourceRequest::SetHttpReferrerLocation::kInspectorNetworkAgent);
       } else {
         request.SetHttpHeaderField(header_name, AtomicString(value));
       }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index a1794ba..0c9af91 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3777,7 +3777,10 @@
 
   available_height -= root_margin_border_padding_height;
 
-  if (IsTable() && IsOutOfFlowPositioned())
+  // LayoutNG already includes padding in
+  // OverrideContainingBlockContentLogicalHeight so we only need to add it here
+  // for legacy containing blocks.
+  if (IsTable() && IsOutOfFlowPositioned() && !cb->IsLayoutNGObject())
     available_height += cb->PaddingLogicalHeight();
 
   return available_height;
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
index 04efa65..e4eaa6d 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
@@ -195,7 +195,7 @@
 
 #if DCHECK_IS_ON()
   LocalFrame& frame = frame_view_->GetFrame();
-  if (ShouldLog(frame)) {
+  if (!HadRecentInput() && ShouldLog(frame)) {
     DVLOG(2) << "in " << (frame.IsMainFrame() ? "" : "subframe ")
              << frame.GetDocument()->Url().GetString() << ", "
              << source.DebugName() << " moved from " << old_rect.ToString()
@@ -314,8 +314,9 @@
   if (!HadRecentInput() && ShouldLog(frame)) {
     DVLOG(1) << "in " << (frame.IsMainFrame() ? "" : "subframe ")
              << frame.GetDocument()->Url().GetString() << ", viewport was "
-             << (jank_fraction * 100) << "% janked; raising score to "
-             << score_;
+             << (jank_fraction * 100) << "% janked with distance fraction "
+             << move_distance_factor << "; raising score to "
+             << score_with_move_distance_;
   }
 #endif
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
index 5e20fc2a..d9aa540 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.cc
@@ -113,27 +113,6 @@
   DISALLOW_COPY_AND_ASSIGN(DescendantCollector);
 };
 
-// The visitor emitting all visited fragments.
-class InclusiveDescendantCollector final
-    : public NGPhysicalFragmentCollectorBase {
-  STACK_ALLOCATED();
-
- public:
-  InclusiveDescendantCollector() = default;
-
-  Vector<Result> CollectFrom(const NGPhysicalFragment& fragment) final {
-    return CollectInclusivelyFrom(fragment);
-  }
-
- private:
-  void Visit() final {
-    Emit();
-    VisitChildren();
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(InclusiveDescendantCollector);
-};
-
 // The visitor emitting fragments generated from the given LayoutInline,
 // supporting culled inline.
 // Note: Since we apply culled inline per line, we have a fragment for
@@ -182,66 +161,6 @@
   DISALLOW_COPY_AND_ASSIGN(LayoutInlineCollector);
 };
 
-// The visitor emitting ancestors of the given fragment in bottom-up order.
-class AncestorCollector : public NGPhysicalFragmentCollectorBase {
-  STACK_ALLOCATED();
-
- public:
-  explicit AncestorCollector(const NGPhysicalFragment& target)
-      : target_(target) {}
-
-  Vector<Result> CollectFrom(const NGPhysicalFragment& fragment) final {
-    // TODO(xiaochengh): Change this into CollectInclusivlyFrom() to include
-    // subtree root to align with NodeTraversal::AncestorsOf().
-    return CollectExclusivelyFrom(fragment);
-  }
-
- private:
-  void Visit() final {
-    if (&GetFragment() == &target_) {
-      SetShouldStopTraversing();
-      return;
-    }
-
-    VisitChildren();
-    if (HasStoppedTraversing())
-      Emit();
-  }
-
-  const NGPhysicalFragment& target_;
-};
-
-// The visitor emitting inclusive ancestors of the given fragment in bottom-up
-// order.
-class InclusiveAncestorCollector : public NGPhysicalFragmentCollectorBase {
-  STACK_ALLOCATED();
-
- public:
-  explicit InclusiveAncestorCollector(const NGPhysicalFragment& target)
-      : target_(target) {}
-
-  Vector<Result> CollectFrom(const NGPhysicalFragment& fragment) final {
-    // TODO(xiaochengh): Change this into CollectInclusivlyFrom() to include
-    // subtree root to align with NodeTraversal::InclusiveAncestorsOf().
-    return CollectExclusivelyFrom(fragment);
-  }
-
- private:
-  void Visit() final {
-    if (&GetFragment() == &target_) {
-      SetShouldStopTraversing();
-      Emit();
-      return;
-    }
-
-    VisitChildren();
-    if (HasStoppedTraversing())
-      Emit();
-  }
-
-  const NGPhysicalFragment& target_;
-};
-
 }  // namespace
 
 // static
@@ -271,24 +190,4 @@
   return DescendantCollector().CollectFrom(container);
 }
 
-// static
-Vector<Result> NGInlineFragmentTraversal::InclusiveDescendantsOf(
-    const NGPhysicalFragment& root) {
-  return InclusiveDescendantCollector().CollectFrom(root);
-}
-
-// static
-Vector<Result> NGInlineFragmentTraversal::InclusiveAncestorsOf(
-    const NGPhysicalContainerFragment& container,
-    const NGPhysicalFragment& target) {
-  return InclusiveAncestorCollector(target).CollectFrom(container);
-}
-
-// static
-Vector<Result> NGInlineFragmentTraversal::AncestorsOf(
-    const NGPhysicalContainerFragment& container,
-    const NGPhysicalFragment& target) {
-  return AncestorCollector(target).CollectFrom(container);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h
index dedf844..e2ec8c6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h
@@ -20,28 +20,11 @@
   STATIC_ONLY(NGInlineFragmentTraversal);
 
  public:
-  // Return list of ancestors from |target| to |container|. Offsets are relative
-  // to |container|.
-  static Vector<NGPhysicalFragmentWithOffset> AncestorsOf(
-      const NGPhysicalContainerFragment& container,
-      const NGPhysicalFragment& target);
-
-  // Return list inclusive ancestors from |target| to |container|. Offsets are
-  // relative to |container|.
-  static Vector<NGPhysicalFragmentWithOffset> InclusiveAncestorsOf(
-      const NGPhysicalContainerFragment& container,
-      const NGPhysicalFragment& target);
-
   // Returns list of descendants in preorder. Offsets are relative to
   // specified fragment.
   static Vector<NGPhysicalFragmentWithOffset> DescendantsOf(
       const NGPhysicalContainerFragment&);
 
-  // Returns list of inclusive descendants in preorder. Offsets are relative to
-  // specified fragment.
-  static Vector<NGPhysicalFragmentWithOffset> InclusiveDescendantsOf(
-      const NGPhysicalFragment&);
-
   // Returns list of inline fragments produced from the specified LayoutObject.
   // The search is restricted in the subtree of |container|.
   // Note: When |target| is a LayoutInline, some/all of its own box fragments
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc
index b0751f82..43d13c4 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal_test.cc
@@ -76,25 +76,6 @@
   EXPECT_EQ(iter, descendants.end());
 }
 
-TEST_F(NGInlineFragmentTraversalTest, InclusiveDescendantsOf) {
-  SetBodyInnerHTML(
-      "<style>* { border: 1px solid}</style>"
-      "<div id=t>foo<b id=b>bar</b><br>baz</div>");
-  auto descendants = NGInlineFragmentTraversal::InclusiveDescendantsOf(
-      GetRootFragmentById("t"));
-  auto* iter = descendants.begin();
-
-  EXPECT_NEXT_BOX(iter, "t");
-  EXPECT_NEXT_LINE_BOX(iter);
-  EXPECT_NEXT_TEXT(iter, "foo");
-  EXPECT_NEXT_BOX(iter, "b");
-  EXPECT_NEXT_TEXT(iter, "bar");
-  EXPECT_NEXT_TEXT(iter, "\n");
-  EXPECT_NEXT_LINE_BOX(iter);
-  EXPECT_NEXT_TEXT(iter, "baz");
-  EXPECT_EQ(iter, descendants.end());
-}
-
 TEST_F(NGInlineFragmentTraversalTest, SelfFragmentsOf) {
   SetBodyInnerHTML(
       "<style>* { border: 1px solid}</style>"
@@ -110,44 +91,4 @@
   EXPECT_EQ(iter, descendants.end());
 }
 
-TEST_F(NGInlineFragmentTraversalTest, AncestorsOf) {
-  SetBodyInnerHTML(
-      "<style>* { border: 1px solid}</style>"
-      "<div id=t>x"
-      "<b id=b>y<i id=i>z<u id=target>foo</u>z</i>y</b>"
-      "x</div>");
-  const NGPhysicalContainerFragment& root = GetRootFragmentById("t");
-  const NGPhysicalFragment& target =
-      GetFragmentOfNode(root, GetElementById("target")->firstChild());
-  auto ancestors = NGInlineFragmentTraversal::AncestorsOf(root, target);
-  auto* iter = ancestors.begin();
-
-  EXPECT_NEXT_BOX(iter, "target");
-  EXPECT_NEXT_BOX(iter, "i");
-  EXPECT_NEXT_BOX(iter, "b");
-  EXPECT_NEXT_LINE_BOX(iter);
-  EXPECT_EQ(iter, ancestors.end());
-}
-
-TEST_F(NGInlineFragmentTraversalTest, InclusiveAncestorsOf) {
-  SetBodyInnerHTML(
-      "<style>* { border: 1px solid}</style>"
-      "<div id=t>x"
-      "<b id=b>y<i id=i>z<u id=target>foo</u>z</i>y</b>"
-      "x</div>");
-  const NGPhysicalContainerFragment& root = GetRootFragmentById("t");
-  const NGPhysicalFragment& target =
-      GetFragmentOfNode(root, GetElementById("target")->firstChild());
-  auto ancestors =
-      NGInlineFragmentTraversal::InclusiveAncestorsOf(root, target);
-  auto* iter = ancestors.begin();
-
-  EXPECT_NEXT_TEXT(iter, "foo");
-  EXPECT_NEXT_BOX(iter, "target");
-  EXPECT_NEXT_BOX(iter, "i");
-  EXPECT_NEXT_BOX(iter, "b");
-  EXPECT_NEXT_LINE_BOX(iter);
-  EXPECT_EQ(iter, ancestors.end());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
index 561f565..802c974 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
@@ -19,15 +19,36 @@
 
 namespace {
 
+// Tables need special handling, unfortunately. The code in this file assumes
+// that if an element has a height or width specified, that's what its final
+// height/width will be. Tables don't follow this pattern though; they treat
+// specified height/width as a second min-height or min-width.
+bool IsTable(const ComputedStyle& style) {
+  return style.Display() == EDisplay::kTable ||
+         style.Display() == EDisplay::kInlineTable;
+}
+
+inline Length TableAwareHeight(const ComputedStyle& style) {
+  if (IsTable(style))
+    return Length::Auto();
+  return style.Height();
+}
+
+inline Length TableAwareWidth(const ComputedStyle& style) {
+  if (IsTable(style))
+    return Length::Auto();
+  return style.Width();
+}
+
 bool AbsoluteHorizontalNeedsEstimate(const ComputedStyle& style) {
-  const Length& width = style.Width();
+  const Length& width = TableAwareWidth(style);
   return width.IsIntrinsic() || style.MinWidth().IsIntrinsic() ||
          style.MaxWidth().IsIntrinsic() ||
          (width.IsAuto() && (style.Left().IsAuto() || style.Right().IsAuto()));
 }
 
 bool AbsoluteVerticalNeedsEstimate(const ComputedStyle& style) {
-  const Length& height = style.Height();
+  const Length& height = TableAwareHeight(style);
   return height.IsIntrinsic() || style.MinHeight().IsIntrinsic() ||
          style.MaxHeight().IsIntrinsic() ||
          (height.IsAuto() && (style.Top().IsAuto() || style.Bottom().IsAuto()));
@@ -56,15 +77,24 @@
 LayoutUnit ResolveMinWidth(const NGConstraintSpace& space,
                            const ComputedStyle& style,
                            const NGBoxStrut& border_padding,
-                           const base::Optional<MinMaxSize>& child_minmax,
-                           const Length& width) {
+                           const base::Optional<MinMaxSize>& child_minmax) {
+  const Length& min_width = style.MinWidth();
   if (space.GetWritingMode() == WritingMode::kHorizontalTb) {
-    return ResolveMinInlineLength(space, style, border_padding, child_minmax,
-                                  width, LengthResolvePhase::kLayout);
+    LayoutUnit resolved_min_width =
+        ResolveMinInlineLength(space, style, border_padding, child_minmax,
+                               min_width, LengthResolvePhase::kLayout);
+    if (!IsTable(style))
+      return resolved_min_width;
+    Length table_width = style.Width();
+    if (table_width.IsAuto())
+      return resolved_min_width;
+    LayoutUnit resolved_width = ResolveMainInlineLength(
+        space, style, border_padding, child_minmax, table_width);
+    return std::max(resolved_min_width, resolved_width);
   }
   LayoutUnit computed_width =
       child_minmax.has_value() ? child_minmax->max_size : LayoutUnit();
-  return ResolveMinBlockLength(space, style, border_padding, width,
+  return ResolveMinBlockLength(space, style, border_padding, min_width,
                                computed_width, LengthResolvePhase::kLayout);
 }
 
@@ -101,15 +131,21 @@
 LayoutUnit ResolveMinHeight(const NGConstraintSpace& space,
                             const ComputedStyle& style,
                             const NGBoxStrut& border_padding,
-                            const base::Optional<MinMaxSize>& child_minmax,
-                            const Length& height) {
+                            const base::Optional<MinMaxSize>& child_minmax) {
+  const Length& min_height = style.MinHeight();
   if (space.GetWritingMode() != WritingMode::kHorizontalTb) {
-    return ResolveMinInlineLength(space, style, border_padding, child_minmax,
-                                  height, LengthResolvePhase::kLayout);
+    LayoutUnit resolved_min_height =
+        ResolveMinInlineLength(space, style, border_padding, child_minmax,
+                               min_height, LengthResolvePhase::kLayout);
+    if (!IsTable(style))
+      return resolved_min_height;
+    LayoutUnit resolved_height = ResolveMainInlineLength(
+        space, style, border_padding, child_minmax, style.Height());
+    return std::max(resolved_min_height, resolved_height);
   }
   LayoutUnit computed_height =
       child_minmax.has_value() ? child_minmax->max_size : LayoutUnit();
-  return ResolveMinBlockLength(space, style, border_padding, height,
+  return ResolveMinBlockLength(space, style, border_padding, min_height,
                                computed_height, LengthResolvePhase::kLayout);
 }
 
@@ -346,12 +382,12 @@
 
   // If calculated width is outside of min/max constraints,
   // rerun the algorithm with constrained width.
-  LayoutUnit min = ResolveMinWidth(space, style, border_padding, child_minmax,
-                                   style.MinWidth());
+  LayoutUnit min = ResolveMinWidth(space, style, border_padding, child_minmax);
   LayoutUnit max = ResolveMaxWidth(space, style, border_padding, child_minmax,
                                    style.MaxWidth());
-  if (width != ConstrainByMinMax(*width, min, max)) {
-    width = ConstrainByMinMax(*width, min, max);
+  LayoutUnit constrained_width = ConstrainByMinMax(*width, min, max);
+  if (width != constrained_width) {
+    width = constrained_width;
     // Because this function only changes "width" when it's not already
     // set, it is safe to recursively call ourselves here because on the
     // second call it is guaranteed to be within min..max.
@@ -512,8 +548,7 @@
 
   // If calculated height is outside of min/max constraints,
   // rerun the algorithm with constrained width.
-  LayoutUnit min = ResolveMinHeight(space, style, border_padding, child_minmax,
-                                    style.MinHeight());
+  LayoutUnit min = ResolveMinHeight(space, style, border_padding, child_minmax);
   LayoutUnit max = ResolveMaxHeight(space, style, border_padding, child_minmax,
                                     style.MaxHeight());
   if (height != ConstrainByMinMax(*height, min, max)) {
@@ -619,7 +654,7 @@
   NGAbsolutePhysicalPosition position;
   if (style.IsHorizontalWritingMode()) {
     base::Optional<LayoutUnit> width;
-    if (!style.Width().IsAuto()) {
+    if (!TableAwareWidth(style).IsAuto()) {
       width = ResolveMainWidth(space, style, border_padding, child_minmax,
                                style.Width());
     } else if (replaced_size.has_value()) {
@@ -630,7 +665,7 @@
         container_writing_mode, container_direction, &position);
   } else {
     base::Optional<LayoutUnit> height;
-    if (!style.Height().IsAuto()) {
+    if (!TableAwareHeight(style).IsAuto()) {
       height = ResolveMainHeight(space, style, border_padding, child_minmax,
                                  style.Height());
     } else if (replaced_size.has_value()) {
@@ -663,7 +698,7 @@
   }
   if (style.IsHorizontalWritingMode()) {
     base::Optional<LayoutUnit> height;
-    if (!style.Height().IsAuto()) {
+    if (!TableAwareHeight(style).IsAuto()) {
       height = ResolveMainHeight(space, style, border_padding, child_minmax,
                                  style.Height());
     } else if (replaced_size.has_value()) {
@@ -674,7 +709,7 @@
         container_writing_mode, container_direction, position);
   } else {
     base::Optional<LayoutUnit> width;
-    if (!style.Width().IsAuto()) {
+    if (!TableAwareWidth(style).IsAuto()) {
       width = ResolveMainWidth(space, style, border_padding, child_minmax,
                                style.Width());
     } else if (replaced_size.has_value()) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index 8f1a91af..9bf6cbb 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -380,17 +380,26 @@
     const NGBoxStrut& border_padding,
     const MinMaxSizeInput& input) {
   LayoutBox* box = node.GetLayoutBox();
-  if (!box->PreferredLogicalWidthsDirty() &&
-      !box->NeedsPreferredWidthsRecalculation()) {
+  // If we've already populated the legacy preferred logical widths cache for
+  // this node at the bottom of this function, use those cached results here.
+  // Or, if we're working on a table, use the legacy preferred widths code
+  // instead of ComputeMinAndMaxContentContribution below because
+  // ComputeMinAndMaxContentContribution assumes that if an element has a
+  // specified size, that's its final size, which tables don't follow.
+  if ((!box->PreferredLogicalWidthsDirty() &&
+       !box->NeedsPreferredWidthsRecalculation()) ||
+      box->IsTable()) {
     return MinMaxSize{box->MinPreferredLogicalWidth(),
                       box->MaxPreferredLogicalWidth()};
   }
 
+  // Compute the intrinsic sizes without regard to the specified sizes.
   MinMaxSize result = node.ComputeMinMaxSize(node.Style().GetWritingMode(),
                                              input, &constraint_space);
-  // Cache these computed values.
+  // Apply the specified min, main, max sizes.
   MinMaxSize contribution = ComputeMinAndMaxContentContribution(
       node.Style().GetWritingMode(), node.Style(), border_padding, result);
+  // Cache these computed values.
   box->SetPreferredLogicalWidthsFromNG(contribution);
   return result;
 }
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.cc b/third_party/blink/renderer/core/loader/frame_load_request.cc
index 1b788678..f04f0034 100644
--- a/third_party/blink/renderer/core/loader/frame_load_request.cc
+++ b/third_party/blink/renderer/core/loader/frame_load_request.cc
@@ -37,7 +37,8 @@
 
   // TODO(domfarolino): Stop storing ResourceRequest's generated referrer as a
   // header and instead use a separate member. See https://crbug.com/850813.
-  request.SetHttpReferrer(referrer);
+  request.SetHttpReferrer(
+      referrer, ResourceRequest::SetHttpReferrerLocation::kFrameLoadRequest);
   request.SetHTTPOriginToMatchReferrerIfNeeded();
 }
 
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 3e36506..ba0149f 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -156,9 +156,11 @@
   // header and instead use a separate member. See https://crbug.com/850813.
   if (client_redirect_policy == ClientRedirectPolicy::kClientRedirect) {
     request.SetHttpReferrer(SecurityPolicy::GenerateReferrer(
-        frame_->GetDocument()->GetReferrerPolicy(),
-        frame_->GetDocument()->Url(),
-        frame_->GetDocument()->OutgoingReferrer()));
+                                frame_->GetDocument()->GetReferrerPolicy(),
+                                frame_->GetDocument()->Url(),
+                                frame_->GetDocument()->OutgoingReferrer()),
+                            ResourceRequest::SetHttpReferrerLocation::
+                                kFrameLoaderResourceRequestForReload);
   }
 
   request.SetSkipServiceWorker(frame_load_type ==
diff --git a/third_party/blink/renderer/core/loader/history_item.cc b/third_party/blink/renderer/core/loader/history_item.cc
index 9bf4ae2..2c77a07c 100644
--- a/third_party/blink/renderer/core/loader/history_item.cc
+++ b/third_party/blink/renderer/core/loader/history_item.cc
@@ -151,9 +151,12 @@
 ResourceRequest HistoryItem::GenerateResourceRequest(
     mojom::FetchCacheMode cache_mode) {
   ResourceRequest request(url_string_);
-  // TODO(domfarolino): Stop storing ResourceRequest's generated referrer as a
-  // header and instead use a separate member. See https://crbug.com/850813.
-  request.SetHttpReferrer(referrer_);
+  request.SetReferrerString(
+      referrer_.referrer,
+      ResourceRequest::SetReferrerStringLocation::kHistoryItem);
+  request.SetReferrerPolicy(
+      referrer_.referrer_policy,
+      ResourceRequest::SetReferrerPolicyLocation::kHistoryItem);
   request.SetCacheMode(cache_mode);
   if (form_data_) {
     request.SetHttpMethod(http_names::kPOST);
diff --git a/third_party/blink/renderer/core/loader/image_loader.cc b/third_party/blink/renderer/core/loader/image_loader.cc
index abb548d..8ba1d7d9 100644
--- a/third_party/blink/renderer/core/loader/image_loader.cc
+++ b/third_party/blink/renderer/core/loader/image_loader.cc
@@ -555,7 +555,9 @@
       resource_request.SetPreviewsState(WebURLRequest::kPreviewsNoTransform);
     }
 
-    resource_request.SetReferrerPolicy(referrer_policy);
+    resource_request.SetReferrerPolicy(
+        referrer_policy,
+        ResourceRequest::SetReferrerPolicyLocation::kImageLoader);
 
     // Correct the RequestContext if necessary.
     if (IsHTMLPictureElement(GetElement()->parentNode()) ||
diff --git a/third_party/blink/renderer/core/loader/link_loader.cc b/third_party/blink/renderer/core/loader/link_loader.cc
index e8c9a52..cdb64f1 100644
--- a/third_party/blink/renderer/core/loader/link_loader.cc
+++ b/third_party/blink/renderer/core/loader/link_loader.cc
@@ -226,7 +226,9 @@
                                 Document& document,
                                 ResourceClient* link_client) {
   ResourceRequest resource_request(document.CompleteURL(params.href));
-  resource_request.SetReferrerPolicy(params.referrer_policy);
+  resource_request.SetReferrerPolicy(
+      params.referrer_policy,
+      ResourceRequest::SetReferrerPolicyLocation::kLoadStylesheet);
 
   mojom::FetchImportanceMode importance_mode =
       GetFetchImportanceAttributeValue(params.importance);
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
index 29a5405..9e76c02c 100644
--- a/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
+++ b/third_party/blink/renderer/core/loader/modulescript/module_script_loader.cc
@@ -161,12 +161,9 @@
 
   // <spec label="SMSR">... its referrer policy to options's referrer
   // policy.</spec>
-  //
-  // Note: For now this is done below with SetHttpReferrer()
-  network::mojom::ReferrerPolicy referrer_policy =
-      module_request.Options().GetReferrerPolicy();
-  if (referrer_policy == network::mojom::ReferrerPolicy::kDefault)
-    referrer_policy = fetch_client_settings_object.GetReferrerPolicy();
+  fetch_params.MutableResourceRequest().SetReferrerPolicy(
+      module_request.Options().GetReferrerPolicy(),
+      ResourceRequest::SetReferrerPolicyLocation::kModuleLoader);
 
   // <spec step="5">... mode is "cors", ...</spec>
   //
@@ -177,18 +174,9 @@
       options_.CredentialsMode());
 
   // <spec step="5">... referrer is referrer, ...</spec>
-  //
-  // Note: For now this is done below with SetHttpReferrer()
-  String referrer_string = module_request.ReferrerString();
-  if (referrer_string == Referrer::ClientReferrerString())
-    referrer_string = fetch_client_settings_object.GetOutgoingReferrer();
-
-  // TODO(domfarolino): Stop storing ResourceRequest's referrer as a
-  // blink::Referrer (https://crbug.com/850813).
-  fetch_params.MutableResourceRequest().SetHttpReferrer(
-      SecurityPolicy::GenerateReferrer(referrer_policy,
-                                       fetch_params.GetResourceRequest().Url(),
-                                       referrer_string));
+  fetch_params.MutableResourceRequest().SetReferrerString(
+      module_request.ReferrerString(),
+      ResourceRequest::SetReferrerStringLocation::kModuleScriptLoader);
 
   // Priority Hints and a request's "importance" are currently non-standard, but
   // we can assume the following (see https://crbug.com/821464):
diff --git a/third_party/blink/renderer/core/loader/ping_loader.cc b/third_party/blink/renderer/core/loader/ping_loader.cc
index 4c1cd9a..2451d28 100644
--- a/third_party/blink/renderer/core/loader/ping_loader.cc
+++ b/third_party/blink/renderer/core/loader/ping_loader.cc
@@ -228,8 +228,12 @@
   request.SetKeepalive(true);
   // TODO(domfarolino): Add WPTs ensuring that pings do not have a referrer
   // header.
-  request.SetReferrerString(Referrer::NoReferrer());
-  request.SetReferrerPolicy(network::mojom::ReferrerPolicy::kNever);
+  request.SetReferrerString(
+      Referrer::NoReferrer(),
+      ResourceRequest::SetReferrerStringLocation::kPingLoader);
+  request.SetReferrerPolicy(
+      network::mojom::ReferrerPolicy::kNever,
+      ResourceRequest::SetReferrerPolicyLocation::kPingLoader);
   request.SetRequestContext(mojom::RequestContextType::PING);
   FetchParameters params(request);
   params.MutableOptions().initiator_info.name =
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index c19ecc5f..7c5fe741 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -280,7 +280,9 @@
   resource_request.SetRequestContext(ResourceFetcher::DetermineRequestContext(
       resource_type.value(), ResourceFetcher::kImageNotImageSet));
 
-  resource_request.SetReferrerPolicy(params.referrer_policy);
+  resource_request.SetReferrerPolicy(
+      params.referrer_policy,
+      ResourceRequest::SetReferrerPolicyLocation::kPreloadIfNeeded);
 
   resource_request.SetFetchImportanceMode(
       GetFetchImportanceAttributeValue(params.importance));
@@ -462,7 +464,9 @@
     UseCounter::Count(document, WebFeature::kLinkRelPrefetch);
 
     ResourceRequest resource_request(params.href);
-    resource_request.SetReferrerPolicy(params.referrer_policy);
+    resource_request.SetReferrerPolicy(
+        params.referrer_policy,
+        ResourceRequest::SetReferrerPolicyLocation::kPrefetchIfNeeded);
     resource_request.SetFetchImportanceMode(
         GetFetchImportanceAttributeValue(params.importance));
 
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.cc b/third_party/blink/renderer/core/loader/threadable_loader.cc
index c216056..315de07f 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.cc
+++ b/third_party/blink/renderer/core/loader/threadable_loader.cc
@@ -169,8 +169,14 @@
   preflight_request->SetRequestContext(request.GetRequestContext());
   preflight_request->SetCredentialsMode(network::mojom::CredentialsMode::kOmit);
   preflight_request->SetSkipServiceWorker(true);
-  preflight_request->SetReferrerString(request.ReferrerString());
-  preflight_request->SetReferrerPolicy(request.GetReferrerPolicy());
+  preflight_request->SetReferrerString(
+      request.ReferrerString(),
+      ResourceRequest::SetReferrerStringLocation::
+          kThreadableLoaderCreateAccessControlPreflightRequest);
+  preflight_request->SetReferrerPolicy(
+      request.GetReferrerPolicy(),
+      ResourceRequest::SetReferrerPolicyLocation::
+          kThreadableLoaderCreateAccessControlPreflightRequest);
 
   if (request.IsExternalRequest()) {
     preflight_request->SetHttpHeaderField(
@@ -341,10 +347,14 @@
   if (GetSecurityOrigin())
     request.SetHTTPOrigin(GetSecurityOrigin());
 
-  // TODO(domfarolino): Stop setting the HTTPReferrer header, and instead use
-  // ResourceRequest::referrer_. See https://crbug.com/850813.
-  if (override_referrer_)
-    request.SetHttpReferrer(referrer_after_redirect_);
+  if (override_referrer_) {
+    request.SetReferrerString(referrer_after_redirect_.referrer,
+                              ResourceRequest::SetReferrerStringLocation::
+                                  kThreadableLoaderPrepareCrossOriginRequest);
+    request.SetReferrerPolicy(referrer_after_redirect_.referrer_policy,
+                              ResourceRequest::SetReferrerPolicyLocation::
+                                  kThreadableLoaderPrepareCrossOriginRequest);
+  }
 }
 
 void ThreadableLoader::LoadPreflightRequest(
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
index 2770d44..4ac1e0f 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder_test.cc
@@ -6533,7 +6533,7 @@
               properties->Effect()->GetCompositorElementId());
   ASSERT_TRUE(cc_effect);
   EXPECT_FLOAT_EQ(cc_effect->opacity, 0.5f);
-  EXPECT_FALSE(cc_effect->effect_changed);
+  EXPECT_TRUE(cc_effect->effect_changed);
   EXPECT_FALSE(GetChromeClient()
                    .layer_tree_host()
                    ->property_trees()
diff --git a/third_party/blink/renderer/modules/locks/lock.cc b/third_party/blink/renderer/modules/locks/lock.cc
index 515ce51..330079cb 100644
--- a/third_party/blink/renderer/modules/locks/lock.cc
+++ b/third_party/blink/renderer/modules/locks/lock.cc
@@ -87,6 +87,10 @@
 
 Lock::~Lock() = default;
 
+void Lock::Dispose() {
+  handle_.reset();
+}
+
 String Lock::mode() const {
   return ModeToString(mode_);
 }
diff --git a/third_party/blink/renderer/modules/locks/lock.h b/third_party/blink/renderer/modules/locks/lock.h
index f5fe9d4..749569c 100644
--- a/third_party/blink/renderer/modules/locks/lock.h
+++ b/third_party/blink/renderer/modules/locks/lock.h
@@ -22,7 +22,7 @@
 class Lock final : public ScriptWrappable, public ContextLifecycleObserver {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(Lock);
-
+  USING_PRE_FINALIZER(Lock, Dispose);
  public:
   static Lock* Create(ScriptState*,
                       const String& name,
@@ -38,7 +38,8 @@
   ~Lock() override;
 
   void Trace(blink::Visitor*) override;
-  EAGERLY_FINALIZE();
+
+  void Dispose();
 
   // Lock.idl
   String name() const { return name_; }
diff --git a/third_party/blink/renderer/modules/locks/lock_manager.cc b/third_party/blink/renderer/modules/locks/lock_manager.cc
index 2ee7e0ab..bae478c4 100644
--- a/third_party/blink/renderer/modules/locks/lock_manager.cc
+++ b/third_party/blink/renderer/modules/locks/lock_manager.cc
@@ -56,7 +56,7 @@
     : public GarbageCollectedFinalized<LockRequestImpl>,
       public NameClient,
       public mojom::blink::LockRequest {
-  EAGERLY_FINALIZE();
+  USING_PRE_FINALIZER(LockManager::LockRequestImpl, Dispose);
 
  public:
   LockRequestImpl(V8LockGrantedCallback* callback,
@@ -78,6 +78,12 @@
 
   ~LockRequestImpl() override = default;
 
+  void Dispose() {
+    // This Impl might still be bound to a LockRequest, so we close
+    // the binding before destroying the object.
+    binding_.Close();
+  }
+
   void Trace(blink::Visitor* visitor) {
     visitor->Trace(resolver_);
     visitor->Trace(manager_);
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
index 2ef0997..e82074b 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
@@ -119,16 +119,10 @@
       audio_codec_id_(AudioTrackRecorder::CodecId::LAST),
       recording_(false),
       recorder_(nullptr),
-      task_runner_(std::move(task_runner)),
-      weak_factory_(this) {}
+      task_runner_(std::move(task_runner)) {}
 
 MediaRecorderHandler::~MediaRecorderHandler() = default;
 
-void MediaRecorderHandler::Dispose() {
-  webm_muxer_.reset();
-  task_runner_.reset();
-}
-
 bool MediaRecorderHandler::CanSupportMimeType(const String& type,
                                               const String& web_codecs) {
   DCHECK(IsMainThread());
@@ -259,7 +253,7 @@
                            CodecIdToMediaAudioCodec(audio_codec_id_),
                            use_video_tracks, use_audio_tracks,
                            WTF::BindRepeating(&MediaRecorderHandler::WriteData,
-                                              weak_factory_.GetWeakPtr())));
+                                              WrapWeakPersistent(this))));
 
   if (use_video_tracks) {
     // TODO(mcasas): The muxer API supports only one video track. Extend it to
@@ -272,7 +266,7 @@
 
     const VideoTrackRecorder::OnEncodedVideoCB on_encoded_video_cb =
         media::BindToCurrentLoop(WTF::BindRepeating(
-            &MediaRecorderHandler::OnEncodedVideo, weak_factory_.GetWeakPtr()));
+            &MediaRecorderHandler::OnEncodedVideo, WrapWeakPersistent(this)));
 
     video_recorders_.emplace_back(MakeGarbageCollected<VideoTrackRecorder>(
         video_codec_id_, video_tracks_[0], on_encoded_video_cb,
@@ -290,7 +284,7 @@
 
     const AudioTrackRecorder::OnEncodedAudioCB on_encoded_audio_cb =
         media::BindToCurrentLoop(base::Bind(
-            &MediaRecorderHandler::OnEncodedAudio, weak_factory_.GetWeakPtr()));
+            &MediaRecorderHandler::OnEncodedAudio, WrapWeakPersistent(this)));
 
     audio_recorders_.emplace_back(MakeGarbageCollected<AudioTrackRecorder>(
         audio_codec_id_, audio_tracks_[0], std::move(on_encoded_audio_cb),
@@ -305,7 +299,6 @@
   DCHECK(IsMainThread());
   // Don't check |recording_| since we can go directly from pause() to stop().
 
-  weak_factory_.InvalidateWeakPtrs();
   recording_ = false;
   timeslice_ = base::TimeDelta::FromMilliseconds(0);
   video_recorders_.clear();
@@ -465,6 +458,9 @@
     bool is_key_frame) {
   DCHECK(IsMainThread());
 
+  if (video_recorders_.IsEmpty())
+    return;
+
   if (UpdateTracksAndCheckIfChanged()) {
     recorder_->OnError("Amount of tracks in MediaStream has changed.");
     return;
@@ -484,6 +480,9 @@
                                           base::TimeTicks timestamp) {
   DCHECK(IsMainThread());
 
+  if (audio_recorders_.IsEmpty())
+    return;
+
   if (UpdateTracksAndCheckIfChanged()) {
     recorder_->OnError("Amount of tracks in MediaStream has changed.");
     return;
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
index b5f413b..7c3da873 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_piece.h"
 #include "base/threading/thread_checker.h"
@@ -45,7 +44,6 @@
 // guarantee this, since VideoTrackRecorder sends back frames on IO thread.)
 class MODULES_EXPORT MediaRecorderHandler
     : public GarbageCollectedFinalized<MediaRecorderHandler> {
-  USING_PRE_FINALIZER(MediaRecorderHandler, Dispose);
 
  public:
   static MediaRecorderHandler* Create(
@@ -82,8 +80,6 @@
                     OnMediaCapabilitiesEncodingInfoCallback cb);
   String ActualMimeType();
 
-   void Dispose();
-
   void Trace(blink::Visitor*);
 
  private:
@@ -143,8 +139,6 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
-  base::WeakPtrFactory<MediaRecorderHandler> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(MediaRecorderHandler);
 };
 
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index 44a6c33..59cffa5 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -12,6 +12,7 @@
     "dom_window_media_stream.h",
     "input_device_info.cc",
     "input_device_info.h",
+    "local_media_stream_audio_source.cc",
     "media_constraints_impl.cc",
     "media_constraints_impl.h",
     "media_device_info.cc",
diff --git a/third_party/blink/renderer/modules/mediastream/DEPS b/third_party/blink/renderer/modules/mediastream/DEPS
index e47c304..de4a7a0 100644
--- a/third_party/blink/renderer/modules/mediastream/DEPS
+++ b/third_party/blink/renderer/modules/mediastream/DEPS
@@ -3,6 +3,7 @@
     # migrated to WTF types in mediastream.
     "+base/strings/utf_string_conversions.h",
     "+base/strings/string_number_conversions.h",
+    "+base/strings/stringprintf.h",
     "+base/containers/flat_map.h",
     "+base/callback_helpers.h",
 
@@ -12,6 +13,7 @@
     "+base/threading/thread_task_runner_handle.h",
 
     "+base/trace_event/trace_event.h",
+    "+media/audio",
     "+media/base",
     "+media/capture",
     "+media/mojo/interfaces",
diff --git a/content/renderer/media/stream/local_media_stream_audio_source.cc b/third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.cc
similarity index 63%
rename from content/renderer/media/stream/local_media_stream_audio_source.cc
rename to third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.cc
index f13deb03e..3a1fead 100644
--- a/content/renderer/media/stream/local_media_stream_audio_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.cc
@@ -2,28 +2,50 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/stream/local_media_stream_audio_source.h"
+#include "third_party/blink/public/web/modules/mediastream/local_media_stream_audio_source.h"
 
 #include <utility>
 
-#include "content/renderer/media/audio/audio_device_factory.h"
-#include "content/renderer/render_frame_impl.h"
+#include "base/strings/stringprintf.h"
+#include "media/audio/audio_source_parameters.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 #include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 
-namespace content {
+namespace blink {
+
+class LocalMediaStreamAudioSource::InternalFrame {
+ public:
+  explicit InternalFrame(WebLocalFrame* web_frame)
+      : frame_(web_frame ? static_cast<LocalFrame*>(
+                               WebLocalFrame::ToCoreFrame(*web_frame))
+                         : nullptr) {}
+
+  LocalFrame* frame() { return frame_.Get(); }
+  WebLocalFrame* web_frame() {
+    if (!frame_)
+      return nullptr;
+
+    return static_cast<WebLocalFrame*>(WebFrame::FromFrame(frame()));
+  }
+
+ private:
+  WeakPersistent<LocalFrame> frame_;
+};
 
 LocalMediaStreamAudioSource::LocalMediaStreamAudioSource(
-    int consumer_render_frame_id,
-    const blink::MediaStreamDevice& device,
+    WebLocalFrame* web_frame,
+    const MediaStreamDevice& device,
     const int* requested_buffer_size,
     bool disable_local_echo,
     ConstraintsRepeatingCallback started_callback,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-    : blink::MediaStreamAudioSource(std::move(task_runner),
-                                    true /* is_local_source */,
-                                    disable_local_echo),
-      consumer_render_frame_id_(consumer_render_frame_id),
+    : MediaStreamAudioSource(std::move(task_runner),
+                             true /* is_local_source */,
+                             disable_local_echo),
+      internal_consumer_frame_(std::make_unique<InternalFrame>(web_frame)),
       started_callback_(std::move(started_callback)) {
   DVLOG(1) << "LocalMediaStreamAudioSource::LocalMediaStreamAudioSource()";
   SetDevice(device);
@@ -35,7 +57,7 @@
   // If the device buffer size was not provided, use a default.
   if (frames_per_buffer <= 0) {
     frames_per_buffer =
-        (device.input.sample_rate() * blink::kFallbackAudioLatencyMs) / 1000;
+        (device.input.sample_rate() * kFallbackAudioLatencyMs) / 1000;
   }
 
   // Set audio format and take into account the special case where a discrete
@@ -63,27 +85,26 @@
     return true;
 
   std::string str = base::StringPrintf(
-      "LocalMediaStreamAudioSource::EnsureSourceIsStarted. render_frame_id=%d"
-      ", channel_layout=%d, sample_rate=%d, buffer_size=%d"
+      "LocalMediaStreamAudioSource::EnsureSourceIsStarted."
+      " channel_layout=%d, sample_rate=%d, buffer_size=%d"
       ", session_id=%d, effects=%d. ",
-      consumer_render_frame_id_, device().input.channel_layout(),
-      device().input.sample_rate(), device().input.frames_per_buffer(),
-      device().session_id, device().input.effects());
-  blink::WebRtcLogMessage(str);
+      device().input.channel_layout(), device().input.sample_rate(),
+      device().input.frames_per_buffer(), device().session_id,
+      device().input.effects());
+  WebRtcLogMessage(str);
   DVLOG(1) << str;
 
-  // Sanity-check that the consuming RenderFrame still exists. This is required
-  // by AudioDeviceFactory.
-  if (!RenderFrameImpl::FromRoutingID(consumer_render_frame_id_))
+  // Sanity-check that the consuming WebLocalFrame still exists.
+  // This is required by AudioDeviceFactory.
+  if (!internal_consumer_frame_->web_frame())
     return false;
 
   VLOG(1) << "Starting local audio input device (session_id="
-          << device().session_id << ") for render frame "
-          << consumer_render_frame_id_ << " with audio parameters={"
+          << device().session_id << ") with audio parameters={"
           << GetAudioParameters().AsHumanReadableString() << "}.";
 
-  source_ = AudioDeviceFactory::NewAudioCapturerSource(
-      consumer_render_frame_id_,
+  source_ = Platform::Current()->NewAudioCapturerSource(
+      internal_consumer_frame_->web_frame(),
       media::AudioSourceParameters(device().session_id));
   source_->Initialize(GetAudioParameters(), this);
   source_->Start();
@@ -100,13 +121,12 @@
   source_ = nullptr;
 
   VLOG(1) << "Stopped local audio input device (session_id="
-          << device().session_id << ") for render frame "
-          << consumer_render_frame_id_ << " with audio parameters={"
+          << device().session_id << ") with audio parameters={"
           << GetAudioParameters().AsHumanReadableString() << "}.";
 }
 
 void LocalMediaStreamAudioSource::OnCaptureStarted() {
-  started_callback_.Run(this, blink::mojom::MediaStreamRequestResult::OK, "");
+  started_callback_.Run(this, mojom::MediaStreamRequestResult::OK, "");
 }
 
 void LocalMediaStreamAudioSource::Capture(const media::AudioBus* audio_bus,
@@ -123,8 +143,7 @@
 }
 
 void LocalMediaStreamAudioSource::OnCaptureError(const std::string& why) {
-  blink::WebRtcLogMessage("LocalMediaStreamAudioSource::OnCaptureError: " +
-                          why);
+  WebRtcLogMessage("LocalMediaStreamAudioSource::OnCaptureError: " + why);
   StopSourceOnError(why);
 }
 
@@ -133,8 +152,8 @@
 }
 
 void LocalMediaStreamAudioSource::ChangeSourceImpl(
-    const blink::MediaStreamDevice& new_device) {
-  blink::WebRtcLogMessage(
+    const MediaStreamDevice& new_device) {
+  WebRtcLogMessage(
       "LocalMediaStreamAudioSource::ChangeSourceImpl(new_device = " +
       new_device.id + ")");
   EnsureSourceIsStopped();
@@ -142,4 +161,4 @@
   EnsureSourceIsStarted();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 6864404..1f179ec 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1632,6 +1632,7 @@
     "bindings/runtime_call_stats_test.cc",
     "exported/file_path_conversion_test.cc",
     "exported/mediastream/media_stream_audio_processor_options_test.cc",
+    "exported/mediastream/media_stream_audio_test.cc",
     "exported/mediastream/webrtc_uma_histograms_test.cc",
     "exported/web_canonical_cookie_test.cc",
     "exported/web_string_test.cc",
diff --git a/third_party/blink/renderer/platform/exported/mediastream/DEPS b/third_party/blink/renderer/platform/exported/mediastream/DEPS
index 731f240..0544038 100644
--- a/third_party/blink/renderer/platform/exported/mediastream/DEPS
+++ b/third_party/blink/renderer/platform/exported/mediastream/DEPS
@@ -1,10 +1,9 @@
-
 include_rules = [
-    # TODO(crbug.com/923394): Remove these dependencies once std types are
-    # migrated to WTF types in mediastream.
-    "+base/strings/utf_string_conversions.h",
-    "+base/strings/string_number_conversions.h",
-    "+base/strings/string_split.h",
-
     "+media/base",
 ]
+
+specific_include_rules = {
+  "media_stream_audio_test\.cc" : [
+    "+base/threading/platform_thread.h",
+  ],
+}
diff --git a/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_processor_options.cc b/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_processor_options.cc
index 18c87df1..c70fc40c 100644
--- a/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_processor_options.cc
+++ b/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_processor_options.cc
@@ -14,10 +14,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "media/base/audio_parameters.h"
diff --git a/content/renderer/media/stream/media_stream_audio_unittest.cc b/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc
similarity index 82%
rename from content/renderer/media/stream/media_stream_audio_unittest.cc
rename to third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc
index 8311e76..7cb47ded 100644
--- a/content/renderer/media/stream/media_stream_audio_unittest.cc
+++ b/third_party/blink/renderer/platform/exported/mediastream/media_stream_audio_test.cc
@@ -4,7 +4,6 @@
 
 #include <stdint.h>
 
-#include "base/atomicops.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/scoped_task_environment.h"
@@ -21,7 +20,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/web_heap.h"
 
-namespace content {
+namespace blink {
 
 namespace {
 
@@ -35,41 +34,40 @@
 // emits audio samples with monotonically-increasing sample values. Includes
 // hooks for the unit tests to confirm lifecycle status and to change audio
 // format.
-class FakeMediaStreamAudioSource : public blink::MediaStreamAudioSource,
+class FakeMediaStreamAudioSource : public MediaStreamAudioSource,
                                    public base::PlatformThread::Delegate {
  public:
   FakeMediaStreamAudioSource()
-      : blink::MediaStreamAudioSource(
-            blink::scheduler::GetSingleThreadTaskRunnerForTesting(),
-            true),
+      : MediaStreamAudioSource(scheduler::GetSingleThreadTaskRunnerForTesting(),
+                               true),
         stop_event_(base::WaitableEvent::ResetPolicy::MANUAL,
                     base::WaitableEvent::InitialState::NOT_SIGNALED),
         next_buffer_size_(kBufferSize),
         sample_count_(0) {}
 
   ~FakeMediaStreamAudioSource() final {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     EnsureSourceIsStopped();
   }
 
   bool was_started() const {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     return !thread_.is_null();
   }
 
   bool was_stopped() const {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     return stop_event_.IsSignaled();
   }
 
   void SetBufferSize(int new_buffer_size) {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     base::subtle::NoBarrier_Store(&next_buffer_size_, new_buffer_size);
   }
 
  protected:
   bool EnsureSourceIsStarted() final {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     if (was_started())
       return true;
     if (was_stopped())
@@ -80,7 +78,7 @@
   }
 
   void EnsureSourceIsStopped() final {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     if (was_stopped())
       return;
     stop_event_.Signal();
@@ -93,7 +91,7 @@
       // If needed, notify of the new format and re-create |audio_bus_|.
       const int buffer_size = base::subtle::NoBarrier_Load(&next_buffer_size_);
       if (!audio_bus_ || audio_bus_->frames() != buffer_size) {
-        blink::MediaStreamAudioSource::SetFormat(media::AudioParameters(
+        MediaStreamAudioSource::SetFormat(media::AudioParameters(
             media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
             media::CHANNEL_LAYOUT_MONO, kSampleRate, buffer_size));
         audio_bus_ = media::AudioBus::Create(1, buffer_size);
@@ -105,8 +103,8 @@
       for (int i = 0; i < buffer_size; ++i)
         data[i] = ++sample_count_;
       CHECK_LT(sample_count_, kMaxValueSafelyConvertableToFloat);
-      blink::MediaStreamAudioSource::DeliverDataToTracks(
-          *audio_bus_, base::TimeTicks::Now());
+      MediaStreamAudioSource::DeliverDataToTracks(*audio_bus_,
+                                                  base::TimeTicks::Now());
 
       // Sleep before producing the next chunk of audio.
       base::PlatformThread::Sleep(base::TimeDelta::FromMicroseconds(
@@ -115,7 +113,7 @@
   }
 
  private:
-  base::ThreadChecker main_thread_checker_;
+  THREAD_CHECKER(main_thread_checker_);
 
   base::PlatformThreadHandle thread_;
   mutable base::WaitableEvent stop_event_;
@@ -127,20 +125,16 @@
   DISALLOW_COPY_AND_ASSIGN(FakeMediaStreamAudioSource);
 };
 
-// A simple blink::WebMediaStreamAudioSink that consumes audio and confirms the
+// A simple WebMediaStreamAudioSink that consumes audio and confirms the
 // sample values. Includes hooks for the unit tests to monitor the format and
 // flow of audio, whether the audio is silent, and the propagation of the
 // "enabled" state.
-class FakeMediaStreamAudioSink : public blink::WebMediaStreamAudioSink {
+class FakeMediaStreamAudioSink : public WebMediaStreamAudioSink {
  public:
-  enum EnableState {
-    NO_ENABLE_NOTIFICATION,
-    WAS_ENABLED,
-    WAS_DISABLED
-  };
+  enum EnableState { NO_ENABLE_NOTIFICATION, WAS_ENABLED, WAS_DISABLED };
 
   FakeMediaStreamAudioSink()
-      : blink::WebMediaStreamAudioSink(),
+      : WebMediaStreamAudioSink(),
         expected_sample_count_(-1),
         num_on_data_calls_(0),
         audio_is_silent_(true),
@@ -148,32 +142,32 @@
         enable_state_(NO_ENABLE_NOTIFICATION) {}
 
   ~FakeMediaStreamAudioSink() final {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   }
 
   media::AudioParameters params() const {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     base::AutoLock auto_lock(params_lock_);
     return params_;
   }
 
   int num_on_data_calls() const {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     return base::subtle::NoBarrier_Load(&num_on_data_calls_);
   }
 
   bool is_audio_silent() const {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     return !!base::subtle::NoBarrier_Load(&audio_is_silent_);
   }
 
   bool was_ended() const {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     return was_ended_;
   }
 
   EnableState enable_state() const {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     return enable_state_;
   }
 
@@ -214,20 +208,19 @@
     base::subtle::NoBarrier_AtomicIncrement(&num_on_data_calls_, 1);
   }
 
-  void OnReadyStateChanged(
-      blink::WebMediaStreamSource::ReadyState state) final {
-    CHECK(main_thread_checker_.CalledOnValidThread());
-    if (state == blink::WebMediaStreamSource::kReadyStateEnded)
+  void OnReadyStateChanged(WebMediaStreamSource::ReadyState state) final {
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
+    if (state == WebMediaStreamSource::kReadyStateEnded)
       was_ended_ = true;
   }
 
   void OnEnabledChanged(bool enabled) final {
-    CHECK(main_thread_checker_.CalledOnValidThread());
+    DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
     enable_state_ = enabled ? WAS_ENABLED : WAS_DISABLED;
   }
 
  private:
-  base::ThreadChecker main_thread_checker_;
+  THREAD_CHECKER(main_thread_checker_);
 
   mutable base::Lock params_lock_;
   media::AudioParameters params_;
@@ -246,10 +239,9 @@
 class MediaStreamAudioTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    blink_audio_source_.Initialize(blink::WebString::FromUTF8("audio_id"),
-                                   blink::WebMediaStreamSource::kTypeAudio,
-                                   blink::WebString::FromUTF8("audio_track"),
-                                   false /* remote */);
+    blink_audio_source_.Initialize(
+        WebString::FromUTF8("audio_id"), WebMediaStreamSource::kTypeAudio,
+        WebString::FromUTF8("audio_track"), false /* remote */);
     blink_audio_track_.Initialize(blink_audio_source_.Id(),
                                   blink_audio_source_);
   }
@@ -257,20 +249,20 @@
   void TearDown() override {
     blink_audio_track_.Reset();
     blink_audio_source_.Reset();
-    blink::WebHeap::CollectAllGarbageForTesting();
+    WebHeap::CollectAllGarbageForTesting();
   }
 
   FakeMediaStreamAudioSource* source() const {
     return static_cast<FakeMediaStreamAudioSource*>(
-        blink::MediaStreamAudioSource::From(blink_audio_source_));
+        MediaStreamAudioSource::From(blink_audio_source_));
   }
 
-  blink::MediaStreamAudioTrack* track() const {
-    return blink::MediaStreamAudioTrack::From(blink_audio_track_);
+  MediaStreamAudioTrack* track() const {
+    return MediaStreamAudioTrack::From(blink_audio_track_);
   }
 
-  blink::WebMediaStreamSource blink_audio_source_;
-  blink::WebMediaStreamTrack blink_audio_track_;
+  WebMediaStreamSource blink_audio_source_;
+  WebMediaStreamTrack blink_audio_track_;
 
   base::test::ScopedTaskEnvironment task_environment_;
 };
@@ -335,19 +327,19 @@
 
   // Now, connect another track. ConnectToTrack() will return false, but there
   // should be a MediaStreamAudioTrack instance created and owned by the
-  // blink::WebMediaStreamTrack.
-  blink::WebMediaStreamTrack another_blink_track;
+  // WebMediaStreamTrack.
+  WebMediaStreamTrack another_blink_track;
   another_blink_track.Initialize(blink_audio_source_.Id(), blink_audio_source_);
-  EXPECT_FALSE(blink::MediaStreamAudioTrack::From(another_blink_track));
+  EXPECT_FALSE(MediaStreamAudioTrack::From(another_blink_track));
   EXPECT_FALSE(source()->ConnectToTrack(another_blink_track));
-  EXPECT_TRUE(blink::MediaStreamAudioTrack::From(another_blink_track));
+  EXPECT_TRUE(MediaStreamAudioTrack::From(another_blink_track));
 }
 
 // Tests that a sink is immediately "ended" when connected to a stopped track.
 TEST_F(MediaStreamAudioTest, AddSinkToStoppedTrack) {
   // Create a track and stop it. Then, when adding a sink, the sink should get
   // the ReadyStateEnded notification immediately.
-  blink::MediaStreamAudioTrack track(true);
+  MediaStreamAudioTrack track(true);
   track.Stop();
   FakeMediaStreamAudioSink sink;
   EXPECT_FALSE(sink.was_ended());
@@ -428,13 +420,12 @@
   // Create a second track and a second sink, but this time the track starts out
   // disabled. Expect the sink to be notified at the start that the track is
   // disabled.
-  blink::WebMediaStreamTrack another_blink_track;
+  WebMediaStreamTrack another_blink_track;
   another_blink_track.Initialize(blink_audio_source_.Id(), blink_audio_source_);
   EXPECT_TRUE(source()->ConnectToTrack(another_blink_track));
-  blink::MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(false);
+  MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(false);
   FakeMediaStreamAudioSink another_sink;
-  blink::MediaStreamAudioTrack::From(another_blink_track)
-      ->AddSink(&another_sink);
+  MediaStreamAudioTrack::From(another_blink_track)->AddSink(&another_sink);
   EXPECT_EQ(FakeMediaStreamAudioSink::WAS_DISABLED,
             another_sink.enable_state());
 
@@ -446,7 +437,7 @@
   EXPECT_TRUE(another_sink.is_audio_silent());
 
   // Now, enable the second track and expect the second sink to be notified.
-  blink::MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(true);
+  MediaStreamAudioTrack::From(another_blink_track)->SetEnabled(true);
   EXPECT_EQ(FakeMediaStreamAudioSink::WAS_ENABLED, another_sink.enable_state());
 
   // Wait until non-silent audio reaches the second sink.
@@ -459,9 +450,8 @@
   EXPECT_EQ(FakeMediaStreamAudioSink::WAS_DISABLED, sink.enable_state());
   EXPECT_TRUE(sink.is_audio_silent());
 
-  blink::MediaStreamAudioTrack::From(another_blink_track)
-      ->RemoveSink(&another_sink);
+  MediaStreamAudioTrack::From(another_blink_track)->RemoveSink(&another_sink);
   track()->RemoveSink(&sink);
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index 2f47655e..d2530d9c 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -172,7 +172,9 @@
       web_referrer.IsEmpty() ? Referrer::NoReferrer() : String(web_referrer);
   // TODO(domfarolino): Stop storing ResourceRequest's generated referrer as a
   // header and instead use a separate member. See https://crbug.com/850813.
-  resource_request_->SetHttpReferrer(Referrer(referrer, referrer_policy));
+  resource_request_->SetHttpReferrer(
+      Referrer(referrer, referrer_policy),
+      ResourceRequest::SetHttpReferrerLocation::kWebURLRequest);
 }
 
 void WebURLRequest::AddHttpHeaderField(const WebString& name,
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index 8277835..4c596286 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -114,8 +114,12 @@
   }
 
   const cc::TransformNode& GetTransformNode(const cc::Layer* layer) {
-    auto* property_trees = layer_tree_->layer_tree_host()->property_trees();
-    return *property_trees->transform_tree.Node(layer->transform_tree_index());
+    return *GetPropertyTrees().transform_tree.Node(
+        layer->transform_tree_index());
+  }
+
+  const cc::EffectNode& GetEffectNode(const cc::Layer* layer) {
+    return *GetPropertyTrees().effect_tree.Node(layer->effect_tree_index());
   }
 
   const cc::LayerTreeHost& GetLayerTreeHost() {
@@ -317,6 +321,7 @@
       Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kWhite)));
   EXPECT_EQ(Translation(50, -50), child->ScreenSpaceTransform());
   EXPECT_EQ(gfx::Size(100, 100), child->bounds());
+  EXPECT_FALSE(GetTransformNode(child).transform_changed);
 }
 
 TEST_P(PaintArtifactCompositorTest, OneTransform) {
@@ -336,6 +341,7 @@
   ASSERT_EQ(2u, ContentLayerCount());
   {
     const cc::Layer* layer = ContentLayerAt(0);
+    EXPECT_TRUE(GetTransformNode(layer).transform_changed);
 
     Vector<RectWithColor> rects_with_color;
     rects_with_color.push_back(
@@ -351,6 +357,7 @@
   }
   {
     const cc::Layer* layer = ContentLayerAt(1);
+    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kGray)));
@@ -376,6 +383,7 @@
   ASSERT_EQ(2u, ContentLayerCount());
   {
     const cc::Layer* layer = ContentLayerAt(0);
+    EXPECT_TRUE(GetTransformNode(layer).transform_changed);
 
     Vector<RectWithColor> rects_with_color;
     rects_with_color.push_back(
@@ -391,6 +399,7 @@
   }
   {
     const cc::Layer* layer = ContentLayerAt(1);
+    EXPECT_FALSE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 100, 100), Color::kGray)));
@@ -417,6 +426,7 @@
   ASSERT_EQ(2u, ContentLayerCount());
   {
     const cc::Layer* layer = ContentLayerAt(0);
+    EXPECT_TRUE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kWhite)));
@@ -426,6 +436,7 @@
   }
   {
     const cc::Layer* layer = ContentLayerAt(1);
+    EXPECT_TRUE(GetTransformNode(layer).transform_changed);
     EXPECT_THAT(
         layer->GetPicture(),
         Pointee(DrawsRectangle(FloatRect(0, 0, 300, 200), Color::kBlack)));
@@ -4597,4 +4608,155 @@
   EXPECT_EQ(cc_effect->clip_id, cc_clip->parent_id);
 }
 
+TEST_P(PaintArtifactCompositorTest, TransformChange) {
+  auto t1 = Create2DTranslation(t0(), 10, 20);
+  TransformPaintPropertyNode::State t2_state{TransformationMatrix().Rotate(45)};
+  t2_state.direct_compositing_reasons = CompositingReason::k3DTransform;
+  auto t2 = TransformPaintPropertyNode::Create(*t1, std::move(t2_state));
+
+  Update(TestPaintArtifact()
+             .Chunk(1)
+             .Properties(*t2, c0(), e0())
+             .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack)
+             .Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  cc::Layer* layer = ContentLayerAt(0);
+
+  // Change t1 but not t2.
+  layer->ClearSubtreePropertyChangedForTesting();
+  t2->ClearChangedToRoot();
+  t1->Update(t0(), TransformPaintPropertyNode::State{FloatSize(20, 30)});
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            t1->NodeChanged());
+  EXPECT_EQ(PaintPropertyChangeType::kUnchanged, t2->NodeChanged());
+  Update(TestPaintArtifact()
+             .Chunk(1)
+             .Properties(*t2, c0(), e0())
+             .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack)
+             .Build());
+
+  ASSERT_EQ(1u, ContentLayerCount());
+  ASSERT_EQ(layer, ContentLayerAt(0));
+  // TODO(wangxianzhu): Probably avoid setting this flag on transform change.
+  EXPECT_TRUE(layer->subtree_property_changed());
+  // This is set by cc when propagating ancestor change flag to descendants.
+  EXPECT_TRUE(GetTransformNode(layer).transform_changed);
+  // This is set by PropertyTreeManager.
+  EXPECT_TRUE(GetPropertyTrees()
+                  .transform_tree.Node(GetTransformNode(layer).parent_id)
+                  ->transform_changed);
+
+  // Change t2 but not t1.
+  layer->ClearSubtreePropertyChangedForTesting();
+  t2->ClearChangedToRoot();
+  t2_state.transform_and_origin = TransformationMatrix().Rotate(135);
+  t2->Update(*t1, std::move(t2_state));
+  EXPECT_EQ(PaintPropertyChangeType::kUnchanged, t1->NodeChanged());
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            t2->NodeChanged());
+  Update(TestPaintArtifact()
+             .Chunk(1)
+             .Properties(*t2, c0(), e0())
+             .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack)
+             .Build());
+
+  ASSERT_EQ(1u, ContentLayerCount());
+  ASSERT_EQ(layer, ContentLayerAt(0));
+  // TODO(wangxianzhu): Probably avoid setting this flag on transform change.
+  EXPECT_TRUE(layer->subtree_property_changed());
+  EXPECT_TRUE(GetTransformNode(layer).transform_changed);
+  EXPECT_FALSE(GetPropertyTrees()
+                   .transform_tree.Node(GetTransformNode(layer).parent_id)
+                   ->transform_changed);
+
+  // Change t2 to be 2d translation which will be decomposited.
+  layer->ClearSubtreePropertyChangedForTesting();
+  t2->ClearChangedToRoot();
+  t2_state.transform_and_origin = FloatSize(20, 30);
+  t2->Update(*t1, std::move(t2_state));
+  EXPECT_EQ(PaintPropertyChangeType::kUnchanged, t1->NodeChanged());
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlyValues, t2->NodeChanged());
+  Update(TestPaintArtifact()
+             .Chunk(1)
+             .Properties(*t2, c0(), e0())
+             .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack)
+             .Build());
+
+  ASSERT_EQ(1u, ContentLayerCount());
+  ASSERT_EQ(layer, ContentLayerAt(0));
+  // The new transform is decomposited, so there is no transform_changed, but
+  // we set subtree_property_changed because offset_from_transform_parent
+  // (calculated from the decomposited transforms) changed.
+  EXPECT_TRUE(layer->subtree_property_changed());
+  EXPECT_FALSE(GetTransformNode(layer).transform_changed);
+}
+
+TEST_P(PaintArtifactCompositorTest, EffectChange) {
+  auto e1 = CreateOpacityEffect(e0(), t0(), nullptr, 0.5f);
+  auto e2 = CreateOpacityEffect(*e1, t0(), nullptr, 0.6f,
+                                CompositingReason::kWillChangeOpacity);
+
+  Update(TestPaintArtifact()
+             .Chunk(1)
+             .Properties(t0(), c0(), *e2)
+             .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack)
+             .Build());
+  ASSERT_EQ(1u, ContentLayerCount());
+  cc::Layer* layer = ContentLayerAt(0);
+
+  // Change e1 but not e2.
+  layer->ClearSubtreePropertyChangedForTesting();
+  e2->ClearChangedToRoot();
+
+  EffectPaintPropertyNode::State e1_state{&t0()};
+  e1_state.opacity = 0.8f;
+  e1_state.compositor_element_id = e1->GetCompositorElementId();
+  e1->Update(e0(), std::move(e1_state));
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            e1->NodeChanged());
+  EXPECT_EQ(PaintPropertyChangeType::kUnchanged, e2->NodeChanged());
+  Update(TestPaintArtifact()
+             .Chunk(1)
+             .Properties(t0(), c0(), *e2)
+             .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack)
+             .Build());
+
+  ASSERT_EQ(1u, ContentLayerCount());
+  ASSERT_EQ(layer, ContentLayerAt(0));
+  // TODO(wangxianzhu): Probably avoid setting this flag on Effect change.
+  EXPECT_TRUE(layer->subtree_property_changed());
+  // This is set by cc when propagating ancestor change flag to descendants.
+  EXPECT_TRUE(GetEffectNode(layer).effect_changed);
+  // This is set by PropertyTreeManager.
+  EXPECT_TRUE(GetPropertyTrees()
+                  .effect_tree.Node(GetEffectNode(layer).parent_id)
+                  ->effect_changed);
+
+  // Change e2 but not e1.
+  layer->ClearSubtreePropertyChangedForTesting();
+  e2->ClearChangedToRoot();
+  EffectPaintPropertyNode::State e2_state{&t0()};
+  e2_state.opacity = 0.9f;
+  e2_state.direct_compositing_reasons = CompositingReason::kWillChangeOpacity;
+  e2_state.compositor_element_id = e2->GetCompositorElementId();
+  e2->Update(*e1, std::move(e2_state));
+  EXPECT_EQ(PaintPropertyChangeType::kUnchanged, e1->NodeChanged());
+  EXPECT_EQ(PaintPropertyChangeType::kChangedOnlySimpleValues,
+            e2->NodeChanged());
+  Update(TestPaintArtifact()
+             .Chunk(1)
+             .Properties(t0(), c0(), *e2)
+             .RectDrawing(FloatRect(100, 100, 200, 100), Color::kBlack)
+             .Build());
+
+  ASSERT_EQ(1u, ContentLayerCount());
+  ASSERT_EQ(layer, ContentLayerAt(0));
+  // TODO(wangxianzhu): Probably avoid setting this flag on Effect change.
+  EXPECT_TRUE(layer->subtree_property_changed());
+  EXPECT_TRUE(GetEffectNode(layer).effect_changed);
+  EXPECT_FALSE(GetPropertyTrees()
+                   .effect_tree.Node(GetEffectNode(layer).parent_id)
+                   ->effect_changed);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
index 674d927..d34a180 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.cc
@@ -106,7 +106,6 @@
                                                      origin.Z());
   }
   compositor_node.needs_local_transform_update = true;
-  compositor_node.transform_changed = true;
 }
 
 static void AdjustPageScaleToUsePostLocal(cc::TransformNode& page_scale) {
@@ -118,7 +117,6 @@
   page_scale.post_local.matrix() = page_scale.local.matrix();
   page_scale.pre_local.matrix().setIdentity();
   page_scale.local.matrix().setIdentity();
-  page_scale.transform_changed = true;
 }
 
 static void SetTransformTreePageScaleFactor(
@@ -178,6 +176,7 @@
   property_trees->scroll_tree.SetScrollOffset(
       scroll_node->GetCompositorElementId(), cc_transform->scroll_offset);
 
+  cc_transform->transform_changed = true;
   property_trees->transform_tree.set_needs_update(true);
   property_trees->scroll_tree.set_needs_update(true);
   return true;
@@ -202,6 +201,7 @@
   // flag, we should clear it to let the compositor respect the new value.
   cc_transform->is_currently_animating = false;
 
+  cc_transform->transform_changed = true;
   property_trees->transform_tree.set_needs_update(true);
   return true;
 }
@@ -221,6 +221,7 @@
 
   SetTransformTreePageScaleFactor(&property_trees->transform_tree,
                                   cc_transform);
+  cc_transform->transform_changed = true;
   property_trees->transform_tree.set_needs_update(true);
   return true;
 }
@@ -416,6 +417,9 @@
   compositor_node.source_node_id = parent_id;
 
   UpdateCcTransformLocalMatrix(compositor_node, transform_node);
+  compositor_node.transform_changed =
+      transform_node.NodeChanged() >=
+      PaintPropertyChangeType::kChangedOnlySimpleValues;
   compositor_node.flattens_inherited_transform =
       transform_node.FlattensInheritedTransform();
   compositor_node.sorting_context_id = transform_node.RenderingContextId();
@@ -1088,6 +1092,8 @@
   }
   effect_node.blend_mode = blend_mode;
   effect_node.double_sided = !effect.LocalTransformSpace().IsBackfaceHidden();
+  effect_node.effect_changed =
+      effect.NodeChanged() >= PaintPropertyChangeType::kChangedOnlySimpleValues;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
index 12bb0ff..b75dca0 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_property_node.h
@@ -146,6 +146,8 @@
     cc_node_id_ = id;
   }
 
+  PaintPropertyChangeType NodeChanged() const { return changed_; }
+
 #if DCHECK_IS_ON()
   String ToTreeString() const;
 
@@ -176,7 +178,6 @@
     DCHECK(!IsRoot());
     changed_ = std::max(changed_, changed);
   }
-  PaintPropertyChangeType NodeChanged() const { return changed_; }
 
  private:
   friend class PaintPropertyNodeTest;
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index 910928f..f270653 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -28,6 +28,8 @@
     "blink_gc.h",
     "blink_gc_memory_dump_provider.cc",
     "blink_gc_memory_dump_provider.h",
+    "cancelable_task_scheduler.cc",
+    "cancelable_task_scheduler.h",
     "finalizer_traits.h",
     "garbage_collected.h",
     "gc_info.cc",
@@ -104,6 +106,7 @@
   sources = [
     "address_cache_test.cc",
     "blink_gc_memory_dump_provider_test.cc",
+    "cancelable_task_scheduler_test.cc",
     "gc_info_test.cc",
     "heap_compact_test.cc",
     "heap_stats_collector_test.cc",
diff --git a/third_party/blink/renderer/platform/heap/DEPS b/third_party/blink/renderer/platform/heap/DEPS
index 5ed63eb..da681b9 100644
--- a/third_party/blink/renderer/platform/heap/DEPS
+++ b/third_party/blink/renderer/platform/heap/DEPS
@@ -10,6 +10,7 @@
     "+base/bits.h",
     "+base/sampling_heap_profiler/poisson_allocation_sampler.h",
     "+base/synchronization/lock.h",
+    "+base/task_runner.h",
 
     "+third_party/blink/renderer/platform/bindings",
     "+third_party/blink/renderer/platform/histogram.h",
@@ -18,6 +19,7 @@
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/runtime_enabled_features.h",
     "+third_party/blink/renderer/platform/scheduler/public",
+    "+third_party/blink/renderer/platform/scheduler/test",
     "+third_party/blink/renderer/platform/testing",
     "+third_party/blink/renderer/platform/web_task_runner.h",
     "+third_party/blink/renderer/platform/wtf",
diff --git a/third_party/blink/renderer/platform/heap/cancelable_task_scheduler.cc b/third_party/blink/renderer/platform/heap/cancelable_task_scheduler.cc
new file mode 100644
index 0000000..e0af134
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/cancelable_task_scheduler.cc
@@ -0,0 +1,134 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/cancelable_task_scheduler.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+class CancelableTaskScheduler::TaskData {
+  USING_FAST_MALLOC(TaskData);
+  DISALLOW_COPY_AND_ASSIGN(TaskData);
+
+ public:
+  TaskData(Task task, CancelableTaskScheduler* scheduler)
+      : task_(std::move(task)), scheduler_(scheduler), status_(kWaiting) {}
+
+  ~TaskData() {
+    // The task runner is responsible for unregistering the task in case the
+    // task hasn't been cancelled. The first check returns true if the task
+    // hasn't been executed, the second returns true if it has.
+    Status previous;
+    if (TryRun(&previous) || previous == kRunning) {
+      scheduler_->UnregisterAndSignal(this);
+    }
+  }
+
+  void Run() {
+    if (TryRun()) {
+      std::move(task_).Run();
+    }
+  }
+
+  bool TryCancel() {
+    Status expected = kWaiting;
+    return status_.compare_exchange_strong(expected, kCancelled,
+                                           std::memory_order_acq_rel,
+                                           std::memory_order_acquire);
+  }
+
+ private:
+  // Identifies the state a cancelable task is in:
+  // |kWaiting|: The task is scheduled and waiting to be executed. {TryRun} will
+  // succeed.
+  // |kCancelled|: The task has been cancelled. {TryRun} will fail.
+  // |kRunning|: The task is currently running and cannot be canceled anymore.
+  enum Status : uint8_t { kWaiting, kCancelled, kRunning };
+
+  friend size_t RemoveCancelledTasks(WTF::HashSet<TaskData*>*);
+
+  bool TryRun(Status* previous = nullptr) {
+    Status expected = kWaiting;
+    const bool success = status_.compare_exchange_strong(
+        expected, kRunning, std::memory_order_acq_rel,
+        std::memory_order_acquire);
+    if (previous) {
+      *previous = expected;
+    }
+    return success;
+  }
+
+  Task task_;
+  CancelableTaskScheduler* const scheduler_;
+  std::atomic<Status> status_;
+};
+
+CancelableTaskScheduler::CancelableTaskScheduler(
+    scoped_refptr<base::TaskRunner> task_runner)
+    : cond_var_(&lock_), task_runner_(std::move(task_runner)) {}
+
+CancelableTaskScheduler::~CancelableTaskScheduler() {
+  base::AutoLock lock(lock_);
+  CHECK(tasks_.IsEmpty());
+}
+
+void CancelableTaskScheduler::ScheduleTask(Task task) {
+  std::unique_ptr<TaskData> task_data = Register(std::move(task));
+  task_runner_->PostTask(FROM_HERE,
+                         base::BindOnce(&TaskData::Run, std::move(task_data)));
+}
+
+size_t CancelableTaskScheduler::CancelAndWait() {
+  size_t result = 0;
+  base::AutoLock lock(lock_);
+  while (!tasks_.IsEmpty()) {
+    result += RemoveCancelledTasks();
+    if (!tasks_.IsEmpty()) {
+      cond_var_.Wait();
+    }
+  }
+  return result;
+}
+
+std::unique_ptr<CancelableTaskScheduler::TaskData>
+CancelableTaskScheduler::Register(Task task) {
+  base::AutoLock lock(lock_);
+  auto task_data = std::make_unique<TaskData>(std::move(task), this);
+  tasks_.insert(task_data.get());
+  return task_data;
+}
+
+void CancelableTaskScheduler::UnregisterAndSignal(TaskData* task_data) {
+  base::AutoLock lock(lock_);
+  CHECK(tasks_.Contains(task_data));
+  tasks_.erase(task_data);
+  cond_var_.Signal();
+}
+
+// This function is needed because WTF::HashSet::erase function invalidates
+// all iterators. Returns number of removed tasks.
+size_t CancelableTaskScheduler::RemoveCancelledTasks() {
+  WTF::Vector<TaskData*> to_be_removed;
+  // Assume worst case.
+  to_be_removed.ReserveCapacity(tasks_.size());
+  for (TaskData* task : tasks_) {
+    if (task->TryCancel()) {
+      to_be_removed.push_back(task);
+    }
+  }
+  tasks_.RemoveAll(to_be_removed);
+  return to_be_removed.size();
+}
+
+size_t CancelableTaskScheduler::NumberOfTasksForTesting() const {
+  base::AutoLock lock(lock_);
+  return tasks_.size();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/cancelable_task_scheduler.h b/third_party/blink/renderer/platform/heap/cancelable_task_scheduler.h
new file mode 100644
index 0000000..dacd97d
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/cancelable_task_scheduler.h
@@ -0,0 +1,59 @@
+// 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_PLATFORM_HEAP_CANCELABLE_TASK_SCHEDULER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CANCELABLE_TASK_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/hash_set.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace blink {
+
+// CancelableTaskScheduler allows for scheduling tasks that can be cancelled
+// before they are invoked. User is responsible for synchronizing completion of
+// tasks and destruction of CancelableTaskScheduler.
+class PLATFORM_EXPORT CancelableTaskScheduler final {
+ public:
+  using Task = WTF::CrossThreadOnceFunction<void()>;
+
+  explicit CancelableTaskScheduler(scoped_refptr<base::TaskRunner>);
+  ~CancelableTaskScheduler();
+
+  // Schedules task to run on TaskRunner.
+  void ScheduleTask(Task);
+  // Cancels all not yet started tasks and waits for running ones to complete.
+  // Returns number of cancelled (not executed) tasks.
+  size_t CancelAndWait();
+
+ private:
+  class TaskData;
+  template <class T>
+  friend class CancelableTaskSchedulerTest;
+
+  std::unique_ptr<TaskData> Register(Task);
+  void UnregisterAndSignal(TaskData*);
+
+  size_t RemoveCancelledTasks();
+
+  size_t NumberOfTasksForTesting() const;
+
+  WTF::HashSet<TaskData*> tasks_;
+  mutable base::Lock lock_;
+  base::ConditionVariable cond_var_;
+  scoped_refptr<base::TaskRunner> task_runner_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_CANCELABLE_TASK_SCHEDULER_H_
diff --git a/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc b/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc
new file mode 100644
index 0000000..a7deef5
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/cancelable_task_scheduler_test.cc
@@ -0,0 +1,97 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/heap/cancelable_task_scheduler.h"
+
+#include <atomic>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
+
+namespace blink {
+
+class ParallelTaskRunner : public base::TaskRunner {
+ public:
+  bool PostDelayedTask(const base::Location& location,
+                       base::OnceClosure task,
+                       base::TimeDelta) override {
+    worker_pool::PostTask(location, WTF::CrossThreadBindOnce(std::move(task)));
+    return true;
+  }
+
+  bool RunsTasksInCurrentSequence() const override { return false; }
+
+  void RunUntilIdle() {}
+};
+
+template <class Runner>
+class CancelableTaskSchedulerTest : public ::testing::Test {
+ public:
+  using Task = CancelableTaskScheduler::Task;
+
+  void ScheduleTask(Task callback) {
+    scheduler_.ScheduleTask(std::move(callback));
+  }
+
+  void RunTaskRunner() { task_runner_->RunUntilIdle(); }
+  size_t CancelAndWait() { return scheduler_.CancelAndWait(); }
+
+  size_t NumberOfRegisteredTasks() const {
+    return scheduler_.NumberOfTasksForTesting();
+  }
+
+ private:
+  scoped_refptr<Runner> task_runner_ = base::MakeRefCounted<Runner>();
+  CancelableTaskScheduler scheduler_{task_runner_};
+};
+
+using RunnerTypes =
+    ::testing::Types<scheduler::FakeTaskRunner, ParallelTaskRunner>;
+TYPED_TEST_SUITE(CancelableTaskSchedulerTest, RunnerTypes);
+
+TYPED_TEST(CancelableTaskSchedulerTest, EmptyCancelTasks) {
+  const size_t cancelled = this->CancelAndWait();
+  EXPECT_EQ(0u, cancelled);
+  EXPECT_EQ(0u, this->NumberOfRegisteredTasks());
+}
+
+TYPED_TEST(CancelableTaskSchedulerTest, RunAndCancelTasks) {
+  static constexpr size_t kNumberOfTasks = 10u;
+
+  const auto callback = [](std::atomic<int>* i) { ++(*i); };
+  std::atomic<int> var{0};
+
+  for (size_t i = 0; i < kNumberOfTasks; ++i) {
+    this->ScheduleTask(
+        WTF::CrossThreadBindOnce(callback, WTF::CrossThreadUnretained(&var)));
+    EXPECT_GE(i + 1, this->NumberOfRegisteredTasks());
+  }
+
+  this->RunTaskRunner();
+  // Tasks will remove themselves after running
+  EXPECT_LE(0u, this->NumberOfRegisteredTasks());
+
+  const size_t cancelled = this->CancelAndWait();
+  EXPECT_EQ(0u, this->NumberOfRegisteredTasks());
+  EXPECT_EQ(kNumberOfTasks, var + cancelled);
+}
+
+TEST(CancelableTaskSchedulerTest, RemoveTasksFromQueue) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  CancelableTaskScheduler scheduler{task_runner};
+  int var = 0;
+  scheduler.ScheduleTask(WTF::CrossThreadBindOnce(
+      [](int* var) { ++(*var); }, WTF::CrossThreadUnretained(&var)));
+  auto tasks = task_runner->TakePendingTasksForTesting();
+  // Clearing the task queue should destroy all cancelable closures, which in
+  // turn will notify CancelableTaskScheduler to remove corresponding tasks.
+  tasks.clear();
+  EXPECT_EQ(0, var);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index b7009907..5cd19d5 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -263,11 +263,11 @@
   return value;
 }
 
+// This function corresponds with step 2 substep 7 of
+// https://fetch.spec.whatwg.org/#main-fetch.
 void SetReferrer(
     ResourceRequest& request,
     const FetchClientSettingsObject& fetch_client_settings_object) {
-  // TODO(domfarolino): we can probably *just set* the HTTP `Referer` here
-  // no matter what now.
   if (!request.DidSetHttpReferrer()) {
     String referrer_to_use = request.ReferrerString();
     network::mojom::ReferrerPolicy referrer_policy_to_use =
@@ -276,20 +276,38 @@
     if (referrer_to_use == Referrer::ClientReferrerString())
       referrer_to_use = fetch_client_settings_object.GetOutgoingReferrer();
 
-    if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault) {
+    if (referrer_policy_to_use == network::mojom::ReferrerPolicy::kDefault)
       referrer_policy_to_use = fetch_client_settings_object.GetReferrerPolicy();
-    }
 
+    request.SetReferrerString(
+        referrer_to_use,
+        ResourceRequest::SetReferrerStringLocation::kResourceFetcher);
+    request.SetReferrerPolicy(
+        referrer_policy_to_use,
+        ResourceRequest::SetReferrerPolicyLocation::kResourceFetcher);
     // TODO(domfarolino): Stop storing ResourceRequest's referrer as a header
     // and store it elsewhere. See https://crbug.com/850813.
-    request.SetHttpReferrer(SecurityPolicy::GenerateReferrer(
-        referrer_policy_to_use, request.Url(), referrer_to_use));
+    request.SetHttpReferrer(
+        SecurityPolicy::GenerateReferrer(referrer_policy_to_use, request.Url(),
+                                         referrer_to_use),
+        ResourceRequest::SetHttpReferrerLocation::kResourceFetcher);
   } else {
-    CHECK_EQ(
-        SecurityPolicy::GenerateReferrer(request.GetReferrerPolicy(),
-                                         request.Url(), request.HttpReferrer())
-            .referrer,
-        request.HttpReferrer());
+    // In the case of stale requests that are being revalidated, these requests
+    // will already have their HttpReferrer set, and we will end up here. We
+    // won't regenerate the referrer, but instead check that it's still correct.
+    Referrer generated_referrer = SecurityPolicy::GenerateReferrer(
+        request.GetReferrerPolicy(), request.Url(), request.ReferrerString());
+    if (generated_referrer.referrer != request.HttpReferrer()) {
+      const auto set_http_referrer_location = request.HttpReferrerLocation();
+      const auto set_referrer_string_location =
+          request.ReferrerStringLocation();
+      const auto set_referrer_policy_location =
+          request.ReferrerPolicyLocation();
+      base::debug::Alias(&set_http_referrer_location);
+      base::debug::Alias(&set_referrer_string_location);
+      base::debug::Alias(&set_referrer_policy_location);
+      CHECK(false);
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
index 187e0e7..a02cfb3a 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.cc
@@ -103,7 +103,8 @@
       new_referrer.IsEmpty() ? Referrer::NoReferrer() : String(new_referrer);
   // TODO(domfarolino): Stop storing ResourceRequest's generated referrer as a
   // header and instead use a separate member. See https://crbug.com/850813.
-  request->SetHttpReferrer(Referrer(referrer, new_referrer_policy));
+  request->SetHttpReferrer(Referrer(referrer, new_referrer_policy),
+                           SetHttpReferrerLocation::kCreateRedirectRequest);
   request->SetSkipServiceWorker(skip_service_worker);
   request->SetRedirectStatus(RedirectStatus::kFollowedRedirect);
 
@@ -218,7 +219,10 @@
   http_header_fields_.Set(name, value);
 }
 
-void ResourceRequest::SetHttpReferrer(const Referrer& referrer) {
+void ResourceRequest::SetHttpReferrer(
+    const Referrer& referrer,
+    SetHttpReferrerLocation set_http_referrer_location) {
+  set_http_referrer_location_ = set_http_referrer_location;
   if (referrer.referrer.IsEmpty())
     http_header_fields_.Remove(http_names::kReferer);
   else
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request.h b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
index 45814eb..80d73c3 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request.h
@@ -67,6 +67,51 @@
 
  public:
   enum class RedirectStatus : uint8_t { kFollowedRedirect, kNoRedirect };
+  enum class SetHttpReferrerLocation : uint8_t {
+    kCreateRedirectRequest,
+    kFrameLoaderResourceRequestForReload,
+    kFrameLoadRequest,
+    kInspectorNetworkAgent,
+    kLocalDomWindow,
+    kResourceFetcher,
+    kWebURLRequest,
+  };
+  enum class SetReferrerStringLocation : uint8_t {
+    kCSSFontFaceSrcValueFetch,
+    kCSSImageSetValueCacheImage,
+    kCSSImageValueCacheImage,
+    kHistoryItem,
+    kModuleScriptLoader,
+    kPerformHTTPFetch,
+    kPingLoader,
+    kPreloadRequestStart,
+    kResourceFetcher,
+    kThreadableLoaderCreateAccessControlPreflightRequest,
+    kThreadableLoaderPrepareCrossOriginRequest,
+    kWebLocalFrameImpl,
+  };
+  enum class SetReferrerPolicyLocation : uint8_t {
+    kAnchorElement,
+    kCSSFontFaceSrcValueFetch,
+    kCSSImageSetValueCacheImage,
+    kCSSImageValueCacheImage,
+    kFrameOwnerLoadOrRedirectSubframe,
+    kHistoryItem,
+    kImageLoader,
+    kLinkImport,
+    kLoadStylesheet,
+    kModuleLoader,
+    kPerformHTTPFetch,
+    kPingLoader,
+    kPrefetchIfNeeded,
+    kPreloadIfNeeded,
+    kPreloadRequestStart,
+    kResourceFetcher,
+    kSFOCreateFetchParameters,
+    kThreadableLoaderCreateAccessControlPreflightRequest,
+    kThreadableLoaderPrepareCrossOriginRequest,
+    kWebLocalFrameImpl,
+  };
 
   ResourceRequest();
   explicit ResourceRequest(const String& url_string);
@@ -151,21 +196,36 @@
   const AtomicString& HttpReferrer() const {
     return HttpHeaderField(http_names::kReferer);
   }
-  void SetHttpReferrer(const Referrer&);
+  SetHttpReferrerLocation HttpReferrerLocation() const {
+    return set_http_referrer_location_;
+  }
+  void SetHttpReferrer(const Referrer&, SetHttpReferrerLocation);
   bool DidSetHttpReferrer() const { return did_set_http_referrer_; }
   void ClearHTTPReferrer();
 
-  void SetReferrerPolicy(network::mojom::ReferrerPolicy referrer_policy) {
+  void SetReferrerPolicy(
+      network::mojom::ReferrerPolicy referrer_policy,
+      SetReferrerPolicyLocation set_referrer_policy_location) {
     referrer_policy_ = referrer_policy;
+    set_referrer_policy_location_ = set_referrer_policy_location;
   }
   network::mojom::ReferrerPolicy GetReferrerPolicy() const {
     return referrer_policy_;
   }
+  SetReferrerPolicyLocation ReferrerPolicyLocation() const {
+    return set_referrer_policy_location_;
+  }
 
-  void SetReferrerString(const String& referrer_string) {
+  void SetReferrerString(
+      const String& referrer_string,
+      SetReferrerStringLocation set_referrer_string_location) {
     referrer_string_ = referrer_string;
+    set_referrer_string_location_ = set_referrer_string_location;
   }
   const String& ReferrerString() const { return referrer_string_; }
+  SetReferrerStringLocation ReferrerStringLocation() const {
+    return set_referrer_string_location_;
+  }
 
   const AtomicString& HttpOrigin() const {
     return HttpHeaderField(http_names::kOrigin);
@@ -497,6 +557,11 @@
   bool is_external_request_;
   network::mojom::CorsPreflightPolicy cors_preflight_policy_;
   RedirectStatus redirect_status_;
+  // TODO(domfarolino): Remove these after crash debugging is complete.
+  SetHttpReferrerLocation set_http_referrer_location_;
+  SetReferrerStringLocation set_referrer_string_location_;
+  SetReferrerPolicyLocation set_referrer_policy_location_;
+
   base::Optional<String> suggested_filename_;
 
   mutable CacheControlHeader cache_control_header_cache_;
diff --git a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc
index f5aebce..ae486e1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/script_fetch_options.cc
@@ -55,7 +55,9 @@
   params.MutableResourceRequest().SetFetchImportanceMode(importance_);
 
   // its referrer policy to options's referrer policy. [spec text]
-  params.MutableResourceRequest().SetReferrerPolicy(referrer_policy_);
+  params.MutableResourceRequest().SetReferrerPolicy(
+      referrer_policy_,
+      ResourceRequest::SetReferrerPolicyLocation::kSFOCreateFetchParameters);
 
   params.SetCharset(encoding);
 
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.cc b/third_party/blink/renderer/platform/weborigin/security_policy.cc
index f8122bf..28062e71 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -68,6 +68,20 @@
   return safelist;
 }
 
+network::mojom::ReferrerPolicy ReferrerPolicyResolveDefault(
+    network::mojom::ReferrerPolicy referrer_policy) {
+  if (referrer_policy == network::mojom::ReferrerPolicy::kDefault) {
+    if (RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()) {
+      return network::mojom::ReferrerPolicy::
+          kNoReferrerWhenDowngradeOriginWhenCrossOrigin;
+    } else {
+      return network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade;
+    }
+  }
+
+  return referrer_policy;
+}
+
 void SecurityPolicy::Init() {
   TrustworthyOriginSafelist();
 }
@@ -93,16 +107,8 @@
     network::mojom::ReferrerPolicy referrer_policy,
     const KURL& url,
     const String& referrer) {
-  network::mojom::ReferrerPolicy referrer_policy_no_default = referrer_policy;
-  if (referrer_policy_no_default == network::mojom::ReferrerPolicy::kDefault) {
-    if (RuntimeEnabledFeatures::ReducedReferrerGranularityEnabled()) {
-      referrer_policy_no_default = network::mojom::ReferrerPolicy::
-          kNoReferrerWhenDowngradeOriginWhenCrossOrigin;
-    } else {
-      referrer_policy_no_default =
-          network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade;
-    }
-  }
+  network::mojom::ReferrerPolicy referrer_policy_no_default =
+      ReferrerPolicyResolveDefault(referrer_policy);
   if (referrer == Referrer::NoReferrer())
     return Referrer(Referrer::NoReferrer(), referrer_policy_no_default);
   DCHECK(!referrer.IsEmpty());
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.h b/third_party/blink/renderer/platform/weborigin/security_policy.h
index d041d1d9..cb4e4bd 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.h
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.h
@@ -46,6 +46,14 @@
   kDoNotSupportReferrerPolicyLegacyKeywords,
 };
 
+// The ReferrerPolicy enum contains a member kDefault, which not a real referrer
+// policy, but instead a value Blink uses to fallback to
+// kNoReferrerWhenDowngrade at certain times. The function below is provided so
+// that a referrer policy which may be kDefault can be resolved to a valid
+// value.
+PLATFORM_EXPORT network::mojom::ReferrerPolicy ReferrerPolicyResolveDefault(
+    network::mojom::ReferrerPolicy);
+
 class PLATFORM_EXPORT SecurityPolicy {
   STATIC_ONLY(SecurityPolicy);
 
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
index b042f560..0ab2b6b 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/disable-blink-features=LayoutNG
@@ -285,3 +285,5 @@
 
 ### virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/
 crbug.com/591099 virtual/omt-worker-fetch/external/wpt/service-workers/service-worker/registration-updateviacache.https.html [ Failure ]
+
+crbug.com/591099 external/wpt/css/css-tables/absolute-tables-002.html [ Failure ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index 42431dc..b3187e0 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2253,6 +2253,7 @@
 # We don't plan to support this use-case in the near future.
 crbug.com/940797 external/wpt/signed-exchange/appcache/sxg-served-from-appcache.tentative.https.html [ WontFix ]
 crbug.com/940797 virtual/sxg-with-network-service/external/wpt/signed-exchange/appcache/sxg-served-from-appcache.tentative.https.html [ WontFix ]
+crbug.com/940797 virtual/sxg-subresource/external/wpt/signed-exchange/appcache/sxg-served-from-appcache.tentative.https.html [ WontFix ]
 
 external/wpt/css/css-overscroll-behavior/overscrollBehavior-manual.html [ WontFix ]
 external/wpt/html/semantics/forms/the-input-element/event-select-manual.html [ WontFix ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index fada6727..b14a2ae 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -551,6 +551,11 @@
     "args": ["--enable-features=NetworkService"]
   },
   {
+    "prefix": "sxg-subresource",
+    "base": "external/wpt/signed-exchange",
+    "args": ["--enable-features=SignedExchangeSubresourcePrefetch"]
+  },
+  {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/content-security-policy/inside-worker",
     "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,PlzDedicatedWorker,OffMainThreadServiceWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch"]
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 3a9fb448..048dc368 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
@@ -113645,6 +113645,18 @@
      {}
     ]
    ],
+   "html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes.html": [
+    [
+     "html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes.html",
+     [
+      [
+       "/html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/the-iframe-element/iframe-with-base.html": [
     [
      "html/semantics/embedded-content/the-iframe-element/iframe-with-base.html",
@@ -140099,6 +140111,12 @@
    "css/css-tables/OWNERS": [
     []
    ],
+   "css/css-tables/absolute-tables-003-expected.txt": [
+    []
+   ],
+   "css/css-tables/absolute-tables-005-expected.txt": [
+    []
+   ],
    "css/css-tables/anonymous-table-ws-001-ref.html": [
     []
    ],
@@ -142283,9 +142301,6 @@
    "css/css-transforms/matrix/reference/svg-matrix-four-color-ref.html": [
     []
    ],
-   "css/css-transforms/parsing/scale-parsing-valid-expected.txt": [
-    []
-   ],
    "css/css-transforms/parsing/transform-box-valid-expected.txt": [
     []
    ],
@@ -157823,6 +157838,9 @@
    "html/semantics/embedded-content/the-iframe-element/iframe-load-event-expected.txt": [
     []
    ],
+   "html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes-ref.html": [
+    []
+   ],
    "html/semantics/embedded-content/the-iframe-element/iframe-with-base-ref.html": [
     []
    ],
@@ -157889,6 +157907,9 @@
    "html/semantics/embedded-content/the-iframe-element/support/iframe-trying-to-navigate-itself.html": [
     []
    ],
+   "html/semantics/embedded-content/the-iframe-element/support/iframe-which-content-height-equals-400px.html": [
+    []
+   ],
    "html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_001.htm": [
     []
    ],
@@ -165236,6 +165257,9 @@
    "resources/webidl2/lib/webidl2.js": [
     []
    ],
+   "resources/webidl2/lib/webidl2.js.headers": [
+    []
+   ],
    "screen-capture/META.yml": [
     []
    ],
@@ -198412,6 +198436,18 @@
      {}
     ]
    ],
+   "css/css-animations/responsive/column-rule-color-001.html": [
+    [
+     "css/css-animations/responsive/column-rule-color-001.html",
+     {}
+    ]
+   ],
+   "css/css-animations/responsive/column-width-001.html": [
+    [
+     "css/css-animations/responsive/column-width-001.html",
+     {}
+    ]
+   ],
    "css/css-animations/style-animation-parsing.html": [
     [
      "css/css-animations/style-animation-parsing.html",
@@ -205870,6 +205906,36 @@
      {}
     ]
    ],
+   "css/css-tables/absolute-tables-001.html": [
+    [
+     "css/css-tables/absolute-tables-001.html",
+     {}
+    ]
+   ],
+   "css/css-tables/absolute-tables-002.html": [
+    [
+     "css/css-tables/absolute-tables-002.html",
+     {}
+    ]
+   ],
+   "css/css-tables/absolute-tables-003.html": [
+    [
+     "css/css-tables/absolute-tables-003.html",
+     {}
+    ]
+   ],
+   "css/css-tables/absolute-tables-004.html": [
+    [
+     "css/css-tables/absolute-tables-004.html",
+     {}
+    ]
+   ],
+   "css/css-tables/absolute-tables-005.html": [
+    [
+     "css/css-tables/absolute-tables-005.html",
+     {}
+    ]
+   ],
    "css/css-tables/border-spacing-included-in-sizes-001.html": [
     [
      "css/css-tables/border-spacing-included-in-sizes-001.html",
@@ -217749,6 +217815,12 @@
      {}
     ]
    ],
+   "element-timing/retrievability.html": [
+    [
+     "element-timing/retrievability.html",
+     {}
+    ]
+   ],
    "element-timing/supported-element-type.html": [
     [
      "element-timing/supported-element-type.html",
@@ -340185,6 +340257,14 @@
    "fb74d7fa7d062d60153d47913df9eb2b0c7267c8",
    "testharness"
   ],
+  "css/css-animations/responsive/column-rule-color-001.html": [
+   "9953db43545990d9f6e0941ee89f10dc83e46438",
+   "testharness"
+  ],
+  "css/css-animations/responsive/column-width-001.html": [
+   "7697eec324064dd6bd1fd75f16fdff5d3050315d",
+   "testharness"
+  ],
   "css/css-animations/style-animation-parsing.html": [
    "792fda33ed8b0bacf355e62066ec05d54cc5c729",
    "testharness"
@@ -370769,6 +370849,34 @@
    "1cc9d5b6637083e2dcb65280083edb82e4aea251",
    "support"
   ],
+  "css/css-tables/absolute-tables-001.html": [
+   "dd401c237907bb9166df85e290bd3d7980644a93",
+   "testharness"
+  ],
+  "css/css-tables/absolute-tables-002.html": [
+   "a72e2f170e36d5b9dcf37b631e0357f82b13e333",
+   "testharness"
+  ],
+  "css/css-tables/absolute-tables-003-expected.txt": [
+   "f1b3b491d2f32de0d7ef044ed951266ef87c563c",
+   "support"
+  ],
+  "css/css-tables/absolute-tables-003.html": [
+   "352d8cd6270c3c731aad09a3e8a62b5583a23b9c",
+   "testharness"
+  ],
+  "css/css-tables/absolute-tables-004.html": [
+   "a74d7dfbd02f2675ede3754e90f912399df2a2f7",
+   "testharness"
+  ],
+  "css/css-tables/absolute-tables-005-expected.txt": [
+   "9ceaa9a0684c2c12d7ca931785b1e82b850c9da4",
+   "support"
+  ],
+  "css/css-tables/absolute-tables-005.html": [
+   "57c3f0dea3a56f0281a44e6be019c3ecc9866937",
+   "testharness"
+  ],
   "css/css-tables/anonymous-table-cell-margin-collapsing.html": [
    "23b467ebacc983122a632f4d5971c8daca569420",
    "reftest"
@@ -379745,12 +379853,8 @@
    "1072d1d6b1ecc46142ffbdd2ad62b87079651672",
    "testharness"
   ],
-  "css/css-transforms/parsing/scale-parsing-valid-expected.txt": [
-   "acffcc24da2b3553daf280ae7070603d22d4c0f0",
-   "support"
-  ],
   "css/css-transforms/parsing/scale-parsing-valid.html": [
-   "b9afbc848a3b5ff3033d070b4f080245e0d822c5",
+   "2ae12222f1361b6b0016ce235fad92d03b3bff45",
    "testharness"
   ],
   "css/css-transforms/parsing/transform-box-invalid.html": [
@@ -410173,6 +410277,10 @@
    "4d51ac4b4633f0a24155f331ee63035e62b6dbe1",
    "support"
   ],
+  "element-timing/retrievability.html": [
+   "3ac04d8278512ae9b35913c215ffd4ace97a9f8b",
+   "testharness"
+  ],
   "element-timing/supported-element-type.html": [
    "15733d46f95c9079acfec06cdd25f70ca0e056a6",
    "testharness"
@@ -412442,7 +412550,7 @@
    "support"
   ],
   "event-timing/retrievability.html": [
-   "3a762e7aa79f2a1d95ad9f916d67e25f94378101",
+   "cdb47c6a50480087b92a35ce86c621e28f23d8a7",
    "testharness"
   ],
   "event-timing/retrieve-firstInput.html": [
@@ -429085,6 +429193,14 @@
    "d245bf0b96451b3419e0e69bda86fe4859c9cbfe",
    "testharness"
   ],
+  "html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes-ref.html": [
+   "3758ea2cab4096beccb3722a1cc534b71c662a64",
+   "support"
+  ],
+  "html/semantics/embedded-content/the-iframe-element/iframe-modify-scrolling-attr-to-yes.html": [
+   "9d85aa543d2d1732b8534fcdfa62a42d11716586",
+   "reftest"
+  ],
   "html/semantics/embedded-content/the-iframe-element/iframe-nosrc.html": [
    "57189a0b884d3a55e4bb2ba1a1d3aa83066c0eb3",
    "testharness"
@@ -429445,6 +429561,10 @@
    "6755d295aa179992b4cf958ee2ad8456e729677c",
    "support"
   ],
+  "html/semantics/embedded-content/the-iframe-element/support/iframe-which-content-height-equals-400px.html": [
+   "bb3dbd31182e91f43c3caa0cb8bb7ffbad485177",
+   "support"
+  ],
   "html/semantics/embedded-content/the-iframe-element/support/iframe_sandbox_001.htm": [
    "051ca5ecd78a17f499d3df76b0bd8f1b6a9dbade",
    "support"
@@ -438706,7 +438826,7 @@
    "testharness"
   ],
   "layout-instability/buffer-layout-shift.html": [
-   "c55f1e88d4cd7084322813102bfe82dc1d14c7a7",
+   "216cafd45de0785b27a0ca7fa009352de7887e23",
    "testharness"
   ],
   "layout-instability/observe-layout-shift.html": [
@@ -462837,6 +462957,10 @@
    "d909c30ff07eda519f969a22a0bae6836550c2d8",
    "support"
   ],
+  "resources/webidl2/lib/webidl2.js.headers": [
+   "6805c323df5a975231648b830e33ce183c3cbbd3",
+   "support"
+  ],
   "screen-capture/META.yml": [
    "47882d3275f1cc928555045b2def8fc90f6bcdb0",
    "support"
@@ -477930,7 +478054,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property-expected.txt": [
-   "0bebfffddc249cb8e7b275b6af1273693e62ac7d",
+   "9e2090927c86d149fbfa25aa4ed30a0ac14d125a",
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property.html": [
@@ -477938,7 +478062,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/addition-per-property-expected.txt": [
-   "4f01f2596e52492b186ab100033c2a7839afc23b",
+   "260c457085f23f4a14491349dbddb58d8878a8b3",
    "support"
   ],
   "web-animations/animation-model/animation-types/addition-per-property.html": [
@@ -477950,7 +478074,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property-expected.txt": [
-   "286b089e58ddfadc3b66379ec1b059f2ac05aee5",
+   "c611fb484228cdc280bc1e24e5b47aa2b81b54c2",
    "support"
   ],
   "web-animations/animation-model/animation-types/interpolation-per-property.html": [
@@ -479150,7 +479274,7 @@
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/automation-rate-testing.js": [
-   "73892dd845887d731779b3794a14df3f6bd36cba",
+   "43279f91d68d3c5e2d7a086c739c838b31dc7335",
    "support"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/automation-rate.html": [
@@ -479182,7 +479306,7 @@
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator.html": [
-   "1672f0d975f2b2e2fd0c127663b403745b669265",
+   "6803f55eab00bbfb9766cc044a7240e6b4a0fda0",
    "testharness"
   ],
   "webaudio/the-audio-api/the-audioparam-interface/k-rate-panner.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-001.html b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-001.html
new file mode 100644
index 0000000..dd401c2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-001.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='/resources/check-layout-th.js'></script>
+<link rel="author" title="Anders Ruud" href="mailto:andruud@chromium.org">
+<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visudet.html#the-width-property" title="See Note in <percentage>">
+<meta name="flags" content="" />
+<meta name="assert" content="percent lengths of an abspos table are resolved against the padding box of the parent" />
+<style>
+    main div {
+        position: relative;
+        border: 5px solid black;
+        height: 60px;
+        width: 60px;
+        padding: 5px 7px 11px 13px;
+        margin: 10px;
+    }
+
+    .tbl {
+        display: table;
+        background-color: skyblue;
+        position: absolute;
+        width: 50%;
+        height: 50%;
+    }
+
+    .cell {
+        display: table-cell;
+        outline: 1px dashed blue;
+    }
+
+    .topleft { left: 0; top: 0; }
+    .topright { right: 0; top: 0; }
+    .bottomright { right: 0; bottom: 0; }
+    .bottomleft { left: 0; bottom: 0; }
+
+    .vertical  { writing-mode: vertical-lr; }
+
+</style>
+<p>Tests that percent lengths of an absolutely positioned table is resolved
+against the <em>padding box</em> of the parent.
+<hr>
+<output id="log"></output>
+<main>
+<div>
+  <span class="tbl topleft" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=0 data-offset-y=0>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+<div>
+  <span class="tbl topright" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=40 data-offset-y=0>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+<div>
+  <span class="tbl bottomright" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=40 data-offset-y=38>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+<div>
+  <span class="tbl bottomleft" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=0 data-offset-y=38>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+
+<div class="vertical">
+  <span class="tbl topleft" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=0 data-offset-y=0>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+<div class="vertical">
+  <span class="tbl topright" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=40 data-offset-y=0>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+<div class="vertical">
+  <span class="tbl bottomright" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=40 data-offset-y=38>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+<div class="vertical">
+  <span class="tbl bottomleft" data-expected-client-width=40 data-expected-client-height=38 data-offset-x=0 data-offset-y=38>
+    <span class="cell" data-expected-client-width=40 data-expected-client-height=38>abc</span>
+  </span>
+</div>
+</main>
+<script>
+    checkLayout(".tbl");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-002.html b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-002.html
new file mode 100644
index 0000000..a72e2f17
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-002.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='/resources/check-layout-th.js'></script>
+<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#used-width-of-table">
+<meta name="flags" content="" />
+<meta name="assert" content="When sizing and positioning abspos tables, the intrinsic width is obeyed when the intrinsic width is larger than specified width" />
+<style>
+.cb {
+  position: relative;
+  height: 200px;
+  width: 200px;
+  outline: 2px dashed lightblue;
+}
+
+.table {
+  background-color: orange;
+  position: absolute;
+  width: 50px;
+  right: 0px;
+}
+
+.cell {
+  border: 1px solid green;
+  width: 100px;
+}
+
+.cell > div {
+  width: 200px;
+}
+
+.vertical { writing-mode: vertical-lr; }
+.horizontal { writing-mode: horizontal-tb; }
+</style>
+
+<output id="log"></output>
+
+<main>
+<div class="cb">
+  <table class="table" data-expected-width=208 data-offset-x="-8">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table horizontal" data-expected-width=208 data-offset-x="-8">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb">
+  <table class="table vertical" data-expected-width=208 data-offset-x="-8">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table vertical" data-expected-width=208 data-offset-x="-8">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</main>
+<script>
+    checkLayout(".table");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-003-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-003-expected.txt
new file mode 100644
index 0000000..f1b3b49
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-003-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS .table 1
+PASS .table 2
+FAIL .table 3 assert_equals: 
+<table class="table vertical" data-expected-width="50" data-offset-x="150">
+    <tbody><tr>
+      <td class="cell" data-expected-width="46">
+        <div></div>
+      </td>
+    </tr>
+  </tbody></table>
+width expected 50 but got 108
+FAIL .table 4 assert_equals: 
+<table class="table vertical" data-expected-width="50" data-offset-x="150">
+    <tbody><tr>
+      <td class="cell" data-expected-width="46">
+        <div></div>
+      </td>
+    </tr>
+  </tbody></table>
+width expected 50 but got 108
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-003.html b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-003.html
new file mode 100644
index 0000000..352d8cd62
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-003.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='/resources/check-layout-th.js'></script>
+<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#used-width-of-table">
+<meta name="flags" content="" />
+<meta name="assert" content="When sizing and positioning abspos tables, the specified width is obeyed when the intrinsic width is smaller" />
+<style>
+.cb {
+  position: relative;
+  height: 200px;
+  width: 200px;
+  outline: 2px dashed lightblue;
+}
+
+.table {
+  background-color: orange;
+  position: absolute;
+  width: 50px;
+  right: 0px;
+}
+
+.cell {
+  border: 1px solid green;
+  width: 100px;
+}
+
+.cell > div {
+  width: 20px;
+}
+
+.vertical { writing-mode: vertical-lr; }
+.horizontal { writing-mode: horizontal-tb; }
+</style>
+
+<output id="log"></output>
+
+<main>
+<div class="cb">
+  <table class="table" data-expected-width=50 data-offset-x="150">
+    <tr>
+      <td class="cell" data-expected-width=46>
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table horizontal" data-expected-width=50 data-offset-x="150">
+    <tr>
+      <td class="cell" data-expected-width=46>
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb">
+  <table class="table vertical" data-expected-width=50 data-offset-x="150">
+    <tr>
+      <td class="cell" data-expected-width=46>
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table vertical" data-expected-width=50 data-offset-x="150">
+    <tr>
+      <td class="cell" data-expected-width=46>
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</main>
+<script>
+    checkLayout(".table");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-004.html b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-004.html
new file mode 100644
index 0000000..a74d7df
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-004.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='/resources/check-layout-th.js'></script>
+<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#computing-the-table-height">
+<meta name="flags" content="" />
+<meta name="assert" content="When sizing and positioning abspos tables, the intrinsic height is obeyed when the intrinsic height is larger than specified height" />
+<style>
+.cb {
+  position: relative;
+  height: 200px;
+  width: 200px;
+  outline: 2px dashed lightblue;
+}
+
+.table {
+  background-color: orange;
+  position: absolute;
+  height: 50px;
+  right: 0px;
+}
+
+.cell {
+  border: 1px solid green;
+  height: 100px;
+}
+
+.cell > div {
+  height: 200px;
+}
+
+.vertical { writing-mode: vertical-lr; }
+.horizontal { writing-mode: horizontal-tb; }
+</style>
+
+<output id="log"></output>
+
+<main>
+<div class="cb">
+  <table class="table" data-expected-width=8 data-expected-height=208 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table horizontal" data-expected-width=8 data-expected-height=208 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb">
+  <table class="table vertical" data-expected-width=8 data-expected-height=208 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table vertical" data-expected-width=8 data-expected-height=208 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</main>
+<script>
+    checkLayout(".table");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-005-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-005-expected.txt
new file mode 100644
index 0000000..9ceaa9a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-005-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS .table 1
+PASS .table 2
+FAIL .table 3 assert_equals: 
+<table class="table vertical" data-expected-width="8" data-expected-height="108" data-offset-x="192">
+    <tbody><tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </tbody></table>
+height expected 108 but got 50
+FAIL .table 4 assert_equals: 
+<table class="table vertical" data-expected-width="8" data-expected-height="108" data-offset-x="192">
+    <tbody><tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </tbody></table>
+height expected 108 but got 50
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-005.html b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-005.html
new file mode 100644
index 0000000..57c3f0de
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/absolute-tables-005.html
@@ -0,0 +1,79 @@
+<!doctype html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='/resources/check-layout-th.js'></script>
+<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#computing-the-table-height">
+<meta name="flags" content="" />
+<meta name="assert" content="When sizing and positioning abspos tables, the specified height is obeyed when the intrinsic height is smaller" />
+<style>
+.cb {
+  position: relative;
+  height: 200px;
+  width: 200px;
+  outline: 2px dashed lightblue;
+}
+
+.table {
+  background-color: orange;
+  position: absolute;
+  height: 50px;
+  right: 0px;
+}
+
+.cell {
+  border: 1px solid green;
+  height: 100px;
+}
+
+.cell > div {
+  height: 20px;
+}
+
+.vertical { writing-mode: vertical-lr; }
+.horizontal { writing-mode: horizontal-tb; }
+</style>
+
+<output id="log"></output>
+
+<main>
+<div class="cb">
+  <table class="table" data-expected-width=8 data-expected-height=108 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table horizontal" data-expected-width=8 data-expected-height=108 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb">
+  <table class="table vertical" data-expected-width=8 data-expected-height=108 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+<div class="cb vertical">
+  <table class="table vertical" data-expected-width=8 data-expected-height=108 data-offset-x="192">
+    <tr>
+      <td class="cell">
+        <div></div>
+      </td>
+    </tr>
+  </table>
+</div>
+</main>
+<script>
+    checkLayout(".table");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/resources/webidl2/lib/webidl2.js.headers b/third_party/blink/web_tests/external/wpt/resources/webidl2/lib/webidl2.js.headers
new file mode 100644
index 0000000..6805c323
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resources/webidl2/lib/webidl2.js.headers
@@ -0,0 +1 @@
+Content-Type: text/javascript; charset=utf-8
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
index 16750f2..81a5e04 100755
--- a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/generate-test-sxgs.sh
@@ -15,7 +15,7 @@
 
 set -e
 
-for cmd in gen-signedexchange gen-certurl; do
+for cmd in gen-signedexchange gen-certurl dump-signedexchange; do
     if ! command -v $cmd > /dev/null 2>&1; then
         echo "$cmd is not installed. Please run:"
         echo "  go get -u github.com/WICG/webpackage/go/signedexchange/cmd/..."
@@ -540,4 +540,40 @@
   -o sxg/sxg-data-cert-url.sxg \
   -miRecordSize 100
 
+# Generate the signed exchange file of sxg-subresource-script-inner.js.
+gen-signedexchange \
+  -version $sxg_version \
+  -uri $inner_url_origin/signed-exchange/resources/sxg-subresource-script.js \
+  -status 200 \
+  -content sxg-subresource-script-inner.js \
+  -certificate $certfile \
+  -certUrl $cert_url_origin/signed-exchange/resources/$certfile.cbor \
+  -validityUrl $inner_url_origin/signed-exchange/resources/resource.validity.msg \
+  -privateKey $keyfile \
+  -date 2030-04-01T00:00:00Z \
+  -expire 168h \
+  -o sxg/sxg-subresource-script.sxg \
+  -miRecordSize 100
+
+# Get the header integrity hash value of sxg-subresource-script.sxg.
+header_integrity=$(dump-signedexchange -i sxg/sxg-subresource-script.sxg | \
+                   grep -o "header integrity: sha256-.*" | \
+                   grep -o "sha256-.*$")
+
+# Generate the signed exchange file of signed exchange subresource test.
+gen-signedexchange \
+  -version $sxg_version \
+  -uri $inner_url_origin/signed-exchange/resources/sxg-subresource-sxg.html \
+  -status 200 \
+  -content sxg-subresource-sxg-inner.html \
+  -certificate $certfile \
+  -certUrl $cert_url_origin/signed-exchange/resources/$certfile.cbor \
+  -validityUrl $inner_url_origin/signed-exchange/resources/resource.validity.msg \
+  -privateKey $keyfile \
+  -date 2030-04-01T00:00:00Z \
+  -expire 168h \
+  -o sxg/sxg-subresource.sxg \
+  -miRecordSize 100 \
+  -responseHeader "link:<$inner_url_origin/signed-exchange/resources/sxg-subresource-script.js>;rel=allowed-alt-sxg;header-integrity=\"$header_integrity\",<$inner_url_origin/signed-exchange/resources/sxg-subresource-script.js>;rel=preload;as=script"
+
 rm -fr $tmpdir
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-iframe.html b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-iframe.html
new file mode 100644
index 0000000..bd81285
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-iframe.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<body>
+<script>
+(async () => {
+  const sxg_path = 'sxg/sxg-subresource.sxg';
+  const scipt_sxg_path = 'sxg/sxg-subresource-script.sxg';
+  const scipt_path = 'sxg-subresource-script.js';
+  const wait_for_prefetch = new Promise((resolve) => {
+    new PerformanceObserver((list) => {
+      for (let e of list.getEntries()) {
+        if (e.name.endsWith(scipt_sxg_path)) {
+          resolve();
+        } else if (e.name.endsWith(scipt_path)) {
+          window.parent.postMessage(
+            scipt_path + ' should not be prefetched', '*');
+        }
+      }
+    }).observe({ entryTypes: ['resource'] });
+  });
+
+  const link = document.createElement('link');
+  link.rel = 'prefetch';
+  link.href = sxg_path;
+  document.body.appendChild(link);
+  await wait_for_prefetch;
+  location.href = sxg_path;
+})()
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-script-inner.js b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-script-inner.js
new file mode 100644
index 0000000..dcc7a356
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-script-inner.js
@@ -0,0 +1,4 @@
+// Usually the alternate resource should have the same content as the original
+// one (sxg-subresource-script.js), but for now we use differentiated content
+// for easy testing.
+window.parent.postMessage('from signed exchange', '*');
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-script.js b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-script.js
new file mode 100644
index 0000000..c730568f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-script.js
@@ -0,0 +1 @@
+window.parent.postMessage('from server', '*');
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-sxg-inner.html b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-sxg-inner.html
new file mode 100644
index 0000000..9dfff56
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg-subresource-sxg-inner.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<script src="sxg-subresource-script.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource-script.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource-script.sxg
new file mode 100644
index 0000000..6a6f37e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource-script.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource.sxg b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource.sxg
new file mode 100644
index 0000000..05be42a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource.sxg.sub.headers b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource.sxg.sub.headers
new file mode 100644
index 0000000..8bc3938d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/resources/sxg/sxg-subresource.sxg.sub.headers
@@ -0,0 +1 @@
+Link: <https://{{host}}:{{ports[https][0]}}/signed-exchange/resources/sxg/sxg-subresource-script.sxg>;rel=alternate;type="application/signed-exchange;v=b3";anchor="https://127.0.0.1:8444/signed-exchange/resources/sxg-subresource-script.js";
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/subresource/sxg-subresource.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/signed-exchange/subresource/sxg-subresource.tentative-expected.txt
new file mode 100644
index 0000000..ee54a8c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/subresource/sxg-subresource.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Subresource signed exchange prefetch. assert_equals: expected "from signed exchange" but got "sxg-subresource-script.js should not be prefetched"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/signed-exchange/subresource/sxg-subresource.tentative.html b/third_party/blink/web_tests/external/wpt/signed-exchange/subresource/sxg-subresource.tentative.html
new file mode 100644
index 0000000..8ca12b4c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/signed-exchange/subresource/sxg-subresource.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Subresource signed exchange prefetch.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="../resources/sxg-util.js"></script>
+<body>
+<script>
+promise_test(async (t) => {
+  const iframe_path =  get_host_info().HTTPS_ORIGIN + '/signed-exchange/resources/sxg-subresource-iframe.html';
+  const wait_message = (new Promise((resolve) => {
+    const on_message = (event) => {
+      window.removeEventListener('message', on_message);
+      resolve(event.data);
+    };
+    window.addEventListener('message', on_message);
+  }));
+  withIframe(iframe_path);
+  const message = await wait_message;
+  assert_equals(message, 'from signed exchange');
+}, 'Subresource signed exchange prefetch.');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/automation-rate-testing.js b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/automation-rate-testing.js
index 73892dd..43279f91 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/automation-rate-testing.js
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/automation-rate-testing.js
@@ -45,7 +45,7 @@
 // The promise from |startRendering| is returned.
 function doTest(context, should, options) {
   let merger = new ChannelMergerNode(
-      context, {numberOfInputs: context.destination.numberOfChannels});
+      context, {numberOfInputs: context.destination.channelCount});
   merger.connect(context.destination);
 
   let src = null;
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator.html
index 1672f0d..6803f55 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/k-rate-oscillator.html
@@ -32,7 +32,7 @@
               });
 
               let merger = new ChannelMergerNode(
-                  context, {numberOfInputs: context.numberOfChannels});
+                  context, {numberOfInputs: context.destination.channelCount});
               merger.connect(context.destination);
               let inverter = new GainNode(context, {gain: -1});
               inverter.connect(merger, 0, 2);
diff --git a/third_party/blink/web_tests/fast/table/absolute-table-percent-lengths.html b/third_party/blink/web_tests/fast/table/absolute-table-percent-lengths.html
deleted file mode 100644
index 3b54201..0000000
--- a/third_party/blink/web_tests/fast/table/absolute-table-percent-lengths.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!doctype html>
-<style>
-    div {
-        position: relative;
-        border: 5px solid black;
-        height: 60px;
-        width: 60px;
-        padding: 20px;
-        margin: 10px;
-    }
-
-    .tbl {
-        display: table;
-        background-color: skyblue;
-        position: absolute;
-        width: 50%;
-        height: 50%;
-    }
-
-    .cell { display: table-cell; }
-
-    .topleft { left: 0; top: 0; }
-    .topright { right: 0; top: 0; }
-    .bottomright { right: 0; bottom: 0; }
-    .bottomleft { left: 0; bottom: 0; }
-
-    .vertical  { -webkit-writing-mode: vertical-lr; }
-
-</style>
-<script src="../../resources/check-layout.js"></script>
-<p>Tests that percent lengths of an absolutely positioned table is resolved
-against the <em>padding box</em> of the parent.
-<hr>
-<output id="output"></output>
-<hr>
-<div><span class="tbl topleft" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-<div><span class="tbl topright" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-<div><span class="tbl bottomright" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-<div><span class="tbl bottomleft" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-
-<div class="vertical"><span class="tbl topleft" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-<div class="vertical"><span class="tbl topright" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-<div class="vertical"><span class="tbl bottomright" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-<div class="vertical"><span class="tbl bottomleft" data-expected-client-width=50 data-expected-client-height=50><span class="cell">abc</span></span></div>
-
-<script>
-    checkLayout(".tbl", output);
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-font-face-expected.html b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-font-face-expected.html
new file mode 100644
index 0000000..5c68ea1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-font-face-expected.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+@font-face {
+  font-family: 'Test Font';
+  src: url(http://127.0.0.1:8000/security/resources/opensans.woff2) format('woff2');
+}
+
+body {
+  font-family: 'Test Font';
+}
+</style>
+<body>
+<p>
+Checks that a font face that was created before a referrer policy was set is
+loaded with the correct referrer, in this case, without a referrer.
+</p>
+<p>
+The test passes, a normal font will be loaded.
+</p>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-font-face.html b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-font-face.html
new file mode 100644
index 0000000..47ef8b1
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-font-face.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+// For documentation on this test, see
+// referrer-policy-conflicting-policies.html.
+document.location = "https://127.0.0.1:8443/security/resources/referrer-policy-conflicting-policies-font-face.html";
+</script>
diff --git a/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-image-set-expected.html b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-image-set-expected.html
new file mode 100644
index 0000000..c42f349
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-image-set-expected.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+    content: -webkit-image-set(url("http://127.0.0.1:8080/security/resources/green250x50.png") 1x);
+}
+</style>
+<body>
+<p>
+Checks that a CSS image that was created from an image-set before a referrer
+policy was set is loaded with the correct referrer, in this case, without a
+referrer.
+</p>
+<p>
+The test passes, if a green rectangle is displayed below.
+</p>
+<div></div>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-image-set.html b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-image-set.html
new file mode 100644
index 0000000..e0a896a
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies-image-set.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+// For documentation on this test, see
+// referrer-policy-conflicting-policies.html.
+document.location = "https://127.0.0.1:8443/security/resources/referrer-policy-conflicting-policies-image-set.html";
+</script>
diff --git a/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies.html b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies.html
index 380d564..25407a3d 100644
--- a/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies.html
+++ b/third_party/blink/web_tests/http/tests/security/referrer-policy-conflicting-policies.html
@@ -2,5 +2,14 @@
 <script>
 if (window.testRunner)
     testRunner.waitUntilDone();
+
+// The following test works by loading a page over HTTPS, which requests a
+// resource over HTTP when the document has the default referrer policy
+// (interpreted as no-referrer-when-downgrade). After the request is
+// prepared/made, the document's referrer policy is set to 'origin'. The test
+// asserts that the referrer associated with the request when the document's
+// referrer policy was no-referrer-when-downgrade is empty, and not the origin.
+// This test is similar to
+// referrer-policy-conflicting-policies-{font-face,image-set}.html
 document.location = "https://127.0.0.1:8443/security/resources/referrer-policy-conflicting-policies.html";
 </script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/montez.woff2 b/third_party/blink/web_tests/http/tests/security/resources/montez.woff2
new file mode 100644
index 0000000..b2f3be6
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/montez.woff2
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/security/resources/normal-font-if-no-referrer.php b/third_party/blink/web_tests/http/tests/security/resources/normal-font-if-no-referrer.php
new file mode 100644
index 0000000..9461235
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/normal-font-if-no-referrer.php
@@ -0,0 +1,10 @@
+<?php
+header("Content-Type: font/woff2");
+header("Access-Control-Allow-Origin: *");
+if ($_SERVER['HTTP_REFERER'] != '') {
+    $font = 'montez.woff2';
+} else {
+    $font = 'opensans.woff2';
+}
+echo file_get_contents($font);
+?>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/opensans.woff2 b/third_party/blink/web_tests/http/tests/security/resources/opensans.woff2
new file mode 100644
index 0000000..e9f58b7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/opensans.woff2
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies-font-face.html b/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies-font-face.html
new file mode 100644
index 0000000..551dcd3
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies-font-face.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script>
+function done()
+{
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+</script>
+<style>
+@font-face {
+  font-family: 'Test Font';
+  src: url("http://127.0.0.1:8080/security/resources/normal-font-if-no-referrer.php") format('woff2');
+}
+
+body {
+  font-family: 'Test Font';
+}
+</style>
+<meta name="referrer" content="origin">
+<body onload="done();">
+<p>
+Checks that a font face that was created before a referrer policy was set is
+loaded with the correct referrer, in this case, without a referrer.
+</p>
+<p>
+The test passes, a normal font will be loaded.
+</p>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies-image-set.html b/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies-image-set.html
new file mode 100644
index 0000000..1750b22
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies-image-set.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script>
+function done()
+{
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+</script>
+<style>
+div {
+    content: -webkit-image-set(url("http://127.0.0.1:8080/security/resources/green-if-no-referrer.php") 1x);
+}
+</style>
+<meta name="referrer" content="origin">
+<body onload="done();">
+<p>
+Checks that a CSS image that was created from an image-set before a referrer
+policy was set is loaded with the correct referrer, in this case, without a
+referrer.
+</p>
+<p>
+The test passes, if a green rectangle is displayed below.
+</p>
+<div></div>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies.html b/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies.html
index 535cd46..3a38380 100644
--- a/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies.html
+++ b/third_party/blink/web_tests/http/tests/security/resources/referrer-policy-conflicting-policies.html
@@ -11,7 +11,7 @@
     content: url("http://127.0.0.1:8080/security/resources/green-if-no-referrer.php");
 }
 </style>
-<meta name="referrer" content="origin" />
+<meta name="referrer" content="origin">
 <body onload="done();">
 <p>
 Checks that an CSS image that was created before a referrer policy was set is
diff --git a/third_party/blink/web_tests/virtual/sxg-subresource/external/wpt/signed-exchange/README.txt b/third_party/blink/web_tests/virtual/sxg-subresource/external/wpt/signed-exchange/README.txt
new file mode 100644
index 0000000..d01256d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/sxg-subresource/external/wpt/signed-exchange/README.txt
@@ -0,0 +1 @@
+This directory is for testing the SignedExchangeSubresourcePrefetch feature.
diff --git a/third_party/blink/web_tests/virtual/sxg-subresource/external/wpt/signed-exchange/subresource/sxg-subresource.tentative-expected.txt b/third_party/blink/web_tests/virtual/sxg-subresource/external/wpt/signed-exchange/subresource/sxg-subresource.tentative-expected.txt
new file mode 100644
index 0000000..943287c
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/sxg-subresource/external/wpt/signed-exchange/subresource/sxg-subresource.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS Subresource signed exchange prefetch.
+Harness: the test ran to completion.
+
diff --git a/tools/grit/grit/format/policy_templates_json_unittest.py b/tools/grit/grit/format/policy_templates_json_unittest.py
index d3816ad30..94b6028 100755
--- a/tools/grit/grit/format/policy_templates_json_unittest.py
+++ b/tools/grit/grit/format/policy_templates_json_unittest.py
@@ -123,7 +123,7 @@
     grd_string_io = StringIO.StringIO(grd_text)
 
     # Parse the grit tree and load the policies' JSON with a gatherer.
-    grd = grd_reader.Parse(grd_string_io, dir=tmp_dir_name)
+    grd = grd_reader.Parse(grd_string_io, dir=tmp_dir_name, defines={'_google_chrome': True})
     grd.SetOutputLanguage('en')
     grd.RunGatherers()
 
diff --git a/tools/grit/grit/gather/policy_json.py b/tools/grit/grit/gather/policy_json.py
index 6e67212..825e713 100644
--- a/tools/grit/grit/gather/policy_json.py
+++ b/tools/grit/grit/gather/policy_json.py
@@ -57,9 +57,15 @@
     if example_text == []:
       # In such cases the original text is okay for an example.
       example_text = text
+
+    replacedText = self.Escape(''.join(text).strip())
+    replacedText = replacedText.replace('$1', self._config['app_name'])
+    replacedText = replacedText.replace('$2', self._config['os_name'])
+    replacedText = replacedText.replace('$3', self._config['frame_name'])
+
     msg.AppendPlaceholder(tclib.Placeholder(
         placeholder.attributes['name'].value,
-        self.Escape(''.join(text).strip()),
+        replacedText,
         ''.join(example_text).strip()))
 
   def _ParseMessage(self, string, desc):
@@ -293,3 +299,21 @@
 
   def Escape(self, text):
     return json.dumps(text, ensure_ascii=False)[1:-1]
+
+  def SetDefines(self, defines):
+    if '_chromium' in defines:
+      self._config = {
+        'build': 'chromium',
+        'app_name': 'Chromium',
+        'frame_name': 'Chromium Frame',
+        'os_name': 'Chromium OS',
+      }
+    elif '_google_chrome' in defines:
+      self._config = {
+        'build': 'chrome',
+        'app_name': 'Google Chrome',
+        'frame_name': 'Google Chrome Frame',
+        'os_name': 'Google Chrome OS',
+      }
+    else:
+      raise Exception('Unknown build')
diff --git a/tools/grit/grit/gather/policy_json_unittest.py b/tools/grit/grit/gather/policy_json_unittest.py
index 187dac1..5fb3b5c 100755
--- a/tools/grit/grit/gather/policy_json_unittest.py
+++ b/tools/grit/grit/gather/policy_json_unittest.py
@@ -272,20 +272,23 @@
         "messages": {}
 }"""
     gatherer = policy_json.PolicyJson(StringIO.StringIO(original))
+    gatherer.SetDefines({'_google_chrome': True})
     gatherer.Parse()
     self.failUnless(len(gatherer.GetCliques()) == 1)
-    expected = json.loads(re.sub('<ph.*ph>', '$1', original))
+    expected = json.loads(re.sub('<ph.*ph>', 'Google Chrome', original))
+    result = json.loads(gatherer.Translate('en'))
     self.failUnless(expected == json.loads(gatherer.Translate('en')))
     self.failUnless(gatherer.GetCliques()[0].translateable)
     msg = gatherer.GetCliques()[0].GetMessage()
     self.failUnless(len(msg.GetPlaceholders()) == 1)
     ph = msg.GetPlaceholders()[0]
-    self.failUnless(ph.GetOriginal() == '$1')
+    self.failUnless(ph.GetOriginal() == 'Google Chrome')
     self.failUnless(ph.GetPresentation() == 'PRODUCT_NAME')
     self.failUnless(ph.GetExample() == 'Google Chrome')
 
   def testGetDescription(self):
     gatherer = policy_json.PolicyJson({})
+    gatherer.SetDefines({'_google_chrome': True})
     self.assertEquals(
         gatherer._GetDescription({'name': 'Policy1'}, 'policy', None, 'desc'),
         'Description of the policy named Policy1')
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 53b461c..698bd69 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -724,6 +724,7 @@
       'gpu-try-linux-nvidia-dbg': 'gpu_tests_debug_trybot',
       'layout_test_leak_detection': 'release_trybot',
       'leak_detection_linux': 'release_trybot',
+      'linux-annotator-rel': 'release_trybot',
       'linux-blink-heap-concurrent-marking-tsan-rel': 'release_trybot_tsan',
       'linux-blink-heap-verification-try': 'release_trybot_enable_blink_heap_verification',
       'linux-coverage-rel': 'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 4e20ad0..1f13161 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -93382,7 +93382,7 @@
 </histogram>
 
 <histogram name="PasswordManager.MediationOptional"
-    enum="CredentialManagerGetResult" expires_after="M77">
+    enum="CredentialManagerGetResult" expires_after="M87">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -93392,7 +93392,7 @@
 </histogram>
 
 <histogram name="PasswordManager.MediationRequired"
-    enum="CredentialManagerGetResult" expires_after="M77">
+    enum="CredentialManagerGetResult" expires_after="M87">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
@@ -93403,7 +93403,7 @@
 </histogram>
 
 <histogram name="PasswordManager.MediationSilent"
-    enum="CredentialManagerGetResult" expires_after="M77">
+    enum="CredentialManagerGetResult" expires_after="M87">
   <owner>jdoerrie@chromium.org</owner>
   <owner>vasilii@chromium.org</owner>
   <summary>
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index abe0282..5949559 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -149,6 +149,8 @@
   // reparented if they are in this list and removed from somewhere else.
   std::set<int> potentially_reparented_ids;
 
+  std::unordered_set<int> node_data_changed_ids;
+
   // Keeps track of new nodes created during this update.
   std::set<const AXNode*> new_nodes;
 
@@ -528,12 +530,23 @@
     changes.push_back(AXTreeObserver::Change(node, change));
   }
 
+  // Update the unignored cached values as necessary.
   for (int parent_id : update_state.invalidate_unignored_cached_values_ids) {
     AXNode* parent = GetFromId(parent_id);
     if (parent)
       parent->UpdateUnignoredCachedValues();
   }
 
+  // Now that the unignored cached values are up to date, update observers to
+  // node changes.
+  for (int node_data_changed_id : update_state.node_data_changed_ids) {
+    AXNode* node = GetFromId(node_data_changed_id);
+    if (node) {
+      for (AXTreeObserver& observer : observers_)
+        observer.OnNodeChanged(this, node);
+    }
+  }
+
   // Tree is no longer updating.
   SetTreeUpdateInProgressState(false);
 
@@ -681,8 +694,7 @@
     node->SetData(src);
   }
 
-  for (AXTreeObserver& observer : observers_)
-    observer.OnNodeChanged(this, node);
+  update_state->node_data_changed_ids.insert(node->id());
 
   // First, delete nodes that used to be children of this node but aren't
   // anymore.
diff --git a/ui/base/ime/chromeos/component_extension_ime_manager.cc b/ui/base/ime/chromeos/component_extension_ime_manager.cc
index 44d53e8..b327e5f3 100644
--- a/ui/base/ime/chromeos/component_extension_ime_manager.cc
+++ b/ui/base/ime/chromeos/component_extension_ime_manager.cc
@@ -42,51 +42,44 @@
 
 } // namespace
 
-ComponentExtensionEngine::ComponentExtensionEngine() {
-}
+ComponentExtensionEngine::ComponentExtensionEngine() = default;
 
 ComponentExtensionEngine::ComponentExtensionEngine(
     const ComponentExtensionEngine& other) = default;
 
-ComponentExtensionEngine::~ComponentExtensionEngine() {
-}
+ComponentExtensionEngine::~ComponentExtensionEngine() = default;
 
-ComponentExtensionIME::ComponentExtensionIME() {
-}
+ComponentExtensionIME::ComponentExtensionIME() = default;
 
 ComponentExtensionIME::ComponentExtensionIME(
     const ComponentExtensionIME& other) = default;
 
-ComponentExtensionIME::~ComponentExtensionIME() {
-}
+ComponentExtensionIME::~ComponentExtensionIME() = default;
 
-ComponentExtensionIMEManagerDelegate::ComponentExtensionIMEManagerDelegate() {
-}
+ComponentExtensionIMEManagerDelegate::ComponentExtensionIMEManagerDelegate() =
+    default;
 
-ComponentExtensionIMEManagerDelegate::~ComponentExtensionIMEManagerDelegate() {
-}
+ComponentExtensionIMEManagerDelegate::~ComponentExtensionIMEManagerDelegate() =
+    default;
 
 ComponentExtensionIMEManager::ComponentExtensionIMEManager() {
-  for (size_t i = 0; i < base::size(input_method::kInputMethods); ++i) {
-    if (input_method::kInputMethods[i].is_login_keyboard)
-      login_layout_set_.insert(input_method::kInputMethods[i].xkb_layout_id);
+  for (const auto& input_method : input_method::kInputMethods) {
+    if (input_method.is_login_keyboard)
+      login_layout_set_.insert(input_method.xkb_layout_id);
   }
 }
 
-ComponentExtensionIMEManager::~ComponentExtensionIMEManager() {
-}
+ComponentExtensionIMEManager::~ComponentExtensionIMEManager() = default;
 
 void ComponentExtensionIMEManager::Initialize(
     std::unique_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
   delegate_ = std::move(delegate);
   std::vector<ComponentExtensionIME> ext_list = delegate_->ListIME();
-  for (size_t i = 0; i < ext_list.size(); ++i) {
-    ComponentExtensionIME& ext = ext_list[i];
+  for (const auto& ext : ext_list) {
     bool extension_exists = IsWhitelistedExtension(ext.id);
     if (!extension_exists)
       component_extension_imes_[ext.id] = ext;
-    for (size_t j = 0; j < ext.engines.size(); ++j) {
-      ComponentExtensionEngine& ime = ext.engines[j];
+    for (const auto& ime : ext.engines) {
       const std::string input_method_id =
           extension_ime_util::GetComponentInputMethodID(ext.id, ime.engine_id);
       if (extension_exists && !IsWhitelisted(input_method_id))
@@ -165,9 +158,9 @@
   input_method::InputMethodDescriptors result;
   const input_method::InputMethodDescriptors& descriptors =
       GetAllIMEAsInputMethodDescriptor();
-  for (size_t i = 0; i < descriptors.size(); ++i) {
-    if (extension_ime_util::IsKeyboardLayoutExtension(descriptors[i].id()))
-      result.push_back(descriptors[i]);
+  for (const auto& descriptor : descriptors) {
+    if (extension_ime_util::IsKeyboardLayoutExtension(descriptor.id()))
+      result.push_back(descriptor);
   }
   return result;
 }
@@ -180,8 +173,7 @@
 
   std::string extension_id =
       extension_ime_util::GetExtensionIDFromInputMethodID(input_method_id);
-  std::map<std::string, ComponentExtensionIME>::iterator it =
-      component_extension_imes_.find(extension_id);
+  auto it = component_extension_imes_.find(extension_id);
   if (it == component_extension_imes_.end())
     return false;
 
@@ -192,8 +184,8 @@
 
 bool ComponentExtensionIMEManager::IsInLoginLayoutWhitelist(
     const std::vector<std::string>& layouts) {
-  for (size_t i = 0; i < layouts.size(); ++i) {
-    if (login_layout_set_.find(layouts[i]) != login_layout_set_.end())
+  for (const auto& layout : layouts) {
+    if (login_layout_set_.find(layout) != login_layout_set_.end())
       return true;
   }
   return false;
diff --git a/ui/base/ime/chromeos/fake_ime_keyboard.cc b/ui/base/ime/chromeos/fake_ime_keyboard.cc
index cc2d29e..fd70580 100644
--- a/ui/base/ime/chromeos/fake_ime_keyboard.cc
+++ b/ui/base/ime/chromeos/fake_ime_keyboard.cc
@@ -12,8 +12,7 @@
       auto_repeat_is_enabled_(false) {
 }
 
-FakeImeKeyboard::~FakeImeKeyboard() {
-}
+FakeImeKeyboard::~FakeImeKeyboard() = default;
 
 bool FakeImeKeyboard::SetCurrentKeyboardLayoutByName(
     const std::string& layout_name) {
diff --git a/ui/base/ime/chromeos/fake_input_method_delegate.cc b/ui/base/ime/chromeos/fake_input_method_delegate.cc
index 12f0566b..4c16afbc5 100644
--- a/ui/base/ime/chromeos/fake_input_method_delegate.cc
+++ b/ui/base/ime/chromeos/fake_input_method_delegate.cc
@@ -11,8 +11,7 @@
     : active_locale_("en") {
 }
 
-FakeInputMethodDelegate::~FakeInputMethodDelegate() {
-}
+FakeInputMethodDelegate::~FakeInputMethodDelegate() = default;
 
 std::string FakeInputMethodDelegate::GetHardwareKeyboardLayouts() const {
   return hardware_keyboard_layout_;
diff --git a/ui/base/ime/chromeos/ime_keyboard.cc b/ui/base/ime/chromeos/ime_keyboard.cc
index f028f39..975b468 100644
--- a/ui/base/ime/chromeos/ime_keyboard.cc
+++ b/ui/base/ime/chromeos/ime_keyboard.cc
@@ -64,8 +64,7 @@
     : caps_lock_is_enabled_(false) {
 }
 
-ImeKeyboard::~ImeKeyboard() {
-}
+ImeKeyboard::~ImeKeyboard() = default;
 
 void ImeKeyboard::AddObserver(Observer* observer) {
   observers_.AddObserver(observer);
@@ -96,16 +95,16 @@
 }
 
 bool ImeKeyboard::IsISOLevel5ShiftAvailable() const {
-  for (size_t i = 0; i < base::size(kISOLevel5ShiftLayoutIds); ++i) {
-    if (last_layout_ == kISOLevel5ShiftLayoutIds[i])
+  for (const auto* id : kISOLevel5ShiftLayoutIds) {
+    if (last_layout_ == id)
       return true;
   }
   return false;
 }
 
 bool ImeKeyboard::IsAltGrAvailable() const {
-  for (size_t i = 0; i < base::size(kAltGrLayoutIds); ++i) {
-    if (last_layout_ == kAltGrLayoutIds[i])
+  for (const auto* id : kAltGrLayoutIds) {
+    if (last_layout_ == id)
       return true;
   }
   return false;
diff --git a/ui/base/ime/chromeos/ime_keymap.cc b/ui/base/ime/chromeos/ime_keymap.cc
index c639df8..016fe6a 100644
--- a/ui/base/ime/chromeos/ime_keymap.cc
+++ b/ui/base/ime/chromeos/ime_keymap.cc
@@ -160,21 +160,19 @@
 class KeyCodeMap {
  public:
   KeyCodeMap() {
-    for (size_t i = 0; i < base::size(kKeyCodeTable); ++i) {
-      map_dom_key_[kKeyCodeTable[i].dom_code] = kKeyCodeTable[i].keyboard_code;
-      map_key_dom_[kKeyCodeTable[i].keyboard_code] = kKeyCodeTable[i].dom_code;
+    for (const auto& key_code : kKeyCodeTable) {
+      map_dom_key_[key_code.dom_code] = key_code.keyboard_code;
+      map_key_dom_[key_code.keyboard_code] = key_code.dom_code;
     }
   }
 
   KeyboardCode GetKeyboardCode(const std::string& dom_code) const {
-    std::map<std::string, KeyboardCode>::const_iterator it =
-        map_dom_key_.find(dom_code);
+    auto it = map_dom_key_.find(dom_code);
     return (it == map_dom_key_.end()) ? VKEY_UNKNOWN : it->second;
   }
 
   std::string GetDomKeycode(KeyboardCode key_code) const {
-    std::map<KeyboardCode, std::string>::const_iterator it =
-        map_key_dom_.find(key_code);
+    auto it = map_key_dom_.find(key_code);
     return (it == map_key_dom_.end()) ? "" : it->second;
   }
 
diff --git a/ui/base/ime/chromeos/input_method_chromeos.cc b/ui/base/ime/chromeos/input_method_chromeos.cc
index b0de01f..ec5b5da 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos.cc
@@ -674,7 +674,7 @@
   } while (char_iterator.Advance());
 
   // The text length in Unicode characters.
-  uint32_t char_length = static_cast<uint32_t>(char16_offsets.size());
+  auto char_length = static_cast<uint32_t>(char16_offsets.size());
   // Make sure we can convert the value of |char_length| as well.
   char16_offsets.push_back(length);
 
@@ -685,16 +685,16 @@
 
   const ImeTextSpans text_ime_text_spans = text.ime_text_spans;
   if (!text_ime_text_spans.empty()) {
-    for (size_t i = 0; i < text_ime_text_spans.size(); ++i) {
-      const uint32_t start = text_ime_text_spans[i].start_offset;
-      const uint32_t end = text_ime_text_spans[i].end_offset;
+    for (const auto& text_ime_text_span : text_ime_text_spans) {
+      const uint32_t start = text_ime_text_span.start_offset;
+      const uint32_t end = text_ime_text_span.end_offset;
       if (start >= end)
         continue;
       ImeTextSpan ime_text_span(ui::ImeTextSpan::Type::kComposition,
                                 char16_offsets[start], char16_offsets[end],
-                                text_ime_text_spans[i].thickness,
-                                text_ime_text_spans[i].background_color);
-      ime_text_span.underline_color = text_ime_text_spans[i].underline_color;
+                                text_ime_text_span.thickness,
+                                text_ime_text_span.background_color);
+      ime_text_span.underline_color = text_ime_text_span.underline_color;
       out_composition->ime_text_spans.push_back(ime_text_span);
     }
   }
diff --git a/ui/base/ime/chromeos/input_method_chromeos_unittest.cc b/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
index d9f9e96..2573439 100644
--- a/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
@@ -173,7 +173,7 @@
     bool is_jp_ime() const { return is_jp_ime_; }
 
    protected:
-    ~TestState() override {}
+    ~TestState() override = default;
 
    private:
     bool is_jp_kbd_ = false;
@@ -217,7 +217,7 @@
     ResetFlags();
   }
 
-  ~InputMethodChromeOSTest() override {}
+  ~InputMethodChromeOSTest() override = default;
 
   void SetUp() override {
     IMEBridge::Initialize();
@@ -243,10 +243,10 @@
 
   void TearDown() override {
     if (ime_.get())
-      ime_->SetFocusedTextInputClient(NULL);
+      ime_->SetFocusedTextInputClient(nullptr);
     ime_.reset();
-    IMEBridge::Get()->SetCurrentEngineHandler(NULL);
-    IMEBridge::Get()->SetCandidateWindowHandler(NULL);
+    IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
+    IMEBridge::Get()->SetCandidateWindowHandler(nullptr);
     mock_ime_engine_handler_.reset();
     mock_ime_candidate_window_handler_.reset();
     IMEBridge::Shutdown();
@@ -385,13 +385,13 @@
 
 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
   EXPECT_EQ(this, ime_->GetTextInputClient());
-  ime_->SetFocusedTextInputClient(NULL);
-  EXPECT_EQ(NULL, ime_->GetTextInputClient());
+  ime_->SetFocusedTextInputClient(nullptr);
+  EXPECT_EQ(nullptr, ime_->GetTextInputClient());
 }
 
 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
-  ime_->SetFocusedTextInputClient(NULL);
+  ime_->SetFocusedTextInputClient(nullptr);
   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
   ime_->OnTextInputTypeChanged(this);
   // The OnTextInputTypeChanged() call above should be ignored since |this| is
@@ -501,7 +501,7 @@
             mock_ime_engine_handler_->last_text_input_context().mode);
 
   // Confirm that FocusOut is called when set focus to NULL client.
-  ime_->SetFocusedTextInputClient(NULL);
+  ime_->SetFocusedTextInputClient(nullptr);
   EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
   EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
   // Confirm that FocusIn is called when set focus to this client.
@@ -900,8 +900,8 @@
 
 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
  public:
-  InputMethodChromeOSKeyEventTest() {}
-  ~InputMethodChromeOSKeyEventTest() override {}
+  InputMethodChromeOSKeyEventTest() = default;
+  ~InputMethodChromeOSKeyEventTest() override = default;
 
   DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
 };
diff --git a/ui/base/ime/chromeos/input_method_delegate.h b/ui/base/ime/chromeos/input_method_delegate.h
index 47aa251..e66c686 100644
--- a/ui/base/ime/chromeos/input_method_delegate.h
+++ b/ui/base/ime/chromeos/input_method_delegate.h
@@ -16,8 +16,8 @@
 // Provides access to read/persist Input Method-related properties.
 class InputMethodDelegate {
  public:
-  InputMethodDelegate() {}
-  virtual ~InputMethodDelegate() {}
+  InputMethodDelegate() = default;
+  virtual ~InputMethodDelegate() = default;
 
   // Returns original VPD value.
   virtual std::string GetHardwareKeyboardLayouts() const = 0;
diff --git a/ui/base/ime/chromeos/input_method_descriptor.cc b/ui/base/ime/chromeos/input_method_descriptor.cc
index 67f9a3c..769bd4e 100644
--- a/ui/base/ime/chromeos/input_method_descriptor.cc
+++ b/ui/base/ime/chromeos/input_method_descriptor.cc
@@ -60,11 +60,9 @@
   return indicator_;
 }
 
-InputMethodDescriptor::InputMethodDescriptor() {
-}
+InputMethodDescriptor::InputMethodDescriptor() = default;
 
-InputMethodDescriptor::~InputMethodDescriptor() {
-}
+InputMethodDescriptor::~InputMethodDescriptor() = default;
 
 }  // namespace input_method
 }  // namespace chromeos
diff --git a/ui/base/ime/chromeos/input_method_manager.cc b/ui/base/ime/chromeos/input_method_manager.cc
index 87fd7796..b5592bb 100644
--- a/ui/base/ime/chromeos/input_method_manager.cc
+++ b/ui/base/ime/chromeos/input_method_manager.cc
@@ -10,17 +10,16 @@
 namespace input_method {
 
 namespace {
-InputMethodManager* g_input_method_manager = NULL;
+InputMethodManager* g_input_method_manager = nullptr;
 }
 
-InputMethodManager::State::~State() {
-}
+InputMethodManager::State::~State() = default;
 
-InputMethodManager::MenuItem::MenuItem() {}
+InputMethodManager::MenuItem::MenuItem() = default;
 
 InputMethodManager::MenuItem::MenuItem(const MenuItem& other) = default;
 
-InputMethodManager::MenuItem::~MenuItem() {}
+InputMethodManager::MenuItem::~MenuItem() = default;
 
 // static
 InputMethodManager* InputMethodManager::Get() {
@@ -38,7 +37,7 @@
   DCHECK(g_input_method_manager)
       << "InputMethodManager() is not initialized.";
   delete g_input_method_manager;
-  g_input_method_manager = NULL;
+  g_input_method_manager = nullptr;
 }
 
 }  // namespace input_method
diff --git a/ui/base/ime/chromeos/input_method_manager.h b/ui/base/ime/chromeos/input_method_manager.h
index 2df18043..dc1ca0c 100644
--- a/ui/base/ime/chromeos/input_method_manager.h
+++ b/ui/base/ime/chromeos/input_method_manager.h
@@ -77,7 +77,7 @@
 
   class Observer {
    public:
-    virtual ~Observer() {}
+    virtual ~Observer() = default;
     // Called when the current input method is changed.  |show_message|
     // indicates whether the user should be notified of this change.
     virtual void InputMethodChanged(InputMethodManager* manager,
@@ -105,7 +105,7 @@
   // keyboard is used, since it controls its own candidate window.
   class CandidateWindowObserver {
    public:
-    virtual ~CandidateWindowObserver() {}
+    virtual ~CandidateWindowObserver() = default;
     // Called when the candidate window is opened.
     virtual void CandidateWindowOpened(InputMethodManager* manager) = 0;
     // Called when the candidate window is closed.
@@ -116,7 +116,7 @@
   // bar.
   class ImeMenuObserver {
    public:
-    virtual ~ImeMenuObserver() {}
+    virtual ~ImeMenuObserver() = default;
 
     // Called when the IME menu is activated or deactivated.
     virtual void ImeMenuActivationChanged(bool is_active) = 0;
@@ -257,7 +257,7 @@
     virtual ~State();
   };
 
-  virtual ~InputMethodManager() {}
+  virtual ~InputMethodManager() = default;
 
   // Gets the global instance of InputMethodManager. Initialize() must be called
   // first.
diff --git a/ui/base/ime/chromeos/input_method_util.cc b/ui/base/ime/chromeos/input_method_util.cc
index a1c4b94f4..aebb9f06 100644
--- a/ui/base/ime/chromeos/input_method_util.cc
+++ b/ui/base/ime/chromeos/input_method_util.cc
@@ -51,8 +51,6 @@
   { chromeos::extension_ime_util::kBrailleImeEngineId,
     IDS_LANGUAGES_MEDIUM_LEN_NAME_BRAILLE },
 };
-const size_t kMappingImeIdToMediumLenNameResourceIdLen =
-    base::size(kMappingImeIdToMediumLenNameResourceId);
 
 // Due to asynchronous initialization of component extension manager,
 // GetFirstLoginInputMethodIds may miss component extension IMEs. To enable
@@ -388,8 +386,7 @@
   // static data, avoiding this up-front cost.
   std::vector<EnglishToIDMap::value_type> map_storage;
   map_storage.reserve(kEnglishToResourceIdArraySize);
-  for (size_t i = 0; i < kEnglishToResourceIdArraySize; ++i) {
-    const EnglishToResouceId& map_entry = kEnglishToResourceIdArray[i];
+  for (const auto& map_entry : kEnglishToResourceIdArray) {
     map_storage.emplace_back(map_entry.english_string_from_ibus,
                              map_entry.resource_id);
   }
@@ -400,7 +397,7 @@
       << "Duplicate string is found";
 }
 
-InputMethodUtil::~InputMethodUtil() {}
+InputMethodUtil::~InputMethodUtil() = default;
 
 std::string InputMethodUtil::GetLocalizedDisplayName(
     const InputMethodDescriptor& descriptor) const {
@@ -454,7 +451,7 @@
     const std::string& input_method_id) const {
   // We can't check the component extension is whilelisted or not here because
   // it might not be initialized.
-  return GetInputMethodDescriptorFromId(input_method_id) != NULL ||
+  return GetInputMethodDescriptorFromId(input_method_id) != nullptr ||
          extension_ime_util::IsComponentExtensionIME(input_method_id) ||
          extension_ime_util::IsArcIME(input_method_id);
 }
@@ -468,8 +465,7 @@
 
 std::string InputMethodUtil::GetKeyboardLayoutName(
     const std::string& input_method_id) const {
-  InputMethodIdToDescriptorMap::const_iterator iter
-      = id_to_descriptor_.find(input_method_id);
+  auto iter = id_to_descriptor_.find(input_method_id);
   return (iter == id_to_descriptor_.end()) ?
       "" : iter->second.GetPreferredKeyboardLayout();
 }
@@ -501,12 +497,10 @@
   // For the "Your input method has changed to..." bubble. In most cases
   // it uses the same name as the short name, unless found in a table
   // for medium length names.
-  for (size_t i = 0; i < kMappingImeIdToMediumLenNameResourceIdLen; ++i) {
-    if (extension_ime_util::GetInputMethodIDByEngineID(
-        kMappingImeIdToMediumLenNameResourceId[i].engine_id) ==
+  for (const auto& i : kMappingImeIdToMediumLenNameResourceId) {
+    if (extension_ime_util::GetInputMethodIDByEngineID(i.engine_id) ==
         input_method.id()) {
-      return delegate_->GetLocalizedString(
-          kMappingImeIdToMediumLenNameResourceId[i].resource_id);
+      return delegate_->GetLocalizedString(i.resource_id);
     }
   }
   return GetInputMethodShortName(input_method);
@@ -553,10 +547,9 @@
 
 const InputMethodDescriptor* InputMethodUtil::GetInputMethodDescriptorFromId(
     const std::string& input_method_id) const {
-  InputMethodIdToDescriptorMap::const_iterator iter =
-      id_to_descriptor_.find(input_method_id);
+  auto iter = id_to_descriptor_.find(input_method_id);
   if (iter == id_to_descriptor_.end())
-    return NULL;
+    return nullptr;
   return &(iter->second);
 }
 
@@ -581,8 +574,7 @@
   std::pair<LanguageCodeToIdsMap::const_iterator,
       LanguageCodeToIdsMap::const_iterator> range =
       language_code_to_ids.equal_range(normalized_language_code);
-  for (LanguageCodeToIdsMap::const_iterator iter = range.first;
-       iter != range.second; ++iter) {
+  for (auto iter = range.first; iter != range.second; ++iter) {
     const std::string& input_method_id = iter->second;
     if ((type == kAllInputMethods) || IsKeyboardLayout(input_method_id)) {
       out_input_method_ids->push_back(input_method_id);
@@ -607,13 +599,11 @@
 
   const std::string current_layout =
       preferred_input_method.GetPreferredKeyboardLayout();
-  for (size_t i = 0; i < base::size(kDefaultInputMethodRecommendation); ++i) {
-    if (kDefaultInputMethodRecommendation[i].locale == language_code &&
-        (!kDefaultInputMethodRecommendation[i].layout[0] ||
-         kDefaultInputMethodRecommendation[i].layout == current_layout)) {
+  for (const auto& i : kDefaultInputMethodRecommendation) {
+    if (i.locale == language_code &&
+        (!i.layout[0] || i.layout == current_layout)) {
       out_input_method_ids->push_back(
-          extension_ime_util::GetInputMethodIDByEngineID(
-              kDefaultInputMethodRecommendation[i].engine_id));
+          extension_ime_util::GetInputMethodIDByEngineID(i.engine_id));
       return;
     }
   }
@@ -633,12 +623,12 @@
     std::vector<std::string>* out_language_codes) const {
   out_language_codes->clear();
 
-  for (size_t i = 0; i < input_method_ids.size(); ++i) {
-    const std::string& input_method_id = input_method_ids[i];
+  for (const auto& i : input_method_ids) {
+    const std::string& input_method_id = i;
     const InputMethodDescriptor* input_method =
         GetInputMethodDescriptorFromId(input_method_id);
     if (!input_method) {
-      DVLOG(1) << "Unknown input method ID: " << input_method_ids[i];
+      DVLOG(1) << "Unknown input method ID: " << i;
       continue;
     }
     DCHECK(!input_method->language_codes().empty());
@@ -666,12 +656,10 @@
     const std::string& input_method_id) {
   std::string engine_id = input_method_id;
   // Migrates some Engine IDs from VPD.
-  for (size_t j = 0; j < base::size(kEngineIdMigrationMap); ++j) {
-    size_t pos = engine_id.find(kEngineIdMigrationMap[j][0]);
+  for (const auto& entry : kEngineIdMigrationMap) {
+    size_t pos = engine_id.find(entry[0]);
     if (pos == 0) {
-      engine_id.replace(0,
-                        strlen(kEngineIdMigrationMap[j][0]),
-                        kEngineIdMigrationMap[j][1]);
+      engine_id.replace(0, strlen(entry[0]), entry[1]);
       break;
     }
   }
@@ -690,10 +678,10 @@
     std::vector<std::string>* input_method_ids) {
   bool rewritten = false;
   std::vector<std::string>& ids = *input_method_ids;
-  for (size_t i = 0; i < ids.size(); ++i) {
-    std::string id = MigrateInputMethod(ids[i]);
-    if (id != ids[i]) {
-      ids[i] = id;
+  for (std::string& i : ids) {
+    std::string id = MigrateInputMethod(i);
+    if (id != i) {
+      i = id;
       rewritten = true;
     }
   }
@@ -701,10 +689,10 @@
     // Removes the duplicates.
     std::vector<std::string> new_ids;
     std::unordered_set<std::string> ids_set;
-    for (size_t i = 0; i < ids.size(); ++i) {
-      if (ids_set.find(ids[i]) == ids_set.end())
-        new_ids.push_back(ids[i]);
-      ids_set.insert(ids[i]);
+    for (const auto& id : ids) {
+      if (ids_set.find(id) == ids_set.end())
+        new_ids.push_back(id);
+      ids_set.insert(id);
     }
     ids.swap(new_ids);
   }
@@ -724,10 +712,10 @@
   MigrateInputMethods(&hardware_layouts_);
 
   bool has_xkb = false;
-  for (size_t i = 0; i < hardware_layouts_.size(); ++i) {
-    if (IsLoginKeyboard(hardware_layouts_[i]))
-      hardware_login_layouts_.push_back(hardware_layouts_[i]);
-    if (extension_ime_util::IsKeyboardLayoutExtension(hardware_layouts_[i]))
+  for (const auto& hardware_layout : hardware_layouts_) {
+    if (IsLoginKeyboard(hardware_layout))
+      hardware_login_layouts_.push_back(hardware_layout);
+    if (extension_ime_util::IsKeyboardLayoutExtension(hardware_layout))
       has_xkb = true;
   }
 
@@ -783,25 +771,25 @@
 }
 
 void InputMethodUtil::AppendInputMethods(const InputMethodDescriptors& imes) {
-  for (size_t i = 0; i < imes.size(); ++i) {
-    const InputMethodDescriptor& input_method = imes[i];
+  for (const auto& input_method : imes) {
     DCHECK(!input_method.language_codes().empty());
     const std::vector<std::string>& language_codes =
         input_method.language_codes();
     id_to_descriptor_[input_method.id()] = input_method;
 
     typedef LanguageCodeToIdsMap::const_iterator It;
-    for (size_t j = 0; j < language_codes.size(); ++j) {
+    for (const auto& language_code : language_codes) {
       std::pair<It, It> range =
-          language_code_to_ids_.equal_range(language_codes[j]);
-      It it = range.first;
+          language_code_to_ids_.equal_range(language_code);
+      auto it = range.first;
       for (; it != range.second; ++it) {
         if (it->second == input_method.id())
           break;
       }
-      if (it == range.second)
+      if (it == range.second) {
         language_code_to_ids_.insert(
-            std::make_pair(language_codes[j], input_method.id()));
+            std::make_pair(language_code, input_method.id()));
+      }
     }
   }
 }
@@ -827,9 +815,9 @@
 
 InputMethodDescriptor InputMethodUtil::GetFallbackInputMethodDescriptor() {
   std::vector<std::string> layouts;
-  layouts.push_back("us");
+  layouts.emplace_back("us");
   std::vector<std::string> languages;
-  languages.push_back("en-US");
+  languages.emplace_back("en-US");
   return InputMethodDescriptor(
       extension_ime_util::GetInputMethodIDByEngineID("xkb:us::eng"),
       "",
diff --git a/ui/base/ime/chromeos/input_method_util_unittest.cc b/ui/base/ime/chromeos/input_method_util_unittest.cc
index 92d13947..363760c 100644
--- a/ui/base/ime/chromeos/input_method_util_unittest.cc
+++ b/ui/base/ime/chromeos/input_method_util_unittest.cc
@@ -60,8 +60,8 @@
 
     std::vector<std::string> layouts;
     std::vector<std::string> languages;
-    layouts.push_back("us");
-    languages.push_back("zh-CN");
+    layouts.emplace_back("us");
+    languages.emplace_back("zh-CN");
 
     InputMethodDescriptor pinyin_ime(Id(pinyin_ime_id),
                                      "Pinyin input for testing",
@@ -74,7 +74,7 @@
     input_methods.push_back(pinyin_ime);
 
     languages.clear();
-    languages.push_back("zh-TW");
+    languages.emplace_back("zh-TW");
     InputMethodDescriptor zhuyin_ime(zhuyin_ime_id,
                                      "Zhuyin input for testing",
                                      "TW",
@@ -177,14 +177,13 @@
 TEST_F(InputMethodUtilTest, GetInputMethodMediumNameTest) {
   {
     // input methods with medium name equal to short name
-    const char* const input_method_id[] = {
+    const char* const input_method_ids[] = {
         "xkb:us:altgr-intl:eng", "xkb:us:dvorak:eng", "xkb:us:intl:eng",
         "xkb:us:colemak:eng",    "xkb:de:neo:ger",    "xkb:es:cat:cat",
         "xkb:gb:dvorak:eng",
     };
-    const int len = base::size(input_method_id);
-    for (int i = 0; i < len; ++i) {
-      InputMethodDescriptor desc = GetDesc(input_method_id[i], "", "", "");
+    for (const char* id : input_method_ids) {
+      InputMethodDescriptor desc = GetDesc(id, "", "", "");
       base::string16 medium_name = util_.GetInputMethodMediumName(desc);
       base::string16 short_name = util_.GetInputMethodShortName(desc);
       EXPECT_EQ(medium_name, short_name);
@@ -192,12 +191,12 @@
   }
   {
     // input methods with medium name not equal to short name
-    const char* const input_method_id[] = {
-        pinyin_ime_id, zhuyin_ime_id,
+    const char* const input_method_ids[] = {
+        pinyin_ime_id,
+        zhuyin_ime_id,
     };
-    const int len = base::size(input_method_id);
-    for (int i = 0; i < len; ++i) {
-      InputMethodDescriptor desc = GetDesc(input_method_id[i], "", "", "");
+    for (const char* id : input_method_ids) {
+      InputMethodDescriptor desc = GetDesc(id, "", "", "");
       base::string16 medium_name = util_.GetInputMethodMediumName(desc);
       base::string16 short_name = util_.GetInputMethodShortName(desc);
       EXPECT_NE(medium_name, short_name);
@@ -292,11 +291,11 @@
 }
 
 TEST_F(InputMethodUtilTest, TestGetInputMethodDescriptorFromId) {
-  EXPECT_EQ(NULL, util_.GetInputMethodDescriptorFromId("non_existent"));
+  EXPECT_EQ(nullptr, util_.GetInputMethodDescriptorFromId("non_existent"));
 
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id(pinyin_ime_id));
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   EXPECT_EQ(Id(pinyin_ime_id), descriptor->id());
   EXPECT_EQ("us", descriptor->GetPreferredKeyboardLayout());
   // This used to be "zh" but now we have "zh-CN" in input_methods.h,
@@ -340,7 +339,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_EnUs) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("en-US", *descriptor, &input_method_ids);
   ASSERT_EQ(1U, input_method_ids.size());
@@ -351,7 +350,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Zh) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("zh-CN", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -363,7 +362,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Ru) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("ru", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -375,7 +374,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_ZhTw) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("zh-TW", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -387,7 +386,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Th) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("th", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -399,7 +398,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Vi) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("vi", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -411,7 +410,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_Jp) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("ja", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -423,7 +422,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Jp_And_Jp) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:jp::jpn"));  // JP keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("ja", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -435,7 +434,7 @@
 TEST_F(InputMethodUtilTest, TestGetFirstLoginInputMethodIds_Us_And_He) {
   const InputMethodDescriptor* descriptor =
       util_.GetInputMethodDescriptorFromId(Id("xkb:us::eng"));  // US keyboard.
-  ASSERT_TRUE(NULL != descriptor);  // ASSERT_NE doesn't compile.
+  ASSERT_TRUE(nullptr != descriptor);  // ASSERT_NE doesn't compile.
   std::vector<std::string> input_method_ids;
   util_.GetFirstLoginInputMethodIds("he", *descriptor, &input_method_ids);
   ASSERT_EQ(2U, input_method_ids.size());
@@ -461,9 +460,8 @@
 TEST_F(InputMethodUtilTest, TestIBusInputMethodText) {
   const std::map<std::string, InputMethodDescriptor>& id_to_descriptor =
       util_.GetIdToDesciptorMapForTesting();
-  for (std::map<std::string, InputMethodDescriptor>::const_iterator it =
-       id_to_descriptor.begin(); it != id_to_descriptor.end(); ++it) {
-    const std::string language_code = it->second.language_codes().at(0);
+  for (const auto& it : id_to_descriptor) {
+    const std::string language_code = it.second.language_codes().at(0);
     const base::string16 display_name =
         l10n_util::GetDisplayNameForLocale(language_code, "en", false);
     // Only two formats, like "fr" (lower case) and "en-US" (lower-upper), are
@@ -495,10 +493,10 @@
       {"unknown", "unknown"},
   };
   std::vector<std::string> input_method_ids;
-  for (size_t i = 0; i < base::size(migration_cases); ++i)
-    input_method_ids.push_back(migration_cases[i][0]);
+  for (const auto& migration_case : migration_cases)
+    input_method_ids.emplace_back(migration_case[0]);
   // Duplicated hangul_2set.
-  input_method_ids.push_back("ime:ko:hangul_2set");
+  input_method_ids.emplace_back("ime:ko:hangul_2set");
 
   util_.MigrateInputMethods(&input_method_ids);
 
diff --git a/ui/base/ime/chromeos/input_method_whitelist.cc b/ui/base/ime/chromeos/input_method_whitelist.cc
index e3078069..0c65bbf 100644
--- a/ui/base/ime/chromeos/input_method_whitelist.cc
+++ b/ui/base/ime/chromeos/input_method_whitelist.cc
@@ -21,13 +21,12 @@
 const char kLanguageDelimiter[] = ",";
 
 InputMethodWhitelist::InputMethodWhitelist() {
-  for (size_t i = 0; i < base::size(kInputMethods); ++i) {
-    supported_input_methods_.insert(kInputMethods[i].input_method_id);
+  for (const auto& input_method : kInputMethods) {
+    supported_input_methods_.insert(input_method.input_method_id);
   }
 }
 
-InputMethodWhitelist::~InputMethodWhitelist() {
-}
+InputMethodWhitelist::~InputMethodWhitelist() = default;
 
 bool InputMethodWhitelist::InputMethodIdIsWhitelisted(
     const std::string& input_method_id) const {
@@ -39,25 +38,22 @@
   std::unique_ptr<InputMethodDescriptors> input_methods(
       new InputMethodDescriptors);
   input_methods->reserve(base::size(kInputMethods));
-  for (size_t i = 0; i < base::size(kInputMethods); ++i) {
+  for (const auto& input_method : kInputMethods) {
     std::vector<std::string> layouts;
-    layouts.push_back(kInputMethods[i].xkb_layout_id);
+    layouts.emplace_back(input_method.xkb_layout_id);
 
-    std::vector<std::string> languages = base::SplitString(
-        kInputMethods[i].language_code, kLanguageDelimiter,
-        base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+    std::vector<std::string> languages =
+        base::SplitString(input_method.language_code, kLanguageDelimiter,
+                          base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
     DCHECK(!languages.empty());
 
     input_methods->push_back(InputMethodDescriptor(
         extension_ime_util::GetInputMethodIDByEngineID(
-            kInputMethods[i].input_method_id),
-        "",
-        kInputMethods[i].indicator,
-        layouts,
-        languages,
-        kInputMethods[i].is_login_keyboard,
-        GURL(), // options page url.
-        GURL() // input view page url.
+            input_method.input_method_id),
+        "", input_method.indicator, layouts, languages,
+        input_method.is_login_keyboard,
+        GURL(),  // options page url.
+        GURL()   // input view page url.
         ));
   }
   return input_methods;
diff --git a/ui/base/ime/chromeos/mock_component_extension_ime_manager_delegate.cc b/ui/base/ime/chromeos/mock_component_extension_ime_manager_delegate.cc
index 82f6c13..3090121 100644
--- a/ui/base/ime/chromeos/mock_component_extension_ime_manager_delegate.cc
+++ b/ui/base/ime/chromeos/mock_component_extension_ime_manager_delegate.cc
@@ -16,8 +16,8 @@
       unload_call_count_(0) {
 }
 
-MockComponentExtIMEManagerDelegate::~MockComponentExtIMEManagerDelegate() {
-}
+MockComponentExtIMEManagerDelegate::~MockComponentExtIMEManagerDelegate() =
+    default;
 
 std::vector<ComponentExtensionIME>
     MockComponentExtIMEManagerDelegate::ListIME() {
diff --git a/ui/base/ime/chromeos/mock_ime_candidate_window_handler.cc b/ui/base/ime/chromeos/mock_ime_candidate_window_handler.cc
index 9f6c7e85..4d31aa9 100644
--- a/ui/base/ime/chromeos/mock_ime_candidate_window_handler.cc
+++ b/ui/base/ime/chromeos/mock_ime_candidate_window_handler.cc
@@ -11,9 +11,7 @@
       update_lookup_table_call_count_(0) {
 }
 
-MockIMECandidateWindowHandler::~MockIMECandidateWindowHandler() {
-
-}
+MockIMECandidateWindowHandler::~MockIMECandidateWindowHandler() = default;
 
 void MockIMECandidateWindowHandler::UpdateLookupTable(
     const ui::CandidateWindow& table,
diff --git a/ui/base/ime/chromeos/mock_ime_engine_handler.cc b/ui/base/ime/chromeos/mock_ime_engine_handler.cc
index a3a6022..20f33712 100644
--- a/ui/base/ime/chromeos/mock_ime_engine_handler.cc
+++ b/ui/base/ime/chromeos/mock_ime_engine_handler.cc
@@ -21,8 +21,7 @@
       last_set_surrounding_cursor_pos_(0),
       last_set_surrounding_anchor_pos_(0) {}
 
-MockIMEEngineHandler::~MockIMEEngineHandler() {
-}
+MockIMEEngineHandler::~MockIMEEngineHandler() = default;
 
 void MockIMEEngineHandler::FocusIn(const InputContext& input_context) {
   last_text_input_context_ = input_context;
diff --git a/ui/base/ime/chromeos/mock_input_method_manager.cc b/ui/base/ime/chromeos/mock_input_method_manager.cc
index 570c59b9..cabbcee 100644
--- a/ui/base/ime/chromeos/mock_input_method_manager.cc
+++ b/ui/base/ime/chromeos/mock_input_method_manager.cc
@@ -9,7 +9,7 @@
 namespace chromeos {
 namespace input_method {
 
-MockInputMethodManager::State::State() {}
+MockInputMethodManager::State::State() = default;
 
 scoped_refptr<InputMethodManager::State> MockInputMethodManager::State::Clone()
     const {
@@ -112,12 +112,12 @@
   return GURL::EmptyGURL();
 }
 
-MockInputMethodManager::State::~State() {}
+MockInputMethodManager::State::~State() = default;
 
 MockInputMethodManager::MockInputMethodManager()
     : features_enabled_state_(InputMethodManager::FEATURE_ALL) {}
 
-MockInputMethodManager::~MockInputMethodManager() {}
+MockInputMethodManager::~MockInputMethodManager() = default;
 
 InputMethodManager::UISessionState MockInputMethodManager::GetUISessionState() {
   return InputMethodManager::STATE_BROWSER_SCREEN;
diff --git a/ui/base/ime/win/tsf_text_store.cc b/ui/base/ime/win/tsf_text_store.cc
index 2db29ce0..ee6a31bc 100644
--- a/ui/base/ime/win/tsf_text_store.cc
+++ b/ui/base/ime/win/tsf_text_store.cc
@@ -1243,8 +1243,15 @@
     } else {
       new_text_size = new_size - replace_text_range_.start();
     }
+    // If |new_text_size| is 0, then we want to commit composition with current
+    // composition text if there is any. Construct |new_committed_string| to be
+    // current composition text so that |TextInputClient::InsertText| will
+    // commit current composition text.
     const base::string16& new_committed_string = string_buffer_document_.substr(
-        replace_text_range_.start(), new_text_size);
+        replace_text_range_.start(),
+        (new_text_size == 0 && selection_.end() > replace_text_range_.start())
+            ? selection_.end() - replace_text_range_.start()
+            : new_text_size);
     // if the |replace_text_range_| start is greater than |old_size|, then we
     // don't need to delete anything because the replacement text hasn't been
     // inserted into blink yet.
@@ -1267,8 +1274,16 @@
       new_committed_string_offset = replace_text_range_.start();
       new_committed_string_size = replace_text_size_;
     }
+    // If |new_committed_string_size| is 0, then we want to commit composition
+    // with current composition text if there is any. Construct
+    // |new_committed_string| to be current composition text so that
+    // |TextInputClient::InsertText| will commit current composition text.
     const base::string16& new_committed_string = string_buffer_document_.substr(
-        new_committed_string_offset, new_committed_string_size);
+        new_committed_string_offset,
+        (new_committed_string_size == 0 &&
+         selection_.end() > new_committed_string_offset)
+            ? selection_.end() - new_committed_string_offset
+            : new_committed_string_size);
     // TODO(crbug.com/978678): Unify the behavior of
     //     |TextInputClient::InsertText(text)| for the empty text.
     if (!new_committed_string.empty())
diff --git a/ui/base/ime/win/tsf_text_store_unittest.cc b/ui/base/ime/win/tsf_text_store_unittest.cc
index 36121c76..e66184d5 100644
--- a/ui/base/ime/win/tsf_text_store_unittest.cc
+++ b/ui/base/ime/win/tsf_text_store_unittest.cc
@@ -2892,5 +2892,100 @@
   EXPECT_EQ(S_OK, result);
 }
 
+// Due to crbug.com/978678, we should not call TextInputClient::InsertText if
+// provided text is empty. In fact, we should call TextInputClient::InsertText
+// with current composition text to commit compositon without losing text.
+class RegressionTest4Callback : public TSFTextStoreTestCallback {
+ public:
+  explicit RegressionTest4Callback(TSFTextStore* text_store)
+      : TSFTextStoreTestCallback(text_store) {}
+
+  HRESULT LockGranted1(DWORD flags) {
+    GetTextTest(0, -1, L"", 0);
+    SetTextTest(0, 0, L"a", S_OK);
+    GetTextTest(0, -1, L"a", 1);
+    SetSelectionTest(0, 1, S_OK);
+
+    text_spans()->clear();
+    ImeTextSpan text_span;
+    text_span.start_offset = 0;
+    text_span.end_offset = 1;
+    text_span.underline_color = SK_ColorBLACK;
+    text_span.thickness = ImeTextSpan::Thickness::kThin;
+    text_span.background_color = SK_ColorTRANSPARENT;
+    text_spans()->push_back(text_span);
+    *edit_flag() = true;
+    *composition_start() = 0;
+    composition_range()->set_start(0);
+    composition_range()->set_end(1);
+    text_store_->OnKeyTraceDown(65u, 1966081u);
+    *has_composition_range() = true;
+
+    return S_OK;
+  }
+
+  void SetCompositionText1(const ui::CompositionText& composition) {
+    EXPECT_EQ(L"a", composition.text);
+    EXPECT_EQ(0u, composition.selection.start());
+    EXPECT_EQ(1u, composition.selection.end());
+    ASSERT_EQ(1u, composition.ime_text_spans.size());
+    EXPECT_EQ(SK_ColorBLACK, composition.ime_text_spans[0].underline_color);
+    EXPECT_EQ(SK_ColorTRANSPARENT,
+              composition.ime_text_spans[0].background_color);
+    EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset);
+    EXPECT_EQ(1u, composition.ime_text_spans[0].end_offset);
+    EXPECT_EQ(ImeTextSpan::Thickness::kThin,
+              composition.ime_text_spans[0].thickness);
+    SetHasCompositionText(true);
+  }
+
+  HRESULT LockGranted2(DWORD flags) {
+    GetTextTest(0, -1, L"a", 1);
+    SetSelectionTest(1, 1, S_OK);
+
+    text_spans()->clear();
+    *edit_flag() = true;
+    *composition_start() = 0;
+    composition_range()->set_start(0);
+    composition_range()->set_end(0);
+
+    *has_composition_range() = false;
+    return S_OK;
+  }
+
+  void InsertText2(const base::string16& text) {
+    EXPECT_EQ(L"a", text);
+    SetHasCompositionText(false);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RegressionTest4Callback);
+};
+
+TEST_F(TSFTextStoreTest, RegressionTest4) {
+  RegressionTest4Callback callback(text_store_.get());
+  EXPECT_CALL(text_input_client_, SetCompositionText(_))
+      .WillOnce(
+          Invoke(&callback, &RegressionTest4Callback::SetCompositionText1));
+
+  EXPECT_CALL(text_input_client_, InsertText(_))
+      .WillOnce(Invoke(&callback, &RegressionTest4Callback::InsertText2));
+
+  EXPECT_CALL(*sink_, OnLockGranted(_))
+      .WillOnce(Invoke(&callback, &RegressionTest4Callback::LockGranted1))
+      .WillOnce(Invoke(&callback, &RegressionTest4Callback::LockGranted2));
+
+  ON_CALL(text_input_client_, HasCompositionText())
+      .WillByDefault(
+          Invoke(&callback, &TSFTextStoreTestCallback::HasCompositionText));
+
+  HRESULT result = kInvalidResult;
+  EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+  EXPECT_EQ(S_OK, result);
+  result = kInvalidResult;
+  EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+  EXPECT_EQ(S_OK, result);
+}
+
 }  // namespace
 }  // namespace ui
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 823eaca..ade3c7f 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -2159,6 +2159,10 @@
   padding-bottom: 8px;
 }
 
+.signals-overscroll {
+  min-height: calc(68px + 24px);
+}
+
 /* Progress center */
 
 @keyframes progress-center-toggle {
diff --git a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js
index 0e5bb12b..e7c5a5b 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js
+++ b/ui/file_manager/file_manager/foreground/elements/files_metadata_box.js
@@ -6,11 +6,13 @@
   is: 'files-metadata-box',
 
   properties: {
-    // File media type, e.g. image, video.
+    // File path and type, e.g. image, video.
+    filePath: String,
     type: String,
+
+    // File size, modification time, mimeType.
     size: String,
     modificationTime: String,
-    filePath: String,
     mediaMimeType: String,
 
     // File type specific metadata below.
@@ -69,6 +71,16 @@
      * @private
      */
     hasFileSpecificInfo_: Boolean,
+
+    /**
+     * FilesMetadataBox [metadata] attribute. Used to indicate the metadata box
+     * field rendering phases.
+     * @private
+     */
+    metadata: {
+      type: String,
+      reflectToAttribute: true,
+    },
   },
 
   /**
@@ -76,10 +88,15 @@
    * @param {boolean} keepSizeFields do not clear size and isSizeLoading fields.
    */
   clear: function(keepSizeFields) {
+    this.filePath = '';
     this.type = '';
+
+    if (!keepSizeFields) {
+      this.size = '';
+      this.isSizeLoading = false;
+    }
     this.modificationTime = '';
     this.mediaMimeType = '';
-    this.filePath = '';
 
     this.imageWidth = 0;
     this.imageHeight = 0;
@@ -92,10 +109,7 @@
     this.mediaYearRecorded = '';
     this.ifd = null;
 
-    if (!keepSizeFields) {
-      this.size = '';
-      this.isSizeLoading = false;
-    }
+    this.metadata = '';
   },
 
   /**
@@ -141,6 +155,21 @@
   },
 
   /**
+   * Update the metadata attribute with the rendered metadata |type|.
+   * @param {?string} type
+   * @public
+   */
+  metadataRendered: function(type) {
+    if (!type) {
+      this.metadata = '';
+    } else if (!this.metadata) {
+      this.metadata = type;
+    } else {
+      this.metadata += ' ' + type;
+    }
+  },
+
+  /**
    * Converts the duration into human friendly string.
    * @param {number} time the duration in seconds.
    * @return {string} String representation of the given duration.
diff --git a/ui/file_manager/file_manager/foreground/js/metadata_box_controller.js b/ui/file_manager/file_manager/foreground/js/metadata_box_controller.js
index 3609d7c..3033a5c 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata_box_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/metadata_box_controller.js
@@ -151,6 +151,7 @@
   } else if (item.size) {
     this.metadataBox_.size =
         this.fileMetadataFormatter_.formatSize(item.size, item.hosted);
+    this.metadataBox_.metadataRendered('size');
   }
 
   this.updateModificationTime_(entry, isSameEntry, items);
@@ -159,11 +160,13 @@
     this.metadataModel_.get([entry], ['contentMimeType']).then(items => {
       const item = items[0];
       this.metadataBox_.mediaMimeType = item.contentMimeType || '';
+      this.metadataBox_.metadataRendered('mime');
     });
   } else {
     this.metadataModel_.get([entry], ['mediaMimeType']).then(items => {
       const item = items[0];
       this.metadataBox_.mediaMimeType = item.mediaMimeType || '';
+      this.metadataBox_.metadataRendered('mime');
     });
   }
 
@@ -178,6 +181,7 @@
                 /** @type {number} */ (item.imageHeight);
             this.metadataBox_.imageWidth =
                 /** @type {number} */ (item.imageWidth);
+            this.metadataBox_.metadataRendered('meta');
           });
     } else {
       this.metadataModel_
@@ -207,6 +211,7 @@
             this.metadataBox_.mediaTitle = item.mediaTitle || '';
             this.metadataBox_.mediaTrack = item.mediaTrack || '';
             this.metadataBox_.mediaYearRecorded = item.mediaYearRecorded || '';
+            this.metadataBox_.metadataRendered('meta');
           });
     }
   }
@@ -293,5 +298,6 @@
 
     this.metadataBox_.size = this.fileMetadataFormatter_.formatSize(size, true);
     this.metadataBox_.isSizeLoading = false;
+    this.metadataBox_.metadataRendered('size');
   });
 };
diff --git a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
index d6f957d..cd932e8 100644
--- a/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/quick_view_controller.js
@@ -271,7 +271,10 @@
   }
 
   /**
-   * Update quick view using file entry and loaded metadata and tasks.
+   * Update quick view for |entry| from its loaded metadata and tasks.
+   *
+   * Note: fast-typing users can change the active selection while the |entry|
+   * metadata and tasks were being async fetched. Bail out in that case.
    *
    * @param {!FileEntry} entry
    * @param {Array<MetadataItem>} items
@@ -280,6 +283,10 @@
    */
   onMetadataLoaded_(entry, items, tasks) {
     return this.getQuickViewParameters_(entry, items, tasks).then(params => {
+      if (this.quickViewModel_.getSelectedEntry() != entry) {
+        return;  // Bail: there's no point drawing a stale selection.
+      }
+
       this.quickView_.type = params.type || '';
       this.quickView_.subtype = params.subtype || '';
       this.quickView_.filePath = params.filePath || '';
@@ -321,10 +328,10 @@
             volumeInfo.volumeType) >= 0;
 
     if (!localFile) {
-      // For Drive files, display a thumbnail if there is one.
+      // Drive files: fetch their thumbnail if there is one.
       if (item.thumbnailUrl) {
         return this.loadThumbnailFromDrive_(item.thumbnailUrl).then(result => {
-          if (result.status === 'success') {
+          if (result.status === LoadImageResponseStatus.SUCCESS) {
             if (params.type == 'video') {
               params.videoPoster = result.data;
             } else if (params.type == 'image') {
@@ -344,9 +351,26 @@
       return Promise.resolve(params);
     }
 
+    if (type === 'raw') {
+      // RAW files: fetch their ImageLoader thumbnail.
+      return this.loadRawFileThumbnailFromImageLoader_(entry)
+          .then(result => {
+            if (result.status === LoadImageResponseStatus.SUCCESS) {
+              params.contentUrl = result.data;
+              params.type = 'image';
+            }
+            return params;
+          })
+          .catch(e => {
+            console.error(e);
+            return params;
+          });
+    }
+
     if (type === '.folder') {
       return Promise.resolve(params);
     }
+
     return new Promise((resolve, reject) => {
              entry.file(resolve, reject);
            })
@@ -407,8 +431,7 @@
    * Loads a thumbnail from Drive.
    *
    * @param {string} url Thumbnail url
-   * @return Promise<{{status: string, data:string, width:number,
-   *     height:number}}>
+   * @return Promise<!LoadImageResponse>
    * @private
    */
   loadThumbnailFromDrive_(url) {
@@ -417,6 +440,30 @@
           LoadImageRequest.createForUrl(url), resolve);
     });
   }
+
+  /**
+   * Loads a RAW image thumbnail from ImageLoader. Resolve the file entry first
+   * to get its |lastModified| time. ImageLoaderClient uses that to work out if
+   * its cached data for |entry| is up-to-date or otherwise call ImageLoader to
+   * refresh the cached |entry| data with the most recent data.
+   *
+   * @param {!Entry} entry The RAW file entry.
+   * @return Promise<!LoadImageResponse>
+   * @private
+   */
+  loadRawFileThumbnailFromImageLoader_(entry) {
+    return new Promise((resolve, reject) => {
+      entry.file(function requestFileThumbnail(file) {
+        const request = LoadImageRequest.createForUrl(entry.toURL());
+        request.maxWidth = ThumbnailLoader.THUMBNAIL_MAX_WIDTH;
+        request.maxHeight = ThumbnailLoader.THUMBNAIL_MAX_HEIGHT;
+        request.timestamp = file.lastModified;
+        request.cache = true;
+        request.priority = 0;
+        ImageLoaderClient.getInstance().load(request, resolve);
+      }, reject);
+    });
+  }
 }
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
index 1b4c0017..e8419ce 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_grid.js
@@ -116,6 +116,12 @@
     self.historyLoader_ = historyLoader;
     self.a11y = a11y;
 
+    // Force the list's ending spacer to be tall enough to allow overscroll.
+    let endSpacer = self.querySelector('.spacer:last-child');
+    if (endSpacer) {
+      endSpacer.classList.add('signals-overscroll');
+    }
+
     self.listThumbnailLoader_ = null;
     self.beginIndex_ = 0;
     self.endIndex_ = 0;
diff --git a/ui/file_manager/file_manager/foreground/js/ui/file_table.js b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
index 379bcbd9..421ab1e 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/file_table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/file_table.js
@@ -433,6 +433,12 @@
     self.historyLoader_ = historyLoader;
     self.a11y = a11y;
 
+    // Force the list's ending spacer to be tall enough to allow overscroll.
+    let endSpacer = self.querySelector('.spacer:last-child');
+    if (endSpacer) {
+      endSpacer.classList.add('signals-overscroll');
+    }
+
     /** @private {ListThumbnailLoader} */
     self.listThumbnailLoader_ = null;
 
diff --git a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
index e05cd70..9c7ebf9 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/progress_center_panel.js
@@ -336,10 +336,16 @@
           panelItem = this.feedbackHost_.addPanelItem(item.id);
           panelItem.panelType = panelItem.panelTypeProgress;
           panelItem.setAttribute('primary-text', item.message);
+          panelItem.setAttribute('data-progress-id', item.id);
           if (item.subMessage) {
             panelItem.setAttribute('secondary-text', item.subMessage);
           }
         }
+        panelItem.signalCallback = (signal) => {
+          if (signal === 'cancel' && item.cancelCallback) {
+            item.cancelCallback(item.id);
+          }
+        };
         panelItem.progress = item.progressRateInPercent;
         // Remove the feedback panel when complete, and create
         // an activity complete panel.
diff --git a/ui/file_manager/integration_tests/file_manager/quick_view.js b/ui/file_manager/integration_tests/file_manager/quick_view.js
index dc3bfde1..7017a17 100644
--- a/ui/file_manager/integration_tests/file_manager/quick_view.js
+++ b/ui/file_manager/integration_tests/file_manager/quick_view.js
@@ -94,11 +94,30 @@
    * @return {string} text Text value in the field name.
    */
   async function getQuickViewMetadataBoxField(appId, name) {
+    let filesMetadataBox = 'files-metadata-box';
+
+    /**
+     * <files-metadata-box> field rendering is async. The field name has been
+     * rendered when the 'metadata' attribute indicates that.
+     */
+    switch (name) {
+      case 'Size':
+        filesMetadataBox += '[metadata~="size"]';
+        break;
+      case 'Modified Time':
+      case 'Type':
+        filesMetadataBox += '[metadata~="mime"]';
+        break;
+      default:
+        filesMetadataBox += '[metadata~="meta"]';
+        break;
+    }
+
     /**
      * The <files-metadata-box> element resides in the #quick-view shadow DOM
      * as a child of the #dialog element.
      */
-    let quickViewQuery = ['#quick-view', '#dialog[open] files-metadata-box'];
+    let quickViewQuery = ['#quick-view', '#dialog[open] ' + filesMetadataBox];
 
     /**
      * The <files-metadata-entry key="name"> element resides in the shadow DOM
@@ -792,6 +811,52 @@
   };
 
   /**
+   * Tests opening Quick View on an RAW image. The RAW image has EXIF and that
+   * information should be displayed in the QuickView metadata box.
+   */
+  testcase.openQuickViewImageRaw = async () => {
+    const caller = getCaller();
+
+    /**
+     * The <webview> resides in the <files-safe-media type="image"> shadow DOM,
+     * which is a child of the #quick-view shadow DOM.
+     */
+    const webView =
+        ['#quick-view', 'files-safe-media[type="image"]', 'webview'];
+
+    // Open Files app on Downloads containing ENTRIES.rawImage.
+    const appId = await setupAndWaitUntilReady(
+        RootPath.DOWNLOADS, [ENTRIES.rawImage], []);
+
+    // Open the file in Quick View.
+    await openQuickView(appId, ENTRIES.rawImage.nameText);
+
+    // Wait for the Quick View <webview> to load and display its content.
+    function checkWebViewImageLoaded(elements) {
+      let haveElements = Array.isArray(elements) && elements.length === 1;
+      if (haveElements) {
+        haveElements = elements[0].styles.display.includes('block');
+      }
+      if (!haveElements || elements[0].attributes.loaded !== '') {
+        return pending(caller, 'Waiting for <webview> to load.');
+      }
+      return;
+    }
+    await repeatUntil(async () => {
+      return checkWebViewImageLoaded(await remoteCall.callRemoteTestUtil(
+          'deepQueryAllElements', appId, [webView, ['display']]));
+    });
+
+    // Check: the correct mimeType should be displayed.
+    const mimeType = await getQuickViewMetadataBoxField(appId, 'Type');
+    chrome.test.assertEq('image/x-olympus-orf', mimeType);
+
+    // Check: the image EXIF metadata should be displayed.
+    // TODO(crbug.com/965370) Make the metadata controller extract and display
+    // the RAW image EXIF metadata in the metadata box.
+  };
+
+  /**
    * Tests opening Quick View containing a video.
    */
   testcase.openQuickViewVideo = async () => {