diff --git a/DEPS b/DEPS
index 6a778af5..2aeb209 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,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': 'edbeb8b842aa86d18388f75a1dbf1e470a565680',
+  'skia_revision': 'c8f1e3a5c08d58657dddccdeedbe5d6e8c16d891',
   # 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': 'd678037326492171f04895ffb360e16e5248182a',
+  'v8_revision': '376fd4b2eb6a1ff398d4a8b58ce1ce9feb5273db',
   # 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.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '4f3d6da8af75d0aa3b0aa535319c13393269512f',
+  'catapult_revision': '78c8d7338e35e82db61513bb2f328c0764d2fe00',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -232,7 +232,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + 'ecefa58dfa5b471817398f09c332a36e8ed8eeab', # commit position 16890
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '02830c5b02494ae3a4f8dcf45ea5c988dc5e5cf8', # commit position 16899
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
diff --git a/ash/virtual_keyboard_controller.cc b/ash/virtual_keyboard_controller.cc
index 32391eb1..b483df6e5 100644
--- a/ash/virtual_keyboard_controller.cc
+++ b/ash/virtual_keyboard_controller.cc
@@ -88,6 +88,7 @@
   } else {
     UpdateKeyboardEnabled();
   }
+  keyboard::SetOverscrollEnabledWithAccessibilityKeyboard(true);
 }
 
 void VirtualKeyboardController::OnMaximizeModeEnded() {
@@ -96,6 +97,7 @@
   } else {
     UpdateKeyboardEnabled();
   }
+  keyboard::SetOverscrollEnabledWithAccessibilityKeyboard(false);
 }
 
 void VirtualKeyboardController::OnTouchscreenDeviceConfigurationChanged() {
diff --git a/cc/surfaces/compositor_frame_sink_support_unittest.cc b/cc/surfaces/compositor_frame_sink_support_unittest.cc
index 5e8f1fc..efa9b1b 100644
--- a/cc/surfaces/compositor_frame_sink_support_unittest.cc
+++ b/cc/surfaces/compositor_frame_sink_support_unittest.cc
@@ -111,10 +111,9 @@
     return surface_manager().parent_to_child_refs_[surface_id];
   }
 
-  // Returns all the temporary references for the given frame sink id.
-  std::vector<LocalSurfaceId> GetTempReferences(
-      const FrameSinkId& frame_sink_id) {
-    return surface_manager().temp_references_[frame_sink_id];
+  // Returns true if there is a temporary reference for |surface_id|.
+  bool HasTemporaryReference(const SurfaceId& surface_id) {
+    return surface_manager().HasTemporaryReference(surface_id);
   }
 
   SurfaceDependencyTracker& dependency_tracker() {
@@ -197,7 +196,7 @@
 
   // A surface reference from the top-level root is added and there shouldn't be
   // a temporary reference.
-  EXPECT_THAT(GetTempReferences(kDisplayFrameSink), IsEmpty());
+  EXPECT_FALSE(HasTemporaryReference(display_id_first));
   EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()),
               UnorderedElementsAre(display_id_first));
 
@@ -208,7 +207,7 @@
 
   // A surface reference from the top-level root to |display_id_second| should
   // be added and the reference to |display_root_first| removed.
-  EXPECT_THAT(GetTempReferences(kDisplayFrameSink), IsEmpty());
+  EXPECT_FALSE(HasTemporaryReference(display_id_second));
   EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()),
               UnorderedElementsAre(display_id_second));
 
@@ -612,7 +611,7 @@
 
   // Verify that there is no temporary reference for the child and that
   // the reference from the parent to the child still exists.
-  EXPECT_THAT(GetTempReferences(child_id.frame_sink_id()), IsEmpty());
+  EXPECT_FALSE(HasTemporaryReference(child_id));
   EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id));
 }
 
@@ -695,7 +694,7 @@
 
   // Verify that there is no temporary reference for the child and that
   // the reference from the parent to the child still exists.
-  EXPECT_THAT(GetTempReferences(child_id1.frame_sink_id()), IsEmpty());
+  EXPECT_FALSE(HasTemporaryReference(child_id1));
   EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1));
 
   // The parent submits another CompositorFrame that depends on |child_id2|.
diff --git a/cc/surfaces/surface_manager.cc b/cc/surfaces/surface_manager.cc
index 776ac2a4..e73e814 100644
--- a/cc/surfaces/surface_manager.cc
+++ b/cc/surfaces/surface_manager.cc
@@ -47,15 +47,11 @@
 SurfaceManager::~SurfaceManager() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  if (lifetime_type_ == LifetimeType::REFERENCES) {
+  if (using_surface_references()) {
     // Remove all temporary references on shutdown.
-    for (const auto& map_entry : temp_references_) {
-      const FrameSinkId& frame_sink_id = map_entry.first;
-      for (const auto& local_surface_id : map_entry.second) {
-        RemoveSurfaceReferenceImpl(GetRootSurfaceId(),
-                                   SurfaceId(frame_sink_id, local_surface_id));
-      }
-    }
+    temporary_references_.clear();
+    temporary_reference_ranges_.clear();
+
     GarbageCollectSurfaces();
   }
 
@@ -76,6 +72,10 @@
 std::string SurfaceManager::SurfaceReferencesToString() {
   std::stringstream str;
   SurfaceReferencesToStringImpl(root_surface_id_, "", &str);
+  // Temporary references will have an asterisk in front of them.
+  for (auto& map_entry : temporary_references_)
+    SurfaceReferencesToStringImpl(map_entry.first, "* ", &str);
+
   return str.str();
 }
 #endif
@@ -136,6 +136,20 @@
 
 void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
   valid_frame_sink_ids_.erase(frame_sink_id);
+
+  if (using_surface_references()) {
+    // Remove any temporary references owned by |frame_sink_id|.
+    std::vector<SurfaceId> temp_refs_to_clear;
+    for (auto& map_entry : temporary_references_) {
+      base::Optional<FrameSinkId>& owner = map_entry.second;
+      if (owner.has_value() && owner.value() == frame_sink_id)
+        temp_refs_to_clear.push_back(map_entry.first);
+    }
+
+    for (auto& surface_id : temp_refs_to_clear)
+      RemoveTemporaryReference(surface_id, false);
+  }
+
   GarbageCollectSurfaces();
 }
 
@@ -143,107 +157,52 @@
   return root_surface_id_;
 }
 
-void SurfaceManager::AddSurfaceReference(const SurfaceId& parent_id,
-                                         const SurfaceId& child_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(lifetime_type_, LifetimeType::REFERENCES);
-
-  // Check some conditions that should never happen. We don't want to crash on
-  // bad input from a compromised client so just return early.
-  if (parent_id.frame_sink_id() == child_id.frame_sink_id()) {
-    DLOG(ERROR) << "Cannot add self reference from " << parent_id << " to "
-                << child_id;
-    return;
-  }
-
-  // There could be a temporary reference to |child_id| which we should now
-  // remove because a real reference is being added to it. To find out whether
-  // or not a temporary reference exists, we need to first look up the
-  // FrameSinkId of |child_id| in |temp_references_|, which returns a vector of
-  // LocalSurfaceIds, and then search for the LocalSurfaceId of |child_id| in
-  // the said vector. If there is no temporary reference, we can immediately add
-  // the reference from |parent_id| and return.
-  auto refs_iter = temp_references_.find(child_id.frame_sink_id());
-  if (refs_iter == temp_references_.end()) {
-    AddSurfaceReferenceImpl(parent_id, child_id);
-    return;
-  }
-  std::vector<LocalSurfaceId>& refs = refs_iter->second;
-  auto temp_ref_iter =
-      std::find(refs.begin(), refs.end(), child_id.local_surface_id());
-  if (temp_ref_iter == refs.end()) {
-    AddSurfaceReferenceImpl(parent_id, child_id);
-    return;
-  }
-
-  // Temporary references are implemented by holding a reference from the top
-  // level root to the child. If |parent_id| is the top level root, we do
-  // nothing because the reference already exists. Otherwise, remove the
-  // temporary reference and add the reference.
-  if (parent_id != GetRootSurfaceId()) {
-    AddSurfaceReferenceImpl(parent_id, child_id);
-    RemoveSurfaceReference(GetRootSurfaceId(), child_id);
-  }
-
-  // Remove temporary references for surfaces with the same FrameSinkId that
-  // were created before |child_id|. The earlier surfaces were never embedded in
-  // the parent and the parent is embedding a later surface, so we know the
-  // parent doesn't need them anymore.
-  for (auto iter = refs.begin(); iter != temp_ref_iter; ++iter) {
-    SurfaceId id = SurfaceId(child_id.frame_sink_id(), *iter);
-    RemoveSurfaceReference(GetRootSurfaceId(), id);
-  }
-
-  // Remove markers for temporary references up to |child_id|, as the temporary
-  // references they correspond to were removed above. If |temp_ref_iter| points
-  // at the last element in |refs| then we are removing all temporary references
-  // for the FrameSinkId and can remove the map entry entirely.
-  if (++temp_ref_iter == refs.end())
-    temp_references_.erase(child_id.frame_sink_id());
-  else
-    refs.erase(refs.begin(), temp_ref_iter);
-}
-
-void SurfaceManager::RemoveSurfaceReference(const SurfaceId& parent_id,
-                                            const SurfaceId& child_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK_EQ(lifetime_type_, LifetimeType::REFERENCES);
-
-  // Check if we have the reference that is requested to be removed. We don't
-  // want to crash on bad input from a compromised client so just return early.
-  if (parent_to_child_refs_.count(parent_id) == 0 ||
-      parent_to_child_refs_[parent_id].count(child_id) == 0) {
-    DLOG(ERROR) << "No reference from " << parent_id.ToString() << " to "
-                << child_id.ToString();
-    return;
-  }
-
-  RemoveSurfaceReferenceImpl(parent_id, child_id);
-}
-
 void SurfaceManager::AddSurfaceReferences(
     const std::vector<SurfaceReference>& references) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(using_surface_references());
 
   for (const auto& reference : references)
-    AddSurfaceReference(reference.parent_id(), reference.child_id());
+    AddSurfaceReferenceImpl(reference.parent_id(), reference.child_id());
 }
 
 void SurfaceManager::RemoveSurfaceReferences(
     const std::vector<SurfaceReference>& references) {
   DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK(using_surface_references());
 
   for (const auto& reference : references)
-    RemoveSurfaceReference(reference.parent_id(), reference.child_id());
+    RemoveSurfaceReferenceImpl(reference.parent_id(), reference.child_id());
 
   GarbageCollectSurfaces();
 }
 
+void SurfaceManager::AssignTemporaryReference(const SurfaceId& surface_id,
+                                              const FrameSinkId& owner) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(lifetime_type_, LifetimeType::REFERENCES);
+
+  if (!HasTemporaryReference(surface_id))
+    return;
+
+  temporary_references_[surface_id] = owner;
+}
+
+void SurfaceManager::DropTemporaryReference(const SurfaceId& surface_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_EQ(lifetime_type_, LifetimeType::REFERENCES);
+
+  if (!HasTemporaryReference(surface_id))
+    return;
+
+  RemoveTemporaryReference(surface_id, false);
+}
+
 void SurfaceManager::GarbageCollectSurfaces() {
   if (surfaces_to_destroy_.empty())
     return;
 
-  SurfaceIdSet reachable_surfaces = lifetime_type_ == LifetimeType::REFERENCES
+  SurfaceIdSet reachable_surfaces = using_surface_references()
                                         ? GetLiveSurfacesForReferences()
                                         : GetLiveSurfacesForSequences();
 
@@ -267,13 +226,20 @@
 }
 
 SurfaceManager::SurfaceIdSet SurfaceManager::GetLiveSurfacesForReferences() {
-  DCHECK_EQ(lifetime_type_, LifetimeType::REFERENCES);
+  DCHECK(using_surface_references());
 
   SurfaceIdSet reachable_surfaces;
 
   // Walk down from the root and mark each SurfaceId we encounter as reachable.
   std::queue<SurfaceId> surface_queue;
   surface_queue.push(root_surface_id_);
+
+  // All temporary references are also reachable.
+  for (auto& map_entry : temporary_references_) {
+    reachable_surfaces.insert(map_entry.first);
+    surface_queue.push(map_entry.first);
+  }
+
   while (!surface_queue.empty()) {
     const SurfaceId& surface_id = surface_queue.front();
     auto iter = parent_to_child_refs_.find(surface_id);
@@ -341,12 +307,27 @@
 
 void SurfaceManager::AddSurfaceReferenceImpl(const SurfaceId& parent_id,
                                              const SurfaceId& child_id) {
+  if (parent_id.frame_sink_id() == child_id.frame_sink_id()) {
+    DLOG(ERROR) << "Cannot add self reference from " << parent_id << " to "
+                << child_id;
+    return;
+  }
+
   parent_to_child_refs_[parent_id].insert(child_id);
   child_to_parent_refs_[child_id].insert(parent_id);
+
+  if (HasTemporaryReference(child_id))
+    RemoveTemporaryReference(child_id, true);
 }
 
 void SurfaceManager::RemoveSurfaceReferenceImpl(const SurfaceId& parent_id,
                                                 const SurfaceId& child_id) {
+  if (parent_to_child_refs_.count(parent_id) == 0 ||
+      parent_to_child_refs_[parent_id].count(child_id) == 0) {
+    DLOG(ERROR) << "No reference from " << parent_id << " to " << child_id;
+    return;
+  }
+
   parent_to_child_refs_[parent_id].erase(child_id);
   child_to_parent_refs_[child_id].erase(parent_id);
 }
@@ -369,6 +350,50 @@
   }
 }
 
+bool SurfaceManager::HasTemporaryReference(const SurfaceId& surface_id) const {
+  return temporary_references_.count(surface_id) != 0;
+}
+
+void SurfaceManager::AddTemporaryReference(const SurfaceId& surface_id) {
+  DCHECK(!HasTemporaryReference(surface_id));
+
+  // Add an entry to |temporary_references_| with no owner for the temporary
+  // reference. Also add a range tracking entry so we know the order that
+  // surfaces were created for the FrameSinkId.
+  temporary_references_[surface_id] = base::Optional<FrameSinkId>();
+  temporary_reference_ranges_[surface_id.frame_sink_id()].push_back(
+      surface_id.local_surface_id());
+}
+
+void SurfaceManager::RemoveTemporaryReference(const SurfaceId& surface_id,
+                                              bool remove_range) {
+  DCHECK(HasTemporaryReference(surface_id));
+
+  const FrameSinkId& frame_sink_id = surface_id.frame_sink_id();
+  std::vector<LocalSurfaceId>& frame_sink_temp_refs =
+      temporary_reference_ranges_[frame_sink_id];
+
+  // Find the iterator to the range tracking entry for |surface_id|. Use that
+  // iterator and |remove_range| to find the right begin and end iterators for
+  // the temporary references we want to remove.
+  auto surface_id_iter =
+      std::find(frame_sink_temp_refs.begin(), frame_sink_temp_refs.end(),
+                surface_id.local_surface_id());
+  auto begin_iter =
+      remove_range ? frame_sink_temp_refs.begin() : surface_id_iter;
+  auto end_iter = surface_id_iter + 1;
+
+  // Remove temporary references and range tracking information.
+  for (auto iter = begin_iter; iter != end_iter; ++iter)
+    temporary_references_.erase(SurfaceId(frame_sink_id, *iter));
+  frame_sink_temp_refs.erase(begin_iter, end_iter);
+
+  // If last temporary reference is removed for |frame_sink_id| then cleanup
+  // range tracking map entry.
+  if (frame_sink_temp_refs.empty())
+    temporary_reference_ranges_.erase(frame_sink_id);
+}
+
 void SurfaceManager::RegisterSurfaceFactoryClient(
     const FrameSinkId& frame_sink_id,
     SurfaceFactoryClient* client) {
@@ -586,11 +611,8 @@
     // use array notation into maps in tests (see https://crbug.com/691115).
     bool has_real_reference =
         it != child_to_parent_refs_.end() && !it->second.empty();
-    if (!has_real_reference) {
-      AddSurfaceReferenceImpl(GetRootSurfaceId(), surface_info.id());
-      temp_references_[surface_info.id().frame_sink_id()].push_back(
-          surface_info.id().local_surface_id());
-    }
+    if (!has_real_reference)
+      AddTemporaryReference(surface_info.id());
   }
 
   for (auto& observer : observer_list_)
@@ -607,21 +629,24 @@
   Surface* surface = GetSurfaceForId(surface_id);
   if (surface) {
     *str << surface->surface_id().ToString();
-    *str << (surface->destroyed() ? " destroyed " : " live ");
+    *str << (surface->destroyed() ? " destroyed" : " live");
 
     if (surface->HasPendingFrame()) {
       // This provides the surface size from the root render pass.
       const CompositorFrame& frame = surface->GetPendingFrame();
-      *str << "pending "
-           << frame.render_pass_list.back()->output_rect.size().ToString()
-           << " ";
+      if (!frame.render_pass_list.empty()) {
+        *str << " pending "
+             << frame.render_pass_list.back()->output_rect.size().ToString();
+      }
     }
 
     if (surface->HasActiveFrame()) {
       // This provides the surface size from the root render pass.
       const CompositorFrame& frame = surface->GetActiveFrame();
-      *str << "active "
-           << frame.render_pass_list.back()->output_rect.size().ToString();
+      if (!frame.render_pass_list.empty()) {
+        *str << " active "
+             << frame.render_pass_list.back()->output_rect.size().ToString();
+      }
     }
   } else {
     *str << surface_id;
diff --git a/cc/surfaces/surface_manager.h b/cc/surfaces/surface_manager.h
index b4785a2..569c6e44 100644
--- a/cc/surfaces/surface_manager.h
+++ b/cc/surfaces/surface_manager.h
@@ -139,15 +139,6 @@
   // SurfaceId and will never correspond to a surface.
   const SurfaceId& GetRootSurfaceId() const;
 
-  // Adds a reference from |parent_id| to |child_id|. If there is a temporary
-  // references for |child_id| then it will be removed.
-  void AddSurfaceReference(const SurfaceId& parent_id,
-                           const SurfaceId& child_id);
-
-  // Removes a reference from |parent_id| to |child_id|.
-  void RemoveSurfaceReference(const SurfaceId& parent_id,
-                              const SurfaceId& child_id);
-
   // Adds all surface references in |references|. This will remove any temporary
   // references for child surface in a surface reference.
   void AddSurfaceReferences(const std::vector<SurfaceReference>& references);
@@ -156,6 +147,18 @@
   // collection to delete unreachable surfaces.
   void RemoveSurfaceReferences(const std::vector<SurfaceReference>& references);
 
+  // Assigns |frame_sink_id| as the owner of the temporary reference to
+  // |surface_id|. If |frame_sink_id| is invalidated the temporary reference
+  // will be removed. If a surface reference has already been added from the
+  // parent to |surface_id| then this will do nothing.
+  void AssignTemporaryReference(const SurfaceId& surface_id,
+                                const FrameSinkId& owner);
+
+  // Drops the temporary reference for |surface_id|. If a surface reference has
+  // already been added from the parent to |surface_id| then this will do
+  // nothing.
+  void DropTemporaryReference(const SurfaceId& surface_id);
+
   scoped_refptr<SurfaceReferenceFactory> reference_factory() {
     return reference_factory_;
   }
@@ -202,6 +205,17 @@
   // surface is about to be deleted.
   void RemoveAllSurfaceReferences(const SurfaceId& surface_id);
 
+  bool HasTemporaryReference(const SurfaceId& surface_id) const;
+
+  // Adds a temporary reference to |surface_id|. The reference will not have an
+  // owner initially.
+  void AddTemporaryReference(const SurfaceId& surface_id);
+
+  // Removes temporary reference to |surface_id|. If |remove_range| is true then
+  // all temporary references to surfaces with the same FrameSinkId as
+  // |surface_id| that were added before |surface_id| will also be removed.
+  void RemoveTemporaryReference(const SurfaceId& surface_id, bool remove_range);
+
 #if DCHECK_IS_ON()
   // Recursively prints surface references starting at |surface_id| to |str|.
   void SurfaceReferencesToStringImpl(const SurfaceId& surface_id,
@@ -271,13 +285,23 @@
   // references.
   scoped_refptr<SurfaceReferenceFactory> reference_factory_;
 
-  // SurfaceIds that have temporary references from top level root so they
-  // aren't GC'd before a real reference is added. This is basically a
-  // collection of surface ids, for example:
+  // A map of surfaces that have temporary references to them. The key is the
+  // SurfaceId and the value is the owner. The owner will initially be empty and
+  // set later by AssignTemporaryReference().
+  std::unordered_map<SurfaceId, base::Optional<FrameSinkId>, SurfaceIdHash>
+      temporary_references_;
+
+  // Range tracking information for temporary references. Each map entry is an
+  // is an ordered list of SurfaceIds that have temporary references with the
+  // same FrameSinkId. A SurfaceId can be reconstructed with:
   //   SurfaceId surface_id(key, value[index]);
-  // The LocalSurfaceIds are stored in the order the surfaces are created in.
+  // The LocalSurfaceIds are stored in the order the surfaces are created in. If
+  // a reference is added to a later SurfaceId then all temporary references up
+  // to that point will be removed. This is to handle clients getting out of
+  // sync, for example the embedded client producing new SurfaceIds faster than
+  // the embedding client can use them.
   std::unordered_map<FrameSinkId, std::vector<LocalSurfaceId>, FrameSinkIdHash>
-      temp_references_;
+      temporary_reference_ranges_;
 
   std::unique_ptr<SurfaceDependencyTracker> dependency_tracker_;
 
diff --git a/cc/surfaces/surface_manager_ref_unittest.cc b/cc/surfaces/surface_manager_ref_unittest.cc
index 5f68c16..fb21ce3 100644
--- a/cc/surfaces/surface_manager_ref_unittest.cc
+++ b/cc/surfaces/surface_manager_ref_unittest.cc
@@ -71,6 +71,11 @@
     manager_->RemoveSurfaceReferences({SurfaceReference(parent_id, child_id)});
   }
 
+  void AddSurfaceReference(const SurfaceId& parent_id,
+                           const SurfaceId& child_id) {
+    manager_->AddSurfaceReferences({SurfaceReference(parent_id, child_id)});
+  }
+
   // Returns all the references where |surface_id| is the parent.
   const SurfaceManager::SurfaceIdSet& GetReferencesFrom(
       const SurfaceId& surface_id) {
@@ -83,24 +88,12 @@
     return manager().child_to_parent_refs_[surface_id];
   }
 
-  const SurfaceManager::SurfaceIdSet& GetReferencesFromRoot() {
-    return GetReferencesFrom(manager().GetRootSurfaceId());
-  }
-
-  // Returns all the temporary references for the given frame sink id.
-  std::vector<LocalSurfaceId> GetTempReferencesFor(
-      const FrameSinkId& frame_sink_id) {
-    return manager().temp_references_[frame_sink_id];
-  }
-
   // Temporary references are stored as a map in SurfaceManager. This method
   // converts the map to a vector.
   std::vector<SurfaceId> GetAllTempReferences() {
     std::vector<SurfaceId> temp_references;
-    for (auto& map_entry : manager().temp_references_) {
-      for (auto local_surface_id : map_entry.second)
-        temp_references.push_back(SurfaceId(map_entry.first, local_surface_id));
-    }
+    for (auto& map_entry : manager().temporary_references_)
+      temp_references.push_back(map_entry.first);
     return temp_references;
   }
 
@@ -128,7 +121,7 @@
 
 TEST_F(SurfaceManagerRefTest, AddReference) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
 
   EXPECT_THAT(GetReferencesFor(id1),
               UnorderedElementsAre(manager().GetRootSurfaceId()));
@@ -138,8 +131,8 @@
 TEST_F(SurfaceManagerRefTest, AddRemoveReference) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), id1);
-  manager().AddSurfaceReference(id1, id2);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(id1, id2);
 
   EXPECT_THAT(GetReferencesFor(id1),
               UnorderedElementsAre(manager().GetRootSurfaceId()));
@@ -159,9 +152,9 @@
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), id1);
-  manager().AddSurfaceReference(id1, id2);
-  manager().AddSurfaceReference(id2, id3);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(id1, id2);
+  AddSurfaceReference(id2, id3);
 
   // |kFramesink2| received a CompositorFrame with a new size, so it destroys
   // |id2| and creates |id2_next|. No reference have been removed yet.
@@ -170,8 +163,8 @@
   EXPECT_NE(nullptr, manager().GetSurfaceForId(id2_next));
 
   // Add references to and from |id2_next|.
-  manager().AddSurfaceReference(id1, id2_next);
-  manager().AddSurfaceReference(id2_next, id3);
+  AddSurfaceReference(id1, id2_next);
+  AddSurfaceReference(id2_next, id3);
   EXPECT_THAT(GetReferencesFor(id2), UnorderedElementsAre(id1));
   EXPECT_THAT(GetReferencesFor(id2_next), UnorderedElementsAre(id1));
   EXPECT_THAT(GetReferencesFor(id3), UnorderedElementsAre(id2, id2_next));
@@ -192,12 +185,12 @@
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), id1);
-  manager().AddSurfaceReference(id1, id2);
-  manager().AddSurfaceReference(id2, id3);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(id1, id2);
+  AddSurfaceReference(id2, id3);
 
   // This reference forms a cycle between id2 and id3.
-  manager().AddSurfaceReference(id3, id2);
+  AddSurfaceReference(id3, id2);
 
   DestroySurface(id3);
   DestroySurface(id2);
@@ -212,12 +205,12 @@
   EXPECT_EQ(nullptr, manager().GetSurfaceForId(id3));
 }
 
-TEST_F(SurfaceManagerRefTest, CheckGC) {
+TEST_F(SurfaceManagerRefTest, SurfacesAreDeletedDuringGarbageCollection) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), id1);
-  manager().AddSurfaceReference(id1, id2);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(id1, id2);
 
   EXPECT_NE(nullptr, manager().GetSurfaceForId(id1));
   EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
@@ -238,14 +231,14 @@
   EXPECT_EQ(nullptr, manager().GetSurfaceForId(id1));
 }
 
-TEST_F(SurfaceManagerRefTest, CheckGCRecusiveFull) {
+TEST_F(SurfaceManagerRefTest, GarbageCollectionWorksRecusively) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
   SurfaceId id3 = CreateSurface(kFrameSink3, 1);
 
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), id1);
-  manager().AddSurfaceReference(id1, id2);
-  manager().AddSurfaceReference(id2, id3);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(id1, id2);
+  AddSurfaceReference(id2, id3);
 
   DestroySurface(id3);
   DestroySurface(id2);
@@ -266,43 +259,45 @@
   EXPECT_EQ(nullptr, manager().GetSurfaceForId(id3));
 }
 
-TEST_F(SurfaceManagerRefTest, TryDoubleAddReference) {
+TEST_F(SurfaceManagerRefTest, TryAddReferenceSameReferenceTwice) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), id1);
-  manager().AddSurfaceReference(id1, id2);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+  AddSurfaceReference(id1, id2);
   EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
   EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
 
   // The second request should be ignored without crashing.
-  manager().AddSurfaceReference(id1, id2);
+  AddSurfaceReference(id1, id2);
   EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
   EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
 }
 
-TEST_F(SurfaceManagerRefTest, TryAddSelfReference) {
-  SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+TEST_F(SurfaceManagerRefTest, AddingSelfReferenceFails) {
+  SurfaceId id1 = CreateSurface(kFrameSink2, 1);
 
   // A temporary reference must exist to |id1|.
-  EXPECT_THAT(GetReferencesFor(id1), SizeIs(1));
+  EXPECT_THAT(GetAllTempReferences(), ElementsAre(id1));
+  EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
+  EXPECT_THAT(GetReferencesFor(id1), IsEmpty());
 
   // Try to add a self reference. This should fail.
-  manager().AddSurfaceReference(id1, id1);
+  AddSurfaceReference(id1, id1);
 
-  // Adding a self reference should be ignored without crashing.
+  // Adding a self reference should be ignored without crashing. The temporary
+  // reference to |id1| must still exist.
+  EXPECT_THAT(GetAllTempReferences(), ElementsAre(id1));
   EXPECT_THAT(GetReferencesFrom(id1), IsEmpty());
-
-  // The temporary reference to |id1| must still exist.
-  EXPECT_THAT(GetReferencesFor(id1), SizeIs(1));
+  EXPECT_THAT(GetReferencesFor(id1), IsEmpty());
 }
 
-TEST_F(SurfaceManagerRefTest, TryRemoveBadReference) {
+TEST_F(SurfaceManagerRefTest, RemovingNonexistantReferenceFails) {
   SurfaceId id1 = CreateSurface(kFrameSink1, 1);
   SurfaceId id2 = CreateSurface(kFrameSink2, 1);
 
   // Removing non-existent reference should be ignored.
-  manager().AddSurfaceReference(id1, id2);
+  AddSurfaceReference(id1, id2);
   RemoveSurfaceReference(id2, id1);
   EXPECT_THAT(GetReferencesFrom(id1), SizeIs(1));
   EXPECT_THAT(GetReferencesFor(id2), SizeIs(1));
@@ -314,17 +309,15 @@
 
   // A temporary reference must be added to |surface_id|.
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));
-  EXPECT_THAT(GetReferencesFromRoot(), ElementsAre(surface_id));
 
   // Create |parent_id| and add a real reference from it to |surface_id|.
   const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
-  manager().AddSurfaceReference(parent_id, surface_id);
+  AddSurfaceReference(parent_id, surface_id);
 
   // The temporary reference to |surface_id| should be gone.
   // The only temporary reference should be to |parent_id|.
   // There must be a real reference from |parent_id| to |child_id|.
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
-  EXPECT_THAT(GetReferencesFromRoot(), ElementsAre(parent_id));
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id));
 }
 
@@ -334,15 +327,15 @@
 
   // Temporary reference should be added to |surface_id|.
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(surface_id));
-  EXPECT_THAT(GetReferencesFromRoot(), ElementsAre(surface_id));
 
   // Add a real reference from root to |surface_id|.
-  manager().AddSurfaceReference(manager().GetRootSurfaceId(), surface_id);
+  AddSurfaceReference(manager().GetRootSurfaceId(), surface_id);
 
-  // The temporary reference should be gone.
-  // There should now be a real reference from root to |surface_id|.
+  // The temporary reference should be gone and there should now be a surface
+  // reference from root to |surface_id|.
   EXPECT_TRUE(GetAllTempReferences().empty());
-  EXPECT_THAT(GetReferencesFromRoot(), ElementsAre(surface_id));
+  EXPECT_THAT(GetReferencesFrom(manager().GetRootSurfaceId()),
+              ElementsAre(surface_id));
 }
 
 TEST_F(SurfaceManagerRefTest, AddTwoSurfacesThenOneReference) {
@@ -353,12 +346,10 @@
   // Temporary reference should be added for both surfaces.
   EXPECT_THAT(GetAllTempReferences(),
               UnorderedElementsAre(surface_id1, surface_id2));
-  EXPECT_THAT(GetReferencesFromRoot(),
-              UnorderedElementsAre(surface_id1, surface_id2));
 
   // Create |parent_id| and add a real reference from it to |surface_id1|.
   const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
-  manager().AddSurfaceReference(parent_id, surface_id1);
+  AddSurfaceReference(parent_id, surface_id1);
 
   // Real reference must be added to |surface_id1| and the temporary reference
   // to it must be gone.
@@ -366,8 +357,6 @@
   // A temporary reference to |parent_id| must be created.
   EXPECT_THAT(GetAllTempReferences(),
               UnorderedElementsAre(parent_id, surface_id2));
-  EXPECT_THAT(GetReferencesFromRoot(),
-              UnorderedElementsAre(parent_id, surface_id2));
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
 }
 
@@ -380,26 +369,22 @@
 
   // Temporary references should be added for both surfaces and they should be
   // stored in the order of creation.
-  EXPECT_THAT(GetTempReferencesFor(surface_id1.frame_sink_id()),
-              ElementsAre(surface_id1.local_surface_id(),
-                          surface_id2.local_surface_id()));
-  EXPECT_THAT(GetReferencesFromRoot(),
+  EXPECT_THAT(GetAllTempReferences(),
               UnorderedElementsAre(surface_id1, surface_id2));
 
   // Create |parent_id| and add a reference from it to |surface_id2| which was
   // created later.
   const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
-  manager().AddSurfaceReference(parent_id, surface_id2);
+  AddSurfaceReference(parent_id, surface_id2);
 
   // The real reference should be added for |surface_id2| and the temporary
   // references to both |surface_id1| and |surface_id2| should be gone.
   // There should be a temporary reference to |parent_id|.
   EXPECT_THAT(GetAllTempReferences(), ElementsAre(parent_id));
-  EXPECT_THAT(GetReferencesFromRoot(), ElementsAre(parent_id));
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id2));
 }
 
-TEST_F(SurfaceManagerRefTest, RemoveFirstTempRefOnly) {
+TEST_F(SurfaceManagerRefTest, RemoveFirstTempReferenceOnly) {
   // Add two surfaces that have the same FrameSinkId. This would happen when a
   // client submits two CFs before parent submits a new CF.
   const SurfaceId surface_id1 = CreateSurface(kFrameSink2, 1);
@@ -407,25 +392,121 @@
 
   // Temporary references should be added for both surfaces and they should be
   // stored in the order of creation.
-  EXPECT_THAT(GetTempReferencesFor(surface_id1.frame_sink_id()),
-              ElementsAre(surface_id1.local_surface_id(),
-                          surface_id2.local_surface_id()));
-  EXPECT_THAT(GetReferencesFromRoot(),
+  EXPECT_THAT(GetAllTempReferences(),
               UnorderedElementsAre(surface_id1, surface_id2));
 
   // Create |parent_id| and add a reference from it to |surface_id1| which was
   // created earlier.
   const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
-  manager().AddSurfaceReference(parent_id, surface_id1);
+  AddSurfaceReference(parent_id, surface_id1);
 
   // The real reference should be added for |surface_id1| and its temporary
   // reference should be removed. The temporary reference for |surface_id2|
   // should remain. A temporary reference must be added for |parent_id|.
   EXPECT_THAT(GetAllTempReferences(),
               UnorderedElementsAre(parent_id, surface_id2));
-  EXPECT_THAT(GetReferencesFromRoot(),
-              UnorderedElementsAre(parent_id, surface_id2));
   EXPECT_THAT(GetReferencesFrom(parent_id), ElementsAre(surface_id1));
 }
 
+TEST_F(SurfaceManagerRefTest, SurfaceWithTemporaryReferenceIsNotDeleted) {
+  const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  AddSurfaceReference(manager().GetRootSurfaceId(), id1);
+
+  // We create |id2| and never add a real reference to it. This leaves the
+  // temporary reference.
+  const SurfaceId id2 = CreateSurface(kFrameSink2, 1);
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id2));
+  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
+
+  // Destroy both surfaces so they can be garbage collected. We remove the
+  // surface reference to |id1| which will run GarbageCollectSurfaces().
+  DestroySurface(id1);
+  DestroySurface(id2);
+  RemoveSurfaceReference(manager().GetRootSurfaceId(), id1);
+
+  // |id1| is destroyed and has no references, so it's deleted.
+  EXPECT_EQ(nullptr, manager().GetSurfaceForId(id1));
+
+  // |id2| is destroyed but has a temporary reference, it's not deleted.
+  EXPECT_NE(nullptr, manager().GetSurfaceForId(id2));
+}
+
+// Checks that when a temporary reference is assigned an owner, if the owner is
+// invalidated then the temporary reference is also removed.
+TEST_F(SurfaceManagerRefTest, InvalidateTempReferenceOwnerRemovesReference) {
+  // Surface |id1| should have a temporary reference on creation.
+  const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+  // |id1| should have a temporary reference after an owner is assigned.
+  manager().AssignTemporaryReference(id1, kFrameSink1);
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+  // When |kFrameSink1| is invalidated the temporary reference will be removed.
+  manager().InvalidateFrameSinkId(kFrameSink1);
+  ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+}
+
+// Checks that adding a surface reference clears the temporary reference and
+// ownership. Invalidating the old owner shouldn't do anything.
+TEST_F(SurfaceManagerRefTest, InvalidateHasNoEffectOnSurfaceReferences) {
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+  AddSurfaceReference(manager().GetRootSurfaceId(), parent_id);
+
+  const SurfaceId id1 = CreateSurface(kFrameSink2, 1);
+  manager().AssignTemporaryReference(id1, kFrameSink1);
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+  // Adding a real surface reference will remove the temporary reference.
+  AddSurfaceReference(parent_id, id1);
+  ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+  ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
+
+  // When |kFrameSink1| is invalidated it shouldn't change the surface
+  // references.
+  manager().InvalidateFrameSinkId(kFrameSink1);
+  ASSERT_THAT(GetReferencesFor(id1), UnorderedElementsAre(parent_id));
+}
+
+TEST_F(SurfaceManagerRefTest, CheckDropTemporaryReferenceWorks) {
+  const SurfaceId id1 = CreateSurface(kFrameSink1, 1);
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1));
+
+  // An example of why this could happen is the window server doesn't know the
+  // owner, maybe it has crashed and been cleanup already, and asks to drop the
+  // temporary reference.
+  manager().DropTemporaryReference(id1);
+  ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+}
+
+// Checks that we handle ownership and temporary references correctly when there
+// are multiple temporary references. This tests something like the parent
+// client crashing, so it's
+TEST_F(SurfaceManagerRefTest, TempReferencesWithClientCrash) {
+  const SurfaceId parent_id = CreateSurface(kFrameSink1, 1);
+  AddSurfaceReference(manager().GetRootSurfaceId(), parent_id);
+
+  const SurfaceId id1a = CreateSurface(kFrameSink2, 1);
+  const SurfaceId id1b = CreateSurface(kFrameSink2, 2);
+
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
+
+  // Assign |id1a| to |kFrameSink1|. This doesn't change the temporary
+  // reference, it just assigns as owner to it.
+  manager().AssignTemporaryReference(id1a, kFrameSink1);
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1a, id1b));
+
+  // If the parent client crashes then the CompositorFrameSink connection will
+  // be closed and the FrameSinkId invalidated. The temporary reference
+  // |kFrameSink1| owns to |id2a| will be removed.
+  manager().InvalidateFrameSinkId(kFrameSink1);
+  ASSERT_THAT(GetAllTempReferences(), UnorderedElementsAre(id1b));
+
+  // If the parent has crashed then the window server will have already removed
+  // it from the ServerWindow hierarchy and won't have an owner for |id2b|. The
+  // window server will ask to drop the reference instead.
+  manager().DropTemporaryReference(id1b);
+  ASSERT_THAT(GetAllTempReferences(), IsEmpty());
+}
+
 }  // namespace cc
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index a186f87..6972694 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -890,6 +890,13 @@
                 ForcedSigninProcessor.checkCanSignIn(ChromeActivity.this);
             }
         });
+        DeferredStartupHandler.getInstance().addDeferredTask(new Runnable() {
+            @Override
+            public void run() {
+                if (isActivityDestroyed() || mBottomSheet == null) return;
+                mBottomSheet.initializeDefaultContent();
+            }
+        });
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index 57a49d7..3ef166e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -260,6 +260,7 @@
         // Set up snippets
         NewTabPageAdapter newTabPageAdapter = new NewTabPageAdapter(mManager, mNewTabPageLayout,
                 mUiConfig, offlinePageBridge, mContextMenuManager, /* tileGroupDelegate = */ null);
+        newTabPageAdapter.refreshSuggestions();
         mRecyclerView.setAdapter(newTabPageAdapter);
 
         int scrollOffset;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index 676f009c..166985f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -175,6 +175,13 @@
         return mRoot.getItemCount();
     }
 
+    /** Resets suggestions, pulling the current state as known by the backend. */
+    public void refreshSuggestions() {
+        // The NTP Tiles already update when changes occurs, they don't need to be explicitly reset,
+        // unlike the cards.
+        mSections.refreshSuggestions();
+    }
+
     public int getAboveTheFoldPosition() {
         if (mAboveTheFoldView == null) return RecyclerView.NO_POSITION;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
index 2f3fe25..98e963a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -42,7 +42,6 @@
         mUiDelegate.getSuggestionsSource().setObserver(this);
         mUiDelegate.getMetricsReporter().setRanker(mSuggestionsRanker);
         mOfflinePageBridge = offlinePageBridge;
-        resetSections(/* alwaysAllowEmptySections = */ false);
 
         mUiDelegate.addDestructionObserver(new DestructionObserver() {
             @Override
@@ -183,6 +182,14 @@
 
     @Override
     public void onFullRefreshRequired() {
+        refreshSuggestions();
+    }
+
+    /**
+     * Resets all the sections, getting the current list of categories and the associated
+     * suggestions from the backend.
+     */
+    public void refreshSuggestions() {
         resetSections(/* alwaysAllowEmptySections = */false);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java
index ad72913..608f406 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBottomSheetContent.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.widget.BottomSheet;
+import org.chromium.chrome.browser.widget.BottomSheetObserver;
 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
 
 /**
@@ -60,10 +61,13 @@
 
         UiConfig uiConfig = new UiConfig(mRecyclerView);
 
+        // This mAdapter does not fetch until later requested, when the sheet is opened.
         NewTabPageAdapter adapter = new NewTabPageAdapter(mSuggestionsManager,
                 /* aboveTheFoldView = */ null, uiConfig, OfflinePageBridge.getForProfile(profile),
                 mContextMenuManager, mTileGroupDelegate);
         mRecyclerView.setAdapter(adapter);
+
+        activity.getBottomSheet().addObserver(new BottomSheetObserverImpl(adapter));
     }
 
     @Override
@@ -119,4 +123,31 @@
 
         return delegate;
     }
+
+    // TODO(dgn): Extract for public usage if it turns out that implementing a single method from
+    // the interface becomes common.
+    private static class BottomSheetObserverImpl implements BottomSheetObserver {
+        private final NewTabPageAdapter mAdapter;
+
+        public BottomSheetObserverImpl(NewTabPageAdapter adapter) {
+            mAdapter = adapter;
+        }
+
+        @Override
+        public void onSheetOpened() {
+            mAdapter.refreshSuggestions();
+        }
+
+        @Override
+        public void onSheetClosed() {}
+
+        @Override
+        public void onLoadUrl(String url) {}
+
+        @Override
+        public void onSheetOffsetChanged(float heightFraction) {}
+
+        @Override
+        public void onTransitionPeekToHalf(float transitionFraction) {}
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
index 8cb6a12..c84d09a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
@@ -391,6 +391,14 @@
         mBottomSheetContentContainer.addView(mPlaceholder, placeHolderParams);
     }
 
+    /** Initialise the default bottom sheet content. */
+    public void initializeDefaultContent() {
+        assert mSheetContent == null;
+        mSuggestionsContent = new SuggestionsBottomSheetContent(
+                mTabModelSelector.getCurrentTab().getActivity(), this, mTabModelSelector);
+        showContent(mSuggestionsContent);
+    }
+
     @Override
     public int loadUrl(LoadUrlParams params, boolean incognito) {
         for (BottomSheetObserver o : mObservers) o.onLoadUrl(params.getUrl());
@@ -450,11 +458,6 @@
      * A notification that the sheet is exiting the peek state into one that shows content.
      */
     private void onSheetOpened() {
-        if (mSuggestionsContent == null) {
-            mSuggestionsContent = new SuggestionsBottomSheetContent(
-                    mTabModelSelector.getCurrentTab().getActivity(), this, mTabModelSelector);
-        }
-
         showContent(mSuggestionsContent);
 
         for (BottomSheetObserver o : mObservers) o.onSheetOpened();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
index 6ec28c3..13ba8b36 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/snippets/ArticleSnippetsTest.java
@@ -81,6 +81,7 @@
                 mAdapter = new NewTabPageAdapter(mUiDelegate, aboveTheFold, mUiConfig,
                         OfflinePageBridge.getForProfile(Profile.getLastUsedProfile()),
                         /* contextMenuManager = */ null, /* tileGroupDelegate = */ null);
+                mAdapter.refreshSuggestions();
                 mRecyclerView.setAdapter(mAdapter);
             }
         });
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
index 3ce31b2..f8fda0fd 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java
@@ -903,7 +903,7 @@
 
         // On Sign in, we should reset the sections, bring back suggestions instead of the All
         // Dismissed item.
-        mAdapter.getSectionListForTesting().onFullRefreshRequired();
+        mAdapter.getSectionListForTesting().refreshSuggestions();
         when(mMockSigninManager.isSignInAllowed()).thenReturn(true);
         signinObserver.onSignedIn();
         // Adapter content:
@@ -989,7 +989,9 @@
 
     private void reloadNtp() {
         mAdapter = new NewTabPageAdapter(mUiDelegate, mock(View.class), makeUiConfig(),
-                mOfflinePageBridge, mock(ContextMenuManager.class), /* tileGroupDelegate = */ null);
+                mOfflinePageBridge, mock(ContextMenuManager.class), /* tileGroupDelegate =
+                */ null);
+        mAdapter.refreshSuggestions();
     }
 
     private void assertArticlesEqual(List<SnippetArticle> articles, int start, int end) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
index 5227ae5..3f4c828 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/SectionListTest.java
@@ -97,6 +97,7 @@
         List<SnippetArticle> suggestions2 = registerCategory(mSuggestionSource, CATEGORY2, 4);
 
         SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge);
+        sectionList.refreshSuggestions();
 
         bindViewHolders(sectionList);
 
@@ -131,6 +132,7 @@
         List<SnippetArticle> suggestions2 = registerCategory(mSuggestionSource, CATEGORY2, 4);
 
         SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge);
+        sectionList.refreshSuggestions();
 
         bindViewHolders(sectionList, 0, 5); // Bind until after the third item from |suggestions1|.
 
@@ -226,6 +228,7 @@
                 new CategoryInfoBuilder(CATEGORY2).withViewAllAction().build(), 3);
 
         SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge);
+        sectionList.refreshSuggestions();
         bindViewHolders(sectionList);
 
         assertThat(sectionList.getSectionForTesting(CATEGORY1)
@@ -246,6 +249,7 @@
                 new CategoryInfoBuilder(CATEGORY2).withViewAllAction().build(), 3);
 
         SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge);
+        sectionList.refreshSuggestions();
         bindViewHolders(sectionList);
 
         ArgumentCaptor<DestructionObserver> argument =
@@ -271,6 +275,7 @@
         registerCategory(mSuggestionSource, KnownCategories.ARTICLES, 1);
 
         SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge);
+        sectionList.refreshSuggestions();
         SuggestionsSection articles = sectionList.getSectionForTesting(KnownCategories.ARTICLES);
         assertFalse(articles.getHeaderItemForTesting().isVisible());
     }
@@ -281,6 +286,7 @@
         registerCategory(mSuggestionSource, CATEGORY1, 1);
 
         SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge);
+        sectionList.refreshSuggestions();
         SuggestionsSection section = sectionList.getSectionForTesting(CATEGORY1);
         assertTrue(section.getHeaderItemForTesting().isVisible());
     }
@@ -292,6 +298,7 @@
         registerCategory(mSuggestionSource, CATEGORY1, 1);
 
         SectionList sectionList = new SectionList(mUiDelegate, mOfflinePageBridge);
+        sectionList.refreshSuggestions();
         SuggestionsSection articles = sectionList.getSectionForTesting(KnownCategories.ARTICLES);
         assertTrue(articles.getHeaderItemForTesting().isVisible());
     }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index f4f4ae9..1c9d173a0 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -14578,6 +14578,12 @@
     </if>
 
     <!-- Web payments -->
+    <message name="IDS_FLAGS_WEB_PAYMENTS_NAME" desc="Name of the flag to enable Web Payments API." translateable="false">
+      Web Payments
+    </message>
+    <message name="IDS_FLAGS_WEB_PAYMENTS_DESCRIPTION" desc="Description for the flag to enable Web Payments API" translateable="false">
+      Enable Web Payments API integration, a JavaScript API for merchants.
+    </message>
     <if expr="is_android">
       <message name="IDS_FLAGS_ENABLE_ANDROID_PAY_INTEGRATION_V1_NAME" desc="Name of the flag to enable the first version of Android Pay integration" translateable="false">
         Enable Android Pay v1
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index e9dfb5e..bc9d0c29 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1692,8 +1692,6 @@
       "guest_view/web_view/context_menu_content_type_web_view.h",
       "media/capture_access_handler_base.cc",
       "media/capture_access_handler_base.h",
-      "media/cast_transport_host_filter.cc",
-      "media/cast_transport_host_filter.h",
       "media/extension_media_access_handler.cc",
       "media/extension_media_access_handler.h",
       "media/webrtc/desktop_capture_access_handler.cc",
@@ -1868,6 +1866,12 @@
       "sync_file_system/task_logger.cc",
       "sync_file_system/task_logger.h",
     ]
+    if (enable_media_router) {
+      sources += [
+        "media/cast_transport_host_filter.cc",
+        "media/cast_transport_host_filter.h",
+      ]
+    }
     public_deps += [ "//chrome/browser/extensions" ]
     allow_circular_includes_from += [ "//chrome/browser/extensions" ]
     deps += [
@@ -2509,10 +2513,6 @@
       "profiles/profile_statistics_common.h",
       "profiles/profile_statistics_factory.cc",
       "profiles/profile_statistics_factory.h",
-      "signin/cross_device_promo.cc",
-      "signin/cross_device_promo.h",
-      "signin/cross_device_promo_factory.cc",
-      "signin/cross_device_promo_factory.h",
       "signin/signin_global_error.cc",
       "signin/signin_global_error.h",
       "signin/signin_global_error_factory.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3670a71..da41720 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -2067,6 +2067,9 @@
     {"enable-framebusting-needs-sameorigin-or-usergesture",
      IDS_FLAGS_FRAMEBUSTING_NAME, IDS_FLAGS_FRAMEBUSTING_DESCRIPTION, kOsAll,
      FEATURE_VALUE_TYPE(features::kFramebustingNeedsSameOriginOrUserGesture)},
+    {"web-payments", IDS_FLAGS_WEB_PAYMENTS_NAME,
+     IDS_FLAGS_WEB_PAYMENTS_DESCRIPTION, kOsDesktop,
+     FEATURE_VALUE_TYPE(features::kWebPayments)},
 #if defined(OS_ANDROID)
     {"enable-android-pay-integration-v1",
      IDS_FLAGS_ENABLE_ANDROID_PAY_INTEGRATION_V1_NAME,
diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc
index d1b4381..84e26ca 100644
--- a/chrome/browser/certificate_manager_model.cc
+++ b/chrome/browser/certificate_manager_model.cc
@@ -115,14 +115,13 @@
 
 void CertificateManagerModel::Refresh() {
   DVLOG(1) << "refresh started";
-  net::CryptoModuleList modules;
+  std::vector<crypto::ScopedPK11Slot> modules;
   cert_db_->ListModules(&modules, false);
   DVLOG(1) << "refresh waiting for unlocking...";
   chrome::UnlockSlotsIfNecessary(
-      modules,
-      chrome::kCryptoModulePasswordListCerts,
+      std::move(modules), chrome::kCryptoModulePasswordListCerts,
       net::HostPortPair(),  // unused.
-      NULL, // TODO(mattm): supply parent window.
+      NULL,                 // TODO(mattm): supply parent window.
       base::Bind(&CertificateManagerModel::RefreshSlotsUnlocked,
                  base::Unretained(this)));
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 8adb92cdde..16647100 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -322,7 +322,6 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/accessibility/animation_policy_prefs.h"
 #include "chrome/browser/extensions/chrome_content_browser_client_extensions_part.h"
-#include "chrome/browser/media/cast_transport_host_filter.h"
 #include "chrome/browser/speech/extension_api/tts_engine_extension_api.h"
 #include "components/guest_view/browser/guest_view_base.h"
 #include "components/guest_view/browser/guest_view_manager.h"
@@ -342,6 +341,10 @@
 #include "extensions/common/switches.h"
 #endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS) && defined(ENABLE_MEDIA_ROUTER)
+#include "chrome/browser/media/cast_transport_host_filter.h"
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS) && defined(ENABLE_MEDIA_ROUTER)
+
 #if BUILDFLAG(ENABLE_PLUGINS)
 #include "chrome/browser/plugins/chrome_content_browser_client_plugins_part.h"
 #include "chrome/browser/plugins/flash_download_interception.h"
@@ -1158,7 +1161,7 @@
   Profile* profile = Profile::FromBrowserContext(host->GetBrowserContext());
   host->AddFilter(new ChromeRenderMessageFilter(
       id, profile, host->GetStoragePartition()->GetServiceWorkerContext()));
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS) && defined(ENABLE_MEDIA_ROUTER)
   host->AddFilter(new cast::CastTransportHostFilter);
 #endif
 #if BUILDFLAG(ENABLE_PRINTING)
@@ -3178,7 +3181,8 @@
                    web_contents->GetJavaInterfaces()->GetWeakPtr()));
   }
 #else
-  if (AreExperimentalWebPlatformFeaturesEnabled()) {
+  if (AreExperimentalWebPlatformFeaturesEnabled() &&
+      base::FeatureList::IsEnabled(features::kWebPayments)) {
     content::WebContents* web_contents =
         content::WebContents::FromRenderFrameHost(render_frame_host);
     if (web_contents) {
diff --git a/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.cc b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.cc
index 58bedb7..48e114ff 100644
--- a/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.cc
+++ b/chrome/browser/chromeos/arc/auth/arc_robot_auth_code_fetcher.cc
@@ -58,6 +58,8 @@
       fetch_request_job_->GetRequest()->mutable_service_api_access_request();
   request->set_oauth2_client_id(kAndoidClientId);
   request->add_auth_scope(GaiaConstants::kAnyApiOAuth2Scope);
+  request->set_device_type(
+      enterprise_management::DeviceServiceApiAccessRequest::ANDROIDOS);
 
   fetch_request_job_->Start(
       base::Bind(&ArcRobotAuthCodeFetcher::OnFetchRobotAuthCodeCompleted,
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
index 595ea45..4b785c2 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
@@ -159,13 +159,7 @@
       << message_;
 }
 
-// http://crbug.com/691449
-#if defined(OS_CHROMEOS)
-#define MAYBE_ReadFile DISABLED_ReadFile
-#else
-#define MAYBE_ReadFile ReadFile
-#endif
-IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, MAYBE_ReadFile) {
+IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, ReadFile) {
   ASSERT_TRUE(RunPlatformAppTestWithFlags("file_system_provider/read_file",
                                           kFlagLoadAsComponent))
       << message_;
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index fa5f9b65..94dee68 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -829,7 +829,6 @@
     "//chrome/app/theme:theme_resources",
     "//chrome/app/vector_icons",
     "//chrome/browser/devtools",
-    "//chrome/browser/media/router",
     "//chrome/common",
     "//chrome/common/extensions/api:api_registration",
     "//chrome/common/extensions/api:extensions_features",
@@ -929,6 +928,10 @@
     "//url",
   ]
 
+  if (enable_media_router) {
+    deps += [ "//chrome/browser/media/router" ]
+  }
+
   if (!is_chromeos) {
     sources += [ "chrome_kiosk_delegate.cc" ]
   }
diff --git a/chrome/browser/extensions/api/tab_capture/offscreen_tab.cc b/chrome/browser/extensions/api/tab_capture/offscreen_tab.cc
index aec99ad9..3adbdf05 100644
--- a/chrome/browser/extensions/api/tab_capture/offscreen_tab.cc
+++ b/chrome/browser/extensions/api/tab_capture/offscreen_tab.cc
@@ -10,7 +10,6 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
-#include "chrome/browser/media/router/receiver_presentation_service_delegate_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/web_contents_sizer.h"
 #include "content/public/browser/render_view_host.h"
@@ -20,6 +19,10 @@
 #include "extensions/browser/extension_host.h"
 #include "extensions/browser/process_manager.h"
 
+#if defined(ENABLE_MEDIA_ROUTER)
+#include "chrome/browser/media/router/receiver_presentation_service_delegate_impl.h"  // nogncheck
+#endif
+
 using content::WebContents;
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::OffscreenTabsOwner);
@@ -118,6 +121,7 @@
   // automatically unmuted, but will be captured into the MediaStream.
   offscreen_tab_web_contents_->SetAudioMuted(true);
 
+#if defined(ENABLE_MEDIA_ROUTER)
   if (!optional_presentation_id.empty()) {
     DVLOG(1) << " Register with ReceiverPresentationServiceDelegateImpl, "
              << "[presentation_id]: " << optional_presentation_id;
@@ -134,6 +138,7 @@
       render_view_host->UpdateWebkitPreferences(web_prefs);
     }
   }
+#endif  // defined(ENABLE_MEDIA_ROUTER)
 
   // Navigate to the initial URL.
   content::NavigationController::LoadURLParams load_params(start_url_);
diff --git a/chrome/browser/extensions/chrome_mojo_service_registration.cc b/chrome/browser/extensions/chrome_mojo_service_registration.cc
index c280f2c14..0e35f1e 100644
--- a/chrome/browser/extensions/chrome_mojo_service_registration.cc
+++ b/chrome/browser/extensions/chrome_mojo_service_registration.cc
@@ -14,8 +14,8 @@
 #include "services/service_manager/public/cpp/interface_registry.h"
 
 #if defined(ENABLE_MEDIA_ROUTER)
-#include "chrome/browser/media/router/media_router_feature.h"
-#include "chrome/browser/media/router/mojo/media_router_mojo_impl.h"
+#include "chrome/browser/media/router/media_router_feature.h"  // nogncheck
+#include "chrome/browser/media/router/mojo/media_router_mojo_impl.h"  // nogncheck
 #endif
 
 namespace extensions {
diff --git a/chrome/browser/payments/site_per_process_payments_browsertest.cc b/chrome/browser/payments/site_per_process_payments_browsertest.cc
index 330c4a3..cc1448e5c 100644
--- a/chrome/browser/payments/site_per_process_payments_browsertest.cc
+++ b/chrome/browser/payments/site_per_process_payments_browsertest.cc
@@ -12,6 +12,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
@@ -34,6 +35,8 @@
     command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
     command_line->AppendSwitch(
         switches::kEnableExperimentalWebPlatformFeatures);
+    command_line->AppendSwitchASCII(switches::kEnableFeatures,
+                                    features::kWebPayments.name);
     // Append --site-per-process flag.
     content::IsolateAllSitesForTesting(command_line);
   }
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc
index a2916ba..2069dbd 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
@@ -606,6 +606,9 @@
       BrowserThread::IO, FROM_HERE,
       base::Bind(&ResourcePrefetcherManager::MaybeAddPrefetch,
                  prefetch_manager_, url, prediction.subresource_urls));
+
+  if (observer_)
+    observer_->OnPrefetchingStarted(url);
 }
 
 void ResourcePrefetchPredictor::StopPrefetching(const GURL& url) {
@@ -625,6 +628,9 @@
       BrowserThread::IO, FROM_HERE,
       base::Bind(&ResourcePrefetcherManager::MaybeRemovePrefetch,
                  prefetch_manager_, url));
+
+  if (observer_)
+    observer_->OnPrefetchingStopped(url);
 }
 
 void ResourcePrefetchPredictor::OnPrefetchingFinished(
@@ -675,15 +681,22 @@
 void ResourcePrefetchPredictor::OnMainFrameResponse(
     const URLRequestSummary& response) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (initialization_state_ != INITIALIZED)
-    return;
+  DCHECK_EQ(INITIALIZED, initialization_state_);
 
-  StopPrefetching(response.navigation_id.main_frame_url);
+  NavigationMap::iterator nav_it =
+      inflight_navigations_.find(response.navigation_id);
+  if (nav_it != inflight_navigations_.end()) {
+    // To match an URL in StartPrefetching().
+    StopPrefetching(nav_it->second->initial_url);
+  } else {
+    StopPrefetching(response.navigation_id.main_frame_url);
+  }
 }
 
 void ResourcePrefetchPredictor::OnMainFrameRedirect(
     const URLRequestSummary& response) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(INITIALIZED, initialization_state_);
 
   const GURL& main_frame_url = response.navigation_id.main_frame_url;
   std::unique_ptr<PageRequestSummary> summary;
@@ -715,6 +728,7 @@
 void ResourcePrefetchPredictor::OnSubresourceResponse(
     const URLRequestSummary& response) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(INITIALIZED, initialization_state_);
 
   NavigationMap::const_iterator nav_it =
         inflight_navigations_.find(response.navigation_id);
@@ -728,6 +742,7 @@
 void ResourcePrefetchPredictor::OnNavigationComplete(
     const NavigationID& nav_id_without_timing_info) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(INITIALIZED, initialization_state_);
 
   NavigationMap::iterator nav_it =
       inflight_navigations_.find(nav_id_without_timing_info);
@@ -903,10 +918,16 @@
 
   for (auto it = inflight_prefetches_.begin();
        it != inflight_prefetches_.end();) {
-    if (time_now - it->second > max_navigation_age)
+    base::TimeDelta prefetch_age = time_now - it->second;
+    if (prefetch_age > max_navigation_age) {
+      // It goes to the last bucket meaning that the duration was unlimited.
+      UMA_HISTOGRAM_TIMES(
+          internal::kResourcePrefetchPredictorPrefetchingDurationHistogram,
+          prefetch_age);
       it = inflight_prefetches_.erase(it);
-    else
+    } else {
       ++it;
+    }
   }
 
   // Remove old prefetches that haven't been claimed.
diff --git a/chrome/browser/predictors/resource_prefetch_predictor.h b/chrome/browser/predictors/resource_prefetch_predictor.h
index 60f057c..1bfdf871 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor.h
+++ b/chrome/browser/predictors/resource_prefetch_predictor.h
@@ -429,13 +429,17 @@
   // De-registers itself from |predictor_| on destruction.
   virtual ~TestObserver();
 
+  virtual void OnPredictorInitialized() {}
+
   virtual void OnNavigationLearned(
       size_t url_visit_count,
       const ResourcePrefetchPredictor::PageRequestSummary& summary) {}
 
-  virtual void OnPrefetchingFinished(const GURL& main_frame_url) {}
+  virtual void OnPrefetchingStarted(const GURL& main_frame_url) {}
 
-  virtual void OnPredictorInitialized() {}
+  virtual void OnPrefetchingStopped(const GURL& main_frame_url) {}
+
+  virtual void OnPrefetchingFinished(const GURL& main_frame_url) {}
 
  protected:
   // |predictor| must be non-NULL and has to outlive the TestObserver.
diff --git a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
index 872022f..44f7cd7 100644
--- a/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
+++ b/chrome/browser/predictors/resource_prefetch_predictor_browsertest.cc
@@ -258,17 +258,30 @@
 };
 
 // Helper class to track and allow waiting for a single OnPrefetchingFinished
-// event. No learning events should be fired while this observer is active.
+// event. Checks also that {Start,Stop}Prefetching are called with the right
+// argument.
 class PrefetchingObserver : public TestObserver {
  public:
   PrefetchingObserver(ResourcePrefetchPredictor* predictor,
-                      const GURL& expected_main_frame_url)
-      : TestObserver(predictor), main_frame_url_(expected_main_frame_url) {}
+                      const GURL& expected_main_frame_url,
+                      bool is_learning_allowed)
+      : TestObserver(predictor),
+        main_frame_url_(expected_main_frame_url),
+        is_learning_allowed_(is_learning_allowed) {}
 
   // TestObserver:
   void OnNavigationLearned(size_t url_visit_count,
                            const PageRequestSummary& summary) override {
-    ADD_FAILURE() << "Prefetching shouldn't activate learning";
+    if (!is_learning_allowed_)
+      ADD_FAILURE() << "Prefetching shouldn't activate learning";
+  }
+
+  void OnPrefetchingStarted(const GURL& main_frame_url) override {
+    EXPECT_EQ(main_frame_url_, main_frame_url);
+  }
+
+  void OnPrefetchingStopped(const GURL& main_frame_url) override {
+    EXPECT_EQ(main_frame_url_, main_frame_url);
   }
 
   void OnPrefetchingFinished(const GURL& main_frame_url) override {
@@ -281,6 +294,7 @@
  private:
   base::RunLoop run_loop_;
   GURL main_frame_url_;
+  bool is_learning_allowed_;
 
   DISALLOW_COPY_AND_ASSIGN(PrefetchingObserver);
 };
@@ -381,8 +395,18 @@
       navigation_id_history_.insert(nav);
   }
 
+  void NavigateToURLAndCheckPrefetching(const GURL& main_frame_url) {
+    PrefetchingObserver observer(predictor_, main_frame_url, true);
+    ui_test_utils::NavigateToURL(browser(), main_frame_url);
+    observer.Wait();
+    for (auto& kv : resources_) {
+      if (kv.second.is_observable)
+        kv.second.request.was_cached = true;
+    }
+  }
+
   void PrefetchURL(const GURL& main_frame_url) {
-    PrefetchingObserver observer(predictor_, main_frame_url);
+    PrefetchingObserver observer(predictor_, main_frame_url, false);
     predictor_->StartPrefetching(main_frame_url, PrefetchOrigin::EXTERNAL);
     observer.Wait();
     for (auto& kv : resources_) {
@@ -637,6 +661,21 @@
   std::set<NavigationID> navigation_id_history_;
 };
 
+// Subclass to test PrefetchOrigin::NAVIGATION.
+class ResourcePrefetchPredictorPrefetchingBrowserTest
+    : public ResourcePrefetchPredictorBrowserTest {
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII("force-fieldtrials", "trial/group");
+    std::string parameter = base::StringPrintf(
+        "trial.group:%s/%s", kModeParamName, kPrefetchingMode);
+    command_line->AppendSwitchASCII("force-fieldtrial-params", parameter);
+    std::string enabled_feature = base::StringPrintf(
+        "%s<trial", kSpeculativeResourcePrefetchingFeatureName);
+    command_line->AppendSwitchASCII("enable-features", enabled_feature);
+  }
+};
+
 IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, Simple) {
   // These resources have default priorities that correspond to
   // blink::typeToPriority function.
@@ -899,4 +938,43 @@
   NavigateToURLAndCheckSubresourcesAllCached(GetURL(kHtmlSubresourcesPath));
 }
 
+// Makes sure that {Stop,Start}Prefetching are called with the same argument.
+IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorPrefetchingBrowserTest,
+                       Simple) {
+  AddResource(GetURL(kImagePath), content::RESOURCE_TYPE_IMAGE, net::LOWEST);
+  AddResource(GetURL(kStylePath), content::RESOURCE_TYPE_STYLESHEET,
+              net::HIGHEST);
+  AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM);
+  AddResource(GetURL(kFontPath), content::RESOURCE_TYPE_FONT_RESOURCE,
+              net::HIGHEST);
+
+  GURL main_frame_url = GetURL(kHtmlSubresourcesPath);
+  NavigateToURLAndCheckSubresources(main_frame_url);
+  ClearCache();
+  NavigateToURLAndCheckSubresources(main_frame_url);
+  ClearCache();
+  NavigateToURLAndCheckPrefetching(main_frame_url);
+}
+
+// Makes sure that {Stop,Start}Prefetching are called with the same argument in
+// presence of redirect.
+IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorPrefetchingBrowserTest,
+                       Redirect) {
+  GURL initial_url = embedded_test_server()->GetURL(kFooHost, kRedirectPath);
+  AddRedirectChain(initial_url, {{net::HTTP_MOVED_PERMANENTLY,
+                                  GetURL(kHtmlSubresourcesPath)}});
+  AddResource(GetURL(kImagePath), content::RESOURCE_TYPE_IMAGE, net::LOWEST);
+  AddResource(GetURL(kStylePath), content::RESOURCE_TYPE_STYLESHEET,
+              net::HIGHEST);
+  AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM);
+  AddResource(GetURL(kFontPath), content::RESOURCE_TYPE_FONT_RESOURCE,
+              net::HIGHEST);
+
+  NavigateToURLAndCheckSubresources(initial_url);
+  ClearCache();
+  NavigateToURLAndCheckSubresources(initial_url);
+  ClearCache();
+  NavigateToURLAndCheckPrefetching(initial_url);
+}
+
 }  // namespace predictors
diff --git a/chrome/browser/prefs/pref_service_syncable_util.cc b/chrome/browser/prefs/pref_service_syncable_util.cc
index 68c2f27..2a40fa9e 100644
--- a/chrome/browser/prefs/pref_service_syncable_util.cc
+++ b/chrome/browser/prefs/pref_service_syncable_util.cc
@@ -36,7 +36,9 @@
   // or behavior of the user should have this property.
   std::vector<const char*> overlay_pref_names;
   overlay_pref_names.push_back(prefs::kBrowserWindowPlacement);
+#if defined(ENABLE_MEDIA_ROUTER)
   overlay_pref_names.push_back(prefs::kMediaRouterTabMirroringSources);
+#endif
   overlay_pref_names.push_back(prefs::kSaveFileDefaultDirectory);
 #if defined(OS_ANDROID)
   overlay_pref_names.push_back(proxy_config::prefs::kProxy);
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index d775c529..77e005b 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -115,10 +115,6 @@
 #endif
 #endif
 
-#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-#include "chrome/browser/signin/cross_device_promo_factory.h"
-#endif
-
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/printer_detector/printer_detector_factory.h"
 #include "chrome/browser/chromeos/printing/cups_print_job_manager_factory.h"
@@ -216,9 +212,6 @@
   CloudPrintProxyServiceFactory::GetInstance();
 #endif
   CookieSettingsFactory::GetInstance();
-#if !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
-  CrossDevicePromoFactory::GetInstance();
-#endif
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   ExtensionWelcomeNotificationFactory::GetInstance();
 #endif
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index c3707d93..eda7c36 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -49,8 +49,6 @@
 #include "chrome/browser/signin/account_fetcher_service_factory.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
-#include "chrome/browser/signin/cross_device_promo.h"
-#include "chrome/browser/signin/cross_device_promo_factory.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
@@ -1634,13 +1632,6 @@
     ProfileAttributesEntry* entry;
     if (GetProfileAttributesStorage().
             GetProfileAttributesWithPath(last_active->GetPath(), &entry)) {
-#if !defined(OS_CHROMEOS)
-      // Incognito Profiles don't have ProfileKeyedServices.
-      if (!last_active->IsOffTheRecord()) {
-        CrossDevicePromoFactory::GetForProfile(last_active)->
-            MaybeBrowsingSessionStarted(entry->GetActiveTime());
-      }
-#endif
       entry->SetActiveTimeToNow();
     }
   }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index f86db71..0589a34 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -853,6 +853,13 @@
           ContextMenuContentType::ITEM_GROUP_PRINT_PREVIEW)) {
     AppendPrintPreviewItems();
   }
+
+  // Remove any redundant trailing separator.
+  if (menu_model_.GetItemCount() > 0 &&
+      menu_model_.GetTypeAt(menu_model_.GetItemCount() - 1) ==
+          ui::MenuModel::TYPE_SEPARATOR) {
+    menu_model_.RemoveItemAt(menu_model_.GetItemCount() - 1);
+  }
 }
 
 Profile* RenderViewContextMenu::GetProfile() {
@@ -1297,10 +1304,12 @@
 }
 
 void RenderViewContextMenu::AppendMediaRouterItem() {
+#if defined(ENABLE_MEDIA_ROUTER)
   if (media_router::MediaRouterEnabled(browser_context_)) {
     menu_model_.AddItemWithStringId(IDC_ROUTE_MEDIA,
                                     IDS_MEDIA_ROUTER_MENU_ITEM_TITLE);
   }
+#endif  // defined(ENABLE_MEDIA_ROUTER)
 }
 
 void RenderViewContextMenu::AppendRotationItems() {
@@ -2142,6 +2151,7 @@
 }
 
 bool RenderViewContextMenu::IsRouteMediaEnabled() const {
+#if defined(ENABLE_MEDIA_ROUTER)
   if (!media_router::MediaRouterEnabled(browser_context_))
     return false;
 
@@ -2161,6 +2171,9 @@
   const web_modal::WebContentsModalDialogManager* manager =
       web_modal::WebContentsModalDialogManager::FromWebContents(web_contents);
   return !manager || !manager->IsDialogActive();
+#else
+  return false;
+#endif  // defined(ENABLE_MEDIA_ROUTER)
 }
 
 bool RenderViewContextMenu::IsOpenLinkOTREnabled() const {
diff --git a/chrome/browser/signin/cross_device_promo.cc b/chrome/browser/signin/cross_device_promo.cc
deleted file mode 100644
index 889b75d..0000000
--- a/chrome/browser/signin/cross_device_promo.cc
+++ /dev/null
@@ -1,459 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/cross_device_promo.h"
-
-#include <stdint.h>
-
-#include "base/metrics/histogram_macros.h"
-#include "base/rand_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/time/time.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_client.h"
-#include "components/signin/core/browser/signin_manager.h"
-#include "components/signin/core/browser/signin_metrics.h"
-#include "components/variations/variations_associated_data.h"
-
-namespace {
-
-// Helper method to set a |local_parameter| to the result of fetching a
-// |variation_parameter| from the variation seed, converting to an int, and then
-// applying |conversion| to create a TimeDelta. Returns false if the
-// |variation_parameter| was not found or the string could not be parsed to an
-// int.
-bool SetParameterFromVariation(
-    const std::string& variation_parameter,
-    base::Callback<base::TimeDelta(int)> conversion,
-    base::TimeDelta* local_parameter) {
-  std::string parameter_as_string = variations::GetVariationParamValue(
-      CrossDevicePromo::kCrossDevicePromoFieldTrial, variation_parameter);
-  if (parameter_as_string.empty())
-    return false;
-
-  int parameter_as_int;
-  if (!base::StringToInt(parameter_as_string, &parameter_as_int))
-    return false;
-
-  *local_parameter = conversion.Run(parameter_as_int);
-  return true;
-}
-
-}  // namespace
-
-// static
-const char CrossDevicePromo::kCrossDevicePromoFieldTrial[] = "CrossDevicePromo";
-const char CrossDevicePromo::kParamHoursBetweenDeviceActivityChecks[] =
-    "HoursBetweenDeviceActivityChecks";
-const char CrossDevicePromo::kParamDaysToVerifySingleUserProfile[] =
-    "DaysToVerifySingleUserProfile";
-const char CrossDevicePromo::kParamMinutesBetweenBrowsingSessions[] =
-    "MinutesBetweenBrowsingSessions";
-const char CrossDevicePromo::kParamMinutesMaxContextSwitchDuration[] =
-    "MinutesMaxContextSwitchDuration";
-const char CrossDevicePromo::kParamRPCThrottle[] =
-    "RPCThrottle";
-
-CrossDevicePromo::CrossDevicePromo(
-    SigninManager* signin_manager,
-    GaiaCookieManagerService* cookie_manager_service,
-    SigninClient* signin_client,
-    PrefService* pref_service)
-    : initialized_(false),
-      signin_manager_(signin_manager),
-      cookie_manager_service_(cookie_manager_service),
-      prefs_(pref_service),
-      signin_client_(signin_client),
-      is_throttled_(true),
-      start_last_browsing_session_(base::Time()) {
-  VLOG(1) << "CrossDevicePromo::CrossDevicePromo.";
-  DCHECK(signin_manager_);
-  DCHECK(cookie_manager_service_);
-  DCHECK(prefs_);
-  DCHECK(signin_client_);
-  Init();
-}
-
-CrossDevicePromo::~CrossDevicePromo() {
-}
-
-void CrossDevicePromo::Shutdown() {
-  VLOG(1) << "CrossDevicePromo::Shutdown.";
-  UnregisterForCookieChanges();
-  if (start_last_browsing_session_ != base::Time())
-    signin_metrics::LogBrowsingSessionDuration(start_last_browsing_session_);
-}
-
-void CrossDevicePromo::AddObserver(CrossDevicePromo::Observer* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void CrossDevicePromo::RemoveObserver(CrossDevicePromo::Observer* observer) {
-  observer_list_.RemoveObserver(observer);
-}
-
-void CrossDevicePromo::OnGaiaAccountsInCookieUpdated(
-    const std::vector<gaia::ListedAccount>& accounts,
-    const std::vector<gaia::ListedAccount>& signed_out_accounts,
-    const GoogleServiceAuthError& error) {
-  VLOG(1) << "CrossDevicePromo::OnGaiaAccountsInCookieUpdated. "
-          << accounts.size() << " accounts with auth error " << error.state();
-  if (error.state() != GoogleServiceAuthError::State::NONE)
-    return;
-
-  const bool single_account = accounts.size() == 1;
-  const bool has_pref =
-      prefs_->HasPrefPath(prefs::kCrossDevicePromoObservedSingleAccountCookie);
-  if (!single_account && has_pref) {
-    prefs_->ClearPref(prefs::kCrossDevicePromoObservedSingleAccountCookie);
-    MarkPromoShouldNotBeShown();
-  } else if (single_account && !has_pref) {
-    SetTimePref(prefs::kCrossDevicePromoObservedSingleAccountCookie,
-                base::Time::Now());
-  }
-}
-
-void CrossDevicePromo::OnFetchDeviceActivitySuccess(
-    const std::vector<DeviceActivityFetcher::DeviceActivity>& devices) {
-  VLOG(1) << "CrossDevicePromo::OnFetchDeviceActivitySuccess. "
-          << devices.size() << " devices.";
-  DetermineEligibilityFromDeviceActivity(devices);
-
-  // |device_activity_fetcher_| must be destroyed last as that object is what
-  // called OnFetchDeviceActivitySuccess().
-  // TODO(mlerman): Mark the DeviceActivityFetcher as stopped() or completed()
-  // rather than deleting the object.
-  device_activity_fetcher_.reset();
-}
-
-void CrossDevicePromo::OnFetchDeviceActivityFailure() {
-  VLOG(1) << "CrossDevicePromo::OnFetchDeviceActivityFailure.";
-  signin_metrics::LogXDevicePromoEligible(
-      signin_metrics::ERROR_FETCHING_DEVICE_ACTIVITY);
-
-  // |device_activity_fetcher_| must be destroyed last as that object is what
-  // called OnFetchDeviceActivityFailure().
-  device_activity_fetcher_.reset();
-}
-
-bool CrossDevicePromo::ShouldShowPromo() const {
-  return prefs_->GetBoolean(prefs::kCrossDevicePromoShouldBeShown);
-}
-
-void CrossDevicePromo::OptOut() {
-  VLOG(1) << "CrossDevicePromo::OptOut.";
-  UnregisterForCookieChanges();
-  prefs_->SetBoolean(prefs::kCrossDevicePromoOptedOut, true);
-  MarkPromoShouldNotBeShown();
-}
-
-void CrossDevicePromo::MaybeBrowsingSessionStarted(
-    const base::Time& previous_last_active) {
-  // In tests, or the first call for a profile, do nothing.
-  if (previous_last_active == base::Time())
-    return;
-
-  const base::Time time_now = base::Time::Now();
-  // Determine how often this method is called as an upper bound for how often
-  // back end servers might be called.
-  UMA_HISTOGRAM_CUSTOM_COUNTS(
-      "Signin.XDevicePromo.BrowsingSessionDurationComputed",
-      (base::Time::Now() - previous_last_active).InMinutes(), 1,
-      base::TimeDelta::FromDays(30).InMinutes(), 50);
-
-  // Check if this is a different browsing session since the last call.
-  if (time_now - previous_last_active <
-      inactivity_between_browsing_sessions_) {
-    VLOG(1) << "CrossDevicePromo::MaybeBrowsingSessionStarted. Same browsing "
-               "session as the last call.";
-    return;
-  }
-
-  if (start_last_browsing_session_ != base::Time())
-    signin_metrics::LogBrowsingSessionDuration(previous_last_active);
-
-  start_last_browsing_session_ = time_now;
-
-  if (!CheckPromoEligibility()) {
-    VLOG(1) << "CrossDevicePromo::MaybeBrowsingSessionStarted; "
-            << "Ineligible for promo.";
-    if (ShouldShowPromo())
-      MarkPromoShouldNotBeShown();
-    return;
-  }
-
-  // Check if there is a record of recent browsing activity on another device.
-  const base::Time device_last_active = GetTimePref(
-      prefs::kCrossDevicePromoLastDeviceActiveTime);
-  if (time_now - device_last_active < context_switch_duration_) {
-    VLOG(1) << "CrossDevicePromo::MaybeBrowsingSessionStarted; promo active.";
-    signin_metrics::LogXDevicePromoEligible(signin_metrics::ELIGIBLE);
-    MarkPromoShouldBeShown();
-    return;
-  }
-
-  // Check for recency of device activity unless a check is already being
-  // executed because the number of devices is being updated. Use a small delay
-  // to ensure server-side data is synchronized.
-  if (!device_activity_fetcher_) {
-    VLOG(1) << "CrossDevicePromo::MaybeBrowsingSessionStarted; "
-            << "Check device activity.";
-    const int kDelayUntilGettingDeviceActivityInMS = 3000;
-    device_activity_timer_.Start(
-        FROM_HERE,
-        base::TimeDelta::FromMilliseconds(kDelayUntilGettingDeviceActivityInMS),
-        this, &CrossDevicePromo::GetDevicesActivityForGAIAAccountInCookieJar);
-  }
-}
-
-bool CrossDevicePromo::CheckPromoEligibilityForTesting() {
-  // The field trial may not have been present when Init() was called from the
-  // constructor.
-  if (!initialized_)
-    Init();
-
-  return CheckPromoEligibility();
-}
-
-void CrossDevicePromo::Init() {
-  DCHECK(!initialized_);
-  // We need a default value for |inactivity_between_browsing_sessions_|
-  // as it is referenced early in MaybeBrowsingSessionStarted and we want to
-  // gather as many stats about browsing sessions as possible.
-  const int kDefaultBrowsingSessionDurationInMinutes = 15;
-  inactivity_between_browsing_sessions_ =
-      base::TimeDelta::FromMinutes(kDefaultBrowsingSessionDurationInMinutes);
-
-  if (prefs_->GetBoolean(prefs::kCrossDevicePromoOptedOut)) {
-    signin_metrics::LogXDevicePromoInitialized(
-        signin_metrics::UNINITIALIZED_OPTED_OUT);
-    return;
-  }
-
-  if (!SetParameterFromVariation(kParamHoursBetweenDeviceActivityChecks,
-                                 base::Bind(&base::TimeDelta::FromHours),
-                                 &delay_until_next_device_activity_fetch_) ||
-      !SetParameterFromVariation(kParamDaysToVerifySingleUserProfile,
-                                 base::Bind(&base::TimeDelta::FromDays),
-                                 &single_account_duration_threshold_) ||
-      !SetParameterFromVariation(kParamMinutesBetweenBrowsingSessions,
-                                 base::Bind(&base::TimeDelta::FromMinutes),
-                                 &inactivity_between_browsing_sessions_) ||
-      !SetParameterFromVariation(kParamMinutesMaxContextSwitchDuration,
-                                 base::Bind(&base::TimeDelta::FromMinutes),
-                                 &context_switch_duration_)) {
-    signin_metrics::LogXDevicePromoInitialized(
-        signin_metrics::NO_VARIATIONS_CONFIG);
-    return;
-  }
-
-  const std::string throttle = variations::GetVariationParamValue(
-      kCrossDevicePromoFieldTrial, kParamRPCThrottle);
-  int throttle_percent;
-  if (throttle.empty() || !base::StringToInt(throttle, &throttle_percent)) {
-    signin_metrics::LogXDevicePromoInitialized(
-        signin_metrics::NO_VARIATIONS_CONFIG);
-    return;
-  }
-
-  is_throttled_ = throttle_percent && base::RandInt(0, 99) < throttle_percent;
-
-  VLOG(1) << "CrossDevicePromo::Init. Service initialized. Parameters: "
-          << "Hour between RPC checks: "
-          << delay_until_next_device_activity_fetch_.InHours()
-          << " Days to verify an account in the cookie: "
-          << single_account_duration_threshold_.InDays()
-          << " Minutes between browsing sessions: "
-          << inactivity_between_browsing_sessions_.InMinutes()
-          << " Window (in minutes) for a context switch: "
-          << context_switch_duration_.InMinutes()
-          << " Throttle rate for RPC calls: " << throttle_percent
-          << " This promo is throttled: " << is_throttled_;
-  RegisterForCookieChanges();
-  initialized_ = true;
-  signin_metrics::LogXDevicePromoInitialized(signin_metrics::INITIALIZED);
-  return;
-}
-
-void CrossDevicePromo::MarkPromoShouldBeShown() {
-  VLOG(1) << "CrossDevicePromo::MarkPromoShouldBeShown.";
-  DCHECK(!prefs_->GetBoolean(prefs::kCrossDevicePromoOptedOut));
-
-  if (!prefs_->GetBoolean(prefs::kCrossDevicePromoShouldBeShown)) {
-    prefs_->SetBoolean(prefs::kCrossDevicePromoShouldBeShown, true);
-    for (CrossDevicePromo::Observer& observer : observer_list_)
-      observer.OnPromoEligibilityChanged(true);
-  }
-}
-
-void CrossDevicePromo::MarkPromoShouldNotBeShown() {
-  VLOG(1) << "CrossDevicePromo::MarkPromoShouldNotBeShown.";
-  if (prefs_->GetBoolean(prefs::kCrossDevicePromoShouldBeShown)) {
-    prefs_->SetBoolean(prefs::kCrossDevicePromoShouldBeShown, false);
-    for (CrossDevicePromo::Observer& observer : observer_list_)
-      observer.OnPromoEligibilityChanged(false);
-  }
-}
-
-bool CrossDevicePromo::CheckPromoEligibility() {
-  if (!initialized_)
-    return false;
-
-  if (prefs_->GetBoolean(prefs::kCrossDevicePromoOptedOut)) {
-    signin_metrics::LogXDevicePromoEligible(signin_metrics::OPTED_OUT);
-    return false;
-  }
-
-  if (signin_manager_->IsAuthenticated()) {
-    signin_metrics::LogXDevicePromoEligible(signin_metrics::SIGNED_IN);
-    return false;
-  }
-
-  if (!prefs_->HasPrefPath(
-          prefs::kCrossDevicePromoObservedSingleAccountCookie) ||
-      (GetTimePref(prefs::kCrossDevicePromoObservedSingleAccountCookie) +
-          single_account_duration_threshold_ > base::Time::Now())) {
-    signin_metrics::LogXDevicePromoEligible(
-        signin_metrics::NOT_SINGLE_GAIA_ACCOUNT);
-    return false;
-  }
-
-  if (!prefs_->HasPrefPath(prefs::kCrossDevicePromoNextFetchListDevicesTime)) {
-    // The missing preference indicates CheckPromoEligibility() has never been
-    // called. Determine when to call the DeviceActivityFetcher for the first
-    // time.
-    const uint64_t milliseconds_until_next_activity_fetch = base::RandGenerator(
-        delay_until_next_device_activity_fetch_.InMilliseconds());
-    const base::Time time_of_next_device_activity_fetch = base::Time::Now() +
-        base::TimeDelta::FromMilliseconds(
-            milliseconds_until_next_activity_fetch);
-    SetTimePref(prefs::kCrossDevicePromoNextFetchListDevicesTime,
-        time_of_next_device_activity_fetch);
-    signin_metrics::LogXDevicePromoEligible(
-        signin_metrics::UNKNOWN_COUNT_DEVICES);
-    return false;
-  }
-
-  if (!prefs_->HasPrefPath(prefs::kCrossDevicePromoNumDevices)) {
-    // The missing pref indicates no knowledge of other device activity yet.
-    const base::Time time_next_list_devices = GetTimePref(
-        prefs::kCrossDevicePromoNextFetchListDevicesTime);
-    // Not time yet to poll the list of devices.
-    if (base::Time::Now() < time_next_list_devices) {
-      signin_metrics::LogXDevicePromoEligible(
-          signin_metrics::UNKNOWN_COUNT_DEVICES);
-      return false;
-    }
-    // We're not eligible... yet! Track metrics in the results.
-    GetDevicesActivityForGAIAAccountInCookieJar();
-    return false;
-  }
-
-  int num_devices = prefs_->GetInteger(prefs::kCrossDevicePromoNumDevices);
-  const base::Time time_next_list_devices = GetTimePref(
-      prefs::kCrossDevicePromoNextFetchListDevicesTime);
-  if (base::Time::Now() > time_next_list_devices) {
-    GetDevicesActivityForGAIAAccountInCookieJar();
-  } else if (num_devices == 0) {
-    signin_metrics::LogXDevicePromoEligible(signin_metrics::ZERO_DEVICES);
-    return false;
-  }
-
-  DCHECK(VerifyPromoEligibleReadOnly());
-  return true;
-}
-
-void CrossDevicePromo::DetermineEligibilityFromDeviceActivity(
-    const std::vector<DeviceActivityFetcher::DeviceActivity>& devices) {
-
-  const base::Time time_now = base::Time::Now();
-  SetTimePref(prefs::kCrossDevicePromoNextFetchListDevicesTime,
-      time_now + delay_until_next_device_activity_fetch_);
-  prefs_->SetInteger(prefs::kCrossDevicePromoNumDevices, devices.size());
-
-  if (devices.empty()) {
-    signin_metrics::LogXDevicePromoEligible(signin_metrics::ZERO_DEVICES);
-    return;
-  }
-
-  const base::Time most_recent_last_active =
-      std::max_element(devices.begin(), devices.end(),
-                       [](const DeviceActivityFetcher::DeviceActivity& first,
-                          const DeviceActivityFetcher::DeviceActivity& second) {
-                         return first.last_active < second.last_active;
-                       })->last_active;
-
-  SetTimePref(prefs::kCrossDevicePromoLastDeviceActiveTime,
-      most_recent_last_active);
-
-  if (time_now - most_recent_last_active < context_switch_duration_) {
-    // Make sure that while the DeviceActivityFetcher was executing the promo
-    // wasn't found as ineligible to be shown.
-    if (!VerifyPromoEligibleReadOnly())
-      return;
-
-    // The context switch will only be valid for so long. Schedule another
-    // device activity check for when our switch would expire to check for more
-    // recent activity.
-    if (!device_activity_timer_.IsRunning()) {
-      base::TimeDelta time_to_next_check = most_recent_last_active +
-                                           context_switch_duration_ -
-                                           time_now;
-      device_activity_timer_.Start(
-          FROM_HERE, time_to_next_check, this,
-          &CrossDevicePromo::GetDevicesActivityForGAIAAccountInCookieJar);
-    }
-
-    signin_metrics::LogXDevicePromoEligible(signin_metrics::ELIGIBLE);
-    MarkPromoShouldBeShown();
-  } else {
-    signin_metrics::LogXDevicePromoEligible(signin_metrics::NO_ACTIVE_DEVICES);
-    MarkPromoShouldNotBeShown();
-  }
-}
-
-base::Time CrossDevicePromo::GetTimePref(const std::string& pref) const {
-  return base::Time::FromInternalValue(prefs_->GetInt64(pref));
-}
-
-void CrossDevicePromo::SetTimePref(const std::string& pref,
-                                   const base::Time& value) {
-  prefs_->SetInt64(pref, value.ToInternalValue());
-}
-
-bool CrossDevicePromo::VerifyPromoEligibleReadOnly() const {
-  return initialized_ &&
-      !prefs_->GetBoolean(prefs::kCrossDevicePromoOptedOut) &&
-      prefs_->HasPrefPath(
-          prefs::kCrossDevicePromoObservedSingleAccountCookie) &&
-      GetTimePref(prefs::kCrossDevicePromoObservedSingleAccountCookie) +
-          single_account_duration_threshold_ <= base::Time::Now();
-}
-
-void CrossDevicePromo::GetDevicesActivityForGAIAAccountInCookieJar() {
-  // Don't start a fetch while one is processing.
-  if (device_activity_fetcher_)
-    return;
-
-  if (is_throttled_) {
-    signin_metrics::LogXDevicePromoEligible(
-        signin_metrics::THROTTLED_FETCHING_DEVICE_ACTIVITY);
-    return;
-  }
-
-  VLOG(1) << "CrossDevicePromo::GetDevicesActivityForGAIAAccountInCookieJar.";
-  DCHECK(VerifyPromoEligibleReadOnly());
-  device_activity_fetcher_.reset(
-      new DeviceActivityFetcher(signin_client_, this));
-  device_activity_fetcher_->Start();
-}
-
-void CrossDevicePromo::RegisterForCookieChanges() {
-  cookie_manager_service_->AddObserver(this);
-}
-
-void CrossDevicePromo::UnregisterForCookieChanges() {
-  cookie_manager_service_->RemoveObserver(this);
-}
diff --git a/chrome/browser/signin/cross_device_promo.h b/chrome/browser/signin/cross_device_promo.h
deleted file mode 100644
index f6a5c55..0000000
--- a/chrome/browser/signin/cross_device_promo.h
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_H_
-#define CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_H_
-
-#include "base/macros.h"
-#include "base/observer_list.h"
-#include "base/timer/timer.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/device_activity_fetcher.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
-
-class PrefService;
-class SigninClient;
-class SigninManager;
-
-// The Cross Device Promo promotes Chrome Signin within a profile where there is
-// one GAIA account signed in to the content area for a long period of time
-// (indicating that this profile is used by only one user) and where that
-// account uses Chrome Sync on other devices but is not signed in here.
-//
-// This class determines whether the above criteria have been met and thus
-// whether the promo should be displayed. This class imposes additional criteria
-// such that the promo is only displayed if there is sufficiently recent
-// activity on another device, indicating the user is switching contexts but
-// continuing their browsing activity. This class's behavior is controlled by
-// the "CrossDevicePromo" field trial.
-// The UI is defined elsewhere.
-//
-// This class implements the preferences necessary to track whether a profile
-// meets the above criteria, and whether the user has permanently opted out of
-// the promo.
-//
-// The class relies on the GaiaCookieManagerService to determine if there has
-// been a single GAIA account signed in to the content area for a long period of
-// time, and relies on the DeviceActivityFetcher to determine if that account
-// uses Chrome Sync on other devices.
-class CrossDevicePromo : public KeyedService,
-                         public GaiaCookieManagerService::Observer,
-                         public DeviceActivityFetcher::Observer {
- public:
-  class Observer {
-   public:
-    // Called when the profile moves from being ineligible to eligible for the
-    // promo or vice versa; the new state is noted in the |eligible| parameter.
-    virtual void OnPromoEligibilityChanged(bool eligible) = 0;
-  };
-
-  // The following constants are the parameters for a particular experiment
-  // within the field trial that controls this class's behaviors.
-  static const char kCrossDevicePromoFieldTrial[];
-  // This field trial parameter specifies how often the device activity should
-  // be fetched.
-  static const char kParamHoursBetweenDeviceActivityChecks[];
-  // This field trial parameter defines for how long the profile's cookie must
-  // contain exactly one GAIA account before the profile is considered
-  // single-user.
-  static const char kParamDaysToVerifySingleUserProfile[];
-  // This field trial parameter defines how much time must pass between calls
-  // to MaybeBrowsingSessionStarted() before the code considers a new browsing
-  // session to have started and re-evaluates if the promo should be shown.
-  static const char kParamMinutesBetweenBrowsingSessions[];
-  // This field trial parameter defines how much time may pass between the last
-  // observed device activity and the start of a new browsing session for the
-  // promo to consider the new browsing session to be a context switch.
-  static const char kParamMinutesMaxContextSwitchDuration[];
-  // This field trial parameter defines what percentage of all device activity
-  // fetches should be not be executed, to throttle requests to the server.
-  static const char kParamRPCThrottle[];
-
-  // Constructor takes non-null pointers to required services. This object does
-  // not take ownership of any of the passed objects. This also calls Init().
-  explicit CrossDevicePromo(SigninManager* signin_manager,
-                            GaiaCookieManagerService* cookie_manager_service,
-                            SigninClient* signin_client,
-                            PrefService* pref_service);
-  ~CrossDevicePromo() override;
-
-  // KeyedService:
-  // Ends observation of other services and records the length of any current
-  // browsing session (see signin_metrics::LogBrowsingSessionDuration()). This
-  // is called only during Chrome shutdown.
-  void Shutdown() override;
-
-  // GaiaCookieManagerService::Observer:
-  // This supports monitoring whether the content area is signed into exactly
-  // one GAIA account for a long period of time. This tracks the earliest time
-  // |accounts| contained (and still contains) exactly one account, so that
-  // other methods can use kParamDaysToVerifySingleUserProfile to verify if this
-  // Profile is considered single-user.
-  void OnGaiaAccountsInCookieUpdated(
-      const std::vector<gaia::ListedAccount>& accounts,
-      const std::vector<gaia::ListedAccount>& signed_out_accounts,
-      const GoogleServiceAuthError& error) override;
-
-  // DeviceActivityFetcher::Observer:
-  // OnFetchDeviceActivity* are called from |device_activity_fetcher_| which
-  // was created in GetDevicesActivityForGAIAAccountInCookieJar(). Deletes
-  // |device_activity_fetcher_| at the end of the method.
-  // See DetermineEligibilityFromDeviceActivity() for details.
-  void OnFetchDeviceActivitySuccess(
-      const std::vector<DeviceActivityFetcher::DeviceActivity>& devices)
-      override;
-  void OnFetchDeviceActivityFailure() override;
-
-  // Callable by third parties to register or unregister for callbacks when the
-  // promo's eligibility-to-be-shown state changes.
-  void AddObserver(CrossDevicePromo::Observer* observer);
-  void RemoveObserver(CrossDevicePromo::Observer* observer);
-
-  // Returns whether the profile has been marked as eligible to be shown the
-  // promo.
-  bool ShouldShowPromo() const;
-
-  // Called when the user requests to opt out of the promo. This will set a pref
-  // that forever marks the profile ineligible for the promo.
-  void OptOut();
-
-  // Called whenever a browser becomes active. Notes the start of a new browsing
-  // session if the last call to this method (noted in |previous_last_active|)
-  // was more than |inactivity_between_browsing_sessions_| ago. For new browsing
-  // sessions, will either determine if the promo is eligible to be shown, or
-  // will use |device_activity_timer_| to schedule getting more information with
-  // GetDevicesActivityForGAIAAccountInCookieJar().
-  void MaybeBrowsingSessionStarted(const base::Time& previous_last_active);
-
-  // Called only in tests; calls Init() if not already initialized. See comments
-  // on |initialized_| for details.
-  bool CheckPromoEligibilityForTesting();
-
- private:
-  // Initializes configuration parameters from the "CrossDevicePromo" field
-  // trial and registers for changes to the relevant GAIA cookie. In tests, this
-  // may be called more than once; see |initialized_| for details.
-  void Init();
-
-  // Called when the determination of whether to show the promo has been made.
-  // This both stores that decision and notifies all registered observers of any
-  // change.
-  void MarkPromoShouldBeShown();
-  void MarkPromoShouldNotBeShown();
-
-  // Performs all checks to determine if this profile could be shown the promo
-  // except for initiating a fetch for additional data. This will return false
-  // if the data available locally indicates the profile should not be shown
-  // the promo; returns true if the profile could be shown the promo (even if
-  // additional checks are to be performed).
-  bool CheckPromoEligibility();
-
-  // Called whenever new device activity is available. Checks that there is at
-  // least one device that had activity within the past
-  // kParamMinutesMaxContextSwitchDuration to determine if the promo should be
-  // shown. Once determined, the MarkPromoShould[Not]BeShown() method is called.
-  // Note that if the device is in a context switch, a followup call to
-  // GetDevicesActivityForGAIAAccountInCookieJar() will be scheduled for when
-  // the context switch would expire.
-  void DetermineEligibilityFromDeviceActivity(
-      const std::vector<DeviceActivityFetcher::DeviceActivity>& devices);
-
-  // Helpers to get and set the time value stored in a pref.
-  base::Time GetTimePref(const std::string& pref) const;
-  void SetTimePref(const std::string& pref, const base::Time& value);
-
-  // Performs checks if the promo is eligible to be displayed to this profile.
-  // This will not write any prefs or initiate any checks that are otherwise
-  // called in CheckPromoEligibility(). Records no metrics. Used for DCHECKs.
-  bool VerifyPromoEligibleReadOnly() const;
-
-  // Adds or removes |this| as an observer of |cookie_manager_service_|.
-  // We observe the |cookie_manager_service_| for its lifetime.
-  void RegisterForCookieChanges();
-  void UnregisterForCookieChanges();
-
-  // Creates a new DeviceActivityFetcher to get the list of devices, and the
-  // details of the devices (see DeviceActivityFetcher.h) where the GAIA account
-  // in this profile's cookie jar is signed in to Chrome Sync.
-  // If a |device_activity_fetcher_| is already executing a fetch, this method
-  // will not start a second fetch, as the results would be the same.
-  void GetDevicesActivityForGAIAAccountInCookieJar();
-
-  // Set by Init() to indicate if the promo service has been successfully
-  // initialized. Initialization will not occur if the user has previously opted
-  // out of the promo. Also, successful initialization requires all necessary
-  // parameters that control the promo to be read from the field trial.
-  // In testing an initial call to Init() may fail and a future call may succeed
-  // (see Init()); in non-test scenarios, however, failed initialization is
-  // unrecoverable and future calls to other class methods should be no-ops.
-  bool initialized_;
-
-  // These four pointers are weak pointers; they are not owned by this object
-  // and will outlive this object.
-  SigninManager* signin_manager_;
-  GaiaCookieManagerService* cookie_manager_service_;
-  PrefService* prefs_;
-  SigninClient* signin_client_;
-
-  std::unique_ptr<DeviceActivityFetcher> device_activity_fetcher_;
-  base::ObserverList<CrossDevicePromo::Observer> observer_list_;
-
-  // Initialized from the |kParamMinutesMaxContextSwitchDuration| field trial
-  // parameter. See |kParamMinutesMaxContextSwitchDuration| for details.
-  base::TimeDelta context_switch_duration_;
-
-  // If the device activity has never been fetched, the delay until the check
-  // will be a random duration between zero and
-  // |kParamHoursBetweenDeviceActivityChecks|. For all other
-  // fetches, the delay will be |kParamHoursBetweenDeviceActivityChecks|. See
-  // |kParamHoursBetweenDeviceActivityChecks| for details.
-  base::TimeDelta delay_until_next_device_activity_fetch_;
-
-  // Initialized from the |kParamDaysToVerifySingleUserProfile| field trial
-  // parameter. See |kParamDaysToVerifySingleUserProfile| for details.
-  base::TimeDelta single_account_duration_threshold_;
-
-  // Initialized from the |kParamMinutesBetweenBrowsingSessions| field trial
-  // parameter. See |kParamMinutesBetweenBrowsingSessions| for details.
-  base::TimeDelta inactivity_between_browsing_sessions_;
-
-  // Randomly initialized from the |kParamRPCThrottle| field trial parameter.
-  // See |kParamRPCThrottle| for details. If true, |device_activity_fetcher_|
-  // should never be initialized.
-  bool is_throttled_;
-
-  // Metric to help us track how long a browsing session is. This is set in
-  // MaybeBrowsingSessionStarted(), see that method for details.
-  // Useful for configuring the field trial to manage our server quota.
-  base::Time start_last_browsing_session_;
-
-  // Used to delay the check of device activity. See
-  // OnFetchDeviceActivitySuccess() or MaybeBrowsingSessionStarted(), as well as
-  // |delay_until_next_device_activity_fetch_|, for details.
-  base::OneShotTimer device_activity_timer_;
-
-  DISALLOW_COPY_AND_ASSIGN(CrossDevicePromo);
-};
-
-#endif  // CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_H_
diff --git a/chrome/browser/signin/cross_device_promo_factory.cc b/chrome/browser/signin/cross_device_promo_factory.cc
deleted file mode 100644
index 92a89282..0000000
--- a/chrome/browser/signin/cross_device_promo_factory.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/cross_device_promo_factory.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "chrome/browser/signin/cross_device_promo.h"
-#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/common/pref_names.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_manager.h"
-#include "components/sync_preferences/pref_service_syncable.h"
-#include "google_apis/gaia/gaia_constants.h"
-
-CrossDevicePromoFactory::CrossDevicePromoFactory()
-    : BrowserContextKeyedServiceFactory(
-          "CrossDevicePromo",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(ChromeSigninClientFactory::GetInstance());
-  DependsOn(GaiaCookieManagerServiceFactory::GetInstance());
-  DependsOn(SigninManagerFactory::GetInstance());
-}
-
-CrossDevicePromoFactory::~CrossDevicePromoFactory() {
-}
-
-// static
-CrossDevicePromo* CrossDevicePromoFactory::GetForProfile(Profile* profile) {
-  return static_cast<CrossDevicePromo*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-// static
-CrossDevicePromoFactory* CrossDevicePromoFactory::GetInstance() {
-  return base::Singleton<CrossDevicePromoFactory>::get();
-}
-
-void CrossDevicePromoFactory::RegisterProfilePrefs(
-    user_prefs::PrefRegistrySyncable* user_prefs) {
-  user_prefs->RegisterBooleanPref(prefs::kCrossDevicePromoOptedOut, false);
-  user_prefs->RegisterBooleanPref(prefs::kCrossDevicePromoShouldBeShown, false);
-  user_prefs->RegisterInt64Pref(
-      prefs::kCrossDevicePromoObservedSingleAccountCookie,
-      base::Time().ToInternalValue());
-  user_prefs->RegisterInt64Pref(
-      prefs::kCrossDevicePromoNextFetchListDevicesTime,
-      base::Time().ToInternalValue());
-  user_prefs->RegisterIntegerPref(prefs::kCrossDevicePromoNumDevices, 0);
-  user_prefs->RegisterInt64Pref(prefs::kCrossDevicePromoLastDeviceActiveTime,
-                                base::Time().ToInternalValue());
-}
-
-KeyedService* CrossDevicePromoFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  Profile* profile = Profile::FromBrowserContext(context);
-  CrossDevicePromo* service = new CrossDevicePromo(
-      SigninManagerFactory::GetForProfile(profile),
-      GaiaCookieManagerServiceFactory::GetForProfile(profile),
-      ChromeSigninClientFactory::GetForProfile(profile), profile->GetPrefs());
-  return service;
-}
diff --git a/chrome/browser/signin/cross_device_promo_factory.h b/chrome/browser/signin/cross_device_promo_factory.h
deleted file mode 100644
index 1f9a5fe..0000000
--- a/chrome/browser/signin/cross_device_promo_factory.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_FACTORY_H_
-#define CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class CrossDevicePromo;
-class Profile;
-
-// Singleton that owns all CrossDevicePromos and associates them with
-// Profiles.
-class CrossDevicePromoFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Returns the instance of CrossDevicePromo associated with this profile,
-  // creating one if none exists.
-  static CrossDevicePromo* GetForProfile(Profile* profile);
-
-  // Returns an instance of the CrossDevicePromoFactory singleton.
-  static CrossDevicePromoFactory* GetInstance();
-
-  // Implementation of BrowserContextKeyedServiceFactory.
-  void RegisterProfilePrefs(
-      user_prefs::PrefRegistrySyncable* registry) override;
-
- private:
-  friend struct base::DefaultSingletonTraits<CrossDevicePromoFactory>;
-
-  CrossDevicePromoFactory();
-  ~CrossDevicePromoFactory() override;
-
-  // BrowserContextKeyedServiceFactory
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-
-  DISALLOW_COPY_AND_ASSIGN(CrossDevicePromoFactory);
-};
-
-#endif  // CHROME_BROWSER_SIGNIN_CROSS_DEVICE_PROMO_FACTORY_H_
diff --git a/chrome/browser/signin/cross_device_promo_unittest.cc b/chrome/browser/signin/cross_device_promo_unittest.cc
deleted file mode 100644
index 611fe7a..0000000
--- a/chrome/browser/signin/cross_device_promo_unittest.cc
+++ /dev/null
@@ -1,679 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/signin/cross_device_promo.h"
-
-#include <stdint.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/run_loop.h"
-#include "base/test/histogram_tester.h"
-#include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "chrome/browser/signin/cross_device_promo_factory.h"
-#include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
-#include "chrome/browser/signin/fake_signin_manager_builder.h"
-#include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
-#include "chrome/browser/signin/test_signin_client_builder.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
-#include "components/signin/core/browser/signin_manager.h"
-#include "components/signin/core/browser/signin_metrics.h"
-#include "components/signin/core/browser/test_signin_client.h"
-#include "components/sync_preferences/pref_service_syncable.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "components/variations/entropy_provider.h"
-#include "components/variations/variations_associated_data.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-typedef std::map<std::string, std::string> VariationsMap;
-
-int64_t InTwoHours() {
-  return (base::Time::Now() + base::TimeDelta::FromHours(2)).ToInternalValue();
-}
-
-}  // namespace
-
-class CrossDevicePromoObserver : public CrossDevicePromo::Observer {
- public:
-  explicit CrossDevicePromoObserver(CrossDevicePromo* promo)
-      : eligible_(false),
-        times_set_eligible_(0),
-        times_set_ineligible_(0),
-        promo_(promo) {
-    promo->AddObserver(this);
-  }
-
-  ~CrossDevicePromoObserver() { promo_->RemoveObserver(this); }
-
-  void OnPromoEligibilityChanged(bool eligible) override {
-    eligible_ = eligible;
-    if (eligible)
-      ++times_set_eligible_;
-    else
-      ++times_set_ineligible_;
-  }
-
-  bool is_eligible() const { return eligible_; }
-  int times_set_eligible() const { return times_set_eligible_; }
-  int times_set_inactive() const { return times_set_ineligible_; }
-
- private:
-  bool eligible_;
-  int times_set_eligible_;
-  int times_set_ineligible_;
-  CrossDevicePromo* promo_;
-
-  DISALLOW_COPY_AND_ASSIGN(CrossDevicePromoObserver);
-};
-
-class CrossDevicePromoTest : public ::testing::Test {
- public:
-  CrossDevicePromoTest();
-
-  void SetUp() override;
-
-  // Destroys any variations which might be defined, and starts fresh.
-  void ResetFieldTrialList();
-
-  // Defines a default set of variation parameters for promo initialization.
-  void InitPromoVariation();
-
-  CrossDevicePromo* promo() { return cross_device_promo_; }
-  TestingProfile* profile() { return profile_; }
-  FakeSigninManagerForTesting* signin_manager() { return signin_manager_; }
-  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
-  sync_preferences::TestingPrefServiceSyncable* prefs() {
-    return pref_service_;
-  }
-  FakeGaiaCookieManagerService* cookie_manager_service() {
-    return cookie_manager_service_;
-  }
-  net::FakeURLFetcherFactory* fetcher_factory() {
-    return &fake_url_fetcher_factory_;
-  }
-
- private:
-  content::TestBrowserThreadBundle bundle_;
-  CrossDevicePromo* cross_device_promo_;
-  TestingProfile* profile_;
-  FakeSigninManagerForTesting* signin_manager_;
-  FakeGaiaCookieManagerService* cookie_manager_service_;
-  sync_preferences::TestingPrefServiceSyncable* pref_service_;
-  std::unique_ptr<TestingProfileManager> testing_profile_manager_;
-  base::HistogramTester histogram_tester_;
-  std::unique_ptr<base::FieldTrialList> field_trial_list_;
-  net::FakeURLFetcherFactory fake_url_fetcher_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(CrossDevicePromoTest);
-};
-
-CrossDevicePromoTest::CrossDevicePromoTest() : fake_url_fetcher_factory_(NULL) {
-  ResetFieldTrialList();
-}
-
-void CrossDevicePromoTest::SetUp() {
-  testing_profile_manager_.reset(
-      new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
-  ASSERT_TRUE(testing_profile_manager_.get()->SetUp());
-
-  TestingProfile::TestingFactories factories;
-  factories.push_back(std::make_pair(ChromeSigninClientFactory::GetInstance(),
-                                     signin::BuildTestSigninClient));
-  factories.push_back(
-      std::make_pair(GaiaCookieManagerServiceFactory::GetInstance(),
-                     BuildFakeGaiaCookieManagerService));
-  factories.push_back(std::make_pair(SigninManagerFactory::GetInstance(),
-                                     BuildFakeSigninManagerBase));
-
-  pref_service_ = new sync_preferences::TestingPrefServiceSyncable();
-  chrome::RegisterUserProfilePrefs(pref_service_->registry());
-
-  profile_ = testing_profile_manager_.get()->CreateTestingProfile(
-      "name",
-      base::WrapUnique<sync_preferences::PrefServiceSyncable>(pref_service_),
-      base::UTF8ToUTF16("name"), 0, std::string(), factories);
-
-  cookie_manager_service_ = static_cast<FakeGaiaCookieManagerService*>(
-      GaiaCookieManagerServiceFactory::GetForProfile(profile()));
-  cookie_manager_service_->Init(&fake_url_fetcher_factory_);
-
-  signin_manager_ = static_cast<FakeSigninManagerForTesting*>(
-      SigninManagerFactory::GetForProfile(profile()));
-
-  cross_device_promo_ = CrossDevicePromoFactory::GetForProfile(profile());
-}
-
-void CrossDevicePromoTest::ResetFieldTrialList() {
-  // Destroy the existing FieldTrialList before creating a new one to avoid
-  // a DCHECK.
-  field_trial_list_.reset();
-  field_trial_list_.reset(
-      new base::FieldTrialList(
-          base::MakeUnique<metrics::SHA1EntropyProvider>("foo")));
-  variations::testing::ClearAllVariationParams();
-}
-
-void CrossDevicePromoTest::InitPromoVariation() {
-  VariationsMap variations_params;
-  variations_params[
-      CrossDevicePromo::kParamHoursBetweenDeviceActivityChecks] = "2";
-  variations_params[
-      CrossDevicePromo::kParamDaysToVerifySingleUserProfile] = "0";
-  variations_params[
-      CrossDevicePromo::kParamMinutesBetweenBrowsingSessions] = "0";
-  variations_params[
-      CrossDevicePromo::kParamMinutesMaxContextSwitchDuration] = "10";
-  variations_params[
-      CrossDevicePromo::kParamRPCThrottle] = "0";
-  EXPECT_TRUE(variations::AssociateVariationParams(
-      CrossDevicePromo::kCrossDevicePromoFieldTrial, "A", variations_params));
-  base::FieldTrialList::CreateFieldTrial(
-      CrossDevicePromo::kCrossDevicePromoFieldTrial, "A");
-}
-
-// Tests for incrementally large portions flow that determines if the promo
-// should be shown.
-
-TEST_F(CrossDevicePromoTest, Uninitialized) {
-  ASSERT_TRUE(promo());
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Initialized",
-                                         signin_metrics::NO_VARIATIONS_CONFIG,
-                                         1);
-
-  promo()->CheckPromoEligibilityForTesting();
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Initialized",
-                                         signin_metrics::NO_VARIATIONS_CONFIG,
-                                         2);
-  histogram_tester()->ExpectTotalCount("Signin.XDevicePromo.Eligibility", 0);
-  EXPECT_FALSE(prefs()->GetBoolean(prefs::kCrossDevicePromoOptedOut));
-}
-
-TEST_F(CrossDevicePromoTest, UnitializedOptedOut) {
-  CrossDevicePromoObserver observer(promo());
-
-  promo()->OptOut();
-  // Opting out doesn't de-activate a never-active promo.
-  EXPECT_EQ(0, observer.times_set_inactive());
-  EXPECT_TRUE(prefs()->GetBoolean(prefs::kCrossDevicePromoOptedOut));
-
-  // An opted-out promo will never be initialized.
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  histogram_tester()->ExpectBucketCount("Signin.XDevicePromo.Initialized",
-                                        signin_metrics::NO_VARIATIONS_CONFIG,
-                                        1);
-  histogram_tester()->ExpectBucketCount("Signin.XDevicePromo.Initialized",
-                                        signin_metrics::UNINITIALIZED_OPTED_OUT,
-                                        1);
-  histogram_tester()->ExpectTotalCount("Signin.XDevicePromo.Eligibility", 0);
-}
-
-TEST_F(CrossDevicePromoTest, PartiallyInitialized) {
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Initialized",
-                                         signin_metrics::NO_VARIATIONS_CONFIG,
-                                         1);
-
-  VariationsMap variations_params;
-  variations_params[
-      CrossDevicePromo::kParamHoursBetweenDeviceActivityChecks] = "1";
-  variations_params[
-      CrossDevicePromo::kParamDaysToVerifySingleUserProfile] = "1";
-  EXPECT_TRUE(variations::AssociateVariationParams(
-      CrossDevicePromo::kCrossDevicePromoFieldTrial, "A", variations_params));
-  base::FieldTrialList::CreateFieldTrial(
-      CrossDevicePromo::kCrossDevicePromoFieldTrial, "A");
-
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Initialized",
-                                         signin_metrics::NO_VARIATIONS_CONFIG,
-                                         2);
-  histogram_tester()->ExpectTotalCount("Signin.XDevicePromo.Eligibility", 0);
-}
-
-TEST_F(CrossDevicePromoTest, FullyInitialized) {
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Initialized",
-                                         signin_metrics::NO_VARIATIONS_CONFIG,
-                                         1);
-
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Initialized",
-                                         signin_metrics::NO_VARIATIONS_CONFIG,
-                                         2);
-
-  InitPromoVariation();
-  signin_manager()->SignIn("12345", "foo@bar.com", "password");
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  histogram_tester()->ExpectBucketCount("Signin.XDevicePromo.Initialized",
-                                        signin_metrics::INITIALIZED, 1);
-  histogram_tester()->ExpectBucketCount("Signin.XDevicePromo.Initialized",
-                                        signin_metrics::NO_VARIATIONS_CONFIG,
-                                        2);
-
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                         signin_metrics::SIGNED_IN, 1);
-}
-
-TEST_F(CrossDevicePromoTest, InitializedOptOut) {
-  // In a normal browser, the variations get set before the CrossDevicePromo is
-  // created. Here, we need to force another Init() by calling
-  // CheckPromoEligibilityForTesting().
-  InitPromoVariation();
-  signin_manager()->SignIn("12345", "foo@bar.com", "password");
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-
-  histogram_tester()->ExpectBucketCount("Signin.XDevicePromo.Initialized",
-                                        signin_metrics::INITIALIZED, 1);
-  histogram_tester()->ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                         signin_metrics::SIGNED_IN, 1);
-
-  // After opting out the initialized state remains; eligibility changes.
-  promo()->OptOut();
-  promo()->CheckPromoEligibilityForTesting();
-  histogram_tester()->ExpectBucketCount("Signin.XDevicePromo.Initialized",
-                                        signin_metrics::INITIALIZED, 1);
-  histogram_tester()->ExpectBucketCount("Signin.XDevicePromo.Eligibility",
-                                        signin_metrics::OPTED_OUT, 1);
-}
-
-TEST_F(CrossDevicePromoTest, SignedInAndOut) {
-  InitPromoVariation();
-
-  {
-    base::HistogramTester test_signed_in;
-    signin_manager()->SignIn("12345", "foo@bar.com", "password");
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    test_signed_in.ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                      signin_metrics::SIGNED_IN, 1);
-  }
-
-  {
-    base::HistogramTester test_signed_out;
-    signin_manager()->SignOut(signin_metrics::SIGNOUT_TEST,
-                              signin_metrics::SignoutDelete::IGNORE_METRIC);
-    promo()->CheckPromoEligibilityForTesting();
-    test_signed_out.ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                       signin_metrics::NOT_SINGLE_GAIA_ACCOUNT,
-                                       1);
-  }
-}
-
-TEST_F(CrossDevicePromoTest, TrackAccountsInCookie) {
-  InitPromoVariation();
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-
-  ASSERT_FALSE(prefs()->HasPrefPath(
-      prefs::kCrossDevicePromoObservedSingleAccountCookie));
-  std::vector<gaia::ListedAccount> accounts;
-
-  // Setting a single cookie sets the time.
-  base::Time before_setting_cookies = base::Time::Now();
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseOneAccount("f@bar.com", "1");
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-
-  base::Time after_setting_cookies = base::Time::Now();
-  EXPECT_TRUE(prefs()->HasPrefPath(
-      prefs::kCrossDevicePromoObservedSingleAccountCookie));
-  EXPECT_LE(
-      before_setting_cookies.ToInternalValue(),
-      prefs()->GetInt64(prefs::kCrossDevicePromoObservedSingleAccountCookie));
-  EXPECT_GE(
-      after_setting_cookies.ToInternalValue(),
-      prefs()->GetInt64(prefs::kCrossDevicePromoObservedSingleAccountCookie));
-
-  // A single cookie a second time doesn't change the time.
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseOneAccount("f@bar.com", "1");
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(prefs()->HasPrefPath(
-      prefs::kCrossDevicePromoObservedSingleAccountCookie));
-  EXPECT_GE(
-      after_setting_cookies.ToInternalValue(),
-      prefs()->GetInt64(prefs::kCrossDevicePromoObservedSingleAccountCookie));
-
-  // Setting accounts with an auth error doesn't change the time.
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseWebLoginRequired();
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_TRUE(prefs()->HasPrefPath(
-      prefs::kCrossDevicePromoObservedSingleAccountCookie));
-  EXPECT_LE(
-      before_setting_cookies.ToInternalValue(),
-      prefs()->GetInt64(prefs::kCrossDevicePromoObservedSingleAccountCookie));
-  EXPECT_GE(
-      after_setting_cookies.ToInternalValue(),
-      prefs()->GetInt64(prefs::kCrossDevicePromoObservedSingleAccountCookie));
-
-  // Seeing zero accounts clears the pref.
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseNoAccounts();
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(prefs()->HasPrefPath(
-      prefs::kCrossDevicePromoObservedSingleAccountCookie));
-}
-
-TEST_F(CrossDevicePromoTest, SingleAccountEligibility) {
-  InitPromoVariation();
-
-  {
-    base::HistogramTester test_single_account;
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    test_single_account.ExpectUniqueSample(
-        "Signin.XDevicePromo.Eligibility",
-        signin_metrics::NOT_SINGLE_GAIA_ACCOUNT, 1);
-  }
-
-  // Notice a single account.
-  {
-    base::HistogramTester test_single_account;
-    std::vector<gaia::ListedAccount> accounts;
-    cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-    cookie_manager_service()->SetListAccountsResponseOneAccount("a@b.com", "1");
-    EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-        &accounts, nullptr, GaiaConstants::kChromeSource));
-    base::RunLoop().RunUntilIdle();
-
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    test_single_account.ExpectUniqueSample(
-        "Signin.XDevicePromo.Eligibility",
-        signin_metrics::UNKNOWN_COUNT_DEVICES, 1);
-  }
-
-  // Set a single account that hasn't been around for "long enough".
-  {
-    base::HistogramTester test_single_account;
-    prefs()->SetInt64(prefs::kCrossDevicePromoObservedSingleAccountCookie,
-                      InTwoHours());
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    test_single_account.ExpectBucketCount(
-        "Signin.XDevicePromo.Eligibility",
-        signin_metrics::NOT_SINGLE_GAIA_ACCOUNT, 1);
-  }
-}
-
-TEST_F(CrossDevicePromoTest, NumDevicesEligibility) {
-  // Start with a variation, signed in, and one account in the cookie jar.
-  InitPromoVariation();
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseOneAccount("f@bar.com", "1");
-  std::vector<gaia::ListedAccount> accounts;
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-
-  // Ensure we appropriate schedule a check for device activity.
-  {
-    base::HistogramTester test_missing_list_devices;
-    int64_t earliest_time_to_check_list_devices =
-        base::Time::Now().ToInternalValue();
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    int64_t latest_time_to_check_list_devices = InTwoHours();
-    test_missing_list_devices.ExpectUniqueSample(
-        "Signin.XDevicePromo.Eligibility",
-        signin_metrics::UNKNOWN_COUNT_DEVICES, 1);
-    EXPECT_TRUE(
-        prefs()->HasPrefPath(prefs::kCrossDevicePromoNextFetchListDevicesTime));
-    int64_t when_to_check_list_devices =
-        prefs()->GetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime);
-    EXPECT_LT(earliest_time_to_check_list_devices, when_to_check_list_devices);
-    EXPECT_GT(latest_time_to_check_list_devices, when_to_check_list_devices);
-  }
-
-  // Don't reschedule the device activity check if there's one pending.
-  {
-    base::HistogramTester test_unknown_devices;
-    int64_t list_devices_time = InTwoHours();
-    prefs()->SetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime,
-                      list_devices_time);
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    test_unknown_devices.ExpectUniqueSample(
-        "Signin.XDevicePromo.Eligibility",
-        signin_metrics::UNKNOWN_COUNT_DEVICES, 1);
-    // The scheduled time to fetch device activity should not have changed.
-    EXPECT_EQ(
-        list_devices_time,
-        prefs()->GetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime));
-  }
-
-  // Execute the device activity fetch if it's time.
-  {
-    base::HistogramTester test_unknown_devices;
-    prefs()->SetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime,
-                      base::Time::Now().ToInternalValue());
-    // The DeviceActivityFetcher will return an error to the promo service.
-    fetcher_factory()->SetFakeResponse(
-        GaiaUrls::GetInstance()->oauth2_iframe_url(), "not json", net::HTTP_OK,
-        net::URLRequestStatus::SUCCESS);
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    base::RunLoop().RunUntilIdle();
-    test_unknown_devices.ExpectUniqueSample(
-        "Signin.XDevicePromo.Eligibility",
-        signin_metrics::ERROR_FETCHING_DEVICE_ACTIVITY, 1);
-  }
-}
-
-TEST_F(CrossDevicePromoTest, ThrottleDeviceActivityCall) {
-  // Start with a variation (fully throttled), signed in, one account in cookie.
-  VariationsMap variations_params;
-  variations_params[
-      CrossDevicePromo::kParamHoursBetweenDeviceActivityChecks] = "1";
-  variations_params[
-      CrossDevicePromo::kParamDaysToVerifySingleUserProfile] = "0";
-  variations_params[
-      CrossDevicePromo::kParamMinutesBetweenBrowsingSessions] = "0";
-  variations_params[
-      CrossDevicePromo::kParamMinutesMaxContextSwitchDuration] = "10";
-  variations_params[
-      CrossDevicePromo::kParamRPCThrottle] = "100";
-  EXPECT_TRUE(variations::AssociateVariationParams(
-      CrossDevicePromo::kCrossDevicePromoFieldTrial, "A", variations_params));
-  base::FieldTrialList::CreateFieldTrial(
-      CrossDevicePromo::kCrossDevicePromoFieldTrial, "A");
-
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseOneAccount("f@bar.com", "1");
-  std::vector<gaia::ListedAccount> accounts;
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-
-  // Ensure device activity fetches get throttled.
-  {
-    base::HistogramTester test_throttle_rpc;
-    prefs()->SetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime,
-                      base::Time::Now().ToInternalValue());
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    test_throttle_rpc.ExpectUniqueSample(
-        "Signin.XDevicePromo.Eligibility",
-        signin_metrics::THROTTLED_FETCHING_DEVICE_ACTIVITY, 1);
-  }
-}
-
-TEST_F(CrossDevicePromoTest, NumDevicesKnown) {
-  // Start with a variation, signed in, and one account, fetch device activity
-  // in two hours.
-  InitPromoVariation();
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseOneAccount("f@bar.com", "1");
-  std::vector<gaia::ListedAccount> accounts;
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-  prefs()->SetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime,
-                    InTwoHours());
-
-  // Verify that knowing there are no devices for this account logs the
-  // appropriate metric for ineligibility.
-  {
-    base::HistogramTester test_no_devices;
-    prefs()->SetInteger(prefs::kCrossDevicePromoNumDevices, 0);
-    EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-    test_no_devices.ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                       signin_metrics::ZERO_DEVICES, 1);
-  }
-
-  // Verify that knowing there is another device for this account results in the
-  // promo being eligible to be shown.
-  {
-    prefs()->SetInteger(prefs::kCrossDevicePromoNumDevices, 1);
-    EXPECT_TRUE(promo()->CheckPromoEligibilityForTesting());
-  }
-}
-
-TEST_F(CrossDevicePromoTest, FetchDeviceResults) {
-  // Start with a variation, signed in, and one account, fetch device activity
-  // in 2 hours.
-  InitPromoVariation();
-  EXPECT_FALSE(promo()->CheckPromoEligibilityForTesting());
-  cookie_manager_service()->set_list_accounts_stale_for_testing(true);
-  cookie_manager_service()->SetListAccountsResponseOneAccount("f@bar.com", "1");
-  std::vector<gaia::ListedAccount> accounts;
-  EXPECT_FALSE(cookie_manager_service()->ListAccounts(
-      &accounts, nullptr, GaiaConstants::kChromeSource));
-  base::RunLoop().RunUntilIdle();
-  prefs()->SetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime,
-                    base::Time::Now().ToInternalValue());
-  prefs()->SetInteger(prefs::kCrossDevicePromoNumDevices, 1);
-
-  // Verify that if the device activity fetcher returns zero devices the
-  // eligibility metric will report a ZERO_DEVICES event, and will not report
-  // the promo as eligible to be shown.
-  {
-    base::HistogramTester test_no_devices;
-    std::vector<DeviceActivityFetcher::DeviceActivity> devices;
-    int64_t in_two_hours = InTwoHours();
-    promo()->OnFetchDeviceActivitySuccess(devices);
-    EXPECT_LE(
-        in_two_hours,
-        prefs()->GetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime));
-    EXPECT_EQ(0, prefs()->GetInteger(prefs::kCrossDevicePromoNumDevices));
-    test_no_devices.ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                       signin_metrics::ZERO_DEVICES, 1);
-  }
-
-  // Verify that if the device activity fetcher returns one device that was
-  // recently active, the promo is marked as eligible and the eligibility
-  // metric reports an ELIGIBLE event.
-  {
-    CrossDevicePromoObserver observer(promo());
-    EXPECT_FALSE(observer.is_eligible());
-    base::HistogramTester test_one_device;
-    std::vector<DeviceActivityFetcher::DeviceActivity> devices;
-    base::Time device_last_active =
-        base::Time::Now() - base::TimeDelta::FromMinutes(4);
-    DeviceActivityFetcher::DeviceActivity device;
-    device.last_active = device_last_active;
-    device.name = "Aslan";
-    devices.push_back(device);
-
-    int64_t in_two_hours = InTwoHours();
-    promo()->OnFetchDeviceActivitySuccess(devices);
-    EXPECT_LE(
-        in_two_hours,
-        prefs()->GetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime));
-    EXPECT_EQ(1, prefs()->GetInteger(prefs::kCrossDevicePromoNumDevices));
-    EXPECT_EQ(device_last_active.ToInternalValue(),
-              prefs()->GetInt64(prefs::kCrossDevicePromoLastDeviceActiveTime));
-    EXPECT_TRUE(prefs()->GetBoolean(prefs::kCrossDevicePromoShouldBeShown));
-    test_one_device.ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                       signin_metrics::ELIGIBLE, 1);
-    EXPECT_TRUE(observer.is_eligible());
-    EXPECT_EQ(1, observer.times_set_eligible());
-  }
-
-  // Verify that if the device activity fetcher returns one device that was not
-  // recently active then the eligibility metric will report a NO_ACTIVE_DEVICES
-  // event, and will not report the promo as eligible to be shown.
-  {
-    CrossDevicePromoObserver observer(promo());
-    EXPECT_FALSE(observer.is_eligible());
-    base::HistogramTester test_one_device;
-    std::vector<DeviceActivityFetcher::DeviceActivity> devices;
-    base::Time device_last_active =
-        base::Time::Now() - base::TimeDelta::FromMinutes(30);
-    DeviceActivityFetcher::DeviceActivity device;
-    device.last_active = device_last_active;
-    device.name = "Aslan";
-    devices.push_back(device);
-
-    int64_t in_two_hours = InTwoHours();
-    promo()->OnFetchDeviceActivitySuccess(devices);
-    EXPECT_LE(
-        in_two_hours,
-        prefs()->GetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime));
-    EXPECT_EQ(1, prefs()->GetInteger(prefs::kCrossDevicePromoNumDevices));
-    EXPECT_EQ(device_last_active.ToInternalValue(),
-              prefs()->GetInt64(prefs::kCrossDevicePromoLastDeviceActiveTime));
-    EXPECT_FALSE(prefs()->GetBoolean(prefs::kCrossDevicePromoShouldBeShown));
-    test_one_device.ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                       signin_metrics::NO_ACTIVE_DEVICES, 1);
-    EXPECT_FALSE(observer.is_eligible());
-  }
-
-  // Verify that if the device activity fetcher returns two devices and one was
-  // recently active, that the promo is eligible to be shown and the last active
-  // time is stored properly.
-  {
-    CrossDevicePromoObserver observer(promo());
-    EXPECT_FALSE(observer.is_eligible());
-    base::HistogramTester test_two_devices;
-    std::vector<DeviceActivityFetcher::DeviceActivity> devices;
-    base::Time device1_last_active =
-        base::Time::Now() - base::TimeDelta::FromMinutes(30);
-    base::Time device2_last_active =
-        base::Time::Now() - base::TimeDelta::FromMinutes(3);
-    DeviceActivityFetcher::DeviceActivity device1;
-    device1.last_active = device1_last_active;
-    device1.name = "Aslan";
-    devices.push_back(device1);
-    DeviceActivityFetcher::DeviceActivity device2;
-    device2.last_active = device2_last_active;
-    device2.name = "Balrog";
-    devices.push_back(device2);
-
-    int64_t in_two_hours = InTwoHours();
-    promo()->OnFetchDeviceActivitySuccess(devices);
-    EXPECT_LE(
-        in_two_hours,
-        prefs()->GetInt64(prefs::kCrossDevicePromoNextFetchListDevicesTime));
-    EXPECT_EQ(2, prefs()->GetInteger(prefs::kCrossDevicePromoNumDevices));
-    EXPECT_EQ(device2_last_active.ToInternalValue(),
-              prefs()->GetInt64(prefs::kCrossDevicePromoLastDeviceActiveTime));
-    EXPECT_TRUE(prefs()->GetBoolean(prefs::kCrossDevicePromoShouldBeShown));
-    test_two_devices.ExpectUniqueSample("Signin.XDevicePromo.Eligibility",
-                                        signin_metrics::ELIGIBLE, 1);
-    EXPECT_TRUE(observer.is_eligible());
-    EXPECT_EQ(1, observer.times_set_eligible());
-  }
-}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 970a1108..a63e782 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1252,8 +1252,6 @@
       "views/select_file_dialog_extension.h",
       "views/select_file_dialog_extension_factory.cc",
       "views/select_file_dialog_extension_factory.h",
-      "webui/cast/cast_ui.cc",
-      "webui/cast/cast_ui.h",
     ]
     deps += [
       "//chrome/browser/chromeos",
@@ -2415,8 +2413,6 @@
       "passwords/manage_passwords_icon.cc",
       "passwords/manage_passwords_icon.h",
       "web_contents_sizer.mm",
-      "webui/cast/cast_ui.cc",
-      "webui/cast/cast_ui.h",
     ]
     deps += [
       "//chrome/app/nibs:localizer_table",
@@ -3062,8 +3058,6 @@
       "views/settings_reset_prompt_dialog.h",
       "views/uninstall_view.cc",
       "views/uninstall_view.h",
-      "webui/cast/cast_ui.cc",
-      "webui/cast/cast_ui.h",
       "webui/conflicts_ui.cc",
       "webui/conflicts_ui.h",
       "webui/welcome_win10_handler.cc",
@@ -3436,6 +3430,12 @@
         "views/toolbar/media_router_action_platform_delegate_views.h",
       ]
     }
+    if (is_chromeos || is_mac || is_win) {
+      sources += [
+        "webui/cast/cast_ui.cc",
+        "webui/cast/cast_ui.h",
+      ]
+    }
     deps += [
       "//chrome/browser/media/router",
       "//components/web_modal",
diff --git a/chrome/browser/ui/crypto_module_password_dialog_nss.cc b/chrome/browser/ui/crypto_module_password_dialog_nss.cc
index a6270dc..b211d9f 100644
--- a/chrome/browser/ui/crypto_module_password_dialog_nss.cc
+++ b/chrome/browser/ui/crypto_module_password_dialog_nss.cc
@@ -17,10 +17,9 @@
 
 namespace {
 
-bool ShouldShowDialog(const net::CryptoModule* module) {
+bool ShouldShowDialog(PK11SlotInfo* slot) {
   // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
-  return (PK11_NeedLogin(module->os_module_handle()) &&
-          !PK11_IsLoggedIn(module->os_module_handle(), NULL /* wincx */));
+  return (PK11_NeedLogin(slot) && !PK11_IsLoggedIn(slot, NULL /* wincx */));
 }
 
 // Basically an asynchronous implementation of NSS's PK11_DoPassword.
@@ -28,7 +27,7 @@
 // GotPassword for what is yet unimplemented.
 class SlotUnlocker {
  public:
-  SlotUnlocker(const net::CryptoModuleList& modules,
+  SlotUnlocker(std::vector<crypto::ScopedPK11Slot> modules,
                chrome::CryptoModulePasswordReason reason,
                const net::HostPortPair& server,
                gfx::NativeWindow parent,
@@ -41,7 +40,7 @@
   void Done();
 
   size_t current_;
-  net::CryptoModuleList modules_;
+  std::vector<crypto::ScopedPK11Slot> modules_;
   chrome::CryptoModulePasswordReason reason_;
   net::HostPortPair server_;
   gfx::NativeWindow parent_;
@@ -49,13 +48,13 @@
   PRBool retry_;
 };
 
-SlotUnlocker::SlotUnlocker(const net::CryptoModuleList& modules,
+SlotUnlocker::SlotUnlocker(std::vector<crypto::ScopedPK11Slot> modules,
                            chrome::CryptoModulePasswordReason reason,
                            const net::HostPortPair& server,
                            gfx::NativeWindow parent,
                            const base::Closure& callback)
     : current_(0),
-      modules_(modules),
+      modules_(std::move(modules)),
       reason_(reason),
       server_(server),
       parent_(parent),
@@ -70,11 +69,8 @@
   for (; current_ < modules_.size(); ++current_) {
     if (ShouldShowDialog(modules_[current_].get())) {
       ShowCryptoModulePasswordDialog(
-          modules_[current_]->GetTokenName(),
-          retry_,
-          reason_,
-          server_.host(),
-          parent_,
+          PK11_GetTokenName(modules_[current_].get()), retry_, reason_,
+          server_.host(), parent_,
           base::Bind(&SlotUnlocker::GotPassword, base::Unretained(this)));
       return;
     }
@@ -95,8 +91,8 @@
   }
 
   // TODO(mattm): handle protectedAuthPath
-  SECStatus rv = PK11_CheckUserPassword(modules_[current_]->os_module_handle(),
-                                        password.c_str());
+  SECStatus rv =
+      PK11_CheckUserPassword(modules_[current_].get(), password.c_str());
   if (rv == SECWouldBlock) {
     // Incorrect password.  Try again.
     retry_ = PR_TRUE;
@@ -123,7 +119,7 @@
 
 namespace chrome {
 
-void UnlockSlotsIfNecessary(const net::CryptoModuleList& modules,
+void UnlockSlotsIfNecessary(std::vector<crypto::ScopedPK11Slot> modules,
                             chrome::CryptoModulePasswordReason reason,
                             const net::HostPortPair& server,
                             gfx::NativeWindow parent,
@@ -131,7 +127,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   for (size_t i = 0; i < modules.size(); ++i) {
     if (ShouldShowDialog(modules[i].get())) {
-      (new SlotUnlocker(modules, reason, server, parent, callback))->Start();
+      (new SlotUnlocker(std::move(modules), reason, server, parent, callback))
+          ->Start();
       return;
     }
   }
@@ -143,10 +140,10 @@
                                const net::HostPortPair& server,
                                gfx::NativeWindow parent,
                                const base::Closure& callback) {
-  net::CryptoModuleList modules;
-  modules.push_back(net::CryptoModule::CreateFromHandle(
-      cert->os_cert_handle()->slot));
-  UnlockSlotsIfNecessary(modules, reason, server, parent, callback);
+  std::vector<crypto::ScopedPK11Slot> modules;
+  modules.push_back(
+      crypto::ScopedPK11Slot(PK11_ReferenceSlot(cert->os_cert_handle()->slot)));
+  UnlockSlotsIfNecessary(std::move(modules), reason, server, parent, callback);
 }
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/crypto_module_password_dialog_nss.h b/chrome/browser/ui/crypto_module_password_dialog_nss.h
index 6d871f3..9fb2d8ed 100644
--- a/chrome/browser/ui/crypto_module_password_dialog_nss.h
+++ b/chrome/browser/ui/crypto_module_password_dialog_nss.h
@@ -11,12 +11,11 @@
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/ui/crypto_module_password_dialog.h"
+#include "crypto/scoped_nss_types.h"
 #include "net/base/host_port_pair.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace net {
-class CryptoModule;
-typedef std::vector<scoped_refptr<CryptoModule> > CryptoModuleList;
 class X509Certificate;
 }
 
@@ -25,7 +24,7 @@
 // Asynchronously unlock |modules|, if necessary. |callback| is called when
 // done (regardless if any modules were successfully unlocked or not).  Should
 // only be called on UI thread.
-void UnlockSlotsIfNecessary(const net::CryptoModuleList& modules,
+void UnlockSlotsIfNecessary(std::vector<crypto::ScopedPK11Slot> modules,
                             CryptoModulePasswordReason reason,
                             const net::HostPortPair& server,
                             gfx::NativeWindow parent,
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 673351f..7c6bd92 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -732,8 +732,11 @@
 
   CreateZoomMenu();
   AddItemWithStringId(IDC_PRINT, IDS_PRINT);
+
+#if defined(ENABLE_MEDIA_ROUTER)
   if (media_router::MediaRouterEnabled(browser()->profile()))
     AddItemWithStringId(IDC_ROUTE_MEDIA, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE);
+#endif  // defined(ENABLE_MEDIA_ROUTER)
 
   AddItemWithStringId(IDC_FIND, IDS_FIND);
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc b/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc
index 0f03b7b..1e7552b8 100644
--- a/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc
+++ b/chrome/browser/ui/views/payments/payment_request_interactive_uitest_base.cc
@@ -26,6 +26,7 @@
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test_utils.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
@@ -50,6 +51,8 @@
     base::CommandLine* command_line) {
   InProcessBrowserTest::SetUpCommandLine(command_line);
   command_line->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
+  command_line->AppendSwitchASCII(switches::kEnableFeatures,
+                                  features::kWebPayments.name);
 }
 
 void PaymentRequestInteractiveTestBase::SetUpOnMainThread() {
diff --git a/chrome/browser/ui/webui/options/certificate_manager_handler.cc b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
index 1ff301c..d23446bf 100644
--- a/chrome/browser/ui/webui/options/certificate_manager_handler.cc
+++ b/chrome/browser/ui/webui/options/certificate_manager_handler.cc
@@ -839,11 +839,10 @@
     slot_ = certificate_manager_model_->cert_db()->GetPublicSlot();
   }
 
-  net::CryptoModuleList modules;
-  modules.push_back(net::CryptoModule::CreateFromHandle(slot_.get()));
+  std::vector<crypto::ScopedPK11Slot> modules;
+  modules.push_back(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_.get())));
   chrome::UnlockSlotsIfNecessary(
-      modules,
-      chrome::kCryptoModulePasswordCertImport,
+      std::move(modules), chrome::kCryptoModulePasswordCertImport,
       net::HostPortPair(),  // unused.
       GetParentWindow(),
       base::Bind(&CertificateManagerHandler::ImportPersonalSlotUnlocked,
diff --git a/chrome/browser/ui/webui/settings/certificates_handler.cc b/chrome/browser/ui/webui/settings/certificates_handler.cc
index 62a4191..24e40be 100644
--- a/chrome/browser/ui/webui/settings/certificates_handler.cc
+++ b/chrome/browser/ui/webui/settings/certificates_handler.cc
@@ -723,10 +723,10 @@
     slot_ = certificate_manager_model_->cert_db()->GetPublicSlot();
   }
 
-  net::CryptoModuleList modules;
-  modules.push_back(net::CryptoModule::CreateFromHandle(slot_.get()));
+  std::vector<crypto::ScopedPK11Slot> modules;
+  modules.push_back(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_.get())));
   chrome::UnlockSlotsIfNecessary(
-      modules, chrome::kCryptoModulePasswordCertImport,
+      std::move(modules), chrome::kCryptoModulePasswordCertImport,
       net::HostPortPair(),  // unused.
       GetParentWindow(),
       base::Bind(&CertificatesHandler::ImportPersonalSlotUnlocked,
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 92cf7fe..e62cad0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2116,7 +2116,6 @@
         "../browser/ui/ash/shelf_browsertest.cc",
         "../browser/ui/ash/system_tray_client_browsertest.cc",
         "../browser/ui/ash/system_tray_delegate_chromeos_browsertest_chromeos.cc",
-        "../browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc",
         "../browser/ui/ash/volume_controller_browsertest.cc",
         "../browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc",
       ]
@@ -2128,6 +2127,10 @@
       if (enable_app_list) {
         deps += [ ":test_support_applist_ash" ]
       }
+
+      if (enable_media_router) {
+        sources += [ "../browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc" ]
+      }
     }
     if (use_aura || toolkit_views) {
       deps += [ "//ui/events:test_support" ]
@@ -2166,13 +2169,15 @@
           "../browser/ui/views/frame/browser_non_client_frame_view_browsertest.cc",
           "../browser/ui/views/frame/browser_view_browsertest.cc",
           "../browser/ui/views/location_bar/zoom_bubble_view_browsertest.cc",
-          "../browser/ui/views/media_router/media_router_ui_browsertest.cc",
           "../browser/ui/views/passwords/password_dialog_view_browsertest.cc",
           "../browser/ui/views/task_manager_view_browsertest.cc",
           "../browser/ui/views/toolbar/browser_actions_container_browsertest.cc",
           "../browser/ui/views/translate/translate_bubble_view_browsertest.cc",
           "../browser/ui/views/web_dialog_view_browsertest.cc",
         ]
+        if (enable_media_router) {
+          sources += [ "../browser/ui/views/media_router/media_router_ui_browsertest.cc" ]
+        }
       }
     }
 
@@ -3696,8 +3701,6 @@
       "../browser/ui/toolbar/component_toolbar_actions_factory_unittest.cc",
       "../browser/ui/toolbar/mock_component_toolbar_actions_factory.cc",
       "../browser/ui/toolbar/mock_component_toolbar_actions_factory.h",
-      "../browser/ui/toolbar/mock_media_router_action_controller.cc",
-      "../browser/ui/toolbar/mock_media_router_action_controller.h",
       "../browser/ui/toolbar/recent_tabs_builder_test_helper.cc",
       "../browser/ui/toolbar/recent_tabs_builder_test_helper.h",
       "../browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc",
@@ -3990,8 +3993,6 @@
       "../browser/extensions/webstore_inline_installer_unittest.cc",
       "../browser/extensions/webstore_installer_unittest.cc",
       "../browser/extensions/zipfile_installer_unittest.cc",
-      "../browser/media/cast_remoting_sender_unittest.cc",
-      "../browser/media/cast_transport_host_filter_unittest.cc",
       "../browser/media_galleries/chromeos/mtp_device_object_enumerator_unittest.cc",
       "../browser/metrics/extensions_metrics_provider_unittest.cc",
       "../browser/notifications/extension_welcome_notification_unittest.cc",
@@ -4144,6 +4145,12 @@
         "../browser/extensions/api/messaging/native_messaging_policy_handler_unittest.cc",
       ]
     }
+    if (enable_media_router) {
+      sources += [
+        "../browser/media/cast_remoting_sender_unittest.cc",
+        "../browser/media/cast_transport_host_filter_unittest.cc",
+      ]
+    }
   }
   if (use_ash) {
     sources += [
@@ -4405,6 +4412,8 @@
         "../browser/ui/toolbar/media_router_action_controller_unittest.cc",
         "../browser/ui/toolbar/media_router_action_unittest.cc",
         "../browser/ui/toolbar/media_router_contextual_menu_unittest.cc",
+        "../browser/ui/toolbar/mock_media_router_action_controller.cc",
+        "../browser/ui/toolbar/mock_media_router_action_controller.h",
         "../browser/ui/webui/media_router/cast_modes_with_media_sources_unittest.cc",
         "../browser/ui/webui/media_router/media_cast_mode_unittest.cc",
         "../browser/ui/webui/media_router/media_router_dialog_controller_impl_unittest.cc",
@@ -4776,7 +4785,6 @@
     sources += [
       "../browser/media/webrtc/native_desktop_media_list_unittest.cc",
       "../browser/metrics/desktop_session_duration/desktop_session_duration_tracker_unittest.cc",
-      "../browser/signin/cross_device_promo_unittest.cc",
       "../browser/signin/signin_global_error_unittest.cc",
       "../browser/sync/sync_global_error_unittest.cc",
       "../browser/ui/webui/signin/signin_create_profile_handler_unittest.cc",
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
index b1cc9246..b46cff2 100644
--- a/chrome/test/data/webrtc/peerconnection_getstats.js
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -17,9 +17,7 @@
  * @private
  */
 var kRTCRTPStreamStats = new RTCStats_(null, {
-  // TODO(hbos): As soon as |ssrc| has changed into a number, change to
-  // 'number'. https://bugs.chromium.org/p/webrtc/issues/detail?id=7065, 7066
-  ssrc: 'any',
+  ssrc: 'number',
   associateStatsId: 'string',
   isRemote: 'boolean',
   mediaType: 'string',
@@ -40,10 +38,13 @@
  */
 var kRTCCodecStats = new RTCStats_(null, {
   payloadType: 'number',
+  mimeType: 'string',
+  // TODO(hbos): As soon as |codec| has been renamed |mimeType| in the webrtc
+  // repo, remove this line. https://bugs.webrtc.org/7061
   codec: 'string',
   clockRate: 'number',
   channels: 'number',
-  parameters: 'string',
+  sdpFmtpLine: 'string',
   implementation: 'string',
 });
 gStatsWhitelist.set('codec', kRTCCodecStats);
diff --git a/chromecast/app/BUILD.gn b/chromecast/app/BUILD.gn
index 8de02c8..55c8287 100644
--- a/chromecast/app/BUILD.gn
+++ b/chromecast/app/BUILD.gn
@@ -92,6 +92,7 @@
 
 grit("resources") {
   source = "//chromecast/app/resources/shell_resources.grd"
+  use_qualified_include = true
 
   resource_ids = "//chromecast/app/resources/resource_ids"
 
@@ -104,6 +105,8 @@
 grit("chromecast_settings") {
   source = "//chromecast/app/resources/chromecast_settings.grd"
   resource_ids = "//chromecast/app/resources/resource_ids"
+
+  # TODO(thakis): Consider removing this in favor of the default directory.
   output_dir = "$root_gen_dir/chromecast_strings"
 
   outputs = [
diff --git a/chromecast/browser/DEPS b/chromecast/browser/DEPS
index ab3d80066b..49dff6b9 100644
--- a/chromecast/browser/DEPS
+++ b/chromecast/browser/DEPS
@@ -2,6 +2,7 @@
   "+cc/base/switches.h",
   "+chromecast/common",
   "+chromecast/graphics",
+  "+chromecast/app/resources/grit/shell_resources.h",
   "+chromecast/media",
   "+chromecast/net",
   "+chromecast/service",
@@ -17,7 +18,6 @@
   "+gin/v8_initializer.h",
   "+gpu/command_buffer/service/gpu_switches.h",
   "+grit/chromecast_settings.h",
-  "+grit/shell_resources.h",
   "+media/audio",
   "+media/base",
   "+media/mojo",
diff --git a/chromecast/browser/devtools/cast_devtools_manager_delegate.cc b/chromecast/browser/devtools/cast_devtools_manager_delegate.cc
index ef3c6b13..d58e8be 100644
--- a/chromecast/browser/devtools/cast_devtools_manager_delegate.cc
+++ b/chromecast/browser/devtools/cast_devtools_manager_delegate.cc
@@ -6,8 +6,8 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "chromecast/app/grit/shell_resources.h"
 #include "content/public/browser/devtools_agent_host.h"
-#include "grit/shell_resources.h"
 #include "ui/base/resource/resource_bundle.h"
 
 namespace chromecast {
diff --git a/cloud_print/DEPS b/cloud_print/DEPS
index 0f0c1a6..edd33b74 100644
--- a/cloud_print/DEPS
+++ b/cloud_print/DEPS
@@ -1,5 +1,5 @@
 include_rules = [
   "+chrome/common",
   "+chrome/installer/launcher_support",
-  "+grit",
+  "+virtual_driver_setup_resources",
 ]
diff --git a/cloud_print/virtual_driver/win/install/BUILD.gn b/cloud_print/virtual_driver/win/install/BUILD.gn
index 3018a21..2762974b 100644
--- a/cloud_print/virtual_driver/win/install/BUILD.gn
+++ b/cloud_print/virtual_driver/win/install/BUILD.gn
@@ -59,7 +59,9 @@
   visibility = [ ":*" ]
 
   source = "virtual_driver_setup_resources.grd"
+  use_qualified_include = true
 
+  # TODO(thakis): Consider removing this in favor of the default directory.
   output_dir = "$root_gen_dir/virtual_driver_setup_resources"
 
   outputs = [
diff --git a/cloud_print/virtual_driver/win/install/setup.cc b/cloud_print/virtual_driver/win/install/setup.cc
index 99c0bef3..64eef52 100644
--- a/cloud_print/virtual_driver/win/install/setup.cc
+++ b/cloud_print/virtual_driver/win/install/setup.cc
@@ -23,7 +23,7 @@
 #include "cloud_print/common/win/install_utils.h"
 #include "cloud_print/virtual_driver/win/virtual_driver_consts.h"
 #include "cloud_print/virtual_driver/win/virtual_driver_helpers.h"
-#include "grit/virtual_driver_setup_resources.h"
+#include "virtual_driver_setup_resources/grit/virtual_driver_setup_resources.h"
 
 #include <strsafe.h>  // Must be after base headers to avoid deprecation
                       // warnings.
diff --git a/components/doodle/BUILD.gn b/components/doodle/BUILD.gn
index 6f4f06ab..1eec0e3 100644
--- a/components/doodle/BUILD.gn
+++ b/components/doodle/BUILD.gn
@@ -7,6 +7,9 @@
     "doodle_fetcher.h",
     "doodle_fetcher_impl.cc",
     "doodle_fetcher_impl.h",
+    "doodle_service.cc",
+    "doodle_service.h",
+    "doodle_types.cc",
     "doodle_types.h",
   ]
 
@@ -22,6 +25,7 @@
   testonly = true
   sources = [
     "doodle_fetcher_impl_unittest.cc",
+    "doodle_service_unittest.cc",
   ]
 
   deps = [
diff --git a/components/doodle/doodle_fetcher.h b/components/doodle/doodle_fetcher.h
index 5745d6c..f5ef780 100644
--- a/components/doodle/doodle_fetcher.h
+++ b/components/doodle/doodle_fetcher.h
@@ -29,7 +29,7 @@
   // Fetches a doodle asynchronously. The |callback| is called with a
   // DoodleState indicating whether the request succeded in fetching a doodle.
   // If a fetch is already running, the callback will be queued and invoked with
-  // result from the next completed request.
+  // the result from the next completed request.
   virtual void FetchDoodle(FinishedCallback callback) = 0;
 };
 
diff --git a/components/doodle/doodle_fetcher_impl.cc b/components/doodle/doodle_fetcher_impl.cc
index 90fcdde..a618ec8 100644
--- a/components/doodle/doodle_fetcher_impl.cc
+++ b/components/doodle/doodle_fetcher_impl.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/strings/string_number_conversions.h"
-#include "base/time/clock.h"
 #include "base/time/default_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -130,14 +129,14 @@
   std::unique_ptr<base::DictionaryValue> config =
       base::DictionaryValue::From(std::move(json));
   if (!config.get()) {
-    DLOG(WARNING) << "Doodle JSON is not valid";
+    DLOG(WARNING) << "Doodle JSON is not valid.";
     RespondToAllCallbacks(DoodleState::PARSING_ERROR, base::nullopt);
     return;
   }
 
   const base::DictionaryValue* ddljson = nullptr;
   if (!config->GetDictionary("ddljson", &ddljson)) {
-    DLOG(WARNING) << "Doodle JSON reponse did not contain 'ddljson' key.";
+    DLOG(WARNING) << "Doodle JSON response did not contain 'ddljson' key.";
     RespondToAllCallbacks(DoodleState::PARSING_ERROR, base::nullopt);
     return;
   }
@@ -159,6 +158,8 @@
 base::Optional<DoodleConfig> DoodleFetcherImpl::ParseDoodle(
     const base::DictionaryValue& ddljson) const {
   DoodleConfig doodle;
+  // The |large_image| field is required (it's the "default" representation for
+  // the doodle).
   if (!ParseImage(ddljson, "large_image", &doodle.large_image)) {
     return base::nullopt;
   }
diff --git a/components/doodle/doodle_fetcher_impl.h b/components/doodle/doodle_fetcher_impl.h
index 82c9c3f8..53dcbd5 100644
--- a/components/doodle/doodle_fetcher_impl.h
+++ b/components/doodle/doodle_fetcher_impl.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "base/time/clock.h"
 #include "components/doodle/doodle_fetcher.h"
 #include "components/doodle/doodle_types.h"
 #include "net/url_request/url_fetcher_delegate.h"
@@ -24,7 +25,6 @@
 class GoogleURLTracker;
 
 namespace base {
-class Clock;
 class DictionaryValue;
 class Value;
 }
@@ -91,7 +91,7 @@
   ParseJSONCallback json_parsing_callback_;
   GoogleURLTracker* google_url_tracker_;
 
-  // Allow for an injectable tick clock for testing.
+  // Allow for an injectable clock for testing.
   std::unique_ptr<base::Clock> clock_;
 
   std::vector<FinishedCallback> callbacks_;
diff --git a/components/doodle/doodle_service.cc b/components/doodle/doodle_service.cc
new file mode 100644
index 0000000..fee586c
--- /dev/null
+++ b/components/doodle/doodle_service.cc
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/doodle/doodle_service.h"
+
+#include <utility>
+
+#include "base/bind.h"
+
+namespace doodle {
+
+DoodleService::DoodleService(std::unique_ptr<DoodleFetcher> fetcher)
+    : fetcher_(std::move(fetcher)) {
+  DCHECK(fetcher_);
+}
+
+DoodleService::~DoodleService() = default;
+
+void DoodleService::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void DoodleService::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void DoodleService::Refresh() {
+  fetcher_->FetchDoodle(
+      base::BindOnce(&DoodleService::DoodleFetched, base::Unretained(this)));
+}
+
+void DoodleService::DoodleFetched(
+    DoodleState state,
+    const base::Optional<DoodleConfig>& doodle_config) {
+  if (!cached_config_.has_value() && !doodle_config.has_value()) {
+    // There was no config before and we didn't get a new one, so there's
+    // nothing to do.
+    return;
+  }
+
+  bool notify = false;
+  if (cached_config_.has_value() != doodle_config.has_value()) {
+    // We got a new config, or an existing one went away.
+    notify = true;
+  } else {
+    // There was a config both before and after the update. Notify observers
+    // only if something relevant changed.
+    notify = !cached_config_.value().IsEquivalent(doodle_config.value());
+  }
+
+  // In any case, update the cache.
+  cached_config_ = doodle_config;
+
+  if (notify) {
+    for (auto& observer : observers_) {
+      observer.OnDoodleConfigUpdated(cached_config_);
+    }
+  }
+}
+
+}  // namespace doodle
diff --git a/components/doodle/doodle_service.h b/components/doodle/doodle_service.h
new file mode 100644
index 0000000..09e1ae1f
--- /dev/null
+++ b/components/doodle/doodle_service.h
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_DOODLE_DOODLE_SERVICE_H_
+#define COMPONENTS_DOODLE_DOODLE_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "components/doodle/doodle_fetcher.h"
+#include "components/doodle/doodle_types.h"
+
+namespace doodle {
+
+class DoodleService {
+ public:
+  class Observer {
+   public:
+    virtual void OnDoodleConfigUpdated(const base::Optional<DoodleConfig>&) = 0;
+  };
+
+  DoodleService(std::unique_ptr<DoodleFetcher> fetcher);
+  ~DoodleService();
+
+  // Returns the current (cached) config, if any.
+  const base::Optional<DoodleConfig>& config() const { return cached_config_; }
+
+  // Adds a new observer to the service. It'll only be called when the config
+  // changes; to get the current (cached) config, call |config()|.
+  void AddObserver(Observer* observer);
+
+  // Prevents |observer| from receiving future updates. This is safe to call
+  // even when the observer is being notified of an update.
+  void RemoveObserver(Observer* observer);
+
+  // Requests an asynchronous refresh of the DoodleConfig from the network.
+  // After the update completes, the observers will be notified only if the
+  // config changed.
+  void Refresh();
+
+ private:
+  void DoodleFetched(DoodleState state,
+                     const base::Optional<DoodleConfig>& doodle_config);
+
+  // The fetcher for getting fresh DoodleConfigs from the network.
+  std::unique_ptr<DoodleFetcher> fetcher_;
+
+  // The result of the last network fetch.
+  base::Optional<DoodleConfig> cached_config_;
+
+  // The list of observers to be notified when the DoodleConfig changes.
+  base::ObserverList<Observer> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(DoodleService);
+};
+
+}  // namespace doodle
+
+#endif  // COMPONENTS_DOODLE_DOODLE_SERVICE_H_
diff --git a/components/doodle/doodle_service_unittest.cc b/components/doodle/doodle_service_unittest.cc
new file mode 100644
index 0000000..48b3a10
--- /dev/null
+++ b/components/doodle/doodle_service_unittest.cc
@@ -0,0 +1,202 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/doodle/doodle_service.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/ptr_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Eq;
+using testing::StrictMock;
+
+namespace doodle {
+
+namespace {
+
+class FakeDoodleFetcher : public DoodleFetcher {
+ public:
+  FakeDoodleFetcher() = default;
+  ~FakeDoodleFetcher() override = default;
+
+  void FetchDoodle(FinishedCallback callback) override {
+    callbacks_.push_back(std::move(callback));
+  }
+
+  size_t num_pending_callbacks() const { return callbacks_.size(); }
+
+  void ServeAllCallbacks(DoodleState state,
+                         const base::Optional<DoodleConfig>& config) {
+    for (auto& callback : callbacks_) {
+      std::move(callback).Run(state, config);
+    }
+    callbacks_.clear();
+  }
+
+ private:
+  std::vector<FinishedCallback> callbacks_;
+};
+
+class MockDoodleObserver : public DoodleService::Observer {
+ public:
+  MOCK_METHOD1(OnDoodleConfigUpdated,
+               void(const base::Optional<DoodleConfig>&));
+};
+
+}  // namespace
+
+// Equality operator for DoodleConfigs, for use by testing::Eq.
+// Note: This must be outside of the anonymous namespace.
+bool operator==(const DoodleConfig& lhs, const DoodleConfig& rhs) {
+  return lhs.IsEquivalent(rhs);
+}
+
+class DoodleServiceTest : public testing::Test {
+ public:
+  DoodleServiceTest() : fetcher_(nullptr) {
+    auto fetcher = base::MakeUnique<FakeDoodleFetcher>();
+    fetcher_ = fetcher.get();
+    service_ = base::MakeUnique<DoodleService>(std::move(fetcher));
+  }
+
+  DoodleService* service() { return service_.get(); }
+  FakeDoodleFetcher* fetcher() { return fetcher_; }
+
+ private:
+  std::unique_ptr<DoodleService> service_;
+  FakeDoodleFetcher* fetcher_;
+};
+
+TEST_F(DoodleServiceTest, FetchesConfigOnRefresh) {
+  ASSERT_THAT(service()->config(), Eq(base::nullopt));
+
+  // Request a refresh of the doodle config.
+  service()->Refresh();
+  // The request should have arrived at the fetcher.
+  EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u));
+
+  // Serve it (with an arbitrary config).
+  DoodleConfig config;
+  config.doodle_type = DoodleType::SIMPLE;
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, config);
+
+  // The config should be available.
+  EXPECT_THAT(service()->config(), Eq(config));
+
+  // Request a refresh again.
+  service()->Refresh();
+  // The request should have arrived at the fetcher again.
+  EXPECT_THAT(fetcher()->num_pending_callbacks(), Eq(1u));
+
+  // Serve it with a different config.
+  DoodleConfig other_config;
+  other_config.doodle_type = DoodleType::SLIDESHOW;
+  DCHECK(!config.IsEquivalent(other_config));
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, other_config);
+
+  // The config should have been updated.
+  EXPECT_THAT(service()->config(), Eq(other_config));
+}
+
+TEST_F(DoodleServiceTest, CallsObserverOnConfigReceived) {
+  StrictMock<MockDoodleObserver> observer;
+  service()->AddObserver(&observer);
+
+  ASSERT_THAT(service()->config(), Eq(base::nullopt));
+
+  // Request a refresh of the doodle config.
+  service()->Refresh();
+  // The request should have arrived at the fetcher.
+  ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u));
+
+  // Serve it (with an arbitrary config). The observer should get notified.
+  DoodleConfig config;
+  config.doodle_type = DoodleType::SIMPLE;
+  EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(config)));
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, config);
+
+  // Remove the observer before the service gets destroyed.
+  service()->RemoveObserver(&observer);
+}
+
+TEST_F(DoodleServiceTest, CallsObserverOnConfigRemoved) {
+  // Load some doodle config.
+  service()->Refresh();
+  DoodleConfig config;
+  config.doodle_type = DoodleType::SIMPLE;
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, config);
+  ASSERT_THAT(service()->config(), Eq(config));
+
+  // Register an observer and request a refresh.
+  StrictMock<MockDoodleObserver> observer;
+  service()->AddObserver(&observer);
+  service()->Refresh();
+  ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u));
+
+  // Serve the request with an empty doodle config. The observer should get
+  // notified.
+  EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(base::nullopt)));
+  fetcher()->ServeAllCallbacks(DoodleState::NO_DOODLE, base::nullopt);
+
+  // Remove the observer before the service gets destroyed.
+  service()->RemoveObserver(&observer);
+}
+
+TEST_F(DoodleServiceTest, CallsObserverOnConfigUpdated) {
+  // Load some doodle config.
+  service()->Refresh();
+  DoodleConfig config;
+  config.doodle_type = DoodleType::SIMPLE;
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, config);
+  ASSERT_THAT(service()->config(), Eq(config));
+
+  // Register an observer and request a refresh.
+  StrictMock<MockDoodleObserver> observer;
+  service()->AddObserver(&observer);
+  service()->Refresh();
+  ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u));
+
+  // Serve the request with a different doodle config. The observer should get
+  // notified.
+  DoodleConfig other_config;
+  other_config.doodle_type = DoodleType::SLIDESHOW;
+  DCHECK(!config.IsEquivalent(other_config));
+  EXPECT_CALL(observer, OnDoodleConfigUpdated(Eq(other_config)));
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, other_config);
+
+  // Remove the observer before the service gets destroyed.
+  service()->RemoveObserver(&observer);
+}
+
+TEST_F(DoodleServiceTest, DoesNotCallObserverWhenConfigEquivalent) {
+  // Load some doodle config.
+  service()->Refresh();
+  DoodleConfig config;
+  config.doodle_type = DoodleType::SIMPLE;
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, config);
+  ASSERT_THAT(service()->config(), Eq(config));
+
+  // Register an observer and request a refresh.
+  StrictMock<MockDoodleObserver> observer;
+  service()->AddObserver(&observer);
+  service()->Refresh();
+  ASSERT_THAT(fetcher()->num_pending_callbacks(), Eq(1u));
+
+  // Serve the request with an equivalent doodle config. The observer should
+  // *not* get notified.
+  DoodleConfig equivalent_config;
+  equivalent_config.doodle_type = DoodleType::SIMPLE;
+  DCHECK(config.IsEquivalent(equivalent_config));
+  fetcher()->ServeAllCallbacks(DoodleState::AVAILABLE, equivalent_config);
+
+  // Remove the observer before the service gets destroyed.
+  service()->RemoveObserver(&observer);
+}
+
+}  // namespace doodle
diff --git a/components/doodle/doodle_types.cc b/components/doodle/doodle_types.cc
new file mode 100644
index 0000000..c0a1dff
--- /dev/null
+++ b/components/doodle/doodle_types.cc
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/doodle/doodle_types.h"
+
+namespace doodle {
+
+bool DoodleImage::operator==(const DoodleImage& other) const {
+  return url == other.url && height == other.height && width == other.width &&
+         is_animated_gif == other.is_animated_gif && is_cta == other.is_cta;
+}
+
+bool DoodleImage::operator!=(const DoodleImage& other) const {
+  return !(*this == other);
+}
+
+bool DoodleConfig::IsEquivalent(const DoodleConfig& other) const {
+  // Note: This compares all fields except for |expiry_date|. The reason is that
+  // |expiry_date| gets computed as "now + time_to_live", so when an identical
+  // config gets re-fetched, the expiry date will generally end up slightly
+  // different.
+  return doodle_type == other.doodle_type && alt_text == other.alt_text &&
+         interactive_html == other.interactive_html &&
+         search_url == other.search_url && target_url == other.target_url &&
+         fullpage_interactive_url == other.fullpage_interactive_url &&
+         large_image == other.large_image &&
+         large_cta_image == other.large_cta_image &&
+         transparent_large_image == other.transparent_large_image;
+}
+
+}  // namespace doodle
diff --git a/components/doodle/doodle_types.h b/components/doodle/doodle_types.h
index a4d92ff..49c749d8 100644
--- a/components/doodle/doodle_types.h
+++ b/components/doodle/doodle_types.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_DOODLE_DOODLE_TYPES_H_
 #define COMPONENTS_DOODLE_DOODLE_TYPES_H_
 
+#include "base/time/time.h"
 #include "url/gurl.h"
 
 namespace doodle {
@@ -32,6 +33,9 @@
   DoodleImage();
   ~DoodleImage();
 
+  bool operator==(const DoodleImage& other) const;
+  bool operator!=(const DoodleImage& other) const;
+
   GURL url;
   int height;
   int width;
@@ -48,11 +52,14 @@
   DoodleConfig(const DoodleConfig& config);  // = default;
   ~DoodleConfig();
 
+  // Checks whether this config is "equivalent" to the other. This compares all
+  // fields for equality, except for |expiry_date|.
+  bool IsEquivalent(const DoodleConfig& other) const;
+
   DoodleType doodle_type;
   std::string alt_text;
   std::string interactive_html;
 
-  base::Time expiry_date;
   GURL search_url;
   GURL target_url;
   GURL fullpage_interactive_url;
@@ -60,6 +67,11 @@
   DoodleImage large_image;
   DoodleImage large_cta_image;
   DoodleImage transparent_large_image;
+
+  // TODO(treib,fhorschig): Don't expose this? Clients don't care about it.
+  base::Time expiry_date;
+
+  // Copying and assignment allowed.
 };
 
 }  // namespace doodle
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index 79cca40..f28872d 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -287,6 +287,7 @@
   request->set_oauth2_client_id(
       GaiaUrls::GetInstance()->oauth2_chrome_client_id());
   request->add_auth_scope(GaiaConstants::kAnyApiOAuth2Scope);
+  request->set_device_type(em::DeviceServiceApiAccessRequest::CHROME);
 
   policy_fetch_request_job_->Start(
       base::Bind(&CloudPolicyClient::OnFetchRobotAuthCodesCompleted,
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index bb08d88..d35f3268 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -165,6 +165,19 @@
 
   // OAuth2 client ID to which the returned authorization code is bound.
   optional string oauth2_client_id = 2;
+
+  // Enumerates different flavors of registration.
+  enum DeviceType {
+    // Authcode will be used by Chrome OS
+    // (this is typically requested during device enrollment)
+    CHROME = 0;
+    // Authcode will be used by Android (ARC) subsystem
+    // (this is typically requested during ARC Kiosk session setup)
+    ANDROIDOS = 1;
+  };
+
+  // Device type indicates the intended use of the auth code.
+  optional DeviceType device_type = 3;
 }
 
 // Response from server to API access request.
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index c06173b2..5265026b 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -32,8 +32,6 @@
     "child_account_info_fetcher_android.h",
     "child_account_info_fetcher_impl.cc",
     "child_account_info_fetcher_impl.h",
-    "device_activity_fetcher.cc",
-    "device_activity_fetcher.h",
     "gaia_cookie_manager_service.cc",
     "gaia_cookie_manager_service.h",
     "profile_identity_provider.cc",
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java
index d5b9249..6aa4198 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/MockAccountManager.java
@@ -23,9 +23,6 @@
 import java.util.HashSet;
 import java.util.Set;
 import java.util.UUID;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 
 /**
  * The MockAccountManager helps out if you want to mock out all calls to the Android AccountManager.
@@ -47,7 +44,7 @@
 
     protected final Context mContext;
 
-    private final Set<AccountHolder> mAccounts;
+    private final Set<AccountHolder> mAccounts = new HashSet<>();
 
     // Tracks the number of in-progress getAccountsByType() tasks so that tests can wait for
     // their completion.
@@ -57,7 +54,6 @@
     public MockAccountManager(Context context, Context testContext, Account... accounts) {
         mContext = context;
         mGetAccountsTaskCounter = new ZeroCounter();
-        mAccounts = new HashSet<AccountHolder>();
         if (accounts != null) {
             for (Account account : accounts) {
                 mAccounts.add(AccountHolder.create().account(account).alwaysAccept(true).build());
@@ -65,34 +61,18 @@
         }
     }
 
-    private static class SingleThreadedExecutor extends ThreadPoolExecutor {
-        public SingleThreadedExecutor() {
-            super(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
-        }
-    }
-
     @Override
     public Account[] getAccountsByType(String type) {
         if (!AccountManagerHelper.GOOGLE_ACCOUNT_TYPE.equals(type)) {
             throw new IllegalArgumentException("Invalid account type: " + type);
         }
-        if (mAccounts == null) {
-            return new Account[0];
-        } else {
-            ArrayList<Account> validAccounts = new ArrayList<Account>();
-            for (AccountHolder ah : mAccounts) {
-                if (type.equals(ah.getAccount().type)) {
-                    validAccounts.add(ah.getAccount());
-                }
+        ArrayList<Account> validAccounts = new ArrayList<>();
+        for (AccountHolder ah : mAccounts) {
+            if (type.equals(ah.getAccount().type)) {
+                validAccounts.add(ah.getAccount());
             }
-
-            Account[] accounts = new Account[validAccounts.size()];
-            int i = 0;
-            for (Account account : validAccounts) {
-                accounts[i++] = account;
-            }
-            return accounts;
         }
+        return validAccounts.toArray(new Account[0]);
     }
 
     @Override
diff --git a/components/signin/core/browser/device_activity_fetcher.cc b/components/signin/core/browser/device_activity_fetcher.cc
deleted file mode 100644
index 39b7433..0000000
--- a/components/signin/core/browser/device_activity_fetcher.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/signin/core/browser/device_activity_fetcher.h"
-
-#include "base/strings/stringprintf.h"
-#include "components/signin/core/browser/signin_client.h"
-#include "google_apis/gaia/gaia_auth_fetcher.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-#include "net/http/http_status_code.h"
-#include "net/url_request/url_fetcher.h"
-
-namespace {
-
-// TODO(mlerman,anthonyvd): Replace the three parameters following with the
-// necessary parameters for calling the ListDevices endpoint, once live.
-// crbug.com/463611 for details!
-const char kSyncListDevicesScope[] =
-    "https://www.googleapis.com/auth/chromesynclistdevices";
-const char kChromeDomain[] = "http://www.chrome.com";
-const char kListDevicesEndpoint[] = "http://127.0.0.1";
-// Template for optional authorization header when using an OAuth access token.
-const char kAuthorizationHeader[] = "Authorization: Bearer %s";
-
-// In case of an error while fetching using the GaiaAuthFetcher, retry with
-// exponential backoff. Try up to 9 times within 10 minutes.
-const net::BackoffEntry::Policy kBackoffPolicy = {
-    // Number of initial errors (in sequence) to ignore before applying
-    // exponential back-off rules.
-    0,
-    // Initial delay for exponential backoff in ms.
-    1000,
-    // Factor by which the waiting time will be multiplied.
-    2,
-    // Fuzzing percentage. ex: 10% will spread requests randomly
-    // between 90%-100% of the calculated time.
-    0.1,  // 10%
-    // Maximum amount of time we are willing to delay our request in ms.
-    1000 * 60 * 15,  // 15 minutes.
-    // Time to keep an entry from being discarded even when it
-    // has no significant state, -1 to never discard.
-    -1,
-    // Don't use initial delay unless the last request was an error.
-    false,
-};
-
-const int kMaxFetcherRetries = 9;
-
-}  // namespace
-
-DeviceActivityFetcher::DeviceActivityFetcher(
-    SigninClient* signin_client,
-    DeviceActivityFetcher::Observer* observer)
-    : fetcher_backoff_(&kBackoffPolicy),
-      fetcher_retries_(0),
-      signin_client_(signin_client),
-      observer_(observer) {
-}
-
-DeviceActivityFetcher::~DeviceActivityFetcher() {
-}
-
-void DeviceActivityFetcher::Start() {
-  fetcher_retries_ = 0;
-  login_hint_ = std::string();
-  StartFetchingListIdpSessions();
-}
-
-void DeviceActivityFetcher::Stop() {
-  if (gaia_auth_fetcher_)
-    gaia_auth_fetcher_->CancelRequest();
-  if (url_fetcher_)
-    url_fetcher_.reset();
-}
-
-void DeviceActivityFetcher::StartFetchingListIdpSessions() {
-  gaia_auth_fetcher_.reset(signin_client_->CreateGaiaAuthFetcher(
-      this, GaiaConstants::kChromeSource,
-      signin_client_->GetURLRequestContext()));
-  gaia_auth_fetcher_->StartListIDPSessions(kSyncListDevicesScope,
-                                           kChromeDomain);
-}
-
-void DeviceActivityFetcher::StartFetchingGetTokenResponse() {
-  gaia_auth_fetcher_.reset(signin_client_->CreateGaiaAuthFetcher(
-      this, GaiaConstants::kChromeSource,
-      signin_client_->GetURLRequestContext()));
-  gaia_auth_fetcher_->StartGetTokenResponse(kSyncListDevicesScope,
-                                            kChromeDomain, login_hint_);
-}
-
-void DeviceActivityFetcher::StartFetchingListDevices() {
-  // Call the Sync Endpoint.
-  url_fetcher_ = net::URLFetcher::Create(GURL(kListDevicesEndpoint),
-                                         net::URLFetcher::GET, this);
-  url_fetcher_->SetRequestContext(signin_client_->GetURLRequestContext());
-  if (!access_token_.empty()) {
-    url_fetcher_->SetExtraRequestHeaders(
-        base::StringPrintf(kAuthorizationHeader, access_token_.c_str()));
-  }
-  url_fetcher_->Start();
-}
-
-void DeviceActivityFetcher::OnURLFetchComplete(const net::URLFetcher* source) {
-  // TODO(mlerman): Uncomment the code below once we have a working proto.
-  // Work done under crbug.com/463611
-
-  // std::string response_string;
-  // ListDevices list_devices;
-
-  // if (!source->GetStatus().is_success()) {
-  //   VLOG(1) << "Failed to fetch listdevices response. Retrying.";
-  //   if (++fetcher_retries_ < kMaxFetcherRetries) {
-  //     fetcher_backoff_.InformOfRequest(false);
-  //     fetcher_timer_.Start(
-  //         FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), this,
-  //         &DeviceActivityFetcher::StartFetchingListDevices);
-  //     return;
-  //   } else {
-  //     observer_->OnFetchDeviceActivityFailure();
-  //     return;
-  //   }
-  // }
-
-  // net::HttpStatusCode response_status = static_cast<net::HttpStatusCode>(
-  //     source->GetResponseCode());
-  // if (response_status == net::HTTP_BAD_REQUEST ||
-  //     response_status == net::HTTP_UNAUTHORIZED) {
-  //   // BAD_REQUEST indicates that the request was malformed.
-  //   // UNAUTHORIZED indicates that security token didn't match the id.
-  //   VLOG(1) << "No point retrying the checkin with status: "
-  //              << response_status << ". Checkin failed.";
-  //   CheckinRequestStatus status = response_status == net::HTTP_BAD_REQUEST ?
-  //       HTTP_BAD_REQUEST : HTTP_UNAUTHORIZED;
-  //   RecordCheckinStatusAndReportUMA(status, recorder_, false);
-  //   callback_.Run(response_proto);
-  //   return;
-  // }
-
-  // if (response_status != net::HTTP_OK ||
-  //     !source->GetResponseAsString(&response_string) ||
-  //     !list_devices.ParseFromString(response_string)) {
-  //   LOG(ERROR) << "Failed to get list devices response. HTTP Status: "
-  //              << response_status;
-  //   if (++fetcher_retries_ < kMaxFetcherRetries) {
-  //     fetcher_backoff_.InformOfRequest(false);
-  //     fetcher_timer_.Start(
-  //         FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(), this,
-  //         &DeviceActivityFetcher::StartFetchingListDevices);
-  //     return;
-  //   } else {
-  //     observer_->OnFetchDeviceActivityFailure();
-  //     return;
-  //   }
-  // }
-
-  std::vector<DeviceActivity> devices;
-  // TODO(mlerman): Fill |devices| from the proto in |source|.
-
-  // Call this last as OnFetchDeviceActivitySuccess will delete |this|.
-  observer_->OnFetchDeviceActivitySuccess(devices);
-}
-
-void DeviceActivityFetcher::OnListIdpSessionsSuccess(
-    const std::string& login_hint) {
-  fetcher_backoff_.InformOfRequest(true);
-  login_hint_ = login_hint;
-  access_token_ = std::string();
-  StartFetchingGetTokenResponse();
-}
-
-void DeviceActivityFetcher::OnListIdpSessionsError(
-    const GoogleServiceAuthError& error) {
-  if (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) {
-    fetcher_backoff_.InformOfRequest(false);
-    fetcher_timer_.Start(FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(),
-                         this,
-                         &DeviceActivityFetcher::StartFetchingListIdpSessions);
-    return;
-  }
-  observer_->OnFetchDeviceActivityFailure();
-}
-
-void DeviceActivityFetcher::OnGetTokenResponseSuccess(
-    const ClientOAuthResult& result) {
-  fetcher_backoff_.InformOfRequest(true);
-  access_token_ = result.access_token;
-  StartFetchingListDevices();
-}
-
-void DeviceActivityFetcher::OnGetTokenResponseError(
-    const GoogleServiceAuthError& error) {
-  if (++fetcher_retries_ < kMaxFetcherRetries && error.IsTransientError()) {
-    fetcher_backoff_.InformOfRequest(false);
-    fetcher_timer_.Start(FROM_HERE, fetcher_backoff_.GetTimeUntilRelease(),
-                         this,
-                         &DeviceActivityFetcher::StartFetchingGetTokenResponse);
-    return;
-  }
-  observer_->OnFetchDeviceActivityFailure();
-}
diff --git a/components/signin/core/browser/device_activity_fetcher.h b/components/signin/core/browser/device_activity_fetcher.h
deleted file mode 100644
index f91e7dfc..0000000
--- a/components/signin/core/browser/device_activity_fetcher.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ACTVITIY_FETCHER_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ACTVITIY_FETCHER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "google_apis/gaia/gaia_auth_consumer.h"
-#include "net/base/backoff_entry.h"
-#include "net/url_request/url_fetcher_delegate.h"
-
-class GaiaAuthFetcher;
-class SigninClient;
-
-namespace net {
-class URLFetcher;
-}
-
-class DeviceActivityFetcher : public GaiaAuthConsumer,
-                              public net::URLFetcherDelegate {
- public:
-  struct DeviceActivity {
-    base::Time last_active;
-    std::string name;
-  };
-
-  class Observer {
-   public:
-    virtual void OnFetchDeviceActivitySuccess(
-        const std::vector<DeviceActivityFetcher::DeviceActivity>& devices) = 0;
-    virtual void OnFetchDeviceActivityFailure() {}
-  };
-
-  DeviceActivityFetcher(SigninClient* signin_client,
-                        DeviceActivityFetcher::Observer* observer);
-  ~DeviceActivityFetcher() override;
-
-  void Start();
-  void Stop();
-
-  // GaiaAuthConsumer:
-  void OnListIdpSessionsSuccess(const std::string& login_hint) override;
-  void OnListIdpSessionsError(const GoogleServiceAuthError& error) override;
-  void OnGetTokenResponseSuccess(const ClientOAuthResult& result) override;
-  void OnGetTokenResponseError(const GoogleServiceAuthError& error) override;
-
-  // net::URLFetcherDelegate:
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
-
- private:
-  void StartFetchingListIdpSessions();
-  void StartFetchingGetTokenResponse();
-  void StartFetchingListDevices();
-
-  // Gaia fetcher used for acquiring an access token.
-  std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
-  // URL Fetcher used for calling List Devices.
-  std::unique_ptr<net::URLFetcher> url_fetcher_;
-
-  // If either fetcher fails, retry with exponential backoff.
-  net::BackoffEntry fetcher_backoff_;
-  base::OneShotTimer fetcher_timer_;
-  int fetcher_retries_;
-
-  std::string access_token_;
-  std::string login_hint_;
-
-  SigninClient* signin_client_;  // Weak pointer.
-  Observer* observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeviceActivityFetcher);
-};
-
-#endif  // COMPONENTS_SIGNIN_CORE_BROWSER_DEVICE_ACTVITIY_FETCHER_H_
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 4a5e874..2d3d66f 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -290,7 +290,10 @@
 // The JavaScript API for payments on the web.
 const base::Feature kWebPayments{"WebPayments",
                                  base::FEATURE_ENABLED_BY_DEFAULT};
-
+#else
+// The JavaScript API for payments on the web.
+const base::Feature kWebPayments{"WebPayments",
+                                 base::FEATURE_DISABLED_BY_DEFAULT};
 #endif
 
 #if !defined(OS_ANDROID)
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index d2c13ef3..5017bbc 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -64,6 +64,7 @@
 CONTENT_EXPORT extern const base::Feature kVrShell;
 CONTENT_EXPORT extern const base::Feature kWebAssembly;
 CONTENT_EXPORT extern const base::Feature kWebGLImageChromium;
+CONTENT_EXPORT extern const base::Feature kWebPayments;
 CONTENT_EXPORT extern const base::Feature kWebRtcEcdsaDefault;
 CONTENT_EXPORT extern const base::Feature kWebRtcHWH264Encoding;
 CONTENT_EXPORT extern const base::Feature kWebRtcUseEchoCanceller3;
@@ -78,7 +79,6 @@
 CONTENT_EXPORT extern const base::Feature kImeThread;
 CONTENT_EXPORT extern const base::Feature kSeccompSandboxAndroid;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
-CONTENT_EXPORT extern const base::Feature kWebPayments;
 #endif  // defined(OS_ANDROID)
 
 #if !defined(OS_ANDROID)
diff --git a/device/bluetooth/DEPS b/device/bluetooth/DEPS
index 94bd004..4e151328 100644
--- a/device/bluetooth/DEPS
+++ b/device/bluetooth/DEPS
@@ -2,7 +2,6 @@
   "+chromeos/dbus",
   "+crypto",
   "+dbus",
-  "+grit",
   "+jni",
   "+net/base",
   "+net/log",
diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc
index 9aaf8e4..587fcb7c 100644
--- a/device/bluetooth/bluetooth_device.cc
+++ b/device/bluetooth/bluetooth_device.cc
@@ -19,7 +19,7 @@
 #include "device/bluetooth/bluetooth_remote_gatt_descriptor.h"
 #include "device/bluetooth/bluetooth_remote_gatt_service.h"
 #include "device/bluetooth/string_util_icu.h"
-#include "grit/bluetooth_strings.h"
+#include "device/bluetooth/strings/grit/bluetooth_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace device {
diff --git a/device/bluetooth/strings/BUILD.gn b/device/bluetooth/strings/BUILD.gn
index 6eaeb0ce..2c27693 100644
--- a/device/bluetooth/strings/BUILD.gn
+++ b/device/bluetooth/strings/BUILD.gn
@@ -6,6 +6,7 @@
 
 grit("strings") {
   source = "../bluetooth_strings.grd"
+  use_qualified_include = true
   outputs = [
     "grit/bluetooth_strings.h",
     "bluetooth_strings_am.pak",
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 5766031..ce5f6a9 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -13,6 +13,7 @@
 
 grit("resources") {
   source = "app_shell_resources.grd"
+  use_qualified_include = true
   outputs = [
     "grit/app_shell_resources.h",
     "app_shell_resources.pak",
diff --git a/extensions/shell/DEPS b/extensions/shell/DEPS
index 74cb6bb..eeb99ae5 100644
--- a/extensions/shell/DEPS
+++ b/extensions/shell/DEPS
@@ -16,10 +16,5 @@
   # the embedder before being used by the extension.
   "+components/storage_monitor",
 
-  # Only allow app_shell and extensions resources, not general Chrome ones.
-  "-grit",
-  "+grit/app_shell_resources.h",
-  "+grit/extensions_resources.h",
-
   # Real DEPS go in subdirectories, for example extensions/shell/browser/DEPS.
 ]
diff --git a/extensions/shell/common/DEPS b/extensions/shell/common/DEPS
index bee8c47a..fe06bdc 100644
--- a/extensions/shell/common/DEPS
+++ b/extensions/shell/common/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+components/nacl/common",
   "+components/nacl/renderer/plugin",
+  "+extensions/shell/grit",
   "+ppapi",
 ]
diff --git a/extensions/shell/common/shell_extensions_client.cc b/extensions/shell/common/shell_extensions_client.cc
index 6ecb7d78..210d6cf 100644
--- a/extensions/shell/common/shell_extensions_client.cc
+++ b/extensions/shell/common/shell_extensions_client.cc
@@ -27,7 +27,7 @@
 #include "extensions/shell/common/api/shell_behavior_features.h"
 #include "extensions/shell/common/api/shell_manifest_features.h"
 #include "extensions/shell/common/api/shell_permission_features.h"
-#include "grit/app_shell_resources.h"
+#include "extensions/shell/grit/app_shell_resources.h"
 
 namespace extensions {
 
diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn
index b8b8280..127c96fc 100644
--- a/gpu/BUILD.gn
+++ b/gpu/BUILD.gn
@@ -108,6 +108,7 @@
     "command_buffer/client/gles2_interface_stub.h",
     "command_buffer/service/error_state_mock.cc",
     "command_buffer/service/gles2_cmd_decoder_mock.cc",
+    "test_message_loop_type.h",
   ]
 
   public_deps = [
@@ -119,6 +120,9 @@
     "//testing/gtest",
     "//ui/gl:gl_unittest_utils",
   ]
+  if (use_ozone) {
+    deps += [ "//ui/ozone" ]
+  }
 }
 
 test("gl_tests") {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
index e9558e0..8cdeb34 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -23,6 +23,7 @@
 #include "gpu/command_buffer/service/program_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/vertex_attrib_manager.h"
+#include "gpu/test_message_loop_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_mock.h"
 #include "ui/gl/init/gl_factory.h"
@@ -90,7 +91,7 @@
 const uint32_t kMaxColorAttachments = 16;
 const uint32_t kMaxDrawBuffers = 16;
 
-}  // namespace Anonymous
+}  // namespace
 
 namespace gpu {
 namespace gles2 {
@@ -123,7 +124,8 @@
       cached_depth_mask_(true),
       cached_stencil_front_mask_(static_cast<GLuint>(-1)),
       cached_stencil_back_mask_(static_cast<GLuint>(-1)),
-      shader_language_version_(100) {
+      shader_language_version_(100),
+      message_loop_(test::GetMessageLoopTypeForGpu()) {
   memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_));
 }
 
diff --git a/gpu/command_buffer/service/gpu_service_test.cc b/gpu/command_buffer/service/gpu_service_test.cc
index d91a81a..f4e38dc 100644
--- a/gpu/command_buffer/service/gpu_service_test.cc
+++ b/gpu/command_buffer/service/gpu_service_test.cc
@@ -5,6 +5,7 @@
 #include "gpu/command_buffer/service/gpu_service_test.h"
 
 #include "gpu/command_buffer/service/test_helper.h"
+#include "gpu/test_message_loop_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gl/gl_context_stub.h"
 #include "ui/gl/gl_implementation.h"
@@ -16,8 +17,10 @@
 namespace gpu {
 namespace gles2 {
 
-GpuServiceTest::GpuServiceTest() : ran_setup_(false), ran_teardown_(false) {
-}
+GpuServiceTest::GpuServiceTest()
+    : ran_setup_(false),
+      ran_teardown_(false),
+      message_loop_(test::GetMessageLoopTypeForGpu()) {}
 
 GpuServiceTest::~GpuServiceTest() {
   DCHECK(ran_teardown_);
diff --git a/gpu/ipc/service/BUILD.gn b/gpu/ipc/service/BUILD.gn
index 887bb5f..ce81b1693 100644
--- a/gpu/ipc/service/BUILD.gn
+++ b/gpu/ipc/service/BUILD.gn
@@ -153,6 +153,7 @@
     ":test_support",
     "//base",
     "//base/test:test_support",
+    "//gpu:test_support",
     "//gpu/command_buffer/common",
     "//gpu/command_buffer/common:gles2_utils",
     "//gpu/command_buffer/service",
diff --git a/gpu/ipc/service/gpu_channel_unittest.cc b/gpu/ipc/service/gpu_channel_unittest.cc
index d8d0bf4..8e913f5 100644
--- a/gpu/ipc/service/gpu_channel_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_unittest.cc
@@ -10,6 +10,7 @@
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/gpu_channel_test_common.h"
+#include "gpu/test_message_loop_type.h"
 #include "ipc/ipc_test_sink.h"
 #include "ui/gl/gl_surface_stub.h"
 #include "ui/gl/init/gl_factory.h"
@@ -19,7 +20,9 @@
 
 class GpuChannelTest : public GpuChannelTestCommon {
  public:
-  GpuChannelTest() : GpuChannelTestCommon() {}
+  GpuChannelTest()
+      : GpuChannelTestCommon(),
+        message_loop_(test::GetMessageLoopTypeForGpu()) {}
   ~GpuChannelTest() override {}
 
   void SetUp() override {
diff --git a/gpu/test_message_loop_type.h b/gpu/test_message_loop_type.h
new file mode 100644
index 0000000..b2d97ff
--- /dev/null
+++ b/gpu/test_message_loop_type.h
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef GPU_TEST_MESSAGE_LOOP_TYPE_H_
+#define GPU_TEST_MESSAGE_LOOP_TYPE_H_
+
+#include "base/message_loop/message_loop.h"
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+namespace gpu {
+namespace test {
+
+// Returns the MessageLoop type needed for GPU tests. The Ozone platform may not
+// work with TYPE_DEFAULT and this needs to be checked at runtime.
+inline base::MessageLoop::Type GetMessageLoopTypeForGpu() {
+#if defined(USE_OZONE)
+  return ui::OzonePlatform::EnsureInstance()->GetMessageLoopTypeForGpu();
+#else
+  return base::MessageLoop::TYPE_DEFAULT;
+#endif
+}
+
+}  // namespace test
+}  // namespace gpu
+
+#endif  // GPU_TEST_MESSAGE_LOOP_TYPE_H_
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 8b8c6a9..cea651df 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -267,6 +267,8 @@
   if (use_aura) {
     sources += [
       "lib/browser/headless_browser_impl_aura.cc",
+      "lib/browser/headless_focus_client.cc",
+      "lib/browser/headless_focus_client.h",
       "lib/browser/headless_screen.cc",
       "lib/browser/headless_screen.h",
       "lib/browser/headless_window_parenting_client.cc",
diff --git a/headless/lib/browser/headless_browser_impl.cc b/headless/lib/browser/headless_browser_impl.cc
index e0546b1..71b79a0 100644
--- a/headless/lib/browser/headless_browser_impl.cc
+++ b/headless/lib/browser/headless_browser_impl.cc
@@ -19,6 +19,7 @@
 #include "headless/lib/browser/headless_browser_main_parts.h"
 #include "headless/lib/browser/headless_web_contents_impl.h"
 #include "headless/lib/headless_content_main_delegate.h"
+#include "ui/aura/client/focus_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/events/devices/device_data_manager.h"
diff --git a/headless/lib/browser/headless_browser_impl.h b/headless/lib/browser/headless_browser_impl.h
index e622227..864e49c 100644
--- a/headless/lib/browser/headless_browser_impl.h
+++ b/headless/lib/browser/headless_browser_impl.h
@@ -19,6 +19,12 @@
 
 #if defined(USE_AURA)
 #include "headless/lib/browser/headless_window_tree_host.h"
+
+namespace aura {
+namespace client {
+class FocusClient;
+}
+}
 #endif
 
 namespace headless {
@@ -81,7 +87,9 @@
   // is used for all web contents. We should probably use one
   // window per web contents, but additional investigation is needed.
   std::unique_ptr<HeadlessWindowTreeHost> window_tree_host_;
+  std::unique_ptr<aura::client::FocusClient> focus_client_;
 #endif
+
   base::Callback<void(HeadlessBrowser*)> on_start_callback_;
   HeadlessBrowser::Options options_;
   HeadlessBrowserMainParts* browser_main_parts_;  // Not owned.
diff --git a/headless/lib/browser/headless_browser_impl_aura.cc b/headless/lib/browser/headless_browser_impl_aura.cc
index 9237e34..4718d491 100644
--- a/headless/lib/browser/headless_browser_impl_aura.cc
+++ b/headless/lib/browser/headless_browser_impl_aura.cc
@@ -6,7 +6,9 @@
 
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
+#include "headless/lib/browser/headless_focus_client.h"
 #include "headless/lib/browser/headless_screen.h"
+#include "ui/aura/client/focus_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/display/screen.h"
@@ -29,6 +31,10 @@
   window_tree_host_->InitHost();
   window_tree_host_->window()->Show();
   window_tree_host_->SetParentWindow(window_tree_host_->window());
+
+  focus_client_.reset(new HeadlessFocusClient());
+  aura::client::SetFocusClient(window_tree_host_->window(),
+                               focus_client_.get());
 }
 
 void HeadlessBrowserImpl::PlatformInitializeWebContents(
diff --git a/headless/lib/browser/headless_focus_client.cc b/headless/lib/browser/headless_focus_client.cc
new file mode 100644
index 0000000..9a4e806
--- /dev/null
+++ b/headless/lib/browser/headless_focus_client.cc
@@ -0,0 +1,63 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "headless/lib/browser/headless_focus_client.h"
+
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/window.h"
+
+namespace headless {
+
+HeadlessFocusClient::HeadlessFocusClient()
+    : focused_window_(NULL), observer_manager_(this) {}
+
+HeadlessFocusClient::~HeadlessFocusClient() {}
+
+void HeadlessFocusClient::AddObserver(
+    aura::client::FocusChangeObserver* observer) {
+  focus_observers_.AddObserver(observer);
+}
+
+void HeadlessFocusClient::RemoveObserver(
+    aura::client::FocusChangeObserver* observer) {
+  focus_observers_.RemoveObserver(observer);
+}
+
+void HeadlessFocusClient::FocusWindow(aura::Window* window) {
+  if (window && !window->CanFocus())
+    return;
+
+  if (focused_window_)
+    observer_manager_.Remove(focused_window_);
+  aura::Window* old_focused_window = focused_window_;
+  focused_window_ = window;
+  if (focused_window_)
+    observer_manager_.Add(focused_window_);
+
+  for (aura::client::FocusChangeObserver& observer : focus_observers_)
+    observer.OnWindowFocused(focused_window_, old_focused_window);
+  aura::client::FocusChangeObserver* observer =
+      aura::client::GetFocusChangeObserver(old_focused_window);
+  if (observer)
+    observer->OnWindowFocused(focused_window_, old_focused_window);
+  observer = aura::client::GetFocusChangeObserver(focused_window_);
+  if (observer)
+    observer->OnWindowFocused(focused_window_, old_focused_window);
+}
+
+void HeadlessFocusClient::ResetFocusWithinActiveWindow(aura::Window* window) {
+  if (!window->Contains(focused_window_))
+    FocusWindow(window);
+}
+
+aura::Window* HeadlessFocusClient::GetFocusedWindow() {
+  return focused_window_;
+}
+
+void HeadlessFocusClient::OnWindowDestroying(aura::Window* window) {
+  DCHECK_EQ(window, focused_window_);
+  FocusWindow(NULL);
+}
+
+}  // namespace headless
diff --git a/headless/lib/browser/headless_focus_client.h b/headless/lib/browser/headless_focus_client.h
new file mode 100644
index 0000000..a218089e
--- /dev/null
+++ b/headless/lib/browser/headless_focus_client.h
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef HEADLESS_LIB_BROWSER_HEADLESS_FOCUS_CLIENT_H_
+#define HEADLESS_LIB_BROWSER_HEADLESS_FOCUS_CLIENT_H_
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/scoped_observer.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/window_observer.h"
+
+namespace headless {
+
+class HeadlessFocusClient : public aura::client::FocusClient,
+                            public aura::WindowObserver {
+ public:
+  HeadlessFocusClient();
+  ~HeadlessFocusClient() override;
+
+ private:
+  // Overridden from aura::client::FocusClient:
+  void AddObserver(aura::client::FocusChangeObserver* observer) override;
+  void RemoveObserver(aura::client::FocusChangeObserver* observer) override;
+  void FocusWindow(aura::Window* window) override;
+  void ResetFocusWithinActiveWindow(aura::Window* window) override;
+  aura::Window* GetFocusedWindow() override;
+
+  // Overridden from aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override;
+
+  aura::Window* focused_window_;
+  ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_;
+  base::ObserverList<aura::client::FocusChangeObserver> focus_observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(HeadlessFocusClient);
+};
+
+}  // namespace headless
+
+#endif  // HEADLESS_LIB_BROWSER_HEADLESS_FOCUS_CLIENT_H_
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
index 9d4b7ad..710fc84 100644
--- a/headless/lib/browser/headless_web_contents_impl.cc
+++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/bindings_policy.h"
@@ -97,6 +98,10 @@
                                             security_style_explanations);
   }
 
+  void ActivateContents(content::WebContents* contents) override {
+    contents->GetRenderViewHost()->GetWidget()->Focus();
+  }
+
  private:
   HeadlessBrowserContextImpl* browser_context_;  // Not owned.
   DISALLOW_COPY_AND_ASSIGN(Delegate);
diff --git a/headless/lib/headless_web_contents_browsertest.cc b/headless/lib/headless_web_contents_browsertest.cc
index 693e2dc..3bb6265 100644
--- a/headless/lib/headless_web_contents_browsertest.cc
+++ b/headless/lib/headless_web_contents_browsertest.cc
@@ -61,6 +61,45 @@
             browser_context->GetAllWebContents().size());
 }
 
+IN_PROC_BROWSER_TEST_F(HeadlessWebContentsTest, Focus) {
+  EXPECT_TRUE(embedded_test_server()->Start());
+
+  HeadlessBrowserContext* browser_context =
+      browser()->CreateBrowserContextBuilder().Build();
+
+  HeadlessWebContents* web_contents =
+      browser_context->CreateWebContentsBuilder()
+          .SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
+          .Build();
+  EXPECT_TRUE(WaitForLoad(web_contents));
+
+  bool result;
+  EXPECT_TRUE(EvaluateScript(web_contents, "document.hasFocus()")
+                  ->GetResult()
+                  ->GetValue()
+                  ->GetAsBoolean(&result));
+  EXPECT_TRUE(result);
+
+  HeadlessWebContents* web_contents2 =
+      browser_context->CreateWebContentsBuilder()
+          .SetInitialURL(embedded_test_server()->GetURL("/hello.html"))
+          .Build();
+  EXPECT_TRUE(WaitForLoad(web_contents2));
+
+  // TODO(irisu): Focus of two web contents should be independent of the other.
+  // Both web_contents and web_contents2 should be focused at this point.
+  EXPECT_TRUE(EvaluateScript(web_contents, "document.hasFocus()")
+                  ->GetResult()
+                  ->GetValue()
+                  ->GetAsBoolean(&result));
+  EXPECT_FALSE(result);
+  EXPECT_TRUE(EvaluateScript(web_contents2, "document.hasFocus()")
+                  ->GetResult()
+                  ->GetValue()
+                  ->GetAsBoolean(&result));
+  EXPECT_TRUE(result);
+}
+
 namespace {
 bool DecodePNG(std::string base64_data, SkBitmap* bitmap) {
   std::string png_data;
diff --git a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
index ef88327..21aaa2e 100644
--- a/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
+++ b/ios/chrome/browser/resources/Settings.bundle/Experimental.plist
@@ -300,28 +300,6 @@
                 </dict>
 		<dict>
 			<key>Type</key>
-			<string>PSMultiValueSpecifier</string>
-			<key>Title</key>
-			<string>Enable Passwords Link</string>
-			<key>Key</key>
-			<string>EnablePasswordsLink</string>
-			<key>DefaultValue</key>
-			<string></string>
-			<key>Values</key>
-			<array>
-				<string></string>
-				<string>Enabled</string>
-				<string>Disabled</string>
-			</array>
-			<key>Titles</key>
-			<array>
-				<string>Default</string>
-				<string>Enabled</string>
-				<string>Disabled</string>
-			</array>
-		</dict>
-		<dict>
-			<key>Type</key>
 			<string>PSToggleSwitchSpecifier</string>
 			<key>Title</key>
 			<string>Force reset Contextual Search</string>
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index 2e80c05..74e62dd6 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -346,6 +346,7 @@
     "//components/search_engines",
     "//components/signin/core/browser",
     "//components/strings",
+    "//ios/chrome/app:app_internal",
     "//ios/chrome/app/strings",
     "//ios/chrome/app/theme",
     "//ios/chrome/browser",
@@ -353,6 +354,7 @@
     "//ios/chrome/browser/content_settings",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/ui:ui_internal",
     "//ios/chrome/browser/ui/tools_menu",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
diff --git a/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm b/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
index a67ab23..ebd4ba10 100644
--- a/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password_details_collection_view_controller.mm
@@ -16,6 +16,7 @@
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
+#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
 #import "ios/chrome/browser/ui/settings/cells/password_details_item.h"
 #import "ios/chrome/browser/ui/settings/reauthentication_module.h"
 #import "ios/chrome/browser/ui/settings/save_passwords_collection_view_controller.h"
@@ -296,7 +297,22 @@
     textCell.textLabel.textColor = [[MDCPalette greyPalette] tint500];
   }
   return view;
-};
+}
+
+- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
+                 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
+  UICollectionViewCell* cell =
+      [super collectionView:collectionView cellForItemAtIndexPath:indexPath];
+
+  NSInteger type = [self.collectionViewModel itemTypeForIndexPath:indexPath];
+  if (type == ItemTypeDelete) {
+    MDCCollectionViewTextCell* textCell =
+        base::mac::ObjCCastStrict<MDCCollectionViewTextCell>(cell);
+    textCell.textLabel.textColor = [[MDCPalette cr_redPalette] tint500];
+  }
+
+  return cell;
+}
 
 #pragma mark - UICollectionViewDelegate
 
diff --git a/ios/chrome/browser/ui/settings/settings_egtest.mm b/ios/chrome/browser/ui/settings/settings_egtest.mm
index 02994b8..c3d83aa 100644
--- a/ios/chrome/browser/ui/settings/settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/settings_egtest.mm
@@ -17,9 +17,11 @@
 #include "components/prefs/pref_member.h"
 #include "components/prefs/pref_service.h"
 #include "components/strings/grit/components_strings.h"
+#import "ios/chrome/app/main_controller.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "ios/chrome/browser/pref_names.h"
+#import "ios/chrome/browser/ui/browser_view_controller.h"
 #import "ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
 #import "ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h"
@@ -1007,4 +1009,48 @@
       performAction:grey_tap()];
 }
 
+// Verifies that the Settings UI registers keyboard commands when presented, but
+// not when it itslef presents something.
+- (void)testSettingsKeyboardCommands {
+  // Open Settings.
+  [ChromeEarlGreyUI openToolsMenu];
+  [[EarlGrey selectElementWithMatcher:SettingsButton()]
+      performAction:grey_tap()];
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(kSettingsCollectionViewId)]
+      assertWithMatcher:grey_notNil()];
+
+  // Verify that the Settings register keyboard commands.
+  MainController* mainController = chrome_test_util::GetMainController();
+  BrowserViewController* bvc =
+      [[mainController browserViewInformation] currentBVC];
+  UIViewController* settings = bvc.presentedViewController;
+  GREYAssertNotNil(settings.keyCommands,
+                   @"Settings should register key commands when presented.");
+
+  // Present the Sign-in UI.
+  id<GREYMatcher> matcher =
+      grey_allOf(grey_accessibilityID(kSettingsSignInCellId),
+                 grey_sufficientlyVisible(), nil);
+  [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_tap()];
+  // Wait for UI to finish loading the Sign-in screen.
+  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
+
+  // Verify that the Settings register keyboard commands.
+  GREYAssertNil(settings.keyCommands,
+                @"Settings should not register key commands when presented.");
+
+  // Dismiss the Sign-in UI.
+  id<GREYMatcher> cancelButton =
+      grey_allOf(grey_accessibilityID(@"cancel"),
+                 grey_accessibilityTrait(UIAccessibilityTraitButton), nil);
+  [[EarlGrey selectElementWithMatcher:cancelButton] performAction:grey_tap()];
+  // Wait for UI to finish closing the Sign-in screen.
+  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
+
+  // Verify that the Settings register keyboard commands.
+  GREYAssertNotNil(settings.keyCommands,
+                   @"Settings should register key commands when presented.");
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index b1b25e5..035118e 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -542,6 +542,9 @@
 #pragma mark - UIResponder
 
 - (NSArray*)keyCommands {
+  if ([self presentedViewController]) {
+    return nil;
+  }
   base::WeakNSObject<SettingsNavigationController> weakSelf(self);
   return @[
     [UIKeyCommand cr_keyCommandWithInput:UIKeyInputEscape
diff --git a/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm b/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm
index 3129e99..93de808 100644
--- a/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm
+++ b/ios/public/provider/chrome/browser/signin/fake_chrome_identity_interaction_manager.mm
@@ -65,6 +65,7 @@
 
   _cancelButton.reset([[UIButton buttonWithType:UIButtonTypeCustom] retain]);
   [_cancelButton setTitle:@"Cancel" forState:UIControlStateNormal];
+  [_cancelButton setAccessibilityIdentifier:@"cancel"];
   [_cancelButton addTarget:self
                     action:@selector(didTapCancel:)
           forControlEvents:UIControlEventTouchUpInside];
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc
index 15e6e868..2774610 100644
--- a/media/test/pipeline_integration_test.cc
+++ b/media/test/pipeline_integration_test.cc
@@ -737,8 +737,6 @@
     EXPECT_CALL(*this, OnDurationChange()).Times(AnyNumber());
     EXPECT_CALL(*this, OnVideoNaturalSizeChange(_)).Times(AtMost(1));
     EXPECT_CALL(*this, OnVideoOpacityChange(_)).Times(AtMost(1));
-    EXPECT_CALL(*this, OnVideoAverageKeyframeDistanceUpdate())
-        .Times(AnyNumber());
 
     source->set_demuxer_failure_cb(base::Bind(
         &PipelineIntegrationTest::OnStatusCallback, base::Unretained(this)));
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 6115e472..2c7776f 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -53,6 +53,7 @@
       last_video_frame_color_space_(COLOR_SPACE_UNSPECIFIED),
       current_duration_(kInfiniteDuration) {
   ResetVideoHash();
+  EXPECT_CALL(*this, OnVideoAverageKeyframeDistanceUpdate()).Times(AnyNumber());
 }
 
 PipelineIntegrationTestBase::~PipelineIntegrationTestBase() {
diff --git a/net/base/crypto_module.h b/net/base/crypto_module.h
index e3765e9..67e8a17 100644
--- a/net/base/crypto_module.h
+++ b/net/base/crypto_module.h
@@ -20,8 +20,6 @@
 
 class CryptoModule;
 
-typedef std::vector<scoped_refptr<CryptoModule> > CryptoModuleList;
-
 class NET_EXPORT CryptoModule
     : public base::RefCountedThreadSafe<CryptoModule> {
  public:
diff --git a/net/cert/nss_cert_database.cc b/net/cert/nss_cert_database.cc
index 98f6882..7a580d6 100644
--- a/net/cert/nss_cert_database.cc
+++ b/net/cert/nss_cert_database.cc
@@ -140,7 +140,7 @@
   return crypto::ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
 }
 
-void NSSCertDatabase::ListModules(CryptoModuleList* modules,
+void NSSCertDatabase::ListModules(std::vector<crypto::ScopedPK11Slot>* modules,
                                   bool need_rw) const {
   modules->clear();
 
@@ -157,7 +157,8 @@
 
   PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
   while (slot_element) {
-    modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
+    modules->push_back(
+        crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_element->slot)));
     slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
                                     PR_FALSE);  // restart
   }
diff --git a/net/cert/nss_cert_database.h b/net/cert/nss_cert_database.h
index a9f5c8ac..27b06aa7 100644
--- a/net/cert/nss_cert_database.h
+++ b/net/cert/nss_cert_database.h
@@ -30,9 +30,6 @@
 
 namespace net {
 
-class CryptoModule;
-typedef std::vector<scoped_refptr<CryptoModule> > CryptoModuleList;
-
 // Provides functions to manipulate the NSS certificate stores.
 // Forwards notifications about certificate changes to the global CertDatabase
 // singleton.
@@ -146,8 +143,8 @@
 
   // Get all modules.
   // If |need_rw| is true, only writable modules will be returned.
-  // TODO(mattm): come up with better alternative to CryptoModuleList.
-  virtual void ListModules(CryptoModuleList* modules, bool need_rw) const;
+  virtual void ListModules(std::vector<crypto::ScopedPK11Slot>* modules,
+                           bool need_rw) const;
 
   // Import certificates and private keys from PKCS #12 blob into the module.
   // If |is_extractable| is false, mark the private key as being unextractable
diff --git a/net/cert/nss_cert_database_chromeos.cc b/net/cert/nss_cert_database_chromeos.cc
index d3bb89c..9f4f916 100644
--- a/net/cert/nss_cert_database_chromeos.cc
+++ b/net/cert/nss_cert_database_chromeos.cc
@@ -61,8 +61,9 @@
   return crypto::ScopedPK11Slot();
 }
 
-void NSSCertDatabaseChromeOS::ListModules(CryptoModuleList* modules,
-                                          bool need_rw) const {
+void NSSCertDatabaseChromeOS::ListModules(
+    std::vector<crypto::ScopedPK11Slot>* modules,
+    bool need_rw) const {
   NSSCertDatabase::ListModules(modules, need_rw);
 
   size_t pre_size = modules->size();
diff --git a/net/cert/nss_cert_database_chromeos.h b/net/cert/nss_cert_database_chromeos.h
index fa440575..df2585f 100644
--- a/net/cert/nss_cert_database_chromeos.h
+++ b/net/cert/nss_cert_database_chromeos.h
@@ -28,7 +28,8 @@
   // NSSCertDatabase implementation.
   void ListCertsSync(CertificateList* certs) override;
   void ListCerts(const NSSCertDatabase::ListCertsCallback& callback) override;
-  void ListModules(CryptoModuleList* modules, bool need_rw) const override;
+  void ListModules(std::vector<crypto::ScopedPK11Slot>* modules,
+                   bool need_rw) const override;
   crypto::ScopedPK11Slot GetSystemSlot() const override;
 
   // TODO(mattm): handle trust setting, deletion, etc correctly when certs exist
diff --git a/net/cert/nss_cert_database_chromeos_unittest.cc b/net/cert/nss_cert_database_chromeos_unittest.cc
index 89b5c23..f4a35d6 100644
--- a/net/cert/nss_cert_database_chromeos_unittest.cc
+++ b/net/cert/nss_cert_database_chromeos_unittest.cc
@@ -105,26 +105,26 @@
 // and does not include the software slot of the other user. (Does not check the
 // private slot, since it is the same as the public slot in tests.)
 TEST_F(NSSCertDatabaseChromeOSTest, ListModules) {
-  CryptoModuleList modules_1;
-  CryptoModuleList modules_2;
+  std::vector<crypto::ScopedPK11Slot> modules_1;
+  std::vector<crypto::ScopedPK11Slot> modules_2;
 
   db_1_->ListModules(&modules_1, false /* need_rw */);
   db_2_->ListModules(&modules_2, false /* need_rw */);
 
   bool found_1 = false;
-  for (CryptoModuleList::iterator it = modules_1.begin(); it != modules_1.end();
-       ++it) {
-    EXPECT_NE(db_2_->GetPublicSlot().get(), (*it)->os_module_handle());
-    if ((*it)->os_module_handle() == db_1_->GetPublicSlot().get())
+  for (std::vector<crypto::ScopedPK11Slot>::iterator it = modules_1.begin();
+       it != modules_1.end(); ++it) {
+    EXPECT_NE(db_2_->GetPublicSlot().get(), (*it).get());
+    if ((*it).get() == db_1_->GetPublicSlot().get())
       found_1 = true;
   }
   EXPECT_TRUE(found_1);
 
   bool found_2 = false;
-  for (CryptoModuleList::iterator it = modules_2.begin(); it != modules_2.end();
-       ++it) {
-    EXPECT_NE(db_1_->GetPublicSlot().get(), (*it)->os_module_handle());
-    if ((*it)->os_module_handle() == db_2_->GetPublicSlot().get())
+  for (std::vector<crypto::ScopedPK11Slot>::iterator it = modules_2.begin();
+       it != modules_2.end(); ++it) {
+    EXPECT_NE(db_1_->GetPublicSlot().get(), (*it).get());
+    if ((*it).get() == db_2_->GetPublicSlot().get())
       found_2 = true;
   }
   EXPECT_TRUE(found_2);
diff --git a/net/cert/nss_profile_filter_chromeos.cc b/net/cert/nss_profile_filter_chromeos.cc
index 71eeab2..ebde973 100644
--- a/net/cert/nss_profile_filter_chromeos.cc
+++ b/net/cert/nss_profile_filter_chromeos.cc
@@ -153,8 +153,8 @@
     : filter_(filter) {}
 
 bool NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate::operator()(
-    const scoped_refptr<CryptoModule>& module) const {
-  return !filter_.IsModuleAllowed(module->os_module_handle());
+    const crypto::ScopedPK11Slot& module) const {
+  return !filter_.IsModuleAllowed(module.get());
 }
 
 }  // namespace net
diff --git a/net/cert/nss_profile_filter_chromeos.h b/net/cert/nss_profile_filter_chromeos.h
index 149f081..20bcf5bf 100644
--- a/net/cert/nss_profile_filter_chromeos.h
+++ b/net/cert/nss_profile_filter_chromeos.h
@@ -7,8 +7,8 @@
 
 #include <memory>
 
+#include "base/memory/ref_counted.h"
 #include "crypto/scoped_nss_types.h"
-#include "net/base/crypto_module.h"
 #include "net/base/net_export.h"
 
 namespace net {
@@ -57,7 +57,7 @@
    public:
     explicit ModuleNotAllowedForProfilePredicate(
         const NSSProfileFilterChromeOS& filter);
-    bool operator()(const scoped_refptr<CryptoModule>& module) const;
+    bool operator()(const crypto::ScopedPK11Slot& module) const;
 
    private:
     const NSSProfileFilterChromeOS& filter_;
diff --git a/net/websockets/websocket_deflate_stream_fuzzer.cc b/net/websockets/websocket_deflate_stream_fuzzer.cc
index ca401d1..4f9bed3 100644
--- a/net/websockets/websocket_deflate_stream_fuzzer.cc
+++ b/net/websockets/websocket_deflate_stream_fuzzer.cc
@@ -11,6 +11,7 @@
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/test/fuzzed_data_provider.h"
 #include "net/base/completion_callback.h"
@@ -28,16 +29,30 @@
 
 namespace {
 
+// If there are less random bytes left than MIN_BYTES_TO_CREATE_A_FRAME then
+// CreateFrame() will always create an empty frame. Since the fuzzer can create
+// the same empty frame with MIN_BYTES_TO_CREATE_A_FRAME bytes of input, save it
+// from exploring a large space of ways to do the same thing.
+constexpr size_t MIN_BYTES_TO_CREATE_A_FRAME = 3;
+
+constexpr size_t BYTES_CONSUMED_BY_PARAMS = 2;
+
+// If there are exactly BYTES_CONSUMED_BY_PARAMS + MIN_BYTES_TO_CREATE_A_FRAME
+// bytes of input, then the fuzzer will test a single frame. In order to also
+// test the case with zero frames, allow one less byte than this.
+constexpr size_t MIN_USEFUL_SIZE =
+    BYTES_CONSUMED_BY_PARAMS + MIN_BYTES_TO_CREATE_A_FRAME - 1;
+
 class WebSocketFuzzedStream final : public WebSocketStream {
  public:
-  WebSocketFuzzedStream(const uint8_t* data, size_t size)
-      : fuzzed_data_provider_(data, size) {}
+  WebSocketFuzzedStream(base::FuzzedDataProvider* fuzzed_data_provider)
+      : fuzzed_data_provider_(fuzzed_data_provider) {}
 
   int ReadFrames(std::vector<std::unique_ptr<WebSocketFrame>>* frames,
                  const CompletionCallback& callback) override {
-    if (fuzzed_data_provider_.remaining_bytes() == 0)
+    if (fuzzed_data_provider_->remaining_bytes() < MIN_BYTES_TO_CREATE_A_FRAME)
       return ERR_CONNECTION_CLOSED;
-    while (fuzzed_data_provider_.remaining_bytes() > 0)
+    while (fuzzed_data_provider_->remaining_bytes() > 0)
       frames->push_back(CreateFrame());
     return OK;
   }
@@ -54,38 +69,54 @@
  private:
   std::unique_ptr<WebSocketFrame> CreateFrame() {
     WebSocketFrameHeader::OpCode opcode =
-        fuzzed_data_provider_.ConsumeInt32InRange(
+        fuzzed_data_provider_->ConsumeUint32InRange(
             WebSocketFrameHeader::kOpCodeContinuation,
             WebSocketFrameHeader::kOpCodeControlUnused);
     auto frame = base::MakeUnique<WebSocketFrame>(opcode);
     // Bad news: ConsumeBool actually consumes a whole byte per call, so do
     // something hacky to conserve precious bits.
-    uint8_t flags = fuzzed_data_provider_.ConsumeUint8();
+    uint8_t flags = fuzzed_data_provider_->ConsumeUint8();
     frame->header.final = flags & 0x1;
     frame->header.reserved1 = (flags >> 1) & 0x1;
     frame->header.reserved2 = (flags >> 2) & 0x1;
     frame->header.reserved3 = (flags >> 3) & 0x1;
     frame->header.masked = (flags >> 4) & 0x1;
-    uint64_t payload_length = fuzzed_data_provider_.ConsumeInt32InRange(0, 64);
-    std::string payload = fuzzed_data_provider_.ConsumeBytes(payload_length);
+    uint64_t payload_length =
+        fuzzed_data_provider_->ConsumeUint32InRange(0, 64);
+    std::string payload = fuzzed_data_provider_->ConsumeBytes(payload_length);
     frame->data = new StringIOBuffer(payload);
     frame->header.payload_length = payload.size();
     return frame;
   }
 
-  base::FuzzedDataProvider fuzzed_data_provider_;
+  base::FuzzedDataProvider* fuzzed_data_provider_;
 };
 
 void WebSocketDeflateStreamFuzz(const uint8_t* data, size_t size) {
+  base::FuzzedDataProvider fuzzed_data_provider(data, size);
+  uint8_t flags = fuzzed_data_provider.ConsumeUint8();
+  bool server_no_context_takeover = flags & 0x1;
+  bool client_no_context_takeover = (flags >> 1) & 0x1;
+  uint8_t window_bits = fuzzed_data_provider.ConsumeUint8();
+  int server_max_window_bits = (window_bits & 0x7) + 8;
+  int client_max_window_bits = ((window_bits >> 3) & 0x7) + 8;
   // WebSocketDeflateStream needs to be constructed on each call because it
   // has state.
+  WebSocketExtension params("permessage-deflate");
+  if (server_no_context_takeover)
+    params.Add(WebSocketExtension::Parameter("server_no_context_takeover"));
+  if (client_no_context_takeover)
+    params.Add(WebSocketExtension::Parameter("client_no_context_takeover"));
+  params.Add(WebSocketExtension::Parameter(
+      "server_max_window_bits", base::IntToString(server_max_window_bits)));
+  params.Add(WebSocketExtension::Parameter(
+      "client_max_window_bits", base::IntToString(client_max_window_bits)));
   std::string failure_message;
   WebSocketDeflateParameters parameters;
-  parameters.Initialize(WebSocketExtension("permessage-deflate"),
-                        &failure_message);
+  DCHECK(parameters.Initialize(params, &failure_message)) << failure_message;
   WebSocketDeflateStream deflate_stream(
-      base::MakeUnique<WebSocketFuzzedStream>(data, size), parameters,
-      base::MakeUnique<WebSocketDeflatePredictorImpl>());
+      base::MakeUnique<WebSocketFuzzedStream>(&fuzzed_data_provider),
+      parameters, base::MakeUnique<WebSocketDeflatePredictorImpl>());
   std::vector<std::unique_ptr<net::WebSocketFrame>> frames;
   deflate_stream.ReadFrames(&frames, CompletionCallback());
 }
@@ -96,6 +127,8 @@
 
 // Entry point for LibFuzzer.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < net::MIN_USEFUL_SIZE)
+    return 0;
   net::WebSocketDeflateStreamFuzz(data, size);
 
   return 0;
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 1b338e05..a30c753 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1913,7 +1913,6 @@
 crbug.com/626703 external/wpt/mixed-content/blockable/no-opt-in/same-host-http/iframe-tag/top-level/swap-scheme-redirect/no-opt-in-blocks.https.html [ Timeout ]
 
 # ====== New tests from w3c-test-autoroller added here ======
-crbug.com/626703 external/wpt/pointerevents/pointerevent_click_during_capture-manual.html [ Timeout ]
 crbug.com/626703 external/wpt/streams/writable-streams/reentrant-strategy.dedicatedworker.html [ Timeout ]
 crbug.com/626703 external/wpt/streams/writable-streams/reentrant-strategy.html [ Timeout ]
 crbug.com/626703 external/wpt/streams/writable-streams/reentrant-strategy.serviceworker.https.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/descriptor-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/descriptor-not-found.html
index e78b249f..9982902 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/descriptor-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/characteristic/getDescriptor/descriptor-not-found.html
@@ -4,6 +4,7 @@
 <script src="../../../resources/bluetooth/bluetooth-helpers.js"></script>
 <script>
 'use strict';
+ let descriptorUUID = '00002906-0000-1000-8000-00805f9b34fb';
 promise_test(() => {
    return setBluetoothFakeAdapter('DisconnectingHealthThermometerAdapter')
     .then(() => requestDeviceWithKeyDown({
@@ -12,11 +13,12 @@
     .then(gattServer => gattServer.getPrimaryService('health_thermometer'))
     .then(service => service.getCharacteristic('measurement_interval'))
     .then(characteristic => {
-      assert_promise_rejects_with_message(
+      return assert_promise_rejects_with_message(
         // Valid Range is not present in this characteristic
-        characteristic.getDescriptor(2906),
+        characteristic.getDescriptor(descriptorUUID),
         new DOMException(
-          'No Descriptors with specified UUID found in Characteristic.',
+          'No Descriptors matching UUID ' + descriptorUUID +
+          ' found in Characteristic with UUID ' + measurement_interval.uuid + '.',
           'NotFoundError'));
     })
 }, 'Request for absent descriptor. Reject with NotFoundError.');
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-invalidates-objects.js
index dfd37d5..6bc6b1d 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-invalidates-objects.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/device-disconnects-invalidates-objects.js
@@ -28,7 +28,8 @@
           continue;
         }
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
index 98454d4..db410ed 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/server/disconnect-invalidates-objects.js
@@ -23,7 +23,8 @@
       let promises = Promise.resolve();
       for (let service of services) {
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/device-disconnects-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/device-disconnects-invalidates-objects.js
index bbbb0b1..666fb54 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/device-disconnects-invalidates-objects.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/device-disconnects-invalidates-objects.js
@@ -25,7 +25,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/disconnect-invalidates-objects.js b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/disconnect-invalidates-objects.js
index bf7a8c8..10061f10 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/disconnect-invalidates-objects.js
+++ b/third_party/WebKit/LayoutTests/bluetooth/script-tests/service/disconnect-invalidates-objects.js
@@ -23,7 +23,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-not-found.html
index f53da55..f919af15 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/delayed-discovery-service-not-found.html
@@ -13,7 +13,7 @@
     .then(gattServer => {
       return assert_promise_rejects_with_message(
         gattServer.getPrimaryService('battery_service'),
-        new DOMException('No Services with specified UUID found in Device.',
+        new DOMException('No Services matching UUID ' + battery_service.uuid + ' found in Device.',
                          'NotFoundError'));
     });
 }, 'Request for absent service. Must reject with NotFoundError even when the ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-invalidates-objects.html
index ce1c1078..c55cbc69 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-device-disconnects-invalidates-objects.html
@@ -31,7 +31,8 @@
           continue;
         }
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
index 36865b2b..6bdd14cb 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.html
@@ -26,7 +26,8 @@
       let promises = Promise.resolve();
       for (let service of services) {
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-not-found.html
index 09ab7b4..51b6ce9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryService/service-not-found.html
@@ -12,7 +12,7 @@
     .then(device => device.gatt.connect())
     .then(gattServer => assert_promise_rejects_with_message(
       gattServer.getPrimaryService('glucose'),
-      new DOMException('No Services with specified UUID found in Device.',
+      new DOMException('No Services matching UUID ' + glucose.uuid + ' found in Device.',
                        'NotFoundError')));
 }, 'Request for absent service. Reject with NotFoundError.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
index 15219a95..69141c0 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/delayed-discovery-service-with-uuid-not-found.html
@@ -13,7 +13,7 @@
     .then(gattServer => {
       return assert_promise_rejects_with_message(
         gattServer.getPrimaryServices('battery_service'),
-        new DOMException('No Services with specified UUID found in Device.',
+        new DOMException('No Services matching UUID ' + battery_service.uuid + ' found in Device.',
                          'NotFoundError'));
     });
 }, 'Request for absent service with UUID. Must reject with NotFoundError ' +
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects-with-uuid.html
index fa1b568..f1426b1 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects-with-uuid.html
@@ -31,7 +31,8 @@
           continue;
         }
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects.html
index 60af7661..fbe41394 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-device-disconnects-invalidates-objects.html
@@ -31,7 +31,8 @@
           continue;
         }
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
index 471fe2ca..eeb4815 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.html
@@ -26,7 +26,8 @@
       let promises = Promise.resolve();
       for (let service of services) {
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
index d9c15cb..4e9f3c9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.html
@@ -26,7 +26,8 @@
       let promises = Promise.resolve();
       for (let service of services) {
         let error = new DOMException(
-          'Service is no longer valid. Remember to retrieve the service ' +
+          'Service with UUID ' + service.uuid +
+          ' is no longer valid. Remember to retrieve the service ' +
           'again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found-with-uuid.html
index 688813e..4b41012 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/server/getPrimaryServices/services-not-found-with-uuid.html
@@ -12,7 +12,7 @@
     .then(device => device.gatt.connect())
     .then(gattServer => assert_promise_rejects_with_message(
       gattServer.getPrimaryServices('glucose'),
-      new DOMException('No Services with specified UUID found in Device.',
+      new DOMException('No Services matching UUID ' + glucose.uuid + ' found in Device.',
                        'NotFoundError')));
 }, 'Request for absent service. Reject with NotFoundError.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-not-found.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-not-found.html
index b16d10f..dcbf0ab2 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-not-found.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/characteristic-not-found.html
@@ -14,7 +14,8 @@
     .then(service => assert_promise_rejects_with_message(
       service.getCharacteristic('battery_level'),
       new DOMException(
-        'No Characteristics with specified UUID found in Service.',
+        'No Characteristics matching UUID ' + battery_level.uuid + ' found in Service with UUID ' +
+          generic_access.uuid + '.',
         'NotFoundError')));
 }, 'Request for absent characteristic. Reject with NotFoundError.');
 </script>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html
index 6628f20..0f19f49 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-device-disconnects-invalidates-objects.html
@@ -28,7 +28,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html
index 801dfc1..e05d356 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristic/gen-disconnect-invalidates-objects.html
@@ -26,7 +26,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found-with-uuid.html
index 70834546..c6f49d7e 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/characteristics-not-found-with-uuid.html
@@ -6,7 +6,8 @@
 'use strict';
 promise_test(() => {
   let expected = new DOMException(
-    'No Characteristics with specified UUID found in Service.',
+    'No Characteristics matching UUID ' + battery_level.uuid + ' found in Service with UUID ' +
+      heart_rate.uuid + '.',
     'NotFoundError');
   return setBluetoothFakeAdapter('HeartRateAdapter')
     .then(() => requestDeviceWithKeyDown({
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html
index 2ed03f9..3fb1fc9 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects-with-uuid.html
@@ -28,7 +28,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html
index eeb6f22a..15fce456 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-device-disconnects-invalidates-objects.html
@@ -28,7 +28,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html
index b7eb08f..fea309f 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects-with-uuid.html
@@ -26,7 +26,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html
index 2941a44..995d2894 100644
--- a/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html
+++ b/third_party/WebKit/LayoutTests/bluetooth/service/getCharacteristics/gen-disconnect-invalidates-objects.html
@@ -26,7 +26,8 @@
       let promises = Promise.resolve();
       for (let characteristic of characteristics) {
         let error = new DOMException(
-          'Characteristic is no longer valid. Remember to retrieve the ' +
+          'Characteristic with UUID ' + characteristic.uuid +
+          ' is no longer valid. Remember to retrieve the ' +
           'characteristic again after reconnecting.',
           'InvalidStateError');
         promises = promises.then(() =>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Node-removeChild-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Node-removeChild-expected.txt
deleted file mode 100644
index f751f73..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/dom/nodes/Node-removeChild-expected.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-This is a testharness.js-based test.
-PASS Passing a detached element from the main document to removeChild should not affect it. 
-PASS Passing a non-detached element from the main document to removeChild should not affect it. 
-PASS Calling removeChild on a element from the main document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached text from the main document to removeChild should not affect it. 
-PASS Passing a non-detached text from the main document to removeChild should not affect it. 
-PASS Calling removeChild on a text from the main document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached comment from the main document to removeChild should not affect it. 
-PASS Passing a non-detached comment from the main document to removeChild should not affect it. 
-PASS Calling removeChild on a comment from the main document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached element from a frame document to removeChild should not affect it. 
-PASS Passing a non-detached element from a frame document to removeChild should not affect it. 
-PASS Calling removeChild on a element from a frame document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached text from a frame document to removeChild should not affect it. 
-PASS Passing a non-detached text from a frame document to removeChild should not affect it. 
-PASS Calling removeChild on a text from a frame document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached comment from a frame document to removeChild should not affect it. 
-PASS Passing a non-detached comment from a frame document to removeChild should not affect it. 
-PASS Calling removeChild on a comment from a frame document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached element from a synthetic document to removeChild should not affect it. 
-PASS Passing a non-detached element from a synthetic document to removeChild should not affect it. 
-PASS Calling removeChild on a element from a synthetic document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached text from a synthetic document to removeChild should not affect it. 
-PASS Passing a non-detached text from a synthetic document to removeChild should not affect it. 
-PASS Calling removeChild on a text from a synthetic document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a detached comment from a synthetic document to removeChild should not affect it. 
-PASS Passing a non-detached comment from a synthetic document to removeChild should not affect it. 
-PASS Calling removeChild on a comment from a synthetic document with no children should throw NOT_FOUND_ERR. 
-PASS Passing a value that is not a Node reference to removeChild should throw TypeError. 
-Harness: the test ran to completion.
-b
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-expected.txt
deleted file mode 100644
index c1e6453..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS Test that javascript: evaluation only performs a navigation to the
-  result when the result is a string value. 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_click_during_capture-manual-automation.js b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_click_during_capture-manual-automation.js
new file mode 100644
index 0000000..75ca5848
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt_automation/pointerevents/pointerevent_click_during_capture-manual-automation.js
@@ -0,0 +1,12 @@
+importAutomationScript('/pointerevents/pointerevent_common_input.js');
+
+function inject_input() {
+  return mouseClickInTarget('#green').then(function() {
+    return mouseDragInTargets(['#blue', '#green']);
+  }).then(function() {
+    return mouseClickInTarget('#green');
+  }).then(function() {
+    return mouseDragInTargets(['#blue', '#green']);
+  });
+}
+
diff --git a/third_party/WebKit/LayoutTests/fast/css/beforeSelectorOnCodeElement.html b/third_party/WebKit/LayoutTests/fast/css/beforeSelectorOnCodeElement.html
index 6d68504..cd3e64b 100644
--- a/third_party/WebKit/LayoutTests/fast/css/beforeSelectorOnCodeElement.html
+++ b/third_party/WebKit/LayoutTests/fast/css/beforeSelectorOnCodeElement.html
@@ -3,7 +3,7 @@
 <style>
     @font-face {
         font-family: '-webkit-monospace';
-        src: local('Courier');
+        src: local('Courier'), local('Courier New');
     }
 
     /* Match Mac OS X's font fallback behavior on Windows */
diff --git a/third_party/WebKit/LayoutTests/fast/css/font-face-multiple-ranges-for-unicode-range.html b/third_party/WebKit/LayoutTests/fast/css/font-face-multiple-ranges-for-unicode-range.html
index e2295266..428f5db 100644
--- a/third_party/WebKit/LayoutTests/fast/css/font-face-multiple-ranges-for-unicode-range.html
+++ b/third_party/WebKit/LayoutTests/fast/css/font-face-multiple-ranges-for-unicode-range.html
@@ -10,77 +10,77 @@
 /* Test 0: Comma-separated list, which is valid. */
 @font-face {
     font-family:myfont_0;
-    src: local('Times');
+    src: local('Times New Roman'), local('Times');
 }
 @font-face {
     font-family:myfont_0;
-    src: local('Courier');
+    src: local('Courier New'), local('Courier');
     unicode-range: u+69, u+6a; /* 'i' and 'j' */
 }
 
 /* Test 1: Comma-separated list with three elements, which is valid. */
 @font-face {
     font-family:myfont_1;
-    src: local('Times');
+    src: local('Times New Roman'), local('Times');
 }
 @font-face {
     font-family:myfont_1;
-    src: local('Courier');
+    src: local('Courier New'), local('Courier');
     unicode-range: u+69 , u+6a ,u+6c; /* 'i', 'j', and 'l' */
 }
 
 /* Test 2: Comma-separated list with two consecutive commas, which is invalid. */
 @font-face {
     font-family:myfont_2;
-    src: local('Times');
+    src: local('Times New Roman'), local('Times');
 }
 @font-face {
     font-family:myfont_2;
-    src: local('Courier');
+    src: local('Courier New'), local('Courier');
     unicode-range: u+69, , u+6a; /* 'i' and 'j' */
 }
 
 /* Test 3: Comma-separated list with a trailing comma, which is invalid. */
 @font-face {
     font-family:myfont_3;
-    src: local('Times');
+    src: local('Times New Roman'), local('Times');
 }
 @font-face {
     font-family:myfont_3;
-    src: local('Courier');
+    src: local('Courier New'), local('Courier');
     unicode-range: u+69, u+6a,; /* 'i' and 'j' */
 }
 
 /* Test 4: Comma-separated list with a leading comma, which is invalid. */
 @font-face {
     font-family:myfont_4;
-    src: local('Times');
+    src: local('Times New Roman'), local('Times');
 }
 @font-face {
     font-family:myfont_4;
-    src: local('Courier');
+    src: local('Courier New'), local('Courier');
     unicode-range: , u+69, u+6a; /* 'i' and 'j' */
 }
 
 /* Test 5: Space-separated list, which is invalid. */
 @font-face {
     font-family:myfont_5;
-    src: local('Times');
+    src: local('Times New Roman'), local('Times');
 }
 @font-face {
     font-family:myfont_5;
-    src: local('Courier');
+    src: local('Courier New'), local('Courier');
     unicode-range: u+69 u+6a ; /* 'i' and 'j' */
 }
 
 /* Test 6: Slash-separated list, which is invalid. */
 @font-face {
     font-family:myfont_6;
-    src: local('Times');
+    src: local('Times New Roman'), local('Times');
 }
 @font-face {
     font-family:myfont_6;
-    src: local('Courier');
+    src: local('Courier New'), local('Courier');
     unicode-range: u+69/u+6a; /* 'i' and 'j' */
 }
 
diff --git a/third_party/WebKit/LayoutTests/media/controls/settings-disable-controls.html b/third_party/WebKit/LayoutTests/media/controls/settings-disable-controls.html
index 5bd7c115..2aac53bc 100644
--- a/third_party/WebKit/LayoutTests/media/controls/settings-disable-controls.html
+++ b/third_party/WebKit/LayoutTests/media/controls/settings-disable-controls.html
@@ -6,6 +6,11 @@
 <script src="../media-controls.js"></script>
 <video controls></video>
 <script>
+// |orphanedVideo| is used to check that the setting should not be despatched
+// if the MediaControls of a video haven't been initialized.
+var orphanedVideo = document.createElement('video');
+orphanedVideo.controls = true;
+
 async_test(t => {
   var video = document.querySelector('video');
 
diff --git a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange-expected.txt
deleted file mode 100644
index dad3fa4b..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange-expected.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-This is a testharness.js-based test.
-PASS test of input.setSelectionRange 
-PASS input typeof(input.setSelectionRange)' 
-PASS input setSelectionRange return void 
-PASS input setSelectionRange(0,1) 
-PASS input setSelectionRange(0,input.value.length+1) 
-PASS input setSelectionRange(input.value.length+1,input.value.length+1) 
-PASS input setSelectionRange(input.value.length+1,1) 
-PASS input setSelectionRange(2,2) 
-PASS input setSelectionRange(2,1) 
-PASS input direction of setSelectionRange(0,1,"backward") 
-PASS input direction of setSelectionRange(0,1,"forward") 
-PASS input direction of setSelectionRange(0,1,"none") 
-PASS input direction of setSelectionRange(0,1,"hoge") 
-PASS input direction of setSelectionRange(0,1,"BACKWARD") 
-PASS input direction of setSelectionRange(0,1) 
-PASS input setSelectionRange(1,-1) 
-PASS input setSelectionRange(-1,1) 
-PASS input setSelectionRange("string",1) 
-PASS input setSelectionRange(true,1) 
-PASS input setSelectionRange([],1) 
-PASS input setSelectionRange({},1) 
-PASS input setSelectionRange(NaN,1) 
-PASS input setSelectionRange(null,1) 
-PASS input setSelectionRange(undefined,1) 
-PASS input setSelectionRange fires a select event 
-PASS test of textarea.setSelectionRange 
-PASS textarea typeof(input.setSelectionRange)' 
-PASS textarea setSelectionRange return void 
-PASS textarea setSelectionRange(0,1) 
-PASS textarea setSelectionRange(0,textarea.value.length+1) 
-PASS textarea setSelectionRange(2,2) 
-PASS textarea setSelectionRange(2,1) 
-PASS textarea direction of setSelectionRange(0,1,"backward") 
-PASS textarea direction of setSelectionRange(0,1,"forward") 
-PASS textarea direction of setSelectionRange(0,1,"none") 
-PASS textarea direction of setSelectionRange(0,1,"hoge") 
-PASS textarea direction of setSelectionRange(0,1,"BACKWARD") 
-PASS textarea direction of setSelectionRange(0,1) 
-PASS textarea setSelectionRange("string",1) 
-PASS textarea setSelectionRange(true,1) 
-PASS textarea setSelectionRange([],1) 
-PASS textarea setSelectionRange({},1) 
-PASS textarea setSelectionRange(NaN,1) 
-PASS textarea setSelectionRange(null,1) 
-PASS textarea setSelectionRange(undefined,1) 
-PASS textarea setSelectionRange fires a select event 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
index 1f0c13d..9b6fae0 100644
--- a/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
+++ b/third_party/WebKit/LayoutTests/resources/bluetooth/bluetooth-helpers.js
@@ -64,6 +64,11 @@
   name: 'gatt.client_characteristic_configuration',
   uuid: '00002902-0000-1000-8000-00805f9b34fb'
 };
+var measurement_interval = {
+  alias: 0x2a21,
+  name: 'measurement_interval',
+  uuid: '00002a21-0000-1000-8000-00805f9b34fb'
+};
 
 // The following tests make sure the Web Bluetooth implementation
 // responds correctly to the different types of errors the
diff --git a/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp b/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp
index e2ff890..d654662e 100644
--- a/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp
@@ -36,9 +36,11 @@
 #include "bindings/core/v8/ToV8.h"
 #include "bindings/core/v8/V8Binding.h"
 #include "bindings/core/v8/V8DOMActivityLogger.h"
+#include "bindings/core/v8/V8GCForContextDispose.h"
 #include "bindings/core/v8/V8HTMLDocument.h"
 #include "bindings/core/v8/V8HiddenValue.h"
 #include "bindings/core/v8/V8Initializer.h"
+#include "bindings/core/v8/V8PagePopupControllerBinding.h"
 #include "bindings/core/v8/V8PrivateProperty.h"
 #include "bindings/core/v8/V8Window.h"
 #include "core/dom/Modulator.h"
@@ -74,7 +76,36 @@
                                                        m_world->worldId());
   MainThreadDebugger::instance()->contextWillBeDestroyed(m_scriptState.get());
 
-  WindowProxy::disposeContext(behavior);
+  if (behavior == DetachGlobal) {
+    v8::Local<v8::Context> context = m_scriptState->context();
+    // Clean up state on the global proxy, which will be reused.
+    if (!m_globalProxy.isEmpty()) {
+      // TODO(yukishiino): This DCHECK failed on Canary (M57) and Dev (M56).
+      // We need to figure out why m_globalProxy != context->Global().
+      DCHECK(m_globalProxy == context->Global());
+      DCHECK_EQ(toScriptWrappable(context->Global()),
+                toScriptWrappable(
+                    context->Global()->GetPrototype().As<v8::Object>()));
+      m_globalProxy.get().SetWrapperClassId(0);
+    }
+    V8DOMWrapper::clearNativeInfo(isolate(), context->Global());
+    m_scriptState->detachGlobalObject();
+
+#if DCHECK_IS_ON()
+    didDetachGlobalProxy();
+#endif
+  }
+
+  m_scriptState->disposePerContextData();
+
+  // It's likely that disposing the context has created a lot of
+  // garbage. Notify V8 about this so it'll have a chance of cleaning
+  // it up when idle.
+  V8GCForContextDispose::instance().notifyContextDisposed(
+      frame()->isMainFrame());
+
+  DCHECK(m_lifecycle == Lifecycle::ContextInitialized);
+  m_lifecycle = Lifecycle::ContextDetached;
 }
 
 void LocalWindowProxy::initialize() {
@@ -130,12 +161,54 @@
     frame()->loader().dispatchDidClearWindowObjectInMainWorld();
 }
 
+void LocalWindowProxy::setupWindowPrototypeChain() {
+  // Associate the window wrapper object and its prototype chain with the
+  // corresponding native DOMWindow object.
+  LocalDOMWindow* window = frame()->domWindow();
+  const WrapperTypeInfo* wrapperTypeInfo = window->wrapperTypeInfo();
+  v8::Local<v8::Context> context = m_scriptState->context();
+
+  // The global proxy object.  Note this is not the global object.
+  v8::Local<v8::Object> globalProxy = context->Global();
+  CHECK(m_globalProxy == globalProxy);
+  V8DOMWrapper::setNativeInfo(isolate(), globalProxy, wrapperTypeInfo, window);
+  // Mark the handle to be traced by Oilpan, since the global proxy has a
+  // reference to the DOMWindow.
+  m_globalProxy.get().SetWrapperClassId(wrapperTypeInfo->wrapperClassId);
+
+#if DCHECK_IS_ON()
+  didAttachGlobalProxy();
+#endif
+
+  // The global object, aka window wrapper object.
+  v8::Local<v8::Object> windowWrapper =
+      globalProxy->GetPrototype().As<v8::Object>();
+  V8DOMWrapper::setNativeInfo(isolate(), windowWrapper, wrapperTypeInfo,
+                              window);
+
+  // The prototype object of Window interface.
+  v8::Local<v8::Object> windowPrototype =
+      windowWrapper->GetPrototype().As<v8::Object>();
+  CHECK(!windowPrototype.IsEmpty());
+  V8DOMWrapper::setNativeInfo(isolate(), windowPrototype, wrapperTypeInfo,
+                              window);
+
+  // The named properties object of Window interface.
+  v8::Local<v8::Object> windowProperties =
+      windowPrototype->GetPrototype().As<v8::Object>();
+  CHECK(!windowProperties.IsEmpty());
+  V8DOMWrapper::setNativeInfo(isolate(), windowProperties, wrapperTypeInfo,
+                              window);
+
+  // TODO(keishi): Remove installPagePopupController and implement
+  // PagePopupController in another way.
+  V8PagePopupControllerBinding::installPagePopupController(context,
+                                                           windowWrapper);
+}
+
 void LocalWindowProxy::createContext() {
   // Create a new v8::Context with the window object as the global object
-  // (aka the inner global).  Reuse the global proxy object (aka the outer
-  // global) if it already exists.  See the comments in
-  // setupWindowPrototypeChain for the structure of the prototype chain of
-  // the global object.
+  // (aka the inner global). Reuse the outer global proxy if it already exists.
   v8::Local<v8::ObjectTemplate> globalTemplate =
       V8Window::domTemplate(isolate(), *m_world)->InstanceTemplate();
   CHECK(!globalTemplate.IsEmpty());
diff --git a/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.h b/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.h
index 5283aa6..c6b8cd6 100644
--- a/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.h
+++ b/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.h
@@ -62,6 +62,11 @@
   // (e.g., after setting docoument.domain).
   void updateSecurityOrigin(SecurityOrigin*);
 
+  ScriptState* getScriptState() const { return m_scriptState.get(); }
+  v8::Local<v8::Context> contextIfInitialized() const {
+    return m_scriptState ? m_scriptState->context() : v8::Local<v8::Context>();
+  }
+
  private:
   LocalWindowProxy(v8::Isolate*, LocalFrame&, RefPtr<DOMWrapperWorld>);
 
@@ -74,6 +79,10 @@
   // wrapper is not yet associated with the native DOMWindow object.
   void createContext();
 
+  // Associates the window wrapper and its prototype chain with the native
+  // DOMWindow object. Also does some more Window-specific initialization.
+  void setupWindowPrototypeChain();
+
   void setSecurityToken(SecurityOrigin*);
 
   // The JavaScript wrapper for the document object is cached on the global
@@ -85,6 +94,8 @@
   void updateActivityLogger();
 
   LocalFrame* frame() const { return toLocalFrame(WindowProxy::frame()); }
+
+  RefPtr<ScriptState> m_scriptState;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.cpp b/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.cpp
index d5c6f4a2..eaa8bbe 100644
--- a/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.cpp
@@ -28,50 +28,20 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "bindings/core/v8/WindowProxy.h"
+#include "bindings/core/v8/RemoteWindowProxy.h"
 
-#include <algorithm>
 #include <utility>
 
-#include "bindings/core/v8/ConditionalFeatures.h"
 #include "bindings/core/v8/DOMWrapperWorld.h"
-#include "bindings/core/v8/ScriptController.h"
-#include "bindings/core/v8/ToV8.h"
-#include "bindings/core/v8/V8Binding.h"
-#include "bindings/core/v8/V8DOMActivityLogger.h"
-#include "bindings/core/v8/V8Document.h"
 #include "bindings/core/v8/V8GCForContextDispose.h"
-#include "bindings/core/v8/V8HTMLCollection.h"
-#include "bindings/core/v8/V8HTMLDocument.h"
-#include "bindings/core/v8/V8HiddenValue.h"
-#include "bindings/core/v8/V8Initializer.h"
-#include "bindings/core/v8/V8ObjectConstructor.h"
-#include "bindings/core/v8/V8PagePopupControllerBinding.h"
-#include "bindings/core/v8/V8PrivateProperty.h"
 #include "bindings/core/v8/V8Window.h"
-#include "core/frame/LocalFrame.h"
-#include "core/frame/LocalFrameClient.h"
-#include "core/frame/csp/ContentSecurityPolicy.h"
-#include "core/html/DocumentNameCollection.h"
-#include "core/html/HTMLCollection.h"
-#include "core/html/HTMLIFrameElement.h"
-#include "core/inspector/InspectorInstrumentation.h"
-#include "core/inspector/MainThreadDebugger.h"
-#include "core/loader/DocumentLoader.h"
-#include "core/loader/FrameLoader.h"
-#include "core/origin_trials/OriginTrialContext.h"
 #include "platform/Histogram.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/ScriptForbiddenScope.h"
 #include "platform/heap/Handle.h"
 #include "platform/instrumentation/tracing/TraceEvent.h"
-#include "platform/weborigin/SecurityOrigin.h"
-#include "public/platform/Platform.h"
-#include "v8/include/v8-debug.h"
 #include "v8/include/v8.h"
 #include "wtf/Assertions.h"
-#include "wtf/StringExtras.h"
-#include "wtf/text/CString.h"
 
 namespace blink {
 
@@ -84,7 +54,36 @@
   if (m_lifecycle != Lifecycle::ContextInitialized)
     return;
 
-  WindowProxy::disposeContext(behavior);
+  if (behavior == DetachGlobal) {
+    v8::Local<v8::Context> context = m_scriptState->context();
+    // Clean up state on the global proxy, which will be reused.
+    if (!m_globalProxy.isEmpty()) {
+      // TODO(yukishiino): This DCHECK failed on Canary (M57) and Dev (M56).
+      // We need to figure out why m_globalProxy != context->Global().
+      DCHECK(m_globalProxy == context->Global());
+      DCHECK_EQ(toScriptWrappable(context->Global()),
+                toScriptWrappable(
+                    context->Global()->GetPrototype().As<v8::Object>()));
+      m_globalProxy.get().SetWrapperClassId(0);
+    }
+    V8DOMWrapper::clearNativeInfo(isolate(), context->Global());
+    m_scriptState->detachGlobalObject();
+
+#if DCHECK_IS_ON()
+    didDetachGlobalProxy();
+#endif
+  }
+
+  m_scriptState->disposePerContextData();
+
+  // It's likely that disposing the context has created a lot of
+  // garbage. Notify V8 about this so it'll have a chance of cleaning
+  // it up when idle.
+  V8GCForContextDispose::instance().notifyContextDisposed(
+      frame()->isMainFrame());
+
+  DCHECK(m_lifecycle == Lifecycle::ContextInitialized);
+  m_lifecycle = Lifecycle::ContextDetached;
 }
 
 void RemoteWindowProxy::initialize() {
@@ -113,12 +112,49 @@
   context->UseDefaultSecurityToken();
 }
 
+void RemoteWindowProxy::setupWindowPrototypeChain() {
+  // Associate the window wrapper object and its prototype chain with the
+  // corresponding native DOMWindow object.
+  DOMWindow* window = frame()->domWindow();
+  const WrapperTypeInfo* wrapperTypeInfo = window->wrapperTypeInfo();
+  v8::Local<v8::Context> context = m_scriptState->context();
+
+  // The global proxy object.  Note this is not the global object.
+  v8::Local<v8::Object> globalProxy = context->Global();
+  CHECK(m_globalProxy == globalProxy);
+  V8DOMWrapper::setNativeInfo(isolate(), globalProxy, wrapperTypeInfo, window);
+  // Mark the handle to be traced by Oilpan, since the global proxy has a
+  // reference to the DOMWindow.
+  m_globalProxy.get().SetWrapperClassId(wrapperTypeInfo->wrapperClassId);
+
+#if DCHECK_IS_ON()
+  didAttachGlobalProxy();
+#endif
+
+  // The global object, aka window wrapper object.
+  v8::Local<v8::Object> windowWrapper =
+      globalProxy->GetPrototype().As<v8::Object>();
+  V8DOMWrapper::setNativeInfo(isolate(), windowWrapper, wrapperTypeInfo,
+                              window);
+
+  // The prototype object of Window interface.
+  v8::Local<v8::Object> windowPrototype =
+      windowWrapper->GetPrototype().As<v8::Object>();
+  CHECK(!windowPrototype.IsEmpty());
+  V8DOMWrapper::setNativeInfo(isolate(), windowPrototype, wrapperTypeInfo,
+                              window);
+
+  // The named properties object of Window interface.
+  v8::Local<v8::Object> windowProperties =
+      windowPrototype->GetPrototype().As<v8::Object>();
+  CHECK(!windowProperties.IsEmpty());
+  V8DOMWrapper::setNativeInfo(isolate(), windowProperties, wrapperTypeInfo,
+                              window);
+}
+
 void RemoteWindowProxy::createContext() {
   // Create a new v8::Context with the window object as the global object
-  // (aka the inner global).  Reuse the global proxy object (aka the outer
-  // global) if it already exists.  See the comments in
-  // setupWindowPrototypeChain for the structure of the prototype chain of
-  // the global object.
+  // (aka the inner global). Reuse the outer global proxy if it already exists.
   v8::Local<v8::ObjectTemplate> globalTemplate =
       V8Window::domTemplate(isolate(), *m_world)->InstanceTemplate();
   CHECK(!globalTemplate.IsEmpty());
diff --git a/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.h b/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.h
index 6e6a394c..7e7fde4 100644
--- a/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.h
+++ b/third_party/WebKit/Source/bindings/core/v8/RemoteWindowProxy.h
@@ -32,12 +32,25 @@
 #define RemoteWindowProxy_h
 
 #include "bindings/core/v8/DOMWrapperWorld.h"
+#include "bindings/core/v8/ScriptState.h"
+#include "bindings/core/v8/WindowProxy.h"
 #include "core/frame/RemoteFrame.h"
 #include "v8/include/v8.h"
+#include "wtf/RefPtr.h"
 
 namespace blink {
 
 // Subclass of WindowProxy that only handles RemoteFrame.
+// TODO(dcheng): This class currently duplicates a lot of logic from
+// LocalWindowPoxy:
+// - contextIfInitialized
+// - initialize
+// - disposeContext
+// - setupWindowPrototypeChain
+// - createContext
+// This is currently duplicated to make it easier to stage the switch to using
+// v8::RemoteContext::NewRemoteContext, and will be removed once the switch
+// is complete.
 class RemoteWindowProxy final : public WindowProxy {
  public:
   static RemoteWindowProxy* create(v8::Isolate* isolate,
@@ -47,17 +60,27 @@
     return new RemoteWindowProxy(isolate, frame, std::move(world));
   }
 
+  v8::Local<v8::Context> contextIfInitialized() const {
+    return m_scriptState ? m_scriptState->context() : v8::Local<v8::Context>();
+  }
+
  private:
   RemoteWindowProxy(v8::Isolate*, RemoteFrame&, RefPtr<DOMWrapperWorld>);
 
   void initialize() override;
   void disposeContext(GlobalDetachmentBehavior) override;
 
+  // Associates the window wrapper and its prototype chain with the native
+  // DOMWindow object. Also does some more Window-specific initialization.
+  void setupWindowPrototypeChain();
+
   // Creates a new v8::Context with the window wrapper object as the global
   // object (aka the inner global).  Note that the window wrapper and its
   // prototype chain do not get fully initialized yet, e.g. the window
   // wrapper is not yet associated with the native DOMWindow object.
   void createContext();
+
+  RefPtr<ScriptState> m_scriptState;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp
index 4dcdb81..561d28e3 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp
@@ -36,8 +36,7 @@
     : m_isolate(context->GetIsolate()),
       m_context(m_isolate, context),
       m_world(world),
-      m_perContextData(V8PerContextData::create(context))
-{
+      m_perContextData(V8PerContextData::create(context)) {
   DCHECK(m_world);
   m_context.setWeak(this, &contextCollectedCallback);
   context->SetAlignedPointerInEmbedderData(v8ContextPerContextDataIndex, this);
@@ -51,9 +50,6 @@
 void ScriptState::detachGlobalObject() {
   ASSERT(!m_context.isEmpty());
   context()->DetachGlobal();
-#if DCHECK_IS_ON()
-  m_globalObjectDetached = true;
-#endif
 }
 
 void ScriptState::disposePerContextData() {
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptState.h b/third_party/WebKit/Source/bindings/core/v8/ScriptState.h
index cbe01ec0..9b60146 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptState.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptState.h
@@ -156,9 +156,6 @@
   }
   void detachGlobalObject();
   void clearContext() { return m_context.clear(); }
-#if DCHECK_IS_ON()
-  bool isGlobalObjectDetached() const { return m_globalObjectDetached; }
-#endif
 
   V8PerContextData* perContextData() const { return m_perContextData.get(); }
   void disposePerContextData();
@@ -184,10 +181,6 @@
   // disposePerContextData() once you no longer need V8PerContextData.
   // Otherwise, the v8::Context will leak.
   std::unique_ptr<V8PerContextData> m_perContextData;
-
-#if DCHECK_IS_ON()
-  bool m_globalObjectDetached = false;
-#endif
 };
 
 // ScriptStateProtectingContext keeps the context associated with the
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Binding.cpp
index 5cd2bab1..0ccf6f2 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.cpp
@@ -811,7 +811,7 @@
   return v8::Local<v8::Context>();
 }
 
-v8::Local<v8::Context> toV8Context(Frame* frame, DOMWrapperWorld& world) {
+v8::Local<v8::Context> toV8Context(LocalFrame* frame, DOMWrapperWorld& world) {
   if (!frame)
     return v8::Local<v8::Context>();
   v8::Local<v8::Context> context = toV8ContextEvenIfDetached(frame, world);
@@ -825,10 +825,10 @@
   return v8::Local<v8::Context>();
 }
 
-v8::Local<v8::Context> toV8ContextEvenIfDetached(Frame* frame,
+v8::Local<v8::Context> toV8ContextEvenIfDetached(LocalFrame* frame,
                                                  DOMWrapperWorld& world) {
   ASSERT(frame);
-  return frame->windowProxy(world)->contextIfInitialized();
+  return frame->script().windowProxy(world)->contextIfInitialized();
 }
 
 bool isValidEnum(const String& value,
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
index 24321bd..1df7fae 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8Binding.h
@@ -1010,10 +1010,10 @@
                                                DOMWrapperWorld&);
 // Returns a V8 context associated with a Frame and a DOMWrapperWorld.
 // This method returns an empty context if the frame is already detached.
-CORE_EXPORT v8::Local<v8::Context> toV8Context(Frame*, DOMWrapperWorld&);
+CORE_EXPORT v8::Local<v8::Context> toV8Context(LocalFrame*, DOMWrapperWorld&);
 // Like toV8Context but also returns the context if the frame is already
 // detached.
-CORE_EXPORT v8::Local<v8::Context> toV8ContextEvenIfDetached(Frame*,
+CORE_EXPORT v8::Local<v8::Context> toV8ContextEvenIfDetached(LocalFrame*,
                                                              DOMWrapperWorld&);
 
 // Returns the frame object of the window object associated with
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
index 1614e622..4a31146 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.cpp
@@ -32,11 +32,7 @@
 
 #include <utility>
 
-#include "bindings/core/v8/V8Binding.h"
 #include "bindings/core/v8/V8DOMWrapper.h"
-#include "bindings/core/v8/V8GCForContextDispose.h"
-#include "bindings/core/v8/V8PagePopupControllerBinding.h"
-#include "core/frame/DOMWindow.h"
 #include "core/frame/Frame.h"
 #include "v8/include/v8.h"
 #include "wtf/Assertions.h"
@@ -62,37 +58,6 @@
       m_world(std::move(world)),
       m_lifecycle(Lifecycle::ContextUninitialized) {}
 
-void WindowProxy::disposeContext(GlobalDetachmentBehavior behavior) {
-  DCHECK(m_lifecycle == Lifecycle::ContextInitialized);
-
-  if (behavior == DetachGlobal) {
-    v8::Local<v8::Context> context = m_scriptState->context();
-    // Clean up state on the global proxy, which will be reused.
-    if (!m_globalProxy.isEmpty()) {
-      // TODO(yukishiino): This DCHECK failed on Canary (M57) and Dev (M56).
-      // We need to figure out why m_globalProxy != context->Global().
-      DCHECK(m_globalProxy == context->Global());
-      DCHECK_EQ(toScriptWrappable(context->Global()),
-                toScriptWrappable(
-                    context->Global()->GetPrototype().As<v8::Object>()));
-      m_globalProxy.get().SetWrapperClassId(0);
-    }
-    V8DOMWrapper::clearNativeInfo(m_isolate, context->Global());
-    m_scriptState->detachGlobalObject();
-  }
-
-  m_scriptState->disposePerContextData();
-
-  // It's likely that disposing the context has created a lot of
-  // garbage. Notify V8 about this so it'll have a chance of cleaning
-  // it up when idle.
-  V8GCForContextDispose::instance().notifyContextDisposed(
-      m_frame->isMainFrame());
-
-  DCHECK(m_lifecycle == Lifecycle::ContextInitialized);
-  m_lifecycle = Lifecycle::ContextDetached;
-}
-
 void WindowProxy::clearForClose() {
   disposeContext(DoNotDetachGlobal);
 }
@@ -102,20 +67,15 @@
 }
 
 v8::Local<v8::Object> WindowProxy::globalIfNotDetached() {
-  if (m_lifecycle == Lifecycle::ContextInitialized) {
-    DCHECK(m_scriptState->contextIsValid());
-    DCHECK(m_globalProxy == m_scriptState->context()->Global());
+  if (m_lifecycle == Lifecycle::ContextInitialized)
     return m_globalProxy.newLocal(m_isolate);
-  }
   return v8::Local<v8::Object>();
 }
 
 v8::Local<v8::Object> WindowProxy::releaseGlobal() {
   DCHECK(m_lifecycle != Lifecycle::ContextInitialized);
-  // Make sure the global object was detached from the proxy by calling
-  // clearForNavigation().
-  if (m_lifecycle == Lifecycle::ContextDetached)
-    ASSERT(m_scriptState->isGlobalObjectDetached());
+  DLOG_IF(FATAL, m_isGlobalProxyAttached)
+      << "Context not detached by calling clearForNavigation()";
 
   v8::Local<v8::Object> global = m_globalProxy.newLocal(m_isolate);
   m_globalProxy.clear();
@@ -177,71 +137,4 @@
   }
 }
 
-void WindowProxy::setupWindowPrototypeChain() {
-  // Associate the window wrapper object and its prototype chain with the
-  // corresponding native DOMWindow object.
-  // The full structure of the global object's prototype chain is as follows:
-  //
-  // global proxy object [1]
-  //   -- has prototype --> global object (window wrapper object) [2]
-  //   -- has prototype --> Window.prototype
-  //   -- has prototype --> WindowProperties [3]
-  //   -- has prototype --> EventTarget.prototype
-  //   -- has prototype --> Object.prototype
-  //   -- has prototype --> null
-  //
-  // [1] Global proxy object is as known as "outer global object".  It's an
-  //   empty object and remains after navigation.  When navigated, points to
-  //   a different global object as the prototype object.
-  // [2] Global object is as known as "inner global object" or "window wrapper
-  //   object".  The prototype chain between global proxy object and global
-  //   object is NOT observable from user JavaScript code.  All other
-  //   prototype chains are observable.  Global proxy object and global object
-  //   together appear to be the same single JavaScript object.  See also:
-  //     https://wiki.mozilla.org/Gecko:SplitWindow
-  //   global object (= window wrapper object) provides most of Window's DOM
-  //   attributes and operations.  Also global variables defined by user
-  //   JavaScript are placed on this object.  When navigated, a new global
-  //   object is created together with a new v8::Context, but the global proxy
-  //   object doesn't change.
-  // [3] WindowProperties is a named properties object of Window interface.
-
-  DOMWindow* window = m_frame->domWindow();
-  const WrapperTypeInfo* wrapperTypeInfo = window->wrapperTypeInfo();
-  v8::Local<v8::Context> context = m_scriptState->context();
-
-  // The global proxy object.  Note this is not the global object.
-  v8::Local<v8::Object> globalProxy = context->Global();
-  CHECK(m_globalProxy == globalProxy);
-  V8DOMWrapper::setNativeInfo(m_isolate, globalProxy, wrapperTypeInfo, window);
-  // Mark the handle to be traced by Oilpan, since the global proxy has a
-  // reference to the DOMWindow.
-  m_globalProxy.get().SetWrapperClassId(wrapperTypeInfo->wrapperClassId);
-
-  // The global object, aka window wrapper object.
-  v8::Local<v8::Object> windowWrapper =
-      globalProxy->GetPrototype().As<v8::Object>();
-  windowWrapper = V8DOMWrapper::associateObjectWithWrapper(
-      m_isolate, window, wrapperTypeInfo, windowWrapper);
-
-  // The prototype object of Window interface.
-  v8::Local<v8::Object> windowPrototype =
-      windowWrapper->GetPrototype().As<v8::Object>();
-  CHECK(!windowPrototype.IsEmpty());
-  V8DOMWrapper::setNativeInfo(m_isolate, windowPrototype, wrapperTypeInfo,
-                              window);
-
-  // The named properties object of Window interface.
-  v8::Local<v8::Object> windowProperties =
-      windowPrototype->GetPrototype().As<v8::Object>();
-  CHECK(!windowProperties.IsEmpty());
-  V8DOMWrapper::setNativeInfo(m_isolate, windowProperties, wrapperTypeInfo,
-                              window);
-
-  // TODO(keishi): Remove installPagePopupController and implement
-  // PagePopupController in another way.
-  V8PagePopupControllerBinding::installPagePopupController(context,
-                                                           windowWrapper);
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h
index 0a52e8e..f0771472 100644
--- a/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h
+++ b/third_party/WebKit/Source/bindings/core/v8/WindowProxy.h
@@ -33,7 +33,7 @@
 
 #include "bindings/core/v8/DOMWrapperWorld.h"
 #include "bindings/core/v8/ScopedPersistent.h"
-#include "bindings/core/v8/ScriptState.h"
+#include "core/CoreExport.h"
 #include "platform/heap/Handle.h"
 #include "v8/include/v8.h"
 #include "wtf/RefPtr.h"
@@ -41,25 +41,98 @@
 namespace blink {
 
 class Frame;
-class ScriptController;
 
-// WindowProxy represents all the per-global object state for a Frame that
-// persist between navigations.
+// WindowProxy implements the split window model of a window for a frame. In the
+// HTML standard, the split window model is composed of the Window interface
+// (the inner global object) and the WindowProxy interface (the outer global
+// proxy).
+//
+// The Window interface is backed by the Blink DOMWindow C++ implementation.
+// In contrast, the WindowProxy interface does not have a corresponding
+// C++ implementation in Blink: the WindowProxy class defined here only manages
+// context initialization and detach. Instead, the behavior of the WindowProxy
+// interface is defined by JSGlobalProxy in v8 and the prototype chain set up
+// during context initialization.
+//
+// ====== Inner Global Object ======
+// The inner global object is the global for the script environment of a Frame.
+// Since Window and Document also have a 1:1 relationship, this means that each
+// inner global object has an associated Document which does not change. On
+// navigation, the new Document receives a new inner global object.
+//
+// However, there is one exception to the 1:1 DOMWindow:Document rule. If:
+// - the previous Document is the initial empty document
+// - the new Document is same-origin to the previous Document
+// then the inner global object will be reused for the new Document. This is the
+// only case where the associated Document of an inner global object can change.
+//
+// All methods and attributes defined on the Window interface are exposed via
+// the inner global object. Global variables defined by script running in the
+// Document also live on the inner global object.
+//
+// ====== Outer Global Proxy ====
+// The outer global proxy is reused across navigations. It implements the
+// security checks for same-origin/cross-origin access to the Window interface.
+// When the check passes (i.e. the access is same-origin), the access is
+// forwarded to the inner global object of the active Document in this
+// WindowProxy's Frame).
+//
+// When the security check fails, the access is delegated to the outer global
+// proxy's cross-origin interceptors. The cross-origin interceptors may choose
+// to return a value (if the property is exposed cross-origin) or throw an
+// exception otherwise.
+//
+// Note that the cross-origin interceptors are only used for cross-origin
+// accesses: a same-origin access to a method that is available cross-origin,
+// such as Window.postMessage, will be delegated to the inner global object.
+//
+// ====== LocalWindowProxy vs RemoteWindowProxy ======
+// WindowProxy has two concrete subclasses:
+// - LocalWindowProxy: implements the split window model for a frame in the same
+//   process, i.e. a LocalFrame.
+// - RemoteWindowProxy: implements the split window model for a frame in a
+//   different process, i.e. a RemoteFrame.
+//
+// While having a RemoteFrame implies the frame must be cross-origin, the
+// opposite is not true: a LocalFrame can be same-origin or cross-origin. One
+// additional complexity (which slightly violates the HTML standard): it is
+// possible to have SecurityOrigin::canAccess() return true for a RemoteFrame's
+// security origin; however, it is important to still deny access as if the
+// frame were cross-origin. This is due to complexities in the process
+// allocation model for renderer processes. See https://crbug.com/601629.
+//
+// ====== LocalWindowProxy/RemoteWindowProxy ======
+// Currently, the prototype chain for LocalWindowProxy and RemoteWindowProxy
+// look the same:
+//
+//   outer global proxy
+//     -- has prototype --> inner global object
+//     -- has prototype --> Window.prototype
+//     -- has prototype --> WindowProperties [1]
+//     -- has prototype --> EventTarget.prototype
+//     -- has prototype --> Object.prototype
+//     -- has prototype --> null
+//
+// [1] WindowProperties is the named properties object of the Window interface.
+//
+// There is work in progress to refactor RemoteWindowProxy to use remote v8
+// contexts, to reduce the overhead of remote frames.
+//
+// ====== References ======
+// https://wiki.mozilla.org/Gecko:SplitWindow
+// https://whatwg.org/C/browsers.html#the-windowproxy-exotic-object
 class WindowProxy : public GarbageCollectedFinalized<WindowProxy> {
  public:
   virtual ~WindowProxy();
 
   DECLARE_TRACE();
 
-  v8::Local<v8::Context> contextIfInitialized() const {
-    return m_scriptState ? m_scriptState->context() : v8::Local<v8::Context>();
-  }
   void initializeIfNeeded();
 
   void clearForClose();
   void clearForNavigation();
 
-  v8::Local<v8::Object> globalIfNotDetached();
+  CORE_EXPORT v8::Local<v8::Object> globalIfNotDetached();
   v8::Local<v8::Object> releaseGlobal();
   void setGlobal(v8::Local<v8::Object>);
 
@@ -68,10 +141,6 @@
   DOMWrapperWorld& world() { return *m_world; }
 
  protected:
-  // TODO(dcheng): Remove this friend declaration once LocalWindowProxyManager
-  // and ScriptController are merged.
-  friend class ScriptController;
-
   // A valid transition is from ContextUninitialized to ContextInitialized,
   // and then ContextDetached. Other transitions are forbidden.
   enum class Lifecycle {
@@ -85,24 +154,24 @@
   virtual void initialize() = 0;
 
   enum GlobalDetachmentBehavior { DoNotDetachGlobal, DetachGlobal };
-  virtual void disposeContext(GlobalDetachmentBehavior);
-
-  // Associates the window wrapper and its prototype chain with the native
-  // DOMWindow object.  Also does some more Window-specific initialization.
-  void setupWindowPrototypeChain();
+  virtual void disposeContext(GlobalDetachmentBehavior) = 0;
 
   v8::Isolate* isolate() const { return m_isolate; }
   Frame* frame() const { return m_frame.get(); }
-  ScriptState* getScriptState() const { return m_scriptState.get(); }
+
+#if DCHECK_IS_ON()
+  void didAttachGlobalProxy() { m_isGlobalProxyAttached = true; }
+  void didDetachGlobalProxy() { m_isGlobalProxyAttached = false; }
+#endif
 
  private:
   v8::Isolate* const m_isolate;
   const Member<Frame> m_frame;
+#if DCHECK_IS_ON()
+  bool m_isGlobalProxyAttached = false;
+#endif
 
  protected:
-  // TODO(dcheng): Move this to LocalWindowProxy once RemoteWindowProxy uses
-  // remote contexts.
-  RefPtr<ScriptState> m_scriptState;
   // TODO(dcheng): Consider making these private and using getters.
   const RefPtr<DOMWrapperWorld> m_world;
   ScopedPersistent<v8::Object> m_globalProxy;
diff --git a/third_party/WebKit/Source/core/css/CSSFontSelector.cpp b/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
index 815048f..eef8eb1 100644
--- a/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
+++ b/third_party/WebKit/Source/core/css/CSSFontSelector.cpp
@@ -152,15 +152,15 @@
     face->willUseRange(fontDescription, rangeSet);
 }
 
-bool CSSFontSelector::isPlatformFontAvailable(
+bool CSSFontSelector::isPlatformFamilyMatchAvailable(
     const FontDescription& fontDescription,
     const AtomicString& passedFamily) {
   AtomicString family = familyNameFromSettings(m_genericFontFamilySettings,
                                                fontDescription, passedFamily);
   if (family.isEmpty())
     family = passedFamily;
-  return FontCache::fontCache()->isPlatformFontAvailable(fontDescription,
-                                                         family);
+  return FontCache::fontCache()->isPlatformFamilyMatchAvailable(fontDescription,
+                                                                family);
 }
 
 void CSSFontSelector::updateGenericFontFamilySettings(Document& document) {
diff --git a/third_party/WebKit/Source/core/css/CSSFontSelector.h b/third_party/WebKit/Source/core/css/CSSFontSelector.h
index 6a590fd8..efc427b 100644
--- a/third_party/WebKit/Source/core/css/CSSFontSelector.h
+++ b/third_party/WebKit/Source/core/css/CSSFontSelector.h
@@ -60,8 +60,8 @@
   void willUseRange(const FontDescription&,
                     const AtomicString& familyName,
                     const FontDataForRangeSet&) override;
-  bool isPlatformFontAvailable(const FontDescription&,
-                               const AtomicString& family);
+  bool isPlatformFamilyMatchAvailable(const FontDescription&,
+                                      const AtomicString& family);
 
   void fontFaceInvalidated();
 
diff --git a/third_party/WebKit/Source/core/css/FontFace.cpp b/third_party/WebKit/Source/core/css/FontFace.cpp
index 8aabfcb5..b9b5f639 100644
--- a/third_party/WebKit/Source/core/css/FontFace.cpp
+++ b/third_party/WebKit/Source/core/css/FontFace.cpp
@@ -157,12 +157,12 @@
 }
 
 FontFace::FontFace(ExecutionContext* context)
-    : ContextLifecycleObserver(context), m_status(Unloaded) {}
+    : ContextClient(context), m_status(Unloaded) {}
 
 FontFace::FontFace(ExecutionContext* context,
                    const AtomicString& family,
                    const FontFaceDescriptors& descriptors)
-    : ContextLifecycleObserver(context), m_family(family), m_status(Unloaded) {
+    : ContextClient(context), m_family(family), m_status(Unloaded) {
   Document* document = toDocument(context);
   setPropertyFromString(document, descriptors.style(), CSSPropertyFontStyle);
   setPropertyFromString(document, descriptors.weight(), CSSPropertyFontWeight);
@@ -666,7 +666,7 @@
   visitor->trace(m_loadedProperty);
   visitor->trace(m_cssFontFace);
   visitor->trace(m_callbacks);
-  ContextLifecycleObserver::trace(visitor);
+  ContextClient::trace(visitor);
 }
 
 bool FontFace::hadBlankText() const {
diff --git a/third_party/WebKit/Source/core/css/FontFace.h b/third_party/WebKit/Source/core/css/FontFace.h
index a72fdb6..88e6850 100644
--- a/third_party/WebKit/Source/core/css/FontFace.h
+++ b/third_party/WebKit/Source/core/css/FontFace.h
@@ -59,7 +59,7 @@
 class CORE_EXPORT FontFace : public GarbageCollectedFinalized<FontFace>,
                              public ScriptWrappable,
                              public ActiveScriptWrappable<FontFace>,
-                             public ContextLifecycleObserver {
+                             public ContextClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(FontFace);
   WTF_MAKE_NONCOPYABLE(FontFace);
diff --git a/third_party/WebKit/Source/core/css/FontFaceSet.cpp b/third_party/WebKit/Source/core/css/FontFaceSet.cpp
index 96ffcdc..7e06f57 100644
--- a/third_party/WebKit/Source/core/css/FontFaceSet.cpp
+++ b/third_party/WebKit/Source/core/css/FontFaceSet.cpp
@@ -427,8 +427,8 @@
     return true;
   for (const FontFamily* f = &font.getFontDescription().family(); f;
        f = f->next()) {
-    if (fontSelector->isPlatformFontAvailable(font.getFontDescription(),
-                                              f->family()))
+    if (fontSelector->isPlatformFamilyMatchAvailable(font.getFontDescription(),
+                                                     f->family()))
       return true;
   }
   return false;
diff --git a/third_party/WebKit/Source/core/css/LocalFontFaceSource.cpp b/third_party/WebKit/Source/core/css/LocalFontFaceSource.cpp
index 51bafcc3..44e71876 100644
--- a/third_party/WebKit/Source/core/css/LocalFontFaceSource.cpp
+++ b/third_party/WebKit/Source/core/css/LocalFontFaceSource.cpp
@@ -13,16 +13,14 @@
 
 bool LocalFontFaceSource::isLocalFontAvailable(
     const FontDescription& fontDescription) {
-  return FontCache::fontCache()->isPlatformFontAvailable(fontDescription,
-                                                         m_fontName);
+  return FontCache::fontCache()->isPlatformFontUniqueNameMatchAvailable(
+      fontDescription, m_fontName);
 }
 
 PassRefPtr<SimpleFontData> LocalFontFaceSource::createFontData(
     const FontDescription& fontDescription) {
-  // We don't want to check alternate font family names here, so pass true as
-  // the checkingAlternateName parameter.
   RefPtr<SimpleFontData> fontData = FontCache::fontCache()->getFontData(
-      fontDescription, m_fontName, AlternateFontName::NoAlternate);
+      fontDescription, m_fontName, AlternateFontName::LocalUniqueFace);
   m_histograms.record(fontData.get());
   return fontData.release();
 }
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp
index c706599c..a84be9c1 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -784,9 +784,9 @@
     const MouseEventWithHitTestResults& event) {
   // If we got the event back, that must mean it wasn't prevented,
   // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
-  m_mouseDownMayStartSelect =
-      (canMouseDownStartSelect(event.innerNode()) || isLinkSelection(event)) &&
-      !event.scrollbar();
+  m_mouseDownMayStartSelect = (canMouseDownStartSelect(event.innerNode()) ||
+                               isSelectionOverLink(event)) &&
+                              !event.scrollbar();
   m_mouseDownWasSingleClickInSelection = false;
   if (!selection().isAvailable()) {
     // "gesture-tap-frame-removed.html" reaches here.
@@ -1079,7 +1079,7 @@
   return m_frame->selection();
 }
 
-bool isLinkSelection(const MouseEventWithHitTestResults& event) {
+bool isSelectionOverLink(const MouseEventWithHitTestResults& event) {
   return (event.event().modifiers() & WebInputEvent::Modifiers::AltKey) != 0 &&
          event.isOverLink();
 }
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.h b/third_party/WebKit/Source/core/editing/SelectionController.h
index 69abbddc..3c300d4 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.h
+++ b/third_party/WebKit/Source/core/editing/SelectionController.h
@@ -144,7 +144,7 @@
   SelectionState m_selectionState;
 };
 
-bool isLinkSelection(const MouseEventWithHitTestResults&);
+bool isSelectionOverLink(const MouseEventWithHitTestResults&);
 bool isExtendingSelection(const MouseEventWithHitTestResults&);
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
index 50de723..2afc55a 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
+++ b/third_party/WebKit/Source/core/editing/VisibleUnits.cpp
@@ -1747,6 +1747,39 @@
   return honorEditingBoundaryAtOrAfter(next, c.deepEquivalent());
 }
 
+EphemeralRange expandEndToSentenceBoundary(const EphemeralRange& range) {
+  DCHECK(range.isNotNull());
+  const VisiblePosition& visibleEnd =
+      createVisiblePosition(range.endPosition());
+  DCHECK(visibleEnd.isNotNull());
+  const Position& sentenceEnd = endOfSentence(visibleEnd).deepEquivalent();
+  // TODO(xiaochengh): |sentenceEnd < range.endPosition()| is possible,
+  // which would trigger a DCHECK in EphemeralRange's constructor if we return
+  // it directly. However, this shouldn't happen and needs to be fixed.
+  return EphemeralRange(
+      range.startPosition(),
+      sentenceEnd.isNotNull() && sentenceEnd > range.endPosition()
+          ? sentenceEnd
+          : range.endPosition());
+}
+
+EphemeralRange expandRangeToSentenceBoundary(const EphemeralRange& range) {
+  DCHECK(range.isNotNull());
+  const VisiblePosition& visibleStart =
+      createVisiblePosition(range.startPosition());
+  DCHECK(visibleStart.isNotNull());
+  const Position& sentenceStart =
+      startOfSentence(visibleStart).deepEquivalent();
+  // TODO(xiaochengh): |sentenceStart > range.startPosition()| is possible,
+  // which would trigger a DCHECK in EphemeralRange's constructor if we return
+  // it directly. However, this shouldn't happen and needs to be fixed.
+  return expandEndToSentenceBoundary(EphemeralRange(
+      sentenceStart.isNotNull() && sentenceStart < range.startPosition()
+          ? sentenceStart
+          : range.startPosition(),
+      range.endPosition()));
+}
+
 static bool nodeIsUserSelectAll(const Node* node) {
   return node && node->layoutObject() &&
          node->layoutObject()->style()->userSelect() == SELECT_ALL;
diff --git a/third_party/WebKit/Source/core/editing/VisibleUnits.h b/third_party/WebKit/Source/core/editing/VisibleUnits.h
index 42a0bafe..d53d38b 100644
--- a/third_party/WebKit/Source/core/editing/VisibleUnits.h
+++ b/third_party/WebKit/Source/core/editing/VisibleUnits.h
@@ -28,6 +28,7 @@
 
 #include "core/CoreExport.h"
 #include "core/editing/EditingBoundary.h"
+#include "core/editing/EphemeralRange.h"
 #include "core/editing/PositionWithAffinity.h"
 #include "core/editing/VisiblePosition.h"
 #include "platform/text/TextDirection.h"
@@ -180,6 +181,8 @@
 endOfSentence(const VisiblePositionInFlatTree&);
 VisiblePosition previousSentencePosition(const VisiblePosition&);
 VisiblePosition nextSentencePosition(const VisiblePosition&);
+EphemeralRange expandEndToSentenceBoundary(const EphemeralRange&);
+EphemeralRange expandRangeToSentenceBoundary(const EphemeralRange&);
 
 // lines
 // TODO(yosin) Return values of |VisiblePosition| version of |startOfLine()|
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
index 4d83f7fa..25f60f1 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/SpellChecker.cpp
@@ -80,40 +80,6 @@
   return SpellChecker::isSpellCheckingEnabledAt(selection.start());
 }
 
-static EphemeralRange expandEndToSentenceBoundary(const EphemeralRange& range) {
-  DCHECK(range.isNotNull());
-  const VisiblePosition& visibleEnd =
-      createVisiblePosition(range.endPosition());
-  DCHECK(visibleEnd.isNotNull());
-  const Position& sentenceEnd = endOfSentence(visibleEnd).deepEquivalent();
-  // TODO(xiaochengh): |sentenceEnd < range.endPosition()| is possible,
-  // which would trigger a DCHECK in EphemeralRange's constructor if we return
-  // it directly. However, this shouldn't happen and needs to be fixed.
-  return EphemeralRange(
-      range.startPosition(),
-      sentenceEnd.isNotNull() && sentenceEnd > range.endPosition()
-          ? sentenceEnd
-          : range.endPosition());
-}
-
-static EphemeralRange expandRangeToSentenceBoundary(
-    const EphemeralRange& range) {
-  DCHECK(range.isNotNull());
-  const VisiblePosition& visibleStart =
-      createVisiblePosition(range.startPosition());
-  DCHECK(visibleStart.isNotNull());
-  const Position& sentenceStart =
-      startOfSentence(visibleStart).deepEquivalent();
-  // TODO(xiaochengh): |sentenceStart > range.startPosition()| is possible,
-  // which would trigger a DCHECK in EphemeralRange's constructor if we return
-  // it directly. However, this shouldn't happen and needs to be fixed.
-  return expandEndToSentenceBoundary(EphemeralRange(
-      sentenceStart.isNotNull() && sentenceStart < range.startPosition()
-          ? sentenceStart
-          : range.startPosition(),
-      range.endPosition()));
-}
-
 SelectionInDOMTree selectWord(const VisiblePosition& position) {
   // TODO(yosin): We should fix |startOfWord()| and |endOfWord()| not to return
   // null position.
diff --git a/third_party/WebKit/Source/core/events/PointerEventFactory.cpp b/third_party/WebKit/Source/core/events/PointerEventFactory.cpp
index 600874c7..d38bd6f0 100644
--- a/third_party/WebKit/Source/core/events/PointerEventFactory.cpp
+++ b/third_party/WebKit/Source/core/events/PointerEventFactory.cpp
@@ -277,9 +277,7 @@
       // and enable this DCHECK again.
       // DCHECK_EQ(mouseEvent.id, coalescedMouseEvent.id);
 
-      // TODO(crbug.com/684292): We need further investigation of why the
-      // following DCHECK fails.
-      // DCHECK_EQ(mouseEvent.pointerType, coalescedMouseEvent.pointerType);
+      DCHECK_EQ(mouseEvent.pointerType, coalescedMouseEvent.pointerType);
       PointerEventInit coalescedEventInit = pointerEventInit;
       updateMousePointerEventInit(coalescedMouseEvent, view,
                                   &coalescedEventInit);
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
index e18b4b81..b20a17e 100644
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -381,7 +381,8 @@
   WeakMediaElementSet& elements = *it->value;
   for (const auto& element : elements) {
     element->updateControlsVisibility();
-    element->mediaControls()->onMediaControlsEnabledChange();
+    if (element->mediaControls())
+      element->mediaControls()->onMediaControlsEnabledChange();
   }
 }
 
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
index 88afa39..7909e8d 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
@@ -363,6 +363,22 @@
   // The parser is unprepared to be told, and doesn't need to be.
   if (isExecutingScript() && pendingScript->resource()->wasCanceled()) {
     pendingScript->dispose();
+
+    if (pendingScript == parserBlockingScript()) {
+      m_parserBlockingScript = nullptr;
+    } else {
+      CHECK_EQ(pendingScript, m_scriptsToExecuteAfterParsing.first());
+
+      // TODO(hiroshige): Remove this CHECK() before going to beta.
+      // This is only to make clusterfuzz to find a test case that executes
+      // this code path.
+      CHECK(false);
+
+      m_scriptsToExecuteAfterParsing.removeFirst();
+      // TODO(hiroshige): executeScriptsWaitingForParsing() should be
+      // called later at the appropriate time. https://crbug.com/696775
+    }
+
     return;
   }
 
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
index e07154a..39d1f5e2 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -978,10 +978,9 @@
 
   WebInputEventResult eventResult = updatePointerTargetAndDispatchEvents(
       EventTypeNames::mouseup, mev.innerNode(), mev.canvasRegionId(),
-      mev.event(), Vector<WebMouseEvent>());
-
-  WebInputEventResult clickEventResult =
-      m_mouseEventManager->dispatchMouseClickIfNeeded(mev);
+      mev.event(), Vector<WebMouseEvent>(),
+      !(selectionController().hasExtendedSelection() &&
+        isSelectionOverLink(mev)));
 
   m_scrollManager->clearResizeScrollableArea(false);
 
@@ -991,7 +990,7 @@
 
   m_mouseEventManager->invalidateClick();
 
-  return EventHandlingUtil::mergeEventResult(clickEventResult, eventResult);
+  return eventResult;
 }
 
 static bool targetIsFrame(Node* target, LocalFrame*& frame) {
@@ -1266,14 +1265,15 @@
     Node* targetNode,
     const String& canvasRegionId,
     const WebMouseEvent& mouseEvent,
-    const Vector<WebMouseEvent>& coalescedEvents) {
+    const Vector<WebMouseEvent>& coalescedEvents,
+    bool selectionOverLink) {
   ASSERT(mouseEventType == EventTypeNames::mousedown ||
          mouseEventType == EventTypeNames::mousemove ||
          mouseEventType == EventTypeNames::mouseup);
 
   const auto& eventResult = m_pointerEventManager->sendMousePointerEvent(
       updateMouseEventTargetNode(targetNode), canvasRegionId, mouseEventType,
-      mouseEvent, coalescedEvents);
+      mouseEvent, coalescedEvents, selectionOverLink);
   return eventResult;
 }
 
diff --git a/third_party/WebKit/Source/core/input/EventHandler.h b/third_party/WebKit/Source/core/input/EventHandler.h
index 813ea35..e3c9ce7 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.h
+++ b/third_party/WebKit/Source/core/input/EventHandler.h
@@ -305,7 +305,8 @@
       Node* target,
       const String& canvasRegionId,
       const WebMouseEvent&,
-      const Vector<WebMouseEvent>& coalescedEvents);
+      const Vector<WebMouseEvent>& coalescedEvents,
+      bool selectionOverLink = false);
 
   // Clears drag target and related states. It is called when drag is done or
   // canceled.
diff --git a/third_party/WebKit/Source/core/input/MouseEventManager.cpp b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
index 524f298..d33f5a8 100644
--- a/third_party/WebKit/Source/core/input/MouseEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
@@ -230,50 +230,50 @@
 }
 
 WebInputEventResult MouseEventManager::dispatchMouseClickIfNeeded(
-    const MouseEventWithHitTestResults& mev) {
+    Node* target,
+    const WebMouseEvent& mouseEvent,
+    const String& canvasRegionId) {
   // We only prevent click event when the click may cause contextmenu to popup.
   // However, we always send auxclick.
   bool contextMenuEvent =
       !RuntimeEnabledFeatures::auxclickEnabled() &&
-      mev.event().button == WebPointerProperties::Button::Right;
+      mouseEvent.button == WebPointerProperties::Button::Right;
 #if OS(MACOSX)
   // FIXME: The Mac port achieves the same behavior by checking whether the
   // context menu is currently open in WebPage::mouseEvent(). Consider merging
   // the implementations.
-  if (mev.event().button == WebPointerProperties::Button::Left &&
-      mev.event().modifiers() & WebInputEvent::Modifiers::ControlKey)
+  if (mouseEvent.button == WebPointerProperties::Button::Left &&
+      mouseEvent.modifiers() & WebInputEvent::Modifiers::ControlKey)
     contextMenuEvent = true;
 #endif
 
   WebInputEventResult clickEventResult = WebInputEventResult::NotHandled;
-  const bool shouldDispatchClickEvent =
-      m_clickCount > 0 && !contextMenuEvent && mev.innerNode() && m_clickNode &&
-      mev.innerNode()->canParticipateInFlatTree() &&
-      m_clickNode->canParticipateInFlatTree() &&
-      !(m_frame->eventHandler().selectionController().hasExtendedSelection() &&
-        isLinkSelection(mev));
+  const bool shouldDispatchClickEvent = m_clickCount > 0 && !contextMenuEvent &&
+                                        target && m_clickNode &&
+                                        target->canParticipateInFlatTree() &&
+                                        m_clickNode->canParticipateInFlatTree();
   if (shouldDispatchClickEvent) {
     Node* clickTargetNode = nullptr;
     // Updates distribution because a 'mouseup' event listener can make the
     // tree dirty at dispatchMouseEvent() invocation above.
     // Unless distribution is updated, commonAncestor would hit ASSERT.
-    if (m_clickNode == mev.innerNode()) {
+    if (m_clickNode == target) {
       clickTargetNode = m_clickNode;
       clickTargetNode->updateDistribution();
-    } else if (m_clickNode->document() == mev.innerNode()->document()) {
+    } else if (m_clickNode->document() == target->document()) {
       m_clickNode->updateDistribution();
-      mev.innerNode()->updateDistribution();
-      clickTargetNode = mev.innerNode()->commonAncestor(
+      target->updateDistribution();
+      clickTargetNode = target->commonAncestor(
           *m_clickNode, EventHandlingUtil::parentForClickEvent);
     }
     if (clickTargetNode) {
       clickEventResult = dispatchMouseEvent(
           clickTargetNode,
           !RuntimeEnabledFeatures::auxclickEnabled() ||
-                  (mev.event().button == WebPointerProperties::Button::Left)
+                  (mouseEvent.button == WebPointerProperties::Button::Left)
               ? EventTypeNames::click
               : EventTypeNames::auxclick,
-          mev.event(), mev.canvasRegionId(), nullptr);
+          mouseEvent, canvasRegionId, nullptr);
     }
   }
   return clickEventResult;
@@ -573,8 +573,8 @@
 
   bool singleClick = event.event().clickCount <= 1;
 
-  m_mouseDownMayStartDrag =
-      singleClick && !isLinkSelection(event) && !isExtendingSelection(event);
+  m_mouseDownMayStartDrag = singleClick && !isSelectionOverLink(event) &&
+                            !isExtendingSelection(event);
 
   m_frame->eventHandler().selectionController().handleMousePressEvent(event);
 
diff --git a/third_party/WebKit/Source/core/input/MouseEventManager.h b/third_party/WebKit/Source/core/input/MouseEventManager.h
index f144769f..87dfb19 100644
--- a/third_party/WebKit/Source/core/input/MouseEventManager.h
+++ b/third_party/WebKit/Source/core/input/MouseEventManager.h
@@ -56,8 +56,9 @@
       const AtomicString& eventType,
       const WebMouseEvent&);
 
-  WebInputEventResult dispatchMouseClickIfNeeded(
-      const MouseEventWithHitTestResults&);
+  WebInputEventResult dispatchMouseClickIfNeeded(Node* target,
+                                                 const WebMouseEvent&,
+                                                 const String& canvasRegionId);
 
   WebInputEventResult dispatchDragSrcEvent(const AtomicString& eventType,
                                            const WebMouseEvent&);
diff --git a/third_party/WebKit/Source/core/input/PointerEventManager.cpp b/third_party/WebKit/Source/core/input/PointerEventManager.cpp
index f0fca95..6e62a16 100644
--- a/third_party/WebKit/Source/core/input/PointerEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/PointerEventManager.cpp
@@ -447,7 +447,8 @@
     const String& canvasRegionId,
     const AtomicString& mouseEventType,
     const WebMouseEvent& mouseEvent,
-    const Vector<WebMouseEvent>& coalescedEvents) {
+    const Vector<WebMouseEvent>& coalescedEvents,
+    bool selectionOverLink) {
   PointerEvent* pointerEvent =
       m_pointerEventFactory.create(mouseEventType, mouseEvent, coalescedEvents,
                                    m_frame->document()->domWindow());
@@ -500,6 +501,14 @@
         result,
         m_mouseEventManager->dispatchMouseEvent(
             mouseTarget, mouseEventType, mouseEvent, canvasRegionId, nullptr));
+
+    if (selectionOverLink && mouseTarget &&
+        mouseEventType == EventTypeNames::mouseup) {
+      WebInputEventResult clickEventResult =
+          m_mouseEventManager->dispatchMouseClickIfNeeded(
+              mouseTarget->toNode(), mouseEvent, canvasRegionId);
+      result = EventHandlingUtil::mergeEventResult(clickEventResult, result);
+    }
   }
 
   if (pointerEvent->type() == EventTypeNames::pointerup ||
diff --git a/third_party/WebKit/Source/core/input/PointerEventManager.h b/third_party/WebKit/Source/core/input/PointerEventManager.h
index bc24fe9..1ee3acc 100644
--- a/third_party/WebKit/Source/core/input/PointerEventManager.h
+++ b/third_party/WebKit/Source/core/input/PointerEventManager.h
@@ -39,7 +39,8 @@
       const String& canvasRegionId,
       const AtomicString& type,
       const WebMouseEvent&,
-      const Vector<WebMouseEvent>& coalescedEvents);
+      const Vector<WebMouseEvent>& coalescedEvents,
+      bool selectionOverLink);
 
   WebInputEventResult handleTouchEvents(
       const WebTouchEvent&,
diff --git a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
index 53831b7..eeec1a0 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorCSSAgent.cpp
@@ -84,7 +84,7 @@
 #include "core/svg/SVGElement.h"
 #include "platform/fonts/Font.h"
 #include "platform/fonts/FontCache.h"
-#include "platform/fonts/GlyphBuffer.h"
+#include "platform/fonts/shaping/CachingWordShaper.h"
 #include "platform/text/TextRun.h"
 #include "wtf/CurrentTime.h"
 #include "wtf/text/CString.h"
@@ -1136,16 +1136,15 @@
     const ComputedStyle& style = layoutText->styleRef(box->isFirstLineStyle());
     const Font& font = style.font();
     TextRun run = box->constructTextRunForInspector(style);
-    TextRunPaintInfo paintInfo(run);
-    GlyphBuffer glyphBuffer;
-    font.buildGlyphBuffer(paintInfo, glyphBuffer);
-    for (unsigned i = 0; i < glyphBuffer.size(); ++i) {
-      const SimpleFontData* simpleFontData = glyphBuffer.fontDataAt(i);
+    CachingWordShaper shaper(font);
+    for (const auto& runFontData : shaper.runFontData(run)) {
+      const auto* simpleFontData = runFontData.m_fontData;
       String familyName = simpleFontData->platformData().fontFamilyName();
       if (familyName.isNull())
         familyName = "";
       fontStats->add(
-          std::make_pair(simpleFontData->isCustomFont() ? 1 : 0, familyName));
+          std::make_pair(simpleFontData->isCustomFont() ? 1 : 0, familyName),
+          runFontData.m_glyphCount);
     }
   }
 }
diff --git a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
index 750fdca..4cd27f84 100644
--- a/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/Bluetooth.cpp
@@ -150,7 +150,7 @@
         getBluetoothDeviceRepresentingDevice(std::move(device), resolver);
     resolver->resolve(bluetoothDevice);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    resolver->reject(BluetoothError::createDOMException(result));
   }
 }
 
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp
index 383649d..bae9bb8c 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.cpp
@@ -9,15 +9,41 @@
 
 namespace blink {
 
-DOMException* BluetoothError::take(ScriptPromiseResolver*,
-                                   mojom::blink::WebBluetoothResult error) {
+// static
+DOMException* BluetoothError::createDOMException(
+    BluetoothErrorCode error,
+    const String& detailedMessage) {
+  switch (error) {
+    case BluetoothErrorCode::InvalidService:
+    case BluetoothErrorCode::InvalidCharacteristic:
+    case BluetoothErrorCode::InvalidDescriptor:
+      return DOMException::create(InvalidStateError, detailedMessage);
+    case BluetoothErrorCode::ServiceNotFound:
+    case BluetoothErrorCode::CharacteristicNotFound:
+    case BluetoothErrorCode::DescriptorNotFound:
+      return DOMException::create(NotFoundError, detailedMessage);
+  }
+  NOTREACHED();
+  return DOMException::create(UnknownError);
+}
+
+// static
+DOMException* BluetoothError::createDOMException(
+    mojom::blink::WebBluetoothResult error) {
   switch (error) {
     case mojom::blink::WebBluetoothResult::SUCCESS:
-      ASSERT_NOT_REACHED();
+    case mojom::blink::WebBluetoothResult::SERVICE_NOT_FOUND:
+    case mojom::blink::WebBluetoothResult::CHARACTERISTIC_NOT_FOUND:
+    case mojom::blink::WebBluetoothResult::DESCRIPTOR_NOT_FOUND:
+      // The above result codes are not expected here. SUCCESS is not
+      // an error and the others have a detailed message and are
+      // expected to be redirected to the switch above that handles
+      // BluetoothErrorCode.
+      NOTREACHED();
       return DOMException::create(UnknownError);
 #define MAP_ERROR(enumeration, name, message)         \
   case mojom::blink::WebBluetoothResult::enumeration: \
-    return DOMException::create(name, message)
+    return DOMException::create(name, message);
 
       // InvalidModificationErrors:
       MAP_ERROR(GATT_INVALID_ATTRIBUTE_LENGTH, InvalidModificationError,
@@ -68,6 +94,17 @@
                 "GATT operation already in progress.");
       MAP_ERROR(UNTRANSLATED_CONNECT_ERROR_CODE, NetworkError,
                 "Unknown ConnectErrorCode.");
+      MAP_ERROR(GATT_SERVER_NOT_CONNECTED, NetworkError,
+                "GATT Server is disconnected. Cannot perform GATT operations.");
+      MAP_ERROR(GATT_SERVER_DISCONNECTED, NetworkError,
+                "GATT Server disconnected while performing a GATT operation.");
+      MAP_ERROR(GATT_SERVER_DISCONNECTED_WHILE_RETRIEVING_CHARACTERISTICS,
+                NetworkError,
+                "GATT Server disconnected while retrieving characteristics.");
+      MAP_ERROR(
+          GATT_SERVER_NOT_CONNECTED_CANNOT_RETRIEVE_CHARACTERISTICS,
+          NetworkError,
+          "GATT Server is disconnected. Cannot retrieve characteristics.");
 
       // NotFoundErrors:
       MAP_ERROR(WEB_BLUETOOTH_NOT_SUPPORTED, NotFoundError,
@@ -86,16 +123,10 @@
       MAP_ERROR(
           CHOOSER_NOT_SHOWN_USER_DENIED_PERMISSION_TO_SCAN, NotFoundError,
           "User denied the browser permission to scan for Bluetooth devices.");
-      MAP_ERROR(SERVICE_NOT_FOUND, NotFoundError,
-                "No Services with specified UUID found in Device.");
       MAP_ERROR(NO_SERVICES_FOUND, NotFoundError,
                 "No Services found in device.");
-      MAP_ERROR(CHARACTERISTIC_NOT_FOUND, NotFoundError,
-                "No Characteristics with specified UUID found in Service.");
       MAP_ERROR(NO_CHARACTERISTICS_FOUND, NotFoundError,
                 "No Characteristics found in service.");
-      MAP_ERROR(DESCRIPTOR_NOT_FOUND, NotFoundError,
-                "No Descriptors with specified UUID found in Characteristic.");
       MAP_ERROR(NO_DESCRIPTORS_FOUND, NotFoundError,
                 "No Descriptors found in Characteristic.");
       MAP_ERROR(BLUETOOTH_LOW_ENERGY_NOT_AVAILABLE, NotFoundError,
@@ -140,13 +171,11 @@
                 "UUID. https://goo.gl/4NeimX");
       MAP_ERROR(REQUEST_DEVICE_FROM_CROSS_ORIGIN_IFRAME, SecurityError,
                 "requestDevice() called from cross-origin iframe.");
-      MAP_ERROR(REQUEST_DEVICE_WITHOUT_FRAME, SecurityError,
-                "No window to show the requestDevice() dialog.");
 
 #undef MAP_ERROR
   }
 
-  ASSERT_NOT_REACHED();
+  NOTREACHED();
   return DOMException::create(UnknownError);
 }
 
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h
index c9374283..8e98950 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothError.h
@@ -11,8 +11,17 @@
 
 namespace blink {
 
+// These error codes requires detailed error messages.
+enum class BluetoothErrorCode {
+  InvalidService,
+  InvalidCharacteristic,
+  InvalidDescriptor,
+  ServiceNotFound,
+  CharacteristicNotFound,
+  DescriptorNotFound
+};
+
 class DOMException;
-class ScriptPromiseResolver;
 
 // BluetoothError is used with CallbackPromiseAdapter to receive
 // WebBluetoothResult responses. See CallbackPromiseAdapter class comments.
@@ -20,9 +29,11 @@
   STATIC_ONLY(BluetoothError);
 
  public:
-  // Interface required by CallbackPromiseAdapter:
-  static DOMException* take(ScriptPromiseResolver*,
-                            mojom::blink::WebBluetoothResult);
+  static DOMException* createDOMException(BluetoothErrorCode,
+                                          const String& detailedMessage);
+
+  static DOMException* createDOMException(
+      mojom::blink::WebBluetoothResult error);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp
index c462463ac..0088df16 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.cpp
@@ -6,6 +6,7 @@
 
 #include "bindings/core/v8/ScriptPromise.h"
 #include "bindings/core/v8/ScriptPromiseResolver.h"
+#include "core/dom/DOMException.h"
 #include "core/events/Event.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "modules/bluetooth/Bluetooth.h"
@@ -105,8 +106,8 @@
 
   // If the device is disconnected, reject.
   if (!getGatt()->RemoveFromActiveAlgorithms(resolver)) {
-    resolver->reject(BluetoothRemoteGATTUtils::CreateDOMException(
-        BluetoothRemoteGATTUtils::ExceptionType::kGATTServerDisconnected));
+    resolver->reject(BluetoothError::createDOMException(
+        blink::mojom::WebBluetoothResult::GATT_SERVER_DISCONNECTED));
     return;
   }
 
@@ -117,7 +118,7 @@
     setValue(domDataView);
     resolver->resolve(domDataView);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    resolver->reject(BluetoothError::createDOMException(result));
   }
 }
 
@@ -126,16 +127,14 @@
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            blink::mojom::WebBluetoothResult::GATT_SERVER_NOT_CONNECTED));
   }
 
   if (!getGatt()->device()->isValidCharacteristic(
           m_characteristic->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kInvalidCharacteristic));
+        scriptState, createInvalidCharacteristicError());
   }
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
@@ -162,8 +161,8 @@
 
   // If the device is disconnected, reject.
   if (!getGatt()->RemoveFromActiveAlgorithms(resolver)) {
-    resolver->reject(BluetoothRemoteGATTUtils::CreateDOMException(
-        BluetoothRemoteGATTUtils::ExceptionType::kGATTServerDisconnected));
+    resolver->reject(BluetoothError::createDOMException(
+        blink::mojom::WebBluetoothResult::GATT_SERVER_DISCONNECTED));
     return;
   }
 
@@ -171,7 +170,7 @@
     setValue(BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(value));
     resolver->resolve();
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    resolver->reject(BluetoothError::createDOMException(result));
   }
 }
 
@@ -181,16 +180,14 @@
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            blink::mojom::WebBluetoothResult::GATT_SERVER_NOT_CONNECTED));
   }
 
   if (!getGatt()->device()->isValidCharacteristic(
           m_characteristic->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kInvalidCharacteristic));
+        scriptState, createInvalidCharacteristicError());
   }
 
   // Partial implementation of writeValue algorithm:
@@ -231,15 +228,15 @@
 
   // If the device is disconnected, reject.
   if (!getGatt()->RemoveFromActiveAlgorithms(resolver)) {
-    resolver->reject(BluetoothRemoteGATTUtils::CreateDOMException(
-        BluetoothRemoteGATTUtils::ExceptionType::kGATTServerDisconnected));
+    resolver->reject(BluetoothError::createDOMException(
+        blink::mojom::WebBluetoothResult::GATT_SERVER_DISCONNECTED));
     return;
   }
 
   if (result == mojom::blink::WebBluetoothResult::SUCCESS) {
     resolver->resolve(this);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    resolver->reject(BluetoothError::createDOMException(result));
   }
 }
 
@@ -248,16 +245,14 @@
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            blink::mojom::WebBluetoothResult::GATT_SERVER_NOT_CONNECTED));
   }
 
   if (!getGatt()->device()->isValidCharacteristic(
           m_characteristic->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kInvalidCharacteristic));
+        scriptState, createInvalidCharacteristicError());
   }
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
@@ -279,16 +274,14 @@
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            blink::mojom::WebBluetoothResult::GATT_SERVER_NOT_CONNECTED));
   }
 
   if (!getGatt()->device()->isValidCharacteristic(
           m_characteristic->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kInvalidCharacteristic));
+        scriptState, createInvalidCharacteristicError());
   }
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
@@ -347,16 +340,14 @@
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            blink::mojom::WebBluetoothResult::GATT_SERVER_NOT_CONNECTED));
   }
 
   if (!getGatt()->device()->isValidCharacteristic(
           m_characteristic->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kInvalidCharacteristic));
+        scriptState, createInvalidCharacteristicError());
   }
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
@@ -366,10 +357,10 @@
   mojom::blink::WebBluetoothService* service = m_device->bluetooth()->service();
   service->RemoteCharacteristicGetDescriptors(
       m_characteristic->instance_id, quantity, descriptorsUUID,
-      convertToBaseCallback(
-          WTF::bind(&BluetoothRemoteGATTCharacteristic::GetDescriptorsCallback,
-                    wrapPersistent(this), m_characteristic->instance_id,
-                    quantity, wrapPersistent(resolver))));
+      convertToBaseCallback(WTF::bind(
+          &BluetoothRemoteGATTCharacteristic::GetDescriptorsCallback,
+          wrapPersistent(this), descriptorsUUID, m_characteristic->instance_id,
+          quantity, wrapPersistent(resolver))));
 
   return promise;
 }
@@ -377,6 +368,7 @@
 // Callback that allows us to resolve the promise with a single descriptor
 // or with a vector owning the descriptors.
 void BluetoothRemoteGATTCharacteristic::GetDescriptorsCallback(
+    const String& requestedDescriptorUUID,
     const String& characteristicInstanceId,
     mojom::blink::WebBluetoothGATTQueryQuantity quantity,
     ScriptPromiseResolver* resolver,
@@ -389,8 +381,8 @@
 
   // If the device is disconnected, reject.
   if (!service()->device()->gatt()->RemoveFromActiveAlgorithms(resolver)) {
-    resolver->reject(BluetoothRemoteGATTUtils::CreateDOMException(
-        BluetoothRemoteGATTUtils::ExceptionType::kGATTServerDisconnected));
+    resolver->reject(BluetoothError::createDOMException(
+        blink::mojom::WebBluetoothResult::GATT_SERVER_DISCONNECTED));
     return;
   }
 
@@ -414,10 +406,26 @@
     }
     resolver->resolve(gattDescriptors);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    if (result == mojom::blink::WebBluetoothResult::DESCRIPTOR_NOT_FOUND) {
+      resolver->reject(BluetoothError::createDOMException(
+          BluetoothErrorCode::DescriptorNotFound,
+          "No Descriptors matching UUID " + requestedDescriptorUUID +
+              " found in Characteristic with UUID " + uuid() + "."));
+    } else {
+      resolver->reject(BluetoothError::createDOMException(result));
+    }
   }
 }
 
+DOMException*
+BluetoothRemoteGATTCharacteristic::createInvalidCharacteristicError() {
+  return BluetoothError::createDOMException(
+      BluetoothErrorCode::InvalidCharacteristic,
+      "Characteristic with UUID " + uuid() +
+          " is no longer valid. Remember to retrieve the characteristic again "
+          "after reconnecting.");
+}
+
 DEFINE_TRACE(BluetoothRemoteGATTCharacteristic) {
   visitor->trace(m_service);
   visitor->trace(m_properties);
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h
index c4aaabd..ca7efb1 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTCharacteristic.h
@@ -118,6 +118,7 @@
                                    const String& descriptorUUID = String());
 
   void GetDescriptorsCallback(
+      const String& requestedDescriptorUUID,
       const String& characteristicInstanceId,
       mojom::blink::WebBluetoothGATTQueryQuantity,
       ScriptPromiseResolver*,
@@ -125,6 +126,8 @@
       Optional<Vector<mojom::blink::WebBluetoothRemoteGATTDescriptorPtr>>
           descriptors);
 
+  DOMException* createInvalidCharacteristicError();
+
   mojom::blink::WebBluetoothRemoteGATTCharacteristicPtr m_characteristic;
   Member<BluetoothRemoteGATTService> m_service;
   bool m_stopped;
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp
index 9bb73d6..ad14abb 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.cpp
@@ -38,8 +38,8 @@
 
   // If the device is disconnected, reject.
   if (!getGatt()->RemoveFromActiveAlgorithms(resolver)) {
-    resolver->reject(BluetoothRemoteGATTUtils::CreateDOMException(
-        BluetoothRemoteGATTUtils::ExceptionType::kGATTServerDisconnected));
+    resolver->reject(BluetoothError::createDOMException(
+        mojom::blink::WebBluetoothResult::GATT_SERVER_DISCONNECTED));
     return;
   }
 
@@ -50,7 +50,7 @@
     m_value = domDataView;
     resolver->resolve(domDataView);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    resolver->reject(BluetoothError::createDOMException(result));
   }
 }
 
@@ -59,15 +59,13 @@
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            mojom::blink::WebBluetoothResult::GATT_SERVER_NOT_CONNECTED));
   }
 
   if (!getGatt()->device()->isValidDescriptor(m_descriptor->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kInvalidDescriptor));
+        scriptState, createInvalidDescriptorError());
   }
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
@@ -93,8 +91,8 @@
   // If the resolver is not in the set of ActiveAlgorithms then the frame
   // disconnected so we reject.
   if (!getGatt()->RemoveFromActiveAlgorithms(resolver)) {
-    resolver->reject(BluetoothRemoteGATTUtils::CreateDOMException(
-        BluetoothRemoteGATTUtils::ExceptionType::kGATTServerDisconnected));
+    resolver->reject(BluetoothError::createDOMException(
+        mojom::blink::WebBluetoothResult::GATT_SERVER_DISCONNECTED));
     return;
   }
 
@@ -102,7 +100,7 @@
     m_value = BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(value);
     resolver->resolve();
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    resolver->reject(BluetoothError::createDOMException(result));
   }
 }
 
@@ -112,15 +110,13 @@
   if (!getGatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            mojom::blink::WebBluetoothResult::GATT_SERVER_NOT_CONNECTED));
   }
 
   if (!getGatt()->device()->isValidDescriptor(m_descriptor->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState,
-        BluetoothRemoteGATTUtils::CreateDOMException(
-            BluetoothRemoteGATTUtils::ExceptionType::kInvalidDescriptor));
+        scriptState, createInvalidDescriptorError());
   }
 
   // Partial implementation of writeValue algorithm:
@@ -151,6 +147,14 @@
   return promise;
 }
 
+DOMException* BluetoothRemoteGATTDescriptor::createInvalidDescriptorError() {
+  return BluetoothError::createDOMException(
+      BluetoothErrorCode::InvalidDescriptor,
+      "Descriptor with UUID " + uuid() +
+          " is no longer valid. Remember to retrieve the Descriptor again "
+          "after reconnecting.");
+}
+
 DEFINE_TRACE(BluetoothRemoteGATTDescriptor) {
   visitor->trace(m_characteristic);
   visitor->trace(m_value);
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.h
index 175db616..289fefd 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTDescriptor.h
@@ -67,6 +67,8 @@
                           const Vector<uint8_t>&,
                           mojom::blink::WebBluetoothResult);
 
+  DOMException* createInvalidDescriptorError();
+
   mojom::blink::WebBluetoothRemoteGATTDescriptorPtr m_descriptor;
   Member<BluetoothRemoteGATTCharacteristic> m_characteristic;
   Member<DOMDataView> m_value;
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp
index 8f52294..8b2185a 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.cpp
@@ -67,7 +67,7 @@
     setConnected(true);
     resolver->resolve(this);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    resolver->reject(BluetoothError::createDOMException(result));
   }
 }
 
@@ -96,6 +96,7 @@
 // Callback that allows us to resolve the promise with a single service or
 // with a vector owning the services.
 void BluetoothRemoteGATTServer::GetPrimaryServicesCallback(
+    const String& requestedServiceUUID,
     mojom::blink::WebBluetoothGATTQueryQuantity quantity,
     ScriptPromiseResolver* resolver,
     mojom::blink::WebBluetoothResult result,
@@ -131,7 +132,14 @@
     }
     resolver->resolve(gattServices);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    if (result == mojom::blink::WebBluetoothResult::SERVICE_NOT_FOUND) {
+      resolver->reject(BluetoothError::createDOMException(
+          BluetoothErrorCode::ServiceNotFound, "No Services matching UUID " +
+                                                   requestedServiceUUID +
+                                                   " found in Device."));
+    } else {
+      resolver->reject(BluetoothError::createDOMException(result));
+    }
   }
 }
 
@@ -187,7 +195,8 @@
       device()->id(), quantity, servicesUUID,
       convertToBaseCallback(
           WTF::bind(&BluetoothRemoteGATTServer::GetPrimaryServicesCallback,
-                    wrapPersistent(this), quantity, wrapPersistent(resolver))));
+                    wrapPersistent(this), servicesUUID, quantity,
+                    wrapPersistent(resolver))));
   return promise;
 }
 
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.h
index 1e33026c..7379ad6 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTServer.h
@@ -72,6 +72,7 @@
   void ConnectCallback(ScriptPromiseResolver*,
                        mojom::blink::WebBluetoothResult);
   void GetPrimaryServicesCallback(
+      const String& requestedServiceUUID,
       mojom::blink::WebBluetoothGATTQueryQuantity,
       ScriptPromiseResolver*,
       mojom::blink::WebBluetoothResult,
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp
index cfcd631..fcdf5d3 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.cpp
@@ -18,18 +18,6 @@
 
 namespace blink {
 
-namespace {
-
-const char kGATTServerDisconnected[] =
-    "GATT Server disconnected while retrieving characteristics.";
-const char kGATTServerNotConnected[] =
-    "GATT Server is disconnected. Cannot retrieve characteristics.";
-const char kInvalidService[] =
-    "Service is no longer valid. Remember to retrieve the service again after "
-    "reconnecting.";
-
-}  // namespace
-
 BluetoothRemoteGATTService::BluetoothRemoteGATTService(
     mojom::blink::WebBluetoothRemoteGATTServicePtr service,
     bool isPrimary,
@@ -48,6 +36,7 @@
 // or with a vector owning the characteristics.
 void BluetoothRemoteGATTService::GetCharacteristicsCallback(
     const String& serviceInstanceId,
+    const String& requestedCharacteristicUUID,
     mojom::blink::WebBluetoothGATTQueryQuantity quantity,
     ScriptPromiseResolver* resolver,
     mojom::blink::WebBluetoothResult result,
@@ -59,8 +48,9 @@
 
   // If the device is disconnected, reject.
   if (!device()->gatt()->RemoveFromActiveAlgorithms(resolver)) {
-    resolver->reject(
-        DOMException::create(NetworkError, kGATTServerDisconnected));
+    resolver->reject(BluetoothError::createDOMException(
+        mojom::blink::WebBluetoothResult::
+            GATT_SERVER_DISCONNECTED_WHILE_RETRIEVING_CHARACTERISTICS));
     return;
   }
 
@@ -85,7 +75,14 @@
     }
     resolver->resolve(gattCharacteristics);
   } else {
-    resolver->reject(BluetoothError::take(resolver, result));
+    if (result == mojom::blink::WebBluetoothResult::CHARACTERISTIC_NOT_FOUND) {
+      resolver->reject(BluetoothError::createDOMException(
+          BluetoothErrorCode::CharacteristicNotFound,
+          "No Characteristics matching UUID " + requestedCharacteristicUUID +
+              " found in Service with UUID " + uuid() + "."));
+    } else {
+      resolver->reject(BluetoothError::createDOMException(result));
+    }
   }
 }
 
@@ -131,12 +128,18 @@
   if (!device()->gatt()->connected()) {
     return ScriptPromise::rejectWithDOMException(
         scriptState,
-        DOMException::create(NetworkError, kGATTServerNotConnected));
+        BluetoothError::createDOMException(
+            mojom::blink::WebBluetoothResult::
+                GATT_SERVER_NOT_CONNECTED_CANNOT_RETRIEVE_CHARACTERISTICS));
   }
 
   if (!device()->isValidService(m_service->instance_id)) {
     return ScriptPromise::rejectWithDOMException(
-        scriptState, DOMException::create(InvalidStateError, kInvalidService));
+        scriptState, BluetoothError::createDOMException(
+                         BluetoothErrorCode::InvalidService,
+                         "Service with UUID " + m_service->uuid +
+                             " is no longer valid. Remember to retrieve "
+                             "the service again after reconnecting."));
   }
 
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
@@ -148,8 +151,8 @@
       m_service->instance_id, quantity, characteristicsUUID,
       convertToBaseCallback(
           WTF::bind(&BluetoothRemoteGATTService::GetCharacteristicsCallback,
-                    wrapPersistent(this), m_service->instance_id, quantity,
-                    wrapPersistent(resolver))));
+                    wrapPersistent(this), m_service->instance_id,
+                    characteristicsUUID, quantity, wrapPersistent(resolver))));
 
   return promise;
 }
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h
index 2884d1f..b3a8fcc9 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTService.h
@@ -56,6 +56,7 @@
  private:
   void GetCharacteristicsCallback(
       const String& serviceInstanceId,
+      const String& requestedCharacteristicUUID,
       mojom::blink::WebBluetoothGATTQueryQuantity,
       ScriptPromiseResolver*,
       mojom::blink::WebBluetoothResult,
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.cpp b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.cpp
index f417871..6f00640 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.cpp
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.cpp
@@ -16,35 +16,4 @@
   return DOMDataView::create(domBuffer, 0, wtfVector.size());
 }
 
-// static
-DOMException* BluetoothRemoteGATTUtils::CreateDOMException(ExceptionType type) {
-  switch (type) {
-    case ExceptionType::kGATTServerDisconnected:
-      return DOMException::create(
-          NetworkError,
-          "GATT Server disconnected while performing a GATT operation.");
-
-    case ExceptionType::kGATTServerNotConnected:
-      return DOMException::create(
-          NetworkError,
-          "GATT Server is disconnected. Cannot perform GATT operations.");
-
-    case ExceptionType::kInvalidCharacteristic:
-      return DOMException::create(
-          InvalidStateError,
-          "Characteristic is no longer valid. Remember to retrieve the "
-          "characteristic again after reconnecting.");
-
-    case ExceptionType::kInvalidDescriptor:
-      return DOMException::create(
-          InvalidStateError,
-          "Descriptor is no longer valid. Remember to retrieve the "
-          "Descriptor again after reconnecting.");
-
-    default:
-      NOTREACHED();
-      return nullptr;
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.h b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.h
index de176f3ba..03c365e 100644
--- a/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.h
+++ b/third_party/WebKit/Source/modules/bluetooth/BluetoothRemoteGATTUtils.h
@@ -6,7 +6,6 @@
 #define BluetoothRemoteGATTUtils_h
 
 #include "core/dom/DOMDataView.h"
-#include "core/dom/DOMException.h"
 #include "wtf/Vector.h"
 
 namespace blink {
@@ -14,15 +13,6 @@
 class BluetoothRemoteGATTUtils final {
  public:
   static DOMDataView* ConvertWTFVectorToDataView(const WTF::Vector<uint8_t>&);
-
-  enum ExceptionType {
-    kGATTServerDisconnected,
-    kGATTServerNotConnected,
-    kInvalidCharacteristic,
-    kInvalidDescriptor
-  };
-
-  static DOMException* CreateDOMException(ExceptionType);
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.cpp b/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.cpp
index e894cd2..a013f6b 100644
--- a/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.cpp
+++ b/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.cpp
@@ -108,7 +108,7 @@
                              FileSystemType type,
                              const KURL& rootURL)
     : DOMFileSystemBase(context, name, type, rootURL),
-      ContextLifecycleObserver(context),
+      ContextClient(context),
       m_numberOfPendingCallbacks(0),
       m_rootEntry(DirectoryEntry::create(this, DOMFilePath::root)) {}
 
@@ -217,9 +217,9 @@
 }
 
 DEFINE_TRACE(DOMFileSystem) {
-  DOMFileSystemBase::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
   visitor->trace(m_rootEntry);
+  DOMFileSystemBase::trace(visitor);
+  ContextClient::trace(visitor);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.h b/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.h
index 60170324..f47fb4bf 100644
--- a/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.h
+++ b/third_party/WebKit/Source/modules/filesystem/DOMFileSystem.h
@@ -55,7 +55,7 @@
     : public DOMFileSystemBase,
       public ScriptWrappable,
       public ActiveScriptWrappable<DOMFileSystem>,
-      public ContextLifecycleObserver {
+      public ContextClient {
   DEFINE_WRAPPERTYPEINFO();
   USING_GARBAGE_COLLECTED_MIXIN(DOMFileSystem);
 
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp
index 22cc3db..f9ce3ff1 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp
@@ -108,7 +108,7 @@
                                int64_t id,
                                const HashSet<String>& scope,
                                IDBDatabase* db)
-    : ContextLifecycleObserver(executionContext),
+    : ContextClient(executionContext),
       m_id(id),
       m_database(db),
       m_mode(WebIDBTransactionModeReadOnly),
@@ -125,7 +125,7 @@
                                const HashSet<String>& scope,
                                WebIDBTransactionMode mode,
                                IDBDatabase* db)
-    : ContextLifecycleObserver(scriptState->getExecutionContext()),
+    : ContextClient(scriptState->getExecutionContext()),
       m_id(id),
       m_database(db),
       m_mode(mode),
@@ -149,7 +149,7 @@
                                IDBDatabase* db,
                                IDBOpenDBRequest* openDBRequest,
                                const IDBDatabaseMetadata& oldMetadata)
-    : ContextLifecycleObserver(executionContext),
+    : ContextClient(executionContext),
       m_id(id),
       m_database(db),
       m_openDBRequest(openDBRequest),
@@ -177,7 +177,7 @@
   visitor->trace(m_oldStoreMetadata);
   visitor->trace(m_deletedIndexes);
   EventTargetWithInlineData::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
+  ContextClient::trace(visitor);
 }
 
 void IDBTransaction::setError(DOMException* error) {
@@ -479,7 +479,7 @@
 }
 
 ExecutionContext* IDBTransaction::getExecutionContext() const {
-  return ContextLifecycleObserver::getExecutionContext();
+  return ContextClient::getExecutionContext();
 }
 
 DispatchEventResult IDBTransaction::dispatchEventInternal(Event* event) {
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.h b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.h
index 148d947..0b9e006 100644
--- a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.h
+++ b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.h
@@ -56,7 +56,7 @@
 class MODULES_EXPORT IDBTransaction final
     : public EventTargetWithInlineData,
       public ActiveScriptWrappable<IDBTransaction>,
-      public ContextLifecycleObserver {
+      public ContextClient {
   USING_GARBAGE_COLLECTED_MIXIN(IDBTransaction);
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp b/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp
index 7f2e17e..3614224 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp
+++ b/third_party/WebKit/Source/modules/presentation/PresentationRequest.cpp
@@ -109,7 +109,7 @@
 }
 
 ExecutionContext* PresentationRequest::getExecutionContext() const {
-  return ContextLifecycleObserver::getExecutionContext();
+  return ContextClient::getExecutionContext();
 }
 
 void PresentationRequest::addedEventListener(
@@ -221,12 +221,12 @@
 DEFINE_TRACE(PresentationRequest) {
   visitor->trace(m_availabilityProperty);
   EventTargetWithInlineData::trace(visitor);
-  ContextLifecycleObserver::trace(visitor);
+  ContextClient::trace(visitor);
 }
 
 PresentationRequest::PresentationRequest(ExecutionContext* executionContext,
                                          const Vector<KURL>& urls)
-    : ContextLifecycleObserver(executionContext), m_urls(urls) {
+    : ContextClient(executionContext), m_urls(urls) {
   recordOriginTypeAccess(executionContext);
 }
 
diff --git a/third_party/WebKit/Source/modules/presentation/PresentationRequest.h b/third_party/WebKit/Source/modules/presentation/PresentationRequest.h
index ac3d2d9b..6f8aef20 100644
--- a/third_party/WebKit/Source/modules/presentation/PresentationRequest.h
+++ b/third_party/WebKit/Source/modules/presentation/PresentationRequest.h
@@ -23,7 +23,7 @@
 class MODULES_EXPORT PresentationRequest final
     : public EventTargetWithInlineData,
       public ActiveScriptWrappable<PresentationRequest>,
-      public ContextLifecycleObserver {
+      public ContextClient {
   USING_GARBAGE_COLLECTED_MIXIN(PresentationRequest);
   DEFINE_WRAPPERTYPEINFO();
 
diff --git a/third_party/WebKit/Source/platform/fonts/Font.cpp b/third_party/WebKit/Source/platform/fonts/Font.cpp
index c7e4ac21..1f4cc61b 100644
--- a/third_party/WebKit/Source/platform/fonts/Font.cpp
+++ b/third_party/WebKit/Source/platform/fonts/Font.cpp
@@ -109,13 +109,13 @@
                              GlyphBuffer& glyphBuffer,
                              const GlyphData* emphasisData) const {
   float width;
-  CachingWordShaper shaper(m_fontFallbackList->shapeCache(m_fontDescription));
+  CachingWordShaper shaper(*this);
   if (emphasisData) {
-    width = shaper.fillGlyphBufferForTextEmphasis(this, runInfo.run,
+    width = shaper.fillGlyphBufferForTextEmphasis(runInfo.run,
                                                   emphasisData, &glyphBuffer,
                                                   runInfo.from, runInfo.to);
   } else {
-    width = shaper.fillGlyphBuffer(this, runInfo.run, nullptr, &glyphBuffer,
+    width = shaper.fillGlyphBuffer(runInfo.run, &glyphBuffer,
                                    runInfo.from, runInfo.to);
   }
   return width;
@@ -224,9 +224,8 @@
                   HashSet<const SimpleFontData*>* fallbackFonts,
                   FloatRect* glyphBounds) const {
   FontCachePurgePreventer purgePreventer;
-  CachingWordShaper shaper(m_fontFallbackList->shapeCache(m_fontDescription));
-  float width = shaper.width(this, run, fallbackFonts, glyphBounds);
-  return width;
+  CachingWordShaper shaper(*this);
+  return shaper.width(run, fallbackFonts, glyphBounds);
 }
 
 namespace {
@@ -439,8 +438,8 @@
 
   FontCachePurgePreventer purgePreventer;
 
-  CachingWordShaper shaper(m_fontFallbackList->shapeCache(m_fontDescription));
-  CharacterRange range = shaper.getCharacterRange(this, run, from, to);
+  CachingWordShaper shaper(*this);
+  CharacterRange range = shaper.getCharacterRange(run, from, to);
 
   return pixelSnappedSelectionRect(
       FloatRect(point.x() + range.start, point.y(), range.width(), height));
@@ -450,8 +449,8 @@
                             float xFloat,
                             bool includePartialGlyphs) const {
   FontCachePurgePreventer purgePreventer;
-  CachingWordShaper shaper(m_fontFallbackList->shapeCache(m_fontDescription));
-  return shaper.offsetForPosition(this, run, xFloat, includePartialGlyphs);
+  CachingWordShaper shaper(*this);
+  return shaper.offsetForPosition(run, xFloat, includePartialGlyphs);
 }
 
 ShapeCache* Font::shapeCache() const {
@@ -562,15 +561,15 @@
                                        unsigned from,
                                        unsigned to) const {
   FontCachePurgePreventer purgePreventer;
-  CachingWordShaper shaper(m_fontFallbackList->shapeCache(m_fontDescription));
-  return shaper.getCharacterRange(this, run, from, to);
+  CachingWordShaper shaper(*this);
+  return shaper.getCharacterRange(run, from, to);
 }
 
 Vector<CharacterRange> Font::individualCharacterRanges(
     const TextRun& run) const {
   FontCachePurgePreventer purgePreventer;
-  CachingWordShaper shaper(m_fontFallbackList->shapeCache(m_fontDescription));
-  auto ranges = shaper.individualCharacterRanges(this, run);
+  CachingWordShaper shaper(*this);
+  auto ranges = shaper.individualCharacterRanges(run);
   // The shaper should return ranges.size == run.length but on some platforms
   // (OSX10.9.5) we are seeing cases in the upper end of the unicode range
   // where this is not true (see: crbug.com/620952). To catch these cases on
diff --git a/third_party/WebKit/Source/platform/fonts/Font.h b/third_party/WebKit/Source/platform/fonts/Font.h
index 8ba2ca3..6afe042 100644
--- a/third_party/WebKit/Source/platform/fonts/Font.h
+++ b/third_party/WebKit/Source/platform/fonts/Font.h
@@ -208,9 +208,8 @@
   mutable unsigned m_canShapeWordByWord : 1;
   mutable unsigned m_shapeWordByWordComputed : 1;
 
-  // For accessing buildGlyphBuffer and retrieving fonts used in rendering a
-  // node.
-  friend class InspectorCSSAgent;
+  // For m_fontDescription & m_fontFallbackList access.
+  friend class CachingWordShaper;
 };
 
 inline Font::~Font() {}
diff --git a/third_party/WebKit/Source/platform/fonts/FontCache.cpp b/third_party/WebKit/Source/platform/fonts/FontCache.cpp
index d5a4e3d..9208ac44 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCache.cpp
+++ b/third_party/WebKit/Source/platform/fonts/FontCache.cpp
@@ -172,7 +172,7 @@
     foundResult = result || !addResult.isNewEntry;
   }
 
-  if (!foundResult && alternateFontName != AlternateFontName::NoAlternate &&
+  if (!foundResult && alternateFontName == AlternateFontName::AllowAlternate &&
       creationParams.creationType() == CreateFontByFamily) {
     // We were unable to find a font. We have a small set of fonts that we alias
     // to other names, e.g., Arial/Helvetica, Courier/Courier New, etc. Try
@@ -296,14 +296,23 @@
   return gFontDataCache->get(platformData, shouldRetain, subpixelAscentDescent);
 }
 
-bool FontCache::isPlatformFontAvailable(const FontDescription& fontDescription,
-                                        const AtomicString& family) {
+bool FontCache::isPlatformFamilyMatchAvailable(
+    const FontDescription& fontDescription,
+    const AtomicString& family) {
   return getFontPlatformData(
       fontDescription,
       FontFaceCreationParams(adjustFamilyNameToAvoidUnsupportedFonts(family)),
       AlternateFontName::NoAlternate);
 }
 
+bool FontCache::isPlatformFontUniqueNameMatchAvailable(
+    const FontDescription& fontDescription,
+    const AtomicString& uniqueFontName) {
+  return getFontPlatformData(fontDescription,
+                             FontFaceCreationParams(uniqueFontName),
+                             AlternateFontName::LocalUniqueFace);
+}
+
 String FontCache::firstAvailableOrFirst(const String& families) {
   // The conversions involve at least two string copies, and more if non-ASCII.
   // For now we prefer shared code over the cost because a) inputs are
diff --git a/third_party/WebKit/Source/platform/fonts/FontCache.h b/third_party/WebKit/Source/platform/fonts/FontCache.h
index d345988..d015e5e 100644
--- a/third_party/WebKit/Source/platform/fonts/FontCache.h
+++ b/third_party/WebKit/Source/platform/fonts/FontCache.h
@@ -69,7 +69,12 @@
 
 enum ShouldRetain { Retain, DoNotRetain };
 enum PurgeSeverity { PurgeIfNeeded, ForcePurge };
-enum class AlternateFontName { AllowAlternate, NoAlternate, LastResort };
+enum class AlternateFontName {
+  AllowAlternate,
+  NoAlternate,
+  LocalUniqueFace,
+  LastResort
+};
 
 class PLATFORM_EXPORT FontCache {
   friend class FontCachePurgePreventer;
@@ -101,7 +106,22 @@
   PassRefPtr<SimpleFontData> getLastResortFallbackFont(const FontDescription&,
                                                        ShouldRetain = Retain);
   SimpleFontData* getNonRetainedLastResortFallbackFont(const FontDescription&);
-  bool isPlatformFontAvailable(const FontDescription&, const AtomicString&);
+
+  // Should be used in determining whether family names listed in font-family:
+  // ... are available locally. Only returns true if family name matches.
+  bool isPlatformFamilyMatchAvailable(const FontDescription&,
+                                      const AtomicString& family);
+
+  // Should be used in determining whether the <abc> argument to local in
+  // @font-face { ... src: local(<abc>) } are available locally, which should
+  // match Postscript name or full font name. Compare
+  // https://drafts.csswg.org/css-fonts-3/#src-desc
+  // TODO crbug.com/627143 complete this and actually look at the right
+  // namerecords.
+  bool isPlatformFontUniqueNameMatchAvailable(
+      const FontDescription&,
+      const AtomicString& uniqueFontName);
+
   static String firstAvailableOrFirst(const String&);
 
   // Returns the ShapeCache instance associated with the given cache key.
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.cpp b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.cpp
index 5cc8b7f..f6ab60ad 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.cpp
@@ -35,13 +35,16 @@
 
 namespace blink {
 
-float CachingWordShaper::width(const Font* font,
-                               const TextRun& run,
+ShapeCache* CachingWordShaper::shapeCache() const {
+  return m_font.m_fontFallbackList->shapeCache(m_font.m_fontDescription);
+}
+
+float CachingWordShaper::width(const TextRun& run,
                                HashSet<const SimpleFontData*>* fallbackFonts,
                                FloatRect* glyphBounds) {
   float width = 0;
   RefPtr<const ShapeResult> wordResult;
-  CachingWordShapeIterator iterator(m_shapeCache, run, font);
+  CachingWordShapeIterator iterator(shapeCache(), run, &m_font);
   while (iterator.next(&wordResult)) {
     if (wordResult) {
       if (glyphBounds) {
@@ -64,7 +67,6 @@
     ShapeCache* shapeCache,
     const Font* font,
     const TextRun& run,
-    HashSet<const SimpleFontData*>* fallbackFonts,
     ShapeResultBuffer* resultsBuffer) {
   CachingWordShapeIterator iterator(shapeCache, run, font);
   RefPtr<const ShapeResult> wordResult;
@@ -72,68 +74,60 @@
   while (iterator.next(&wordResult)) {
     if (wordResult) {
       totalWidth += wordResult->width();
-      if (fallbackFonts)
-        wordResult->fallbackFonts(fallbackFonts);
       resultsBuffer->appendResult(std::move(wordResult));
     }
   }
   return totalWidth;
 }
 
-int CachingWordShaper::offsetForPosition(const Font* font,
-                                         const TextRun& run,
+int CachingWordShaper::offsetForPosition(const TextRun& run,
                                          float targetX,
                                          bool includePartialGlyphs) {
   ShapeResultBuffer buffer;
-  shapeResultsForRun(m_shapeCache, font, run, nullptr, &buffer);
+  shapeResultsForRun(shapeCache(), &m_font, run, &buffer);
 
   return buffer.offsetForPosition(run, targetX, includePartialGlyphs);
 }
 
 float CachingWordShaper::fillGlyphBuffer(
-    const Font* font,
     const TextRun& run,
-    HashSet<const SimpleFontData*>* fallbackFonts,
     GlyphBuffer* glyphBuffer,
     unsigned from,
     unsigned to) {
   ShapeResultBuffer buffer;
-  shapeResultsForRun(m_shapeCache, font, run, fallbackFonts, &buffer);
+  shapeResultsForRun(shapeCache(), &m_font, run, &buffer);
 
   return buffer.fillGlyphBuffer(glyphBuffer, run, from, to);
 }
 
 float CachingWordShaper::fillGlyphBufferForTextEmphasis(
-    const Font* font,
     const TextRun& run,
     const GlyphData* emphasisData,
     GlyphBuffer* glyphBuffer,
     unsigned from,
     unsigned to) {
   ShapeResultBuffer buffer;
-  shapeResultsForRun(m_shapeCache, font, run, nullptr, &buffer);
+  shapeResultsForRun(shapeCache(), &m_font, run, &buffer);
 
   return buffer.fillGlyphBufferForTextEmphasis(glyphBuffer, run, emphasisData,
                                                from, to);
 }
 
-CharacterRange CachingWordShaper::getCharacterRange(const Font* font,
-                                                    const TextRun& run,
+CharacterRange CachingWordShaper::getCharacterRange(const TextRun& run,
                                                     unsigned from,
                                                     unsigned to) {
   ShapeResultBuffer buffer;
   float totalWidth =
-      shapeResultsForRun(m_shapeCache, font, run, nullptr, &buffer);
+      shapeResultsForRun(shapeCache(), &m_font, run, &buffer);
 
   return buffer.getCharacterRange(run.direction(), totalWidth, from, to);
 }
 
 Vector<CharacterRange> CachingWordShaper::individualCharacterRanges(
-    const Font* font,
     const TextRun& run) {
   ShapeResultBuffer buffer;
   float totalWidth =
-      shapeResultsForRun(m_shapeCache, font, run, nullptr, &buffer);
+      shapeResultsForRun(shapeCache(), &m_font, run, &buffer);
 
   auto ranges = buffer.individualCharacterRanges(run.direction(), totalWidth);
   // The shaper can fail to return glyph metrics for all characters (see
@@ -144,4 +138,12 @@
   return ranges;
 }
 
+Vector<ShapeResultBuffer::RunFontData> CachingWordShaper::runFontData(
+      const TextRun& run) const {
+  ShapeResultBuffer buffer;
+  shapeResultsForRun(shapeCache(), &m_font, run, &buffer);
+
+  return buffer.runFontData();
+}
+
 };  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.h b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.h
index f3c4225..bf17fa12 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaper.h
@@ -26,18 +26,21 @@
 #ifndef CachingWordShaper_h
 #define CachingWordShaper_h
 
+#include "platform/fonts/shaping/ShapeResultBuffer.h"
 #include "platform/geometry/FloatRect.h"
 #include "platform/text/TextRun.h"
 #include "wtf/Allocator.h"
 #include "wtf/PassRefPtr.h"
+#include "wtf/Vector.h"
+#include <tuple>
 
 namespace blink {
 
 struct CharacterRange;
 class Font;
 class GlyphBuffer;
-class SimpleFontData;
 class ShapeCache;
+class SimpleFontData;
 struct GlyphData;
 
 class PLATFORM_EXPORT CachingWordShaper final {
@@ -45,37 +48,35 @@
   WTF_MAKE_NONCOPYABLE(CachingWordShaper);
 
  public:
-  CachingWordShaper(ShapeCache* cache) : m_shapeCache(cache) {}
+  explicit CachingWordShaper(const Font& font) : m_font(font) {}
   ~CachingWordShaper() {}
 
-  float width(const Font*,
-              const TextRun&,
+  float width(const TextRun&,
               HashSet<const SimpleFontData*>* fallbackFonts,
               FloatRect* glyphBounds);
-  int offsetForPosition(const Font*,
-                        const TextRun&,
+  int offsetForPosition(const TextRun&,
                         float targetX,
                         bool includePartialGlyphs);
-  float fillGlyphBuffer(const Font*,
-                        const TextRun&,
-                        HashSet<const SimpleFontData*>*,
+  float fillGlyphBuffer(const TextRun&,
                         GlyphBuffer*,
                         unsigned from,
                         unsigned to);
-  float fillGlyphBufferForTextEmphasis(const Font*,
-                                       const TextRun&,
+  float fillGlyphBufferForTextEmphasis(const TextRun&,
                                        const GlyphData* emphasisData,
                                        GlyphBuffer*,
                                        unsigned from,
                                        unsigned to);
-  CharacterRange getCharacterRange(const Font*,
-                                   const TextRun&,
+  CharacterRange getCharacterRange(const TextRun&,
                                    unsigned from,
                                    unsigned to);
-  Vector<CharacterRange> individualCharacterRanges(const Font*, const TextRun&);
+  Vector<CharacterRange> individualCharacterRanges(const TextRun&);
+
+  Vector<ShapeResultBuffer::RunFontData> runFontData(const TextRun&) const;
 
  private:
-  ShapeCache* m_shapeCache;
+  ShapeCache* shapeCache() const;
+
+  const Font& m_font;
 };
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp
index 733586e..31f30f6 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/CachingWordShaperTest.cpp
@@ -116,16 +116,16 @@
   const UChar str[] = {0x2F, 0x301, 0x2E, 0x20, 0x2E, 0x0};
   TextRun textRun(str, 5);
 
-  CachingWordShaper shaper(cache.get());
+  CachingWordShaper shaper(font);
   GlyphBuffer glyphBuffer;
-  shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 0, 3);
+  shaper.fillGlyphBuffer(textRun, &glyphBuffer, 0, 3);
 
-  std::unique_ptr<ShapeCache> referenceCache = WTF::makeUnique<ShapeCache>();
-  CachingWordShaper referenceShaper(referenceCache.get());
+  Font referenceFont(fontDescription);
+  referenceFont.update(nullptr);
+  CachingWordShaper referenceShaper(referenceFont);
   GlyphBuffer referenceGlyphBuffer;
-  font.setCanShapeWordByWordForTesting(false);
-  referenceShaper.fillGlyphBuffer(&font, textRun, fallbackFonts,
-                                  &referenceGlyphBuffer, 0, 3);
+  referenceFont.setCanShapeWordByWordForTesting(false);
+  referenceShaper.fillGlyphBuffer(textRun, &referenceGlyphBuffer, 0, 3);
 
   ASSERT_EQ(referenceGlyphBuffer.glyphAt(0), glyphBuffer.glyphAt(0));
   ASSERT_EQ(referenceGlyphBuffer.glyphAt(1), glyphBuffer.glyphAt(1));
@@ -140,16 +140,16 @@
   TextRun textRun(str, 6);
   textRun.setDirection(TextDirection::kRtl);
 
-  CachingWordShaper shaper(cache.get());
+  CachingWordShaper shaper(font);
   GlyphBuffer glyphBuffer;
-  shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 1, 6);
+  shaper.fillGlyphBuffer(textRun, &glyphBuffer, 1, 6);
 
-  std::unique_ptr<ShapeCache> referenceCache = WTF::makeUnique<ShapeCache>();
-  CachingWordShaper referenceShaper(referenceCache.get());
+  Font referenceFont(fontDescription);
+  referenceFont.update(nullptr);
+  CachingWordShaper referenceShaper(referenceFont);
   GlyphBuffer referenceGlyphBuffer;
-  font.setCanShapeWordByWordForTesting(false);
-  referenceShaper.fillGlyphBuffer(&font, textRun, fallbackFonts,
-                                  &referenceGlyphBuffer, 1, 6);
+  referenceFont.setCanShapeWordByWordForTesting(false);
+  referenceShaper.fillGlyphBuffer(textRun, &referenceGlyphBuffer, 1, 6);
 
   ASSERT_EQ(5u, referenceGlyphBuffer.size());
   ASSERT_EQ(referenceGlyphBuffer.size(), glyphBuffer.size());
@@ -169,14 +169,14 @@
                        0x20, 0x62, 0x61, 0x71, 0x0};
   TextRun textRun(str, 9);
 
-  CachingWordShaper shaper(cache.get());
+  CachingWordShaper shaper(font);
   FloatRect glyphBounds;
-  ASSERT_GT(shaper.width(&font, textRun, nullptr, &glyphBounds), 0);
+  ASSERT_GT(shaper.width(textRun, nullptr, &glyphBounds), 0);
 
   GlyphBuffer glyphBuffer;
-  shaper.fillGlyphBuffer(&font, textRun, fallbackFonts, &glyphBuffer, 0, 8);
+  shaper.fillGlyphBuffer(textRun, &glyphBuffer, 0, 8);
 
-  shaper.getCharacterRange(&font, textRun, 0, 8);
+  shaper.getCharacterRange(textRun, 0, 8);
 }
 
 TEST_F(CachingWordShaperTest, SegmentCJKByCharacter) {
@@ -442,27 +442,26 @@
   verticalMixedFont.update(nullptr);
   ASSERT_TRUE(verticalMixedFont.canShapeWordByWord());
 
-  CachingWordShaper shaper(cache.get());
+  CachingWordShaper shaper(verticalMixedFont);
   FloatRect glyphBounds;
   HashSet<const SimpleFontData*> fallbackFonts;
   ASSERT_GT(
-      shaper.width(&verticalMixedFont, textRun, &fallbackFonts, &glyphBounds),
+      shaper.width(textRun, &fallbackFonts, &glyphBounds),
       0);
   EXPECT_EQ(0u, fallbackFonts.size());
 }
 
 TEST_F(CachingWordShaperTest, GlyphBoundsWithSpaces) {
-  CachingWordShaper shaper(cache.get());
+  CachingWordShaper shaper(font);
 
   TextRun periods(reinterpret_cast<const LChar*>(".........."), 10);
   FloatRect periodsGlyphBounds;
-  float periodsWidth =
-      shaper.width(&font, periods, nullptr, &periodsGlyphBounds);
+  float periodsWidth = shaper.width(periods, nullptr, &periodsGlyphBounds);
 
   TextRun periodsAndSpaces(
       reinterpret_cast<const LChar*>(". . . . . . . . . ."), 19);
   FloatRect periodsAndSpacesGlyphBounds;
-  float periodsAndSpacesWidth = shaper.width(&font, periodsAndSpaces, nullptr,
+  float periodsAndSpacesWidth = shaper.width(periodsAndSpaces, nullptr,
                                              &periodsAndSpacesGlyphBounds);
 
   // The total width of periods and spaces should be longer than the width of
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp
index 1c0b74dc..a093da6 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp
@@ -208,10 +208,10 @@
     GlyphBuffer* glyphBuffer,
     const TextRun& textRun) const {
   DCHECK(!hasVerticalOffsets());
+  DCHECK_NE(glyphBuffer->type(), GlyphBuffer::Type::TextIntercepts);
 
   float advance = 0;
 
-  unsigned characterIndex = 0;
   for (unsigned i = 0; i < m_results.size(); ++i) {
     const auto& wordResult = isLeftToRightDirection(textRun.direction())
                                  ? m_results[i]
@@ -228,16 +228,11 @@
               float totalAdvance) -> bool {
             DCHECK(!glyphData.offset.height());
 
-            if (!isSkipInkException(
-                    *glyphBuffer, textRun,
-                    characterIndex + glyphData.characterIndex)) {
-              glyphBuffer->add(glyphData.glyph, run->m_fontData.get(),
-                               totalAdvance + glyphData.offset.width());
-            }
+            glyphBuffer->add(glyphData.glyph, run->m_fontData.get(),
+                             totalAdvance + glyphData.offset.width());
             return true;
           });
     }
-    characterIndex += wordResult->m_numCharacters;
   }
 
   ASSERT(!glyphBuffer->hasVerticalOffsets());
@@ -249,8 +244,9 @@
                                          const TextRun& textRun,
                                          unsigned from,
                                          unsigned to) const {
-  // Fast path: full run with no vertical offsets
-  if (!from && to == textRun.length() && !hasVerticalOffsets())
+  // Fast path: full run with no vertical offsets, no text intercepts.
+  if (!from && to == textRun.length() && !hasVerticalOffsets() &&
+      glyphBuffer->type() != GlyphBuffer::Type::TextIntercepts)
     return fillFastHorizontalGlyphBuffer(glyphBuffer, textRun);
 
   float advance = 0;
@@ -488,4 +484,17 @@
   return totalOffset;
 }
 
+Vector<ShapeResultBuffer::RunFontData>
+ShapeResultBuffer::runFontData() const {
+  Vector<RunFontData> fontData;
+
+  for (const auto& result : m_results) {
+    for (const auto& run : result->m_runs) {
+      fontData.push_back(RunFontData({run->m_fontData.get(),
+                                      run->m_glyphData.size()}));
+    }
+  }
+  return fontData;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
index 573e548f..fb94c45 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
@@ -10,6 +10,7 @@
 #include "wtf/Allocator.h"
 #include "wtf/RefPtr.h"
 #include "wtf/Vector.h"
+#include <tuple>
 
 namespace blink {
 
@@ -57,6 +58,13 @@
                                           unsigned from,
                                           unsigned to);
 
+  struct RunFontData {
+      SimpleFontData* m_fontData;
+      size_t m_glyphCount;
+  };
+
+  Vector<RunFontData> runFontData() const;
+
  private:
   static CharacterRange getCharacterRangeInternal(
       const Vector<RefPtr<const ShapeResult>, 64>&,
diff --git a/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp b/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp
index be0a9b7..d9a6692 100644
--- a/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp
+++ b/third_party/WebKit/Source/platform/fonts/win/FontCacheSkiaWin.cpp
@@ -347,6 +347,20 @@
     FontWeight variantWeight;
     FontStretch variantStretch;
 
+    // TODO: crbug.com/627143 LocalFontFaceSource.cpp, which implements
+    // retrieving src: local() font data uses getFontData, which in turn comes
+    // here, to retrieve fonts from the cache and specifies the argument to
+    // local() as family name. So we do not match by full font name or
+    // postscript name as the spec says:
+    // https://drafts.csswg.org/css-fonts-3/#src-desc
+
+    // Prevent one side effect of the suffix translation below where when
+    // matching local("Roboto Regular") it tries to find the closest match even
+    // though that can be a bold font in case of Roboto Bold.
+    if (alternateFontName == AlternateFontName::LocalUniqueFace) {
+      return nullptr;
+    }
+
     if (alternateFontName == AlternateFontName::LastResort) {
       if (!tf)
         return nullptr;
diff --git a/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReader.cpp b/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReader.cpp
index 2120daa..a8c14ae 100644
--- a/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/FastSharedBufferReader.cpp
@@ -86,7 +86,7 @@
 void FastSharedBufferReader::getSomeDataInternal(size_t dataPosition) const {
   m_dataPosition = dataPosition;
   m_segmentLength = m_data->getSomeData(m_segment, dataPosition);
-  ASSERT(m_segmentLength);
+  DCHECK(m_segmentLength);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
index 06910757..7e4e93e9 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.cpp
@@ -425,7 +425,7 @@
 
 size_t ImageDecoder::findRequiredPreviousFrame(size_t frameIndex,
                                                bool frameRectIsOpaque) {
-  ASSERT(frameIndex <= m_frameBufferCache.size());
+  DCHECK_LT(frameIndex, m_frameBufferCache.size());
   if (!frameIndex) {
     // The first frame doesn't rely on any previous data.
     return kNotFound;
@@ -467,7 +467,7 @@
                  ? kNotFound
                  : prevFrame;
     default:
-      ASSERT_NOT_REACHED();
+      NOTREACHED();
       return kNotFound;
   }
 }
@@ -487,12 +487,14 @@
 }
 
 void* ImagePlanes::plane(int i) {
-  ASSERT((i >= 0) && i < 3);
+  DCHECK_GE(i, 0);
+  DCHECK_LT(i, 3);
   return m_planes[i];
 }
 
 size_t ImagePlanes::rowBytes(int i) const {
-  ASSERT((i >= 0) && i < 3);
+  DCHECK_GE(i, 0);
+  DCHECK_LT(i, 3);
   return m_rowBytes[i];
 }
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
index 85e1649..3131b5f 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h
@@ -149,14 +149,14 @@
   // Image decoders that support YUV decoding must override this to
   // provide the size of each component.
   virtual IntSize decodedYUVSize(int component) const {
-    ASSERT(false);
+    NOTREACHED();
     return IntSize();
   }
 
   // Image decoders that support YUV decoding must override this to
   // return the width of each row of the memory allocation.
   virtual size_t decodedYUVWidthBytes(int component) const {
-    ASSERT(false);
+    NOTREACHED();
     return 0;
   }
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h b/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h
index 64de184..5b5eb8a 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h
+++ b/third_party/WebKit/Source/platform/image-decoders/ImageFrame.h
@@ -108,10 +108,10 @@
   // same X-coordinates on each subsequent row up to but not including
   // endY.
   void copyRowNTimes(int startX, int endX, int startY, int endY) {
-    ASSERT(startX < width());
-    ASSERT(endX <= width());
-    ASSERT(startY < height());
-    ASSERT(endY <= height());
+    DCHECK_LT(startX, width());
+    DCHECK_LE(endX, width());
+    DCHECK_LT(startY, height());
+    DCHECK_LE(endY, height());
     const int rowBytes = (endX - startX) * sizeof(PixelData);
     const PixelData* const startAddr = getAddr(startX, startY);
     for (int destY = startY + 1; destY < endY; ++destY)
diff --git a/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp b/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp
index 7857158..1f157379 100644
--- a/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/SegmentReader.cpp
@@ -128,7 +128,7 @@
 
   for (size_t sizeOfBlock = m_iter.size(); sizeOfBlock != 0;
        m_positionOfBlock += sizeOfBlock, sizeOfBlock = m_iter.size()) {
-    ASSERT(m_positionOfBlock <= position);
+    DCHECK_LE(m_positionOfBlock, position);
 
     if (m_positionOfBlock + sizeOfBlock > position) {
       // |position| is in this block.
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
index 3686236..c4bb3dd 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageDecoder.cpp
@@ -91,7 +91,7 @@
 
 bool BMPImageDecoder::processFileHeader(size_t& imgDataOffset) {
   // Read file header.
-  ASSERT(!m_decodedOffset);
+  DCHECK(!m_decodedOffset);
   if (m_data->size() < sizeOfFileHeader)
     return false;
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.cpp b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.cpp
index c6dacc9..a23eca7 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.cpp
@@ -115,7 +115,7 @@
     return false;
 
   // Initialize the framebuffer if needed.
-  ASSERT(m_buffer);  // Parent should set this before asking us to decode!
+  DCHECK(m_buffer);  // Parent should set this before asking us to decode!
   if (m_buffer->getStatus() == ImageFrame::FrameEmpty) {
     if (!m_buffer->setSizeAndColorSpace(m_parent->size().width(),
                                         m_parent->size().height(),
@@ -175,7 +175,7 @@
 
 bool BMPImageReader::readInfoHeaderSize() {
   // Get size of info header.
-  ASSERT(m_decodedOffset == m_headerOffset);
+  DCHECK_EQ(m_decodedOffset, m_headerOffset);
   if ((m_decodedOffset > m_data->size()) ||
       ((m_data->size() - m_decodedOffset) < 4))
     return false;
@@ -211,7 +211,7 @@
 
 bool BMPImageReader::processInfoHeader() {
   // Read info header.
-  ASSERT(m_decodedOffset == m_headerOffset);
+  DCHECK_EQ(m_decodedOffset, m_headerOffset);
   if ((m_decodedOffset > m_data->size()) ||
       ((m_data->size() - m_decodedOffset) < m_infoHeader.biSize) ||
       !readInfoHeader())
@@ -261,7 +261,7 @@
   if (m_isOS21x) {
     m_infoHeader.biWidth = readUint16(4);
     m_infoHeader.biHeight = readUint16(6);
-    ASSERT(!m_isInICO);  // ICO is a Windows format, not OS/2!
+    DCHECK(!m_isInICO);  // ICO is a Windows format, not OS/2!
     m_infoHeader.biBitCount = readUint16(10);
     return true;
   }
@@ -398,7 +398,7 @@
     default:
       // Some type we don't understand.  This should have been caught in
       // readInfoHeader().
-      ASSERT_NOT_REACHED();
+      NOTREACHED();
       return false;
   }
 
diff --git a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.h b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.h
index 50437be..7df8112 100644
--- a/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.h
+++ b/third_party/WebKit/Source/platform/image-decoders/bmp/BMPImageReader.h
@@ -226,7 +226,7 @@
         return readUint32(encodedPixel);
 
       default:
-        ASSERT_NOT_REACHED();
+        NOTREACHED();
         return 0;
     }
   }
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
index ea4acb2..8c4e988 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp
@@ -236,13 +236,13 @@
   referenceDecoder->setData(referenceData.get(), true);
   EXPECT_EQ(1u, referenceDecoder->frameCount());
   ImageFrame* referenceFrame = referenceDecoder->frameBufferAtIndex(0);
-  ASSERT(referenceFrame);
+  DCHECK(referenceFrame);
 
   std::unique_ptr<ImageDecoder> testDecoder = createDecoder();
   testDecoder->setData(testData.get(), true);
   EXPECT_EQ(1u, testDecoder->frameCount());
   ImageFrame* testFrame = testDecoder->frameBufferAtIndex(0);
-  ASSERT(testFrame);
+  DCHECK(testFrame);
 
   EXPECT_EQ(hashBitmap(referenceFrame->bitmap()),
             hashBitmap(testFrame->bitmap()));
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
index dabfd44..d518c747 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.cpp
@@ -312,7 +312,7 @@
 
   RELEASE_ASSERT(m_position + m_colors * BYTES_PER_COLORMAP_ENTRY <=
                  reader->size());
-  ASSERT(m_colors <= MAX_COLORS);
+  DCHECK_LE(m_colors, MAX_COLORS);
   char buffer[MAX_COLORS * BYTES_PER_COLORMAP_ENTRY];
   const unsigned char* srcColormap =
       reinterpret_cast<const unsigned char*>(reader->getConsecutiveData(
@@ -441,7 +441,7 @@
 
     switch (m_state) {
       case GIFLZW:
-        ASSERT(!m_frames.isEmpty());
+        DCHECK(!m_frames.isEmpty());
         // m_bytesToConsume is the current component size because it hasn't been
         // updated.
         m_frames.back()->addLzwBlock(currentComponentPosition,
@@ -450,7 +450,7 @@
         break;
 
       case GIFLZWStart: {
-        ASSERT(!m_frames.isEmpty());
+        DCHECK(!m_frames.isEmpty());
         m_frames.back()->setDataSize(static_cast<unsigned char>(
             reader.getOneByte(currentComponentPosition)));
         GETN(1, GIFSubBlock);
@@ -789,7 +789,7 @@
       }
 
       case GIFImageColormap: {
-        ASSERT(!m_frames.isEmpty());
+        DCHECK(!m_frames.isEmpty());
         m_frames.back()->localColorMap().setDefined();
         GETN(1, GIFLZWStart);
         break;
@@ -802,7 +802,7 @@
           GETN(bytesInBlock, GIFLZW);
         else {
           // Finished parsing one frame; Process next frame.
-          ASSERT(!m_frames.isEmpty());
+          DCHECK(!m_frames.isEmpty());
           // Note that some broken GIF files do not have enough LZW blocks to
           // fully decode all rows; we treat this case as "frame complete".
           m_frames.back()->setComplete();
@@ -828,7 +828,7 @@
 }
 
 void GIFImageReader::setRemainingBytes(size_t remainingBytes) {
-  ASSERT(remainingBytes <= m_data->size());
+  DCHECK_LE(remainingBytes, m_data->size());
   m_bytesRead = m_data->size() - remainingBytes;
 }
 
@@ -839,8 +839,8 @@
 
 // FIXME: Move this method to close to doLZW().
 bool GIFLZWContext::prepareToDecode() {
-  ASSERT(m_frameContext->isDataSizeDefined() &&
-         m_frameContext->isHeaderDefined());
+  DCHECK(m_frameContext->isDataSizeDefined());
+  DCHECK(m_frameContext->isHeaderDefined());
 
   // Since we use a codesize of 1 more than the datasize, we need to ensure
   // that our datasize is strictly less than the MAX_DICTIONARY_ENTRY_BITS.
diff --git a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h
index de91c1a..9525fe7 100644
--- a/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h
+++ b/third_party/WebKit/Source/platform/image-decoders/gif/GIFImageReader.h
@@ -49,7 +49,7 @@
 
 #define MAX_DICTIONARY_ENTRY_BITS 12
 #define MAX_DICTIONARY_ENTRIES 4096  // 2^MAX_DICTIONARY_ENTRY_BITS
-#define MAX_COLORS 256
+#define MAX_COLORS 256u
 #define BYTES_PER_COLORMAP_ENTRY 3
 
 const int cLoopCountNotSeen = -2;
diff --git a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
index 0288899..75944c4 100644
--- a/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/ico/ICOImageDecoder.cpp
@@ -228,7 +228,7 @@
 
 bool ICOImageDecoder::processDirectory() {
   // Read directory.
-  ASSERT(!m_decodedOffset);
+  DCHECK(!m_decodedOffset);
   if (m_data->size() < sizeOfDirectory)
     return false;
   const uint16_t fileType = readUint16(2);
@@ -246,7 +246,7 @@
 
 bool ICOImageDecoder::processDirectoryEntries() {
   // Read directory entries.
-  ASSERT(m_decodedOffset == sizeOfDirectory);
+  DCHECK_EQ(m_decodedOffset, sizeOfDirectory);
   if ((m_decodedOffset > m_data->size()) ||
       ((m_data->size() - m_decodedOffset) <
        (m_dirEntriesCount * sizeOfDirEntry)))
diff --git a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
index 84609d6e..64101275 100644
--- a/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/jpeg/JPEGImageDecoder.cpp
@@ -528,7 +528,7 @@
             return false;  // I/O suspension.
 
           // If we've completed image output...
-          ASSERT(m_info.output_scanline == m_info.output_height);
+          DCHECK_EQ(m_info.output_scanline, m_info.output_height);
           m_state = JPEG_DONE;
         }
       // FALL THROUGH
@@ -737,18 +737,22 @@
 }
 
 IntSize JPEGImageDecoder::decodedYUVSize(int component) const {
-  ASSERT((component >= 0) && (component <= 2) && m_reader);
+  DCHECK_GE(component, 0);
+  DCHECK_LE(component, 2);
+  DCHECK(m_reader);
   const jpeg_decompress_struct* info = m_reader->info();
 
-  ASSERT(info->out_color_space == JCS_YCbCr);
+  DCHECK_EQ(info->out_color_space, JCS_YCbCr);
   return computeYUVSize(info, component);
 }
 
 size_t JPEGImageDecoder::decodedYUVWidthBytes(int component) const {
-  ASSERT((component >= 0) && (component <= 2) && m_reader);
+  DCHECK_GE(component, 0);
+  DCHECK_LE(component, 2);
+  DCHECK(m_reader);
   const jpeg_decompress_struct* info = m_reader->info();
 
-  ASSERT(info->out_color_space == JCS_YCbCr);
+  DCHECK_EQ(info->out_color_space, JCS_YCbCr);
   return computeYUVWidthBytes(info, component);
 }
 
@@ -793,7 +797,7 @@
               ImageFrame::PixelData* pixel,
               JSAMPARRAY samples,
               int column) {
-  ASSERT_NOT_REACHED();
+  NOTREACHED();
 }
 
 // Used only for debugging with libjpeg (instead of libjpeg-turbo).
@@ -928,10 +932,10 @@
   // Initialize the framebuffer if needed.
   ImageFrame& buffer = m_frameBufferCache[0];
   if (buffer.getStatus() == ImageFrame::FrameEmpty) {
-    ASSERT(info->output_width ==
-           static_cast<JDIMENSION>(m_decodedSize.width()));
-    ASSERT(info->output_height ==
-           static_cast<JDIMENSION>(m_decodedSize.height()));
+    DCHECK_EQ(info->output_width,
+              static_cast<JDIMENSION>(m_decodedSize.width()));
+    DCHECK_EQ(info->output_height,
+              static_cast<JDIMENSION>(m_decodedSize.height()));
 
     if (!buffer.setSizeAndColorSpace(info->output_width, info->output_height,
                                      colorSpaceForSkImages()))
@@ -971,7 +975,7 @@
     case JCS_CMYK:
       return outputRows<JCS_CMYK>(m_reader.get(), buffer);
     default:
-      ASSERT_NOT_REACHED();
+      NOTREACHED();
   }
 
   return setFailed();
diff --git a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
index c9045baf..27757d08 100644
--- a/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/webp/WEBPImageDecoder.cpp
@@ -199,7 +199,7 @@
     return setFailed();
   }
 
-  ASSERT(m_demuxState > WEBP_DEMUX_PARSING_HEADER);
+  DCHECK_GT(m_demuxState, WEBP_DEMUX_PARSING_HEADER);
   if (!WebPDemuxGetI(m_demux, WEBP_FF_FRAME_COUNT))
     return false;  // Wait until the encoded image frame data arrives.
 
@@ -218,7 +218,7 @@
       // an ANIM chunk must precede the ANMF frame chunks.
       m_repetitionCount = WebPDemuxGetI(m_demux, WEBP_FF_LOOP_COUNT);
       // Repetition count is always <= 16 bits.
-      ASSERT(m_repetitionCount == (m_repetitionCount & 0xffff));
+      DCHECK_EQ(m_repetitionCount, m_repetitionCount & 0xffff);
       if (!m_repetitionCount)
         m_repetitionCount = cAnimationLoopInfinite;
       // FIXME: Implement ICC profile support for animated images.
@@ -229,7 +229,7 @@
       readColorProfile();
   }
 
-  ASSERT(isDecodedSizeAvailable());
+  DCHECK(isDecodedSizeAvailable());
 
   size_t frameCount = WebPDemuxGetI(m_demux, WEBP_FF_FRAME_COUNT);
   updateAggressivePurging(frameCount);
@@ -344,7 +344,7 @@
       buffer.getAlphaBlendSource() == ImageFrame::BlendAtopPreviousFrame &&
       buffer.requiredPreviousFrameIndex() != kNotFound) {
     ImageFrame& prevBuffer = m_frameBufferCache[frameIndex - 1];
-    ASSERT(prevBuffer.getStatus() == ImageFrame::FrameComplete);
+    DCHECK_EQ(prevBuffer.getStatus(), ImageFrame::FrameComplete);
     ImageFrame::DisposalMethod prevDisposalMethod =
         prevBuffer.getDisposalMethod();
     if (prevDisposalMethod == ImageFrame::DisposeKeep) {
@@ -385,12 +385,12 @@
 
 void WEBPImageDecoder::initializeNewFrame(size_t index) {
   if (!(m_formatFlags & ANIMATION_FLAG)) {
-    ASSERT(!index);
+    DCHECK(!index);
     return;
   }
   WebPIterator animatedFrame;
   WebPDemuxGetFrame(m_demux, index + 1, &animatedFrame);
-  ASSERT(animatedFrame.complete == 1);
+  DCHECK_EQ(animatedFrame.complete, 1);
   ImageFrame* buffer = &m_frameBufferCache[index];
   IntRect frameRect(animatedFrame.x_offset, animatedFrame.y_offset,
                     animatedFrame.width, animatedFrame.height);
@@ -415,7 +415,7 @@
 
   Vector<size_t> framesToDecode = findFramesToDecode(index);
 
-  ASSERT(m_demux);
+  DCHECK(m_demux);
   for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) {
     if ((m_formatFlags & ANIMATION_FLAG) && !initFrameBuffer(*i))
       return;
@@ -447,11 +447,11 @@
   if (failed())
     return false;
 
-  ASSERT(isDecodedSizeAvailable());
+  DCHECK(isDecodedSizeAvailable());
 
-  ASSERT(m_frameBufferCache.size() > frameIndex);
+  DCHECK_GT(m_frameBufferCache.size(), frameIndex);
   ImageFrame& buffer = m_frameBufferCache[frameIndex];
-  ASSERT(buffer.getStatus() != ImageFrame::FrameComplete);
+  DCHECK_NE(buffer.getStatus(), ImageFrame::FrameComplete);
 
   if (buffer.getStatus() == ImageFrame::FrameEmpty) {
     if (!buffer.setSizeAndColorSpace(size().width(), size().height(),
diff --git a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
index e43dcc01..3fcba55 100644
--- a/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebRemoteFrameImpl.cpp
@@ -4,6 +4,7 @@
 
 #include "web/WebRemoteFrameImpl.h"
 
+#include "bindings/core/v8/RemoteWindowProxy.h"
 #include "core/dom/Fullscreen.h"
 #include "core/dom/RemoteSecurityContext.h"
 #include "core/dom/SecurityContext.h"
@@ -21,10 +22,10 @@
 #include "public/web/WebPerformance.h"
 #include "public/web/WebRange.h"
 #include "public/web/WebTreeScopeType.h"
+#include "v8/include/v8.h"
 #include "web/RemoteFrameOwner.h"
 #include "web/WebLocalFrameImpl.h"
 #include "web/WebViewImpl.h"
-#include <v8/include/v8.h>
 
 namespace blink {
 
@@ -214,7 +215,9 @@
 
 v8::Local<v8::Context> WebRemoteFrameImpl::deprecatedMainWorldScriptContext()
     const {
-  return toV8Context(frame(), DOMWrapperWorld::mainWorld());
+  return static_cast<RemoteWindowProxy*>(
+             frame()->windowProxy(DOMWrapperWorld::mainWorld()))
+      ->contextIfInitialized();
 }
 
 void WebRemoteFrameImpl::reload(WebFrameLoadType) {
diff --git a/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom b/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom
index 43ac52f4..ddd5fd7 100644
--- a/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom
+++ b/third_party/WebKit/public/platform/modules/bluetooth/web_bluetooth.mojom
@@ -70,6 +70,10 @@
   DEVICE_NO_LONGER_IN_RANGE,
   GATT_NOT_PAIRED,
   GATT_OPERATION_IN_PROGRESS,
+  GATT_SERVER_DISCONNECTED,
+  GATT_SERVER_NOT_CONNECTED,
+  GATT_SERVER_DISCONNECTED_WHILE_RETRIEVING_CHARACTERISTICS,
+  GATT_SERVER_NOT_CONNECTED_CANNOT_RETRIEVE_CHARACTERISTICS,
   UNTRANSLATED_CONNECT_ERROR_CODE,
   // NotFoundError:
   NO_BLUETOOTH_ADAPTER,
@@ -102,7 +106,6 @@
   NOT_ALLOWED_TO_ACCESS_SERVICE,
   REQUEST_DEVICE_WITH_BLOCKLISTED_UUID,
   REQUEST_DEVICE_FROM_CROSS_ORIGIN_IFRAME,
-  REQUEST_DEVICE_WITHOUT_FRAME,
   DESCRIPTOR_NO_LONGER_EXISTS,
 };
 
diff --git a/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py b/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
index 5805345..c4bb1dc 100755
--- a/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
+++ b/tools/android/customtabs_benchmark/scripts/customtabs_benchmark.py
@@ -13,7 +13,6 @@
 import os
 import random
 import re
-import subprocess
 import sys
 import time
 
@@ -30,6 +29,7 @@
 import devil_chromium
 
 sys.path.append(os.path.join(_SRC_PATH, 'tools', 'android', 'loading'))
+import chrome_setup
 import device_setup
 
 
@@ -40,35 +40,6 @@
 _INVALID_VALUE = -1
 
 
-# Command line arguments for Chrome.
-CHROME_ARGS = [
-    # Disable backgound network requests that may pollute WPR archive, pollute
-    # HTTP cache generation, and introduce noise in loading performance.
-    '--disable-background-networking',
-    '--disable-default-apps',
-    '--no-proxy-server',
-    # TODO(droger): Remove once crbug.com/354743 is fixed.
-    '--safebrowsing-disable-auto-update',
-
-    # Disables actions that chrome performs only on first run or each launches,
-    # which can interfere with page load performance, or even block its
-    # execution by waiting for user input.
-    '--disable-fre',
-    '--no-default-browser-check',
-    '--no-first-run',
-]
-
-
-def ResetChromeLocalState(device):
-  """Remove the Chrome Profile and the various disk caches."""
-  profile_dirs = ['app_chrome/Default', 'cache', 'app_chrome/ShaderCache',
-                  'app_tabs']
-  cmd = ['rm', '-rf']
-  cmd.extend(
-      '/data/data/{}/{}'.format(_CHROME_PACKAGE, d) for d in profile_dirs)
-  device.adb.Shell(subprocess.list2cmdline(cmd))
-
-
 def RunOnce(device, url, warmup, speculation_mode, delay_to_may_launch_url,
             delay_to_launch_url, cold, chrome_args, reset_chrome_state):
   """Runs a test on a device once.
@@ -116,7 +87,7 @@
     device.ForceStop(_TEST_APP_PACKAGE_NAME)
 
     if reset_chrome_state:
-      ResetChromeLocalState(device)
+      chrome_setup.ResetChromeLocalState(device, _CHROME_PACKAGE)
 
     if cold:
       cache_control.CacheControl(device).DropRamCaches()
@@ -180,7 +151,7 @@
     try:
       while should_stop is None or not should_stop.is_set():
         config = configs[random.randint(0, len(configs) - 1)]
-        chrome_args = CHROME_ARGS + wpr_attributes.chrome_args
+        chrome_args = chrome_setup.CHROME_ARGS + wpr_attributes.chrome_args
         if config['speculation_mode'] == 'no_state_prefetch':
           # NoStatePrefetch is enabled through an experiment.
           chrome_args.extend([
diff --git a/tools/android/loading/chrome_setup.py b/tools/android/loading/chrome_setup.py
new file mode 100644
index 0000000..889f86d
--- /dev/null
+++ b/tools/android/loading/chrome_setup.py
@@ -0,0 +1,34 @@
+# 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.
+
+import subprocess
+
+
+# Command line arguments for Chrome for loading performance measurements.
+CHROME_ARGS = [
+    # Disable backgound network requests that may pollute WPR archive, pollute
+    # HTTP cache generation, and introduce noise in loading performance.
+    '--disable-background-networking',
+    '--disable-default-apps',
+    '--no-proxy-server',
+    # TODO(droger): Remove once crbug.com/354743 is fixed.
+    '--safebrowsing-disable-auto-update',
+
+    # Disables actions that chrome performs only on first run or each launches,
+    # which can interfere with page load performance, or even block its
+    # execution by waiting for user input.
+    '--disable-fre',
+    '--no-default-browser-check',
+    '--no-first-run',
+]
+
+
+def ResetChromeLocalState(device, package):
+  """Remove the Chrome Profile and the various disk caches."""
+  profile_dirs = ['app_chrome/Default', 'cache', 'app_chrome/ShaderCache',
+                  'app_tabs']
+  cmd = ['rm', '-rf']
+  cmd.extend(
+      '/data/data/{}/{}'.format(package, d) for d in profile_dirs)
+  device.adb.Shell(subprocess.list2cmdline(cmd))
diff --git a/tools/android/loading/controller.py b/tools/android/loading/controller.py
index ace200b03..b15b62d 100644
--- a/tools/android/loading/controller.py
+++ b/tools/android/loading/controller.py
@@ -27,6 +27,7 @@
 import psutil
 
 import chrome_cache
+import chrome_setup
 import common_util
 import device_setup
 import devtools_monitor
@@ -146,23 +147,7 @@
   DEVTOOLS_CONNECTION_ATTEMPT_INTERVAL_SECONDS = 1
 
   def __init__(self):
-    self._chrome_args = [
-        # Disable backgound network requests that may pollute WPR archive,
-        # pollute HTTP cache generation, and introduce noise in loading
-        # performance.
-        '--disable-background-networking',
-        '--disable-default-apps',
-        '--no-proxy-server',
-        # TODO(gabadie): Remove once crbug.com/354743 done.
-        '--safebrowsing-disable-auto-update',
-
-        # Disables actions that chrome performs only on first run or each
-        # launches, which can interfere with page load performance, or even
-        # block its execution by waiting for user input.
-        '--disable-fre',
-        '--no-default-browser-check',
-        '--no-first-run',
-
+    self._chrome_args = chrome_setup.CHROME_ARGS + [
         # Tests & dev-tools related stuff.
         '--enable-test-events',
         '--remote-debugging-port=%d' % OPTIONS.devtools_port,
@@ -402,14 +387,9 @@
   def ResetBrowserState(self):
     """Override resetting Chrome local state."""
     logging.info('Resetting Chrome local state')
-    package = OPTIONS.ChromePackage().package
-    # Remove the Chrome Profile and the various disk caches. Other parts
-    # theoretically should not affect loading performance. Also remove the tab
-    # state to prevent it from growing infinitely. [:D]
-    for directory in ['app_chrome/Default', 'cache', 'app_chrome/ShaderCache',
-                      'app_tabs']:
-      cmd = ['rm', '-rf', '/data/data/{}/{}'.format(package, directory)]
-      self._device.adb.Shell(subprocess.list2cmdline(cmd))
+    chrome_setup.ResetChromeLocalState(self._device,
+                                       OPTIONS.ChromePackage().package)
+
 
   def RebootDevice(self):
     """Reboot the remote device."""
diff --git a/tools/android/loading/wpr_helper.py b/tools/android/loading/wpr_helper.py
new file mode 100755
index 0000000..ab655a59
--- /dev/null
+++ b/tools/android/loading/wpr_helper.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python
+#
+# 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.
+
+"""Helper script to launch Chrome on device and WebPageReplay on host."""
+
+import logging
+import optparse
+import os
+import sys
+import time
+
+_SRC_PATH = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
+
+sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'catapult', 'devil'))
+from devil.android import device_utils
+from devil.android.constants import chrome
+from devil.android.perf import cache_control
+from devil.android.sdk import intent
+
+sys.path.append(os.path.join(_SRC_PATH, 'build', 'android'))
+import devil_chromium
+
+import chrome_setup
+import device_setup
+
+
+def RunChrome(device, cold, chrome_args, package_info):
+  """Runs Chrome on the device.
+
+  Args:
+    device: (DeviceUtils) device to run the tests on.
+    cold: (bool) Whether caches should be dropped.
+    chrome_args: ([str]) List of arguments to pass to Chrome.
+    package_info: (PackageInfo) Chrome package info.
+  """
+  if not device.HasRoot():
+    device.EnableRoot()
+
+  cmdline_file = package_info.cmdline_file
+  package = package_info.package
+  with device_setup.FlagReplacer(device, cmdline_file, chrome_args):
+    device.ForceStop(package)
+
+    if cold:
+      chrome_setup.ResetChromeLocalState(device, package)
+      cache_control.CacheControl(device).DropRamCaches()
+
+    start_intent = intent.Intent(package=package, data='about:blank',
+                                 activity=package_info.activity)
+    try:
+      device.StartActivity(start_intent, blocking=True)
+      print (
+          '\n\n'
+          '   +---------------------------------------------+\n'
+          '   | Chrome launched, press Ctrl-C to interrupt. |\n'
+          '   +---------------------------------------------+')
+      while True:
+        time.sleep(1)
+    except KeyboardInterrupt:
+      pass
+    finally:
+      device.ForceStop(package)
+
+
+def _CreateOptionParser():
+  description = 'Launches Chrome on a device, connected to a WebPageReplay ' \
+                'instance running on the host. The WPR archive must be ' \
+                'passed as parameter.'
+  parser = optparse.OptionParser(description=description,
+                                 usage='Usage: %prog [options] wpr_archive')
+
+  # Device-related options.
+  d = optparse.OptionGroup(parser, 'Device options')
+  d.add_option('--device', help='Device ID')
+  d.add_option('--cold', help='Purge all caches before running Chrome.',
+               default=False, action='store_true')
+  d.add_option('--chrome_package_name',
+               help='Chrome package name (e.g. "chrome" or "chromium") '
+               '[default: %default].', default='chrome')
+  parser.add_option_group(d)
+
+  # WebPageReplay-related options.
+  w = optparse.OptionGroup(parser, 'WebPageReplay options')
+  w.add_option('--record',
+               help='Enable this to record a new WPR archive.',
+               action='store_true', default=False)
+  w.add_option('--wpr_log', help='WPR log path.')
+  w.add_option('--network_condition', help='Network condition for emulation.')
+  parser.add_option_group(w)
+
+  return parser
+
+
+def main():
+  parser = _CreateOptionParser()
+  options, args = parser.parse_args()
+  if len(args) != 1:
+    parser.error("Incorrect number of arguments.")
+  devil_chromium.Initialize()
+  devices = device_utils.DeviceUtils.HealthyDevices()
+  device = devices[0]
+  if len(devices) != 1 and options.device is None:
+    logging.error('Several devices attached, must specify one with --device.')
+    sys.exit(0)
+  if options.device is not None:
+    matching_devices = [d for d in devices if str(d) == options.device]
+    if not matching_devices:
+      logging.error('Device not found.')
+      sys.exit(0)
+    device = matching_devices[0]
+
+  with device_setup.RemoteWprHost(device, args[0], options.record,
+                                  options.network_condition,
+                                  out_log_path=options.wpr_log) as wpr_attr:
+    RunChrome(device, options.cold,
+              chrome_setup.CHROME_ARGS + wpr_attr.chrome_args,
+              chrome.PACKAGE_INFO[options.chrome_package_name])
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 17197fe9..6c89c0a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -98393,6 +98393,7 @@
   <int value="1586022426" label="AutofillCreditCardAssist:enabled"/>
   <int value="1589341623" label="disable-easy-unlock"/>
   <int value="1612446645" label="enable-weak-memorycache"/>
+  <int value="1612871297" label="WebPayments:disabled"/>
   <int value="1612974229" label="allow-insecure-localhost"/>
   <int value="1617187093" label="enable-improved-a2hs"/>
   <int value="1622131033" label="ozone-test-single-overlay-support"/>
@@ -98462,6 +98463,7 @@
   <int value="1889076955" label="disable-app-link"/>
   <int value="1891210939" label="enable-blink-features"/>
   <int value="1892201400" label="enable-password-separated-signin-flow"/>
+  <int value="1893317228" label="WebPayments:enabled"/>
   <int value="1895587769" label="CredentialManagementAPI:disabled"/>
   <int value="1896456311" label="enable-password-save-in-page-navigation"/>
   <int value="1898231011" label="enable-native-notifications"/>
diff --git a/ui/app_list/app_list_item_list.cc b/ui/app_list/app_list_item_list.cc
index 6e1e564b..2cb13fe7 100644
--- a/ui/app_list/app_list_item_list.cc
+++ b/ui/app_list/app_list_item_list.cc
@@ -25,18 +25,16 @@
 }
 
 AppListItem* AppListItemList::FindItem(const std::string& id) {
-  for (size_t i = 0; i < app_list_items_.size(); ++i) {
-    AppListItem* item = app_list_items_[i];
+  for (const auto& item : app_list_items_) {
     if (item->id() == id)
-      return item;
+      return item.get();
   }
-  return NULL;
+  return nullptr;
 }
 
 bool AppListItemList::FindItemIndex(const std::string& id, size_t* index) {
   for (size_t i = 0; i < app_list_items_.size(); ++i) {
-    AppListItem* item = app_list_items_[i];
-    if (item->id() == id) {
+    if (app_list_items_[i]->id() == id) {
       *index = i;
       return true;
     }
@@ -50,17 +48,15 @@
   if (from_index == to_index)
     return;
 
-  AppListItem* target_item = app_list_items_[from_index];
+  auto target_item = std::move(app_list_items_[from_index]);
   DVLOG(2) << "MoveItem: " << from_index << " -> " << to_index << " ["
            << target_item->position().ToDebugString() << "]";
-
   // Remove the target item
-  app_list_items_.weak_erase(app_list_items_.begin() + from_index);
+  app_list_items_.erase(app_list_items_.begin() + from_index);
 
   // Update the position
-  AppListItem* prev = to_index > 0 ? app_list_items_[to_index - 1] : NULL;
-  AppListItem* next =
-      to_index < item_count() ? app_list_items_[to_index] : NULL;
+  AppListItem* prev = to_index > 0 ? item_at(to_index - 1) : nullptr;
+  AppListItem* next = to_index < item_count() ? item_at(to_index) : nullptr;
   CHECK_NE(prev, next);
   syncer::StringOrdinal new_position;
   if (!prev) {
@@ -83,9 +79,11 @@
            << " -> " << new_position.ToDebugString();
 
   // Insert the item and notify observers.
-  app_list_items_.insert(app_list_items_.begin() + to_index, target_item);
+  app_list_items_.insert(app_list_items_.begin() + to_index,
+                         std::move(target_item));
+  AppListItem* item = item_at(to_index);
   for (auto& observer : observers_)
-    observer.OnListItemMoved(from_index, to_index, target_item);
+    observer.OnListItemMoved(from_index, to_index, item);
 }
 
 void AppListItemList::SetItemPosition(AppListItem* item,
@@ -96,12 +94,12 @@
     LOG(ERROR) << "SetItemPosition: Not in list: " << item->id().substr(0, 8);
     return;
   }
-  DCHECK(app_list_items_[from_index] == item);
+  DCHECK(item_at(from_index) == item);
   if (!new_position.IsValid()) {
     size_t last_index = app_list_items_.size() - 1;
     if (from_index == last_index)
       return;  // Already last item, do nothing.
-    new_position = app_list_items_[last_index]->position().CreateAfter();
+    new_position = item_at(last_index)->position().CreateAfter();
   }
   // First check if the order would remain the same, in which case just update
   // the position.
@@ -112,13 +110,15 @@
     return;
   }
   // Remove the item and get the updated to index.
-  app_list_items_.weak_erase(app_list_items_.begin() + from_index);
-  to_index = GetItemSortOrderIndex(new_position, item->id());
-  DVLOG(2) << "SetItemPosition: " << item->id().substr(0, 8) << " -> "
+  auto target_item = std::move(app_list_items_[from_index]);
+  app_list_items_.erase(app_list_items_.begin() + from_index);
+  to_index = GetItemSortOrderIndex(new_position, target_item->id());
+  DVLOG(2) << "SetItemPosition: " << target_item->id().substr(0, 8) << " -> "
            << new_position.ToDebugString() << " From: " << from_index
            << " To: " << to_index;
-  item->set_position(new_position);
-  app_list_items_.insert(app_list_items_.begin() + to_index, item);
+  target_item->set_position(new_position);
+  app_list_items_.insert(app_list_items_.begin() + to_index,
+                         std::move(target_item));
   for (auto& observer : observers_)
     observer.OnListItemMoved(from_index, to_index, item);
 }
@@ -157,25 +157,27 @@
     index = nitems;
   } else {
     for (index = 0; index < nitems; ++index) {
-      if (!app_list_items_[index]->position().LessThan(position))
+      if (!item_at(index)->position().LessThan(position))
         break;
     }
   }
   if (index == 0)
-    return app_list_items_[0]->position().CreateBefore();
+    return item_at(0)->position().CreateBefore();
   if (index == nitems)
-    return app_list_items_[nitems - 1]->position().CreateAfter();
-  return app_list_items_[index - 1]->position().CreateBetween(
-      app_list_items_[index]->position());
+    return item_at(nitems - 1)->position().CreateAfter();
+  return item_at(index - 1)->position().CreateBetween(
+      item_at(index)->position());
 }
 
 AppListItem* AppListItemList::AddItem(std::unique_ptr<AppListItem> item_ptr) {
   AppListItem* item = item_ptr.get();
-  CHECK(std::find(app_list_items_.begin(), app_list_items_.end(), item)
-        == app_list_items_.end());
+  CHECK(std::find_if(app_list_items_.cbegin(), app_list_items_.cend(),
+                     [item](const std::unique_ptr<AppListItem>& item_p) {
+                       return item_p.get() == item;
+                     }) == app_list_items_.cend());
   EnsureValidItemPosition(item);
   size_t index = GetItemSortOrderIndex(item->position(), item->id());
-  app_list_items_.insert(app_list_items_.begin() + index, item_ptr.release());
+  app_list_items_.insert(app_list_items_.begin() + index, std::move(item_ptr));
   for (auto& observer : observers_)
     observer.OnListItemAdded(index, item);
 
@@ -203,11 +205,11 @@
 
 std::unique_ptr<AppListItem> AppListItemList::RemoveItemAt(size_t index) {
   CHECK_LT(index, item_count());
-  AppListItem* item = app_list_items_[index];
-  app_list_items_.weak_erase(app_list_items_.begin() + index);
+  auto item = std::move(app_list_items_[index]);
+  app_list_items_.erase(app_list_items_.begin() + index);
   for (auto& observer : observers_)
-    observer.OnListItemRemoved(index, item);
-  return base::WrapUnique<AppListItem>(item);
+    observer.OnListItemRemoved(index, item.get());
+  return item;
 }
 
 void AppListItemList::DeleteItemAt(size_t index) {
@@ -223,7 +225,7 @@
   if (nitems == 0) {
     position = syncer::StringOrdinal::CreateInitialOrdinal();
   } else {
-    position = app_list_items_[nitems - 1]->position().CreateAfter();
+    position = item_at(nitems - 1)->position().CreateAfter();
   }
   item->set_position(position);
 }
@@ -233,9 +235,9 @@
     const std::string& id) {
   DCHECK(position.IsValid());
   for (size_t index = 0; index < app_list_items_.size(); ++index) {
-    if (position.LessThan(app_list_items_[index]->position()) ||
-        (position.Equals(app_list_items_[index]->position()) &&
-         (id < app_list_items_[index]->id()))) {
+    if (position.LessThan(item_at(index)->position()) ||
+        (position.Equals(item_at(index)->position()) &&
+         (id < item_at(index)->id()))) {
       return index;
     }
   }
@@ -249,23 +251,24 @@
   DCHECK_GT(index, 0u);
   // Update the position of |index| and any necessary subsequent items.
   // First, find the next item that has a different position.
-  AppListItem* prev = app_list_items_[index - 1];
+  AppListItem* prev = item_at(index - 1);
   size_t last_index = index + 1;
   for (; last_index < nitems; ++last_index) {
-    if (!app_list_items_[last_index]->position().Equals(prev->position()))
+    if (!item_at(last_index)->position().Equals(prev->position()))
       break;
   }
-  AppListItem* last = last_index < nitems ? app_list_items_[last_index] : NULL;
+  AppListItem* last = last_index < nitems ? item_at(last_index) : nullptr;
   for (size_t i = index; i < last_index; ++i) {
-    AppListItem* cur = app_list_items_[i];
+    AppListItem* cur = item_at(i);
     if (last)
       cur->set_position(prev->position().CreateBetween(last->position()));
     else
       cur->set_position(prev->position().CreateAfter());
     prev = cur;
   }
+  AppListItem* item = item_at(index);
   for (auto& observer : observers_)
-    observer.OnListItemMoved(index, index, app_list_items_[index]);
+    observer.OnListItemMoved(index, index, item);
 }
 
 }  // namespace app_list
diff --git a/ui/app_list/app_list_item_list.h b/ui/app_list/app_list_item_list.h
index 99044e9b..72ae543 100644
--- a/ui/app_list/app_list_item_list.h
+++ b/ui/app_list/app_list_item_list.h
@@ -9,9 +9,9 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/observer_list.h"
 #include "components/sync/model/string_ordinal.h"
 #include "ui/app_list/app_list_export.h"
@@ -57,11 +57,11 @@
 
   AppListItem* item_at(size_t index) {
     DCHECK_LT(index, app_list_items_.size());
-    return app_list_items_[index];
+    return app_list_items_[index].get();
   }
   const AppListItem* item_at(size_t index) const {
     DCHECK_LT(index, app_list_items_.size());
-    return app_list_items_[index];
+    return app_list_items_[index].get();
   }
   size_t item_count() const { return app_list_items_.size(); }
 
@@ -108,7 +108,7 @@
   // previous item's position. |index| must be > 0.
   void FixItemPosition(size_t index);
 
-  ScopedVector<AppListItem> app_list_items_;
+  std::vector<std::unique_ptr<AppListItem>> app_list_items_;
   base::ObserverList<AppListItemListObserver, true> observers_;
   std::string highlighted_id_;
 
diff --git a/ui/app_list/search/mixer.cc b/ui/app_list/search/mixer.cc
index 78db4de..537c834 100644
--- a/ui/app_list/search/mixer.cc
+++ b/ui/app_list/search/mixer.cc
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "ui/app_list/search_provider.h"
 #include "ui/app_list/search_result.h"
 
@@ -126,7 +127,7 @@
 }
 
 size_t Mixer::AddGroup(size_t max_results, double multiplier) {
-  groups_.push_back(new Group(max_results, multiplier));
+  groups_.push_back(base::MakeUnique<Group>(max_results, multiplier));
   return groups_.size() - 1;
 }
 
@@ -144,7 +145,7 @@
 
   // Add results from each group. Limit to the maximum number of results in each
   // group.
-  for (const Group* group : groups_) {
+  for (const auto& group : groups_) {
     const size_t num_results =
         std::min(group->results().size(), group->max_results());
     results.insert(results.end(), group->results().begin(),
@@ -162,7 +163,7 @@
     // We didn't get enough results. Insert all the results again, and this
     // time, do not limit the maximum number of results from each group. (This
     // will result in duplicates, which will be removed by RemoveDuplicates.)
-    for (const Group* group : groups_) {
+    for (const auto& group : groups_) {
       results.insert(results.end(), group->results().begin(),
                      group->results().end());
     }
@@ -238,7 +239,7 @@
 
 void Mixer::FetchResults(bool is_voice_query,
                          const KnownResults& known_results) {
-  for (auto* group : groups_)
+  for (const auto& group : groups_)
     group->FetchResults(is_voice_query, known_results);
 }
 
diff --git a/ui/app_list/search/mixer.h b/ui/app_list/search/mixer.h
index aa77f6dd..253de6fd3 100644
--- a/ui/app_list/search/mixer.h
+++ b/ui/app_list/search/mixer.h
@@ -7,11 +7,11 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/app_list/app_list_export.h"
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/search/history_types.h"
@@ -65,7 +65,7 @@
   typedef std::vector<Mixer::SortData> SortedResults;
 
   class Group;
-  typedef ScopedVector<Group> Groups;
+  typedef std::vector<std::unique_ptr<Group>> Groups;
 
   // Publishes the given |new_results| to |ui_results|, deleting any existing
   // results that are not in |new_results|. Results that already exist in
diff --git a/ui/app_list/search/mixer_unittest.cc b/ui/app_list/search/mixer_unittest.cc
index 084c579..fb4f91d 100644
--- a/ui/app_list/search/mixer_unittest.cc
+++ b/ui/app_list/search/mixer_unittest.cc
@@ -6,12 +6,13 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/strings/string16.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -122,9 +123,9 @@
   void SetUp() override {
     results_.reset(new AppListModel::SearchResults);
 
-    providers_.push_back(new TestSearchProvider("app"));
-    providers_.push_back(new TestSearchProvider("omnibox"));
-    providers_.push_back(new TestSearchProvider("webstore"));
+    providers_.push_back(base::MakeUnique<TestSearchProvider>("app"));
+    providers_.push_back(base::MakeUnique<TestSearchProvider>("omnibox"));
+    providers_.push_back(base::MakeUnique<TestSearchProvider>("webstore"));
 
     is_voice_query_ = false;
 
@@ -134,9 +135,9 @@
     size_t omnibox_group_id = mixer_->AddGroup(kMaxOmniboxResults, 1.0);
     size_t webstore_group_id = mixer_->AddGroup(kMaxWebstoreResults, 0.5);
 
-    mixer_->AddProviderToGroup(apps_group_id, providers_[0]);
-    mixer_->AddProviderToGroup(omnibox_group_id, providers_[1]);
-    mixer_->AddProviderToGroup(webstore_group_id, providers_[2]);
+    mixer_->AddProviderToGroup(apps_group_id, providers_[0].get());
+    mixer_->AddProviderToGroup(omnibox_group_id, providers_[1].get());
+    mixer_->AddProviderToGroup(webstore_group_id, providers_[2].get());
   }
 
   void RunQuery() {
@@ -163,9 +164,9 @@
   }
 
   Mixer* mixer() { return mixer_.get(); }
-  TestSearchProvider* app_provider() { return providers_[0]; }
-  TestSearchProvider* omnibox_provider() { return providers_[1]; }
-  TestSearchProvider* webstore_provider() { return providers_[2]; }
+  TestSearchProvider* app_provider() { return providers_[0].get(); }
+  TestSearchProvider* omnibox_provider() { return providers_[1].get(); }
+  TestSearchProvider* webstore_provider() { return providers_[2].get(); }
 
   // Sets whether test runs should be treated as a voice query.
   void set_is_voice_query(bool is_voice_query) {
@@ -183,7 +184,7 @@
 
   bool is_voice_query_;
 
-  ScopedVector<TestSearchProvider> providers_;
+  std::vector<std::unique_ptr<TestSearchProvider>> providers_;
 
   DISALLOW_COPY_AND_ASSIGN(MixerTest);
 };
diff --git a/ui/app_list/views/app_list_main_view.h b/ui/app_list/views/app_list_main_view.h
index bee65ddb..790bc44 100644
--- a/ui/app_list/views/app_list_main_view.h
+++ b/ui/app_list/views/app_list_main_view.h
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "ui/app_list/app_list_export.h"
diff --git a/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc b/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc
index 720dfd5..519ffe4 100644
--- a/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc
+++ b/ui/events/ozone/evdev/event_converter_evdev_impl_unittest.cc
@@ -11,7 +11,6 @@
 
 #include "base/bind.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/message_loop/message_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
diff --git a/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h
index 9ae1956f..c453b67d 100644
--- a/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h
+++ b/ui/events/ozone/evdev/libgestures_glue/gesture_property_provider.h
@@ -18,7 +18,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/events/ozone/evdev/event_device_info.h"
 #include "ui/events/ozone/evdev/events_ozone_evdev_export.h"
 
diff --git a/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc b/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc
index 30bd240..ecbd840 100644
--- a/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc
+++ b/ui/events/ozone/evdev/tablet_event_converter_evdev_unittest.cc
@@ -17,7 +17,6 @@
 #include "base/files/file_util.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/run_loop.h"
 #include "base/time/time.h"
diff --git a/ui/gfx/render_text_harfbuzz.cc b/ui/gfx/render_text_harfbuzz.cc
index ebe3f2f..cbc9c27 100644
--- a/ui/gfx/render_text_harfbuzz.cc
+++ b/ui/gfx/render_text_harfbuzz.cc
@@ -491,7 +491,8 @@
   // no larger than |available_width_|.
   size_t GetCutoffPos(const internal::LineSegment& segment) const {
     DCHECK(!segment.char_range.is_empty());
-    const internal::TextRunHarfBuzz& run = *(run_list_.runs()[segment.run]);
+    const internal::TextRunHarfBuzz& run =
+        *(run_list_.runs()[segment.run]).get();
     size_t end_pos = segment.char_range.start();
     SkScalar width = 0;
     while (end_pos < segment.char_range.end()) {
@@ -804,7 +805,7 @@
   // Precalculate run width information.
   width_ = 0.0f;
   for (size_t i = 0; i < runs_.size(); ++i) {
-    TextRunHarfBuzz* run = runs_[visual_to_logical_[i]];
+    const auto& run = runs_[visual_to_logical_[i]];
     run->preceding_run_widths = width_;
     width_ += run->width;
   }
@@ -943,7 +944,7 @@
 
   internal::TextRunList* run_list = GetRunList();
   std::vector<RenderText::FontSpan> spans;
-  for (auto* run : run_list->runs()) {
+  for (const auto& run : run_list->runs()) {
     spans.push_back(RenderText::FontSpan(
         run->font, Range(DisplayIndexToTextIndex(run->range.start()),
                          DisplayIndexToTextIndex(run->range.end()))));
@@ -961,7 +962,7 @@
   if (run_index >= run_list->size())
     return Range(GetStringSize().width());
   const size_t layout_index = TextIndexToDisplayIndex(index);
-  internal::TextRunHarfBuzz* run = run_list->runs()[run_index];
+  internal::TextRunHarfBuzz* run = run_list->runs()[run_index].get();
   RangeF bounds = run->GetGraphemeBounds(this, layout_index);
   // If cursor is enabled, extend the last glyph up to the rightmost cursor
   // position since clients expect them to be contiguous.
@@ -1003,11 +1004,11 @@
     if (edge.caret_pos() == selection.caret_pos())
       return edge;
     int visual_index = (direction == CURSOR_RIGHT) ? 0 : run_list->size() - 1;
-    run = run_list->runs()[run_list->visual_to_logical(visual_index)];
+    run = run_list->runs()[run_list->visual_to_logical(visual_index)].get();
   } else {
     // If the cursor is moving within the current run, just move it by one
     // grapheme in the appropriate direction.
-    run = run_list->runs()[run_index];
+    run = run_list->runs()[run_index].get();
     size_t caret = selection.caret_pos();
     bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT);
     if (forward_motion) {
@@ -1026,7 +1027,7 @@
     visual_index += (direction == CURSOR_LEFT) ? -1 : 1;
     if (visual_index < 0 || visual_index >= static_cast<int>(run_list->size()))
       return EdgeSelectionModel(direction);
-    run = run_list->runs()[run_list->visual_to_logical(visual_index)];
+    run = run_list->runs()[run_list->visual_to_logical(visual_index)].get();
   }
   bool forward_motion = run->is_rtl == (direction == CURSOR_LEFT);
   return forward_motion ? FirstSelectionModelInsideRun(run) :
@@ -1317,7 +1318,7 @@
   LogicalCursorDirection affinity = caret.caret_affinity();
   internal::TextRunList* run_list = GetRunList();
   for (size_t i = 0; i < run_list->size(); ++i) {
-    internal::TextRunHarfBuzz* run = run_list->runs()[i];
+    internal::TextRunHarfBuzz* run = run_list->runs()[i].get();
     if (RangeContainsCaret(run->range, layout_position, affinity))
       return i;
   }
@@ -1348,10 +1349,10 @@
   // to misbehave since they expect non-zero text metrics from a non-empty text.
   base::i18n::BiDiLineIterator bidi_iterator;
   if (!bidi_iterator.Open(text, GetTextDirection(text))) {
-    internal::TextRunHarfBuzz* run =
-        new internal::TextRunHarfBuzz(font_list().GetPrimaryFont());
+    auto run = base::MakeUnique<internal::TextRunHarfBuzz>(
+        font_list().GetPrimaryFont());
     run->range = Range(0, text.length());
-    run_list_out->add(run);
+    run_list_out->Add(std::move(run));
     run_list_out->InitIndexMap();
     return;
   }
@@ -1369,8 +1370,8 @@
   internal::StyleIterator style(empty_colors, baselines(), weights(), styles());
 
   for (size_t run_break = 0; run_break < text.length();) {
-    internal::TextRunHarfBuzz* run =
-        new internal::TextRunHarfBuzz(font_list().GetPrimaryFont());
+    auto run = base::MakeUnique<internal::TextRunHarfBuzz>(
+        font_list().GetPrimaryFont());
     run->range.set_start(run_break);
     run->italic = style.style(ITALIC);
     run->baseline_type = style.baseline();
@@ -1406,7 +1407,7 @@
     style.UpdatePosition(DisplayIndexToTextIndex(run_break));
     run->range.set_end(run_break);
 
-    run_list_out->add(run);
+    run_list_out->Add(std::move(run));
   }
 
   // Undo the temporarily applied composition underlines and selection colors.
@@ -1437,8 +1438,8 @@
 
 void RenderTextHarfBuzz::ShapeRunList(const base::string16& text,
                                       internal::TextRunList* run_list) {
-  for (auto* run : run_list->runs())
-    ShapeRun(text, run);
+  for (const auto& run : run_list->runs())
+    ShapeRun(text, run.get());
   run_list->ComputePrecedingRunWidths();
 }
 
diff --git a/ui/gfx/render_text_harfbuzz.h b/ui/gfx/render_text_harfbuzz.h
index 4f52cdee..3c31f51 100644
--- a/ui/gfx/render_text_harfbuzz.h
+++ b/ui/gfx/render_text_harfbuzz.h
@@ -9,9 +9,9 @@
 #include <stdint.h>
 
 #include <memory>
+#include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "third_party/harfbuzz-ng/src/hb.h"
 #include "third_party/icu/source/common/unicode/ubidi.h"
 #include "third_party/icu/source/common/unicode/uscript.h"
@@ -109,10 +109,14 @@
     return logical_to_visual_[index];
   }
 
-  const ScopedVector<TextRunHarfBuzz>& runs() const { return runs_; }
+  const std::vector<std::unique_ptr<TextRunHarfBuzz>>& runs() const {
+    return runs_;
+  }
 
   // Adds the new |run| to the run list.
-  void add(TextRunHarfBuzz* run) { runs_.push_back(run); }
+  void Add(std::unique_ptr<TextRunHarfBuzz> run) {
+    runs_.push_back(std::move(run));
+  }
 
   // Reset the run list.
   void Reset();
@@ -132,7 +136,7 @@
 
  private:
   // Text runs in logical order.
-  ScopedVector<TextRunHarfBuzz> runs_;
+  std::vector<std::unique_ptr<TextRunHarfBuzz>> runs_;
 
   // Maps visual run indices to logical run indices and vice versa.
   std::vector<int32_t> visual_to_logical_;
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 0f3e5e9..a565606 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -3434,7 +3434,7 @@
     test_api()->EnsureLayout();
     internal::TextRunList* run_list = GetHarfBuzzRunList();
     ASSERT_EQ(1U, run_list->size());
-    internal::TextRunHarfBuzz* run = run_list->runs()[0];
+    internal::TextRunHarfBuzz* run = run_list->runs()[0].get();
 
     auto first_grapheme_bounds = run->GetGraphemeBounds(render_text, 0);
     EXPECT_EQ(first_grapheme_bounds, run->GetGraphemeBounds(render_text, 1));
@@ -3613,7 +3613,7 @@
   test_api()->EnsureLayout();
   internal::TextRunList* run_list = GetHarfBuzzRunList();
   ASSERT_EQ(1U, run_list->size());
-  internal::TextRunHarfBuzz* run = run_list->runs()[0];
+  internal::TextRunHarfBuzz* run = run_list->runs()[0].get();
   ShapeRunWithFont(render_text->text(), Font("TheFontThatDoesntExist", 13),
                    FontRenderParams(), run);
 }
diff --git a/ui/keyboard/keyboard_util.cc b/ui/keyboard/keyboard_util.cc
index b6bd071..b9bb9e00 100644
--- a/ui/keyboard/keyboard_util.cc
+++ b/ui/keyboard/keyboard_util.cc
@@ -54,6 +54,8 @@
 
 bool g_touch_keyboard_enabled = false;
 
+bool g_overscroll_enabled_with_accessibility_keyboard = false;
+
 keyboard::KeyboardState g_requested_keyboard_state =
     keyboard::KEYBOARD_STATE_AUTO;
 
@@ -145,8 +147,10 @@
 
   // Users of the accessibility on-screen keyboard are likely to be using mouse
   // input, which may interfere with overscrolling.
-  if (g_accessibility_keyboard_enabled)
+  if (g_accessibility_keyboard_enabled &&
+      !g_overscroll_enabled_with_accessibility_keyboard) {
     return false;
+  }
 
   // If overscroll enabled override is set, use it instead. Currently
   // login / out-of-box disable keyboard overscroll. http://crbug.com/363635
@@ -393,4 +397,8 @@
       keyboard::KEYBOARD_CONTROL_MAX);
 }
 
+void SetOverscrollEnabledWithAccessibilityKeyboard(bool enabled) {
+  g_overscroll_enabled_with_accessibility_keyboard = enabled;
+}
+
 }  // namespace keyboard
diff --git a/ui/keyboard/keyboard_util.h b/ui/keyboard/keyboard_util.h
index d32b098..ce4df68 100644
--- a/ui/keyboard/keyboard_util.h
+++ b/ui/keyboard/keyboard_util.h
@@ -178,6 +178,10 @@
 // Logs the keyboard control event as a UMA stat.
 void LogKeyboardControlEvent(KeyboardControlEvent event);
 
+// Sets true if keyboard overscrolling is enabled with accessibility keyboard.
+KEYBOARD_EXPORT void SetOverscrollEnabledWithAccessibilityKeyboard(
+    bool enabled);
+
 }  // namespace keyboard
 
 #endif  // UI_KEYBOARD_KEYBOARD_UTIL_H_
diff --git a/ui/message_center/message_center_impl.cc b/ui/message_center/message_center_impl.cc
index aa27168..0db1a54 100644
--- a/ui/message_center/message_center_impl.cc
+++ b/ui/message_center/message_center_impl.cc
@@ -6,11 +6,13 @@
 
 #include <algorithm>
 #include <deque>
+#include <memory>
 #include <utility>
+#include <vector>
 
 #include "base/command_line.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
+#include "base/memory/ptr_util.h"
 #include "base/observer_list.h"
 #include "base/stl_util.h"
 #include "build/build_config.h"
@@ -117,7 +119,7 @@
   void ApplyChangeInternal(MessageCenterImpl* message_center,
                            std::unique_ptr<Change> change);
 
-  ScopedVector<Change> changes_;
+  std::vector<std::unique_ptr<Change>> changes_;
 
   DISALLOW_COPY_AND_ASSIGN(ChangeQueue);
 };
@@ -127,7 +129,9 @@
 
 struct ChangeFinder {
   explicit ChangeFinder(const std::string& id) : id(id) {}
-  bool operator()(ChangeQueue::Change* change) { return change->id() == id; }
+  bool operator()(const std::unique_ptr<ChangeQueue::Change>& change) {
+    return change->id() == id;
+  }
 
   std::string id;
 };
@@ -170,10 +174,10 @@
 void ChangeQueue::ApplyChanges(MessageCenterImpl* message_center) {
   // This method is re-entrant.
   while (!changes_.empty()) {
-    ScopedVector<Change>::iterator iter = changes_.begin();
-    std::unique_ptr<Change> change(*iter);
+    auto iter = changes_.begin();
+    std::unique_ptr<Change> change(std::move(*iter));
     // TODO(dewittj): Replace changes_ with a deque.
-    changes_.weak_erase(iter);
+    changes_.erase(iter);
     ApplyChangeInternal(message_center, std::move(change));
   }
 }
@@ -185,16 +189,16 @@
 
   // Traverses the queue in reverse so shat we can track changes which change
   // the notification's ID.
-  ScopedVector<Change>::iterator iter = changes_.end();
+  auto iter = changes_.end();
   while (iter != changes_.begin()) {
     --iter;
     if (interesting_id != (*iter)->id())
       continue;
-    std::unique_ptr<Change> change(*iter);
+    std::unique_ptr<Change> change(std::move(*iter));
 
     interesting_id = change->notification_list_id();
 
-    iter = changes_.weak_erase(iter);
+    iter = changes_.erase(iter);
     changes_for_id.push_back(change.release());
   }
 
@@ -207,21 +211,22 @@
 
 void ChangeQueue::AddNotification(std::unique_ptr<Notification> notification) {
   std::string id = notification->id();
-  changes_.push_back(new Change(CHANGE_TYPE_ADD, id, std::move(notification)));
+  changes_.push_back(
+      base::MakeUnique<Change>(CHANGE_TYPE_ADD, id, std::move(notification)));
 }
 
 void ChangeQueue::UpdateNotification(
     const std::string& old_id,
     std::unique_ptr<Notification> notification) {
-  ScopedVector<Change>::reverse_iterator iter =
-    std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(old_id));
+  auto iter =
+      std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(old_id));
   if (iter == changes_.rend()) {
-    changes_.push_back(
-        new Change(CHANGE_TYPE_UPDATE, old_id, std::move(notification)));
+    changes_.push_back(base::MakeUnique<Change>(CHANGE_TYPE_UPDATE, old_id,
+                                                std::move(notification)));
     return;
   }
 
-  Change* change = *iter;
+  Change* change = iter->get();
   switch (change->type()) {
     case CHANGE_TYPE_ADD: {
       std::string id = notification->id();
@@ -229,8 +234,8 @@
       // its ID, some previous changes may affect new ID.
       // (eg. Add A, Update B->C, and This update A->B).
       changes_.erase(--(iter.base()));
-      changes_.push_back(
-          new Change(CHANGE_TYPE_ADD, id, std::move(notification)));
+      changes_.push_back(base::MakeUnique<Change>(CHANGE_TYPE_ADD, id,
+                                                  std::move(notification)));
       break;
     }
     case CHANGE_TYPE_UPDATE:
@@ -241,18 +246,18 @@
         std::string id = notification->id();
         // Safe to place the change at the last.
         changes_.erase(--(iter.base()));
-        changes_.push_back(
-            new Change(CHANGE_TYPE_ADD, id, std::move(notification)));
+        changes_.push_back(base::MakeUnique<Change>(CHANGE_TYPE_ADD, id,
+                                                    std::move(notification)));
       } else {
         // Complex case: gives up to optimize.
-        changes_.push_back(
-            new Change(CHANGE_TYPE_UPDATE, old_id, std::move(notification)));
+        changes_.push_back(base::MakeUnique<Change>(CHANGE_TYPE_UPDATE, old_id,
+                                                    std::move(notification)));
       }
       break;
     case CHANGE_TYPE_DELETE:
       // DELETE -> UPDATE. Something is wrong. Treats the UPDATE as ADD.
-      changes_.push_back(
-          new Change(CHANGE_TYPE_ADD, old_id, std::move(notification)));
+      changes_.push_back(base::MakeUnique<Change>(CHANGE_TYPE_ADD, old_id,
+                                                  std::move(notification)));
       break;
     default:
       NOTREACHED();
@@ -260,16 +265,16 @@
 }
 
 void ChangeQueue::EraseNotification(const std::string& id, bool by_user) {
-  ScopedVector<Change>::reverse_iterator iter =
-    std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(id));
+  auto iter =
+      std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(id));
   if (iter == changes_.rend()) {
-    std::unique_ptr<Change> change(new Change(CHANGE_TYPE_DELETE, id, nullptr));
+    auto change = base::MakeUnique<Change>(CHANGE_TYPE_DELETE, id, nullptr);
     change->set_by_user(by_user);
     changes_.push_back(std::move(change));
     return;
   }
 
-  Change* change = *iter;
+  Change* change = iter->get();
   switch (change->type()) {
     case CHANGE_TYPE_ADD:
       // ADD -> DELETE. Just removes both.
@@ -292,14 +297,12 @@
 }
 
 bool ChangeQueue::Has(const std::string& id) const {
-  ScopedVector<Change>::const_iterator iter =
-      std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id));
+  auto iter = std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id));
   return iter != changes_.end();
 }
 
 Notification* ChangeQueue::GetLatestNotification(const std::string& id) const {
-  ScopedVector<Change>::const_iterator iter =
-      std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id));
+  auto iter = std::find_if(changes_.begin(), changes_.end(), ChangeFinder(id));
   if (iter == changes_.end())
     return NULL;
 
diff --git a/ui/ozone/demo/ozone_demo.cc b/ui/ozone/demo/ozone_demo.cc
index a501fe79..42c08c3 100644
--- a/ui/ozone/demo/ozone_demo.cc
+++ b/ui/ozone/demo/ozone_demo.cc
@@ -8,7 +8,6 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "base/memory/scoped_vector.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/task_scheduler/task_scheduler.h"
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index eb0501c..9d41e98ad 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -14,6 +14,7 @@
 #include <utility>
 
 #include "base/containers/small_map.h"
+#include "base/memory/ptr_util.h"
 #include "ui/display/util/edid_parser.h"
 
 #if !defined(DRM_FORMAT_YV12)
@@ -28,8 +29,10 @@
 static const size_t kDefaultCursorWidth = 64;
 static const size_t kDefaultCursorHeight = 64;
 
-bool IsCrtcInUse(uint32_t crtc,
-                 const ScopedVector<HardwareDisplayControllerInfo>& displays) {
+bool IsCrtcInUse(
+    uint32_t crtc,
+    const std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>&
+        displays) {
   for (size_t i = 0; i < displays.size(); ++i) {
     if (crtc == displays[i]->crtc()->crtc_id)
       return true;
@@ -41,10 +44,12 @@
 // Return a CRTC compatible with |connector| and not already used in |displays|.
 // If there are multiple compatible CRTCs, the one that supports the majority of
 // planes will be returned.
-uint32_t GetCrtc(int fd,
-                 drmModeConnector* connector,
-                 drmModeRes* resources,
-                 const ScopedVector<HardwareDisplayControllerInfo>& displays) {
+uint32_t GetCrtc(
+    int fd,
+    drmModeConnector* connector,
+    drmModeRes* resources,
+    const std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>&
+        displays) {
   ScopedDrmPlaneResPtr plane_resources(drmModeGetPlaneResources(fd));
   std::vector<ScopedDrmPlanePtr> planes;
   for (uint32_t i = 0; i < plane_resources->count_planes; i++)
@@ -218,11 +223,11 @@
 HardwareDisplayControllerInfo::~HardwareDisplayControllerInfo() {
 }
 
-ScopedVector<HardwareDisplayControllerInfo> GetAvailableDisplayControllerInfos(
-    int fd) {
+std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>
+GetAvailableDisplayControllerInfos(int fd) {
   ScopedDrmResourcesPtr resources(drmModeGetResources(fd));
   DCHECK(resources) << "Failed to get DRM resources";
-  ScopedVector<HardwareDisplayControllerInfo> displays;
+  std::vector<std::unique_ptr<HardwareDisplayControllerInfo>> displays;
 
   std::vector<ScopedDrmConnectorPtr> available_connectors;
   std::vector<ScopedDrmConnectorPtr::element_type*> connectors;
@@ -270,7 +275,7 @@
     size_t index = std::find(connectors.begin(), connectors.end(), c.get()) -
                    connectors.begin();
     DCHECK_LT(index, connectors.size());
-    displays.push_back(new HardwareDisplayControllerInfo(
+    displays.push_back(base::MakeUnique<HardwareDisplayControllerInfo>(
         std::move(c), std::move(crtc), index));
   }
 
diff --git a/ui/ozone/platform/drm/common/drm_util.h b/ui/ozone/platform/drm/common/drm_util.h
index 17a62bb..52e90fe 100644
--- a/ui/ozone/platform/drm/common/drm_util.h
+++ b/ui/ozone/platform/drm/common/drm_util.h
@@ -7,9 +7,11 @@
 
 #include <stddef.h>
 
+#include <memory>
+#include <vector>
+
 #include "base/files/file_path.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 #include "ui/ozone/platform/drm/common/scoped_drm_types.h"
 
@@ -45,8 +47,8 @@
 
 // Looks-up and parses the native display configurations returning all available
 // displays.
-ScopedVector<HardwareDisplayControllerInfo> GetAvailableDisplayControllerInfos(
-    int fd);
+std::vector<std::unique_ptr<HardwareDisplayControllerInfo>>
+GetAvailableDisplayControllerInfos(int fd);
 
 bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs);
 
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
index 0d406a96..100c7a4 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.cc
@@ -75,9 +75,8 @@
   const DrmDeviceVector& devices = drm_device_manager_->GetDrmDevices();
   size_t device_index = 0;
   for (const auto& drm : devices) {
-    ScopedVector<HardwareDisplayControllerInfo> display_infos =
-        GetAvailableDisplayControllerInfos(drm->get_fd());
-    for (auto* display_info : display_infos) {
+    auto display_infos = GetAvailableDisplayControllerInfos(drm->get_fd());
+    for (const auto& display_info : display_infos) {
       auto it = std::find_if(
           old_displays.begin(), old_displays.end(),
           DisplayComparator(drm, display_info->crtc()->crtc_id,
@@ -89,7 +88,7 @@
         displays_.push_back(base::MakeUnique<DrmDisplay>(screen_manager_, drm));
       }
       params_list.push_back(
-          displays_.back()->Update(display_info, device_index));
+          displays_.back()->Update(display_info.get(), device_index));
     }
     device_index++;
   }
diff --git a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
index 068a62a1..389dfc5 100644
--- a/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
+++ b/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h
@@ -7,8 +7,10 @@
 
 #include <stdint.h>
 
+#include <memory>
+#include <vector>
+
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
 
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
index 989eec4..24e17c7 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -41,7 +41,7 @@
           HasEGLExtension("EGL_EXT_image_flush_external")),
       weak_factory_(this) {
   surface_factory_->RegisterSurface(window_->widget(), this);
-  unsubmitted_frames_.push_back(new PendingFrame());
+  unsubmitted_frames_.push_back(base::MakeUnique<PendingFrame>());
 }
 
 void GbmSurfaceless::QueueOverlayPlane(const OverlayPlane& plane) {
@@ -111,9 +111,9 @@
   SwapCompletionCallback surface_swap_callback = base::Bind(
       &GbmSurfaceless::SwapCompleted, weak_factory_.GetWeakPtr(), callback);
 
-  PendingFrame* frame = unsubmitted_frames_.back();
+  PendingFrame* frame = unsubmitted_frames_.back().get();
   frame->callback = surface_swap_callback;
-  unsubmitted_frames_.push_back(new PendingFrame());
+  unsubmitted_frames_.push_back(base::MakeUnique<PendingFrame>());
 
   // TODO: the following should be replaced by a per surface flush as it gets
   // implemented in GL drivers.
@@ -198,8 +198,8 @@
   DCHECK(!unsubmitted_frames_.empty());
 
   if (unsubmitted_frames_.front()->ready && !swap_buffers_pending_) {
-    std::unique_ptr<PendingFrame> frame(unsubmitted_frames_.front());
-    unsubmitted_frames_.weak_erase(unsubmitted_frames_.begin());
+    std::unique_ptr<PendingFrame> frame(std::move(unsubmitted_frames_.front()));
+    unsubmitted_frames_.erase(unsubmitted_frames_.begin());
     swap_buffers_pending_ = true;
 
     if (!frame->ScheduleOverlayPlanes(widget_)) {
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
index c800f1a..91b944c 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
+++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/gl/gl_image.h"
@@ -92,7 +91,7 @@
   // The native surface. Deleting this is allowed to free the EGLNativeWindow.
   gfx::AcceleratedWidget widget_;
   std::unique_ptr<gfx::VSyncProvider> vsync_provider_;
-  ScopedVector<PendingFrame> unsubmitted_frames_;
+  std::vector<std::unique_ptr<PendingFrame>> unsubmitted_frames_;
   bool has_implicit_external_sync_;
   bool has_image_flush_external_;
   bool last_swap_buffers_result_ = true;
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
index eda2ce3..1851ee4f 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_controller.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_controller.h
@@ -17,7 +17,6 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/gfx/swap_result.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h"
 #include "ui/ozone/platform/drm/gpu/overlay_plane.h"
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
index 800aa5a..15538c5 100644
--- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
+++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/ozone/platform/drm/common/scoped_drm_types.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
 #include "ui/ozone/platform/drm/gpu/overlay_plane.h"
diff --git a/ui/ozone/platform/drm/gpu/screen_manager.h b/ui/ozone/platform/drm/gpu/screen_manager.h
index 7785b94..3854422 100644
--- a/ui/ozone/platform/drm/gpu/screen_manager.h
+++ b/ui/ozone/platform/drm/gpu/screen_manager.h
@@ -10,7 +10,6 @@
 #include <unordered_map>
 
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/observer_list.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.cc b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
index ae8b0516..c603352 100644
--- a/ui/ozone/platform/drm/host/drm_display_host_manager.cc
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.cc
@@ -139,14 +139,15 @@
   proxy_->RegisterHandlerForDrmDisplayHostManager(this);
   proxy_->AddGpuThreadObserver(this);
 
-  ScopedVector<HardwareDisplayControllerInfo> display_infos =
+  auto display_infos =
       GetAvailableDisplayControllerInfos(primary_drm_device_handle_->fd());
   has_dummy_display_ = !display_infos.empty();
-  for (size_t i = 0; i < display_infos.size(); ++i) {
+  for (const auto& display_info : display_infos) {
     displays_.push_back(base::MakeUnique<DrmDisplayHost>(
-        proxy_, CreateDisplaySnapshotParams(
-                    display_infos[i], primary_drm_device_handle_->fd(),
-                    primary_drm_device_handle_->sys_path(), 0, gfx::Point()),
+        proxy_,
+        CreateDisplaySnapshotParams(
+            display_info.get(), primary_drm_device_handle_->fd(),
+            primary_drm_device_handle_->sys_path(), 0, gfx::Point()),
         true /* is_dummy */));
   }
 }
diff --git a/ui/ozone/platform/drm/host/drm_display_host_manager.h b/ui/ozone/platform/drm/host/drm_display_host_manager.h
index c2b68e6f..48097c7 100644
--- a/ui/ozone/platform/drm/host/drm_display_host_manager.h
+++ b/ui/ozone/platform/drm/host/drm_display_host_manager.h
@@ -14,7 +14,6 @@
 #include "base/file_descriptor_posix.h"
 #include "base/files/scoped_file.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "ui/display/types/native_display_delegate.h"
 #include "ui/events/ozone/device/device_event.h"
diff --git a/ui/ozone/platform/drm/host/drm_overlay_manager.h b/ui/ozone/platform/drm/host/drm_overlay_manager.h
index 7731eec..8afad3bb 100644
--- a/ui/ozone/platform/drm/host/drm_overlay_manager.h
+++ b/ui/ozone/platform/drm/host/drm_overlay_manager.h
@@ -11,7 +11,6 @@
 
 #include "base/containers/mru_cache.h"
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/ozone/platform/drm/host/gpu_thread_adapter.h"
 #include "ui/ozone/public/overlay_candidates_ozone.h"
 #include "ui/ozone/public/overlay_manager_ozone.h"
diff --git a/ui/views/examples/text_example.cc b/ui/views/examples/text_example.cc
index 0e65163..58e9994 100644
--- a/ui/views/examples/text_example.cc
+++ b/ui/views/examples/text_example.cc
@@ -5,6 +5,7 @@
 #include "ui/views/examples/text_example.h"
 
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font_list.h"
@@ -137,9 +138,9 @@
                                    int count) {
   layout->StartRow(0, 0);
   layout->AddView(new Label(base::ASCIIToUTF16(name)));
-  ExampleComboboxModel* model = new ExampleComboboxModel(strings, count);
-  example_combobox_model_.push_back(model);
-  Combobox* combobox = new Combobox(model);
+  example_combobox_model_.push_back(
+      base::MakeUnique<ExampleComboboxModel>(strings, count));
+  Combobox* combobox = new Combobox(example_combobox_model_.back().get());
   combobox->SetSelectedIndex(0);
   combobox->set_listener(this);
   layout->AddView(combobox, kNumColumns - 1, 1);
diff --git a/ui/views/examples/text_example.h b/ui/views/examples/text_example.h
index ad3090f..9f13a95 100644
--- a/ui/views/examples/text_example.h
+++ b/ui/views/examples/text_example.h
@@ -5,8 +5,10 @@
 #ifndef UI_VIEWS_EXAMPLES_TEXT_EXAMPLE_H_
 #define UI_VIEWS_EXAMPLES_TEXT_EXAMPLE_H_
 
+#include <memory>
+#include <vector>
+
 #include "base/macros.h"
-#include "base/memory/scoped_vector.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/examples/example_base.h"
@@ -81,7 +83,7 @@
 
   // We create a model for each of the combobox, so we need to keep them
   // around until destruction time.
-  ScopedVector<ExampleComboboxModel> example_combobox_model_;
+  std::vector<std::unique_ptr<ExampleComboboxModel>> example_combobox_model_;
 
   DISALLOW_COPY_AND_ASSIGN(TextExample);
 };