diff --git a/DEPS b/DEPS
index 9f33d05..df441687 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,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': 'b965fcb47296870643d001acb4a43cec3d88579a',
+  'skia_revision': 'c45a5c559365b2e04c89a3ef09752e5c1bb24aa2',
   # 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': '4fb310a1d7c008cf6b5772ab02c06f6f62bc4d6e',
+  'v8_revision': '66a2db59c17dfef7405a3ad13b47d28d815d7584',
   # 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.
@@ -117,7 +117,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '419acc8f4f3b238e714cbaaddeeea1bedd366bf8',
+  'angle_revision': 'e076a2327f0d082d46683f32614c495e049a4bb2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -550,7 +550,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '70331946770e5d627ce0be4648102ce357b614f3',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9a962fc1e6e5de1a1881292cb7575fc0bfd4b1a9',
       'condition': 'checkout_linux',
   },
 
@@ -575,7 +575,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8d3925b164822e2660d3b985402a5681432b0285',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'fb734036f4b5ae6d5afc63cbfc41d3a5d1c29a82',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1045,7 +1045,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a5c263cc63ffc2cc189b5214074c8792067c1853',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '3643aef89c785c6f1af625495023b64f5ffdbefe',
+    Var('webrtc_git') + '/src.git' + '@' + '056d811b6a044cd7e02da1133a520bc2d489836f',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1079,7 +1079,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5e752946b94c956c3def0ad2dd65e00705b8d4e6',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ed1bf6ade3398bd2b6580d62343c1037fe6aee6c',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/parent_output_surface.cc b/android_webview/browser/parent_output_surface.cc
index c109e0c..67b4f127 100644
--- a/android_webview/browser/parent_output_surface.cc
+++ b/android_webview/browser/parent_output_surface.cc
@@ -43,10 +43,12 @@
   context_provider_->ContextGL()->ShallowFlushCHROMIUM();
 }
 
+#if BUILDFLAG(ENABLE_VULKAN)
 gpu::VulkanSurface* ParentOutputSurface::GetVulkanSurface() {
   NOTIMPLEMENTED();
   return nullptr;
 }
+#endif
 
 bool ParentOutputSurface::HasExternalStencilTest() const {
   return ScopedAppGLStateRestore::Current()
diff --git a/android_webview/browser/parent_output_surface.h b/android_webview/browser/parent_output_surface.h
index 1e86919..973e2c8 100644
--- a/android_webview/browser/parent_output_surface.h
+++ b/android_webview/browser/parent_output_surface.h
@@ -29,7 +29,9 @@
                bool has_alpha,
                bool use_stencil) override;
   void SwapBuffers(viz::OutputSurfaceFrame frame) override;
+#if BUILDFLAG(ENABLE_VULKAN)
   gpu::VulkanSurface* GetVulkanSurface() override;
+#endif
   bool HasExternalStencilTest() const override;
   void ApplyExternalStencil() override;
   uint32_t GetFramebufferCopyTextureFormat() override;
diff --git a/ash/assistant/assistant_notification_controller.cc b/ash/assistant/assistant_notification_controller.cc
index 534d40f..1dac8e0 100644
--- a/ash/assistant/assistant_notification_controller.cc
+++ b/ash/assistant/assistant_notification_controller.cc
@@ -76,12 +76,22 @@
   DISALLOW_COPY_AND_ASSIGN(AssistantNotificationDelegate);
 };
 
+std::string GetNotificationId(const std::string& grouping_key) {
+  return kNotificationId + grouping_key;
+}
+
+message_center::NotifierId GetNotifierId() {
+  return message_center::NotifierId(
+      message_center::NotifierId::SYSTEM_COMPONENT, kNotifierAssistant);
+}
+
 }  // namespace
 
 AssistantNotificationController::AssistantNotificationController(
     AssistantController* assistant_controller)
     : assistant_controller_(assistant_controller),
       assistant_notification_subscriber_binding_(this),
+      notifier_id_(GetNotifierId()),
       weak_factory_(this) {}
 
 AssistantNotificationController::~AssistantNotificationController() = default;
@@ -113,9 +123,8 @@
 
   // Create the specified |notification| that should be rendered in the
   // |message_center| for the interaction.
-  notification_ = std::move(notification);
-  const base::string16 title = base::UTF8ToUTF16(notification_->title);
-  const base::string16 message = base::UTF8ToUTF16(notification_->message);
+  const base::string16 title = base::UTF8ToUTF16(notification->title);
+  const base::string16 message = base::UTF8ToUTF16(notification->message);
   const base::string16 display_source =
       l10n_util::GetStringUTF16(IDS_ASH_ASSISTANT_NOTIFICATION_DISPLAY_SOURCE);
 
@@ -125,14 +134,12 @@
 
   std::unique_ptr<message_center::Notification> system_notification =
       message_center::Notification::CreateSystemNotification(
-          message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, title,
-          message, gfx::Image(), display_source, GURL(),
-          message_center::NotifierId(
-              message_center::NotifierId::SYSTEM_COMPONENT, kNotifierAssistant),
-          optional_field,
+          message_center::NOTIFICATION_TYPE_SIMPLE,
+          GetNotificationId(notification->grouping_key), title, message,
+          gfx::Image(), display_source, GURL(), notifier_id_, optional_field,
           new AssistantNotificationDelegate(weak_factory_.GetWeakPtr(),
                                             assistant_controller_->GetWeakPtr(),
-                                            notification_.Clone()),
+                                            notification.Clone()),
           kAssistantIcon,
           message_center::SystemNotificationWarningLevel::NORMAL);
   system_notification->set_priority(message_center::DEFAULT_PRIORITY);
@@ -141,17 +148,17 @@
 
 void AssistantNotificationController::OnRemoveNotification(
     const std::string& grouping_key) {
-  if (!grouping_key.empty() &&
-      (!notification_ || notification_->grouping_key != grouping_key)) {
-    return;
-  }
-
-  // message_center has only one Assistant notification, so there is no
-  // difference between removing all and removing one Assistant notification.
-  notification_.reset();
   message_center::MessageCenter* message_center =
       message_center::MessageCenter::Get();
-  message_center->RemoveNotification(kNotificationId, /*by_user=*/false);
+  if (grouping_key.empty()) {
+    // Remove all assistant notifications by NotifierId.
+    message_center->RemoveNotificationsForNotifierId(notifier_id_);
+  } else {
+    // Remove the notification with |grouping_key|. It is no-op if no
+    // corresponding notification is found in |message_center|.
+    message_center->RemoveNotification(GetNotificationId(grouping_key),
+                                       /*by_user=*/false);
+  }
 }
 
 }  // namespace ash
diff --git a/ash/assistant/assistant_notification_controller.h b/ash/assistant/assistant_notification_controller.h
index 9188871..e332918 100644
--- a/ash/assistant/assistant_notification_controller.h
+++ b/ash/assistant/assistant_notification_controller.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chromeos/services/assistant/public/mojom/assistant.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "ui/message_center/public/cpp/notifier_id.h"
 
 namespace ash {
 
@@ -48,8 +49,7 @@
   // Owned by AssistantController.
   chromeos::assistant::mojom::Assistant* assistant_ = nullptr;
 
-  // Save the latest notification for future retrieval or dismiss operations.
-  AssistantNotificationPtr notification_;
+  const message_center::NotifierId notifier_id_;
 
   base::WeakPtrFactory<AssistantNotificationController> weak_factory_;
 
diff --git a/ash/content/keyboard_overlay/DEPS b/ash/content/keyboard_overlay/DEPS
index 0bffc37f..275939a 100644
--- a/ash/content/keyboard_overlay/DEPS
+++ b/ash/content/keyboard_overlay/DEPS
@@ -3,6 +3,6 @@
 ]
 specific_include_rules = {
   ".*test\.cc": [
-    "+content/public/test/test_browser_context.h",
+    "+content/public/test",
   ],
 }
diff --git a/ash/public/cpp/ash_layout_constants.cc b/ash/public/cpp/ash_layout_constants.cc
index 5129f56..5401f98 100644
--- a/ash/public/cpp/ash_layout_constants.cc
+++ b/ash/public/cpp/ash_layout_constants.cc
@@ -11,28 +11,19 @@
 
 gfx::Size GetAshLayoutSize(AshLayoutSize size) {
   constexpr int kButtonWidth = 32;
-  const int mode = ui::MaterialDesignController::GetMode();
-  switch (size) {
-    case AshLayoutSize::kBrowserCaptionMaximized: {
-      // These constants should be kept in sync with those for TAB_HEIGHT in
-      // chrome/browser/ui/layout_constants.cc.
-      // TODO: Ideally these values should be obtained from a common location.
-      constexpr int kBrowserMaximizedCaptionButtonHeight[] = {29, 33, 41, 34,
-                                                              41};
-      return gfx::Size(kButtonWidth,
-                       kBrowserMaximizedCaptionButtonHeight[mode]);
-    }
-    case AshLayoutSize::kBrowserCaptionRestored: {
-      constexpr int kBrowserRestoredCaptionButtonHeight[] = {36, 40, 48, 41,
-                                                             48};
-      return gfx::Size(kButtonWidth, kBrowserRestoredCaptionButtonHeight[mode]);
-    }
-    case AshLayoutSize::kNonBrowserCaption:
-      return gfx::Size(kButtonWidth, 32);
-  }
 
-  NOTREACHED();
-  return gfx::Size();
+  if (size == AshLayoutSize::kNonBrowserCaption)
+    return gfx::Size(kButtonWidth, 32);
+
+  // |kBrowserMaximizedCaptionButtonHeight| should be kept in sync with those
+  // for TAB_HEIGHT in // chrome/browser/ui/layout_constants.cc.
+  // TODO: Ideally these values should be obtained from a common location.
+  constexpr int kBrowserMaximizedCaptionButtonHeight[] = {29, 33, 41, 34, 41};
+  const int mode = ui::MaterialDesignController::GetMode();
+  int height = kBrowserMaximizedCaptionButtonHeight[mode];
+  if (size == AshLayoutSize::kBrowserCaptionRestored)
+    height += 8;  // Restored window titlebars are 8 DIP taller than maximized.
+  return gfx::Size(kButtonWidth, height);
 }
 
 }  // namespace ash
diff --git a/ash/wm/overview/scoped_transform_overview_window.cc b/ash/wm/overview/scoped_transform_overview_window.cc
index 864ab2aee..7f2456a 100644
--- a/ash/wm/overview/scoped_transform_overview_window.cc
+++ b/ash/wm/overview/scoped_transform_overview_window.cc
@@ -278,9 +278,8 @@
   return true;
 }
 
-void ScopedTransformOverviewWindow::OnCompositingStarted(
-    ui::Compositor* compositor,
-    base::TimeTicks start_time) {
+void ScopedTransformOverviewWindow::OnCompositingDidCommit(
+    ui::Compositor* compositor) {
   views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_);
   DCHECK(widget);
   DCHECK_EQ(compositor, widget->GetCompositor());
diff --git a/ash/wm/overview/scoped_transform_overview_window.h b/ash/wm/overview/scoped_transform_overview_window.h
index 3c0e085..f70003e 100644
--- a/ash/wm/overview/scoped_transform_overview_window.h
+++ b/ash/wm/overview/scoped_transform_overview_window.h
@@ -200,9 +200,9 @@
   void OnImplicitAnimationsCompleted() override;
 
   // ui::CompositorObserver:
-  void OnCompositingDidCommit(ui::Compositor* compositor) override {}
+  void OnCompositingDidCommit(ui::Compositor* compositor) override;
   void OnCompositingStarted(ui::Compositor* compositor,
-                            base::TimeTicks start_time) override;
+                            base::TimeTicks start_time) override {}
   void OnCompositingEnded(ui::Compositor* compositor) override {}
   void OnCompositingLockStateChanged(ui::Compositor* compositor) override {}
   void OnCompositingChildResizing(ui::Compositor* compositor) override {}
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index cd16765..c45a685 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -137,8 +137,10 @@
 bool HeadsUpDisplayLayerImpl::WillDraw(
     DrawMode draw_mode,
     viz::ClientResourceProvider* resource_provider) {
-  if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE)
+  if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE &&
+      !LayerImpl::WillDraw(draw_mode, resource_provider)) {
     return false;
+  }
 
   int max_texture_size = layer_tree_impl()->max_texture_size();
   internal_contents_scale_ = GetIdealContentsScale();
@@ -147,7 +149,7 @@
   internal_content_bounds_.SetToMin(
       gfx::Size(max_texture_size, max_texture_size));
 
-  return LayerImpl::WillDraw(draw_mode, resource_provider);
+  return true;
 }
 
 void HeadsUpDisplayLayerImpl::AppendQuads(viz::RenderPass* render_pass,
diff --git a/cc/layers/heads_up_display_layer_impl_unittest.cc b/cc/layers/heads_up_display_layer_impl_unittest.cc
index 1ffb91f9..f5dc43ee 100644
--- a/cc/layers/heads_up_display_layer_impl_unittest.cc
+++ b/cc/layers/heads_up_display_layer_impl_unittest.cc
@@ -51,6 +51,7 @@
   std::unique_ptr<HeadsUpDisplayLayerImpl> layer_ptr =
       HeadsUpDisplayLayerImpl::Create(host_impl.pending_tree(), 1);
   layer_ptr->SetBounds(gfx::Size(100, 100));
+  layer_ptr->set_visible_layer_rect(gfx::Rect(100, 100));
 
   HeadsUpDisplayLayerImpl* layer = layer_ptr.get();
 
@@ -84,6 +85,7 @@
   std::unique_ptr<HeadsUpDisplayLayerImpl> layer_ptr =
       HeadsUpDisplayLayerImpl::Create(host_impl.pending_tree(), 1);
   layer_ptr->SetBounds(gfx::Size(100, 100));
+  layer_ptr->set_visible_layer_rect(gfx::Rect(100, 100));
 
   HeadsUpDisplayLayerImpl* layer = layer_ptr.get();
 
diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc
index 389e630..f0ae0f4b 100644
--- a/cc/layers/layer_impl.cc
+++ b/cc/layers/layer_impl.cc
@@ -71,7 +71,6 @@
       effect_tree_index_(EffectTree::kInvalidNodeId),
       clip_tree_index_(ClipTree::kInvalidNodeId),
       scroll_tree_index_(ScrollTree::kInvalidNodeId),
-      current_draw_mode_(DRAW_MODE_NONE),
       debug_info_(nullptr),
       has_will_change_transform_hint_(false),
       needs_push_properties_(false),
@@ -89,7 +88,6 @@
 }
 
 LayerImpl::~LayerImpl() {
-  DCHECK_EQ(DRAW_MODE_NONE, current_draw_mode_);
   layer_tree_impl_->UnregisterLayer(this);
   layer_tree_impl_->RemoveFromElementLayerList(element_id_);
   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
@@ -167,17 +165,17 @@
 
 bool LayerImpl::WillDraw(DrawMode draw_mode,
                          viz::ClientResourceProvider* resource_provider) {
-  // WillDraw/DidDraw must be matched.
-  DCHECK_NE(DRAW_MODE_NONE, draw_mode);
-  DCHECK_EQ(DRAW_MODE_NONE, current_draw_mode_);
+  if (visible_layer_rect().IsEmpty() ||
+      draw_properties().occlusion_in_content_space.IsOccluded(
+          visible_layer_rect())) {
+    return false;
+  }
+
   current_draw_mode_ = draw_mode;
   return true;
 }
 
-void LayerImpl::DidDraw(viz::ClientResourceProvider* resource_provider) {
-  DCHECK_NE(DRAW_MODE_NONE, current_draw_mode_);
-  current_draw_mode_ = DRAW_MODE_NONE;
-}
+void LayerImpl::DidDraw(viz::ClientResourceProvider* resource_provider) {}
 
 bool LayerImpl::ShowDebugBorders(DebugBorderType type) const {
   return layer_tree_impl()->debug_state().show_debug_borders.test(type);
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index 9555515..dcbd0fb 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -133,9 +133,7 @@
   // WillDraw must be called before AppendQuads. If WillDraw returns false,
   // AppendQuads and DidDraw will not be called. If WillDraw returns true,
   // DidDraw is guaranteed to be called before another WillDraw or before
-  // the layer is destroyed. To enforce this, any class that overrides
-  // WillDraw/DidDraw must call the base class version only if WillDraw
-  // returns true.
+  // the layer is destroyed.
   virtual bool WillDraw(DrawMode draw_mode,
                         viz::ClientResourceProvider* resource_provider);
   virtual void AppendQuads(viz::RenderPass* render_pass,
diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc
index 5b099a3..3f457b5 100644
--- a/cc/layers/surface_layer.cc
+++ b/cc/layers/surface_layer.cc
@@ -17,8 +17,19 @@
   return base::WrapRefCounted(new SurfaceLayer());
 }
 
+scoped_refptr<SurfaceLayer> SurfaceLayer::Create(
+    UpdateSubmissionStateCB update_submission_state_callback) {
+  return base::WrapRefCounted(
+      new SurfaceLayer(std::move(update_submission_state_callback)));
+}
+
 SurfaceLayer::SurfaceLayer() = default;
 
+SurfaceLayer::SurfaceLayer(
+    UpdateSubmissionStateCB update_submission_state_callback)
+    : update_submission_state_callback_(
+          std::move(update_submission_state_callback)) {}
+
 SurfaceLayer::~SurfaceLayer() {
   DCHECK(!layer_tree_host());
 }
@@ -97,9 +108,16 @@
   SetNeedsPushProperties();
 }
 
+void SurfaceLayer::SetMayContainVideo(bool may_contain_video) {
+  may_contain_video_ = may_contain_video;
+}
+
 std::unique_ptr<LayerImpl> SurfaceLayer::CreateLayerImpl(
     LayerTreeImpl* tree_impl) {
-  return SurfaceLayerImpl::Create(tree_impl, id());
+  auto layer_impl = SurfaceLayerImpl::Create(tree_impl, id(),
+                                             update_submission_state_callback_);
+  layer_impl->set_may_contain_video(may_contain_video_);
+  return layer_impl;
 }
 
 bool SurfaceLayer::HasDrawableContent() const {
diff --git a/cc/layers/surface_layer.h b/cc/layers/surface_layer.h
index fcc684c..cee2367 100644
--- a/cc/layers/surface_layer.h
+++ b/cc/layers/surface_layer.h
@@ -16,11 +16,16 @@
 
 namespace cc {
 
+// If given true, we should submit frames, as we are unoccluded on screen.
+// If given false, we should not submit compositor frames.
+using UpdateSubmissionStateCB = base::RepeatingCallback<void(bool)>;
+
 // A layer that renders a surface referencing the output of another compositor
 // instance or client.
 class CC_EXPORT SurfaceLayer : public Layer {
  public:
   static scoped_refptr<SurfaceLayer> Create();
+  static scoped_refptr<SurfaceLayer> Create(UpdateSubmissionStateCB);
 
   void SetPrimarySurfaceId(const viz::SurfaceId& surface_id,
                            const DeadlinePolicy& deadline_policy);
@@ -36,6 +41,8 @@
   void SetSurfaceHitTestable(bool surface_hit_testable);
   bool surface_hit_testable() const { return surface_hit_testable_; }
 
+  void SetMayContainVideo(bool);
+
   // Layer overrides.
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void SetLayerTreeHost(LayerTreeHost* host) override;
@@ -55,6 +62,7 @@
 
  protected:
   SurfaceLayer();
+  explicit SurfaceLayer(UpdateSubmissionStateCB);
   bool HasDrawableContent() const override;
 
  private:
@@ -63,6 +71,9 @@
   // Returns a SurfaceRange corresponding to the surface layer.
   viz::SurfaceRange GetSurfaceRange() const;
 
+  UpdateSubmissionStateCB update_submission_state_callback_;
+
+  bool may_contain_video_ = false;
   viz::SurfaceId primary_surface_id_;
   viz::SurfaceId fallback_surface_id_;
   base::Optional<uint32_t> deadline_in_frames_ = 0u;
diff --git a/cc/layers/surface_layer_impl.cc b/cc/layers/surface_layer_impl.cc
index 8e383af..3577afa 100644
--- a/cc/layers/surface_layer_impl.cc
+++ b/cc/layers/surface_layer_impl.cc
@@ -16,14 +16,20 @@
 
 namespace cc {
 
-SurfaceLayerImpl::SurfaceLayerImpl(LayerTreeImpl* tree_impl, int id)
-    : LayerImpl(tree_impl, id) {}
+SurfaceLayerImpl::SurfaceLayerImpl(
+    LayerTreeImpl* tree_impl,
+    int id,
+    UpdateSubmissionStateCB update_submission_state_callback)
+    : LayerImpl(tree_impl, id),
+      update_submission_state_callback_(
+          std::move(update_submission_state_callback)) {}
 
 SurfaceLayerImpl::~SurfaceLayerImpl() = default;
 
 std::unique_ptr<LayerImpl> SurfaceLayerImpl::CreateLayerImpl(
     LayerTreeImpl* tree_impl) {
-  return SurfaceLayerImpl::Create(tree_impl, id());
+  return SurfaceLayerImpl::Create(tree_impl, id(),
+                                  std::move(update_submission_state_callback_));
 }
 
 void SurfaceLayerImpl::SetPrimarySurfaceId(
@@ -89,6 +95,22 @@
   layer_impl->SetSurfaceHitTestable(surface_hit_testable_);
 }
 
+bool SurfaceLayerImpl::WillDraw(
+    DrawMode draw_mode,
+    viz::ClientResourceProvider* resource_provider) {
+  bool will_draw = LayerImpl::WillDraw(draw_mode, resource_provider);
+  // If we have a change in WillDraw (meaning that visibility has changed), we
+  // want to inform the VideoFrameSubmitter to start or stop submitting
+  // compositor frames.
+  if (will_draw_ != will_draw) {
+    will_draw_ = will_draw;
+    if (update_submission_state_callback_)
+      update_submission_state_callback_.Run(will_draw);
+  }
+
+  return primary_surface_id_.is_valid() && will_draw;
+}
+
 void SurfaceLayerImpl::AppendQuads(viz::RenderPass* render_pass,
                                    AppendQuadsData* append_quads_data) {
   AppendRainbowDebugBorder(render_pass);
diff --git a/cc/layers/surface_layer_impl.h b/cc/layers/surface_layer_impl.h
index ce24320..cd433f7 100644
--- a/cc/layers/surface_layer_impl.h
+++ b/cc/layers/surface_layer_impl.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "cc/cc_export.h"
@@ -17,12 +18,25 @@
 
 namespace cc {
 
+// This must match SurfaceLayer::UpdateSubmissionStateCB.
+using UpdateSubmissionStateCB = base::RepeatingCallback<void(bool)>;
+
 class CC_EXPORT SurfaceLayerImpl : public LayerImpl {
  public:
+  static std::unique_ptr<SurfaceLayerImpl> Create(
+      LayerTreeImpl* tree_impl,
+      int id,
+      UpdateSubmissionStateCB update_submission_state_callback) {
+    return base::WrapUnique(new SurfaceLayerImpl(
+        tree_impl, id, std::move(update_submission_state_callback)));
+  }
+
   static std::unique_ptr<SurfaceLayerImpl> Create(LayerTreeImpl* tree_impl,
                                                   int id) {
-    return base::WrapUnique(new SurfaceLayerImpl(tree_impl, id));
+    return base::WrapUnique(
+        new SurfaceLayerImpl(tree_impl, id, base::BindRepeating([](bool) {})));
   }
+
   ~SurfaceLayerImpl() override;
 
   void SetPrimarySurfaceId(const viz::SurfaceId& surface_id,
@@ -56,12 +70,14 @@
   // LayerImpl overrides.
   std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
   void PushPropertiesTo(LayerImpl* layer) override;
+  bool WillDraw(DrawMode draw_mode,
+                viz::ClientResourceProvider* resource_provider) override;
   void AppendQuads(viz::RenderPass* render_pass,
                    AppendQuadsData* append_quads_data) override;
   bool is_surface_layer() const override;
 
  protected:
-  SurfaceLayerImpl(LayerTreeImpl* tree_impl, int id);
+  SurfaceLayerImpl(LayerTreeImpl* tree_impl, int id, UpdateSubmissionStateCB);
 
  private:
   viz::SurfaceDrawQuad* CreateSurfaceDrawQuad(
@@ -74,12 +90,14 @@
   void AsValueInto(base::trace_event::TracedValue* dict) const override;
   const char* LayerTypeAsString() const override;
 
+  UpdateSubmissionStateCB update_submission_state_callback_;
   viz::SurfaceId primary_surface_id_;
   viz::SurfaceId fallback_surface_id_;
   base::Optional<uint32_t> deadline_in_frames_;
 
   bool stretch_content_to_fill_bounds_ = false;
   bool surface_hit_testable_ = false;
+  bool will_draw_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(SurfaceLayerImpl);
 };
diff --git a/cc/layers/surface_layer_impl_unittest.cc b/cc/layers/surface_layer_impl_unittest.cc
index d8a5f6b..36a57d2 100644
--- a/cc/layers/surface_layer_impl_unittest.cc
+++ b/cc/layers/surface_layer_impl_unittest.cc
@@ -44,6 +44,7 @@
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(),
                                                  gfx::Rect(layer_size));
     EXPECT_EQ(1u, impl.quad_list().size());
+    EXPECT_TRUE(surface_layer_impl->WillDraw(DRAW_MODE_HARDWARE, nullptr));
   }
 
   {
@@ -53,6 +54,7 @@
 
     LayerTestCommon::VerifyQuadsExactlyCoverRect(impl.quad_list(), gfx::Rect());
     EXPECT_EQ(impl.quad_list().size(), 0u);
+    EXPECT_FALSE(surface_layer_impl->WillDraw(DRAW_MODE_HARDWARE, nullptr));
   }
 
   {
@@ -66,6 +68,7 @@
     // The layer outputs one quad, which is partially occluded.
     EXPECT_EQ(1u, impl.quad_list().size());
     EXPECT_EQ(1u, partially_occluded_count);
+    EXPECT_TRUE(surface_layer_impl->WillDraw(DRAW_MODE_HARDWARE, nullptr));
   }
 }
 
diff --git a/cc/layers/texture_layer_impl.cc b/cc/layers/texture_layer_impl.cc
index 9bf803b..137534d 100644
--- a/cc/layers/texture_layer_impl.cc
+++ b/cc/layers/texture_layer_impl.cc
@@ -88,6 +88,9 @@
     return false;
   }
 
+  if (!LayerImpl::WillDraw(draw_mode, resource_provider))
+    return false;
+
   if (own_resource_) {
     DCHECK(!resource_id_);
     if (!transferable_resource_.mailbox_holder.mailbox.IsZero()) {
@@ -98,7 +101,7 @@
     own_resource_ = false;
   }
 
-  return resource_id_ && LayerImpl::WillDraw(draw_mode, resource_provider);
+  return resource_id_;
 }
 
 void TextureLayerImpl::AppendQuads(viz::RenderPass* render_pass,
diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc
index 4624e15..18c72de3 100644
--- a/cc/layers/texture_layer_unittest.cc
+++ b/cc/layers/texture_layer_unittest.cc
@@ -850,6 +850,12 @@
     EXPECT_TRUE(host_impl_.InitializeFrameSink(layer_tree_frame_sink_.get()));
   }
 
+  std::unique_ptr<TextureLayerImpl> CreateTextureLayer() {
+    auto layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1);
+    layer->set_visible_layer_rect(gfx::Rect(100, 100));
+    return layer;
+  }
+
   bool WillDraw(TextureLayerImpl* layer, DrawMode mode) {
     bool will_draw = layer->WillDraw(
         mode, host_impl_.active_tree()->resource_provider());
@@ -873,8 +879,7 @@
       .Times(AnyNumber());
   // Hardware mode.
   {
-    std::unique_ptr<TextureLayerImpl> impl_layer =
-        TextureLayerImpl::Create(host_impl_.active_tree(), 1);
+    std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer();
     impl_layer->SetTransferableResource(
         test_data_.resource1_,
         viz::SingleReleaseCallback::Create(test_data_.release_callback1_));
@@ -890,8 +895,7 @@
 
   // Software mode.
   {
-    std::unique_ptr<TextureLayerImpl> impl_layer =
-        TextureLayerImpl::Create(host_impl_.active_tree(), 1);
+    std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer();
     impl_layer->SetTransferableResource(
         test_data_.resource1_,
         viz::SingleReleaseCallback::Create(test_data_.release_callback1_));
@@ -899,16 +903,14 @@
   }
 
   {
-    std::unique_ptr<TextureLayerImpl> impl_layer =
-        TextureLayerImpl::Create(host_impl_.active_tree(), 1);
+    std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer();
     impl_layer->SetTransferableResource(viz::TransferableResource(), nullptr);
     EXPECT_FALSE(WillDraw(impl_layer.get(), DRAW_MODE_SOFTWARE));
   }
 
   {
     // Software resource.
-    std::unique_ptr<TextureLayerImpl> impl_layer =
-        TextureLayerImpl::Create(host_impl_.active_tree(), 1);
+    std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer();
     impl_layer->SetTransferableResource(
         test_data_.sw_resource_,
         viz::SingleReleaseCallback::Create(test_data_.sw_release_callback_));
@@ -917,8 +919,7 @@
 
   // Resourceless software mode.
   {
-    std::unique_ptr<TextureLayerImpl> impl_layer =
-        TextureLayerImpl::Create(host_impl_.active_tree(), 1);
+    std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer();
     impl_layer->SetTransferableResource(
         test_data_.resource1_,
         viz::SingleReleaseCallback::Create(test_data_.release_callback1_));
@@ -990,8 +991,7 @@
 
 TEST_F(TextureLayerImplWithResourceTest,
        TestDestructorCallbackOnCreatedResource) {
-  std::unique_ptr<TextureLayerImpl> impl_layer;
-  impl_layer = TextureLayerImpl::Create(host_impl_.active_tree(), 1);
+  std::unique_ptr<TextureLayerImpl> impl_layer = CreateTextureLayer();
   ASSERT_TRUE(impl_layer);
 
   EXPECT_CALL(test_data_.mock_callback_,
diff --git a/cc/layers/video_layer_impl.cc b/cc/layers/video_layer_impl.cc
index 3e41407..d4a18b06 100644
--- a/cc/layers/video_layer_impl.cc
+++ b/cc/layers/video_layer_impl.cc
@@ -82,6 +82,9 @@
   if (draw_mode == DRAW_MODE_RESOURCELESS_SOFTWARE)
     return false;
 
+  if (!LayerImpl::WillDraw(draw_mode, resource_provider))
+    return false;
+
   // Explicitly acquire and release the provider mutex so it can be held from
   // WillDraw to DidDraw. Since the compositor thread is in the middle of
   // drawing, the layer will not be destroyed before DidDraw is called.
@@ -99,9 +102,6 @@
     return false;
   }
 
-  if (!LayerImpl::WillDraw(draw_mode, resource_provider))
-    return false;
-
   if (!updater_) {
     const LayerTreeSettings& settings = layer_tree_impl()->settings();
     updater_ = std::make_unique<media::VideoResourceUpdater>(
diff --git a/cc/layers/video_layer_impl_unittest.cc b/cc/layers/video_layer_impl_unittest.cc
index fc67628..56d9170 100644
--- a/cc/layers/video_layer_impl_unittest.cc
+++ b/cc/layers/video_layer_impl_unittest.cc
@@ -50,6 +50,7 @@
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
+  video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size));
 
   impl.CalcDrawProps(viewport_size);
 
@@ -313,6 +314,7 @@
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
+  video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size));
   impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   gfx::Rect occluded;
@@ -350,6 +352,7 @@
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
+  video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size));
   impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   gfx::Rect occluded;
@@ -393,6 +396,7 @@
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
+  video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size));
   impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   gfx::Rect occluded;
@@ -436,6 +440,7 @@
       impl.AddChildToRoot<VideoLayerImpl>(&provider, media::VIDEO_ROTATION_0);
   video_layer_impl->SetBounds(layer_size);
   video_layer_impl->SetDrawsContent(true);
+  video_layer_impl->set_visible_layer_rect(gfx::Rect(layer_size));
   impl.host_impl()->active_tree()->BuildLayerListAndPropertyTreesForTesting();
 
   gfx::Rect occluded;
diff --git a/cc/test/layer_test_common.cc b/cc/test/layer_test_common.cc
index 58a40c0..a657f266 100644
--- a/cc/test/layer_test_common.cc
+++ b/cc/test/layer_test_common.cc
@@ -191,9 +191,10 @@
                       SimpleEnclosedRegion(occluded), SimpleEnclosedRegion());
   layer_impl->draw_properties().occlusion_in_content_space = occlusion;
 
-  layer_impl->WillDraw(DRAW_MODE_HARDWARE, resource_provider());
-  layer_impl->AppendQuads(render_pass_.get(), &data);
-  layer_impl->DidDraw(resource_provider());
+  if (layer_impl->WillDraw(DRAW_MODE_HARDWARE, resource_provider())) {
+    layer_impl->AppendQuads(render_pass_.get(), &data);
+    layer_impl->DidDraw(resource_provider());
+  }
 }
 
 void LayerTestCommon::LayerImplTest::AppendQuadsForPassWithOcclusion(
diff --git a/cc/test/pixel_test.cc b/cc/test/pixel_test.cc
index 434e417..0597570 100644
--- a/cc/test/pixel_test.cc
+++ b/cc/test/pixel_test.cc
@@ -245,7 +245,8 @@
   gpu_service_ = std::make_unique<viz::GpuServiceImpl>(
       gpu::GPUInfo(), nullptr /* watchdog_thread */, io_thread_->task_runner(),
       gpu::GpuFeatureInfo(), gpu::GpuPreferences(), gpu::GPUInfo(),
-      gpu::GpuFeatureInfo(), base::DoNothing() /* exit_callback */);
+      gpu::GpuFeatureInfo(), nullptr /* vulkan_implementation */,
+      base::DoNothing() /* exit_callback */);
 
   // Uses a null gpu_host here, because we don't want to receive any message.
   std::unique_ptr<viz::mojom::GpuHost> gpu_host;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index a329a48..308f811 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1097,13 +1097,9 @@
         render_surface->AppendQuads(draw_mode, target_render_pass,
                                     &append_quads_data);
       }
-    } else if (it.state() == EffectTreeLayerListIterator::State::LAYER &&
-               !it.current_layer()->visible_layer_rect().IsEmpty()) {
+    } else if (it.state() == EffectTreeLayerListIterator::State::LAYER) {
       LayerImpl* layer = it.current_layer();
-      bool occluded =
-          layer->draw_properties().occlusion_in_content_space.IsOccluded(
-              layer->visible_layer_rect());
-      if (!occluded && layer->WillDraw(draw_mode, &resource_provider_)) {
+      if (layer->WillDraw(draw_mode, &resource_provider_)) {
         DCHECK_EQ(active_tree_.get(), layer->layer_tree_impl());
 
         frame->will_draw_layers.push_back(layer);
@@ -2205,6 +2201,8 @@
 }
 
 void LayerTreeHostImpl::DidDrawAllLayers(const FrameData& frame) {
+  // TODO(lethalantidote): LayerImpl::DidDraw can be removed when
+  // VideoLayerImpl is removed.
   for (size_t i = 0; i < frame.will_draw_layers.size(); ++i)
     frame.will_draw_layers[i]->DidDraw(&resource_provider_);
 
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 9cd7cf6..f996663 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -4638,10 +4638,12 @@
 
   bool WillDraw(DrawMode draw_mode,
                 viz::ClientResourceProvider* provider) override {
-    will_draw_called_ = true;
+    if (!LayerImpl::WillDraw(draw_mode, provider))
+      return false;
     if (will_draw_returns_false_)
       return false;
-    return LayerImpl::WillDraw(draw_mode, provider);
+    will_draw_returned_true_ = true;
+    return true;
   }
 
   void AppendQuads(viz::RenderPass* render_pass,
@@ -4655,14 +4657,14 @@
     LayerImpl::DidDraw(provider);
   }
 
-  bool will_draw_called() const { return will_draw_called_; }
+  bool will_draw_returned_true() const { return will_draw_returned_true_; }
   bool append_quads_called() const { return append_quads_called_; }
   bool did_draw_called() const { return did_draw_called_; }
 
   void set_will_draw_returns_false() { will_draw_returns_false_ = true; }
 
   void ClearDidDrawCheck() {
-    will_draw_called_ = false;
+    will_draw_returned_true_ = false;
     append_quads_called_ = false;
     did_draw_called_ = false;
   }
@@ -4676,7 +4678,7 @@
   DidDrawCheckLayer(LayerTreeImpl* tree_impl, int id)
       : LayerImpl(tree_impl, id),
         will_draw_returns_false_(false),
-        will_draw_called_(false),
+        will_draw_returned_true_(false),
         append_quads_called_(false),
         did_draw_called_(false) {
     SetBounds(gfx::Size(10, 10));
@@ -4686,7 +4688,7 @@
 
  private:
   bool will_draw_returns_false_;
-  bool will_draw_called_;
+  bool will_draw_returned_true_;
   bool append_quads_called_;
   bool did_draw_called_;
 };
@@ -4786,7 +4788,7 @@
     host_impl_->DrawLayers(&frame);
     host_impl_->DidDrawAllLayers(frame);
 
-    EXPECT_TRUE(layer->will_draw_called());
+    EXPECT_TRUE(layer->will_draw_returned_true());
     EXPECT_TRUE(layer->append_quads_called());
     EXPECT_TRUE(layer->did_draw_called());
   }
@@ -4803,7 +4805,7 @@
     host_impl_->DrawLayers(&frame);
     host_impl_->DidDrawAllLayers(frame);
 
-    EXPECT_TRUE(layer->will_draw_called());
+    EXPECT_FALSE(layer->will_draw_returned_true());
     EXPECT_FALSE(layer->append_quads_called());
     EXPECT_FALSE(layer->did_draw_called());
   }
@@ -4829,14 +4831,14 @@
 
   TestFrameData frame;
 
-  EXPECT_FALSE(layer->will_draw_called());
+  EXPECT_FALSE(layer->will_draw_returned_true());
   EXPECT_FALSE(layer->did_draw_called());
 
   EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame));
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
 
-  EXPECT_FALSE(layer->will_draw_called());
+  EXPECT_FALSE(layer->will_draw_returned_true());
   EXPECT_FALSE(layer->did_draw_called());
 
   EXPECT_TRUE(layer->visible_layer_rect().IsEmpty());
@@ -4846,14 +4848,14 @@
   layer->NoteLayerPropertyChanged();
   host_impl_->active_tree()->BuildPropertyTreesForTesting();
 
-  EXPECT_FALSE(layer->will_draw_called());
+  EXPECT_FALSE(layer->will_draw_returned_true());
   EXPECT_FALSE(layer->did_draw_called());
 
   EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame));
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
 
-  EXPECT_TRUE(layer->will_draw_called());
+  EXPECT_TRUE(layer->will_draw_returned_true());
   EXPECT_TRUE(layer->did_draw_called());
 
   EXPECT_FALSE(layer->visible_layer_rect().IsEmpty());
@@ -4886,18 +4888,18 @@
 
   TestFrameData frame;
 
-  EXPECT_FALSE(occluded_layer->will_draw_called());
+  EXPECT_FALSE(occluded_layer->will_draw_returned_true());
   EXPECT_FALSE(occluded_layer->did_draw_called());
-  EXPECT_FALSE(top_layer->will_draw_called());
+  EXPECT_FALSE(top_layer->will_draw_returned_true());
   EXPECT_FALSE(top_layer->did_draw_called());
 
   EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame));
   host_impl_->DrawLayers(&frame);
   host_impl_->DidDrawAllLayers(frame);
 
-  EXPECT_FALSE(occluded_layer->will_draw_called());
+  EXPECT_FALSE(occluded_layer->will_draw_returned_true());
   EXPECT_FALSE(occluded_layer->did_draw_called());
-  EXPECT_TRUE(top_layer->will_draw_called());
+  EXPECT_TRUE(top_layer->will_draw_returned_true());
   EXPECT_TRUE(top_layer->did_draw_called());
 }
 
diff --git a/chrome/android/java/res/layout/icon_row_menu_footer.xml b/chrome/android/java/res/layout/icon_row_menu_footer.xml
new file mode 100644
index 0000000..2c7f986
--- /dev/null
+++ b/chrome/android/java/res/layout/icon_row_menu_footer.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file.
+-->
+<org.chromium.chrome.browser.appmenu.AppMenuIconRowFooter
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+    <View style="@style/Divider" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:orientation="horizontal"
+        android:id="@+id/menu_items" >
+
+        <org.chromium.chrome.browser.widget.TintedImageButton
+            android:id="@+id/forward_menu_id"
+            style="@style/OverflowMenuButton"
+            android:src="@drawable/btn_forward"
+            android:contentDescription="@string/accessibility_menu_forward" />
+
+        <org.chromium.chrome.browser.widget.TintedImageButton
+            android:id="@+id/bookmark_this_page_id"
+            style="@style/OverflowMenuButton"
+            android:src="@drawable/btn_star"
+            android:contentDescription="@string/accessibility_menu_bookmark" />
+
+        <org.chromium.chrome.browser.widget.TintedImageButton
+            android:id="@+id/offline_page_id"
+            style="@style/OverflowMenuButton"
+            android:src="@drawable/ic_file_download_white_24dp"
+            android:contentDescription="@string/download_page" />
+
+        <org.chromium.chrome.browser.widget.TintedImageButton
+            android:id="@+id/info_menu_id"
+            style="@style/OverflowMenuButton"
+            android:src="@drawable/btn_info"
+            android:contentDescription="@string/accessibility_menu_info" />
+
+        <org.chromium.chrome.browser.widget.TintedImageButton
+            android:id="@+id/reload_menu_id"
+            style="@style/OverflowMenuButton"
+            android:src="@drawable/btn_reload_stop"
+            android:contentDescription="@string/accessibility_btn_refresh" />
+    </LinearLayout>
+</org.chromium.chrome.browser.appmenu.AppMenuIconRowFooter>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index ed85170..dafacd8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -214,6 +214,7 @@
     public static final String GENERIC_SENSOR_EXTRA_CLASSES = "GenericSensorExtraClasses";
     public static final String HANDLE_MEDIA_INTENTS = "HandleMediaIntents";
     public static final String HOME_PAGE_BUTTON_FORCE_ENABLED = "HomePageButtonForceEnabled";
+    public static final String HOMEPAGE_TILE = "HomepageTile";
     public static final String HORIZONTAL_TAB_SWITCHER_ANDROID = "HorizontalTabSwitcherAndroid";
     public static final String INTEREST_FEED_CONTENT_SUGGESTIONS = "InterestFeedContentSuggestions";
     public static final String LANGUAGES_PREFERENCE = "LanguagesPreference";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index ef9e844..da6a225 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -53,6 +53,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate;
 import org.chromium.chrome.browser.IntentHandler.TabOpenType;
+import org.chromium.chrome.browser.appmenu.AppMenu;
+import org.chromium.chrome.browser.appmenu.AppMenuIconRowFooter;
 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
 import org.chromium.chrome.browser.browseractions.BrowserActionsService;
@@ -1512,16 +1514,28 @@
 
             @Override
             public int getFooterResourceId() {
+                if (FeatureUtilities.isBottomToolbarEnabled()) {
+                    return this.shouldShowPageMenu() ? R.layout.icon_row_menu_footer : 0;
+                }
                 return showDataSaverFooter() ? R.layout.data_reduction_main_menu_item : 0;
             }
 
             @Override
+            public void onFooterViewInflated(AppMenu menu, View view) {
+                if (view instanceof AppMenuIconRowFooter) {
+                    ((AppMenuIconRowFooter) view)
+                            .initialize(ChromeTabbedActivity.this, menu, mBookmarkBridge);
+                }
+            }
+
+            @Override
             public View getHeaderView() {
                 return null;
             }
 
             @Override
             public boolean shouldShowFooter(int maxMenuHeight) {
+                if (FeatureUtilities.isBottomToolbarEnabled()) return true;
                 if (showDataSaverFooter()) {
                     return canShowDataReductionItem(maxMenuHeight);
                 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
index 18a6181..af12b47a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
@@ -529,6 +529,8 @@
             ViewHighlighter.turnOnHighlight(viewToHighlight, viewToHighlight != mFooterView);
         }
 
+        if (mHandler != null) mHandler.onFooterInflated(mFooterView);
+
         return mFooterView.getMeasuredHeight();
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java
index 60de9ef..55df0064 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java
@@ -233,4 +233,12 @@
             mObservers.get(i).onMenuVisibilityChanged(isVisible);
         }
     }
+
+    /**
+     * A notification that the footer view has been inflated.
+     * @param view The inflated view.
+     */
+    void onFooterInflated(View view) {
+        if (mDelegate != null) mDelegate.onFooterViewInflated(mAppMenu, view);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuIconRowFooter.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuIconRowFooter.java
new file mode 100644
index 0000000..72be6c58
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuIconRowFooter.java
@@ -0,0 +1,113 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.appmenu;
+
+import android.content.Context;
+import android.support.v7.content.res.AppCompatResources;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.bookmarks.BookmarkBridge;
+import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.widget.TintedImageButton;
+
+/**
+ * A {@link LinearLayout} that displays a horizontal row of icons for page actions.
+ */
+public class AppMenuIconRowFooter extends LinearLayout implements View.OnClickListener {
+    private ChromeActivity mActivity;
+    private AppMenu mAppMenu;
+
+    private TintedImageButton mForwardButton;
+    private TintedImageButton mBookmarkButton;
+    private TintedImageButton mDownloadButton;
+    private TintedImageButton mPageInfoButton;
+    private TintedImageButton mReloadButton;
+
+    public AppMenuIconRowFooter(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mForwardButton = (TintedImageButton) findViewById(R.id.forward_menu_id);
+        mForwardButton.setOnClickListener(this);
+
+        mBookmarkButton = (TintedImageButton) findViewById(R.id.bookmark_this_page_id);
+        mBookmarkButton.setOnClickListener(this);
+
+        mDownloadButton = (TintedImageButton) findViewById(R.id.offline_page_id);
+        mDownloadButton.setOnClickListener(this);
+
+        mPageInfoButton = (TintedImageButton) findViewById(R.id.info_menu_id);
+        mPageInfoButton.setOnClickListener(this);
+
+        mReloadButton = (TintedImageButton) findViewById(R.id.reload_menu_id);
+        mReloadButton.setOnClickListener(this);
+    }
+
+    /**
+     * Initializes the icons, setting enabled state, drawables, and content descriptions.
+     * @param activity The {@link ChromeActivity} displaying the menu.
+     * @param appMenu The {@link AppMenu} that contains the icon row.
+     * @param bookmarkBridge The {@link BookmarkBridge} used to retrieve information about
+     *                       bookmarks.
+     */
+    public void initialize(
+            ChromeActivity activity, AppMenu appMenu, BookmarkBridge bookmarkBridge) {
+        mActivity = activity;
+        mAppMenu = appMenu;
+        Tab currentTab = mActivity.getActivityTab();
+
+        mForwardButton.setEnabled(currentTab.canGoForward());
+
+        updateBookmarkMenuItem(bookmarkBridge, currentTab);
+
+        mDownloadButton.setEnabled(DownloadUtils.isAllowedToDownloadPage(currentTab));
+
+        mReloadButton.setImageResource(R.drawable.btn_reload_stop);
+        loadingStateChanged(currentTab.isLoading());
+    }
+
+    @Override
+    public void onClick(View v) {
+        mActivity.onMenuOrKeyboardAction(v.getId(), true);
+        mAppMenu.dismiss();
+    }
+
+    /**
+     * Called when the current tab's load state  has changed.
+     * @param isLoading Whether the tab is currently loading.
+     */
+    public void loadingStateChanged(boolean isLoading) {
+        mReloadButton.getDrawable().setLevel(isLoading
+                        ? getResources().getInteger(R.integer.reload_button_level_stop)
+                        : getResources().getInteger(R.integer.reload_button_level_reload));
+        mReloadButton.setContentDescription(isLoading
+                        ? mActivity.getString(R.string.accessibility_btn_stop_loading)
+                        : mActivity.getString(R.string.accessibility_btn_refresh));
+    }
+
+    private void updateBookmarkMenuItem(BookmarkBridge bookmarkBridge, Tab currentTab) {
+        mBookmarkButton.setEnabled(bookmarkBridge.isEditBookmarksEnabled());
+
+        if (currentTab.getBookmarkId() != Tab.INVALID_BOOKMARK_ID) {
+            mBookmarkButton.setImageResource(R.drawable.btn_star_filled);
+            mBookmarkButton.setContentDescription(mActivity.getString(R.string.edit_bookmark));
+            mBookmarkButton.setTint(
+                    AppCompatResources.getColorStateList(getContext(), R.color.blue_mode_tint));
+        } else {
+            mBookmarkButton.setImageResource(R.drawable.btn_star);
+            mBookmarkButton.setContentDescription(
+                    mActivity.getString(R.string.accessibility_menu_bookmark));
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
index ff5958d..114d0b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java
@@ -120,6 +120,10 @@
                             < DeviceFormFactor.getMinimumTabletWidthPx(
                                       mActivity.getWindowAndroid().getDisplay());
 
+            boolean bottomToolbarEnabled = mActivity.getToolbarManager() != null
+                    && mActivity.getToolbarManager().getBottomToolbarCoordinator() != null;
+            shouldShowIconRow &= !bottomToolbarEnabled;
+
             // Update the icon row items (shown in narrow form factors).
             menu.findItem(R.id.icon_row_menu_id).setVisible(shouldShowIconRow);
             if (shouldShowIconRow) {
@@ -412,4 +416,11 @@
                         ? mActivity.getString(R.string.menu_request_desktop_site_on)
                         : mActivity.getString(R.string.menu_request_desktop_site_off));
     }
+
+    /**
+     * A notification that the footer view has finished inflating.
+     * @param view The view that was inflated.
+     * @param appMenu The menu the view is inside of.
+     */
+    public void onFooterViewInflated(AppMenu appMenu, View view) {}
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index 3ccf292..1efac69 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -8,6 +8,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
@@ -117,6 +118,7 @@
     // Cache objects that should not be created frequently.
     private final RectF mCacheViewport = new RectF();
     private final Rect mCacheRect = new Rect();
+    private final Point mCachePoint = new Point();
 
     // If we've drawn at least one frame.
     private boolean mHasDrawnOnce;
@@ -213,7 +215,8 @@
                 // ViewAndroid that stores the size.
                 View view = getContentView();
                 if (view != null) {
-                    setSize(getWebContents(), view, getWidthForViewport(), getHeightForViewport());
+                    Point viewportSize = getViewportSize();
+                    setSize(getWebContents(), view, viewportSize.x, viewportSize.y);
                 }
                 onViewportChanged();
 
@@ -243,7 +246,7 @@
         handleSystemUiVisibilityChange();
     }
 
-    private int getWidthForViewport() {
+    private Point getViewportSize() {
         // When in fullscreen mode, the window does not get resized when showing the onscreen
         // keyboard[1].  To work around this, we monitor the visible display frame to mimic the
         // resize state to ensure the web contents has the correct width and height.
@@ -255,26 +258,18 @@
         // contents.
         //
         // [1] - https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#FLAG_FULLSCREEN
-        if (mShowingFullscreenNonVideoContent) {
+        if (mShowingFullscreenNonVideoContent && UiUtils.isKeyboardShowing(getContext(), this)) {
             getWindowVisibleDisplayFrame(mCacheRect);
 
             // On certain devices, getWindowVisibleDisplayFrame is larger than the screen size, so
             // this ensures we never draw beyond the underlying dimensions of the view.
             // https://crbug.com/854109
-            return Math.min(mCacheRect.width(), getWidth());
+            mCachePoint.set(Math.min(mCacheRect.width(), getWidth()),
+                    Math.min(mCacheRect.height(), getHeight()));
         } else {
-            return getWidth();
+            mCachePoint.set(getWidth(), getHeight());
         }
-    }
-
-    private int getHeightForViewport() {
-        // See comment in getWidthForViewport() for an explainer of why this is done.
-        if (mShowingFullscreenNonVideoContent) {
-            getWindowVisibleDisplayFrame(mCacheRect);
-            return Math.min(mCacheRect.height(), getHeight());
-        } else {
-            return getHeight();
-        }
+        return mCachePoint;
     }
 
     private void handleSystemUiVisibilityChange() {
@@ -313,8 +308,8 @@
             mSystemUiFullscreenResizeRunnable = () -> {
                 View contentView = getContentView();
                 if (contentView != null) {
-                    setSize(getWebContents(), contentView, getWidthForViewport(),
-                            getHeightForViewport());
+                    Point viewportSize = getViewportSize();
+                    setSize(getWebContents(), contentView, viewportSize.x, viewportSize.y);
                 }
                 onViewportChanged();
             };
@@ -557,13 +552,12 @@
         super.onSizeChanged(w, h, oldw, oldh);
         if (mTabModelSelector == null) return;
 
-        w = getWidthForViewport();
-        h = getHeightForViewport();
+        Point viewportSize = getViewportSize();
         for (TabModel tabModel : mTabModelSelector.getModels()) {
             for (int i = 0; i < tabModel.getCount(); ++i) {
                 Tab tab = tabModel.getTabAt(i);
                 if (tab == null) continue;
-                setSize(tab.getWebContents(), tab.getContentView(), w, h);
+                setSize(tab.getWebContents(), tab.getContentView(), viewportSize.x, viewportSize.y);
             }
         }
     }
@@ -646,8 +640,9 @@
     public void onBottomControlsHeightChanged(int bottomControlsHeight) {
         if (mTabVisible == null) return;
         mTabVisible.setBottomControlsHeight(bottomControlsHeight);
-        setSize(mTabVisible.getWebContents(), mTabVisible.getContentView(), getWidthForViewport(),
-                getHeightForViewport());
+        Point viewportSize = getViewportSize();
+        setSize(mTabVisible.getWebContents(), mTabVisible.getContentView(), viewportSize.x,
+                viewportSize.y);
     }
 
     @Override
@@ -660,7 +655,8 @@
     @Override
     public void onUpdateViewportSize() {
         // Reflect the changes that may have happened in in view/control size.
-        setSize(getWebContents(), getContentView(), getWidthForViewport(), getHeightForViewport());
+        Point viewportSize = getViewportSize();
+        setSize(getWebContents(), getContentView(), viewportSize.x, viewportSize.y);
     }
 
     /**
@@ -714,7 +710,8 @@
 
     @Override
     public void getWindowViewport(RectF outRect) {
-        outRect.set(0, 0, getWidthForViewport(), getHeightForViewport());
+        Point viewportSize = getViewportSize();
+        outRect.set(0, 0, viewportSize.x, viewportSize.y);
     }
 
     @Override
@@ -1067,8 +1064,9 @@
         // size than the ContentViewCore it might think a future size update is a NOOP and not call
         // onSizeChanged() on the ContentViewCore.
         if (isAttachedToWindow(view)) return;
-        int width = getWidthForViewport();
-        int height = getHeightForViewport();
+        Point viewportSize = getViewportSize();
+        int width = viewportSize.x;
+        int height = viewportSize.y;
         view.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
         view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
index 1e67eeb..95c03b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/ToolbarSwipeLayout.java
@@ -312,22 +312,22 @@
             mLeftTab.setX(leftX);
             needUpdate = mLeftTab.updateSnap(dt) || needUpdate;
             if (mLeftBottomToolbarSceneLayer != null) {
-                mLeftBottomToolbarSceneLayer.setIsVisibile(true);
+                mLeftBottomToolbarSceneLayer.setIsVisible(true);
                 mLeftBottomToolbarSceneLayer.setXOffset((int) (mLeftTab.getX() * mDpToPx));
             }
         } else if (mLeftBottomToolbarSceneLayer != null) {
-            mLeftBottomToolbarSceneLayer.setIsVisibile(false);
+            mLeftBottomToolbarSceneLayer.setIsVisible(false);
         }
 
         if (mRightTab != null) {
             mRightTab.setX(rightX);
             needUpdate = mRightTab.updateSnap(dt) || needUpdate;
             if (mRightBottomToolbarSceneLayer != null) {
-                mRightBottomToolbarSceneLayer.setIsVisibile(true);
+                mRightBottomToolbarSceneLayer.setIsVisible(true);
                 mRightBottomToolbarSceneLayer.setXOffset((int) (mRightTab.getX() * mDpToPx));
             }
         } else if (mRightBottomToolbarSceneLayer != null) {
-            mRightBottomToolbarSceneLayer.setIsVisibile(false);
+            mRightBottomToolbarSceneLayer.setIsVisible(false);
         }
 
         if (needUpdate) requestUpdate();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
index 07a1db91..709d7a9c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ScrollingBottomViewSceneLayer.java
@@ -40,7 +40,7 @@
     private int mCurrentXOffsetPx;
 
     /** Whether the {@link SceneLayer}is visible. */
-    private boolean mIsVisibile;
+    private boolean mIsVisible;
 
     /** The {@link ViewResourceFrameLayout} that this scene layer represents. */
     private ViewResourceFrameLayout mBottomView;
@@ -55,7 +55,7 @@
         mBottomView = bottomView;
         mResourceId = mBottomView.getId();
         mTopShadowHeightPx = topShadowHeightPx;
-        mIsVisibile = true;
+        mIsVisible = true;
     }
 
     /**
@@ -86,8 +86,8 @@
     /**
      * @param visible Whether this {@link SceneLayer} is visible.
      */
-    public void setIsVisibile(boolean visible) {
-        mIsVisibile = visible;
+    public void setIsVisible(boolean visible) {
+        mIsVisible = visible;
     }
 
     @Override
@@ -119,7 +119,7 @@
     @Override
     public boolean isSceneOverlayTreeShowing() {
         // If the offset is greater than the toolbar's height, don't draw the layer.
-        return mIsVisibile && mCurrentYOffsetPx < mBottomView.getHeight() - mTopShadowHeightPx;
+        return mIsVisible && mCurrentYOffsetPx < mBottomView.getHeight() - mTopShadowHeightPx;
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java
index c85aa43..a25c183 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLogger.java
@@ -4,57 +4,74 @@
 
 package org.chromium.chrome.browser.contextualsearch;
 
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 
 import org.chromium.content_public.browser.WebContents;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * An interface for logging to UMA via Ranker.
  */
 public interface ContextualSearchRankerLogger {
-    // TODO(donnd): consider changing this enum to an IntDef.
     // NOTE: this list needs to be kept in sync with the white list in
     // predictor_config_definitions.cc, the names list in ContextualSearchRankerLoggerImpl.java
     // and with ukm.xml!
-    enum Feature {
-        UNKNOWN,
+    @IntDef({Feature.UNKNOWN, Feature.OUTCOME_WAS_PANEL_OPENED,
+            Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, Feature.OUTCOME_WAS_QUICK_ANSWER_SEEN,
+            Feature.OUTCOME_WAS_CARDS_DATA_SHOWN, Feature.DURATION_AFTER_SCROLL_MS,
+            Feature.SCREEN_TOP_DPS, Feature.WAS_SCREEN_BOTTOM,
+            Feature.PREVIOUS_WEEK_IMPRESSIONS_COUNT, Feature.PREVIOUS_WEEK_CTR_PERCENT,
+            Feature.PREVIOUS_28DAY_IMPRESSIONS_COUNT, Feature.PREVIOUS_28DAY_CTR_PERCENT,
+            Feature.DID_OPT_IN, Feature.IS_SHORT_WORD, Feature.IS_LONG_WORD, Feature.IS_WORD_EDGE,
+            Feature.IS_ENTITY, Feature.TAP_DURATION_MS, Feature.FONT_SIZE,
+            Feature.IS_SECOND_TAP_OVERRIDE, Feature.IS_HTTP, Feature.IS_ENTITY_ELIGIBLE,
+            Feature.IS_LANGUAGE_MISMATCH, Feature.PORTION_OF_ELEMENT, Feature.TAP_COUNT,
+            Feature.OPEN_COUNT, Feature.QUICK_ANSWER_COUNT, Feature.ENTITY_IMPRESSIONS_COUNT,
+            Feature.ENTITY_OPENS_COUNT, Feature.QUICK_ACTION_IMPRESSIONS_COUNT,
+            Feature.QUICK_ACTIONS_TAKEN_COUNT, Feature.QUICK_ACTIONS_IGNORED_COUNT})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Feature {
+        int UNKNOWN = 0;
         // Outcome labels:
-        OUTCOME_WAS_PANEL_OPENED,
-        OUTCOME_WAS_QUICK_ACTION_CLICKED,
-        OUTCOME_WAS_QUICK_ANSWER_SEEN,
-        OUTCOME_WAS_CARDS_DATA_SHOWN, // a UKM CS v2 label.
-        // Features:
-        DURATION_AFTER_SCROLL_MS,
-        SCREEN_TOP_DPS,
-        WAS_SCREEN_BOTTOM,
+        int OUTCOME_WAS_PANEL_OPENED = 1;
+        int OUTCOME_WAS_QUICK_ACTION_CLICKED = 2;
+        int OUTCOME_WAS_QUICK_ANSWER_SEEN = 3;
+        int OUTCOME_WAS_CARDS_DATA_SHOWN = 4; // a UKM CS v2 label.
+                                              // Features:
+        int DURATION_AFTER_SCROLL_MS = 5;
+        int SCREEN_TOP_DPS = 6;
+        int WAS_SCREEN_BOTTOM = 7;
         // User usage features:
-        PREVIOUS_WEEK_IMPRESSIONS_COUNT,
-        PREVIOUS_WEEK_CTR_PERCENT,
-        PREVIOUS_28DAY_IMPRESSIONS_COUNT,
-        PREVIOUS_28DAY_CTR_PERCENT,
+        int PREVIOUS_WEEK_IMPRESSIONS_COUNT = 8;
+        int PREVIOUS_WEEK_CTR_PERCENT = 9;
+        int PREVIOUS_28DAY_IMPRESSIONS_COUNT = 10;
+        int PREVIOUS_28DAY_CTR_PERCENT = 11;
         // UKM CS v2 features (see go/ukm-cs-2).
-        DID_OPT_IN,
-        IS_SHORT_WORD,
-        IS_LONG_WORD,
-        IS_WORD_EDGE,
-        IS_ENTITY,
-        TAP_DURATION_MS,
+        int DID_OPT_IN = 12;
+        int IS_SHORT_WORD = 13;
+        int IS_LONG_WORD = 14;
+        int IS_WORD_EDGE = 15;
+        int IS_ENTITY = 16;
+        int TAP_DURATION_MS = 17;
         // UKM CS v3 features (see go/ukm-cs-3).
-        FONT_SIZE,
-        IS_SECOND_TAP_OVERRIDE,
-        IS_HTTP,
-        IS_ENTITY_ELIGIBLE,
-        IS_LANGUAGE_MISMATCH,
-        PORTION_OF_ELEMENT,
+        int FONT_SIZE = 18;
+        int IS_SECOND_TAP_OVERRIDE = 19;
+        int IS_HTTP = 20;
+        int IS_ENTITY_ELIGIBLE = 21;
+        int IS_LANGUAGE_MISMATCH = 22;
+        int PORTION_OF_ELEMENT = 23;
         // UKM CS v4 features (see go/ukm-cs-4).
-        TAP_COUNT,
-        OPEN_COUNT,
-        QUICK_ANSWER_COUNT,
-        ENTITY_IMPRESSIONS_COUNT,
-        ENTITY_OPENS_COUNT,
-        QUICK_ACTION_IMPRESSIONS_COUNT,
-        QUICK_ACTIONS_TAKEN_COUNT,
-        QUICK_ACTIONS_IGNORED_COUNT,
+        int TAP_COUNT = 24;
+        int OPEN_COUNT = 25;
+        int QUICK_ANSWER_COUNT = 26;
+        int ENTITY_IMPRESSIONS_COUNT = 27;
+        int ENTITY_OPENS_COUNT = 28;
+        int QUICK_ACTION_IMPRESSIONS_COUNT = 29;
+        int QUICK_ACTIONS_TAKEN_COUNT = 30;
+        int QUICK_ACTIONS_IGNORED_COUNT = 31;
     }
 
     /**
@@ -69,7 +86,7 @@
      * @param feature The feature to log.
      * @param value The value to log, which is associated with the given key.
      */
-    void logFeature(Feature feature, Object value);
+    void logFeature(@Feature int feature, Object value);
 
     /**
      * Returns whether or not AssistRanker query is enabled.
@@ -81,7 +98,7 @@
      * @param feature The feature to log.
      * @param value The outcome label value.
      */
-    void logOutcome(Feature feature, Object value);
+    void logOutcome(@Feature int feature, Object value);
 
     /**
      * Tries to run the machine intelligence model for tap suppression and returns an int that
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
index ab792cc1..200434b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchRankerLoggerImpl.java
@@ -21,13 +21,17 @@
     private static final String TAG = "ContextualSearch";
 
     // Names for all our features and labels.
-    private static final Map<Feature, String> ALL_NAMES;
+    // Integer values should contain @Feature values only.
+    private static final Map<Integer, String> ALL_NAMES;
     @VisibleForTesting
-    static final Map<Feature, String> OUTCOMES;
+    // Integer values should contain @Feature values only.
+    static final Map<Integer, String> OUTCOMES;
     @VisibleForTesting
-    static final Map<Feature, String> FEATURES;
+    // Integer values should contain @Feature values only.
+    static final Map<Integer, String> FEATURES;
     static {
-        Map<Feature, String> outcomes = new HashMap<Feature, String>();
+        // Integer values should contain @Feature values only.
+        Map<Integer, String> outcomes = new HashMap<Integer, String>();
         outcomes.put(Feature.OUTCOME_WAS_PANEL_OPENED, "OutcomeWasPanelOpened");
         outcomes.put(Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED, "OutcomeWasQuickActionClicked");
         outcomes.put(Feature.OUTCOME_WAS_QUICK_ANSWER_SEEN, "OutcomeWasQuickAnswerSeen");
@@ -37,7 +41,8 @@
 
         // NOTE: this list needs to be kept in sync with the white list in
         // predictor_config_definitions.cc and with ukm.xml!
-        Map<Feature, String> features = new HashMap<Feature, String>();
+        // Integer values should contain @Feature values only.
+        Map<Integer, String> features = new HashMap<Integer, String>();
         features.put(Feature.DURATION_AFTER_SCROLL_MS, "DurationAfterScrollMs");
         features.put(Feature.SCREEN_TOP_DPS, "ScreenTopDps");
         features.put(Feature.WAS_SCREEN_BOTTOM, "WasScreenBottom");
@@ -70,7 +75,8 @@
         features.put(Feature.QUICK_ACTIONS_IGNORED_COUNT, "QuickActionsIgnored");
         FEATURES = Collections.unmodifiableMap(features);
 
-        Map<Feature, String> allNames = new HashMap<Feature, String>();
+        // Integer values should contain @Feature values only.
+        Map<Integer, String> allNames = new HashMap<Integer, String>();
         allNames.putAll(outcomes);
         allNames.putAll(features);
         ALL_NAMES = Collections.unmodifiableMap(allNames);
@@ -94,11 +100,13 @@
             AssistRankerPrediction.UNDETERMINED;
 
     // Map that accumulates all of the Features to log for a specific user-interaction.
-    private Map<Feature, Object> mFeaturesToLog;
+    // Integer values should contain @Feature values only.
+    private Map<Integer, Object> mFeaturesToLog;
 
     // A for-testing copy of all the features to log setup so that it will survive a {@link #reset}.
-    private Map<Feature, Object> mFeaturesLoggedForTesting;
-    private Map<Feature, Object> mOutcomesLoggedForTesting;
+    // Integer values should contain @Feature values only.
+    private Map<Integer, Object> mFeaturesLoggedForTesting;
+    private Map<Integer, Object> mOutcomesLoggedForTesting;
 
     /**
      * Constructs a Ranker Logger and associated native implementation to write Contextual Search
@@ -139,7 +147,7 @@
     }
 
     @Override
-    public void logFeature(Feature feature, Object value) {
+    public void logFeature(@Feature int feature, Object value) {
         assert mIsLoggingReadyForPage : "mIsLoggingReadyForPage false.";
         assert !mHasInferenceOccurred;
         if (!isEnabled()) return;
@@ -148,7 +156,7 @@
     }
 
     @Override
-    public void logOutcome(Feature feature, Object value) {
+    public void logOutcome(@Feature int feature, Object value) {
         assert mIsLoggingReadyForPage;
         assert mHasInferenceOccurred;
         if (!isEnabled()) return;
@@ -163,11 +171,11 @@
         mHasInferenceOccurred = true;
         if (isEnabled() && mBasePageWebContents != null && mFeaturesToLog != null
                 && !mFeaturesToLog.isEmpty()) {
-            for (Map.Entry<Feature, Object> entry : mFeaturesToLog.entrySet()) {
+            for (Map.Entry<Integer, Object> entry : mFeaturesToLog.entrySet()) {
                 logObject(entry.getKey(), entry.getValue());
             }
             mFeaturesLoggedForTesting = mFeaturesToLog;
-            mFeaturesToLog = new HashMap<Feature, Object>();
+            mFeaturesToLog = new HashMap<Integer, Object>();
             mAssistRankerPrediction = nativeRunInference(mNativePointer);
             ContextualSearchUma.logRecordedFeaturesToRanker();
         }
@@ -197,7 +205,7 @@
                 assert mHasInferenceOccurred;
                 // Only the outcomes will be present, since we logged inference features at
                 // inference time.
-                for (Map.Entry<Feature, Object> entry : mFeaturesToLog.entrySet()) {
+                for (Map.Entry<Integer, Object> entry : mFeaturesToLog.entrySet()) {
                     logObject(entry.getKey(), entry.getValue());
                 }
                 mOutcomesLoggedForTesting = mFeaturesToLog;
@@ -214,8 +222,8 @@
      * @param feature The feature to log.
      * @param value The value to log.
      */
-    private void logInternal(Feature feature, Object value) {
-        if (mFeaturesToLog == null) mFeaturesToLog = new HashMap<Feature, Object>();
+    private void logInternal(@Feature int feature, Object value) {
+        if (mFeaturesToLog == null) mFeaturesToLog = new HashMap<Integer, Object>();
         mFeaturesToLog.put(feature, value);
     }
 
@@ -230,7 +238,7 @@
      * @param feature The feature to log.
      * @param value An {@link Object} value to log (must be convertible to a {@code long}).
      */
-    private void logObject(Feature feature, Object value) {
+    private void logObject(@Feature int feature, Object value) {
         if (value instanceof Boolean) {
             logToNative(feature, ((boolean) value ? 1 : 0));
         } else if (value instanceof Integer) {
@@ -240,7 +248,8 @@
         } else if (value instanceof Character) {
             logToNative(feature, Character.getNumericValue((char) value));
         } else {
-            assert false : "Could not log feature to Ranker: " + feature.toString() + " of class "
+            assert false : "Could not log feature to Ranker: " + String.valueOf(feature)
+                           + " of class "
                            + value.getClass();
         }
     }
@@ -250,7 +259,7 @@
      * @param feature The feature to log.
      * @param value The value to log.
      */
-    private void logToNative(Feature feature, long value) {
+    private void logToNative(@Feature int feature, long value) {
         String featureName = getFeatureName(feature);
         assert featureName != null : "No Name for feature " + feature;
         nativeLogLong(mNativePointer, featureName, value);
@@ -259,7 +268,7 @@
     /**
      * @return The name of the given feature.
      */
-    private String getFeatureName(Feature feature) {
+    private String getFeatureName(@Feature int feature) {
         return ALL_NAMES.get(feature);
     }
 
@@ -270,7 +279,7 @@
      */
     @VisibleForTesting
     @Nullable
-    Map<Feature, Object> getFeaturesLogged() {
+    Map<Integer, Object> getFeaturesLogged() {
         return mFeaturesLoggedForTesting;
     }
 
@@ -281,7 +290,7 @@
      */
     @VisibleForTesting
     @Nullable
-    Map<Feature, Object> getOutcomesLogged() {
+    Map<Integer, Object> getOutcomesLogged() {
         return mOutcomesLoggedForTesting;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
index cad17f4..2f98be01 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadBroadcastManager.java
@@ -338,7 +338,7 @@
         String referrer = IntentUtils.safeGetStringExtra(intent, Intent.EXTRA_REFERRER);
         DownloadManagerService.openDownloadedContent(context, downloadFilename, isSupportedMimeType,
                 isOffTheRecord, contentId.id, id, originalUrl, referrer,
-                DownloadMetrics.NOTIFICATION);
+                DownloadMetrics.DownloadOpenSource.NOTIFICATION);
     }
 
     @Nullable
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
index aaac06f..9095f646 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
@@ -24,6 +24,9 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.AppHooks;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Keep-alive foreground service for downloads.
  */
@@ -34,15 +37,13 @@
 
     private NotificationManager mNotificationManager;
 
-    @IntDef({
-            StopForegroundNotification.KILL, // Kill notification regardless of ability to detach.
-            StopForegroundNotification.DETACH_OR_PERSIST, // Try to detach, otherwise persist info.
-            StopForegroundNotification.DETACH_OR_ADJUST // Try detach, otherwise kill and relaunch.
-    })
+    @IntDef({StopForegroundNotification.KILL, StopForegroundNotification.DETACH_OR_PERSIST,
+            StopForegroundNotification.DETACH_OR_ADJUST})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface StopForegroundNotification {
-        int KILL = 0;
-        int DETACH_OR_PERSIST = 1;
-        int DETACH_OR_ADJUST = 2;
+        int KILL = 0; // Kill notification regardless of ability to detach.
+        int DETACH_OR_PERSIST = 1; // Try to detach, otherwise persist info.
+        int DETACH_OR_ADJUST = 2; // Try detach, otherwise kill and relaunch.
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java
index 0c84aab..ab07ca9d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfo.java
@@ -317,22 +317,22 @@
         }
 
         switch (DownloadFilter.fromMimeType(downloadInfo.getMimeType())) {
-            case DownloadFilter.FILTER_PAGE:
+            case DownloadFilter.Type.PAGE:
                 offlineItem.filter = OfflineItemFilter.FILTER_PAGE;
                 break;
-            case DownloadFilter.FILTER_VIDEO:
+            case DownloadFilter.Type.VIDEO:
                 offlineItem.filter = OfflineItemFilter.FILTER_VIDEO;
                 break;
-            case DownloadFilter.FILTER_AUDIO:
+            case DownloadFilter.Type.AUDIO:
                 offlineItem.filter = OfflineItemFilter.FILTER_AUDIO;
                 break;
-            case DownloadFilter.FILTER_IMAGE:
+            case DownloadFilter.Type.IMAGE:
                 offlineItem.filter = OfflineItemFilter.FILTER_IMAGE;
                 break;
-            case DownloadFilter.FILTER_DOCUMENT:
+            case DownloadFilter.Type.DOCUMENT:
                 offlineItem.filter = OfflineItemFilter.FILTER_DOCUMENT;
                 break;
-            case DownloadFilter.FILTER_OTHER:
+            case DownloadFilter.Type.OTHER:
             default:
                 offlineItem.filter = OfflineItemFilter.FILTER_OTHER;
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
index 5b7f5ba..d0a9989 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadInfoBarController.java
@@ -61,17 +61,24 @@
 
     // Values for the histogram Android.Download.InfoBar.Shown. Keep this in sync with the
     // DownloadInfoBar.ShownState enum in enums.xml.
-    private static final int UMA_INFOBAR_SHOWN_ANY_STATE = 0;
-    private static final int UMA_INFOBAR_SHOWN_ACCELERATED = 1;
-    private static final int UMA_INFOBAR_SHOWN_DOWNLOADING = 2;
-    private static final int UMA_INFOBAR_SHOWN_COMPLETE = 3;
-    private static final int UMA_INFOBAR_SHOWN_FAILED = 4;
-    private static final int UMA_INFOBAR_SHOWN_PENDING = 5;
-    private static final int UMA_INFOBAR_SHOWN_MULTIPLE_DOWNLOADING = 6;
-    private static final int UMA_INFOBAR_SHOWN_MULTIPLE_COMPLETE = 7;
-    private static final int UMA_INFOBAR_SHOWN_MULTIPLE_FAILED = 8;
-    private static final int UMA_INFOBAR_SHOWN_MULTIPLE_PENDING = 9;
-    private static final int UMA_INFOBAR_SHOWN_COUNT = 10;
+    @IntDef({UmaInfobarShown.ANY_STATE, UmaInfobarShown.ACCELERATED, UmaInfobarShown.DOWNLOADING,
+            UmaInfobarShown.COMPLETE, UmaInfobarShown.FAILED, UmaInfobarShown.PENDING,
+            UmaInfobarShown.MULTIPLE_DOWNLOADING, UmaInfobarShown.MULTIPLE_COMPLETE,
+            UmaInfobarShown.MULTIPLE_FAILED, UmaInfobarShown.MULTIPLE_PENDING})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface UmaInfobarShown {
+        int ANY_STATE = 0;
+        int ACCELERATED = 1;
+        int DOWNLOADING = 2;
+        int COMPLETE = 3;
+        int FAILED = 4;
+        int PENDING = 5;
+        int MULTIPLE_DOWNLOADING = 6;
+        int MULTIPLE_COMPLETE = 7;
+        int MULTIPLE_FAILED = 8;
+        int MULTIPLE_PENDING = 9;
+        int NUM_ENTRIES = 10;
+    }
 
     /**
      * Represents various UI states that the InfoBar cycles through.
@@ -348,14 +355,13 @@
     }
 
     private boolean isVisibleToUser(OfflineItem offlineItem) {
-        if (offlineItem.isTransient) return false;
-        if (offlineItem.isOffTheRecord != mIsIncognito) return false;
-        if (offlineItem.isSuggested) return false;
-        if (offlineItem.isDangerous) return false;
-        if (LegacyHelpers.isLegacyDownload(offlineItem.id)) {
-            if (TextUtils.isEmpty(offlineItem.filePath)) {
-                return false;
-            }
+        if (offlineItem.isTransient || offlineItem.isOffTheRecord != mIsIncognito
+                || offlineItem.isSuggested || offlineItem.isDangerous) {
+            return false;
+        }
+        if (LegacyHelpers.isLegacyDownload(offlineItem.id)
+                && TextUtils.isEmpty(offlineItem.filePath)) {
+            return false;
         }
 
         return true;
@@ -888,8 +894,8 @@
             mTrackedItems.remove(itemId);
             removeNotification(itemId);
             if (itemId != null) {
-                DownloadUtils.openItem(
-                        itemId, mIsIncognito, DownloadMetrics.DOWNLOAD_PROGRESS_INFO_BAR);
+                DownloadUtils.openItem(itemId, mIsIncognito,
+                        DownloadMetrics.DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR);
             } else {
                 DownloadManagerService.getDownloadManagerService().openDownloadsPage(getContext());
             }
@@ -914,24 +920,23 @@
         int multipleDownloadState = -1;
         if (state == DownloadInfoBarState.DOWNLOADING) {
             shownState = mEndTimerRunnable != null
-                    ? UMA_INFOBAR_SHOWN_ACCELERATED
-                    : (info.downloadCount.inProgress == 1 ? UMA_INFOBAR_SHOWN_DOWNLOADING
-                                                          : UMA_INFOBAR_SHOWN_MULTIPLE_DOWNLOADING);
+                    ? UmaInfobarShown.ACCELERATED
+                    : (info.downloadCount.inProgress == 1 ? UmaInfobarShown.DOWNLOADING
+                                                          : UmaInfobarShown.MULTIPLE_DOWNLOADING);
         } else if (state == DownloadInfoBarState.SHOW_RESULT) {
             switch (info.resultState) {
                 case OfflineItemState.COMPLETE:
                     shownState = info.downloadCount.completed == 1
-                            ? UMA_INFOBAR_SHOWN_COMPLETE
-                            : UMA_INFOBAR_SHOWN_MULTIPLE_COMPLETE;
+                            ? UmaInfobarShown.COMPLETE
+                            : UmaInfobarShown.MULTIPLE_COMPLETE;
                     break;
                 case OfflineItemState.FAILED:
-                    shownState = info.downloadCount.failed == 1 ? UMA_INFOBAR_SHOWN_FAILED
-                                                                : UMA_INFOBAR_SHOWN_MULTIPLE_FAILED;
+                    shownState = info.downloadCount.failed == 1 ? UmaInfobarShown.FAILED
+                                                                : UmaInfobarShown.MULTIPLE_FAILED;
                     break;
                 case OfflineItemState.PENDING:
-                    shownState = info.downloadCount.pending == 1
-                            ? UMA_INFOBAR_SHOWN_PENDING
-                            : UMA_INFOBAR_SHOWN_MULTIPLE_PENDING;
+                    shownState = info.downloadCount.pending == 1 ? UmaInfobarShown.PENDING
+                                                                 : UmaInfobarShown.MULTIPLE_PENDING;
                     break;
                 default:
                     assert false : "Unexpected state " + info.resultState;
@@ -942,12 +947,12 @@
         assert shownState != -1 : "Invalid state " + state;
 
         RecordHistogram.recordEnumeratedHistogram(
-                "Android.Download.InfoBar.Shown", shownState, UMA_INFOBAR_SHOWN_COUNT);
+                "Android.Download.InfoBar.Shown", shownState, UmaInfobarShown.NUM_ENTRIES);
         RecordHistogram.recordEnumeratedHistogram("Android.Download.InfoBar.Shown",
-                UMA_INFOBAR_SHOWN_ANY_STATE, UMA_INFOBAR_SHOWN_COUNT);
+                UmaInfobarShown.ANY_STATE, UmaInfobarShown.NUM_ENTRIES);
         if (multipleDownloadState != -1) {
             RecordHistogram.recordEnumeratedHistogram("Android.Download.InfoBar.Shown",
-                    multipleDownloadState, UMA_INFOBAR_SHOWN_COUNT);
+                    multipleDownloadState, UmaInfobarShown.NUM_ENTRIES);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java
index aa15cee..9cff4b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerDelegate.java
@@ -215,18 +215,19 @@
                     new DownloadManager.Query().setFilterById(mDownloadItem.getSystemDownloadId()));
             if (c == null) {
                 return new DownloadQueryResult(mDownloadItem,
-                        DownloadManagerService.DOWNLOAD_STATUS_CANCELLED, 0, 0, false, 0);
+                        DownloadManagerService.DownloadStatus.CANCELLED, 0, 0, false, 0);
             }
             long bytesDownloaded = 0;
             boolean canResolve = false;
-            int downloadStatus = DownloadManagerService.DOWNLOAD_STATUS_IN_PROGRESS;
+            @DownloadManagerService.DownloadStatus
+            int downloadStatus = DownloadManagerService.DownloadStatus.IN_PROGRESS;
             int failureReason = 0;
             long lastModifiedTime = 0;
             if (c.moveToNext()) {
                 int statusIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
                 int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
                 if (status == DownloadManager.STATUS_SUCCESSFUL) {
-                    downloadStatus = DownloadManagerService.DOWNLOAD_STATUS_COMPLETE;
+                    downloadStatus = DownloadManagerService.DownloadStatus.COMPLETE;
                     DownloadInfo.Builder builder = mDownloadItem.getDownloadInfo() == null
                             ? new DownloadInfo.Builder()
                             : DownloadInfo.Builder.fromDownloadInfo(
@@ -241,7 +242,7 @@
                                         mContext, mDownloadItem, false);
                     }
                 } else if (status == DownloadManager.STATUS_FAILED) {
-                    downloadStatus = DownloadManagerService.DOWNLOAD_STATUS_FAILED;
+                    downloadStatus = DownloadManagerService.DownloadStatus.FAILED;
                     failureReason = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_REASON));
                 }
                 lastModifiedTime =
@@ -249,7 +250,7 @@
                 bytesDownloaded =
                         c.getLong(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
             } else {
-                downloadStatus = DownloadManagerService.DOWNLOAD_STATUS_CANCELLED;
+                downloadStatus = DownloadManagerService.DownloadStatus.CANCELLED;
             }
             c.close();
             long totalTime = Math.max(0, lastModifiedTime - mDownloadItem.getStartTime());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
index e83f185..8efcdee8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadManagerService.java
@@ -14,6 +14,7 @@
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.Handler;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -55,6 +56,8 @@
 import org.chromium.ui.widget.Toast;
 
 import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -80,11 +83,16 @@
                    DownloadManagerDelegate.EnqueueDownloadRequestCallback, DownloadServiceDelegate,
                    BackendProvider.DownloadDelegate {
     // Download status.
-    public static final int DOWNLOAD_STATUS_IN_PROGRESS = 0;
-    public static final int DOWNLOAD_STATUS_COMPLETE = 1;
-    public static final int DOWNLOAD_STATUS_FAILED = 2;
-    public static final int DOWNLOAD_STATUS_CANCELLED = 3;
-    public static final int DOWNLOAD_STATUS_INTERRUPTED = 4;
+    @IntDef({DownloadStatus.IN_PROGRESS, DownloadStatus.COMPLETE, DownloadStatus.FAILED,
+            DownloadStatus.CANCELLED, DownloadStatus.INTERRUPTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DownloadStatus {
+        int IN_PROGRESS = 0;
+        int COMPLETE = 1;
+        int FAILED = 2;
+        int CANCELLED = 3;
+        int INTERRUPTED = 4;
+    }
 
     private static final String TAG = "DownloadService";
     private static final String DOWNLOAD_DIRECTORY = "Download";
@@ -102,12 +110,18 @@
             "org.chromium.chrome.browser.download.IS_DOWNLOAD_HOME_ENABLED";
 
     // Values for the histogram MobileDownloadResumptionCount.
-    private static final int UMA_DOWNLOAD_RESUMPTION_MANUAL_PAUSE = 0;
-    private static final int UMA_DOWNLOAD_RESUMPTION_BROWSER_KILLED = 1;
-    private static final int UMA_DOWNLOAD_RESUMPTION_CLICKED = 2;
-    private static final int UMA_DOWNLOAD_RESUMPTION_FAILED = 3;
-    private static final int UMA_DOWNLOAD_RESUMPTION_AUTO_STARTED = 4;
-    private static final int UMA_DOWNLOAD_RESUMPTION_COUNT = 5;
+    @IntDef({UmaDownloadResumption.MANUAL_PAUSE, UmaDownloadResumption.BROWSER_KILLED,
+            UmaDownloadResumption.CLICKED, UmaDownloadResumption.FAILED,
+            UmaDownloadResumption.AUTO_STARTED})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface UmaDownloadResumption {
+        int MANUAL_PAUSE = 0;
+        int BROWSER_KILLED = 1;
+        int CLICKED = 2;
+        int FAILED = 3;
+        int AUTO_STARTED = 4;
+        int NUM_ENTRIES = 5;
+    }
 
     // Set will be more expensive to initialize, so use an ArrayList here.
     private static final List<String> MIME_TYPES_TO_OPEN = new ArrayList<String>(Arrays.asList(
@@ -188,13 +202,14 @@
         final long mStartTimeInMillis;
         boolean mCanDownloadWhileMetered;
         DownloadItem mDownloadItem;
+        @DownloadStatus
         int mDownloadStatus;
         boolean mIsAutoResumable;
         boolean mIsUpdated;
         boolean mIsSupportedMimeType;
 
         DownloadProgress(long startTimeInMillis, boolean canDownloadWhileMetered,
-                DownloadItem downloadItem, int downloadStatus) {
+                DownloadItem downloadItem, @DownloadStatus int downloadStatus) {
             mStartTimeInMillis = startTimeInMillis;
             mCanDownloadWhileMetered = canDownloadWhileMetered;
             mDownloadItem = downloadItem;
@@ -296,9 +311,7 @@
                 hasChanges = true;
             }
         }
-        if (hasChanges) {
-            storeUmaEntries();
-        }
+        if (hasChanges) storeUmaEntries();
     }
 
     /**
@@ -319,10 +332,11 @@
 
     @Override
     public void onDownloadCompleted(final DownloadInfo downloadInfo) {
-        int status = DOWNLOAD_STATUS_COMPLETE;
+        @DownloadStatus
+        int status = DownloadStatus.COMPLETE;
         String mimeType = downloadInfo.getMimeType();
         if (downloadInfo.getBytesReceived() == 0) {
-            status = DOWNLOAD_STATUS_FAILED;
+            status = DownloadStatus.FAILED;
         } else {
             String origMimeType = mimeType;
             if (TextUtils.isEmpty(origMimeType)) origMimeType = UNKNOWN_MIME_TYPE;
@@ -342,7 +356,7 @@
         if (downloadInfo.isPaused()) {
             removeAutoResumableDownload(item.getId());
         }
-        updateDownloadProgress(item, DOWNLOAD_STATUS_IN_PROGRESS);
+        updateDownloadProgress(item, DownloadStatus.IN_PROGRESS);
         scheduleUpdateIfNeeded();
     }
 
@@ -350,15 +364,16 @@
     public void onDownloadCancelled(final DownloadInfo downloadInfo) {
         DownloadItem item = new DownloadItem(false, downloadInfo);
         removeAutoResumableDownload(item.getId());
-        updateDownloadProgress(new DownloadItem(false, downloadInfo), DOWNLOAD_STATUS_CANCELLED);
+        updateDownloadProgress(new DownloadItem(false, downloadInfo), DownloadStatus.CANCELLED);
     }
 
     @Override
     public void onDownloadInterrupted(final DownloadInfo downloadInfo, boolean isAutoResumable) {
-        int status = DOWNLOAD_STATUS_INTERRUPTED;
+        @DownloadStatus
+        int status = DownloadStatus.INTERRUPTED;
         DownloadItem item = new DownloadItem(false, downloadInfo);
         if (!downloadInfo.isResumable()) {
-            status = DOWNLOAD_STATUS_FAILED;
+            status = DownloadStatus.FAILED;
         } else if (isAutoResumable) {
             addAutoResumableDownload(item.getId());
         }
@@ -477,30 +492,30 @@
         boolean notificationUpdateScheduled = true;
         boolean removeFromDownloadProgressMap = true;
         switch (progress.mDownloadStatus) {
-            case DOWNLOAD_STATUS_COMPLETE:
+            case DownloadStatus.COMPLETE:
                 notificationUpdateScheduled = updateDownloadSuccessNotification(progress);
                 removeFromDownloadProgressMap = notificationUpdateScheduled;
                 break;
-            case DOWNLOAD_STATUS_FAILED:
+            case DownloadStatus.FAILED:
                 // TODO(cmsy): Use correct FailState.
                 mDownloadNotifier.notifyDownloadFailed(info, FailState.CANNOT_DOWNLOAD);
                 Log.w(TAG, "Download failed: " + info.getFilePath());
                 onDownloadFailed(item, DownloadManager.ERROR_UNKNOWN);
                 break;
-            case DOWNLOAD_STATUS_IN_PROGRESS:
+            case DownloadStatus.IN_PROGRESS:
                 if (info.isPaused()) {
                     mDownloadNotifier.notifyDownloadPaused(info);
-                    recordDownloadResumption(UMA_DOWNLOAD_RESUMPTION_MANUAL_PAUSE);
+                    recordDownloadResumption(UmaDownloadResumption.MANUAL_PAUSE);
                 } else {
                     mDownloadNotifier.notifyDownloadProgress(
                             info, progress.mStartTimeInMillis, progress.mCanDownloadWhileMetered);
                     removeFromDownloadProgressMap = false;
                 }
                 break;
-            case DOWNLOAD_STATUS_CANCELLED:
+            case DownloadStatus.CANCELLED:
                 mDownloadNotifier.notifyDownloadCanceled(item.getContentId());
                 break;
-            case DOWNLOAD_STATUS_INTERRUPTED:
+            case DownloadStatus.INTERRUPTED:
                 mDownloadNotifier.notifyDownloadInterrupted(
                         info, progress.mIsAutoResumable, PendingState.PENDING_NETWORK);
                 removeFromDownloadProgressMap = !progress.mIsAutoResumable;
@@ -509,12 +524,8 @@
                 assert false;
                 break;
         }
-        if (notificationUpdateScheduled) {
-            progress.mIsUpdated = false;
-        }
-        if (removeFromDownloadProgressMap) {
-            mDownloadProgressMap.remove(item.getId());
-        }
+        if (notificationUpdateScheduled) progress.mIsUpdated = false;
+        if (removeFromDownloadProgressMap) mDownloadProgressMap.remove(item.getId());
     }
 
     /**
@@ -606,7 +617,7 @@
             return;
         }
         openDownloadedContent(download.getDownloadInfo(), download.getSystemDownloadId(),
-                DownloadMetrics.AUTO_OPEN);
+                DownloadMetrics.DownloadOpenSource.AUTO_OPEN);
     }
 
     /**
@@ -644,8 +655,9 @@
      * @param downloadItem Information about the download.
      * @param downloadStatus Status of the download.
      */
-    private void updateDownloadProgress(DownloadItem downloadItem, int downloadStatus) {
-        boolean isSupportedMimeType = downloadStatus == DOWNLOAD_STATUS_COMPLETE
+    private void updateDownloadProgress(
+            DownloadItem downloadItem, @DownloadStatus int downloadStatus) {
+        boolean isSupportedMimeType = downloadStatus == DownloadStatus.COMPLETE
                 && isSupportedMimeType(downloadItem.getDownloadInfo().getMimeType());
         String id = downloadItem.getId();
         DownloadProgress progress = mDownloadProgressMap.get(id);
@@ -661,17 +673,16 @@
                 sFirstSeenDownloadIds.add(id);
                 DownloadUmaStatsEntry entry = getUmaStatsEntry(downloadItem.getId());
                 if (entry == null) {
-                    addUmaStatsEntry(new DownloadUmaStatsEntry(
-                            downloadItem.getId(), startTime,
-                            downloadStatus == DOWNLOAD_STATUS_INTERRUPTED ? 1 : 0, false, false,
+                    addUmaStatsEntry(new DownloadUmaStatsEntry(downloadItem.getId(), startTime,
+                            downloadStatus == DownloadStatus.INTERRUPTED ? 1 : 0, false, false,
                             bytesReceived, 0));
                 } else if (updateBytesReceived(entry, bytesReceived)) {
                     storeUmaEntries();
                 }
 
                 // This is mostly for testing, when the download is not tracked/progress is null but
-                // downloadStatus is not DOWNLOAD_STATUS_IN_PROGRESS.
-                if (downloadStatus != DOWNLOAD_STATUS_IN_PROGRESS) {
+                // downloadStatus is not DownloadStatus.IN_PROGRESS.
+                if (downloadStatus != DownloadStatus.IN_PROGRESS) {
                     updateNotification(progress);
                 }
             }
@@ -685,9 +696,9 @@
         progress.mIsSupportedMimeType = isSupportedMimeType;
         DownloadUmaStatsEntry entry;
         switch (downloadStatus) {
-            case DOWNLOAD_STATUS_COMPLETE:
-            case DOWNLOAD_STATUS_FAILED:
-            case DOWNLOAD_STATUS_CANCELLED:
+            case DownloadStatus.COMPLETE:
+            case DownloadStatus.FAILED:
+            case DownloadStatus.CANCELLED:
                 recordDownloadFinishedUMA(
                         downloadStatus, id, downloadItem.getDownloadInfo().getBytesReceived());
                 clearDownloadRetryCount(id, true);
@@ -695,14 +706,14 @@
                 updateNotification(progress);
                 sFirstSeenDownloadIds.remove(id);
                 break;
-            case DOWNLOAD_STATUS_INTERRUPTED:
+            case DownloadStatus.INTERRUPTED:
                 entry = getUmaStatsEntry(id);
                 entry.numInterruptions++;
                 updateBytesReceived(entry, bytesReceived);
                 storeUmaEntries();
                 updateNotification(progress);
                 break;
-            case DOWNLOAD_STATUS_IN_PROGRESS:
+            case DownloadStatus.IN_PROGRESS:
                 entry = getUmaStatsEntry(id);
                 if (entry.isPaused != downloadItem.getDownloadInfo().isPaused()
                         || updateBytesReceived(entry, bytesReceived)) {
@@ -761,7 +772,7 @@
         if (!result) {
             onDownloadFailed(downloadItem, failureReason);
             recordDownloadCompletionStats(
-                    true, DownloadManagerService.DOWNLOAD_STATUS_FAILED, 0, 0, 0, 0);
+                    true, DownloadManagerService.DownloadStatus.FAILED, 0, 0, 0, 0);
             return;
         }
 
@@ -956,13 +967,13 @@
     @Override
     public void resumeDownload(ContentId id, DownloadItem item, boolean hasUserGesture) {
         DownloadProgress progress = mDownloadProgressMap.get(item.getId());
-        if (progress != null && progress.mDownloadStatus == DOWNLOAD_STATUS_IN_PROGRESS
+        if (progress != null && progress.mDownloadStatus == DownloadStatus.IN_PROGRESS
                 && !progress.mDownloadItem.getDownloadInfo().isPaused()) {
             // Download already in progress, do nothing
             return;
         }
-        int uma = hasUserGesture ? UMA_DOWNLOAD_RESUMPTION_CLICKED
-                : UMA_DOWNLOAD_RESUMPTION_AUTO_STARTED;
+        int uma =
+                hasUserGesture ? UmaDownloadResumption.CLICKED : UmaDownloadResumption.AUTO_STARTED;
         recordDownloadResumption(uma);
         if (progress == null) {
             assert !item.getDownloadInfo().isPaused();
@@ -970,9 +981,9 @@
             // download is active.
             if (!sFirstSeenDownloadIds.contains(item.getId())) {
                 sFirstSeenDownloadIds.add(item.getId());
-                recordDownloadResumption(UMA_DOWNLOAD_RESUMPTION_BROWSER_KILLED);
+                recordDownloadResumption(UmaDownloadResumption.BROWSER_KILLED);
             }
-            updateDownloadProgress(item, DOWNLOAD_STATUS_IN_PROGRESS);
+            updateDownloadProgress(item, DownloadStatus.IN_PROGRESS);
             progress = mDownloadProgressMap.get(item.getId());
         }
         if (hasUserGesture) {
@@ -1016,7 +1027,7 @@
             onDownloadCancelled(info);
             removeDownloadProgress(id.id);
         }
-        recordDownloadFinishedUMA(DOWNLOAD_STATUS_CANCELLED, id.id, 0);
+        recordDownloadFinishedUMA(DownloadStatus.CANCELLED, id.id, 0);
     }
 
     /**
@@ -1031,8 +1042,9 @@
         // Calling pause will stop listening to the download item. Update its progress now.
         // If download is already completed, canceled or failed, there is no need to update the
         // download notification.
-        if (progress != null && (progress.mDownloadStatus == DOWNLOAD_STATUS_INTERRUPTED
-                || progress.mDownloadStatus == DOWNLOAD_STATUS_IN_PROGRESS)) {
+        if (progress != null
+                && (progress.mDownloadStatus == DownloadStatus.INTERRUPTED
+                           || progress.mDownloadStatus == DownloadStatus.IN_PROGRESS)) {
             DownloadInfo info = DownloadInfo.Builder.fromDownloadInfo(
                     progress.mDownloadItem.getDownloadInfo()).setIsPaused(true)
                     .setBytesReceived(UNKNOWN_BYTES_RECEIVED).build();
@@ -1107,8 +1119,8 @@
                 new DownloadInfo.Builder().setDownloadGuid(downloadGuid).build(),
                 FailState.CANNOT_DOWNLOAD);
         removeDownloadProgress(downloadGuid);
-        recordDownloadResumption(UMA_DOWNLOAD_RESUMPTION_FAILED);
-        recordDownloadFinishedUMA(DOWNLOAD_STATUS_FAILED, downloadGuid, 0);
+        recordDownloadResumption(UmaDownloadResumption.FAILED);
+        recordDownloadFinishedUMA(DownloadStatus.FAILED, downloadGuid, 0);
     }
 
     /**
@@ -1145,10 +1157,9 @@
      * Helper method to record the download resumption UMA.
      * @param type UMA type to be recorded.
      */
-    private void recordDownloadResumption(int type) {
-        assert type < UMA_DOWNLOAD_RESUMPTION_COUNT && type >= 0;
+    private void recordDownloadResumption(@UmaDownloadResumption int type) {
         RecordHistogram.recordEnumeratedHistogram(
-                "MobileDownload.DownloadResumption", type, UMA_DOWNLOAD_RESUMPTION_COUNT);
+                "MobileDownload.DownloadResumption", type, UmaDownloadResumption.NUM_ENTRIES);
     }
 
     /**
@@ -1162,7 +1173,7 @@
     private void recordDownloadCompletionStats(boolean useDownloadManager, int status,
             long totalDuration, long bytesDownloaded, int numInterruptions, long bytesWasted) {
         switch (status) {
-            case DOWNLOAD_STATUS_COMPLETE:
+            case DownloadStatus.COMPLETE:
                 if (useDownloadManager) {
                     RecordHistogram.recordLongTimesHistogram(
                             "MobileDownload.DownloadTime.DownloadManager.Success",
@@ -1184,7 +1195,7 @@
                             "MobileDownload.BytesWasted.ChromeNetworkStack.Success", bytesWasted);
                 }
                 break;
-            case DOWNLOAD_STATUS_FAILED:
+            case DownloadStatus.FAILED:
                 if (useDownloadManager) {
                     RecordHistogram.recordLongTimesHistogram(
                             "MobileDownload.DownloadTime.DownloadManager.Failure",
@@ -1206,7 +1217,7 @@
                             "MobileDownload.BytesWasted.ChromeNetworkStack.Failure", bytesWasted);
                 }
                 break;
-            case DOWNLOAD_STATUS_CANCELLED:
+            case DownloadStatus.CANCELLED:
                 if (!useDownloadManager) {
                     RecordHistogram.recordLongTimesHistogram(
                             "MobileDownload.DownloadTime.ChromeNetworkStack.Cancel",
@@ -1237,10 +1248,10 @@
     @Override
     public void onQueryCompleted(
             DownloadManagerDelegate.DownloadQueryResult result, boolean showNotification) {
-        if (result.downloadStatus == DOWNLOAD_STATUS_IN_PROGRESS) return;
+        if (result.downloadStatus == DownloadStatus.IN_PROGRESS) return;
         if (showNotification) {
             switch (result.downloadStatus) {
-                case DOWNLOAD_STATUS_COMPLETE:
+                case DownloadStatus.COMPLETE:
                     if (shouldOpenAfterDownload(result.item.getDownloadInfo())
                             && result.canResolve) {
                         handleAutoOpenAfterDownload(result.item);
@@ -1251,7 +1262,7 @@
                                 result.item.getSystemDownloadId(), result.canResolve, true);
                     }
                     break;
-                case DOWNLOAD_STATUS_FAILED:
+                case DownloadStatus.FAILED:
                     onDownloadFailed(result.item, result.failureReason);
                     break;
                 default:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java
index f41aaf4..064daa6e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadMetrics.java
@@ -22,21 +22,24 @@
     // Tracks where the users interact with download files on Android. Used in histogram.
     // See AndroidDownloadOpenSource in enums.xml. The values used by this enum will be persisted
     // to server logs and should not be deleted, changed or reused.
+    @IntDef({DownloadOpenSource.UNKNOWN, DownloadOpenSource.ANDROID_DOWNLOAD_MANAGER,
+            DownloadOpenSource.DOWNLOAD_HOME, DownloadOpenSource.NOTIFICATION,
+            DownloadOpenSource.NEW_TAP_PAGE, DownloadOpenSource.INFO_BAR,
+            DownloadOpenSource.SNACK_BAR, DownloadOpenSource.AUTO_OPEN,
+            DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR})
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({UNKNOWN, ANDROID_DOWNLOAD_MANAGER, DOWNLOAD_HOME, NOTIFICATION, NEW_TAP_PAGE, INFO_BAR,
-            SNACK_BAR, AUTO_OPEN, DOWNLOAD_PROGRESS_INFO_BAR, DOWNLOAD_SOURCE_BOUNDARY})
-    public @interface DownloadOpenSource {}
-
-    public static final int UNKNOWN = 0;
-    public static final int ANDROID_DOWNLOAD_MANAGER = 1;
-    public static final int DOWNLOAD_HOME = 2;
-    public static final int NOTIFICATION = 3;
-    public static final int NEW_TAP_PAGE = 4;
-    public static final int INFO_BAR = 5;
-    public static final int SNACK_BAR = 6;
-    public static final int AUTO_OPEN = 7;
-    public static final int DOWNLOAD_PROGRESS_INFO_BAR = 8;
-    private static final int DOWNLOAD_SOURCE_BOUNDARY = 9;
+    public @interface DownloadOpenSource {
+        int UNKNOWN = 0;
+        int ANDROID_DOWNLOAD_MANAGER = 1;
+        int DOWNLOAD_HOME = 2;
+        int NOTIFICATION = 3;
+        int NEW_TAP_PAGE = 4;
+        int INFO_BAR = 5;
+        int SNACK_BAR = 6;
+        int AUTO_OPEN = 7;
+        int DOWNLOAD_PROGRESS_INFO_BAR = 8;
+        int NUM_ENTRIES = 9;
+    }
 
     private static final String TAG = "DownloadMetrics";
     private static final int MAX_VIEW_RETENTION_MINUTES = 30 * 24 * 60;
@@ -52,13 +55,14 @@
             return;
         }
 
+        @DownloadFilter.Type
         int type = DownloadFilter.fromMimeType(mimeType);
-        if (type == DownloadFilter.FILTER_VIDEO) {
-            RecordHistogram.recordEnumeratedHistogram(
-                    "Android.DownloadManager.OpenSource.Video", source, DOWNLOAD_SOURCE_BOUNDARY);
-        } else if (type == DownloadFilter.FILTER_AUDIO) {
-            RecordHistogram.recordEnumeratedHistogram(
-                    "Android.DownloadManager.OpenSource.Audio", source, DOWNLOAD_SOURCE_BOUNDARY);
+        if (type == DownloadFilter.Type.VIDEO) {
+            RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.OpenSource.Video",
+                    source, DownloadOpenSource.NUM_ENTRIES);
+        } else if (type == DownloadFilter.Type.AUDIO) {
+            RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.OpenSource.Audio",
+                    source, DownloadOpenSource.NUM_ENTRIES);
         }
     }
 
@@ -74,14 +78,15 @@
             return;
         }
 
+        @DownloadFilter.Type
         int type = DownloadFilter.fromMimeType(mimeType);
         int viewRetentionTimeMinutes = (int) ((System.currentTimeMillis() - startTime) / 60000);
 
-        if (type == DownloadFilter.FILTER_VIDEO) {
+        if (type == DownloadFilter.Type.VIDEO) {
             RecordHistogram.recordCustomCountHistogram(
                     "Android.DownloadManager.ViewRetentionTime.Video", viewRetentionTimeMinutes, 1,
                     MAX_VIEW_RETENTION_MINUTES, 50);
-        } else if (type == DownloadFilter.FILTER_AUDIO) {
+        } else if (type == DownloadFilter.Type.AUDIO) {
             RecordHistogram.recordCustomCountHistogram(
                     "Android.DownloadManager.ViewRetentionTime.Audio", viewRetentionTimeMinutes, 1,
                     MAX_VIEW_RETENTION_MINUTES, 50);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
index 1e4983f..6a6c5a1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -1279,7 +1279,7 @@
         ContentId contentId = DownloadNotificationService.getContentIdFromIntent(intent);
         DownloadManagerService.openDownloadedContent(context, downloadFilename, isSupportedMimeType,
                 isOffTheRecord, contentId.id, id, originalUrl, referrer,
-                DownloadMetrics.NOTIFICATION);
+                DownloadMetrics.DownloadOpenSource.NOTIFICATION);
     }
 
     /**
@@ -1321,10 +1321,9 @@
      * @return delegate for interactions with the entry
      */
     DownloadServiceDelegate getServiceDelegate(ContentId id) {
-        if (LegacyHelpers.isLegacyDownload(id)) {
-            return DownloadManagerService.getDownloadManagerService();
-        }
-        return OfflineContentAggregatorNotificationBridgeUiFactory.instance();
+        return LegacyHelpers.isLegacyDownload(id)
+                ? DownloadManagerService.getDownloadManagerService()
+                : OfflineContentAggregatorNotificationBridgeUiFactory.instance();
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
index 1b569eec..81ae4a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadSnackbarController.java
@@ -63,7 +63,7 @@
                                                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
             } else {
                 manager.openDownloadedContent(download.downloadInfo, download.systemDownloadId,
-                        DownloadMetrics.SNACK_BAR);
+                        DownloadMetrics.DownloadOpenSource.SNACK_BAR);
             }
         } else {
             OfflineContentAggregatorNotificationBridgeUiFactory.instance().openItem(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
index 4e07c63b..4bca328 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadUtils.java
@@ -368,13 +368,13 @@
             }
 
             if (selectedItemsFilterType != wrappedItem.getFilterType()) {
-                selectedItemsFilterType = DownloadFilter.FILTER_ALL;
+                selectedItemsFilterType = DownloadFilter.Type.ALL;
             }
-            if (wrappedItem.getFilterType() == DownloadFilter.FILTER_OTHER) {
+            if (wrappedItem.getFilterType() == DownloadFilter.Type.OTHER) {
                 RecordHistogram.recordEnumeratedHistogram(
                         "Android.DownloadManager.OtherExtensions.Share",
                         wrappedItem.getFileExtensionType(),
-                        DownloadHistoryItemWrapper.FILE_EXTENSION_BOUNDARY);
+                        DownloadHistoryItemWrapper.FileExtension.NUM_ENTRIES);
             }
 
             // If a mime type was not retrieved from the backend or could not be normalized,
@@ -639,7 +639,7 @@
             return true;
         } catch (ActivityNotFoundException e) {
             // Can't launch the Intent.
-            if (source != DownloadMetrics.DOWNLOAD_PROGRESS_INFO_BAR) {
+            if (source != DownloadMetrics.DownloadOpenSource.DOWNLOAD_PROGRESS_INFO_BAR) {
                 Toast.makeText(context, context.getString(R.string.download_cant_open_file),
                              Toast.LENGTH_SHORT)
                         .show();
@@ -650,7 +650,7 @@
 
     private static void recordShareHistograms(int count, int filterType) {
         RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.Share.FileTypes",
-                filterType, DownloadFilter.FILTER_BOUNDARY);
+                filterType, DownloadFilter.Type.NUM_ENTRIES);
 
         RecordHistogram.recordLinearCountHistogram("Android.DownloadManager.Share.Count",
                 count, 1, 20, 20);
@@ -695,11 +695,9 @@
 
         switch (progress.unit) {
             case OfflineItemProgressUnit.PERCENTAGE:
-                if (progress.isIndeterminate()) {
-                    return context.getResources().getString(R.string.download_started);
-                } else {
-                    return getPercentageString(progress.getPercentage());
-                }
+                return progress.isIndeterminate()
+                        ? context.getResources().getString(R.string.download_started)
+                        : getPercentageString(progress.getPercentage());
             case OfflineItemProgressUnit.BYTES:
                 String bytes = getStringForBytes(context, progress.value);
                 if (progress.isIndeterminate()) {
@@ -744,11 +742,9 @@
      */
     public static String getTimeOrFilesLeftString(
             Context context, Progress progress, long timeRemainingInMillis) {
-        if (progress.unit == OfflineItemProgressUnit.FILES) {
-            return formatRemainingFiles(context, progress);
-        } else {
-            return formatRemainingTime(context, timeRemainingInMillis);
-        }
+        return progress.unit == OfflineItemProgressUnit.FILES
+                ? formatRemainingFiles(context, progress)
+                : formatRemainingTime(context, timeRemainingInMillis);
     }
 
     /**
@@ -758,11 +754,8 @@
      */
     public static String formatRemainingFiles(Context context, Progress progress) {
         int filesLeft = (int) (progress.max - progress.value);
-        if (filesLeft == 1) {
-            return context.getResources().getString(R.string.one_file_left);
-        } else {
-            return context.getResources().getString(R.string.files_left, filesLeft);
-        }
+        return filesLeft == 1 ? context.getResources().getString(R.string.one_file_left)
+                              : context.getResources().getString(R.string.files_left, filesLeft);
     }
 
     /**
@@ -836,7 +829,7 @@
             case OfflineItemState.INTERRUPTED: // intentional fall through
             case OfflineItemState.FAILED:
                 break;
-            case OfflineItemState.MAX_DOWNLOAD_STATE:
+            // case OfflineItemState.MAX_DOWNLOAD_STATE:
             default:
                 assert false : "Unexpected OfflineItemState: " + item.state;
         }
@@ -895,20 +888,21 @@
      */
     public static String getFailStatusString(@FailState int failState) {
         Context context = ContextUtils.getApplicationContext();
-        if (BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
-                        .isStartupSuccessfullyCompleted()
-                && ChromeFeatureList.isEnabled(
-                           ChromeFeatureList.OFFLINE_PAGES_DESCRIPTIVE_FAIL_STATUS)) {
-            switch (failState) {
-                // TODO(cmsy): Return correct status for failure reasons once strings are finalized.
-                case FailState.CANNOT_DOWNLOAD:
-                case FailState.NETWORK_INSTABILITY:
-                default:
-                    return context.getString(R.string.download_notification_failed);
-            }
-        } else {
-            return context.getString(R.string.download_notification_failed);
-        }
+
+        // TODO(cmsy): Return correct status for failure reasons once strings are finalized.
+        // if (BrowserStartupController.get(LibraryProcessType.PROCESS_BROWSER)
+        //                .isStartupSuccessfullyCompleted()
+        //        && ChromeFeatureList.isEnabled(
+        //                   ChromeFeatureList.OFFLINE_PAGES_DESCRIPTIVE_FAIL_STATUS)) {
+        //    switch (failState) {
+        //        case FailState.CANNOT_DOWNLOAD:
+        //        case FailState.NETWORK_INSTABILITY:
+        //        default:
+        //          return context.getString(R.string.download_notification_failed);
+        //    }
+        // }
+
+        return context.getString(R.string.download_notification_failed);
     }
 
     /**
@@ -1048,9 +1042,7 @@
     public static String getAbbreviatedFileName(String fileName, int limit) {
         assert limit >= 1;  // Abbreviated file name should at least be 1 characters (a...)
 
-        if (TextUtils.isEmpty(fileName)) return fileName;
-
-        if (fileName.length() <= limit) return fileName;
+        if (TextUtils.isEmpty(fileName) || fileName.length() <= limit) return fileName;
 
         // Find the file name extension
         int index = fileName.lastIndexOf(".");
@@ -1073,19 +1065,19 @@
     public static int getIconResId(int fileType, @IconSize int iconSize) {
         // TODO(huayinz): Make image view size same as icon size so that 36dp icons can be removed.
         switch (fileType) {
-            case DownloadFilter.FILTER_PAGE:
+            case DownloadFilter.Type.PAGE:
                 return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_globe_24dp
                                                    : R.drawable.ic_globe_36dp;
-            case DownloadFilter.FILTER_VIDEO:
+            case DownloadFilter.Type.VIDEO:
                 return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_videocam_24dp
                                                    : R.drawable.ic_videocam_36dp;
-            case DownloadFilter.FILTER_AUDIO:
+            case DownloadFilter.Type.AUDIO:
                 return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_music_note_24dp
                                                    : R.drawable.ic_music_note_36dp;
-            case DownloadFilter.FILTER_IMAGE:
+            case DownloadFilter.Type.IMAGE:
                 return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_drive_image_24dp
                                                    : R.drawable.ic_drive_image_36dp;
-            case DownloadFilter.FILTER_DOCUMENT:
+            case DownloadFilter.Type.DOCUMENT:
                 return iconSize == ICON_SIZE_24_DP ? R.drawable.ic_drive_document_24dp
                                                    : R.drawable.ic_drive_document_36dp;
             default:
@@ -1156,7 +1148,6 @@
         }
         if (primaryDir == null || path == null) return false;
         String primaryPath = primaryDir.getAbsolutePath();
-        if (primaryPath == null) return false;
-        return path.contains(primaryPath);
+        return primaryPath == null ? false : path.contains(primaryPath);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
index 84aa906..a177f259 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/OMADownloadHandler.java
@@ -294,7 +294,8 @@
             manager.remove(mDownloadId);
             mFreeSpace = Environment.getExternalStorageDirectory().getUsableSpace();
             DownloadMetrics.recordDownloadOpen(
-                    DownloadMetrics.ANDROID_DOWNLOAD_MANAGER, mDownloadInfo.getMimeType());
+                    DownloadMetrics.DownloadOpenSource.ANDROID_DOWNLOAD_MANAGER,
+                    mDownloadInfo.getMimeType());
             return omaInfo;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
index 2d4bf9d2..896f446 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/SystemDownloadNotifier.java
@@ -9,6 +9,7 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.os.IBinder;
+import android.support.annotation.IntDef;
 
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
@@ -18,6 +19,8 @@
 import org.chromium.components.offline_items_collection.FailState;
 import org.chromium.components.offline_items_collection.PendingState;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -32,14 +35,22 @@
  */
 public class SystemDownloadNotifier implements DownloadNotifier, Observer {
     private static final String TAG = "DownloadNotifier";
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_PROGRESS = 0;
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_SUCCESS = 1;
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_FAILURE = 2;
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_CANCEL = 3;
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_RESUME_ALL = 4;
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_PAUSE = 5;
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_INTERRUPT = 6;
-    private static final int DOWNLOAD_NOTIFICATION_TYPE_REMOVE_NOTIFICATION = 7;
+
+    @IntDef({DownloadNotificationType.PROGRESS, DownloadNotificationType.SUCCESS,
+            DownloadNotificationType.FAILURE, DownloadNotificationType.CANCEL,
+            DownloadNotificationType.RESUME_ALL, DownloadNotificationType.PAUSE,
+            DownloadNotificationType.INTERRUPT, DownloadNotificationType.REMOVE_NOTIFICATION})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface DownloadNotificationType {
+        int PROGRESS = 0;
+        int SUCCESS = 1;
+        int FAILURE = 2;
+        int CANCEL = 3;
+        int RESUME_ALL = 4;
+        int PAUSE = 5;
+        int INTERRUPT = 6;
+        int REMOVE_NOTIFICATION = 7;
+    }
 
     private final Context mApplicationContext;
 
@@ -171,14 +182,14 @@
     public void notifyDownloadCanceled(ContentId id) {
         DownloadInfo downloadInfo = new DownloadInfo.Builder().setContentId(id).build();
         updateDownloadNotification(
-                new PendingNotificationInfo(DOWNLOAD_NOTIFICATION_TYPE_CANCEL, downloadInfo), true);
+                new PendingNotificationInfo(DownloadNotificationType.CANCEL, downloadInfo), true);
     }
 
     @Override
     public void notifyDownloadSuccessful(DownloadInfo downloadInfo, long systemDownloadId,
             boolean canResolve, boolean isSupportedMimeType) {
         PendingNotificationInfo info =
-                new PendingNotificationInfo(DOWNLOAD_NOTIFICATION_TYPE_SUCCESS, downloadInfo);
+                new PendingNotificationInfo(DownloadNotificationType.SUCCESS, downloadInfo);
         info.canResolve = canResolve;
         info.systemDownloadId = systemDownloadId;
         info.isSupportedMimeType = isSupportedMimeType;
@@ -188,15 +199,14 @@
     @Override
     public void notifyDownloadFailed(DownloadInfo downloadInfo, @FailState int notUsed) {
         updateDownloadNotification(
-                new PendingNotificationInfo(DOWNLOAD_NOTIFICATION_TYPE_FAILURE, downloadInfo),
-                true);
+                new PendingNotificationInfo(DownloadNotificationType.FAILURE, downloadInfo), true);
     }
 
     @Override
     public void notifyDownloadProgress(
             DownloadInfo downloadInfo, long startTime, boolean canDownloadWhileMetered) {
         PendingNotificationInfo info =
-                new PendingNotificationInfo(DOWNLOAD_NOTIFICATION_TYPE_PROGRESS, downloadInfo);
+                new PendingNotificationInfo(DownloadNotificationType.PROGRESS, downloadInfo);
         info.startTime = startTime;
         info.canDownloadWhileMetered = canDownloadWhileMetered;
         updateDownloadNotification(info, true);
@@ -205,7 +215,7 @@
     @Override
     public void notifyDownloadPaused(DownloadInfo downloadInfo) {
         PendingNotificationInfo info =
-                new PendingNotificationInfo(DOWNLOAD_NOTIFICATION_TYPE_PAUSE, downloadInfo);
+                new PendingNotificationInfo(DownloadNotificationType.PAUSE, downloadInfo);
         updateDownloadNotification(info, true);
     }
 
@@ -213,7 +223,7 @@
     public void notifyDownloadInterrupted(
             DownloadInfo downloadInfo, boolean isAutoResumable, @PendingState int notUsed) {
         PendingNotificationInfo info =
-                new PendingNotificationInfo(DOWNLOAD_NOTIFICATION_TYPE_INTERRUPT, downloadInfo);
+                new PendingNotificationInfo(DownloadNotificationType.INTERRUPT, downloadInfo);
         info.isAutoResumable = isAutoResumable;
         updateDownloadNotification(info, true);
     }
@@ -221,7 +231,7 @@
     @Override
     public void removeDownloadNotification(int notificationId, DownloadInfo downloadInfo) {
         PendingNotificationInfo info = new PendingNotificationInfo(
-                DOWNLOAD_NOTIFICATION_TYPE_REMOVE_NOTIFICATION, downloadInfo);
+                DownloadNotificationType.REMOVE_NOTIFICATION, downloadInfo);
         info.notificationId = notificationId;
         updateDownloadNotification(info, true);
     }
@@ -230,7 +240,7 @@
     public void resumePendingDownloads() {
         if (!DownloadNotificationService.isTrackingResumableDownloads(mApplicationContext)) return;
         updateDownloadNotification(
-                new PendingNotificationInfo(DOWNLOAD_NOTIFICATION_TYPE_RESUME_ALL, null), true);
+                new PendingNotificationInfo(DownloadNotificationType.RESUME_ALL, null), true);
     }
 
     /**
@@ -278,30 +288,30 @@
         }
 
         DownloadInfo info = notificationInfo.downloadInfo;
-        if (notificationInfo.type == DOWNLOAD_NOTIFICATION_TYPE_PROGRESS) {
+        if (notificationInfo.type == DownloadNotificationType.PROGRESS) {
             mActiveDownloads.add(info.getDownloadGuid());
-        } else if (notificationInfo.type != DOWNLOAD_NOTIFICATION_TYPE_RESUME_ALL) {
+        } else if (notificationInfo.type != DownloadNotificationType.RESUME_ALL) {
             mActiveDownloads.remove(info.getDownloadGuid());
         }
 
         switch (notificationInfo.type) {
-            case DOWNLOAD_NOTIFICATION_TYPE_PROGRESS:
+            case DownloadNotificationType.PROGRESS:
                 mBoundService.notifyDownloadProgress(info.getContentId(), info.getFileName(),
                         info.getProgress(), info.getBytesReceived(),
                         info.getTimeRemainingInMillis(), notificationInfo.startTime,
                         info.isOffTheRecord(), notificationInfo.canDownloadWhileMetered,
                         info.getIsTransient(), info.getIcon());
                 break;
-            case DOWNLOAD_NOTIFICATION_TYPE_PAUSE:
+            case DownloadNotificationType.PAUSE:
                 mBoundService.notifyDownloadPaused(info.getContentId(), info.getFileName(), true,
                         false, info.isOffTheRecord(), info.getIsTransient(), info.getIcon());
                 break;
-            case DOWNLOAD_NOTIFICATION_TYPE_INTERRUPT:
+            case DownloadNotificationType.INTERRUPT:
                 mBoundService.notifyDownloadPaused(info.getContentId(), info.getFileName(),
                         info.isResumable(), notificationInfo.isAutoResumable, info.isOffTheRecord(),
                         info.getIsTransient(), info.getIcon());
                 break;
-            case DOWNLOAD_NOTIFICATION_TYPE_SUCCESS:
+            case DownloadNotificationType.SUCCESS:
                 final int notificationId = mBoundService.notifyDownloadSuccessful(
                         info.getContentId(), info.getFilePath(), info.getFileName(),
                         notificationInfo.systemDownloadId, info.isOffTheRecord(),
@@ -309,17 +319,17 @@
                         info.getOriginalUrl(), info.getReferrer());
                 onSuccessNotificationShown(notificationInfo, notificationId);
                 break;
-            case DOWNLOAD_NOTIFICATION_TYPE_FAILURE:
+            case DownloadNotificationType.FAILURE:
                 mBoundService.notifyDownloadFailed(
                         info.getContentId(), info.getFileName(), info.getIcon());
                 break;
-            case DOWNLOAD_NOTIFICATION_TYPE_CANCEL:
+            case DownloadNotificationType.CANCEL:
                 mBoundService.notifyDownloadCanceled(info.getContentId());
                 break;
-            case DOWNLOAD_NOTIFICATION_TYPE_RESUME_ALL:
+            case DownloadNotificationType.RESUME_ALL:
                 mBoundService.resumeAllPendingDownloads();
                 break;
-            case DOWNLOAD_NOTIFICATION_TYPE_REMOVE_NOTIFICATION:
+            case DownloadNotificationType.REMOVE_NOTIFICATION:
                 mBoundService.cancelNotification(
                         notificationInfo.notificationId, info.getContentId());
                 break;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
index a3de2cd4..263ed23 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/DownloadManagerCoordinatorImpl.java
@@ -65,7 +65,7 @@
 
     @Override
     public void showPrefetchSection() {
-        updateForUrl(Filters.toUrl(Filters.PREFETCHED));
+        updateForUrl(Filters.toUrl(Filters.FilterType.PREFETCHED));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterChipsProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterChipsProvider.java
index e7258f55..0dfe5282 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterChipsProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterChipsProvider.java
@@ -44,24 +44,25 @@
         mDelegate = delegate;
         mSource = source;
 
-        Chip noneChip = new Chip(Filters.NONE, R.string.download_manager_ui_all_downloads,
-                R.string.download_manager_ui_all_downloads, Chip.INVALID_ICON_ID,
-                () -> onChipSelected(Filters.NONE));
-        Chip videosChip = new Chip(Filters.VIDEOS, R.string.download_manager_ui_video,
+        Chip noneChip =
+                new Chip(Filters.FilterType.NONE, R.string.download_manager_ui_all_downloads,
+                        R.string.download_manager_ui_all_downloads, Chip.INVALID_ICON_ID,
+                        () -> onChipSelected(Filters.FilterType.NONE));
+        Chip videosChip = new Chip(Filters.FilterType.VIDEOS, R.string.download_manager_ui_video,
                 R.string.download_manager_ui_video, R.drawable.ic_videocam_24dp,
-                () -> onChipSelected(Filters.VIDEOS));
-        Chip musicChip = new Chip(Filters.MUSIC, R.string.download_manager_ui_audio,
+                () -> onChipSelected(Filters.FilterType.VIDEOS));
+        Chip musicChip = new Chip(Filters.FilterType.MUSIC, R.string.download_manager_ui_audio,
                 R.string.download_manager_ui_audio, R.drawable.ic_music_note_24dp,
-                () -> onChipSelected(Filters.MUSIC));
-        Chip imagesChip = new Chip(Filters.IMAGES, R.string.download_manager_ui_images,
+                () -> onChipSelected(Filters.FilterType.MUSIC));
+        Chip imagesChip = new Chip(Filters.FilterType.IMAGES, R.string.download_manager_ui_images,
                 R.string.download_manager_ui_images, R.drawable.ic_drive_image_24dp,
-                () -> onChipSelected(Filters.IMAGES));
-        Chip sitesChip = new Chip(Filters.SITES, R.string.download_manager_ui_pages,
+                () -> onChipSelected(Filters.FilterType.IMAGES));
+        Chip sitesChip = new Chip(Filters.FilterType.SITES, R.string.download_manager_ui_pages,
                 R.string.download_manager_ui_pages, R.drawable.ic_globe_24dp,
-                () -> onChipSelected(Filters.SITES));
-        Chip otherChip = new Chip(Filters.OTHER, R.string.download_manager_ui_other,
+                () -> onChipSelected(Filters.FilterType.SITES));
+        Chip otherChip = new Chip(Filters.FilterType.OTHER, R.string.download_manager_ui_other,
                 R.string.download_manager_ui_other, R.drawable.ic_drive_file_24dp,
-                () -> onChipSelected(Filters.OTHER));
+                () -> onChipSelected(Filters.FilterType.OTHER));
 
         // By default select the none chip.
         noneChip.selected = true;
@@ -119,7 +120,7 @@
             if (chip.selected) return chip.id;
         }
 
-        return Filters.NONE;
+        return Filters.FilterType.NONE;
     }
 
     // ChipsProvider implementation.
@@ -159,7 +160,7 @@
     private void generateFilterStates() {
         // Build a set of all filter types in our data set.
         Set</* @FilterType */ Integer> filters = new HashSet<>();
-        filters.add(Filters.NONE);
+        filters.add(Filters.FilterType.NONE);
         for (OfflineItem item : mSource.getItems()) {
             filters.add(Filters.fromOfflineItem(item.filter));
         }
@@ -170,7 +171,7 @@
         // Validate that selection is on a valid type.
         for (Chip chip : mSortedChips) {
             if (chip.selected && !chip.enabled) {
-                onChipSelected(Filters.NONE);
+                onChipSelected(Filters.FilterType.NONE);
                 break;
             }
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
index 4b17c42d..af04186 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java
@@ -18,11 +18,12 @@
 
 /** A Coordinator responsible for showing the tab filter selection UI for downloads home. */
 public class FilterCoordinator {
+    @IntDef({TabType.FILES, TabType.PREFETCH})
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TAB_FILES, TAB_PREFETCH})
-    public @interface TabType {}
-    public static final int TAB_FILES = 0;
-    public static final int TAB_PREFETCH = 1;
+    public @interface TabType {
+        int FILES = 0;
+        int PREFETCH = 1;
+    }
 
     /** An Observer to notify when the selected tab has changed. */
     public interface Observer {
@@ -52,7 +53,7 @@
         mModel.addObserver(new PropertyModelChangeProcessor<>(mModel, mView, mViewBinder));
 
         mModel.setChangeListener(selectedTab -> handleTabSelected(selectedTab));
-        selectTab(TAB_FILES);
+        selectTab(TabType.FILES);
     }
 
     /** @return The {@link View} representing this widget. */
@@ -75,20 +76,20 @@
      * components might need to update the UI state.
      */
     public void setSelectedFilter(@FilterType int filter) {
-        if (filter == Filters.PREFETCHED) {
-            selectTab(TAB_PREFETCH);
+        if (filter == Filters.FilterType.PREFETCHED) {
+            selectTab(TabType.PREFETCH);
         } else {
             mChipsProvider.setFilterSelected(filter);
-            selectTab(TAB_FILES);
+            selectTab(TabType.FILES);
         }
     }
 
     private void selectTab(@TabType int selectedTab) {
         mModel.setSelectedTab(selectedTab);
 
-        if (selectedTab == TAB_FILES) {
+        if (selectedTab == TabType.FILES) {
             mModel.setContentView(mChipsCoordinator.getView());
-        } else if (selectedTab == TAB_PREFETCH) {
+        } else if (selectedTab == TabType.PREFETCH) {
             mModel.setContentView(null);
         }
     }
@@ -98,10 +99,10 @@
 
         @FilterType
         int filterType;
-        if (selectedTab == TAB_FILES) {
+        if (selectedTab == TabType.FILES) {
             filterType = mChipsProvider.getSelectedFilter();
         } else {
-            filterType = Filters.PREFETCHED;
+            filterType = Filters.FilterType.PREFETCHED;
         }
 
         for (Observer observer : mObserverList) observer.onFilterChanged(filterType);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java
index 26a0c73..42c90cda4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java
@@ -19,8 +19,6 @@
         static final PropertyKey CONTENT_VIEW = new PropertyKey();
         static final PropertyKey SELECTED_TAB = new PropertyKey();
         static final PropertyKey CHANGE_LISTENER = new PropertyKey();
-
-        private PropertyKey() {}
     }
 
     private View mContentView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterView.java
index 76cd2da7..28f2673 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterView.java
@@ -47,8 +47,8 @@
                 if (mTabSelectedCallback == null) return;
 
                 @TabType
-                int tabType = tab.getPosition() == 0 ? FilterCoordinator.TAB_FILES
-                                                     : FilterCoordinator.TAB_PREFETCH;
+                int tabType = tab.getPosition() == 0 ? FilterCoordinator.TabType.FILES
+                                                     : FilterCoordinator.TabType.PREFETCH;
                 mTabSelectedCallback.onResult(tabType);
             }
         });
@@ -70,7 +70,7 @@
 
     /** Sets which of the two tabs are selected based on {code selectedType}. */
     public void setTabSelected(@TabType int selectedType) {
-        int selectedIndex = selectedType == FilterCoordinator.TAB_FILES ? 0 : 1;
+        int selectedIndex = selectedType == FilterCoordinator.TabType.FILES ? 0 : 1;
         if (mTabsView.getSelectedTabPosition() == selectedIndex) return;
         mTabsView.getTabAt(selectedIndex).select();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
index 08b63d6..7c2e955 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
@@ -17,17 +17,19 @@
 /** Helper containing a list of Downloads Home filter types and conversion methods. */
 public class Filters {
     /** A list of possible filter types on offlined items. */
+    @IntDef({FilterType.NONE, FilterType.VIDEOS, FilterType.MUSIC, FilterType.IMAGES,
+            FilterType.SITES, FilterType.OTHER, FilterType.PREFETCHED})
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NONE, VIDEOS, MUSIC, IMAGES, SITES, OTHER, PREFETCHED})
-    public @interface FilterType {}
-    public static final int NONE = 0;
-    public static final int VIDEOS = 1;
-    public static final int MUSIC = 2;
-    public static final int IMAGES = 3;
-    public static final int SITES = 4;
-    public static final int OTHER = 5;
-    public static final int PREFETCHED = 6;
-    public static final int FILTER_BOUNDARY = 7;
+    public @interface FilterType {
+        int NONE = 0;
+        int VIDEOS = 1;
+        int MUSIC = 2;
+        int IMAGES = 3;
+        int SITES = 4;
+        int OTHER = 5;
+        int PREFETCHED = 6;
+        int NUM_ENTRIES = 7;
+    }
 
     /**
      * Converts from a {@link OfflineItem#filter} to a {@link FilterType}.  Note that not all
@@ -40,17 +42,17 @@
     public static @FilterType int fromOfflineItem(@OfflineItemFilter int filter) {
         switch (filter) {
             case OfflineItemFilter.FILTER_PAGE:
-                return SITES;
+                return FilterType.SITES;
             case OfflineItemFilter.FILTER_VIDEO:
-                return VIDEOS;
+                return FilterType.VIDEOS;
             case OfflineItemFilter.FILTER_AUDIO:
-                return MUSIC;
+                return FilterType.MUSIC;
             case OfflineItemFilter.FILTER_IMAGE:
-                return IMAGES;
-            case OfflineItemFilter.FILTER_OTHER:
-            case OfflineItemFilter.FILTER_DOCUMENT:
+                return FilterType.IMAGES;
+            // case OfflineItemFilter.FILTER_OTHER
+            // case OfflineItemFilter.FILTER_DOCUMENT
             default:
-                return OTHER;
+                return FilterType.OTHER;
         }
     }
 
@@ -59,18 +61,18 @@
             @OfflineItemFilter int filter) {
         switch (filter) {
             case OfflineItemFilter.FILTER_PAGE:
-                return DownloadFilter.FILTER_PAGE;
+                return DownloadFilter.Type.PAGE;
             case OfflineItemFilter.FILTER_VIDEO:
-                return DownloadFilter.FILTER_VIDEO;
+                return DownloadFilter.Type.VIDEO;
             case OfflineItemFilter.FILTER_AUDIO:
-                return DownloadFilter.FILTER_AUDIO;
+                return DownloadFilter.Type.AUDIO;
             case OfflineItemFilter.FILTER_IMAGE:
-                return DownloadFilter.FILTER_IMAGE;
+                return DownloadFilter.Type.IMAGE;
             case OfflineItemFilter.FILTER_DOCUMENT:
-                return DownloadFilter.FILTER_DOCUMENT;
-            case OfflineItemFilter.FILTER_OTHER:
+                return DownloadFilter.Type.DOCUMENT;
+            // case OfflineItemFilter.FILTER_OTHER
             default:
-                return DownloadFilter.FILTER_OTHER;
+                return DownloadFilter.Type.OTHER;
         }
     }
 
@@ -79,8 +81,8 @@
      * @see DownloadFilter#getUrlForFilter(int)
      */
     public static String toUrl(@FilterType int filter) {
-        if (filter == NONE) return UrlConstants.DOWNLOADS_URL;
-        return UrlConstants.DOWNLOADS_FILTER_URL + filter;
+        return filter == FilterType.NONE ? UrlConstants.DOWNLOADS_URL
+                                         : UrlConstants.DOWNLOADS_FILTER_URL + filter;
     }
 
     /**
@@ -88,19 +90,18 @@
      * @see DownloadFilter#getFilterFromUrl(String)
      */
     public static @FilterType int fromUrl(String url) {
-        if (TextUtils.isEmpty(url)) return NONE;
-        if (!url.startsWith(UrlConstants.DOWNLOADS_FILTER_URL)) return NONE;
+        if (TextUtils.isEmpty(url) || !url.startsWith(UrlConstants.DOWNLOADS_FILTER_URL)) {
+            return FilterType.NONE;
+        }
 
         @FilterType
-        int filter = NONE;
+        int filter = FilterType.NONE;
         try {
             filter = Integer.parseInt(url.substring(UrlConstants.DOWNLOADS_FILTER_URL.length()));
-            if (filter < 0 || filter >= FILTER_BOUNDARY) filter = NONE;
+            if (filter < 0 || filter >= FilterType.NUM_ENTRIES) filter = FilterType.NONE;
         } catch (NumberFormatException ex) {
         }
 
         return filter;
     }
-
-    private Filters() {}
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilter.java
index 0c41c92..402a772 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilter.java
@@ -12,7 +12,7 @@
  * {@link OfflineItem#filter} and {@link FilterType}.
  */
 public class TypeOfflineItemFilter extends OfflineItemFilter {
-    private @FilterType int mFilter = Filters.NONE;
+    private @FilterType int mFilter = Filters.FilterType.NONE;
 
     /** Creates an instance of this filter and wraps {@code source}. */
     public TypeOfflineItemFilter(OfflineItemFilterSource source) {
@@ -28,14 +28,12 @@
     // OfflineItemFilter implementation.
     @Override
     protected boolean isFilteredOut(OfflineItem item) {
+        @FilterType
         int requiredFilter = Filters.fromOfflineItem(item.filter);
 
         // Filter out based on prefetch suggestions before resorting to other types.
-        if (mFilter == Filters.PREFETCHED) return !item.isSuggested;
-        if (item.isSuggested) return mFilter != Filters.PREFETCHED;
-        if (mFilter == Filters.NONE) return false;
-        if (mFilter == requiredFilter) return false;
-
-        return true;
+        if (mFilter == Filters.FilterType.PREFETCHED) return !item.isSuggested;
+        if (item.isSuggested) return mFilter != Filters.FilterType.PREFETCHED;
+        return !(mFilter == Filters.FilterType.NONE || mFilter == requiredFilter);
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
index 455e5452..28e34f8e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/glue/DownloadGlue.java
@@ -99,7 +99,7 @@
     public void openItem(OfflineItem item) {
         // TODO(shaktisahu): May be pass metrics as a param.
         DownloadManagerService.getDownloadManagerService().openDownload(
-                item.id, item.isOffTheRecord, DownloadMetrics.DOWNLOAD_HOME);
+                item.id, item.isOffTheRecord, DownloadMetrics.DownloadOpenSource.DOWNLOAD_HOME);
     }
 
     /** @see OfflineContentProvider#removeItem(ContentId) */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
index fc6a429..e6485a8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/DateOrderedListView.java
@@ -129,13 +129,13 @@
             if (position < 0 || position >= mModel.size()) return;
 
             switch (ListUtils.getViewTypeForItem(mModel.get(position))) {
-                case ListUtils.IMAGE:
+                case ListUtils.ViewType.IMAGE:
                     outRect.left = mImagePaddingPx;
                     outRect.right = mImagePaddingPx;
                     outRect.top = mImagePaddingPx;
                     outRect.bottom = mImagePaddingPx;
                     break;
-                case ListUtils.PREFETCH:
+                case ListUtils.ViewType.PREFETCH:
                     outRect.left = mPrefetchHorizontalPaddingPx;
                     outRect.right = mPrefetchHorizontalPaddingPx;
                     outRect.top = mPrefetchVerticalPaddingPx / 2;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java
index 043da8b..62ad781 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListItemViewHolder.java
@@ -51,19 +51,19 @@
      */
     public static ListItemViewHolder create(ViewGroup parent, @ListUtils.ViewType int viewType) {
         switch (viewType) {
-            case ListUtils.DATE:
+            case ListUtils.ViewType.DATE:
                 return DateViewHolder.create(parent);
-            case ListUtils.IN_PROGRESS:
+            case ListUtils.ViewType.IN_PROGRESS:
                 return new InProgressViewHolder(parent);
-            case ListUtils.GENERIC:
+            case ListUtils.ViewType.GENERIC:
                 return GenericViewHolder.create(parent);
-            case ListUtils.VIDEO:
+            case ListUtils.ViewType.VIDEO:
                 return new VideoViewHolder(parent);
-            case ListUtils.IMAGE:
+            case ListUtils.ViewType.IMAGE:
                 return new ImageViewHolder(parent);
-            case ListUtils.CUSTOM_VIEW:
+            case ListUtils.ViewType.CUSTOM_VIEW:
                 return new CustomViewHolder(parent);
-            case ListUtils.PREFETCH:
+            case ListUtils.ViewType.PREFETCH:
                 return PrefetchViewHolder.create(parent);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
index 915c11a..1d6ac3d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/list/ListUtils.java
@@ -18,16 +18,18 @@
 /** Utility methods for representing {@link ListItem}s in a {@link RecyclerView} list. */
 class ListUtils {
     /** The potential types of list items that could be displayed. */
+    @IntDef({ViewType.DATE, ViewType.IN_PROGRESS, ViewType.GENERIC, ViewType.VIDEO, ViewType.IMAGE,
+            ViewType.CUSTOM_VIEW, ViewType.PREFETCH})
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({DATE, IN_PROGRESS, GENERIC, VIDEO, IMAGE, CUSTOM_VIEW, PREFETCH})
-    public @interface ViewType {}
-    public static final int DATE = 0;
-    public static final int IN_PROGRESS = 1;
-    public static final int GENERIC = 2;
-    public static final int VIDEO = 3;
-    public static final int IMAGE = 4;
-    public static final int CUSTOM_VIEW = 5;
-    public static final int PREFETCH = 6;
+    public @interface ViewType {
+        int DATE = 0;
+        int IN_PROGRESS = 1;
+        int GENERIC = 2;
+        int VIDEO = 3;
+        int IMAGE = 4;
+        int CUSTOM_VIEW = 5;
+        int PREFETCH = 6;
+    }
 
     private ListUtils() {}
 
@@ -39,35 +41,35 @@
      * @see        ViewType
      */
     public static @ViewType int getViewTypeForItem(ListItem item) {
-        if (item instanceof ViewListItem) return ListUtils.CUSTOM_VIEW;
+        if (item instanceof ViewListItem) return ViewType.CUSTOM_VIEW;
         if (item instanceof DateListItem) {
             if (item instanceof OfflineItemListItem) {
                 OfflineItemListItem offlineItem = (OfflineItemListItem) item;
 
-                if (offlineItem.item.isSuggested) return ListUtils.PREFETCH;
+                if (offlineItem.item.isSuggested) return ViewType.PREFETCH;
                 if (offlineItem.item.state == OfflineItemState.IN_PROGRESS) {
-                    return ListUtils.IN_PROGRESS;
+                    return ViewType.IN_PROGRESS;
                 }
 
                 switch (offlineItem.item.filter) {
                     case OfflineItemFilter.FILTER_VIDEO:
-                        return ListUtils.VIDEO;
+                        return ViewType.VIDEO;
                     case OfflineItemFilter.FILTER_IMAGE:
-                        return ListUtils.IMAGE;
-                    case OfflineItemFilter.FILTER_PAGE:
-                    case OfflineItemFilter.FILTER_AUDIO:
-                    case OfflineItemFilter.FILTER_OTHER:
-                    case OfflineItemFilter.FILTER_DOCUMENT:
+                        return ViewType.IMAGE;
+                    // case OfflineItemFilter.FILTER_PAGE:
+                    // case OfflineItemFilter.FILTER_AUDIO:
+                    // case OfflineItemFilter.FILTER_OTHER:
+                    // case OfflineItemFilter.FILTER_DOCUMENT:
                     default:
-                        return ListUtils.GENERIC;
+                        return ViewType.GENERIC;
                 }
             } else {
-                return ListUtils.DATE;
+                return ViewType.DATE;
             }
         }
 
         assert false;
-        return ListUtils.GENERIC;
+        return ViewType.GENERIC;
     }
 
     /**
@@ -80,6 +82,6 @@
      * @see             GridLayoutManager.SpanSizeLookup
      */
     public static int getSpanSize(ListItem item, int spanCount) {
-        return getViewTypeForItem(item) == IMAGE ? 1 : spanCount;
+        return getViewTypeForItem(item) == ViewType.IMAGE ? 1 : spanCount;
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java
index a15789d2..2e49528 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/snackbars/UndoUiUtils.java
@@ -19,20 +19,15 @@
 
     /** @return A {@link String} representing the title text for an undo snackbar. */
     public static String getTitleFor(Collection<OfflineItem> items) {
-        if (items.size() == 1) {
-            return items.iterator().next().title;
-        } else {
-            return String.format(Locale.getDefault(), "%d", items.size());
-        }
+        return items.size() == 1 ? items.iterator().next().title
+                                 : String.format(Locale.getDefault(), "%d", items.size());
     }
 
     /** @return A {@link String} representing the template text for an undo snackbar. */
     public static String getTemplateTextFor(Collection<OfflineItem> items) {
         Context context = ContextUtils.getApplicationContext();
-        if (items.size() == 1) {
-            return context.getString(R.string.undo_bar_delete_message);
-        } else {
-            return context.getString(R.string.undo_bar_multiple_downloads_delete_message);
-        }
+        return items.size() == 1
+                ? context.getString(R.string.undo_bar_delete_message)
+                : context.getString(R.string.undo_bar_multiple_downloads_delete_message);
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
index aee8dc4f..1516ea28 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
@@ -189,7 +189,7 @@
             case OfflineItemState.FAILED:
             case OfflineItemState.PAUSED:
                 return true;
-            case OfflineItemState.CANCELLED:
+            // OfflineItemState.CANCELLED
             default:
                 return false;
         }
@@ -200,11 +200,9 @@
             case OfflineItemState.IN_PROGRESS:
             case OfflineItemState.PENDING:
                 return true;
-            case OfflineItemState.COMPLETE:
-            case OfflineItemState.INTERRUPTED:
-            case OfflineItemState.FAILED:
-            case OfflineItemState.PAUSED:
-            case OfflineItemState.CANCELLED:
+            // OfflineItemState.COMPLETE, OfflineItemState.INTERRUPTED,
+            // OfflineItemState.FAILED, OfflineItemState.PAUSED,
+            // OfflineItemState.CANCELLED
             default:
                 return false;
         }
@@ -217,9 +215,8 @@
             case OfflineItemState.INTERRUPTED:
             case OfflineItemState.PAUSED:
                 return true;
-            case OfflineItemState.FAILED:
-            case OfflineItemState.COMPLETE:
-            case OfflineItemState.CANCELLED:
+            // OfflineItemState.FAILED, OfflineItemState.COMPLETE,
+            // OfflineItemState.CANCELLED
             default:
                 return false;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendItems.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendItems.java
index be7692d2..ab708f5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendItems.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/BackendItems.java
@@ -32,7 +32,7 @@
         HashSet<String> filePaths = new HashSet<>();
         for (DownloadHistoryItemWrapper item : this) {
             String path = item.getFilePath();
-            if (item.isVisibleToUser(DownloadFilter.FILTER_ALL) && !filePaths.contains(path)) {
+            if (item.isVisibleToUser(DownloadFilter.Type.ALL) && !filePaths.contains(path)) {
                 totalSize += item.getFileSize();
             }
             if (path != null && !path.isEmpty()) filePaths.add(path);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java
index 933db93f..9ca6e3f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadFilter.java
@@ -11,6 +11,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.UrlConstants;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Locale;
 
 /**
@@ -20,18 +22,18 @@
 public class DownloadFilter {
     // These statics are used for UMA logging. Please update the AndroidDownloadFilterType enum in
     // histograms.xml if these change.
-    @IntDef({FILTER_ALL, FILTER_PAGE, FILTER_VIDEO, FILTER_AUDIO, FILTER_IMAGE, FILTER_DOCUMENT,
-            FILTER_OTHER, FILTER_BOUNDARY})
-    public @interface Type {}
-
-    public static final int FILTER_ALL = 0;
-    public static final int FILTER_PAGE = 1;
-    public static final int FILTER_VIDEO = 2;
-    public static final int FILTER_AUDIO = 3;
-    public static final int FILTER_IMAGE = 4;
-    public static final int FILTER_DOCUMENT = 5;
-    public static final int FILTER_OTHER = 6;
-    public static final int FILTER_BOUNDARY = 7;
+    @IntDef({Type.ALL, Type.PAGE, Type.VIDEO, Type.AUDIO, Type.IMAGE, Type.DOCUMENT, Type.OTHER})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {
+        int ALL = 0;
+        int PAGE = 1;
+        int VIDEO = 2;
+        int AUDIO = 3;
+        int IMAGE = 4;
+        int DOCUMENT = 5;
+        int OTHER = 6;
+        int NUM_ENTRIES = 7;
+    }
 
     private static final String MIMETYPE_VIDEO = "video";
     private static final String MIMETYPE_AUDIO = "audio";
@@ -81,18 +83,17 @@
      * @return The URL representing the filter.
      */
     public static String getUrlForFilter(@Type int filter) {
-        if (filter == FILTER_ALL) {
-            return UrlConstants.DOWNLOADS_URL;
-        }
-        return UrlConstants.DOWNLOADS_FILTER_URL + filter;
+        return filter == Type.ALL ? UrlConstants.DOWNLOADS_URL
+                                  : UrlConstants.DOWNLOADS_FILTER_URL + filter;
     }
 
     /**
      * @return The filter that the given URL represents.
      */
     public static @Type int getFilterFromUrl(String url) {
-        if (TextUtils.isEmpty(url) || UrlConstants.DOWNLOADS_HOST.equals(url)) return FILTER_ALL;
-        int result = FILTER_ALL;
+        if (TextUtils.isEmpty(url) || UrlConstants.DOWNLOADS_HOST.equals(url)) return Type.ALL;
+        @Type
+        int result = Type.ALL;
         if (url.startsWith(UrlConstants.DOWNLOADS_FILTER_URL)) {
             try {
                 result = Integer
@@ -106,21 +107,21 @@
 
     /** Identifies the type of file represented by the given MIME type string. */
     public static @Type int fromMimeType(String mimeType) {
-        if (TextUtils.isEmpty(mimeType)) return DownloadFilter.FILTER_OTHER;
+        if (TextUtils.isEmpty(mimeType)) return Type.OTHER;
 
         String[] pieces = mimeType.toLowerCase(Locale.getDefault()).split("/");
-        if (pieces.length != 2) return DownloadFilter.FILTER_OTHER;
+        if (pieces.length != 2) return Type.OTHER;
 
         if (MIMETYPE_VIDEO.equals(pieces[0])) {
-            return DownloadFilter.FILTER_VIDEO;
+            return Type.VIDEO;
         } else if (MIMETYPE_AUDIO.equals(pieces[0])) {
-            return DownloadFilter.FILTER_AUDIO;
+            return Type.AUDIO;
         } else if (MIMETYPE_IMAGE.equals(pieces[0])) {
-            return DownloadFilter.FILTER_IMAGE;
+            return Type.IMAGE;
         } else if (MIMETYPE_DOCUMENT.equals(pieces[0])) {
-            return DownloadFilter.FILTER_DOCUMENT;
+            return Type.DOCUMENT;
         } else {
-            return DownloadFilter.FILTER_OTHER;
+            return Type.OTHER;
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
index b199d0b..704a914 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapter.java
@@ -204,7 +204,7 @@
     private final List<DownloadItemView> mViews = new ArrayList<>();
 
     private BackendProvider mBackendProvider;
-    private @DownloadFilter.Type int mFilter = DownloadFilter.FILTER_ALL;
+    private @DownloadFilter.Type int mFilter = DownloadFilter.Type.ALL;
     private String mSearchQuery = EMPTY_QUERY;
     // TODO(xingliu): Remove deprecated storage info. See https://crbug/853260.
     private SpaceDisplay mSpaceDisplay;
@@ -276,22 +276,22 @@
         if (list.isInitialized()) return;
         assert list.size() == 0;
 
-        int[] itemCounts = new int[DownloadFilter.FILTER_BOUNDARY];
-        int[] viewedItemCounts = new int[DownloadFilter.FILTER_BOUNDARY];
+        int[] itemCounts = new int[DownloadFilter.Type.NUM_ENTRIES];
+        int[] viewedItemCounts = new int[DownloadFilter.Type.NUM_ENTRIES];
 
         for (DownloadItem item : result) {
             DownloadItemWrapper wrapper = createDownloadItemWrapper(item);
             if (addDownloadHistoryItemWrapper(wrapper)
-                    && wrapper.isVisibleToUser(DownloadFilter.FILTER_ALL)) {
+                    && wrapper.isVisibleToUser(DownloadFilter.Type.ALL)) {
                 itemCounts[wrapper.getFilterType()]++;
 
                 if (DownloadUtils.isDownloadViewed(wrapper.getItem()))
                     viewedItemCounts[wrapper.getFilterType()]++;
-                if (!isOffTheRecord && wrapper.getFilterType() == DownloadFilter.FILTER_OTHER) {
+                if (!isOffTheRecord && wrapper.getFilterType() == DownloadFilter.Type.OTHER) {
                     RecordHistogram.recordEnumeratedHistogram(
                             "Android.DownloadManager.OtherExtensions.InitialCount",
                             wrapper.getFileExtensionType(),
-                            DownloadHistoryItemWrapper.FILE_EXTENSION_BOUNDARY);
+                            DownloadHistoryItemWrapper.FileExtension.NUM_ENTRIES);
                 }
             }
         }
@@ -760,26 +760,26 @@
 
     private void recordDownloadCountHistograms(int[] itemCounts, int[] viewedItemCounts) {
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Audio",
-                itemCounts[DownloadFilter.FILTER_AUDIO]);
+                itemCounts[DownloadFilter.Type.AUDIO]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Document",
-                itemCounts[DownloadFilter.FILTER_DOCUMENT]);
+                itemCounts[DownloadFilter.Type.DOCUMENT]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Image",
-                itemCounts[DownloadFilter.FILTER_IMAGE]);
+                itemCounts[DownloadFilter.Type.IMAGE]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Other",
-                itemCounts[DownloadFilter.FILTER_OTHER]);
+                itemCounts[DownloadFilter.Type.OTHER]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Video",
-                itemCounts[DownloadFilter.FILTER_VIDEO]);
+                itemCounts[DownloadFilter.Type.VIDEO]);
 
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Viewed.Audio",
-                viewedItemCounts[DownloadFilter.FILTER_AUDIO]);
+                viewedItemCounts[DownloadFilter.Type.AUDIO]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Viewed.Document",
-                viewedItemCounts[DownloadFilter.FILTER_DOCUMENT]);
+                viewedItemCounts[DownloadFilter.Type.DOCUMENT]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Viewed.Image",
-                viewedItemCounts[DownloadFilter.FILTER_IMAGE]);
+                viewedItemCounts[DownloadFilter.Type.IMAGE]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Viewed.Other",
-                viewedItemCounts[DownloadFilter.FILTER_OTHER]);
+                viewedItemCounts[DownloadFilter.Type.OTHER]);
         RecordHistogram.recordCountHistogram("Android.DownloadManager.InitialCount.Viewed.Video",
-                viewedItemCounts[DownloadFilter.FILTER_VIDEO]);
+                viewedItemCounts[DownloadFilter.Type.VIDEO]);
     }
 
     private void recordTotalDownloadCountHistogram() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
index dd7085d..1f5b2b72 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadHistoryItemWrapper.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.download.ui;
 
 import android.content.ComponentName;
+import android.support.annotation.IntDef;
 import android.text.TextUtils;
 
 import org.chromium.base.VisibleForTesting;
@@ -27,6 +28,8 @@
 import org.chromium.components.url_formatter.UrlFormatter;
 
 import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Locale;
@@ -34,40 +37,47 @@
 
 /** Wraps different classes that contain information about downloads. */
 public abstract class DownloadHistoryItemWrapper extends TimedItem {
-    public static final Integer FILE_EXTENSION_OTHER = 0;
-    public static final Integer FILE_EXTENSION_APK = 1;
-    public static final Integer FILE_EXTENSION_CSV = 2;
-    public static final Integer FILE_EXTENSION_DOC = 3;
-    public static final Integer FILE_EXTENSION_DOCX = 4;
-    public static final Integer FILE_EXTENSION_EXE = 5;
-    public static final Integer FILE_EXTENSION_PDF = 6;
-    public static final Integer FILE_EXTENSION_PPT = 7;
-    public static final Integer FILE_EXTENSION_PPTX = 8;
-    public static final Integer FILE_EXTENSION_PSD = 9;
-    public static final Integer FILE_EXTENSION_RTF = 10;
-    public static final Integer FILE_EXTENSION_TXT = 11;
-    public static final Integer FILE_EXTENSION_XLS = 12;
-    public static final Integer FILE_EXTENSION_XLSX = 13;
-    public static final Integer FILE_EXTENSION_ZIP = 14;
-    public static final Integer FILE_EXTENSION_BOUNDARY = 15;
+    @IntDef({FileExtension.OTHER, FileExtension.APK, FileExtension.CSV, FileExtension.DOC,
+            FileExtension.DOCX, FileExtension.EXE, FileExtension.PDF, FileExtension.PPT,
+            FileExtension.PPTX, FileExtension.PSD, FileExtension.RTF, FileExtension.TXT,
+            FileExtension.XLS, FileExtension.XLSX, FileExtension.ZIP})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FileExtension {
+        int OTHER = 0;
+        int APK = 1;
+        int CSV = 2;
+        int DOC = 3;
+        int DOCX = 4;
+        int EXE = 5;
+        int PDF = 6;
+        int PPT = 7;
+        int PPTX = 8;
+        int PSD = 9;
+        int RTF = 10;
+        int TXT = 11;
+        int XLS = 12;
+        int XLSX = 13;
+        int ZIP = 14;
+        int NUM_ENTRIES = 15;
+    }
 
     private static final Map<String, Integer> EXTENSIONS_MAP;
     static {
         Map<String, Integer> extensions = new HashMap<>();
-        extensions.put("apk", FILE_EXTENSION_APK);
-        extensions.put("csv", FILE_EXTENSION_CSV);
-        extensions.put("doc", FILE_EXTENSION_DOC);
-        extensions.put("docx", FILE_EXTENSION_DOCX);
-        extensions.put("exe", FILE_EXTENSION_EXE);
-        extensions.put("pdf", FILE_EXTENSION_PDF);
-        extensions.put("ppt", FILE_EXTENSION_PPT);
-        extensions.put("pptx", FILE_EXTENSION_PPTX);
-        extensions.put("psd", FILE_EXTENSION_PSD);
-        extensions.put("rtf", FILE_EXTENSION_RTF);
-        extensions.put("txt", FILE_EXTENSION_TXT);
-        extensions.put("xls", FILE_EXTENSION_XLS);
-        extensions.put("xlsx", FILE_EXTENSION_XLSX);
-        extensions.put("zip", FILE_EXTENSION_ZIP);
+        extensions.put("apk", FileExtension.APK);
+        extensions.put("csv", FileExtension.CSV);
+        extensions.put("doc", FileExtension.DOC);
+        extensions.put("docx", FileExtension.DOCX);
+        extensions.put("exe", FileExtension.EXE);
+        extensions.put("pdf", FileExtension.PDF);
+        extensions.put("ppt", FileExtension.PPT);
+        extensions.put("pptx", FileExtension.PPTX);
+        extensions.put("psd", FileExtension.PSD);
+        extensions.put("rtf", FileExtension.RTF);
+        extensions.put("txt", FileExtension.TXT);
+        extensions.put("xls", FileExtension.XLS);
+        extensions.put("xlsx", FileExtension.XLSX);
+        extensions.put("zip", FileExtension.ZIP);
 
         EXTENSIONS_MAP = Collections.unmodifiableMap(extensions);
     }
@@ -107,7 +117,7 @@
     /** @return Whether this download should be shown to the user. */
     boolean isVisibleToUser(@DownloadFilter.Type int filter) {
         if (isDeletionPending()) return false;
-        return filter == getFilterType() || filter == DownloadFilter.FILTER_ALL;
+        return filter == getFilterType() || filter == DownloadFilter.Type.ALL;
     }
 
     /** Called when this download should be shared. */
@@ -234,23 +244,23 @@
     protected void recordOpenSuccess() {
         RecordUserAction.record("Android.DownloadManager.Item.OpenSucceeded");
         RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.Item.OpenSucceeded",
-                getFilterType(), DownloadFilter.FILTER_BOUNDARY);
+                getFilterType(), DownloadFilter.Type.NUM_ENTRIES);
 
-        if (getFilterType() == DownloadFilter.FILTER_OTHER) {
+        if (getFilterType() == DownloadFilter.Type.OTHER) {
             RecordHistogram.recordEnumeratedHistogram(
-                    "Android.DownloadManager.OtherExtensions.OpenSucceeded",
-                    getFileExtensionType(), FILE_EXTENSION_BOUNDARY);
+                    "Android.DownloadManager.OtherExtensions.OpenSucceeded", getFileExtensionType(),
+                    FileExtension.NUM_ENTRIES);
         }
     }
 
     protected void recordOpenFailure() {
         RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.Item.OpenFailed",
-                getFilterType(), DownloadFilter.FILTER_BOUNDARY);
+                getFilterType(), DownloadFilter.Type.NUM_ENTRIES);
 
-        if (getFilterType() == DownloadFilter.FILTER_OTHER) {
+        if (getFilterType() == DownloadFilter.Type.OTHER) {
             RecordHistogram.recordEnumeratedHistogram(
-                    "Android.DownloadManager.OtherExtensions.OpenFailed",
-                    getFileExtensionType(), FILE_EXTENSION_BOUNDARY);
+                    "Android.DownloadManager.OtherExtensions.OpenFailed", getFileExtensionType(),
+                    FileExtension.NUM_ENTRIES);
         }
     }
 
@@ -329,7 +339,7 @@
             if (mFileExtensionType == null) {
                 int extensionIndex = getFilePath().lastIndexOf(".");
                 if (extensionIndex == -1 || extensionIndex == getFilePath().length() - 1) {
-                    mFileExtensionType = FILE_EXTENSION_OTHER;
+                    mFileExtensionType = FileExtension.OTHER;
                     return mFileExtensionType;
                 }
 
@@ -339,7 +349,7 @@
                     mFileExtensionType = EXTENSIONS_MAP.get(
                             extension.toLowerCase(Locale.getDefault()));
                 } else {
-                    mFileExtensionType = FILE_EXTENSION_OTHER;
+                    mFileExtensionType = FileExtension.OTHER;
                 }
             }
 
@@ -361,7 +371,8 @@
             if (DownloadUtils.openFile(getFile(), getMimeType(),
                         mItem.getDownloadInfo().getDownloadGuid(), isOffTheRecord(),
                         mItem.getDownloadInfo().getOriginalUrl(),
-                        mItem.getDownloadInfo().getReferrer(), DownloadMetrics.DOWNLOAD_HOME)) {
+                        mItem.getDownloadInfo().getReferrer(),
+                        DownloadMetrics.DownloadOpenSource.DOWNLOAD_HOME)) {
                 recordOpenSuccess();
                 DownloadMetrics.recordDownloadViewRetentionTime(
                         getMimeType(), getItem().getStartTime());
@@ -527,9 +538,9 @@
         }
 
         @Override
-        public int getFilterType() {
+        public @DownloadFilter.Type int getFilterType() {
             // TODO(shaktisahu): Make DownloadFilter unnecessary.
-            return isOfflinePage() ? DownloadFilter.FILTER_PAGE
+            return isOfflinePage() ? DownloadFilter.Type.PAGE
                                    : DownloadFilter.fromMimeType(mItem.mimeType);
         }
 
@@ -546,7 +557,7 @@
         @Override
         public int getFileExtensionType() {
             // TODO(shaktisahu): Fix this.
-            return FILE_EXTENSION_OTHER;
+            return FileExtension.OTHER;
         }
 
         @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
index 57e6a8ab..8d76460 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadItemView.java
@@ -40,6 +40,8 @@
 import org.chromium.components.variations.VariationsAssociatedData;
 import org.chromium.ui.UiUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -53,17 +55,18 @@
 
     // Please treat this list as append only and keep it in sync with
     // Android.DownloadManager.List.View.Actions in enums.xml.
-    @IntDef({VIEW_ACTION_OPEN, VIEW_ACTION_RESUME, VIEW_ACTION_PAUSE, VIEW_ACTION_CANCEL,
-            VIEW_ACTION_MENU_SHARE, VIEW_ACTION_MENU_DELETE})
-    public @interface ViewAction {}
-
-    private static final int VIEW_ACTION_OPEN = 0;
-    private static final int VIEW_ACTION_RESUME = 1;
-    private static final int VIEW_ACTION_PAUSE = 2;
-    private static final int VIEW_ACTION_CANCEL = 3;
-    private static final int VIEW_ACTION_MENU_SHARE = 4;
-    private static final int VIEW_ACTION_MENU_DELETE = 5;
-    private static final int VIEW_ACTION_BOUNDARY = 6;
+    @IntDef({ViewAction.OPEN, ViewAction.RESUME, ViewAction.PAUSE, ViewAction.CANCEL,
+            ViewAction.MENU_SHARE, ViewAction.MENU_DELETE})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface ViewAction {
+        int OPEN = 0;
+        int RESUME = 1;
+        int PAUSE = 2;
+        int CANCEL = 3;
+        int MENU_SHARE = 4;
+        int MENU_DELETE = 5;
+        int NUM_ENTRIES = 6;
+    }
 
     /**
      * Set based on Chrome Variations to determine whether or not to show the "more" menu button on
@@ -138,10 +141,10 @@
     @Override
     public void onItemSelected(Item item) {
         if (item.getTextId() == R.string.share) {
-            recordViewActionHistogram(VIEW_ACTION_MENU_SHARE);
+            recordViewActionHistogram(ViewAction.MENU_SHARE);
             mItem.share();
         } else if (item.getTextId() == R.string.delete) {
-            recordViewActionHistogram(VIEW_ACTION_MENU_DELETE);
+            recordViewActionHistogram(ViewAction.MENU_DELETE);
             mItem.startRemove();
             RecordUserAction.record("Android.DownloadManager.RemoveItem");
         }
@@ -170,15 +173,15 @@
         mMoreButton.setDelegate(this);
         mPauseResumeButton.setOnClickListener(view -> {
             if (mItem.isPaused()) {
-                recordViewActionHistogram(VIEW_ACTION_RESUME);
+                recordViewActionHistogram(ViewAction.RESUME);
                 mItem.resume();
             } else if (!mItem.isComplete()) {
-                recordViewActionHistogram(VIEW_ACTION_PAUSE);
+                recordViewActionHistogram(ViewAction.PAUSE);
                 mItem.pause();
             }
         });
         mCancelButton.setOnClickListener(view -> {
-            recordViewActionHistogram(VIEW_ACTION_CANCEL);
+            recordViewActionHistogram(ViewAction.CANCEL);
             mItem.cancel();
         });
     }
@@ -249,8 +252,7 @@
         // immediately if the thumbnail is cached or asynchronously if it has to be fetched from a
         // remote source.
         mThumbnailBitmap = null;
-        if (item.isOfflinePage()
-                || (fileType == DownloadFilter.FILTER_IMAGE && item.isComplete())) {
+        if (item.isOfflinePage() || (fileType == DownloadFilter.Type.IMAGE && item.isComplete())) {
             thumbnailProvider.getThumbnail(this);
         } else {
             // TODO(dfalcantara): Get thumbnails for audio and video files when possible.
@@ -337,18 +339,14 @@
     @Override
     public void onClick() {
         if (mItem != null && mItem.isComplete()) {
-            recordViewActionHistogram(VIEW_ACTION_OPEN);
+            recordViewActionHistogram(ViewAction.OPEN);
             mItem.open();
         }
     }
 
     @Override
     public boolean onLongClick(View view) {
-        if (mItem != null && mItem.isComplete()) {
-            return super.onLongClick(view);
-        } else {
-            return true;
-        }
+        return mItem != null && mItem.isComplete() ? super.onLongClick(view) : true;
     }
 
     @Override
@@ -407,7 +405,7 @@
 
     private static void recordViewActionHistogram(@ViewAction int action) {
         RecordHistogram.recordEnumeratedHistogram(
-                "Android.DownloadManager.List.View.Action", action, VIEW_ACTION_BOUNDARY);
+                "Android.DownloadManager.List.View.Action", action, ViewAction.NUM_ENTRIES);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
index 82d8e5e..e9e9594 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/DownloadManagerUi.java
@@ -452,8 +452,8 @@
             mNativePage.onStateChange(DownloadFilter.getUrlForFilter(filter));
         }
 
-        RecordHistogram.recordEnumeratedHistogram("Android.DownloadManager.Filter", filter,
-                DownloadFilter.FILTER_BOUNDARY);
+        RecordHistogram.recordEnumeratedHistogram(
+                "Android.DownloadManager.Filter", filter, DownloadFilter.Type.NUM_ENTRIES);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/LoadingStateDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/LoadingStateDelegate.java
index 9c947caf..5eb9afc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/LoadingStateDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/LoadingStateDelegate.java
@@ -19,7 +19,7 @@
     private static final int ALL_LOADED = 0b111;
 
     private int mLoadingState;
-    private @DownloadFilter.Type int mPendingFilter = DownloadFilter.FILTER_ALL;
+    private @DownloadFilter.Type int mPendingFilter = DownloadFilter.Type.ALL;
 
     /** @param offTheRecord Whether this delegate needs to consider incognito. */
     public LoadingStateDelegate(boolean offTheRecord) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java
index 2535bdbd..8338e3e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/HistoryReportJniBridge.java
@@ -101,6 +101,12 @@
     }
 
     @Override
+    public void clearUsageReports() {
+        if (!isInitialized()) return;
+        nativeClearUsageReports(mNativeHistoryReportJniBridge);
+    }
+
+    @Override
     public boolean addHistoricVisitsToUsageReportsBuffer() {
         if (!isInitialized()) return false;
         return nativeAddHistoricVisitsToUsageReportsBuffer(mNativeHistoryReportJniBridge);
@@ -171,6 +177,7 @@
             int batchSize);
     private native void nativeRemoveUsageReports(long nativeHistoryReportJniBridge,
             String[] reportIds);
+    private native void nativeClearUsageReports(long nativeHistoryReportJniBridge);
     private native boolean nativeAddHistoricVisitsToUsageReportsBuffer(
             long nativeHistoryReportJniBridge);
     private native String nativeDump(long nativeHistoryReportJniBridge);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/SearchJniBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/SearchJniBridge.java
index 2ba996d..a8e8c86 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/SearchJniBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/SearchJniBridge.java
@@ -45,6 +45,11 @@
     void removeUsageReports(UsageReport[] reports);
 
     /**
+     * Clear the buffer of usage reports.
+     */
+    void clearUsageReports();
+
+    /**
      * Adds all the historic visits to the usage report buffer.
      *
      * Should be done only once.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
index fe6d61e7..ad96cd7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DuplicateDownloadInfoBar.java
@@ -89,7 +89,7 @@
                     protected void onPostExecute(Boolean fileExists) {
                         if (fileExists) {
                             DownloadUtils.openFile(file, mimeType, null, mIsIncognito, null, null,
-                                    DownloadMetrics.INFO_BAR);
+                                    DownloadMetrics.DownloadOpenSource.INFO_BAR);
                         } else {
                             DownloadManagerService.openDownloadsPage(
                                     ContextUtils.getApplicationContext());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index d9455e1..858b308f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -35,7 +35,6 @@
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.ChromeStrictMode;
 import org.chromium.chrome.browser.ChromeSwitches;
-import org.chromium.chrome.browser.ClassRegister;
 import org.chromium.chrome.browser.FileProviderHelper;
 import org.chromium.chrome.browser.crash.LogcatExtractionRunnable;
 import org.chromium.chrome.browser.download.DownloadManagerService;
@@ -399,7 +398,6 @@
         AppHooks.get().registerPolicyProviders(CombinedPolicyProvider.get());
 
         SpeechRecognition.initialize(mApplication);
-        ClassRegister.get().registerContentClassFactory();
     }
 
     private void onFinishNativeInitialization() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
index e75acce..46491df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.payments;
 
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.content_public.browser.WebContents;
 
 /**
  * A class used to record journey metrics for the Payment Request feature.
@@ -19,10 +20,10 @@
     private boolean mWasPaymentRequestTriggered;
     private boolean mHasRecorded;
 
-    public JourneyLogger(boolean isIncognito, String url) {
+    public JourneyLogger(boolean isIncognito, WebContents webContents) {
         // Note that this pointer could leak the native object. The called must call destroy() to
         // ensure that the native object is destroyed.
-        mJourneyLoggerAndroid = nativeInitJourneyLoggerAndroid(isIncognito, url);
+        mJourneyLoggerAndroid = nativeInitJourneyLoggerAndroid(isIncognito, webContents);
     }
 
     /** Will destroy the native object. This class shouldn't be used afterwards. */
@@ -176,7 +177,8 @@
         }
     }
 
-    private native long nativeInitJourneyLoggerAndroid(boolean isIncognito, String url);
+    private native long nativeInitJourneyLoggerAndroid(
+            boolean isIncognito, WebContents webContents);
     private native void nativeDestroy(long nativeJourneyLoggerAndroid);
     private native void nativeSetNumberOfSuggestionsShown(long nativeJourneyLoggerAndroid,
             int section, int number, boolean hasCompleteSuggestion);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index f4da574..a73f044 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -404,7 +404,7 @@
                 new AddressEditor(/*emailFieldIncluded=*/false, /*saveToDisk=*/!mIsIncognito);
         mCardEditor = new CardEditor(mWebContents, mAddressEditor, sObserverForTest);
 
-        mJourneyLogger = new JourneyLogger(mIsIncognito, mWebContents.getLastCommittedUrl());
+        mJourneyLogger = new JourneyLogger(mIsIncognito, mWebContents);
 
         if (sCanMakePaymentQueries == null) sCanMakePaymentQueries = new ArrayMap<>();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
index 82be72393..cf3a9ebb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -106,6 +106,7 @@
 
     private static final String HOME_PAGE_BUTTON_FORCE_ENABLED_KEY =
             "home_page_button_force_enabled";
+    private static final String HOMEPAGE_TILE_ENABLED_KEY = "homepage_tile_enabled";
 
     private static final String NTP_BUTTON_ENABLED_KEY = "ntp_button_enabled";
 
@@ -469,6 +470,22 @@
     }
 
     /**
+     * Set whether or not the homepage tile will be shown.
+     * @param isEnabled If homepage tile is enabled.
+     */
+    public void setHomepageTileEnabled(boolean isEnabled) {
+        writeBoolean(HOMEPAGE_TILE_ENABLED_KEY, isEnabled);
+    }
+
+    /**
+     * Get whether or not the homepage tile is enabled.
+     * @return True if the homepage tile is enabled.
+     */
+    public boolean isHomepageTileEnabled() {
+        return mSharedPreferences.getBoolean(HOMEPAGE_TILE_ENABLED_KEY, false);
+    }
+
+    /**
      * Clean up unused Chrome Home preferences.
      */
     public void clearObsoleteChromeHomePrefs() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java
index 53053f41..94567b8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/MostVisitedSitesBridge.java
@@ -40,7 +40,8 @@
         mNativeMostVisitedSitesBridge = nativeInit(profile);
         // The first tile replaces is replaced with homepage tile if NTPButton is enabled. Setting
         // a homepage client to provide Java side information.
-        if (FeatureUtilities.isNewTabPageButtonEnabled()) {
+        if (FeatureUtilities.isNewTabPageButtonEnabled()
+                && FeatureUtilities.isHomepageTileEnabled()) {
             nativeSetHomepageClient(mNativeMostVisitedSitesBridge, new HomepageClient() {
                 @Override
                 public boolean isHomepageEnabled() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
index 5cef527..7b6a7850 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
@@ -265,12 +265,13 @@
     private void setDownloadThumbnail() {
         assert mSuggestion.isDownload();
         if (!mSuggestion.isAssetDownload()) {
-            setThumbnailFromFileType(DownloadFilter.FILTER_PAGE);
+            setThumbnailFromFileType(DownloadFilter.Type.PAGE);
             return;
         }
 
+        @DownloadFilter.Type
         int fileType = DownloadFilter.fromMimeType(mSuggestion.getAssetDownloadMimeType());
-        if (fileType == DownloadFilter.FILTER_IMAGE) {
+        if (fileType == DownloadFilter.Type.IMAGE) {
             // For image downloads, attempt to fetch a thumbnail.
             ImageFetcher.DownloadThumbnailRequest thumbnailRequest =
                     mImageFetcher.makeDownloadThumbnailRequest(mSuggestion, mThumbnailSize);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java
index 1215d57..32e5f92 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsNavigationDelegateImpl.java
@@ -113,7 +113,7 @@
                     || windowOpenDisposition == WindowOpenDisposition.NEW_BACKGROUND_TAB;
             DownloadUtils.openFile(article.getAssetDownloadFile(),
                     article.getAssetDownloadMimeType(), article.getAssetDownloadGuid(), false, null,
-                    null, DownloadMetrics.NEW_TAP_PAGE);
+                    null, DownloadMetrics.DownloadOpenSource.NEW_TAP_PAGE);
             return;
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java
index ee1f40f4b..64c891e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarCoordinator.java
@@ -20,6 +20,7 @@
 import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.BottomToolbarViewBinder.ViewHolder;
+import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.resources.ResourceManager;
 
 /**
@@ -95,13 +96,14 @@
             OnClickListener tabSwitcherListener, OnClickListener searchAcceleratorListener,
             OnClickListener homeButtonListener, OnTouchListener menuButtonListener,
             TabModelSelector tabModelSelector, OverviewModeBehavior overviewModeBehavior,
-            ContextualSearchManager contextualSearchManager) {
+            ContextualSearchManager contextualSearchManager, WindowAndroid windowAndroid) {
         mMediator.setSearchAcceleratorListener(searchAcceleratorListener);
         mMediator.setLayoutManager(layoutManager);
         mMediator.setResourceManager(resourceManager);
         mMediator.setOverviewModeBehavior(overviewModeBehavior);
         mMediator.setToolbarSwipeHandler(layoutManager.getToolbarSwipeHandler());
         mMediator.setContextualSearchManager(contextualSearchManager);
+        mMediator.setWindowAndroid(windowAndroid);
 
         mTabSwitcherButtonCoordinator.setTabSwitcherListener(tabSwitcherListener);
         mTabSwitcherButtonCoordinator.setTabModelSelector(tabModelSelector);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
index 3f314ea..5fb8bc92f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarMediator.java
@@ -20,6 +20,8 @@
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
 import org.chromium.chrome.browser.gsa.GSAContextDisplaySelection;
+import org.chromium.ui.base.WindowAndroid;
+import org.chromium.ui.base.WindowAndroid.KeyboardVisibilityListener;
 import org.chromium.ui.resources.ResourceManager;
 
 import javax.annotation.Nullable;
@@ -29,8 +31,8 @@
  * coordinators, running most of the business logic associated with the bottom toolbar, and updating
  * the model accordingly.
  */
-class BottomToolbarMediator
-        implements FullscreenListener, OverviewModeObserver, ContextualSearchObserver {
+class BottomToolbarMediator implements ContextualSearchObserver, FullscreenListener,
+                                       KeyboardVisibilityListener, OverviewModeObserver {
     /** The model for the bottom toolbar that holds all of its state. */
     private BottomToolbarModel mModel;
 
@@ -43,6 +45,12 @@
     /** The manager for Contextual Search to observe appearance/disappearance of the feature. */
     private ContextualSearchManager mContextualSearchManger;
 
+    /** A {@link WindowAndroid} for watching keyboard visibility events. */
+    private WindowAndroid mWindowAndroid;
+
+    /** The previous height of the bottom toolbar. */
+    private int mBottomToolbarHeightBeforeHide;
+
     /**
      * Build a new mediator that handles events from outside the bottom toolbar.
      * @param model The {@link BottomToolbarModel} that holds all the state for the bottom toolbar.
@@ -76,6 +84,7 @@
         mFullscreenManager.removeListener(this);
         if (mContextualSearchManger != null) mContextualSearchManger.removeObserver(this);
         if (mOverviewModeBehavior != null) mOverviewModeBehavior.removeOverviewModeObserver(this);
+        if (mWindowAndroid != null) mWindowAndroid.removeKeyboardVisibilityListener(this);
     }
 
     @Override
@@ -130,6 +139,24 @@
         mModel.setValue(BottomToolbarModel.SEARCH_ACCELERATOR_LISTENER, searchAcceleratorListener);
     }
 
+    @Override
+    public void keyboardVisibilityChanged(boolean isShowing) {
+        // The toolbars are force shown when the keyboard is visible, so we can blindly set
+        // the bottom toolbar view to visible or invisible regardless of the previous state.
+        ChromeFullscreenManager fullscreenManager =
+                mModel.getValue(BottomToolbarModel.LAYOUT_MANAGER).getFullscreenManager();
+        if (isShowing) {
+            mBottomToolbarHeightBeforeHide = fullscreenManager.getBottomControlsHeight();
+            mModel.setValue(BottomToolbarModel.ANDROID_VIEW_VISIBLE, false);
+            mModel.setValue(BottomToolbarModel.COMPOSITED_VIEW_VISIBLE, false);
+            fullscreenManager.setBottomControlsHeight(0);
+        } else {
+            fullscreenManager.setBottomControlsHeight(mBottomToolbarHeightBeforeHide);
+            mModel.setValue(BottomToolbarModel.ANDROID_VIEW_VISIBLE, true);
+            mModel.setValue(BottomToolbarModel.COMPOSITED_VIEW_VISIBLE, true);
+        }
+    }
+
     public void setLayoutManager(LayoutManager layoutManager) {
         mModel.setValue(BottomToolbarModel.LAYOUT_MANAGER, layoutManager);
 
@@ -172,4 +199,12 @@
     public void setToolbarSwipeLayout(ToolbarSwipeLayout layout) {
         mModel.setValue(BottomToolbarModel.TOOLBAR_SWIPE_LAYOUT, layout);
     }
+
+    public void setWindowAndroid(WindowAndroid windowAndroid) {
+        assert mWindowAndroid == null : "#setWindowAndroid should only be called once per toolbar.";
+
+        // Watch for keyboard events so we can hide the bottom toolbar when the keyboard is showing.
+        mWindowAndroid = windowAndroid;
+        mWindowAndroid.addKeyboardVisibilityListener(this);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
index d0642b24..8a59f0d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarModel.java
@@ -22,6 +22,9 @@
     /** Whether the Android view version of the toolbar is visible. */
     public static final BooleanPropertyKey ANDROID_VIEW_VISIBLE = new BooleanPropertyKey();
 
+    /** Whether the composited version of the toolbar is visible. */
+    public static final BooleanPropertyKey COMPOSITED_VIEW_VISIBLE = new BooleanPropertyKey();
+
     /** The click listener for the search accelerator. */
     public static final ObjectPropertyKey<OnClickListener> SEARCH_ACCELERATOR_LISTENER =
             new ObjectPropertyKey<>();
@@ -46,8 +49,8 @@
 
     /** Default constructor. */
     public BottomToolbarModel() {
-        super(Y_OFFSET, ANDROID_VIEW_VISIBLE, SEARCH_ACCELERATOR_LISTENER, LAYOUT_MANAGER,
-                TOOLBAR_SWIPE_LAYOUT, RESOURCE_MANAGER, SEARCH_ACCELERATOR_VISIBLE,
+        super(Y_OFFSET, ANDROID_VIEW_VISIBLE, COMPOSITED_VIEW_VISIBLE, SEARCH_ACCELERATOR_LISTENER,
+                LAYOUT_MANAGER, TOOLBAR_SWIPE_LAYOUT, RESOURCE_MANAGER, SEARCH_ACCELERATOR_VISIBLE,
                 TOOLBAR_SWIPE_HANDLER);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
index 95bc86cf..03f1cac 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarViewBinder.java
@@ -54,6 +54,10 @@
             view.toolbarRoot.setVisibility(model.getValue(BottomToolbarModel.ANDROID_VIEW_VISIBLE)
                             ? View.VISIBLE
                             : View.INVISIBLE);
+        } else if (BottomToolbarModel.COMPOSITED_VIEW_VISIBLE == propertyKey) {
+            view.sceneLayer.setIsVisible(
+                    model.getValue(BottomToolbarModel.COMPOSITED_VIEW_VISIBLE));
+            model.getValue(BottomToolbarModel.LAYOUT_MANAGER).requestUpdate();
         } else if (BottomToolbarModel.SEARCH_ACCELERATOR_LISTENER == propertyKey) {
             view.toolbarRoot.findViewById(R.id.search_button)
                     .setOnClickListener(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index b0508ea..5db08881 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -767,7 +767,7 @@
                     mActivity.getCompositorViewHolder().getLayoutManager(), tabSwitcherClickHandler,
                     searchAcceleratorListener, homeButtonListener, mAppMenuButtonHelper,
                     mTabModelSelector, mOverviewModeBehavior,
-                    mActivity.getContextualSearchManager());
+                    mActivity.getContextualSearchManager(), mActivity.getWindowAndroid());
         }
 
         onNativeLibraryReady();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
index 3b9e5be..83bf6c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -1865,7 +1865,9 @@
         assert mTextureCaptureMode != textureMode;
         mTextureCaptureMode = textureMode;
         if (mTextureCaptureMode) {
-            if (!hideShadowForIncognitoNtp()) mToolbarShadow.setVisibility(VISIBLE);
+            if (!hideShadowForIncognitoNtp() && !hideShadowForInterstitial()) {
+                mToolbarShadow.setVisibility(VISIBLE);
+            }
             mPreTextureCaptureAlpha = getAlpha();
             setAlpha(1);
         } else {
@@ -2525,7 +2527,8 @@
     protected boolean shouldDrawShadow() {
         // TODO(twellington): Move this shadow state information to ToolbarDataProvider and show
         // shadow when incognito NTP is scrolled.
-        return mTabSwitcherState == STATIC_TAB && !hideShadowForIncognitoNtp();
+        return mTabSwitcherState == STATIC_TAB && !hideShadowForIncognitoNtp()
+                && !hideShadowForInterstitial();
     }
 
     private boolean hideShadowForIncognitoNtp() {
@@ -2533,6 +2536,12 @@
                 && NewTabPage.isNTPUrl(getToolbarDataProvider().getCurrentUrl());
     }
 
+    private boolean hideShadowForInterstitial() {
+        return mLocationBar.useModernDesign() && getToolbarDataProvider() != null
+                && getToolbarDataProvider().getTab() != null
+                && getToolbarDataProvider().getTab().isShowingInterstitialPage();
+    }
+
     private @VisualState int computeVisualState(boolean isInTabSwitcherMode) {
         if (isInTabSwitcherMode && isIncognito()) return VisualState.TAB_SWITCHER_INCOGNITO;
         if (isInTabSwitcherMode && !isIncognito()) return VisualState.TAB_SWITCHER_NORMAL;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index 350b39ac..955d9d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -49,6 +49,7 @@
     private static Boolean sIsSoleEnabled;
     private static Boolean sIsChromeModernDesignEnabled;
     private static Boolean sIsHomePageButtonForceEnabled;
+    private static Boolean sIsHomepageTileEnabled;
     private static Boolean sIsNewTabPageButtonEnabled;
     private static Boolean sIsBottomToolbarEnabled;
 
@@ -157,6 +158,7 @@
         FirstRunUtils.cacheFirstRunPrefs();
         cacheChromeModernDesignEnabled();
         cacheHomePageButtonForceEnabled();
+        cacheHomepageTileEnabled();
         cacheNewTabPageButtonEnabled();
         cacheBottomToolbarEnabled();
 
@@ -225,6 +227,29 @@
      * Cache whether or not the new tab page button is enabled so on next startup, the value can
      * be made available immediately.
      */
+    public static void cacheHomepageTileEnabled() {
+        ChromePreferenceManager.getInstance().setHomepageTileEnabled(
+                ChromeFeatureList.isEnabled(ChromeFeatureList.HOMEPAGE_TILE));
+    }
+
+    /**
+     * @return Whether or not the new tab page button is enabled.
+     */
+    public static boolean isHomepageTileEnabled() {
+        if (sIsHomepageTileEnabled == null) {
+            ChromePreferenceManager prefManager = ChromePreferenceManager.getInstance();
+
+            try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+                sIsHomepageTileEnabled = prefManager.isHomepageTileEnabled();
+            }
+        }
+        return sIsHomepageTileEnabled;
+    }
+
+    /**
+     * Cache whether or not the new tab page button is enabled so on next startup, the value can
+     * be made available immediately.
+     */
     public static void cacheNewTabPageButtonEnabled() {
         ChromePreferenceManager.getInstance().setNewTabPageButtonEnabled(
                 ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_BUTTON));
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 9379e719..5ed73ce 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -85,6 +85,7 @@
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuButtonHelper.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuDragHelper.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuHandler.java",
+  "java/src/org/chromium/chrome/browser/appmenu/AppMenuIconRowFooter.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuItemIcon.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuObserver.java",
   "java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index b1ae5b0..34762a1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -139,21 +139,23 @@
     private static final int ACTIVITY_STARTUP_DELAY_MS = 1000;
 
     // Ranker data that's expected to be logged.
-    private static final Set<ContextualSearchRankerLogger.Feature> EXPECTED_RANKER_OUTCOMES;
+    // Integer values should contain @Feature values only.
+    private static final Set<Integer> EXPECTED_RANKER_OUTCOMES;
     static {
-        Set<ContextualSearchRankerLogger.Feature> expectedOutcomes =
-                new HashSet<ContextualSearchRankerLogger.Feature>(
-                        ContextualSearchRankerLoggerImpl.OUTCOMES.keySet());
+        // Integer values should contain @Feature values only.
+        Set<Integer> expectedOutcomes =
+                new HashSet<Integer>(ContextualSearchRankerLoggerImpl.OUTCOMES.keySet());
         // We don't log whether the quick action was clicked unless we actually have a quick action.
         expectedOutcomes.remove(
                 ContextualSearchRankerLogger.Feature.OUTCOME_WAS_QUICK_ACTION_CLICKED);
         EXPECTED_RANKER_OUTCOMES = Collections.unmodifiableSet(expectedOutcomes);
     }
-    private static final Set<ContextualSearchRankerLogger.Feature> EXPECTED_RANKER_FEATURES;
+    // Integer values should contain @Feature values only.
+    private static final Set<Integer> EXPECTED_RANKER_FEATURES;
     static {
-        Set<ContextualSearchRankerLogger.Feature> expectedFeatures =
-                new HashSet<ContextualSearchRankerLogger.Feature>(
-                        ContextualSearchRankerLoggerImpl.FEATURES.keySet());
+        // Integer values should contain @Feature values only.
+        Set<Integer> expectedFeatures =
+                new HashSet<Integer>(ContextualSearchRankerLoggerImpl.FEATURES.keySet());
         // We don't log previous user impressions and CTR if not available for the current user.
         expectedFeatures.remove(ContextualSearchRankerLogger.Feature.PREVIOUS_WEEK_CTR_PERCENT);
         expectedFeatures.remove(
@@ -1109,20 +1111,20 @@
     }
 
     /** @return The value of the given logged feature, or {@code null} if not logged. */
-    private Object loggedToRanker(ContextualSearchRankerLogger.Feature feature) {
+    private Object loggedToRanker(@ContextualSearchRankerLogger.Feature int feature) {
         return getRankerLogger().getFeaturesLogged().get(feature);
     }
 
     /** Asserts that all the expected features have been logged to Ranker. **/
     private void assertLoggedAllExpectedFeaturesToRanker() {
-        for (ContextualSearchRankerLogger.Feature feature : EXPECTED_RANKER_FEATURES) {
+        for (@ContextualSearchRankerLogger.Feature Integer feature : EXPECTED_RANKER_FEATURES) {
             Assert.assertNotNull(loggedToRanker(feature));
         }
     }
 
     /** Asserts that all the expected outcomes have been logged to Ranker. **/
     private void assertLoggedAllExpectedOutcomesToRanker() {
-        for (ContextualSearchRankerLogger.Feature feature : EXPECTED_RANKER_OUTCOMES) {
+        for (@ContextualSearchRankerLogger.Feature Integer feature : EXPECTED_RANKER_OUTCOMES) {
             Assert.assertNotNull("Expected this outcome to be logged: " + feature,
                     getRankerLogger().getOutcomesLogged().get(feature));
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java
index d2a14e5..59778ae 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/OMADownloadHandlerTest.java
@@ -296,14 +296,13 @@
 
         DownloadManagerDelegate downloadManagerDelegate = new DownloadManagerDelegate(context);
         DownloadQueryResultVerifier verifier =
-                new DownloadQueryResultVerifier(DownloadManagerService.DOWNLOAD_STATUS_COMPLETE);
+                new DownloadQueryResultVerifier(DownloadManagerService.DownloadStatus.COMPLETE);
         downloadManagerDelegate.queryDownloadResult(downloadItem, false, verifier);
         waitForQueryCompletion(verifier);
 
         manager.remove(downloadId1);
         downloadItem.setSystemDownloadId(downloadId1);
-        verifier =
-                new DownloadQueryResultVerifier(DownloadManagerService.DOWNLOAD_STATUS_CANCELLED);
+        verifier = new DownloadQueryResultVerifier(DownloadManagerService.DownloadStatus.CANCELLED);
         downloadManagerDelegate.queryDownloadResult(downloadItem, false, verifier);
         waitForQueryCompletion(verifier);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapterTest.java
index 97940b2..c7a73cb 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/ui/DownloadHistoryAdapterTest.java
@@ -499,20 +499,20 @@
                 HEADER, null, item5, item4, item6, null, item3, item2, null, item1, item0);
         Assert.assertEquals(1666, mAdapter.getTotalDownloadSize());
 
-        onFilterChanged(DownloadFilter.FILTER_AUDIO, 2);
+        onFilterChanged(DownloadFilter.Type.AUDIO, 2);
         checkAdapterContents(HEADER, null, item5, item4);
         Assert.assertEquals(1666, mAdapter.getTotalDownloadSize()); // Total size ignores filters.
 
-        onFilterChanged(DownloadFilter.FILTER_VIDEO, 2);
+        onFilterChanged(DownloadFilter.Type.VIDEO, 2);
         checkAdapterContents(HEADER, null, item3);
 
-        onFilterChanged(DownloadFilter.FILTER_IMAGE, 2);
+        onFilterChanged(DownloadFilter.Type.IMAGE, 2);
         checkAdapterContents(HEADER, null, item1, item0);
 
-        onFilterChanged(DownloadFilter.FILTER_PAGE, 2);
+        onFilterChanged(DownloadFilter.Type.PAGE, 2);
         checkAdapterContents(HEADER, null, item6);
 
-        onFilterChanged(DownloadFilter.FILTER_ALL, 2);
+        onFilterChanged(DownloadFilter.Type.ALL, 2);
         checkAdapterContents(
                 HEADER, null, item5, item4, item6, null, item3, item2, null, item1, item0);
         Assert.assertEquals(1666, mAdapter.getTotalDownloadSize());
@@ -537,13 +537,13 @@
         checkAdapterContents(HEADER, null, item2, null, item0);
 
         // Filter shows nothing when the item is deleted because it's a different kind of item.
-        onFilterChanged(DownloadFilter.FILTER_AUDIO, 1);
+        onFilterChanged(DownloadFilter.Type.AUDIO, 1);
         Assert.assertEquals(0, mAdapter.getItemCount());
         onOfflineItemDeleted(item0.id, 0);
         Assert.assertEquals(0, mAdapter.getItemCount());
 
         // Filter shows just pages.
-        onFilterChanged(DownloadFilter.FILTER_PAGE, 2);
+        onFilterChanged(DownloadFilter.Type.PAGE, 2);
         checkAdapterContents(HEADER, null, item2);
         onOfflineItemDeleted(item2.id, 1);
         Assert.assertEquals(0, mAdapter.getItemCount());
@@ -668,7 +668,7 @@
                 HEADER, null, item5, item4, item6, null, item3, item2, null, item1, item0);
 
         // Change the filter
-        onFilterChanged(DownloadFilter.FILTER_IMAGE, 2);
+        onFilterChanged(DownloadFilter.Type.IMAGE, 2);
         checkAdapterContents(HEADER, null, item1, item0);
 
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index 351fdf6..7c4496f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -202,6 +202,7 @@
     }
 
     @Test
+    @DisabledTest(message = "https://crbug.com/813589")
     @MediumTest
     @Feature({"NewTabPage", "RenderTest"})
     @DisableFeatures({ChromeFeatureList.SIMPLIFIED_NTP})
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java
index 62abf602..6cc5fb7 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/FiltersTest.java
@@ -18,14 +18,17 @@
 public class FiltersTest {
     @Test
     public void testFilterConversions() {
-        Assert.assertEquals(Filters.SITES, Filters.fromOfflineItem(OfflineItemFilter.FILTER_PAGE));
         Assert.assertEquals(
-                Filters.VIDEOS, Filters.fromOfflineItem(OfflineItemFilter.FILTER_VIDEO));
-        Assert.assertEquals(Filters.MUSIC, Filters.fromOfflineItem(OfflineItemFilter.FILTER_AUDIO));
+                Filters.FilterType.SITES, Filters.fromOfflineItem(OfflineItemFilter.FILTER_PAGE));
         Assert.assertEquals(
-                Filters.IMAGES, Filters.fromOfflineItem(OfflineItemFilter.FILTER_IMAGE));
-        Assert.assertEquals(Filters.OTHER, Filters.fromOfflineItem(OfflineItemFilter.FILTER_OTHER));
+                Filters.FilterType.VIDEOS, Filters.fromOfflineItem(OfflineItemFilter.FILTER_VIDEO));
         Assert.assertEquals(
-                Filters.OTHER, Filters.fromOfflineItem(OfflineItemFilter.FILTER_DOCUMENT));
+                Filters.FilterType.MUSIC, Filters.fromOfflineItem(OfflineItemFilter.FILTER_AUDIO));
+        Assert.assertEquals(
+                Filters.FilterType.IMAGES, Filters.fromOfflineItem(OfflineItemFilter.FILTER_IMAGE));
+        Assert.assertEquals(
+                Filters.FilterType.OTHER, Filters.fromOfflineItem(OfflineItemFilter.FILTER_OTHER));
+        Assert.assertEquals(Filters.FilterType.OTHER,
+                Filters.fromOfflineItem(OfflineItemFilter.FILTER_DOCUMENT));
     }
 }
\ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java
index edcd55cc..63e36bc 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/download/home/filter/TypeOfflineItemFilterTest.java
@@ -56,37 +56,37 @@
         Assert.assertEquals(CollectionUtil.newHashSet(item1, item2, item3, item4, item5, item6),
                 filter.getItems());
 
-        filter.onFilterSelected(Filters.VIDEOS);
+        filter.onFilterSelected(Filters.FilterType.VIDEOS);
         verify(mObserver, times(1))
                 .onItemsRemoved(CollectionUtil.newHashSet(item1, item3, item4, item5, item6));
         Assert.assertEquals(CollectionUtil.newHashSet(item2), filter.getItems());
 
-        filter.onFilterSelected(Filters.MUSIC);
+        filter.onFilterSelected(Filters.FilterType.MUSIC);
         verify(mObserver, times(1)).onItemsRemoved(CollectionUtil.newHashSet(item2));
         verify(mObserver, times(1)).onItemsAdded(CollectionUtil.newHashSet(item3));
         Assert.assertEquals(CollectionUtil.newHashSet(item3), filter.getItems());
 
-        filter.onFilterSelected(Filters.IMAGES);
+        filter.onFilterSelected(Filters.FilterType.IMAGES);
         verify(mObserver, times(1)).onItemsRemoved(CollectionUtil.newHashSet(item3));
         verify(mObserver, times(1)).onItemsAdded(CollectionUtil.newHashSet(item4));
         Assert.assertEquals(CollectionUtil.newHashSet(item4), filter.getItems());
 
-        filter.onFilterSelected(Filters.SITES);
+        filter.onFilterSelected(Filters.FilterType.SITES);
         verify(mObserver, times(1)).onItemsRemoved(CollectionUtil.newHashSet(item4));
         verify(mObserver, times(1)).onItemsAdded(CollectionUtil.newHashSet(item1));
         Assert.assertEquals(CollectionUtil.newHashSet(item1), filter.getItems());
 
-        filter.onFilterSelected(Filters.OTHER);
+        filter.onFilterSelected(Filters.FilterType.OTHER);
         verify(mObserver, times(1)).onItemsRemoved(CollectionUtil.newHashSet(item1));
         verify(mObserver, times(1)).onItemsAdded(CollectionUtil.newHashSet(item5, item6));
         Assert.assertEquals(CollectionUtil.newHashSet(item5, item6), filter.getItems());
 
-        filter.onFilterSelected(Filters.PREFETCHED);
+        filter.onFilterSelected(Filters.FilterType.PREFETCHED);
         verify(mObserver, times(1)).onItemsRemoved(CollectionUtil.newHashSet(item5, item6));
         verify(mObserver, times(1)).onItemsAdded(CollectionUtil.newHashSet(item7, item8));
         Assert.assertEquals(CollectionUtil.newHashSet(item7, item8), filter.getItems());
 
-        filter.onFilterSelected(Filters.NONE);
+        filter.onFilterSelected(Filters.FilterType.NONE);
         verify(mObserver, times(1))
                 .onItemsAdded(CollectionUtil.newHashSet(item1, item2, item3, item4, item5, item6));
         Assert.assertEquals(CollectionUtil.newHashSet(item1, item2, item3, item4, item5, item6),
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d4cdb7b1..94cd53ab 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1942,6 +1942,9 @@
     {"enable-ntp-button", flag_descriptions::kNtpButtonName,
      flag_descriptions::kNtpButtonDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(chrome::android::kNTPButton)},
+    {"enable-homepage-tile", flag_descriptions::kHomepageTileName,
+     flag_descriptions::kHomepageTileDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kHomepageTile)},
 #endif  // OS_ANDROID
 #if defined(OS_ANDROID)
     {"enable-tab-modal-js-dialog-android",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 7126ee9..59762341 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -103,6 +103,7 @@
     &kFullscreenActivity,
     &kHandleMediaIntents,
     &kHomePageButtonForceEnabled,
+    &kHomepageTile,
     &kHorizontalTabSwitcherAndroid,
     &kImprovedA2HS,
     &kLanguagesPreference,
@@ -284,6 +285,9 @@
 const base::Feature kHomePageButtonForceEnabled{
     "HomePageButtonForceEnabled", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kHomepageTile{"HomepageTile",
+                                  base::FEATURE_ENABLED_BY_DEFAULT};
+
 const base::Feature kHorizontalTabSwitcherAndroid{
     "HorizontalTabSwitcherAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 5d24a03f..ad96f313 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -49,6 +49,7 @@
 extern const base::Feature kFullscreenActivity;
 extern const base::Feature kHandleMediaIntents;
 extern const base::Feature kHomePageButtonForceEnabled;
+extern const base::Feature kHomepageTile;
 extern const base::Feature kHorizontalTabSwitcherAndroid;
 extern const base::Feature kImprovedA2HS;
 extern const base::Feature kLanguagesPreference;
diff --git a/chrome/browser/android/history_report/history_report_jni_bridge.cc b/chrome/browser/android/history_report/history_report_jni_bridge.cc
index 37acfe9..0c847ce 100644
--- a/chrome/browser/android/history_report/history_report_jni_bridge.cc
+++ b/chrome/browser/android/history_report/history_report_jni_bridge.cc
@@ -9,6 +9,7 @@
 
 #include <vector>
 
+#include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
@@ -145,6 +146,12 @@
   usage_reports_buffer_service_->Remove(to_remove);
 }
 
+void HistoryReportJniBridge::ClearUsageReports(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj) {
+  usage_reports_buffer_service_->Clear();
+}
+
 void HistoryReportJniBridge::NotifyDataChanged() {
   JNIEnv* env = base::android::AttachCurrentThread();
   ScopedJavaLocalRef<jobject> obj = weak_java_provider_.get(env);
diff --git a/chrome/browser/android/history_report/history_report_jni_bridge.h b/chrome/browser/android/history_report/history_report_jni_bridge.h
index df596da..cb8a5ed 100644
--- a/chrome/browser/android/history_report/history_report_jni_bridge.h
+++ b/chrome/browser/android/history_report/history_report_jni_bridge.h
@@ -50,6 +50,9 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
       const base::android::JavaParamRef<jobjectArray>& batch);
+  // Clear all entries from the usage reports.
+  void ClearUsageReports(JNIEnv* env,
+                         const base::android::JavaParamRef<jobject>& obj);
   // Populates the usage reports buffer with historic visits.
   // This should happen only once per corpus registration.
   jboolean AddHistoricVisitsToUsageReportsBuffer(
diff --git a/chrome/browser/android/vr/vr_shell_delegate.cc b/chrome/browser/android/vr/vr_shell_delegate.cc
index 09f64b0..f843dd95 100644
--- a/chrome/browser/android/vr/vr_shell_delegate.cc
+++ b/chrome/browser/android/vr/vr_shell_delegate.cc
@@ -176,7 +176,7 @@
 void VrShellDelegate::OnPresentResult(
     device::mojom::VRDisplayInfoPtr display_info,
     device::mojom::XRDeviceRuntimeSessionOptionsPtr options,
-    device::mojom::VRDisplayHost::RequestSessionCallback callback,
+    base::OnceCallback<void(device::mojom::XRSessionPtr)> callback,
     bool success) {
   DVLOG(1) << __FUNCTION__ << ": success=" << success;
   if (!success) {
@@ -227,8 +227,11 @@
     connection->provider = provider.PassInterface();
     connection->transport_options = std::move(transport_options);
 
+    device::mojom::XRSessionPtr xr_session = device::mojom::XRSession::New();
+    xr_session->connection = std::move(connection);
+
     base::ResetAndReturn(&request_present_response_callback_)
-        .Run(std::move(connection));
+        .Run(std::move(xr_session));
   } else {
     base::ResetAndReturn(&request_present_response_callback_).Run(nullptr);
   }
@@ -299,7 +302,7 @@
 void VrShellDelegate::StartWebXRPresentation(
     device::mojom::VRDisplayInfoPtr display_info,
     device::mojom::XRDeviceRuntimeSessionOptionsPtr options,
-    device::mojom::VRDisplayHost::RequestSessionCallback callback) {
+    base::OnceCallback<void(device::mojom::XRSessionPtr)> callback) {
   if (!on_present_result_callback_.is_null() ||
       !request_present_response_callback_.is_null()) {
     // Can only handle one request at a time. This is also extremely unlikely to
diff --git a/chrome/browser/android/vr/vr_shell_delegate.h b/chrome/browser/android/vr/vr_shell_delegate.h
index 9a4f6d4..859f616 100644
--- a/chrome/browser/android/vr/vr_shell_delegate.h
+++ b/chrome/browser/android/vr/vr_shell_delegate.h
@@ -84,7 +84,7 @@
   void StartWebXRPresentation(
       device::mojom::VRDisplayInfoPtr display_info,
       device::mojom::XRDeviceRuntimeSessionOptionsPtr options,
-      device::mojom::VRDisplayHost::RequestSessionCallback callback) override;
+      base::OnceCallback<void(device::mojom::XRSessionPtr)> callback) override;
   void OnListeningForActivateChanged(bool listening) override;
 
   void OnActivateDisplayHandled(bool will_not_present);
@@ -92,7 +92,7 @@
   void OnPresentResult(
       device::mojom::VRDisplayInfoPtr display_info,
       device::mojom::XRDeviceRuntimeSessionOptionsPtr options,
-      device::mojom::VRDisplayHost::RequestSessionCallback callback,
+      base::OnceCallback<void(device::mojom::XRSessionPtr)> callback,
       bool success);
 
   std::unique_ptr<VrCoreInfo> MakeVrCoreInfo(JNIEnv* env);
@@ -108,7 +108,7 @@
   // Mojo callback waiting for request present response. This is temporarily
   // stored here from OnPresentResult's outgoing ConnectPresentingService call
   // until the reply arguments are received by SendRequestPresentReply.
-  device::mojom::VRDisplayHost::RequestSessionCallback
+  base::OnceCallback<void(device::mojom::XRSessionPtr)>
       request_present_response_callback_;
 
   bool pending_successful_present_request_ = false;
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index b1cd4aa..70ec6d2 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -81,6 +81,7 @@
 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/browser/web_applications/web_app_mac.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths_internal.h"
diff --git a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
index 0f48942..2d71f4a 100644
--- a/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
+++ b/chrome/browser/apps/app_shim/extension_app_shim_handler_mac.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
 #include "chrome/browser/ui/user_manager.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/browser/web_applications/web_app_mac.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/extension_metrics.h"
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index f208051..5c6069f 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -421,6 +421,7 @@
       <include name="IDR_DICE_SYNC_CONFIRMATION_BROWSER_PROXY_JS" file="resources\signin\dice_sync_confirmation\sync_confirmation_browser_proxy.js" type="BINDATA" />
       <include name="IDR_DICE_SYNC_CONFIRMATION_APP_HTML" file="resources\signin\dice_sync_confirmation\sync_confirmation_app.html" type="BINDATA" flattenhtml="true" allowexternalscript="true" />
       <include name="IDR_DICE_SYNC_CONFIRMATION_APP_JS" file="resources\signin\dice_sync_confirmation\sync_confirmation_app.js" type="BINDATA" />
+      <include name="IDR_DICE_SYNC_CONFIRMATION_ICONS_HTML" file="resources\signin\dice_sync_confirmation\icons.html" type="BINDATA" />
       <include name="IDR_SIGNIN_EMAIL_CONFIRMATION_HTML" file="resources\signin\signin_email_confirmation\signin_email_confirmation.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
       <include name="IDR_SIGNIN_EMAIL_CONFIRMATION_JS" file="resources\signin\signin_email_confirmation\signin_email_confirmation.js" type="BINDATA" />
       <include name="IDR_SIGNIN_ERROR_HTML" file="resources\signin\signin_error\signin_error.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index bff4877..ccb9ca7 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -1081,11 +1081,13 @@
 
   ~MockNetworkErrorLoggingService() override = default;
 
-  void OnHeader(const url::Origin& origin, const std::string& value) override {
+  void OnHeader(const url::Origin& origin,
+                const net::IPAddress& received_ip_address,
+                const std::string& value) override {
     NOTREACHED();
   }
 
-  void OnRequest(const RequestDetails& details) override { NOTREACHED(); }
+  void OnRequest(RequestDetails details) override { NOTREACHED(); }
 
   void RemoveBrowsingData(const base::RepeatingCallback<bool(const GURL&)>&
                               origin_filter) override {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 21415b5..e0946c0 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -925,15 +925,6 @@
 }
 #endif
 
-// Gets the URL request context getter for the browser process.
-// Must be called on the UI thread.
-scoped_refptr<net::URLRequestContextGetter>
-GetSystemRequestContextOnUIThread() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return scoped_refptr<net::URLRequestContextGetter>(
-      g_browser_process->system_request_context());
-}
-
 chrome::mojom::PrerenderCanceler* GetPrerenderCanceller(
     const base::Callback<content::WebContents*()>& wc_getter) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -2557,12 +2548,14 @@
   return NULL;
 }
 
-void ChromeContentBrowserClient::GetGeolocationRequestContext(
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        callback) {
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&GetSystemRequestContextOnUIThread), std::move(callback));
+scoped_refptr<network::SharedURLLoaderFactory>
+ChromeContentBrowserClient::GetSystemSharedURLLoaderFactory() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!g_browser_process->system_network_context_manager())
+    return nullptr;
+
+  return g_browser_process->system_network_context_manager()
+      ->GetSharedURLLoaderFactory();
 }
 
 std::string ChromeContentBrowserClient::GetGeolocationApiKey() {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index e925fdb9..ff1be12 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -233,9 +233,8 @@
   net::URLRequestContext* OverrideRequestContextForURL(
       const GURL& url,
       content::ResourceContext* context) override;
-  void GetGeolocationRequestContext(
-      base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-          callback) override;
+  scoped_refptr<network::SharedURLLoaderFactory>
+  GetSystemSharedURLLoaderFactory() override;
   std::string GetGeolocationApiKey() override;
 
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/chromeos/login/screen_manager.cc b/chrome/browser/chromeos/login/screen_manager.cc
index 23ee4fa..5a2e9a91 100644
--- a/chrome/browser/chromeos/login/screen_manager.cc
+++ b/chrome/browser/chromeos/login/screen_manager.cc
@@ -9,20 +9,21 @@
 
 namespace chromeos {
 
-ScreenManager::ScreenManager(WizardController* wizard_controller)
-    : wizard_controller_(wizard_controller) {}
+ScreenManager::ScreenManager() = default;
 
-ScreenManager::~ScreenManager() {}
+ScreenManager::~ScreenManager() = default;
 
 BaseScreen* ScreenManager::GetScreen(OobeScreen screen) {
   auto iter = screens_.find(screen);
   if (iter != screens_.end())
     return iter->second.get();
 
-  BaseScreen* result = wizard_controller_->CreateScreen(screen);
+  std::unique_ptr<BaseScreen> result =
+      WizardController::default_controller()->CreateScreen(screen);
   DCHECK(result) << "Can not create screen named " << GetOobeScreenName(screen);
-  screens_[screen] = base::WrapUnique(result);
-  return result;
+  BaseScreen* unowned_result = result.get();
+  screens_[screen] = std::move(result);
+  return unowned_result;
 }
 
 bool ScreenManager::HasScreen(OobeScreen screen) {
diff --git a/chrome/browser/chromeos/login/screen_manager.h b/chrome/browser/chromeos/login/screen_manager.h
index 9c52cd5..159b0019 100644
--- a/chrome/browser/chromeos/login/screen_manager.h
+++ b/chrome/browser/chromeos/login/screen_manager.h
@@ -15,13 +15,10 @@
 
 namespace chromeos {
 
-class WizardController;
-
 // Class that manages creation and ownership of screens.
 class ScreenManager {
  public:
-  // |wizard_controller| is not owned by this class.
-  explicit ScreenManager(WizardController* wizard_controller);
+  ScreenManager();
   ~ScreenManager();
 
   // Getter for screen with lazy initialization.
@@ -41,9 +38,6 @@
   // Created screens.
   std::map<OobeScreen, std::unique_ptr<BaseScreen>> screens_;
 
-  // Used to allocate BaseScreen instances. Unowned.
-  WizardController* wizard_controller_;
-
   DISALLOW_COPY_AND_ASSIGN(ScreenManager);
 };
 
diff --git a/chrome/browser/chromeos/login/ui/fake_login_display_host.cc b/chrome/browser/chromeos/login/ui/fake_login_display_host.cc
index b889c171..3a54ba0 100644
--- a/chrome/browser/chromeos/login/ui/fake_login_display_host.cc
+++ b/chrome/browser/chromeos/login/ui/fake_login_display_host.cc
@@ -56,10 +56,7 @@
 void FakeLoginDisplayHost::SetStatusAreaVisible(bool visible) {}
 
 void FakeLoginDisplayHost::StartWizard(OobeScreen first_screen) {
-  // Reset the controller first since there could only be one wizard
-  // controller at any time.
-  wizard_controller_.reset();
-  wizard_controller_ = std::make_unique<WizardController>(nullptr, nullptr);
+  wizard_controller_ = std::make_unique<WizardController>();
 
   fake_screen_ = std::make_unique<FakeBaseScreen>(first_screen);
   wizard_controller_->SetCurrentScreenForTesting(fake_screen_.get());
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
index 0c3a91d..2e673b39 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
@@ -148,10 +148,7 @@
 void LoginDisplayHostMojo::StartWizard(OobeScreen first_screen) {
   DCHECK(GetOobeUI());
 
-  // Dtor of the old WizardController should be called before ctor of the
-  // new one to ensure only one |ExistingUserController| instance at a time.
-  wizard_controller_.reset();
-  wizard_controller_.reset(new WizardController(this, GetOobeUI()));
+  wizard_controller_ = std::make_unique<WizardController>();
   wizard_controller_->Init(first_screen);
 
   // Post login screens should not be closable by escape key.
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index 5b111be..87522ff 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -602,11 +602,7 @@
   DVLOG(1) << "Starting wizard, first_screen: "
            << GetOobeScreenName(first_screen);
   // Create and show the wizard.
-  // Note, dtor of the old WizardController should be called before ctor of the
-  // new one, because "default_controller()" is updated there. So pure "reset()"
-  // is done before new controller creation.
-  wizard_controller_.reset();
-  wizard_controller_.reset(CreateWizardController());
+  wizard_controller_ = std::make_unique<WizardController>();
 
   oobe_progress_bar_visible_ = !StartupUtils::IsDeviceRegistered();
   SetOobeProgressBarVisible(oobe_progress_bar_visible_);
@@ -755,12 +751,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 // LoginDisplayHostWebUI, public
 
-WizardController* LoginDisplayHostWebUI::CreateWizardController() {
-  // TODO(altimofeev): ensure that WebUI is ready.
-  OobeUI* oobe_ui = GetOobeUI();
-  return new WizardController(this, oobe_ui);
-}
-
 void LoginDisplayHostWebUI::OnBrowserCreated() {
   // Close lock window now so that the launched browser can receive focus.
   ResetLoginWindowAndView();
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.h b/chrome/browser/chromeos/login/ui/login_display_host_webui.h
index 585e88f..c8f55db 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.h
@@ -81,9 +81,6 @@
   void ShowFeedback() override;
   void OnCancelPasswordChangedFlow() override;
 
-  // Creates WizardController instance.
-  WizardController* CreateWizardController();
-
   // Trace id for ShowLoginWebUI event (since there exists at most one login
   // WebUI at a time).
   static const int kShowLoginWebUIid;
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index a737896..901b4319 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -251,6 +251,15 @@
       chromeos::switches::kEnableOobeRecommendAppsScreen);
 }
 
+chromeos::LoginDisplayHost* GetLoginDisplayHost() {
+  return chromeos::LoginDisplayHost::default_host();
+}
+
+chromeos::OobeUI* GetOobeUI() {
+  auto* host = chromeos::LoginDisplayHost::default_host();
+  return host ? host->GetOobeUI() : nullptr;
+}
+
 }  // namespace
 
 namespace chromeos {
@@ -258,10 +267,6 @@
 // static
 const int WizardController::kMinAudibleOutputVolumePercent = 10;
 
-// Initialize default controller.
-// static
-WizardController* WizardController::default_controller_ = nullptr;
-
 // static
 bool WizardController::skip_post_login_screens_ = false;
 
@@ -271,16 +276,19 @@
 // static
 bool WizardController::zero_delay_enabled_ = false;
 
+// static
+WizardController* WizardController::default_controller() {
+  auto* host = chromeos::LoginDisplayHost::default_host();
+  return host ? host->GetWizardController() : nullptr;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // WizardController, public:
 
 PrefService* WizardController::local_state_for_testing_ = nullptr;
 
-WizardController::WizardController(LoginDisplayHost* host, OobeUI* oobe_ui)
-    : host_(host), oobe_ui_(oobe_ui), weak_factory_(this) {
-  DCHECK(default_controller_ == nullptr);
-  default_controller_ = this;
-  screen_manager_ = std::make_unique<ScreenManager>(this);
+WizardController::WizardController()
+    : screen_manager_(std::make_unique<ScreenManager>()), weak_factory_(this) {
   // In session OOBE was initiated from voice interaction keyboard shortcuts.
   is_in_session_oobe_ =
       session_manager::SessionManager::Get()->IsSessionStarted();
@@ -308,11 +316,6 @@
     base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
         FROM_HERE, shark_connection_listener_.release());
   }
-  if (default_controller_ == this) {
-    default_controller_ = nullptr;
-  } else {
-    NOTREACHED() << "More than one controller are alive.";
-  }
 }
 
 void WizardController::Init(OobeScreen first_screen) {
@@ -378,7 +381,7 @@
 }
 
 ErrorScreen* WizardController::GetErrorScreen() {
-  return oobe_ui_->GetErrorScreen();
+  return GetOobeUI()->GetErrorScreen();
 }
 
 BaseScreen* WizardController::GetScreen(OobeScreen screen) {
@@ -387,66 +390,74 @@
   return screen_manager_->GetScreen(screen);
 }
 
-BaseScreen* WizardController::CreateScreen(OobeScreen screen) {
+std::unique_ptr<BaseScreen> WizardController::CreateScreen(OobeScreen screen) {
+  OobeUI* oobe_ui = GetOobeUI();
+
   if (screen == OobeScreen::SCREEN_OOBE_WELCOME) {
-    return new WelcomeScreen(this, this, oobe_ui_->GetWelcomeView());
+    return std::make_unique<WelcomeScreen>(this, this,
+                                           oobe_ui->GetWelcomeView());
   } else if (screen == OobeScreen::SCREEN_OOBE_UPDATE) {
-    return new UpdateScreen(this, oobe_ui_->GetUpdateView(),
-                            remora_controller_.get());
+    return std::make_unique<UpdateScreen>(this, oobe_ui->GetUpdateView(),
+                                          remora_controller_.get());
   } else if (screen == OobeScreen::SCREEN_USER_IMAGE_PICKER) {
-    return new UserImageScreen(this, oobe_ui_->GetUserImageView());
+    return std::make_unique<UserImageScreen>(this, oobe_ui->GetUserImageView());
   } else if (screen == OobeScreen::SCREEN_OOBE_EULA) {
-    return new EulaScreen(this, this, oobe_ui_->GetEulaView());
+    return std::make_unique<EulaScreen>(this, this, oobe_ui->GetEulaView());
   } else if (screen == OobeScreen::SCREEN_OOBE_ENROLLMENT) {
-    return new EnrollmentScreen(this, oobe_ui_->GetEnrollmentScreenView());
+    return std::make_unique<EnrollmentScreen>(
+        this, oobe_ui->GetEnrollmentScreenView());
   } else if (screen == OobeScreen::SCREEN_OOBE_RESET) {
-    return new chromeos::ResetScreen(this, oobe_ui_->GetResetView());
+    return std::make_unique<chromeos::ResetScreen>(this,
+                                                   oobe_ui->GetResetView());
   } else if (screen == OobeScreen::SCREEN_OOBE_DEMO_SETUP) {
-    return new chromeos::DemoSetupScreen(this,
-                                         oobe_ui_->GetDemoSetupScreenView());
+    return std::make_unique<chromeos::DemoSetupScreen>(
+        this, oobe_ui->GetDemoSetupScreenView());
   } else if (screen == OobeScreen::SCREEN_OOBE_DEMO_PREFERENCES) {
-    return new chromeos::DemoPreferencesScreen(
-        this, oobe_ui_->GetDemoPreferencesScreenView());
+    return std::make_unique<chromeos::DemoPreferencesScreen>(
+        this, oobe_ui->GetDemoPreferencesScreenView());
   } else if (screen == OobeScreen::SCREEN_OOBE_ENABLE_DEBUGGING) {
-    return new EnableDebuggingScreen(this,
-                                     oobe_ui_->GetEnableDebuggingScreenView());
+    return std::make_unique<EnableDebuggingScreen>(
+        this, oobe_ui->GetEnableDebuggingScreenView());
   } else if (screen == OobeScreen::SCREEN_KIOSK_ENABLE) {
-    return new KioskEnableScreen(this, oobe_ui_->GetKioskEnableScreenView());
+    return std::make_unique<KioskEnableScreen>(
+        this, oobe_ui->GetKioskEnableScreenView());
   } else if (screen == OobeScreen::SCREEN_KIOSK_AUTOLAUNCH) {
-    return new KioskAutolaunchScreen(this,
-                                     oobe_ui_->GetKioskAutolaunchScreenView());
+    return std::make_unique<KioskAutolaunchScreen>(
+        this, oobe_ui->GetKioskAutolaunchScreenView());
   } else if (screen == OobeScreen::SCREEN_TERMS_OF_SERVICE) {
-    return new TermsOfServiceScreen(this,
-                                    oobe_ui_->GetTermsOfServiceScreenView());
+    return std::make_unique<TermsOfServiceScreen>(
+        this, oobe_ui->GetTermsOfServiceScreenView());
   } else if (screen == OobeScreen::SCREEN_SYNC_CONSENT) {
-    return new SyncConsentScreen(this, oobe_ui_->GetSyncConsentScreenView());
+    return std::make_unique<SyncConsentScreen>(
+        this, oobe_ui->GetSyncConsentScreenView());
   } else if (screen == OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE) {
-    return new ArcTermsOfServiceScreen(
-        this, oobe_ui_->GetArcTermsOfServiceScreenView());
+    return std::make_unique<ArcTermsOfServiceScreen>(
+        this, oobe_ui->GetArcTermsOfServiceScreenView());
   } else if (screen == OobeScreen::SCREEN_RECOMMEND_APPS) {
-    return new RecommendAppsScreen(this,
-                                   oobe_ui_->GetRecommendAppsScreenView());
+    return std::make_unique<RecommendAppsScreen>(
+        this, oobe_ui->GetRecommendAppsScreenView());
   } else if (screen == OobeScreen::SCREEN_APP_DOWNLOADING) {
-    return new AppDownloadingScreen(this,
-                                    oobe_ui_->GetAppDownloadingScreenView());
+    return std::make_unique<AppDownloadingScreen>(
+        this, oobe_ui->GetAppDownloadingScreenView());
   } else if (screen == OobeScreen::SCREEN_WRONG_HWID) {
-    return new WrongHWIDScreen(this, oobe_ui_->GetWrongHWIDScreenView());
+    return std::make_unique<WrongHWIDScreen>(this,
+                                             oobe_ui->GetWrongHWIDScreenView());
   } else if (screen == OobeScreen::SCREEN_CREATE_SUPERVISED_USER_FLOW) {
-    return new SupervisedUserCreationScreen(
-        this, oobe_ui_->GetSupervisedUserCreationScreenView());
+    return std::make_unique<SupervisedUserCreationScreen>(
+        this, oobe_ui->GetSupervisedUserCreationScreenView());
   } else if (screen == OobeScreen::SCREEN_OOBE_HID_DETECTION) {
-    return new chromeos::HIDDetectionScreen(this,
-                                            oobe_ui_->GetHIDDetectionView());
+    return std::make_unique<chromeos::HIDDetectionScreen>(
+        this, oobe_ui->GetHIDDetectionView());
   } else if (screen == OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK) {
-    return new AutoEnrollmentCheckScreen(
-        this, oobe_ui_->GetAutoEnrollmentCheckScreenView());
+    return std::make_unique<AutoEnrollmentCheckScreen>(
+        this, oobe_ui->GetAutoEnrollmentCheckScreenView());
   } else if (screen == OobeScreen::SCREEN_OOBE_CONTROLLER_PAIRING) {
     if (!shark_controller_) {
       shark_controller_.reset(
           new pairing_chromeos::BluetoothControllerPairingController());
     }
-    return new ControllerPairingScreen(
-        this, this, oobe_ui_->GetControllerPairingScreenView(),
+    return std::make_unique<ControllerPairingScreen>(
+        this, this, oobe_ui->GetControllerPairingScreenView(),
         shark_controller_.get());
   } else if (screen == OobeScreen::SCREEN_OOBE_HOST_PAIRING) {
     if (!remora_controller_) {
@@ -458,24 +469,24 @@
           new pairing_chromeos::BluetoothHostPairingController(connector));
       remora_controller_->StartPairing();
     }
-    return new HostPairingScreen(this, this,
-                                 oobe_ui_->GetHostPairingScreenView(),
-                                 remora_controller_.get());
+    return std::make_unique<HostPairingScreen>(
+        this, this, oobe_ui->GetHostPairingScreenView(),
+        remora_controller_.get());
   } else if (screen == OobeScreen::SCREEN_DEVICE_DISABLED) {
-    return new DeviceDisabledScreen(this,
-                                    oobe_ui_->GetDeviceDisabledScreenView());
+    return std::make_unique<DeviceDisabledScreen>(
+        this, oobe_ui->GetDeviceDisabledScreenView());
   } else if (screen == OobeScreen::SCREEN_ENCRYPTION_MIGRATION) {
-    return new EncryptionMigrationScreen(
-        this, oobe_ui_->GetEncryptionMigrationScreenView());
+    return std::make_unique<EncryptionMigrationScreen>(
+        this, oobe_ui->GetEncryptionMigrationScreenView());
   } else if (screen == OobeScreen::SCREEN_VOICE_INTERACTION_VALUE_PROP) {
-    return new VoiceInteractionValuePropScreen(
-        this, oobe_ui_->GetVoiceInteractionValuePropScreenView());
+    return std::make_unique<VoiceInteractionValuePropScreen>(
+        this, oobe_ui->GetVoiceInteractionValuePropScreenView());
   } else if (screen == OobeScreen::SCREEN_WAIT_FOR_CONTAINER_READY) {
-    return new WaitForContainerReadyScreen(
-        this, oobe_ui_->GetWaitForContainerReadyScreenView());
+    return std::make_unique<WaitForContainerReadyScreen>(
+        this, oobe_ui->GetWaitForContainerReadyScreenView());
   } else if (screen == OobeScreen::SCREEN_UPDATE_REQUIRED) {
-    return new UpdateRequiredScreen(this,
-                                    oobe_ui_->GetUpdateRequiredScreenView());
+    return std::make_unique<UpdateRequiredScreen>(
+        this, oobe_ui->GetUpdateRequiredScreenView());
   }
   return nullptr;
 }
@@ -512,7 +523,7 @@
   }
   VLOG(1) << "Showing login screen.";
   UpdateStatusAreaVisibilityForScreen(OobeScreen::SCREEN_SPECIAL_LOGIN);
-  host_->StartSignInScreen(context);
+  GetLoginDisplayHost()->StartSignInScreen(context);
   smooth_show_timer_.Stop();
   login_screen_started_ = true;
 }
@@ -630,7 +641,7 @@
     // which use ArcSupport for now, because we're interested in only OOBE flow.
     // Note that this part also needs to be updated on b/65861628.
     // TODO(khmel): add unit test once we have support for OobeUI.
-    if (!host_->IsVoiceInteractionOobe()) {
+    if (!GetLoginDisplayHost()->IsVoiceInteractionOobe()) {
       ProfileManager::GetActiveUserProfile()->GetPrefs()->SetBoolean(
           arc::prefs::kArcTermsShownInOobe, true);
     }
@@ -1055,9 +1066,8 @@
 
 void WizardController::OnOobeFlowFinished() {
   if (is_in_session_oobe_) {
-    host_->SetStatusAreaVisible(true);
-    host_->Finalize(base::OnceClosure());
-    host_ = nullptr;
+    GetLoginDisplayHost()->SetStatusAreaVisible(true);
+    GetLoginDisplayHost()->Finalize(base::OnceClosure());
     return;
   }
 
@@ -1074,8 +1084,8 @@
       BrowserThread::UI, FROM_HERE,
       base::BindOnce(&UserSessionManager::DoBrowserLaunch,
                      base::Unretained(UserSessionManager::GetInstance()),
-                     ProfileManager::GetActiveUserProfile(), host_));
-  host_ = nullptr;
+                     ProfileManager::GetActiveUserProfile(),
+                     GetLoginDisplayHost()));
 }
 
 void WizardController::OnDeviceDisabledChecked(bool device_disabled) {
@@ -1151,7 +1161,7 @@
   NetworkHandler::Get()->network_state_handler()->SetCheckPortalList(
       NetworkStateHandler::kDefaultCheckPortalList);
   GetAutoEnrollmentController()->Start();
-  host_->PrewarmAuthentication();
+  GetLoginDisplayHost()->PrewarmAuthentication();
   network_portal_detector::GetInstance()->Enable(true);
 }
 
@@ -1180,7 +1190,7 @@
 void WizardController::ShowCurrentScreen() {
   // ShowCurrentScreen may get called by smooth_show_timer_ even after
   // flow has been switched to sign in screen (ExistingUserController).
-  if (!oobe_ui_)
+  if (!GetOobeUI())
     return;
 
   // First remember how far have we reached so that we can resume if needed.
@@ -1201,7 +1211,7 @@
   VLOG(1) << "SetCurrentScreenSmooth: "
           << GetOobeScreenName(new_current->screen_id());
   if (current_screen_ == new_current || new_current == nullptr ||
-      oobe_ui_ == nullptr) {
+      GetOobeUI() == nullptr) {
     return;
   }
 
@@ -1232,7 +1242,7 @@
   if (screen == OobeScreen::SCREEN_OOBE_WELCOME) {
     // Hide the status area initially; it only appears after OOBE first animates
     // in. Keep it visible if the user goes back to the existing welcome screen.
-    host_->SetStatusAreaVisible(
+    GetLoginDisplayHost()->SetStatusAreaVisible(
         screen_manager_->HasScreen(OobeScreen::SCREEN_OOBE_WELCOME));
   } else if (screen == OobeScreen::SCREEN_OOBE_RESET ||
              screen == OobeScreen::SCREEN_KIOSK_ENABLE ||
@@ -1242,14 +1252,14 @@
              screen == OobeScreen::SCREEN_ARC_KIOSK_SPLASH ||
              screen == OobeScreen::SCREEN_OOBE_CONTROLLER_PAIRING ||
              screen == OobeScreen::SCREEN_OOBE_HOST_PAIRING) {
-    host_->SetStatusAreaVisible(false);
+    GetLoginDisplayHost()->SetStatusAreaVisible(false);
   } else {
-    host_->SetStatusAreaVisible(true);
+    GetLoginDisplayHost()->SetStatusAreaVisible(true);
   }
 }
 
 void WizardController::OnHIDScreenNecessityCheck(bool screen_needed) {
-  if (!oobe_ui_)
+  if (!GetOobeUI())
     return;
 
   if (screen_needed) {
@@ -1337,7 +1347,7 @@
         base::Callback<void(bool)> on_check =
             base::Bind(&WizardController::OnHIDScreenNecessityCheck,
                        weak_factory_.GetWeakPtr());
-        oobe_ui_->GetHIDDetectionView()->CheckIsScreenRequired(on_check);
+        GetOobeUI()->GetHIDDetectionView()->CheckIsScreenRequired(on_check);
       } else {
         ShowWelcomeScreen();
       }
@@ -1623,7 +1633,7 @@
     // If the |cros_settings_| are permanently untrusted, show an error message
     // and refuse to auto-launch the kiosk app.
     GetErrorScreen()->SetUIState(NetworkError::UI_STATE_LOCAL_STATE_ERROR);
-    host_->SetStatusAreaVisible(false);
+    GetLoginDisplayHost()->SetStatusAreaVisible(false);
     ShowErrorScreen();
     return;
   }
@@ -1636,7 +1646,7 @@
 
   const bool diagnostic_mode = false;
   const bool auto_launch = true;
-  host_->StartAppLaunch(app_id, diagnostic_mode, auto_launch);
+  GetLoginDisplayHost()->StartAppLaunch(app_id, diagnostic_mode, auto_launch);
 }
 
 // static
@@ -1664,16 +1674,16 @@
 // static
 void WizardController::SkipPostLoginScreensForTesting() {
   skip_post_login_screens_ = true;
-  if (!default_controller_ || !default_controller_->current_screen())
+  if (!default_controller() || !default_controller()->current_screen())
     return;
 
   const OobeScreen current_screen_id =
-      default_controller_->current_screen()->screen_id();
+      default_controller()->current_screen()->screen_id();
   if (current_screen_id == OobeScreen::SCREEN_TERMS_OF_SERVICE ||
       current_screen_id == OobeScreen::SCREEN_SYNC_CONSENT ||
       current_screen_id == OobeScreen::SCREEN_ARC_TERMS_OF_SERVICE ||
       current_screen_id == OobeScreen::SCREEN_USER_IMAGE_PICKER) {
-    default_controller_->OnOobeFlowFinished();
+    default_controller()->OnOobeFlowFinished();
   } else {
     LOG(WARNING) << "SkipPostLoginScreensForTesting(): Ignore screen "
                  << static_cast<int>(current_screen_id);
@@ -1698,7 +1708,7 @@
     return;
   }
   GetErrorScreen()->SetUIState(NetworkError::UI_STATE_LOCAL_STATE_ERROR);
-  host_->SetStatusAreaVisible(false);
+  GetLoginDisplayHost()->SetStatusAreaVisible(false);
   ShowErrorScreen();
 }
 
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h
index 588f4e51..4b5cc9a5 100644
--- a/chrome/browser/chromeos/login/wizard_controller.h
+++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -44,7 +44,6 @@
 struct Geoposition;
 class LoginDisplayHost;
 class LoginScreenContext;
-class OobeUI;
 class SimpleGeolocationProvider;
 class TimeZoneProvider;
 struct TimeZoneResponseData;
@@ -59,11 +58,12 @@
                          public HIDDetectionScreen::Delegate,
                          public OobeConfiguration::Observer {
  public:
-  WizardController(LoginDisplayHost* host, OobeUI* oobe_ui);
+  WizardController();
   ~WizardController() override;
 
-  // Returns the default wizard controller if it has been created.
-  static WizardController* default_controller() { return default_controller_; }
+  // Returns the default wizard controller if it has been created. This is a
+  // helper for LoginDisplayHost::default_host()->GetWizardController();
+  static WizardController* default_controller();
 
   // Whether to skip any screens that may normally be shown after login
   // (registration, Terms of Service, user image selection).
@@ -144,7 +144,7 @@
 
   // Allocate a given BaseScreen for the given |Screen|. Used by
   // |screen_manager_|.
-  BaseScreen* CreateScreen(OobeScreen screen);
+  std::unique_ptr<BaseScreen> CreateScreen(OobeScreen screen);
 
   // Set the current screen. For Test use only.
   void SetCurrentScreenForTesting(BaseScreen* screen);
@@ -393,16 +393,8 @@
   // Value of the screen name that WizardController was started with.
   OobeScreen first_screen_;
 
-  // OOBE/login display host.
-  LoginDisplayHost* host_ = nullptr;
-
-  // Default WizardController.
-  static WizardController* default_controller_;
-
   base::OneShotTimer smooth_show_timer_;
 
-  OobeUI* const oobe_ui_;
-
   // State of Usage stat/error reporting checkbox on EULA screen
   // during wizard lifetime.
   bool usage_statistics_reporting_ = true;
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
index aa044a3..d3c65a1 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_apitest.cc
@@ -224,8 +224,15 @@
   ASSERT_FALSE(profile()->HasOffTheRecordProfile());
 }
 
+#if defined(OS_MACOSX)
+// Timeout on Mac. crbug.com/864250
+#define MAYBE_OffscreenTabEvilTests DISABLED_OffscreenTabEvilTests
+#else
+#define MAYBE_OffscreenTabEvilTests OffscreenTabEvilTests
+#endif
+
 // Tests that off-screen tabs can't do evil things (e.g., access local files).
-IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, OffscreenTabEvilTests) {
+IN_PROC_BROWSER_TEST_F(TabCaptureApiPixelTest, MAYBE_OffscreenTabEvilTests) {
   if (IsTooIntensiveForThisPlatform()) {
     LOG(WARNING) << "Skipping this CPU-intensive test on this platform/build.";
     return;
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index f2e7c6f..6dbd04f 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -53,7 +53,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/window_sizer/window_sizer.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/api/tabs.h"
 #include "chrome/common/extensions/api/windows.h"
diff --git a/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc b/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc
index e4cfe51d..4dd5db6a 100644
--- a/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc
+++ b/chrome/browser/extensions/bookmark_app_navigation_throttle_browsertest.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc b/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc
index 94ee3ff..9466a41 100644
--- a/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc
+++ b/chrome/browser/extensions/bookmark_app_navigation_throttle_utils.cc
@@ -11,7 +11,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
diff --git a/chrome/browser/extensions/browsertest_util.cc b/chrome/browser/extensions/browsertest_util.cc
index d085db6..7a917e00 100644
--- a/chrome/browser/extensions/browsertest_util.cc
+++ b/chrome/browser/extensions/browsertest_util.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/web_application_info.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
diff --git a/chrome/browser/extensions/tab_helper.cc b/chrome/browser/extensions/tab_helper.cc
index ec493e80..ce98467 100644
--- a/chrome/browser/extensions/tab_helper.cc
+++ b/chrome/browser/extensions/tab_helper.cc
@@ -29,7 +29,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/extensions/api/webstore/webstore_api_constants.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 33b48ec..dc8cea4 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2353,8 +2353,15 @@
 const char kHomePageButtonName[] = "Force Enable Home Page Button";
 const char kHomePageButtonDescription[] = "Displays a home button if enabled.";
 
+const char kHomepageTileName[] =
+    "Enable Homepage tile shown in Suggested Tiles";
+const char kHomepageTileDescription[] =
+    "When NTPButton is enabled, the first tile of the Suggested Tiles will be "
+    "used for homepage. It will not have an effect when NTPButton is disabled.";
+
 const char kInterestFeedContentSuggestionsDescription[] =
-    "Use the interest feed to render content suggestions. Currently content "
+    "Use the interest feed to render content suggestions. Currently "
+    "content "
     "suggestions are shown on the New Tab Page.";
 const char kInterestFeedContentSuggestionsName[] =
     "Interest Feed Content Suggestions";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 22bfea0e..076d6e6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1427,6 +1427,9 @@
 extern const char kHomePageButtonName[];
 extern const char kHomePageButtonDescription[];
 
+extern const char kHomepageTileName[];
+extern const char kHomepageTileDescription[];
+
 extern const char kInterestFeedContentSuggestionsName[];
 extern const char kInterestFeedContentSuggestionsDescription[];
 
diff --git a/chrome/browser/payments/android/journey_logger_android.cc b/chrome/browser/payments/android/journey_logger_android.cc
index 9d2edea..8280ce7 100644
--- a/chrome/browser/payments/android/journey_logger_android.cc
+++ b/chrome/browser/payments/android/journey_logger_android.cc
@@ -5,9 +5,9 @@
 #include "chrome/browser/payments/android/journey_logger_android.h"
 
 #include "base/android/jni_string.h"
+#include "components/ukm/content/source_url_recorder.h"
+#include "content/public/browser/web_contents.h"
 #include "jni/JourneyLogger_jni.h"
-#include "services/metrics/public/cpp/ukm_recorder.h"
-#include "url/gurl.h"
 
 namespace payments {
 namespace {
@@ -18,8 +18,8 @@
 }  // namespace
 
 JourneyLoggerAndroid::JourneyLoggerAndroid(bool is_incognito,
-                                           const std::string& url)
-    : journey_logger_(is_incognito, GURL(url), ukm::UkmRecorder::Get()) {}
+                                           ukm::SourceId source_id)
+    : journey_logger_(is_incognito, source_id) {}
 
 JourneyLoggerAndroid::~JourneyLoggerAndroid() {}
 
@@ -137,9 +137,11 @@
     JNIEnv* env,
     const JavaParamRef<jobject>& jcaller,
     jboolean jis_incognito,
-    const base::android::JavaParamRef<jstring>& jurl) {
+    const JavaParamRef<jobject>& jweb_contents) {
+  content::WebContents* web_contents =
+      content::WebContents::FromJavaWebContents(jweb_contents);
   return reinterpret_cast<jlong>(new JourneyLoggerAndroid(
-      jis_incognito, ConvertJavaStringToUTF8(env, jurl)));
+      jis_incognito, ukm::GetSourceIdForWebContentsDocument(web_contents)));
 }
 
 }  // namespace payments
diff --git a/chrome/browser/payments/android/journey_logger_android.h b/chrome/browser/payments/android/journey_logger_android.h
index e2ca58b..ed9a062 100644
--- a/chrome/browser/payments/android/journey_logger_android.h
+++ b/chrome/browser/payments/android/journey_logger_android.h
@@ -8,13 +8,14 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
 #include "components/payments/core/journey_logger.h"
+#include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace payments {
 
 // Forwarding calls to payments::JourneyLogger.
 class JourneyLoggerAndroid {
  public:
-  JourneyLoggerAndroid(bool is_incognito, const std::string& url);
+  JourneyLoggerAndroid(bool is_incognito, ukm::SourceId source_id);
   ~JourneyLoggerAndroid();
 
   // Message from Java to destroy this object.
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 6970e77..20f74be 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -64,7 +64,7 @@
 #include "chrome/browser/ui/exclusive_access/keyboard_lock_controller.h"
 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_render_frame.mojom.h"
diff --git a/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.cc b/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.cc
index 4fa7171..aef0bc89 100644
--- a/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.cc
+++ b/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.cc
@@ -19,7 +19,8 @@
 
 void DiscardMetricsLifecycleUnitObserver::OnLifecycleUnitStateChanged(
     LifecycleUnit* lifecycle_unit,
-    LifecycleUnitState last_state) {
+    LifecycleUnitState last_state,
+    LifecycleUnitStateChangeReason reason) {
   if (lifecycle_unit->GetState() == LifecycleUnitState::DISCARDED)
     OnDiscard(lifecycle_unit);
   else if (last_state == LifecycleUnitState::DISCARDED)
diff --git a/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.h b/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.h
index 426d0ff3..55e5ef46 100644
--- a/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.h
+++ b/chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.h
@@ -22,8 +22,10 @@
   ~DiscardMetricsLifecycleUnitObserver() override;
 
   // LifecycleUnitObserver:
-  void OnLifecycleUnitStateChanged(LifecycleUnit* lifecycle_unit,
-                                   LifecycleUnitState last_state) override;
+  void OnLifecycleUnitStateChanged(
+      LifecycleUnit* lifecycle_unit,
+      LifecycleUnitState last_state,
+      LifecycleUnitStateChangeReason reason) override;
   void OnLifecycleUnitDestroyed(LifecycleUnit* lifecycle_unit) override;
 
  private:
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_base.cc b/chrome/browser/resource_coordinator/lifecycle_unit_base.cc
index a6e6563..54937b0 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_base.cc
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_base.cc
@@ -63,7 +63,7 @@
   state_change_time_ = NowTicks();
   OnLifecycleUnitStateChanged(last_state, reason);
   for (auto& observer : observers_)
-    observer.OnLifecycleUnitStateChanged(this, last_state);
+    observer.OnLifecycleUnitStateChanged(this, last_state, reason);
 }
 
 void LifecycleUnitBase::OnLifecycleUnitStateChanged(
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_base_unittest.cc b/chrome/browser/resource_coordinator/lifecycle_unit_base_unittest.cc
index 4fcfff04..3abe79d 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_base_unittest.cc
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_base_unittest.cc
@@ -23,8 +23,10 @@
  public:
   MockLifecycleUnitObserver() = default;
 
-  MOCK_METHOD2(OnLifecycleUnitStateChanged,
-               void(LifecycleUnit*, LifecycleUnitState));
+  MOCK_METHOD3(OnLifecycleUnitStateChanged,
+               void(LifecycleUnit*,
+                    LifecycleUnitState,
+                    LifecycleUnitStateChangeReason));
   MOCK_METHOD2(OnLifecycleUnitVisibilityChanged,
                void(LifecycleUnit*, content::Visibility));
   MOCK_METHOD1(OnLifecycleUnitDestroyed, void(LifecycleUnit*));
@@ -101,8 +103,10 @@
   lifecycle_unit.AddObserver(&observer_);
 
   // Observer is notified when the state changes.
-  EXPECT_CALL(observer_, OnLifecycleUnitStateChanged(
-                             &lifecycle_unit, lifecycle_unit.GetState()));
+  EXPECT_CALL(observer_,
+              OnLifecycleUnitStateChanged(
+                  &lifecycle_unit, lifecycle_unit.GetState(),
+                  LifecycleUnitStateChangeReason::BROWSER_INITIATED));
   lifecycle_unit.SetState(LifecycleUnitState::DISCARDED,
                           LifecycleUnitStateChangeReason::BROWSER_INITIATED);
   testing::Mock::VerifyAndClear(&observer_);
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_observer.cc b/chrome/browser/resource_coordinator/lifecycle_unit_observer.cc
index 65a3fb8..4c7ddb6e 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_observer.cc
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_observer.cc
@@ -10,7 +10,8 @@
 
 void LifecycleUnitObserver::OnLifecycleUnitStateChanged(
     LifecycleUnit* lifecycle_unit,
-    LifecycleUnitState last_state) {}
+    LifecycleUnitState last_state,
+    LifecycleUnitStateChangeReason reason) {}
 
 void LifecycleUnitObserver::OnLifecycleUnitVisibilityChanged(
     LifecycleUnit* lifecycle_unit,
diff --git a/chrome/browser/resource_coordinator/lifecycle_unit_observer.h b/chrome/browser/resource_coordinator/lifecycle_unit_observer.h
index 6e4560bf8..4ef437c 100644
--- a/chrome/browser/resource_coordinator/lifecycle_unit_observer.h
+++ b/chrome/browser/resource_coordinator/lifecycle_unit_observer.h
@@ -11,6 +11,7 @@
 namespace resource_coordinator {
 
 using ::mojom::LifecycleUnitState;
+using ::mojom::LifecycleUnitStateChangeReason;
 
 class LifecycleUnit;
 
@@ -20,8 +21,10 @@
   virtual ~LifecycleUnitObserver();
 
   // Invoked when the state of the observed LifecycleUnit changes.
-  virtual void OnLifecycleUnitStateChanged(LifecycleUnit* lifecycle_unit,
-                                           LifecycleUnitState last_state);
+  virtual void OnLifecycleUnitStateChanged(
+      LifecycleUnit* lifecycle_unit,
+      LifecycleUnitState last_state,
+      LifecycleUnitStateChangeReason reason);
 
   // Invoked when the visibility of the observed LifecyleUnit changes.
   virtual void OnLifecycleUnitVisibilityChanged(LifecycleUnit* lifecycle_unit,
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
index b5f56b51..91d2a0ac 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -74,8 +74,10 @@
  public:
   MockLifecycleUnitObserver() = default;
 
-  MOCK_METHOD2(OnLifecycleUnitStateChanged,
-               void(LifecycleUnit* lifecycle_unit, LifecycleUnitState));
+  MOCK_METHOD3(OnLifecycleUnitStateChanged,
+               void(LifecycleUnit* lifecycle_unit,
+                    LifecycleUnitState,
+                    LifecycleUnitStateChangeReason));
   MOCK_METHOD2(OnLifecycleUnitVisibilityChanged,
                void(LifecycleUnit* lifecycle_unit,
                     content::Visibility visibility));
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 940e7419..0b2d807 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -61,8 +61,10 @@
  public:
   MockLifecycleUnitObserver() = default;
 
-  MOCK_METHOD2(OnLifecycleUnitStateChanged,
-               void(LifecycleUnit*, LifecycleUnitState));
+  MOCK_METHOD3(OnLifecycleUnitStateChanged,
+               void(LifecycleUnit*,
+                    LifecycleUnitState,
+                    LifecycleUnitStateChangeReason));
   MOCK_METHOD1(OnLifecycleUnitDestroyed, void(LifecycleUnit*));
   MOCK_METHOD2(OnLifecycleUnitVisibilityChanged,
                void(LifecycleUnit*, content::Visibility));
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index eb20716..e52483c8 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -1102,8 +1102,10 @@
   return base::TimeTicks::Max();
 }
 
-void TabManager::OnLifecycleUnitStateChanged(LifecycleUnit* lifecycle_unit,
-                                             LifecycleUnitState last_state) {
+void TabManager::OnLifecycleUnitStateChanged(
+    LifecycleUnit* lifecycle_unit,
+    LifecycleUnitState last_state,
+    LifecycleUnitStateChangeReason reason) {
   LifecycleUnitState state = lifecycle_unit->GetState();
   bool was_discarded = (last_state == LifecycleUnitState::PENDING_DISCARD ||
                         last_state == LifecycleUnitState::DISCARDED);
diff --git a/chrome/browser/resource_coordinator/tab_manager.h b/chrome/browser/resource_coordinator/tab_manager.h
index d02e58a7..83b7c1f 100644
--- a/chrome/browser/resource_coordinator/tab_manager.h
+++ b/chrome/browser/resource_coordinator/tab_manager.h
@@ -462,8 +462,10 @@
       LifecycleUnit* lifecycle_unit,
       content::Visibility visibility) override;
   void OnLifecycleUnitDestroyed(LifecycleUnit* lifecycle_unit) override;
-  void OnLifecycleUnitStateChanged(LifecycleUnit* lifecycle_unit,
-                                   LifecycleUnitState last_state) override;
+  void OnLifecycleUnitStateChanged(
+      LifecycleUnit* lifecycle_unit,
+      LifecycleUnitState last_state,
+      LifecycleUnitStateChangeReason reason) override;
 
   // LifecycleUnitSourceObserver:
   void OnLifecycleUnitCreated(LifecycleUnit* lifecycle_unit) override;
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index 0a930729..db2b3eda 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -99,8 +99,10 @@
 
  private:
   // LifecycleUnitObserver:
-  void OnLifecycleUnitStateChanged(LifecycleUnit* lifecycle_unit,
-                                   LifecycleUnitState last_state) override {
+  void OnLifecycleUnitStateChanged(
+      LifecycleUnit* lifecycle_unit,
+      LifecycleUnitState last_state,
+      LifecycleUnitStateChangeReason reason) override {
     EXPECT_EQ(lifecycle_unit, lifecycle_unit_);
     if (lifecycle_unit_->GetState() == expected_state_) {
       run_loop_.Quit();
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.css b/chrome/browser/resources/chromeos/login/offline_ad_login.css
new file mode 100644
index 0000000..b4c9f83
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/offline_ad_login.css
@@ -0,0 +1,21 @@
+/* Copyright 2018 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* Offline AD login page mostly uses styles from offline_gaia.css.
+ * Only changes to offline gaia style are defined here.
+ */
+
+.advanced-option-subtitle {
+  color: rgba(0, 0, 0, 0.54);
+  font: 13px Roboto, sans-serif;
+}
+
+iron-icon[icon='warning'] {
+  -webkit-margin-end: 15px;
+  -webkit-margin-start: 0;
+  color: var(--google-yellow-500);
+  margin-bottom: 0;
+  margin-top: 0;
+}
diff --git a/chrome/browser/resources/chromeos/login/offline_ad_login.html b/chrome/browser/resources/chromeos/login/offline_ad_login.html
index 2bed4df..1fbb55b 100644
--- a/chrome/browser/resources/chromeos/login/offline_ad_login.html
+++ b/chrome/browser/resources/chromeos/login/offline_ad_login.html
@@ -47,6 +47,7 @@
   <link rel="stylesheet" href="offline_gaia.css">
   <link rel="stylesheet" href="oobe_flex_layout.css">
   <link rel="stylesheet" href="gaia_card_parameters.css">
+  <link rel="stylesheet" href="offline_ad_login.css">
   <template>
     <style include="md-select"></style>
     <gaia-card id="gaiaCard" class="fit">
diff --git a/chrome/browser/resources/chromeos/login/offline_gaia.css b/chrome/browser/resources/chromeos/login/offline_gaia.css
index 7e65290..08c58858 100644
--- a/chrome/browser/resources/chromeos/login/offline_gaia.css
+++ b/chrome/browser/resources/chromeos/login/offline_gaia.css
@@ -3,6 +3,12 @@
  * found in the LICENSE file. */
 
 :host {
+  --title-font-size: 28px;
+  --title-font-distance-to-baseline: 7px;
+  --subtitle-font-size: 13px;
+  --subtitle-font-distance-to-baseline: 3px;
+  --subtitle-line-height: 18px;
+  --offline-gaia-dialog-width: 768px;
   display: flex;
   flex-direction: column;
   font-size: 18px;
@@ -18,68 +24,72 @@
   }
 }
 
-/*
- * Due to |margin-right| of e-mail section required for animations, containers
- * store oversized items.
- */
-#headerContainer,
-#footerContainer {
-  max-width: 640px;
-  min-width: 640px;
+/* icon, title, subtitle styles must approximate current Gaia style. */
+
+#icon {
+  height: 32px;
+  margin: 60px 64px 0 64px;
 }
 
-/*
- * e-mail and password section headers have slightly different size.
- * To make animations move same size objects, fix it here.
- */
-#headerContainer {
-  min-height: 90px;
+#title-container {
+  min-height: calc(64px + var(--title-font-distance-to-baseline));
+}
+
+h1 {
+  color: var(--google-grey-900);
+  font-size: var(--title-font-size);
+  font-weight: normal;
+  margin: 0;
+}
+
+#subtitle-container {
+  min-height: calc(64px - var(--title-font-distance-to-baseline));
+}
+
+#subtitle-container * {
+  color: var(--google-grey-900);
+  font-size: var(--subtitle-font-size);
+  line-height: var(--subtitle-line-height);
+  /* margin 12px = 32 - line-height
+                   - 5 (lihe height - font size)
+                   + 3 (distance to baseline 13px)
+                 =  32 + font size + distance to baseline 13px */
+  margin: calc(32 + var(--subtitle-font-size)
+               + var(--subtitle-font-distance-to-baseline)) 0 0 0;
 }
 
 /** ******** Animations ******* */
 
-
 /*
  * Normally, only e-mail section is animated, pushing password section to the
  * right outside of visible area.
  */
 
-/*
- * Animation of e-mail parts (to the 'start' to hide, and to the 'end' to show)
- * allows password section to either slide-in (from the right) or slide-out
- * (to the right).
- *
- * --webkit-margin-start doesn't work with animations, so use conventional
- * left/right margins.
- */
-
-:host(:not([rtl])) .email-section {
-  /*
-   * OOBE dialog header/footer width is less than oobe-dialog width.
-   * To make sure password section is completely shifted out of visible area,
-   * we need right margin that matches dialog margins.
-   */
-  margin-left: 0;
-  margin-right: 128px; /** double dialog margin = 2 * (768 - 640) / 2 */
+/* Fixed window over sliding content in #animation-inner-container. */
+#animation-outer-container {
+  overflow: hidden;
+  width: var(--offline-gaia-dialog-width);
 }
 
-:host([rtl]) .email-section {
-  margin-left: 128px; /** double dialog margin = 2 * (768 - 640) / 2 */
-  margin-right: 0;
+#animation-inner-container {
+  width: calc(2 * var(--offline-gaia-dialog-width));
 }
 
-
 .section {
+  --section-padding: 64px;
+  --section-width: calc(var(--offline-gaia-dialog-width)
+                         - 2 * var(--section-padding));
   animation-duration: 700ms;
   display: none;
   /** For sliding to work correctly we need fixed size of moving objects. */
-  max-width: 640px;
-  min-width: 640px;
+  max-width: var(--section-width);
+  min-width: var(--section-width);
+  padding: 0 var(--section-padding);
 }
 
 @keyframes show-from-left {
   from {
-    margin-left: -768px; /** container width + double margin = 640 + 2 * 64 */
+    margin-left: -768px; /** Full dialog width negative. */
   }
   to {
     margin-left: 0;
@@ -88,10 +98,10 @@
 
 @keyframes show-from-right {
   from {
-    margin-right: -768px; /** container width + double margin = 640 + 2 * 64 */
+    margin-left: var(--offline-gaia-dialog-width);
   }
   to {
-    margin-right: 0;
+    margin-left: 0;
   }
 }
 
@@ -100,21 +110,21 @@
     margin-left: 0;
   }
   to {
-    margin-left: -768px; /** container width + double margin = 640 + 2 * 64 */
+    margin-left: calc(-1 * var(--offline-gaia-dialog-width));
   }
 }
 
 @keyframes hide-to-right {
   from {
-    margin-right: 0;
+    margin-left: 0;
   }
   to {
-    margin-right: -768px; /** container width + double margin = 640 + 2 * 64 */
+    margin-left: var(--offline-gaia-dialog-width);
   }
 }
 
-oobe-dialog[selected='emailSection'] .email-section,
-oobe-dialog[selected='passwordSection'] .password-section {
+oobe-dialog[selected='emailSection'] #email-section,
+oobe-dialog[selected='passwordSection'] #password-section {
   display: block;
 }
 
@@ -123,19 +133,19 @@
  * Dialog always starts with e-mail section visible, so only "show" animation
  * depends on |animation-in-progress| attribute.
  */
-oobe-dialog[animation-in-progress] .email-section {
+oobe-dialog[animation-in-progress] #email-section {
   animation-name: show-from-left;
 }
 
-oobe-dialog[selected='passwordSection'] .email-section {
+oobe-dialog[selected='passwordSection'] #email-section {
   animation-name: hide-to-left;
 }
 
-:host([rtl]) oobe-dialog[animation-in-progress] .email-section {
+:host([rtl]) oobe-dialog[animation-in-progress] #email-section {
   animation-name: show-from-right;
 }
 
-:host([rtl]) oobe-dialog[selected='passwordSection'] .email-section {
+:host([rtl]) oobe-dialog[selected='passwordSection'] #email-section {
   animation-name: hide-to-right;
 }
 
@@ -143,16 +153,3 @@
 oobe-dialog[animation-in-progress] .section {
   display: block;
 }
-
-.advanced-option-subtitle {
-  color: rgba(0, 0, 0, 0.54);
-  font: 13px Roboto, sans-serif;
-}
-
-iron-icon[icon='warning'] {
-  -webkit-margin-end: 15px;
-  -webkit-margin-start: 0;
-  color: var(--google-yellow-500);
-  margin-bottom: 0;
-  margin-top: 0;
-}
diff --git a/chrome/browser/resources/chromeos/login/offline_gaia.html b/chrome/browser/resources/chromeos/login/offline_gaia.html
index 72b2ae09..2a5ea7c2 100644
--- a/chrome/browser/resources/chromeos/login/offline_gaia.html
+++ b/chrome/browser/resources/chromeos/login/offline_gaia.html
@@ -53,27 +53,25 @@
     <link rel="stylesheet" href="oobe_flex_layout.css">
     <link rel="stylesheet" href="oobe_dialog_host.css">
     <link rel="stylesheet" href="gaia_card_parameters.css">
-    <template is="dom-if" if="[[glifMode]]" restamp>
-      <oobe-dialog role="dialog" has-buttons selected$="[[activeSection]]"
-          animation-in-progress$="[[animationInProgress]]">
-        <img src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90"
-            slot="oobe-icon" alt="">
-        <div id="headerContainer" slot="header" class="layout horizontal"
-            on-animationend="onSlideAnimationEnd_">
-          <div id="emailSectionHeader" class="section email-section">
-            <h1 class="title">
-              [[i18nDynamic(locale, 'loginWelcomeMessage')]]
-            </h1>
-            <p id="managedBy" class="enterprise-info"
-                hidden$="[[!showEnterpriseMessage]]">
-            </p>
-          </div>
-          <div id="passwordSectionHeader" class="section password-section">
-            <gaia-header class="title" id="passwordHeader"></gaia-header>
-          </div>
-        </div>
-        <div id="footerContainer" slot="footer" class="layout horizontal">
-          <div id="emailSectionFooter" class="section email-section">
+    <oobe-dialog role="dialog" has-buttons selected$="[[activeSection]]"
+        animation-in-progress$="[[animationInProgress]]" no-header
+        no-footer-padding>
+      <div slot="footer">
+        <img id="icon" src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90" alt="">
+      </div>
+      <div id="animation-outer-container" slot="footer">
+        <div id="animation-inner-container" class="flex layout horizontal">
+          <div id="email-section" class="section"
+              on-animationend="onSlideAnimationEnd_">
+            <div id="title-container">
+              <h1>[[i18nDynamic(locale, 'loginWelcomeMessage')]]</h1>
+            </div>
+            <div id="subtitle-container">
+              <div id="managedBy" class="enterprise-info"
+                  hidden$="[[!domain]]">
+                  [[i18nDynamic(locale, 'enterpriseInfoMessage', domain)]]
+              </div>
+            </div>
             <gaia-input-form on-submit="onEmailSubmitted_"
                 disabled="[[disabled]]"
                 button-text="[[i18nDynamic(locale, 'offlineLoginNextBtn')]]">
@@ -84,8 +82,13 @@
               </gaia-input>
             </gaia-input-form>
           </div>
-          <div id="passwordSectionFooter" class="section password-section">
-            <gaia-input-form slot="footer" disabled="[[disabled]]"
+          <div id="password-section" class="section">
+            <div id="title-container">
+              <gaia-header id="passwordHeader"></gaia-header>
+            </div>
+            <div id="subtitle-container">
+            </div>
+            <gaia-input-form disabled="[[disabled]]"
                 on-submit="onPasswordSubmitted_"
                 button-text="[[i18nDynamic(locale, 'offlineLoginNextBtn')]]">
               <gaia-input slot="inputs" id="passwordInput" type="password"
@@ -100,67 +103,14 @@
             </gaia-input-form>
           </div>
         </div>
-        <div slot="bottom-buttons" class="flex layout horizontal">
-          <oobe-back-button id="offline-gaia-back-button"
-              on-tap="onBackButtonClicked_"></oobe-back-button>
-          <div class="flex">
-          </div>
+      </div>
+      <div slot="bottom-buttons" class="flex layout horizontal">
+        <oobe-back-button id="offline-gaia-back-button"
+            on-tap="onBackButtonClicked_"></oobe-back-button>
+        <div class="flex">
         </div>
-      </oobe-dialog>
-    </template>
-
-    <template is="dom-if" if="[[!glifMode]]" restamp>
-      <neon-animated-pages id="animatedPages" class="fit" attr-for-selected="id"
-          entry-animation="slide-from-right-animation"
-          exit-animation="slide-to-left-animation"
-          on-neon-animation-finish="onAnimationFinish_" selected="emailSection">
-
-        <neon-animatable id="emailSection" class="fit">
-            <gaia-card class="fit">
-              <div slot="header"
-                  class="flex vertical layout end-justified start">
-                <h1 class="welcome-message" i18n-content="loginWelcomeMessage">
-                </h1>
-                <p id="managedBy" class="enterprise-info"
-                    hidden$="[[!showEnterpriseMessage]]">
-                </p>
-              </div>
-              <div slot="footer" class="flex vertical layout justified">
-                <gaia-input-form on-submit="onEmailSubmitted_"
-                    disabled="[[disabled]]"
-                    i18n-values="button-text:offlineLoginNextBtn">
-                  <gaia-input slot="inputs" id="emailInput" type="email"
-                      domain="[[emailDomain]]"
-                      i18n-values="error:offlineLoginInvalidEmail;
-                                   label:offlineLoginEmail" required>
-                  </gaia-input>
-                </gaia-input-form>
-                <img src="chrome://theme/IDR_LOGO_GOOGLE_COLOR_90"
-                    class="self-center" alt="">
-              </div>
-            </gaia-card>
-        </neon-animatable>
-
-        <neon-animatable id="passwordSection" class="fit">
-            <gaia-card id="passwordCard" class="fit">
-              <gaia-header slot="header" class="flex" id="passwordHeader">
-              </gaia-header>
-              <gaia-input-form slot="footer" disabled="[[disabled]]"
-                  on-submit="onPasswordSubmitted_"
-                  i18n-values="button-text:offlineLoginNextBtn">
-                <gaia-input slot="inputs" id="passwordInput" type="password"
-                    i18n-values="error:offlineLoginInvalidPassword;
-                                 label:offlineLoginPassword" required>
-                </gaia-input>
-                <gaia-button type="link" on-tap="onForgotPasswordClicked_"
-                    i18n-content="offlineLoginForgotPasswordBtn">
-                </gaia-button>
-              </gaia-input-form>
-            </gaia-card>
-        </neon-animatable>
-      </neon-animated-pages>
-    </template>
-
+      </div>
+    </oobe-dialog>
     <cr-dialog id="forgotPasswordDlg"
         on-close="onDialogOverlayClosed_">
       <div slot="body"
diff --git a/chrome/browser/resources/chromeos/login/offline_gaia.js b/chrome/browser/resources/chromeos/login/offline_gaia.js
index 372edd6..39b39eae 100644
--- a/chrome/browser/resources/chromeos/login/offline_gaia.js
+++ b/chrome/browser/resources/chromeos/login/offline_gaia.js
@@ -18,14 +18,8 @@
         value: false,
       },
 
-      showEnterpriseMessage: {
-        type: Boolean,
-        value: false,
-      },
-
       domain: {
         type: String,
-        observer: 'onDomainChanged_',
       },
 
       emailDomain: String,
@@ -36,14 +30,6 @@
       },
 
       animationInProgress: Boolean,
-
-      /**
-       * Controls GLIF MM mode.
-       */
-      glifMode: {
-        type: Boolean,
-        value: false,
-      },
     },
 
     attached: function() {
@@ -62,12 +48,6 @@
       this.switchToEmailCard(true /* animated */);
     },
 
-    onDomainChanged_: function() {
-      this.$$('#managedBy').textContent =
-          loadTimeData.getStringF('enterpriseInfoMessage', this.domain);
-      this.showEnterpriseMessage = !!this.domain.length;
-    },
-
     onAnimationFinish_: function() {
       this.fire('backButton', !this.isEmailSectionActive_());
       this.focus();
@@ -77,7 +57,6 @@
       this.disabled = true;
       this.fire('dialogShown');
       this.$$('#forgotPasswordDlg').showModal();
-      this.$$('#passwordCard').classList.add('full-disabled');
     },
 
     onForgotPasswordCloseTap_: function() {
@@ -87,7 +66,6 @@
     onDialogOverlayClosed_: function() {
       this.fire('dialogHidden');
       this.disabled = false;
-      this.$$('#passwordCard').classList.remove('full-disabled');
     },
 
     setEmail: function(email) {
diff --git a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
index 65b14d53..1611e5b 100644
--- a/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
+++ b/chrome/browser/resources/chromeos/login/screen_gaia_signin.js
@@ -716,14 +716,12 @@
         $('signin-frame-container-v2').appendChild($('signin-frame'));
         $('gaia-signin')
             .insertBefore($('offline-gaia'), $('gaia-step-contents'));
-        $('offline-gaia').glifMode = true;
         $('offline-gaia').removeAttribute('not-a-dialog');
         $('offline-gaia').classList.toggle('fit', false);
       } else {
         $('gaia-signin-form-container').appendChild($('signin-frame'));
         $('gaia-signin-form-container')
             .appendChild($('offline-gaia'), $('gaia-step-contents'));
-        $('offline-gaia').glifMode = false;
         $('offline-gaia').setAttribute('not-a-dialog', true);
         $('offline-gaia').classList.toggle('fit', true);
       }
diff --git a/chrome/browser/resources/print_preview/new/model.js b/chrome/browser/resources/print_preview/new/model.js
index ac495e4..4e73435 100644
--- a/chrome/browser/resources/print_preview/new/model.js
+++ b/chrome/browser/resources/print_preview/new/model.js
@@ -741,7 +741,7 @@
       }
     }
     if (this.settings.copies.available)
-      cjt.print.copies = {copies: this.settings.copies.value};
+      cjt.print.copies = {copies: parseInt(this.getSettingValue('copies'), 10)};
     if (this.settings.duplex.available) {
       cjt.print.duplex = {
         type: this.settings.duplex.value ? 'LONG_EDGE' : 'NO_DUPLEX'
@@ -769,7 +769,7 @@
       }
     } else {
       cjt.print.page_orientation = {
-        type: this.settings.layout ? 'LANDSCAPE' : 'PORTRAIT'
+        type: this.settings.layout.value ? 'LANDSCAPE' : 'PORTRAIT'
       };
     }
     if (this.settings.dpi.available) {
diff --git a/chrome/browser/resources/settings/icons.html b/chrome/browser/resources/settings/icons.html
index 6257edc..e3749b6 100644
--- a/chrome/browser/resources/settings/icons.html
+++ b/chrome/browser/resources/settings/icons.html
@@ -138,7 +138,6 @@
       <g id="smartphone"><path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z"></path></g>
 </if>
       <g id="supervisor-account"><path d="M16.5 12c1.38 0 2.49-1.12 2.49-2.5S17.88 7 16.5 7C15.12 7 14 8.12 14 9.5s1.12 2.5 2.5 2.5zM9 11c1.66 0 2.99-1.34 2.99-3S10.66 5 9 5C7.34 5 6 6.34 6 8s1.34 3 3 3zm7.5 3c-1.83 0-5.5.92-5.5 2.75V19h11v-2.25c0-1.83-3.67-2.75-5.5-2.75zM9 13c-2.33 0-7 1.17-7 3.5V19h7v-2.25c0-.85.33-2.34 2.37-3.47C10.5 13.1 9.66 13 9 13z"></path></g>
-      <g id="sync"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"></path></g>
       <g id="sync-disabled"><path d="M10 6.35V4.26c-.8.21-1.55.54-2.23.96l1.46 1.46c.25-.12.5-.24.77-.33zm-7.14-.94l2.36 2.36C4.45 8.99 4 10.44 4 12c0 2.21.91 4.2 2.36 5.64L4 20h6v-6l-2.24 2.24C6.68 15.15 6 13.66 6 12c0-1 .25-1.94.68-2.77l8.08 8.08c-.25.13-.5.25-.77.34v2.09c.8-.21 1.55-.54 2.23-.96l2.36 2.36 1.27-1.27L4.14 4.14 2.86 5.41zM20 4h-6v6l2.24-2.24C17.32 8.85 18 10.34 18 12c0 1-.25 1.94-.68 2.77l1.46 1.46C19.55 15.01 20 13.56 20 12c0-2.21-.91-4.2-2.36-5.64L20 4z"></path></g>
       <g id="sync-problem"><path d="M3 12c0 2.21.91 4.2 2.36 5.64L3 20h6v-6l-2.24 2.24C5.68 15.15 5 13.66 5 12c0-2.61 1.67-4.83 4-5.65V4.26C5.55 5.15 3 8.27 3 12zm8 5h2v-2h-2v2zM21 4h-6v6l2.24-2.24C18.32 8.85 19 10.34 19 12c0 2.61-1.67 4.83-4 5.65v2.09c3.45-.89 6-4.01 6-7.74 0-2.21-.91-4.2-2.36-5.64L21 4zm-10 9h2V7h-2v6z"></path></g>
 <if expr="chromeos">
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
index 79c1bfc6..f2ed052 100644
--- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
+++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
@@ -151,13 +151,14 @@
   /** @override */
   attached: function() {
     // Create listener functions.
-    const setSavedPasswordsListener = list => this.updateList(
-        'savedPasswords',
-        item => `${item.entry.index}_${item.entry.loginPair.urls.shown}`,
-        list.map(entry => ({
-                   entry: entry,
-                   password: '',
-                 })));
+    const setSavedPasswordsListener = list =>
+        this.updateList('savedPasswords', item => {
+          return item.entry.loginPair.urls.origin + '_' +
+              item.entry.loginPair.username;
+        }, list.map(entry => ({
+                      entry: entry,
+                      password: '',
+                    })));
 
     const setPasswordExceptionsListener = list => {
       this.passwordExceptions = list;
diff --git a/chrome/browser/resources/settings/people_page/people_page.html b/chrome/browser/resources/settings/people_page/people_page.html
index 4097de8..757d4115 100644
--- a/chrome/browser/resources/settings/people_page/people_page.html
+++ b/chrome/browser/resources/settings/people_page/people_page.html
@@ -63,7 +63,7 @@
         font-weight: bold;
       }
 
-      iron-icon[icon='settings:sync'] {
+      iron-icon[icon='cr:sync'] {
        --iron-icon-fill-color: var(--google-green-700);
       }
 
diff --git a/chrome/browser/resources/settings/people_page/people_page.js b/chrome/browser/resources/settings/people_page/people_page.js
index 1195b1e..5302d6a 100644
--- a/chrome/browser/resources/settings/people_page/people_page.js
+++ b/chrome/browser/resources/settings/people_page/people_page.js
@@ -548,7 +548,7 @@
     if (!syncStatus)
       return '';
 
-    let syncIcon = 'settings:sync';
+    let syncIcon = 'cr:sync';
 
     if (syncStatus.hasError)
       syncIcon = 'settings:sync-problem';
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.html b/chrome/browser/resources/settings/people_page/sync_account_control.html
index dd6c0fd..fa9129c 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.html
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.html
@@ -1,6 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
@@ -164,7 +165,7 @@
               class$="[[getSyncIconStyle_(
                   syncStatus.hasError, syncStatus.statusAction,
                   syncStatus.disabled)]]">
-            <iron-icon icon$="settings:[[getSyncIcon_(
+            <iron-icon icon$="[[getSyncIcon_(
                 syncStatus.hasError, syncStatus.statusAction,
                 syncStatus.disabled)]]"></iron-icon>
           </div>
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.js b/chrome/browser/resources/settings/people_page/sync_account_control.js
index 76fb87e..1a5f4b10 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.js
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.js
@@ -228,11 +228,11 @@
   getSyncIcon_: function() {
     switch (this.getSyncIconStyle_()) {
       case 'sync-problem':
-        return 'sync-problem';
+        return 'settings:sync-problem';
       case 'sync-paused':
-        return 'sync-disabled';
+        return 'settings:sync-disabled';
       default:
-        return 'sync';
+        return 'cr:sync';
     }
   },
 
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html
index ab798c7..5741588cb 100644
--- a/chrome/browser/resources/settings/site_settings/site_details.html
+++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -6,6 +6,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
+<link rel="import" href="../icons.html">
 <link rel="import" href="../route.html">
 <link rel="import" href="../settings_shared_css.html">
 <link rel="import" href="constants.html">
@@ -142,7 +143,7 @@
       </site-details-permission>
       <site-details-permission
           category="{{ContentSettingsTypes.BACKGROUND_SYNC}}"
-          icon="settings:sync" id="backgroundSync"
+          icon="cr:sync" id="backgroundSync"
           label="$i18n{siteSettingsBackgroundSync}">
       </site-details-permission>
       <site-details-permission category="{{ContentSettingsTypes.SOUND}}"
diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chrome/browser/resources/settings/site_settings/site_details_permission.html
index 76881dc..3e305be01 100644
--- a/chrome/browser/resources/settings/site_settings/site_details_permission.html
+++ b/chrome/browser/resources/settings/site_settings/site_details_permission.html
@@ -9,6 +9,8 @@
 <link rel="import" href="site_settings_behavior.html">
 <link rel="import" href="site_settings_prefs_browser_proxy.html">
 
+<!-- `site-details-permission` does not include any icon-set, so containing
+     elements should import the icon-set needed for the specified |icon|. -->
 <dom-module id="site-details-permission">
   <template>
     <style include="settings-shared md-select"></style>
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
index bbd499c..92c863f 100644
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -245,7 +245,7 @@
         category$="[[ContentSettingsTypes.BACKGROUND_SYNC]]"
         data-route="SITE_SETTINGS_BACKGROUND_SYNC" on-click="onTapNavigate_"
         actionable>
-      <iron-icon icon="settings:sync"></iron-icon>
+      <iron-icon icon="cr:sync"></iron-icon>
       <div class="middle">
         $i18n{siteSettingsBackgroundSync}
         <div class="secondary" id="backgroundSyncSecondary">
diff --git a/chrome/browser/resources/signin/dice_sync_confirmation/icons.html b/chrome/browser/resources/signin/dice_sync_confirmation/icons.html
new file mode 100644
index 0000000..6dec71c
--- /dev/null
+++ b/chrome/browser/resources/signin/dice_sync_confirmation/icons.html
@@ -0,0 +1,11 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html">
+
+<iron-iconset-svg name="sync-confirmation" size="24">
+<svg>
+<defs>
+  <g id="assistant"><path d="M19 2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h4l3 3 3-3h4c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-5.12 10.88L12 17l-1.88-4.12L6 11l4.12-1.88L12 5l1.88 4.12L18 11l-4.12 1.88z"></path></g>
+  <g id="settings"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"></path></g>
+</defs>
+</svg>
+</iron-iconset-svg>
diff --git a/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html b/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
index 9b51f8c..d01186f1 100644
--- a/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
+++ b/chrome/browser/resources/signin/dice_sync_confirmation/sync_confirmation_app.html
@@ -1,16 +1,15 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html">
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/load_time_data.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/image-icons.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/notification-icons.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-group/paper-radio-group.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html">
+<link rel="import" href="icons.html">
 <link rel="import" href="signin_shared_css.html">
 <link rel="import" href="sync_confirmation_browser_proxy.html">
 
@@ -215,13 +214,13 @@
       <div class="message-container">
         <!-- Container needed to contain the icon in a green circle. -->
         <div id="sync-logo-container" class="logo">
-          <iron-icon icon="notification:sync" class="logo">
-          </iron-icon>
+          <iron-icon icon="cr:sync" class="logo"></iron-icon>
         </div>
         <div consent-description>$i18n{syncConfirmationChromeSyncBody}</div>
       </div>
       <div class="message-container">
-        <iron-icon icon="image:assistant" id="personalize-logo" class="logo">
+        <iron-icon icon="sync-confirmation:assistant" id="personalize-logo"
+            class="logo">
         </iron-icon>
         <div consent-description>
           $i18n{syncConfirmationPersonalizeServicesBody}
@@ -233,7 +232,7 @@
       </div>
       <div class="footer">
         <div class="message-container">
-          <iron-icon icon="icons:settings" class="logo"></iron-icon>
+          <iron-icon icon="sync-confirmation:settings" class="logo"></iron-icon>
           <div consent-description>
             $i18n{syncConfirmationSyncSettingsDescription}
           </div>
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
index eaa18de..c80eae9 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.cc
@@ -40,7 +40,8 @@
       frame_id(-1),
       last_updated(base::Time::Now()),
       navigation_initiation(ReferrerChainEntry::UNDEFINED),
-      has_committed(false) {}
+      has_committed(false),
+      maybe_launched_by_external_application() {}
 
 NavigationEvent::NavigationEvent(NavigationEvent&& nav_event)
     : source_url(std::move(nav_event.source_url)),
@@ -52,7 +53,9 @@
       frame_id(nav_event.frame_id),
       last_updated(nav_event.last_updated),
       navigation_initiation(nav_event.navigation_initiation),
-      has_committed(nav_event.has_committed) {}
+      has_committed(nav_event.has_committed),
+      maybe_launched_by_external_application(
+          nav_event.maybe_launched_by_external_application) {}
 
 NavigationEvent& NavigationEvent::operator=(NavigationEvent&& nav_event) {
   source_url = std::move(nav_event.source_url);
@@ -64,6 +67,8 @@
   last_updated = nav_event.last_updated;
   navigation_initiation = nav_event.navigation_initiation;
   has_committed = nav_event.has_committed;
+  maybe_launched_by_external_application =
+      nav_event.maybe_launched_by_external_application;
   server_redirect_urls = std::move(nav_event.server_redirect_urls);
   return *this;
 }
@@ -233,6 +238,9 @@
   }
   NavigationEvent* nav_event = navigation_handle_map_[navigation_handle].get();
 
+  nav_event->maybe_launched_by_external_application =
+      PageTransitionCoreTypeIs(navigation_handle->GetPageTransition(),
+                               ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
   nav_event->has_committed = navigation_handle->HasCommitted();
   nav_event->target_tab_id =
       SessionTabHelper::IdForTab(navigation_handle->GetWebContents());
@@ -268,7 +276,7 @@
     bool renderer_initiated) {
   manager_->RecordNewWebContents(
       web_contents(), source_render_frame_host->GetProcess()->GetID(),
-      source_render_frame_host->GetRoutingID(), url, new_contents,
+      source_render_frame_host->GetRoutingID(), url, transition, new_contents,
       renderer_initiated);
 }
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
index 1cfb5f07..6bd61fb3 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer.h
@@ -68,6 +68,9 @@
   // committed.
   bool has_committed;
 
+  // Whether we think this event was launched by an external application.
+  bool maybe_launched_by_external_application;
+
   const GURL& GetDestinationUrl() const {
     if (!server_redirect_urls.empty())
       return server_redirect_urls.back();
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
index 0a03332c..07972af 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.cc
@@ -366,12 +366,9 @@
   AddToReferrerChain(out_referrer_chain, nav_event, GURL(),
                      ReferrerChainEntry::EVENT_URL);
   int user_gesture_count = 0;
-  GetRemainingReferrerChain(
-      nav_event,
-      user_gesture_count,
-      user_gesture_count_limit,
-      out_referrer_chain,
-      &result);
+  GetRemainingReferrerChain(nav_event, user_gesture_count,
+                            user_gesture_count_limit, out_referrer_chain,
+                            &result);
   return result;
 }
 
@@ -427,12 +424,9 @@
                        ReferrerChainEntry::CLIENT_REDIRECT);
   }
 
-  GetRemainingReferrerChain(
-      nav_event,
-      user_gesture_count,
-      user_gesture_count_limit,
-      out_referrer_chain,
-      &result);
+  GetRemainingReferrerChain(nav_event, user_gesture_count,
+                            user_gesture_count_limit, out_referrer_chain,
+                            &result);
   return result;
 }
 
@@ -444,6 +438,7 @@
     int source_render_process_id,
     int source_render_frame_id,
     GURL target_url,
+    ui::PageTransition page_transition,
     content::WebContents* target_web_contents,
     bool renderer_initiated) {
   DCHECK(source_web_contents);
@@ -469,6 +464,9 @@
   nav_event->original_request_url = cleaned_target_url;
   nav_event->target_tab_id = SessionTabHelper::IdForTab(target_web_contents);
   nav_event->frame_id = rfh ? rfh->GetFrameTreeNodeId() : -1;
+  nav_event->maybe_launched_by_external_application =
+      ui::PageTransitionCoreTypeIs(page_transition,
+                                   ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
 
   auto it = user_gesture_map_.find(source_web_contents);
   if (it == user_gesture_map_.end() ||
@@ -623,6 +621,8 @@
       server_redirect->set_url(ShortURLForReporting(redirect));
     }
   }
+  referrer_chain_entry->set_maybe_launched_by_external_application(
+      nav_event->maybe_launched_by_external_application);
   referrer_chain->Add()->Swap(referrer_chain_entry.get());
 }
 
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
index 700e24c..f6f10da0 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h
@@ -200,6 +200,7 @@
                             int source_render_process_id,
                             int source_render_frame_id,
                             GURL target_url,
+                            ui::PageTransition page_transition,
                             content::WebContents* target_web_contents,
                             bool renderer_initiated);
 
diff --git a/chrome/browser/search_engines/template_url_fetcher_factory.cc b/chrome/browser/search_engines/template_url_fetcher_factory.cc
index 0a058349..581151f3 100644
--- a/chrome/browser/search_engines/template_url_fetcher_factory.cc
+++ b/chrome/browser/search_engines/template_url_fetcher_factory.cc
@@ -9,7 +9,6 @@
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/search_engines/template_url_fetcher.h"
-#include "content/public/browser/storage_partition.h"
 
 // static
 TemplateURLFetcher* TemplateURLFetcherFactory::GetForProfile(
@@ -43,9 +42,7 @@
 KeyedService* TemplateURLFetcherFactory::BuildServiceInstanceFor(
     content::BrowserContext* profile) const {
   return new TemplateURLFetcher(
-      TemplateURLServiceFactory::GetForProfile(static_cast<Profile*>(profile)),
-      content::BrowserContext::GetDefaultStoragePartition(profile)->
-          GetURLRequestContext());
+      TemplateURLServiceFactory::GetForProfile(static_cast<Profile*>(profile)));
 }
 
 content::BrowserContext* TemplateURLFetcherFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/search_engines/template_url_fetcher_unittest.cc b/chrome/browser/search_engines/template_url_fetcher_unittest.cc
index e58a047..07564fdeb 100644
--- a/chrome/browser/search_engines/template_url_fetcher_unittest.cc
+++ b/chrome/browser/search_engines/template_url_fetcher_unittest.cc
@@ -20,8 +20,11 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -32,9 +35,8 @@
 class TestTemplateUrlFetcher : public TemplateURLFetcher {
  public:
   TestTemplateUrlFetcher(TemplateURLService* template_url_service,
-                         net::URLRequestContextGetter* request_context,
                          const base::Closure& request_completed_callback)
-      : TemplateURLFetcher(template_url_service, request_context),
+      : TemplateURLFetcher(template_url_service),
         callback_(request_completed_callback) {}
   ~TestTemplateUrlFetcher() override {}
 
@@ -57,10 +59,8 @@
   TemplateURLFetcherTest();
 
   void SetUp() override {
-    TestingProfile* profile = test_util_.profile();
-    ASSERT_TRUE(profile->GetRequestContext());
     template_url_fetcher_.reset(new TestTemplateUrlFetcher(
-        test_util_.model(), profile->GetRequestContext(),
+        test_util_.model(),
         base::Bind(&TemplateURLFetcherTest::RequestCompletedCallback,
                    base::Unretained(this))));
 
@@ -145,9 +145,14 @@
   // Start the fetch.
   GURL osdd_url = test_server_.GetURL("/" + osdd_file_name);
   GURL favicon_url;
+
+  TestingProfile* profile = test_util_.profile();
   template_url_fetcher_->ScheduleDownload(
-      keyword, osdd_url, favicon_url,
-      TemplateURLFetcher::URLFetcherCustomizeCallback());
+      keyword, osdd_url, favicon_url, url::Origin::Create(GURL()),
+      content::BrowserContext::GetDefaultStoragePartition(profile)
+          ->GetURLLoaderFactoryForBrowserProcess()
+          .get(),
+      0 /* render_frame_id */, 0 /* resource_type */);
 }
 
 void TemplateURLFetcherTest::WaitForDownloadToFinish() {
diff --git a/chrome/browser/shell_integration_linux_unittest.cc b/chrome/browser/shell_integration_linux_unittest.cc
index d005dae..1c603bb8 100644
--- a/chrome/browser/shell_integration_linux_unittest.cc
+++ b/chrome/browser/shell_integration_linux_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_path_override.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
 #include "chrome/common/chrome_constants.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/shell_integration_win.cc b/chrome/browser/shell_integration_win.cc
index 7f8f3e6a..1f68085 100644
--- a/chrome/browser/shell_integration_win.cc
+++ b/chrome/browser/shell_integration_win.cc
@@ -40,7 +40,8 @@
 #include "base/win/windows_version.h"
 #include "chrome/browser/policy/policy_path_parser.h"
 #include "chrome/browser/shell_integration.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/browser/win/settings_app_monitor.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths_internal.h"
diff --git a/chrome/browser/shell_integration_win_unittest.cc b/chrome/browser/shell_integration_win_unittest.cc
index 89596e7..42d14dc 100644
--- a/chrome/browser/shell_integration_win_unittest.cc
+++ b/chrome/browser/shell_integration_win_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_shortcut_win.h"
 #include "base/win/scoped_com_initializer.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths_internal.h"
 #include "chrome/install_static/install_util.h"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 80c3909..b1e13ab5 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1753,9 +1753,13 @@
       sources += [ "media_router/cloud_services_dialog.cc" ]
     }
 
-    # TODO(loyso): Rework these dependencies. http://crbug.com/862049
     if (enable_extensions) {
-      deps += [ "//chrome/browser/web_applications" ]
+      deps += [
+        # TODO(loyso): Remove the first dependency. http://crbug.com/862049
+        "//chrome/browser/web_applications",
+        "//chrome/browser/web_applications/components",
+        "//chrome/browser/web_applications/extensions",
+      ]
     }
   }
 
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
index 6d760f9..b5054d5 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_shortcut_launcher_item_controller.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
index f4d3ee4..5c57d574 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/browser_shortcut_launcher_item_controller.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
diff --git a/chrome/browser/ui/ash/launcher/browser_status_monitor.cc b/chrome/browser/ui/ash/launcher/browser_status_monitor.cc
index 545fd12..da36018 100644
--- a/chrome/browser/ui/ash/launcher/browser_status_monitor.cc
+++ b/chrome/browser/ui/ash/launcher/browser_status_monitor.cc
@@ -17,7 +17,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
index ddcbb1a..52d459d 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc
@@ -59,7 +59,7 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
index 8d94b6f..0bc3636 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc
@@ -50,7 +50,7 @@
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/test/test_app_window_icon_observer.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/test/base/ui_test_utils.h"
diff --git a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
index 1a558bc..91e3da05 100644
--- a/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_controller_helper.cc
@@ -27,7 +27,7 @@
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "components/arc/arc_util.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/ui/autofill/OWNERS b/chrome/browser/ui/autofill/OWNERS
index 8d6b49f..389a1a38 100644
--- a/chrome/browser/ui/autofill/OWNERS
+++ b/chrome/browser/ui/autofill/OWNERS
@@ -1,4 +1,5 @@
 estade@chromium.org
+ftirelo@chromium.org
 mathp@chromium.org
 
-# COMPONENT: UI>Browser>Autofill
+# COMPONENT: UI>Browser>Autofill>UI
diff --git a/chrome/browser/ui/autofill/popup_view_common.cc b/chrome/browser/ui/autofill/popup_view_common.cc
index baacfff1..004e9cb 100644
--- a/chrome/browser/ui/autofill/popup_view_common.cc
+++ b/chrome/browser/ui/autofill/popup_view_common.cc
@@ -24,6 +24,37 @@
 
 namespace {
 
+// CalculateWidthValues below ultimately determines the maximum popup width
+// given the constraints of the window and the element's position within it.
+// Returning a struct allows callers to reuse intermediary values produced
+// during this calculation which are also relevant to positioning and sizing.
+struct WidthCalculationResults {
+  int right_growth_start;  // Popup start coordinate when growing to the right.
+  int left_growth_end;     // Popup end coordinate when growing to the left.
+  int right_available;     // Amount of space available if drawing to the right.
+  int left_available;      // Amount of space available if drawing to the left.
+  int max_popup_width;     // The max of |right_available| and |left_available|.
+};
+
+WidthCalculationResults CalculateWidthValues(int leftmost_available_x,
+                                             int rightmost_available_x,
+                                             const gfx::Rect& element_bounds) {
+  WidthCalculationResults result;
+  result.right_growth_start =
+      std::max(leftmost_available_x,
+               std::min(rightmost_available_x, element_bounds.x()));
+  result.left_growth_end =
+      std::max(leftmost_available_x,
+               std::min(rightmost_available_x, element_bounds.right()));
+
+  result.right_available = rightmost_available_x - result.right_growth_start;
+  result.left_available = result.left_growth_end - leftmost_available_x;
+
+  result.max_popup_width =
+      std::max(result.right_available, result.left_available);
+  return result;
+}
+
 // Sets the |x| and |width| components of |popup_bounds| as the x-coordinate of
 // the starting point and the width of the popup, taking into account the
 // direction it's supposed to grow (either to the left or to the right).
@@ -34,36 +65,26 @@
                              const gfx::Rect& element_bounds,
                              bool is_rtl,
                              gfx::Rect* popup_bounds) {
-  // Calculate the start coordinates for the popup if it is growing right or
-  // the end position if it is growing to the left, capped to screen space.
-  int right_growth_start =
-      std::max(leftmost_available_x,
-               std::min(rightmost_available_x, element_bounds.x()));
-  int left_growth_end =
-      std::max(leftmost_available_x,
-               std::min(rightmost_available_x, element_bounds.right()));
+  WidthCalculationResults result = CalculateWidthValues(
+      leftmost_available_x, rightmost_available_x, element_bounds);
 
-  int right_available = rightmost_available_x - right_growth_start;
-  int left_available = left_growth_end - leftmost_available_x;
-
-  int popup_width =
-      std::min(popup_required_width, std::max(right_available, left_available));
+  int popup_width = std::min(popup_required_width, result.max_popup_width);
 
   // Prefer to grow towards the end (right for LTR, left for RTL). But if there
   // is not enough space available in the desired direction and more space in
   // the other direction, reverse it.
   bool grow_left = false;
   if (is_rtl) {
-    grow_left =
-        left_available >= popup_width || left_available >= right_available;
+    grow_left = result.left_available >= popup_width ||
+                result.left_available >= result.right_available;
   } else {
-    grow_left =
-        right_available < popup_width && right_available < left_available;
+    grow_left = result.right_available < popup_width &&
+                result.right_available < result.left_available;
   }
 
   popup_bounds->set_width(popup_width);
-  popup_bounds->set_x(grow_left ? left_growth_end - popup_width
-                                : right_growth_start);
+  popup_bounds->set_x(grow_left ? result.left_growth_end - popup_width
+                                : result.right_growth_start);
 }
 
 // Sets the |y| and |height| components of |popup_bounds| as the y-coordinate of
@@ -171,4 +192,14 @@
 #endif
 }
 
+int PopupViewCommon::CalculateMaxWidth(const gfx::Rect& element_bounds,
+                                       gfx::NativeView container_view) {
+  const gfx::Rect window_bounds = GetWindowBounds(container_view);
+  int leftmost_available_x = window_bounds.x();
+  int rightmost_available_x = window_bounds.x() + window_bounds.width();
+  return CalculateWidthValues(leftmost_available_x, rightmost_available_x,
+                              element_bounds)
+      .max_popup_width;
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/popup_view_common.h b/chrome/browser/ui/autofill/popup_view_common.h
index 347a734d..555337c5 100644
--- a/chrome/browser/ui/autofill/popup_view_common.h
+++ b/chrome/browser/ui/autofill/popup_view_common.h
@@ -48,6 +48,12 @@
   // testing.
   virtual gfx::Rect GetWindowBounds(gfx::NativeView container_view);
 
+  // Returns the greatest possible width for the popup, based on the distances
+  // between the edges of the element and the respective far edges of the
+  // window.
+  int CalculateMaxWidth(const gfx::Rect& element_bounds,
+                        gfx::NativeView container_view);
+
   DISALLOW_COPY_AND_ASSIGN(PopupViewCommon);
 };
 
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index f87da74..ff9edd8 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -101,7 +101,7 @@
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/ui/extensions/settings_api_bubble_helpers.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/extensions/extension_metrics.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index f5baeba..47ec2e9 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -57,7 +57,7 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/tab_helper.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/common/extension.h"
 #include "extensions/common/extension_set.h"
diff --git a/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc
index 23eac50..42fdfa05 100644
--- a/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc
+++ b/chrome/browser/ui/cocoa/apps/quit_with_apps_controller_mac.cc
@@ -17,7 +17,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc
index 969fb98..a52f4c1 100644
--- a/chrome/browser/ui/extensions/application_launch.cc
+++ b/chrome/browser/ui/extensions/application_launch.cc
@@ -31,7 +31,8 @@
 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
 #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 18566d9..937eef4 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 3e2ca90..9e50d18 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -37,7 +37,7 @@
 #include "chrome/browser/ui/page_info/page_info_dialog.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/web_application_info.h"
diff --git a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
index 2c9c32a..b9880aa 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
@@ -13,7 +13,6 @@
 #include "build/build_config.h"
 #include "build/buildflag.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
@@ -33,7 +32,6 @@
 #include "chrome/test/base/find_in_page_observer.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/history/core/browser/history_service.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/notification_service.h"
@@ -197,14 +195,6 @@
         base::FilePath().AppendASCII("find_in_page"),
         base::FilePath().AppendASCII(filename));
   }
-
-  void FlushHistoryService() {
-    HistoryServiceFactory::GetForProfile(browser()->profile(),
-                                         ServiceAccessType::IMPLICIT_ACCESS)
-        ->FlushForTest(
-            base::Bind(&base::RunLoop::QuitCurrentWhenIdleDeprecated));
-    content::RunMessageLoop();
-  }
 };
 
 // This test loads a page with frames and starts FindInPage requests.
diff --git a/chrome/browser/ui/omnibox/omnibox_theme.cc b/chrome/browser/ui/omnibox/omnibox_theme.cc
index 9d527940..65260f7 100644
--- a/chrome/browser/ui/omnibox/omnibox_theme.cc
+++ b/chrome/browser/ui/omnibox/omnibox_theme.cc
@@ -246,7 +246,7 @@
           gfx::ToRoundedInt(GetOmniboxStateAlpha(state) * 0xff));
     case OmniboxPart::LOCATION_BAR_TEXT_DEFAULT:
     case OmniboxPart::RESULTS_TEXT_DEFAULT:
-      return dark ? gfx::kGoogleGrey100 : gfx::kGoogleGrey800;
+      return dark ? gfx::kGoogleGrey100 : gfx::kGoogleGrey900;
 
     case OmniboxPart::LOCATION_BAR_TEXT_DIMMED:
     case OmniboxPart::RESULTS_ICON:
diff --git a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
index df76810..f1c6133 100644
--- a/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
+++ b/chrome/browser/ui/search_engines/search_engine_tab_helper.cc
@@ -24,7 +24,7 @@
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/url_fetcher.h"
+#include "content/public/common/resource_type.h"
 
 using content::NavigationController;
 using content::NavigationEntry;
@@ -71,14 +71,6 @@
   return TemplateURL::GenerateKeyword(url);
 }
 
-void AssociateURLFetcherWithWebContents(content::WebContents* web_contents,
-                                        net::URLFetcher* url_fetcher) {
-  content::AssociateURLFetcherWithRenderFrame(
-      url_fetcher, url::Origin::Create(web_contents->GetURL()),
-      web_contents->GetMainFrame()->GetProcess()->GetID(),
-      web_contents->GetMainFrame()->GetRoutingID());
-}
-
 }  // namespace
 
 SearchEngineTabHelper::~SearchEngineTabHelper() {
@@ -146,11 +138,17 @@
   if (keyword.empty())
     return;
 
+  auto* frame = web_contents()->GetMainFrame();
+  network::mojom::URLLoaderFactoryPtr url_loader_factory;
+  frame->CreateNetworkServiceDefaultFactory(
+      mojo::MakeRequest(&url_loader_factory));
+
   // Download the OpenSearch description document. If this is successful, a
   // new keyword will be created when done.
   TemplateURLFetcherFactory::GetForProfile(profile)->ScheduleDownload(
       keyword, osdd_url, entry->GetFavicon().url,
-      base::Bind(&AssociateURLFetcherWithWebContents, web_contents()));
+      url::Origin::Create(web_contents()->GetURL()), url_loader_factory.get(),
+      frame->GetRoutingID(), content::RESOURCE_TYPE_SUB_RESOURCE);
 }
 
 void SearchEngineTabHelper::OnFaviconUpdated(
diff --git a/chrome/browser/ui/toolbar/media_router_action.cc b/chrome/browser/ui/toolbar/media_router_action.cc
index da9ed70..b3e02bbc 100644
--- a/chrome/browser/ui/toolbar/media_router_action.cc
+++ b/chrome/browser/ui/toolbar/media_router_action.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/toolbar/media_router_action.h"
 
+#include "base/bind.h"
+#include "base/location.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/media/router/media_router.h"
@@ -22,6 +24,7 @@
 #include "chrome/common/media_router/media_route.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/vector_icons/vector_icons.h"
+#include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image_skia.h"
@@ -137,6 +140,9 @@
 }
 
 ui::MenuModel* MediaRouterAction::GetContextMenu() {
+  // If there is an existing context menu, destroy it before we instantiate a
+  // new one.
+  DestroyContextMenu();
   MediaRouterActionController* controller =
       media_router::MediaRouterUIService::Get(browser_->profile())
           ->action_controller();
@@ -155,7 +161,14 @@
       !GetMediaRouterDialogController()->IsShowingMediaRouterDialog()) {
     toolbar_actions_bar_->UndoPopOut();
   }
-  contextual_menu_.reset();
+  // We must destroy the context menu asynchronously to prevent it from being
+  // destroyed before the command execution.
+  // TODO(takumif): Using task sequence to order operations is fragile. Consider
+  // other ways to do so when we move the icon to the trusted area.
+  content::BrowserThread::PostTask(
+      content::BrowserThread::UI, FROM_HERE,
+      base::BindOnce(&MediaRouterAction::DestroyContextMenu,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 bool MediaRouterAction::ExecuteAction(bool by_user) {
@@ -302,3 +315,7 @@
   return has_local_display_route_ ? vector_icons::kMediaRouterActiveIcon
                                   : vector_icons::kMediaRouterIdleIcon;
 }
+
+void MediaRouterAction::DestroyContextMenu() {
+  contextual_menu_.reset();
+}
diff --git a/chrome/browser/ui/toolbar/media_router_action.h b/chrome/browser/ui/toolbar/media_router_action.h
index d4512f31..3cddec5 100644
--- a/chrome/browser/ui/toolbar/media_router_action.h
+++ b/chrome/browser/ui/toolbar/media_router_action.h
@@ -115,6 +115,8 @@
 
   const gfx::VectorIcon& GetCurrentIcon() const;
 
+  void DestroyContextMenu();
+
   // The current icon to show. This is updated based on the current issues and
   // routes since |this| is an IssueObserver and MediaRoutesObserver.
   const gfx::VectorIcon* current_icon_;
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
index 8d850904..b241544 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura.cc
@@ -11,7 +11,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/ui/views/apps/app_window_easy_resize_window_targeter.h"
 #include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_observer.h"
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc
index f58c6ac5..fae187d 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_win.cc
@@ -12,7 +12,7 @@
 #include "chrome/browser/shell_integration_win.h"
 #include "chrome/browser/ui/views/apps/app_window_desktop_native_widget_aura_win.h"
 #include "chrome/browser/ui/views/apps/glass_app_window_frame_view_win.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/browser/web_applications/web_app_win.h"
 #include "chrome/common/chrome_switches.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index 2088cd8e..a362a72 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -40,10 +40,11 @@
 
 namespace {
 
-// By spec, dropdowns should have a min width of 64, and should always have
-// a width which is a multiple of 12.
+// By spec, dropdowns should have a min width of 64, a max width of 456, and
+// should always have a width which is a multiple of 12.
 const int kAutofillPopupWidthMultiple = 12;
 const int kAutofillPopupMinWidth = 64;
+const int kAutofillPopupMaxWidth = 456;
 
 // TODO(crbug.com/831603): Determine how colors should be shared with menus
 // and/or omnibox, and how these should interact (if at all) with native
@@ -52,6 +53,7 @@
 const SkColor kAutofillPopupSelectedBackgroundColor = gfx::kGoogleGrey200;
 const SkColor kAutofillPopupFooterBackgroundColor = gfx::kGoogleGrey050;
 const SkColor kAutofillPopupSeparatorColor = gfx::kGoogleGrey200;
+const SkColor kAutofillPopupWarningColor = gfx::kGoogleRed600;
 
 // A space between the input element and the dropdown, so that the dropdown's
 // border doesn't look too close to the element.
@@ -67,6 +69,11 @@
       DISTANCE_CONTENT_LIST_VERTICAL_MULTI);
 }
 
+int GetHorizontalMargin() {
+  return views::MenuConfig::instance().item_horizontal_padding +
+         GetCornerRadius();
+}
+
 }  // namespace
 
 namespace autofill {
@@ -83,6 +90,7 @@
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnMouseEntered(const ui::MouseEvent& event) override;
+  void OnMouseExited(const ui::MouseEvent& event) override;
   void OnMouseReleased(const ui::MouseEvent& event) override;
 
  protected:
@@ -165,6 +173,7 @@
   // views::View:
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnMouseEntered(const ui::MouseEvent& event) override {}
+  void OnMouseExited(const ui::MouseEvent& event) override {}
   void OnMouseReleased(const ui::MouseEvent& event) override {}
 
  protected:
@@ -180,6 +189,32 @@
   DISALLOW_COPY_AND_ASSIGN(AutofillPopupSeparatorView);
 };
 
+// Draws a row which contains a warning message.
+class AutofillPopupWarningView : public AutofillPopupRowView {
+ public:
+  ~AutofillPopupWarningView() override = default;
+
+  static AutofillPopupWarningView* Create(AutofillPopupController* controller,
+                                          int line_number);
+
+  // views::View:
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void OnMouseEntered(const ui::MouseEvent& event) override {}
+  void OnMouseReleased(const ui::MouseEvent& event) override {}
+
+ protected:
+  // AutofillPopupRowView:
+  void CreateContent() override;
+  void RefreshStyle() override {}
+  std::unique_ptr<views::Background> CreateBackground() override;
+
+ private:
+  AutofillPopupWarningView(AutofillPopupController* controller, int line_number)
+      : AutofillPopupRowView(controller, line_number) {}
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillPopupWarningView);
+};
+
 void AutofillPopupItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
   auto suggestion = controller_->GetSuggestionAt(line_number_);
   std::vector<base::string16> text;
@@ -220,6 +255,10 @@
   controller_->SetSelectedLine(line_number_);
 }
 
+void AutofillPopupItemView::OnMouseExited(const ui::MouseEvent& event) {
+  controller_->SelectionCleared();
+}
+
 void AutofillPopupItemView::OnMouseReleased(const ui::MouseEvent& event) {
   if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
     controller_->AcceptSuggestion(line_number_);
@@ -227,9 +266,7 @@
 
 void AutofillPopupItemView::CreateContent() {
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kHorizontal,
-      gfx::Insets(0, views::MenuConfig::instance().item_horizontal_padding +
-                         GetCornerRadius())));
+      views::BoxLayout::kHorizontal, gfx::Insets(0, GetHorizontalMargin())));
 
   layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER);
@@ -309,8 +346,7 @@
 }
 
 int AutofillPopupSuggestionView::GetPrimaryTextStyle() {
-  return is_warning_ ? ChromeTextStyle::STYLE_RED
-                     : views::style::TextStyle::STYLE_PRIMARY;
+  return views::style::TextStyle::STYLE_PRIMARY;
 }
 
 AutofillPopupSuggestionView::AutofillPopupSuggestionView(
@@ -405,6 +441,53 @@
   SetFocusBehavior(FocusBehavior::NEVER);
 }
 
+// static
+AutofillPopupWarningView* AutofillPopupWarningView::Create(
+    AutofillPopupController* controller,
+    int line_number) {
+  AutofillPopupWarningView* result =
+      new AutofillPopupWarningView(controller, line_number);
+  result->Init();
+  return result;
+}
+
+void AutofillPopupWarningView::GetAccessibleNodeData(
+    ui::AXNodeData* node_data) {
+  node_data->SetName(controller_->GetSuggestionAt(line_number_).value);
+  node_data->role = ax::mojom::Role::kStaticText;
+}
+
+void AutofillPopupWarningView::CreateContent() {
+  int horizontal_margin = GetHorizontalMargin();
+  int vertical_margin = GetCornerRadius();
+
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+  SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(vertical_margin, horizontal_margin)));
+
+  views::Label* text_label = new views::Label(
+      controller_->GetElidedValueAt(line_number_),
+      {views::style::GetFont(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
+                             ChromeTextStyle::STYLE_RED)});
+  text_label->SetEnabledColor(kAutofillPopupWarningColor);
+  text_label->SetMultiLine(true);
+  int max_width =
+      std::min(kAutofillPopupMaxWidth,
+               PopupViewCommon().CalculateMaxWidth(
+                   gfx::ToEnclosingRect(controller_->element_bounds()),
+                   controller_->container_view()));
+  max_width -= 2 * horizontal_margin;
+  text_label->SetMaximumWidth(max_width);
+  text_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
+
+  AddChildView(text_label);
+}
+
+std::unique_ptr<views::Background>
+AutofillPopupWarningView::CreateBackground() {
+  return views::CreateSolidBackground(SK_ColorWHITE);
+}
+
 }  // namespace
 
 void AutofillPopupRowView::SetSelected(bool is_selected) {
@@ -426,12 +509,7 @@
 
 AutofillPopupRowView::AutofillPopupRowView(AutofillPopupController* controller,
                                            int line_number)
-    : controller_(controller), line_number_(line_number) {
-  int frontend_id = controller_->GetSuggestionAt(line_number_).frontend_id;
-  is_warning_ =
-      frontend_id ==
-      autofill::POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE;
-}
+    : controller_(controller), line_number_(line_number) {}
 
 void AutofillPopupRowView::Init() {
   CreateContent();
@@ -505,7 +583,6 @@
   rows_.clear();
 
   // Create one container to wrap the "regular" (non-footer) rows.
-  // TODO(crbug.com/768881): Make |body_container| scrollable.
   views::View* body_container = new views::View();
   views::BoxLayout* body_layout =
       body_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
@@ -520,28 +597,36 @@
   // Process and add all the suggestions which are in the primary container.
   // Stop once the first footer item is found, or there are no more items.
   while (line_number < controller_->GetLineCount()) {
-    int item_id = controller_->GetSuggestionAt(line_number).frontend_id;
+    switch (controller_->GetSuggestionAt(line_number).frontend_id) {
+      case autofill::PopupItemId::POPUP_ITEM_ID_CLEAR_FORM:
+      case autofill::PopupItemId::POPUP_ITEM_ID_AUTOFILL_OPTIONS:
+      case autofill::PopupItemId::POPUP_ITEM_ID_SCAN_CREDIT_CARD:
+      case autofill::PopupItemId::POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO:
+      case autofill::PopupItemId::POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY:
+        // This is a footer, so this suggestion will be processed later. Don't
+        // increment |line_number|, or else it will be skipped when adding
+        // footer rows below.
+        has_footer = true;
+        break;
 
-    if (item_id == autofill::PopupItemId::POPUP_ITEM_ID_CLEAR_FORM ||
-        item_id == autofill::PopupItemId::POPUP_ITEM_ID_AUTOFILL_OPTIONS ||
-        item_id == autofill::PopupItemId::POPUP_ITEM_ID_SCAN_CREDIT_CARD ||
-        item_id ==
-            autofill::PopupItemId::POPUP_ITEM_ID_CREDIT_CARD_SIGNIN_PROMO ||
-        item_id == POPUP_ITEM_ID_ALL_SAVED_PASSWORDS_ENTRY) {
-      // This is a footer, so this suggestion will be processed later. Don't
-      // increment |line_number|, or else it will be skipped when adding footer
-      // rows below.
-      has_footer = true;
+      case autofill::PopupItemId::POPUP_ITEM_ID_SEPARATOR:
+        rows_.push_back(
+            AutofillPopupSeparatorView::Create(controller_, line_number));
+        break;
+
+      case autofill::PopupItemId::
+          POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE:
+        rows_.push_back(
+            AutofillPopupWarningView::Create(controller_, line_number));
+        break;
+
+      default:
+        rows_.push_back(
+            AutofillPopupSuggestionView::Create(controller_, line_number));
+    }
+
+    if (has_footer)
       break;
-    }
-
-    if (item_id == autofill::PopupItemId::POPUP_ITEM_ID_SEPARATOR) {
-      rows_.push_back(
-          AutofillPopupSeparatorView::Create(controller_, line_number));
-    } else {
-      rows_.push_back(
-          AutofillPopupSuggestionView::Create(controller_, line_number));
-    }
     body_container->AddChildView(rows_.back());
     line_number++;
   }
@@ -580,27 +665,31 @@
 }
 
 int AutofillPopupViewNativeViews::AdjustWidth(int width) const {
-  // The border of the input element should be aligned with the border of the
-  // dropdown when suggestions are not too wide.
-  int adjusted_width =
-      gfx::ToEnclosingRect(controller_->element_bounds()).width();
+  if (width >= kAutofillPopupMaxWidth)
+    return kAutofillPopupMaxWidth;
 
-  // Allow the dropdown to grow beyond the element width if it requires more
-  // horizontal space to render the suggestions.
-  if (adjusted_width < width) {
-    adjusted_width = width;
-    // Use multiples of |kAutofillPopupWidthMultiple| if the required width is
-    // larger than the element width.
-    if (adjusted_width % kAutofillPopupWidthMultiple) {
-      adjusted_width += kAutofillPopupWidthMultiple -
-                        (adjusted_width % kAutofillPopupWidthMultiple);
-    }
+  int elem_width = gfx::ToEnclosingRect(controller_->element_bounds()).width();
+
+  // If the element width is within the range of legal sizes for the popup, use
+  // it as the min width, so that the popup will align with its edges when
+  // possible.
+  int min_width = (kAutofillPopupMinWidth <= elem_width &&
+                   elem_width < kAutofillPopupMaxWidth)
+                      ? elem_width
+                      : kAutofillPopupMinWidth;
+
+  if (width <= min_width)
+    return min_width;
+
+  // The popup size is being determined by the contents, rather than the min/max
+  // or the element bounds. Round up to a multiple of
+  // |kAutofillPopupWidthMultiple|.
+  if (width % kAutofillPopupWidthMultiple) {
+    width +=
+        (kAutofillPopupWidthMultiple - (width % kAutofillPopupWidthMultiple));
   }
 
-  // Notwithstanding all the above rules, enforce a hard minimum so the dropdown
-  // is not too small to interact with.
-  adjusted_width = std::max(kAutofillPopupMinWidth, adjusted_width);
-  return adjusted_width;
+  return width;
 }
 
 void AutofillPopupViewNativeViews::AddExtraInitParams(
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
index e943ce6..ea5cbc6 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.h
@@ -54,7 +54,6 @@
 
   AutofillPopupController* controller_;
   const int line_number_;
-  bool is_warning_ = false;  // overwritten in ctor
   bool is_selected_ = false;
 };
 
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
index 064a74e7..bc9ff542 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
@@ -30,7 +31,7 @@
 
 const struct TypeClicks kClickTestCase[] = {
     {autofill::POPUP_ITEM_ID_AUTOCOMPLETE_ENTRY, 1},
-    {autofill::POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE, 1},
+    {autofill::POPUP_ITEM_ID_INSECURE_CONTEXT_PAYMENT_DISABLED_MESSAGE, 0},
     {autofill::POPUP_ITEM_ID_PASSWORD_ENTRY, 1},
     {autofill::POPUP_ITEM_ID_SEPARATOR, 0},
     {autofill::POPUP_ITEM_ID_CLEAR_FORM, 1},
@@ -61,7 +62,10 @@
   MOCK_CONST_METHOD0(HasSelection, bool());
   MOCK_CONST_METHOD0(popup_bounds, gfx::Rect());
   MOCK_METHOD0(container_view, gfx::NativeView());
-  MOCK_CONST_METHOD0(element_bounds, const gfx::RectF&());
+  const gfx::RectF& element_bounds() const override {
+    static base::NoDestructor<gfx::RectF> bounds({100, 100, 250, 50});
+    return *bounds;
+  }
   MOCK_CONST_METHOD0(IsRTL, bool());
   const std::vector<autofill::Suggestion> GetSuggestions() override {
     return suggestions_;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
index 597f400d..0ae0b376 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc
@@ -45,7 +45,6 @@
 #include "chrome/browser/ui/views/tab_icon_view.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
-#include "chrome/browser/web_applications/web_app.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
diff --git a/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc b/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc
index 3080979d..b8d609b 100644
--- a/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc
+++ b/chrome/browser/ui/views/frame/browser_window_property_manager_win.cc
@@ -13,7 +13,7 @@
 #include "chrome/browser/profiles/profile_shortcut_manager_win.h"
 #include "chrome/browser/shell_integration_win.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/browser/web_applications/web_app_win.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/views/frame/desktop_browser_frame_aura.cc b/chrome/browser/ui/views/frame/desktop_browser_frame_aura.cc
index 466add9..c1d558c 100644
--- a/chrome/browser/ui/views/frame/desktop_browser_frame_aura.cc
+++ b/chrome/browser/ui/views/frame/desktop_browser_frame_aura.cc
@@ -7,7 +7,6 @@
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/ui/views/frame/browser_desktop_window_tree_host.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/web_applications/web_app.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
diff --git a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
index c78d555..9f20b90e 100644
--- a/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
+++ b/chrome/browser/ui/views/media_router/media_router_ui_browsertest.cc
@@ -97,10 +97,14 @@
     nav_observer.StopWatchingNewWebContents();
   }
 
-  MediaRouterAction* GetMediaRouterAction() {
+  // Returns the dialog controller for the active WebContents.
+  MediaRouterDialogControllerImplBase* GetDialogController() {
     return MediaRouterDialogControllerImplBase::GetOrCreateForWebContents(
-               browser()->tab_strip_model()->GetActiveWebContents())
-        ->action();
+        browser()->tab_strip_model()->GetActiveWebContents());
+  }
+
+  MediaRouterAction* GetMediaRouterAction() {
+    return GetDialogController()->action();
   }
 
   ui::SimpleMenuModel* GetActionContextMenu() {
@@ -134,9 +138,7 @@
 
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
-    MediaRouterDialogController* dialog_controller =
-        MediaRouterDialogController::GetOrCreateForWebContents(
-            browser()->tab_strip_model()->GetActiveWebContents());
+    MediaRouterDialogController* dialog_controller = GetDialogController();
     content::ContextMenuParams params;
     params.page_url =
         web_contents->GetController().GetLastCommittedEntry()->GetURL();
@@ -164,9 +166,7 @@
         test::AppMenuTestApi::Create(browser());
     app_menu_test_api->ShowMenu();
 
-    MediaRouterDialogController* dialog_controller =
-        MediaRouterDialogController::GetOrCreateForWebContents(
-            browser()->tab_strip_model()->GetActiveWebContents());
+    MediaRouterDialogController* dialog_controller = GetDialogController();
     ASSERT_FALSE(dialog_controller->IsShowingMediaRouterDialog());
     app_menu_test_api->ExecuteCommand(IDC_ROUTE_MEDIA);
     EXPECT_TRUE(dialog_controller->IsShowingMediaRouterDialog());
@@ -180,9 +180,7 @@
   }
 
   void TestEphemeralToolbarIconForDialog() {
-    MediaRouterDialogController* dialog_controller =
-        MediaRouterDialogController::GetOrCreateForWebContents(
-            browser()->tab_strip_model()->GetActiveWebContents());
+    MediaRouterDialogController* dialog_controller = GetDialogController();
 
     EXPECT_FALSE(ActionExists());
     dialog_controller->ShowMediaRouterDialog();
@@ -211,6 +209,31 @@
     EXPECT_FALSE(ActionExists());
   }
 
+  void TestPinAndUnpinToolbarIcon() {
+    GetDialogController()->ShowMediaRouterDialog();
+    EXPECT_TRUE(ActionExists());
+
+    // Pin the icon via its context menu.
+    ui::SimpleMenuModel* context_menu = GetActionContextMenu();
+    const int command_index = context_menu->GetIndexOfCommandId(
+        IDC_MEDIA_ROUTER_ALWAYS_SHOW_TOOLBAR_ACTION);
+    if (IsCocoaBrowser()) {
+      // With Cocoa, OnContextMenuClosed() gets called before command execution.
+      GetMediaRouterAction()->OnContextMenuClosed();
+      context_menu->ActivatedAt(command_index);
+    } else {
+      context_menu->ActivatedAt(command_index);
+      GetMediaRouterAction()->OnContextMenuClosed();
+    }
+    GetDialogController()->HideMediaRouterDialog();
+    EXPECT_TRUE(ActionExists());
+
+    // Unpin the icon via its context menu.
+    GetActionContextMenu()->ActivatedAt(command_index);
+    GetMediaRouterAction()->OnContextMenuClosed();
+    EXPECT_FALSE(ActionExists());
+  }
+
   ToolbarActionsBar* toolbar_actions_bar_ = nullptr;
 
   Issue issue_;
@@ -393,6 +416,10 @@
       toolbar_actions_bar_->IsActionVisibleOnMainBar(GetMediaRouterAction()));
 }
 
+IN_PROC_BROWSER_TEST_F(MediaRouterUIBrowserTest, PinAndUnpinToolbarIcon) {
+  TestPinAndUnpinToolbarIcon();
+}
+
 // Runs dialog-related tests with the Views Cast dialog.
 class MediaRouterViewsUIBrowserTest : public MediaRouterUIBrowserTest {
  protected:
@@ -424,4 +451,10 @@
   TestEphemeralToolbarIconForDialog();
 }
 
+IN_PROC_BROWSER_TEST_F(MediaRouterViewsUIBrowserTest, PinAndUnpinToolbarIcon) {
+  if (IsCocoaBrowser())
+    return;
+  TestPinAndUnpinToolbarIcon();
+}
+
 }  // namespace media_router
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index 7bc77e8..68d0d8a0b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -318,9 +318,6 @@
        !match.image_url.empty());
   is_search_type_ = AutocompleteMatch::IsSearchType(match.type);
   has_tab_match_ = match.has_tab_match;
-  is_definition_ =
-      !!match.answer &&
-      match.answer->type() == SuggestionAnswer::ANSWER_TYPE_DICTIONARY;
 
   // Set up the small icon.
   if (is_rich_suggestion_) {
@@ -483,15 +480,9 @@
     description_view_->SetSize(gfx::Size());
   } else {
     const int text_height = content_view_->GetLineHeight();
-    int content_y = y;
-    int description_y = y + text_height;
-    if (OmniboxFieldTrial::IsReverseAnswersEnabled() && !is_definition_) {
-      std::swap(content_y, description_y);
-    }
-    content_view_->SetBounds(x + kTextIndent, content_y, text_width,
-                             text_height);
+    content_view_->SetBounds(x + kTextIndent, y, text_width, text_height);
     description_view_->SetBounds(
-        x + kTextIndent, description_y, text_width,
+        x + kTextIndent, y + text_height, text_width,
         description_view_->GetHeightForWidth(text_width));
   }
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
index 81c7ca7..838798c 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.h
@@ -46,7 +46,6 @@
   void LayoutNewStyleTwoLineSuggestion();
   void LayoutSplit(int icon_view_width, int text_indent);
 
-  bool is_definition_ = false;
   bool is_old_style_answer_;
   bool is_rich_suggestion_;
   bool is_search_type_;
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 7717ed7..da79a97 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -120,8 +120,9 @@
   }
 
   // Reapply the dim color to account for the highlight state.
-  suggestion_view_->separator()->Dim();
-  keyword_view_->separator()->Dim();
+  const OmniboxPart dim = OmniboxPart::RESULTS_TEXT_DIMMED;
+  suggestion_view_->separator()->ApplyTextColor(dim);
+  keyword_view_->separator()->ApplyTextColor(dim);
   if (suggestion_tab_switch_button_)
     suggestion_tab_switch_button_->UpdateBackground();
 
@@ -135,15 +136,33 @@
       omnibox::kKeywordSearchIcon, GetLayoutConstant(LOCATION_BAR_ICON_SIZE),
       GetColor(OmniboxPart::RESULTS_ICON)));
 
-  // The content text is set to the match text and calculated classifications.
   // Answers use their own styling for additional content text and the
   // description text, whereas non-answer suggestions use the match text and
   // calculated classifications for the description text.
-  suggestion_view_->content()->SetText(match_.contents, match_.contents_class);
   if (match_.answer) {
-    suggestion_view_->content()->AppendExtraText(match_.answer->first_line());
-    suggestion_view_->description()->SetText(match_.answer->second_line());
+    if (OmniboxFieldTrial::IsReverseAnswersEnabled()) {
+      // Answers may swap the content and description fields to change emphasis.
+      // But even when fields swap, the font size and color changes should not.
+      OmniboxTextView* primary = suggestion_view_->content();
+      OmniboxTextView* secondary = suggestion_view_->description();
+      bool swap = !match_.IsExceptedFromLineReversal();
+      if (swap)
+        std::swap(primary, secondary);
+      primary->SetText(match_.contents, match_.contents_class, swap ? -1 : 0);
+      primary->AppendExtraText(match_.answer->first_line());
+      primary->ApplyTextColor(swap ? dim : OmniboxPart::RESULTS_TEXT_DEFAULT);
+      secondary->SetText(match_.answer->second_line(), swap ? 0 : -1);
+      secondary->ApplyTextColor(swap ? OmniboxPart::RESULTS_TEXT_DEFAULT : dim);
+    } else {
+      suggestion_view_->content()->SetText(match_.contents,
+                                           match_.contents_class);
+      suggestion_view_->content()->AppendExtraText(match_.answer->first_line());
+      suggestion_view_->description()->SetText(match_.answer->second_line());
+    }
   } else {
+    // Content and description use match text and calculated classifications.
+    suggestion_view_->content()->SetText(match_.contents,
+                                         match_.contents_class);
     suggestion_view_->description()->SetText(match_.description,
                                              match_.description_class);
   }
@@ -157,7 +176,7 @@
                                       keyword_match->contents_class);
     keyword_view_->description()->SetText(keyword_match->description,
                                           keyword_match->description_class);
-    keyword_view_->description()->Dim();
+    keyword_view_->description()->ApplyTextColor(dim);
   }
 }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
index 6663d701..ce2be0b 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
@@ -137,7 +137,10 @@
 }  // namespace
 
 OmniboxTextView::OmniboxTextView(OmniboxResultView* result_view)
-    : result_view_(result_view), font_height_(0), wrap_text_lines_(false) {}
+    : result_view_(result_view),
+      font_height_(0),
+      font_size_delta_(0),
+      wrap_text_lines_(false) {}
 
 OmniboxTextView::~OmniboxTextView() {}
 
@@ -179,9 +182,8 @@
   render_text_->Draw(canvas);
 }
 
-void OmniboxTextView::Dim() {
-  render_text_->SetColor(
-      result_view_->GetColor(OmniboxPart::RESULTS_TEXT_DIMMED));
+void OmniboxTextView::ApplyTextColor(OmniboxPart part) {
+  render_text_->SetColor(result_view_->GetColor(part));
 }
 
 const base::string16& OmniboxTextView::text() const {
@@ -191,15 +193,17 @@
   return render_text_->text();
 }
 
-void OmniboxTextView::SetText(const base::string16& text) {
+void OmniboxTextView::SetText(const base::string16& text, int size_delta) {
   if (cached_classifications_) {
     cached_classifications_.reset();
-  } else if (render_text_ && render_text_->text() == text) {
+  } else if (render_text_ && render_text_->text() == text &&
+             size_delta == font_size_delta_) {
     // Only exit early if |cached_classifications_| was empty,
     // i.e. the last time text was set was through this method.
     return;
   }
 
+  font_size_delta_ = size_delta;
   render_text_.reset();
   render_text_ = CreateRenderText(text);
   UpdateLineHeight();
@@ -207,11 +211,15 @@
 }
 
 void OmniboxTextView::SetText(const base::string16& text,
-                              const ACMatchClassifications& classifications) {
+                              const ACMatchClassifications& classifications,
+                              int size_delta) {
   if (render_text_ && render_text_->text() == text && cached_classifications_ &&
-      classifications == *cached_classifications_)
+      classifications == *cached_classifications_ &&
+      size_delta == font_size_delta_)
     return;
 
+  font_size_delta_ = size_delta;
+
   cached_classifications_ =
       std::make_unique<ACMatchClassifications>(classifications);
   render_text_ = CreateRenderText(text);
@@ -248,11 +256,14 @@
   SetPreferredSize(CalculatePreferredSize());
 }
 
-void OmniboxTextView::SetText(const SuggestionAnswer::ImageLine& line) {
+void OmniboxTextView::SetText(const SuggestionAnswer::ImageLine& line,
+                              int size_delta) {
+  font_size_delta_ = size_delta;
   cached_classifications_.reset();
   wrap_text_lines_ = line.num_text_lines() > 1;
   render_text_.reset();
   render_text_ = CreateRenderText(base::string16());
+
   if (!OmniboxFieldTrial::IsNewAnswerLayoutEnabled()) {
     // This assumes that the first text type in the line can be used to specify
     // the font for all the text fields in the line.  For now this works but
@@ -304,8 +315,13 @@
   render_text->SetDisplayRect(gfx::Rect(gfx::Size(INT_MAX, 0)));
   render_text->SetCursorEnabled(false);
   render_text->SetElideBehavior(gfx::ELIDE_TAIL);
-  render_text->SetFontList(
-      views::style::GetFont(CONTEXT_OMNIBOX_PRIMARY, kTextStyle));
+  const gfx::FontList& font =
+      views::style::GetFont(CONTEXT_OMNIBOX_PRIMARY, kTextStyle);
+  if (font_size_delta_ == 0) {
+    render_text->SetFontList(font);
+  } else {
+    render_text->SetFontList(font.DeriveWithSizeDelta(font_size_delta_));
+  }
   render_text->SetText(text);
   return render_text;
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_text_view.h b/chrome/browser/ui/views/omnibox/omnibox_text_view.h
index 49734393..020633e 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_text_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_text_view.h
@@ -36,9 +36,11 @@
   int GetHeightForWidth(int width) const override;
   void OnPaint(gfx::Canvas* canvas) override;
 
-  // Dims the text (i.e. makes it gray). This is used for secondary text (so
-  // that the non-dimmed text stands out more).
-  void Dim();
+  // Applies given part's theme color to underlying render text. Using
+  // OmniboxPart::RESULTS_TEXT_DIMMED gives the gray used by Dim() in the past.
+  // This is called Apply* instead of Set* because the only state kept is in
+  // render_text, so call this after setting text with methods below.
+  void ApplyTextColor(OmniboxPart part);
 
   // Returns the render text, or an empty string if there is none.
   const base::string16& text() const;
@@ -46,10 +48,13 @@
   // Sets the render text with default rendering for the given |text|. The
   // |classifications| are used to style the text. An ImageLine incorporates
   // both the text and the styling.
-  void SetText(const base::string16& text);
+  // The size_delta is specified here so it can be known in advance of creating
+  // the render text. Applying later would kill bold (clear weights BreakList).
+  void SetText(const base::string16& text, int size_delta = 0);
   void SetText(const base::string16& text,
-               const ACMatchClassifications& classifications);
-  void SetText(const SuggestionAnswer::ImageLine& line);
+               const ACMatchClassifications& classifications,
+               int font_size_delta = 0);
+  void SetText(const SuggestionAnswer::ImageLine& line, int size_delta = 0);
 
   // Adds the "additional" and "status" text from |line|, if any.
   void AppendExtraText(const SuggestionAnswer::ImageLine& line);
@@ -77,6 +82,9 @@
   // Font settings for this view.
   int font_height_;
 
+  // Delta (in px) from default font size.
+  int font_size_delta_;
+
   // Whether to wrap lines if the width is too narrow for the whole string.
   bool wrap_text_lines_;
 
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 611b221..2177268 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -169,40 +169,39 @@
   // on UI affordances, such as buttons.
   min_size_ = kMinWindowSize;
 
-  // Initial size of the window is always 20% of the display width and height,
-  // constrained by the min and max sizes. Only explicitly update this the first
-  // time |window_size| is being calculated.
-  // Once |window_size| is calculated at least once, it should stay within the
-  // bounds of |min_size_| and |max_size_|.
   gfx::Size window_size;
-  if (!window_bounds_.size().IsEmpty()) {
+  gfx::Point origin;
+
+  if (is_initialized_) {
     window_size = window_bounds_.size();
+    origin = window_bounds_.origin();
   } else {
+    // Determine the initial window bounds:
+    // The initial window size is 20% of the |work_area| screen.
     window_size = gfx::Size(work_area.width() / 5, work_area.height() / 5);
     window_size.set_width(std::min(
         max_size_.width(), std::max(min_size_.width(), window_size.width())));
     window_size.set_height(
         std::min(max_size_.height(),
                  std::max(min_size_.height(), window_size.height())));
+
+    // Determine the initial origin point:
+    // The window is positioned on the bottom right of the |work_area| screen.
+    int window_diff_width = work_area.right() - window_size.width();
+    int window_diff_height = work_area.bottom() - window_size.height();
+
+    // There will be a margin between the edges of the Picture-in-Picture
+    // window and the |work_area| screen by taking 2% of the average of the
+    // window dimensions.
+    int margin = (window_diff_width + window_diff_height) / 2 * 0.02;
+
+    origin =
+        gfx::Point(window_diff_width - margin, window_diff_height - margin);
   }
 
-  // Determine the window size by fitting |natural_size_| within
-  // |window_size|, keeping to |natural_size_|'s aspect ratio.
-  if (!natural_size_.IsEmpty()) {
-    UpdateVideoLayerSizeWithAspectRatio(window_size);
-    window_size = video_bounds_.size();
-  }
+  UpdateVideoLayerSizeWithAspectRatio(window_size);
 
-  int window_diff_width = work_area.right() - window_size.width();
-  int window_diff_height = work_area.bottom() - window_size.height();
-
-  // Keep a margin distance of 2% the average of the two window size
-  // differences, keeping the margins consistent.
-  int buffer = (window_diff_width + window_diff_height) / 2 * 0.02;
-  window_bounds_ = gfx::Rect(
-      gfx::Point(window_diff_width - buffer, window_diff_height - buffer),
-      window_size);
-
+  window_bounds_ = gfx::Rect(origin, window_size);
   return window_bounds_;
 }
 
@@ -573,6 +572,13 @@
   views::Widget::OnNativeBlur();
 }
 
+void OverlayWindowViews::OnNativeWidgetMove() {
+  // Update the existing |window_bounds_| when the window moves. This allows
+  // the window to reappear with the same origin point when a new video is
+  // shown.
+  window_bounds_ = GetBounds();
+}
+
 void OverlayWindowViews::OnNativeWidgetSizeChanged(const gfx::Size& new_size) {
   // Update the view layers to scale to |new_size|.
   UpdateCloseControlsSize();
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.h b/chrome/browser/ui/views/overlay/overlay_window_views.h
index abac8a3b..3649222 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.h
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.h
@@ -48,6 +48,7 @@
   // views::internal::NativeWidgetDelegate:
   void OnNativeFocus() override;
   void OnNativeBlur() override;
+  void OnNativeWidgetMove() override;
   void OnNativeWidgetSizeChanged(const gfx::Size& new_size) override;
 
   // Gets the bounds of the controls.
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 9d53835..9066e64 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -154,29 +154,37 @@
       flags);
 }
 
-// Scales |bounds| by scale and aligns so that the layout portion is snapped to
-// the pixel grid.  This ensures adjacent tabs meet up exactly during painting.
+// Scales |bounds| by scale and aligns so that adjacent tabs meet up exactly
+// during painting.
 const gfx::RectF ScaleAndAlignBounds(const gfx::Rect& bounds, float scale) {
-  // Convert full bounds to layout bounds and scale from DIP to px.
+  // Convert to layout bounds.  We must inset the width such that the right edge
+  // of one tab's layout bounds is the same as the left edge of the next tab's;
+  // this way the two tabs' separators will be drawn at the same coordinate.
   gfx::RectF aligned_bounds(bounds);
+  const int stroke_height = Tab::GetStrokeHeight();
   const int corner_radius = Tab::GetCornerRadius();
-  aligned_bounds.Inset(corner_radius, 0);
+  // Note: This intentionally doesn't subtract TABSTRIP_TOOLBAR_OVERLAP from the
+  // bottom inset, because we want to pixel-align the bottom of the stroke, not
+  // the bottom of the overlap.
+  gfx::InsetsF layout_insets(stroke_height, corner_radius, stroke_height,
+                             corner_radius + Tab::kSeparatorThickness);
+  aligned_bounds.Inset(layout_insets);
+
+  // Scale layout bounds from DIP to px.
   aligned_bounds.Scale(scale);
 
-  // Snap layout bounds to nearest pixels.
+  // Snap layout bounds to nearest pixels so we get clean lines.
   const float x = std::round(aligned_bounds.x());
   const float y = std::round(aligned_bounds.y());
   // It's important to round the right edge and not the width, since rounding
   // both x and width would mean the right edge would accumulate error.
   const float right = std::round(aligned_bounds.right());
-  // The bottom is ceiled rather than rounded to ensure it overlaps the toolbar
-  // rather than leaving a gap.
-  const float bottom = std::ceil(aligned_bounds.bottom());
+  const float bottom = std::round(aligned_bounds.bottom());
   aligned_bounds = gfx::RectF(x, y, right - x, bottom - y);
 
-  // Convert back to full bounds.  The endcap widths are not rounded, since it's
-  // OK if the corners do not snap to the pixel grid.
-  aligned_bounds.Inset(-corner_radius * scale, 0);
+  // Convert back to full bounds.  It's OK that the outer corners of the curves
+  // around the separator may not be snapped to the pixel grid as a result.
+  aligned_bounds.Inset(-layout_insets.Scale(scale));
   return aligned_bounds;
 }
 
@@ -1189,7 +1197,10 @@
 gfx::Insets Tab::GetContentsInsets() {
   const int endcap_width = MD::IsRefreshUi() ? (GetCornerRadius() * 2)
                                              : GetTabEndcapWidthForLayout();
-  return gfx::Insets(GetStrokeHeight(), endcap_width);
+  return gfx::Insets(
+      GetStrokeHeight(), endcap_width,
+      GetStrokeHeight() + GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP),
+      endcap_width);
 }
 
 // static
diff --git a/chrome/browser/ui/webui/settings/chromeos/smb_handler.cc b/chrome/browser/ui/webui/settings/chromeos/smb_handler.cc
index b50b708d..8a5af4e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/smb_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/smb_handler.cc
@@ -59,9 +59,17 @@
   mo.display_name = mount_name.empty() ? mount_url : mount_name;
   mo.writable = true;
 
-  service->Mount(mo, base::FilePath(mount_url), username, password,
-                 base::BindOnce(&SmbHandler::HandleSmbMountResponse,
-                                weak_ptr_factory_.GetWeakPtr()));
+  auto mount_response = base::BindOnce(&SmbHandler::HandleSmbMountResponse,
+                                       weak_ptr_factory_.GetWeakPtr());
+  auto mount_call = base::BindOnce(
+      &smb_client::SmbService::Mount, base::Unretained(service), mo,
+      base::FilePath(mount_url), username, password, std::move(mount_response));
+
+  if (host_discovery_done_) {
+    std::move(mount_call).Run();
+  } else {
+    stored_mount_call_ = std::move(mount_call);
+  }
 }
 
 void SmbHandler::HandleSmbMountResponse(SmbMountResult result) {
@@ -78,6 +86,11 @@
 
 void SmbHandler::HandleGatherSharesResponse(
     const std::vector<smb_client::SmbUrl>& shares_gathered) {
+  host_discovery_done_ = true;
+  if (!stored_mount_call_.is_null()) {
+    std::move(stored_mount_call_).Run();
+  }
+
   // TODO(zentaro): Pass the shares discovered back to the UI.
   // https://crbug.com/852199.
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/smb_handler.h b/chrome/browser/ui/webui/settings/chromeos/smb_handler.h
index 5e8ee1d0..fe6c85d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/smb_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/smb_handler.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SMB_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SMB_HANDLER_H_
 
+#include "base/callback_forward.h"
 #include "base/files/file.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -41,6 +42,8 @@
   void HandleGatherSharesResponse(
       const std::vector<smb_client::SmbUrl>& shares_gathered);
 
+  bool host_discovery_done_ = false;
+  base::OnceClosure stored_mount_call_;
   Profile* const profile_;
   base::WeakPtrFactory<SmbHandler> weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
index 1912aa4..fd173de 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -41,6 +41,8 @@
   int undo_button_ids = -1;
   if (is_unified_consent_enabled && is_sync_allowed) {
     source->SetDefaultResource(IDR_DICE_SYNC_CONFIRMATION_HTML);
+    source->AddResourcePath("icons.html",
+                            IDR_DICE_SYNC_CONFIRMATION_ICONS_HTML);
     source->AddResourcePath("sync_confirmation_browser_proxy.html",
                             IDR_DICE_SYNC_CONFIRMATION_BROWSER_PROXY_HTML);
     source->AddResourcePath("sync_confirmation_browser_proxy.js",
diff --git a/chrome/browser/vr/service/browser_xr_device.cc b/chrome/browser/vr/service/browser_xr_device.cc
index 0c95f94..b70ed62 100644
--- a/chrome/browser/vr/service/browser_xr_device.cc
+++ b/chrome/browser/vr/service/browser_xr_device.cc
@@ -119,7 +119,10 @@
       presenting_display_host_ = display;
       immersive_session_controller_ = std::move(immersive_session_controller);
     }
-    std::move(callback).Run(std::move(connection));
+
+    device::mojom::XRSessionPtr xr_session = device::mojom::XRSession::New();
+    xr_session->connection = std::move(connection);
+    std::move(callback).Run(std::move(xr_session));
   } else {
     std::move(callback).Run(nullptr);
     if (connection) {
diff --git a/chrome/browser/vr/service/vr_display_host.cc b/chrome/browser/vr/service/vr_display_host.cc
index 7aa883f..5918a2f 100644
--- a/chrome/browser/vr/service/vr_display_host.cc
+++ b/chrome/browser/vr/service/vr_display_host.cc
@@ -62,39 +62,58 @@
       render_frame_host_(render_frame_host),
       binding_(this),
       weak_ptr_factory_(this) {
-  device::mojom::VRMagicWindowProviderPtr magic_window_provider;
-  device->GetRuntime()->RequestMagicWindowSession(
-      mojo::MakeRequest(&magic_window_provider),
-      mojo::MakeRequest(&magic_window_controller_),
-      base::BindOnce(&VRDisplayHost::OnMagicWindowSessionCreated,
-                     weak_ptr_factory_.GetWeakPtr()));
-
   // Tell blink that we are available.
   device::mojom::VRDisplayHostPtr display_host;
   binding_.Bind(mojo::MakeRequest(&display_host));
 
-  service_client->OnDisplayConnected(
-      std::move(magic_window_provider), std::move(display_host),
-      mojo::MakeRequest(&client_), browser_device_->GetVRDisplayInfo());
-
   // Tell the BrowserXrDevice about us.
   browser_device_->OnDisplayHostAdded(this);
+  service_client->OnDisplayConnected(std::move(display_host),
+                                     mojo::MakeRequest(&client_),
+                                     device->GetVRDisplayInfo());
 }
 
-void VRDisplayHost::OnMagicWindowSessionCreated(bool success) {
-  if (success) {
-    // Start giving out magic window data if we are focused.
-    magic_window_controller_->SetFrameDataRestricted(!in_focused_frame_);
+void VRDisplayHost::OnARSessionCreated(
+    device::mojom::VRDisplayHost::RequestSessionCallback callback,
+    device::mojom::XRSessionPtr session) {
+  if (!session) {
+    std::move(callback).Run(nullptr);
+    return;
   }
+
+  browser_device_->GetRuntime()->RequestMagicWindowSession(
+      base::BindOnce(&VRDisplayHost::OnMagicWindowSessionCreated,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void VRDisplayHost::OnMagicWindowSessionCreated(
+    device::mojom::VRDisplayHost::RequestSessionCallback callback,
+    device::mojom::VRMagicWindowProviderPtr magic_window_provider,
+    device::mojom::XRSessionControllerPtr controller) {
+  if (!magic_window_provider) {
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
+  // Start giving out magic window data if we are focused.
+  controller->SetFrameDataRestricted(!in_focused_frame_);
+
+  magic_window_controllers_.AddPtr(std::move(controller));
+
+  device::mojom::XRSessionPtr xr_session = device::mojom::XRSession::New();
+  xr_session->magic_window_provider = magic_window_provider.PassInterface();
+
+  std::move(callback).Run(std::move(xr_session));
 }
 
 VRDisplayHost::~VRDisplayHost() {
   browser_device_->OnDisplayHostRemoved(this);
 }
 
-void VRDisplayHost::RequestSession(device::mojom::XRSessionOptionsPtr options,
-                                   bool triggered_by_displayactive,
-                                   RequestSessionCallback callback) {
+void VRDisplayHost::RequestSession(
+    device::mojom::XRSessionOptionsPtr options,
+    bool triggered_by_displayactive,
+    device::mojom::VRDisplayHost::RequestSessionCallback callback) {
   DCHECK(options);
 
   if (!InternalSupportsSession(options.get()) ||
@@ -121,28 +140,26 @@
 
   // AR currently uses a non-immersive session but we still want to call request
   // session on it.
-  if (runtime_options->immersive ||
-      base::FeatureList::IsEnabled(features::kWebXrHitTest)) {
+  if (runtime_options->immersive) {
     if (!triggered_by_displayactive) {
       ReportRequestPresent();
     }
 
     browser_device_->RequestSession(this, std::move(runtime_options),
                                     std::move(callback));
+  } else if (base::FeatureList::IsEnabled(features::kWebXrHitTest)) {
+    // WebXrHitTest enabled means we are requesting an AR session.  This means
+    // we make two requests to the device - one to request permissions and start
+    // the runtime, then a followup to actually get the magic window provider.
+    // TODO(offenwanger): Clean this up and make only one request.
+    browser_device_->RequestSession(
+        this, std::move(runtime_options),
+        base::BindOnce(&VRDisplayHost::OnARSessionCreated,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   } else {
-    // TODO(offenwanger) When the XRMagicWindowProvider or equivalent is
-    // returned here, clean out this dummy code.
-    auto connection = device::mojom::XRPresentationConnection::New();
-    device::mojom::VRSubmitFrameClientPtr submit_client;
-    connection->client_request = mojo::MakeRequest(&submit_client);
-    device::mojom::VRPresentationProviderPtr provider;
-    mojo::MakeRequest(&provider);
-    connection->provider = provider.PassInterface();
-    connection->transport_options =
-        device::mojom::VRDisplayFrameTransportOptions::New();
-    // Non immersive session setup happens on device initialization, so we don't
-    // need to do anything further.
-    std::move(callback).Run(std::move(connection));
+    browser_device_->GetRuntime()->RequestMagicWindowSession(
+        base::BindOnce(&VRDisplayHost::OnMagicWindowSessionCreated,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 }
 
@@ -186,8 +203,11 @@
 void VRDisplayHost::SetInFocusedFrame(bool in_focused_frame) {
   in_focused_frame_ = in_focused_frame;
   browser_device_->UpdateListeningForActivate(this);
-  if (magic_window_controller_)
-    magic_window_controller_->SetFrameDataRestricted(!in_focused_frame_);
+
+  magic_window_controllers_.ForAllPtrs(
+      [&in_focused_frame](device::mojom::XRSessionController* controller) {
+        controller->SetFrameDataRestricted(!in_focused_frame);
+      });
 }
 
 void VRDisplayHost::OnChanged(device::mojom::VRDisplayInfoPtr vr_device_info) {
diff --git a/chrome/browser/vr/service/vr_display_host.h b/chrome/browser/vr/service/vr_display_host.h
index acd354b..a9db54c 100644
--- a/chrome/browser/vr/service/vr_display_host.h
+++ b/chrome/browser/vr/service/vr_display_host.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_VR_SERVICE_VR_DISPLAY_HOST_H_
 #define CHROME_BROWSER_VR_SERVICE_VR_DISPLAY_HOST_H_
 
+#include <map>
 #include <memory>
 
 #include "base/macros.h"
@@ -12,6 +13,7 @@
 #include "device/vr/public/mojom/vr_service.mojom.h"
 #include "device/vr/vr_device.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
 
 namespace content {
 class RenderFrameHost;
@@ -36,9 +38,10 @@
   ~VRDisplayHost() override;
 
   // device::mojom::VRDisplayHost
-  void RequestSession(device::mojom::XRSessionOptionsPtr options,
-                      bool triggered_by_displayactive,
-                      RequestSessionCallback callback) override;
+  void RequestSession(
+      device::mojom::XRSessionOptionsPtr options,
+      bool triggered_by_displayactive,
+      device::mojom::VRDisplayHost::RequestSessionCallback callback) override;
   void SupportsSession(device::mojom::XRSessionOptionsPtr options,
                        SupportsSessionCallback callback) override;
   void ExitPresent() override;
@@ -62,7 +65,13 @@
   bool IsAnotherHostPresenting();
 
   bool InternalSupportsSession(device::mojom::XRSessionOptions* options);
-  void OnMagicWindowSessionCreated(bool success);
+  void OnMagicWindowSessionCreated(
+      device::mojom::VRDisplayHost::RequestSessionCallback callback,
+      device::mojom::VRMagicWindowProviderPtr session,
+      device::mojom::XRSessionControllerPtr controller);
+  void OnARSessionCreated(
+      device::mojom::VRDisplayHost::RequestSessionCallback callback,
+      device::mojom::XRSessionPtr session);
 
   // TODO(https://crbug.com/837538): Instead, check before returning this
   // object.
@@ -76,7 +85,9 @@
   mojo::Binding<device::mojom::VRDisplayHost> binding_;
   device::mojom::VRDisplayClientPtr client_;
 
-  device::mojom::XRSessionControllerPtr magic_window_controller_;
+  mojo::InterfacePtrSet<device::mojom::XRSessionController>
+      magic_window_controllers_;
+  int next_key_ = 0;
 
   base::WeakPtrFactory<VRDisplayHost> weak_ptr_factory_;
 
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index a168494..83a7034 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -3,5 +3,12 @@
 # found in the LICENSE file.
 
 source_set("components") {
-  # TODO(loyso): Add os_shortcuts component here.
+  sources = [
+    "web_app_helpers.cc",
+    "web_app_helpers.h",
+  ]
+
+  deps = [
+    "//base",
+  ]
 }
diff --git a/chrome/browser/web_applications/components/web_app_helpers.cc b/chrome/browser/web_applications/components/web_app_helpers.cc
new file mode 100644
index 0000000..b53b454
--- /dev/null
+++ b/chrome/browser/web_applications/components/web_app_helpers.cc
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+
+#include "url/gurl.h"
+
+namespace web_app {
+
+std::string GenerateApplicationNameFromURL(const GURL& url) {
+  std::string t;
+  t.append(url.host());
+  t.append("_");
+  t.append(url.path());
+  return t;
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_helpers.h b/chrome/browser/web_applications/components/web_app_helpers.h
new file mode 100644
index 0000000..2dd9f1bf
--- /dev/null
+++ b/chrome/browser/web_applications/components/web_app_helpers.h
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_HELPERS_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_HELPERS_H_
+
+#include <string>
+
+class GURL;
+
+namespace web_app {
+
+// Compute a deterministic name based on the URL. We use this pseudo name
+// as a key to store window location per application URLs in Browser and
+// as app id for BrowserWindow, shortcut and jump list.
+std::string GenerateApplicationNameFromURL(const GURL& url);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_HELPERS_H_
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn
index 073b8be..c8058dd 100644
--- a/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -10,9 +10,12 @@
   sources = [
     "pending_bookmark_app_manager.cc",
     "pending_bookmark_app_manager.h",
+    "web_app_extension_helpers.cc",
+    "web_app_extension_helpers.h",
   ]
 
   deps = [
+    "//base",
     "//chrome/browser/web_applications/components",
   ]
 }
diff --git a/chrome/browser/web_applications/extensions/web_app_extension_helpers.cc b/chrome/browser/web_applications/extensions/web_app_extension_helpers.cc
new file mode 100644
index 0000000..0d42b0d
--- /dev/null
+++ b/chrome/browser/web_applications/extensions/web_app_extension_helpers.cc
@@ -0,0 +1,30 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
+
+namespace web_app {
+
+// The following string is used to build the directory name for
+// shortcuts to chrome applications (the kind which are installed
+// from a CRX).  Application shortcuts to URLs use the {host}_{path}
+// for the name of this directory.  Hosts can't include an underscore.
+// By starting this string with an underscore, we ensure that there
+// are no naming conflicts.
+static const char kCrxAppPrefix[] = "_crx_";
+
+std::string GenerateApplicationNameFromExtensionId(const std::string& id) {
+  std::string t(kCrxAppPrefix);
+  t.append(id);
+  return t;
+}
+
+std::string GetExtensionIdFromApplicationName(const std::string& app_name) {
+  std::string prefix(kCrxAppPrefix);
+  if (app_name.substr(0, prefix.length()) != prefix)
+    return std::string();
+  return app_name.substr(prefix.length());
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/extensions/web_app_extension_helpers.h b/chrome/browser/web_applications/extensions/web_app_extension_helpers.h
new file mode 100644
index 0000000..d55a74f0
--- /dev/null
+++ b/chrome/browser/web_applications/extensions/web_app_extension_helpers.h
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_WEB_APP_EXTENSION_HELPERS_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_WEB_APP_EXTENSION_HELPERS_H_
+
+#include <string>
+
+namespace web_app {
+
+// Compute a deterministic name based on an extension/apps's id.
+std::string GenerateApplicationNameFromExtensionId(const std::string& id);
+
+// Extracts the extension id from the app name.
+std::string GetExtensionIdFromApplicationName(const std::string& app_name);
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_WEB_APP_EXTENSION_HELPERS_H_
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index 220f6240..911cec9 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -23,6 +23,8 @@
 #include "chrome/browser/extensions/extension_ui_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
@@ -135,14 +137,6 @@
 
 namespace web_app {
 
-// The following string is used to build the directory name for
-// shortcuts to chrome applications (the kind which are installed
-// from a CRX).  Application shortcuts to URLs use the {host}_{path}
-// for the name of this directory.  Hosts can't include an underscore.
-// By starting this string with an underscore, we ensure that there
-// are no naming conflicts.
-static const char kCrxAppPrefix[] = "_crx_";
-
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
   registry->RegisterListPref(prefs::kWebAppInstallForceList);
 }
@@ -369,27 +363,6 @@
     return GenerateApplicationNameFromURL(shortcut_info.url);
 }
 
-std::string GenerateApplicationNameFromURL(const GURL& url) {
-  std::string t;
-  t.append(url.host());
-  t.append("_");
-  t.append(url.path());
-  return t;
-}
-
-std::string GenerateApplicationNameFromExtensionId(const std::string& id) {
-  std::string t(kCrxAppPrefix);
-  t.append(id);
-  return t;
-}
-
-std::string GetExtensionIdFromApplicationName(const std::string& app_name) {
-  std::string prefix(kCrxAppPrefix);
-  if (app_name.substr(0, prefix.length()) != prefix)
-    return std::string();
-  return app_name.substr(prefix.length());
-}
-
 void CreateShortcutsWithInfo(ShortcutCreationReason reason,
                              const ShortcutLocations& locations,
                              std::unique_ptr<ShortcutInfo> shortcut_info) {
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index a2f68083..4d4e43e 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -140,17 +140,6 @@
 // Compute a deterministic name based on data in the shortcut_info.
 std::string GenerateApplicationNameFromInfo(const ShortcutInfo& shortcut_info);
 
-// Compute a deterministic name based on the URL. We use this pseudo name
-// as a key to store window location per application URLs in Browser and
-// as app id for BrowserWindow, shortcut and jump list.
-std::string GenerateApplicationNameFromURL(const GURL& url);
-
-// Compute a deterministic name based on an extension/apps's id.
-std::string GenerateApplicationNameFromExtensionId(const std::string& id);
-
-// Extracts the extension id from the app name.
-std::string GetExtensionIdFromApplicationName(const std::string& app_name);
-
 // Create shortcuts for web application based on given shortcut data.
 // |shortcut_info| contains information about the shortcuts to create, and
 // |locations| contains information about where to create them.
diff --git a/chrome/test/data/webui/print_preview/model_test.js b/chrome/test/data/webui/print_preview/model_test.js
index 67733dd..e0ffbd0 100644
--- a/chrome/test/data/webui/print_preview/model_test.js
+++ b/chrome/test/data/webui/print_preview/model_test.js
@@ -6,6 +6,8 @@
   /** @enum {string} */
   const TestNames = {
     SetStickySettings: 'set sticky settings',
+    GetPrintTicket: 'get print ticket',
+    GetCloudPrintTicket: 'get cloud print ticket',
   };
 
   const suiteName = 'ModelTest';
@@ -81,14 +83,11 @@
              */
             function(e) {
               let settings = JSON.parse(e.detail);
-              for (let settingName in stickySettingsDefault) {
-                if (stickySettingsDefault.hasOwnProperty(settingName)) {
-                  let toCompare = settingName == field ? stickySettingsChange :
-                                                         stickySettingsDefault;
-                  assertDeepEquals(
-                      toCompare[settingName], settings[settingName]);
-                }
-              }
+              Object.keys(stickySettingsDefault).forEach(settingName => {
+                let toCompare = settingName == field ? stickySettingsChange :
+                                                       stickySettingsDefault;
+                assertDeepEquals(toCompare[settingName], settings[settingName]);
+              });
               let restorePromise =
                   test_util.eventToPromise('save-sticky-settings', model);
               model.set(
@@ -97,7 +96,7 @@
             });
       };
 
-      model.initialized_ = true;
+      model.applyStickySettings();
       return testStickySetting('collate', 'isCollateEnabled')
           .then(() => testStickySetting('color', 'isColorEnabled'))
           .then(
@@ -115,6 +114,225 @@
           .then(() => testStickySetting('fitToPage', 'isFitToPageEnabled'))
           .then(() => testStickySetting('vendorItems', 'vendorOptions'));
     });
+
+    function toggleSettings() {
+      // Some non default setting values to change to.
+      const settingsChange = {
+        pages: [2],
+        copies: '2',
+        collate: false,
+        layout: true,
+        color: false,
+        mediaSize: {
+          width_microns: 240000,
+          height_microns: 200000,
+        },
+        margins: print_preview.ticket_items.MarginsTypeValue.CUSTOM,
+        customMargins: {
+          marginTop: 100,
+          marginRight: 200,
+          marginBottom: 300,
+          marginLeft: 400,
+        },
+        dpi: {
+          horizontal_dpi: 100,
+          vertical_dpi: 100,
+        },
+        fitToPage: true,
+        scaling: '90',
+        duplex: false,
+        cssBackground: true,
+        selectionOnly: true,
+        headerFooter: false,
+        rasterize: true,
+        vendorItems: {
+          paperType: 1,
+          printArea: 6,
+        },
+        ranges: [{from: 2, to: 2}],
+      };
+
+      // Update settings
+      Object.keys(settingsChange).forEach(setting => {
+        model.set(`settings.${setting}.value`, settingsChange[setting]);
+      });
+    }
+
+    function initializeModel() {
+      model.documentInfo = new print_preview.DocumentInfo();
+      model.documentInfo.init(true, 'title', true);
+      model.documentInfo.updatePageCount(3);
+      // Update pages accordingly.
+      model.set('settings.pages.value', [1, 2, 3]);
+
+      // Initialize some settings that don't have defaults to the destination
+      // defaults.
+      model.set('settings.dpi.value', {horizontal_dpi: 200, vertical_dpi: 200});
+      model.set('settings.vendorItems.value', {paperType: 0, printArea: 4});
+
+      // Set rasterize available so that it can be tested.
+      model.set('settings.rasterize.available', true);
+    }
+
+    /**
+     * Tests that toggling each setting results in the expected change to the
+     * print ticket.
+     */
+    test(assert(TestNames.GetPrintTicket), function() {
+      const testDestination = new print_preview.Destination(
+          'FooDevice', print_preview.DestinationType.LOCAL,
+          print_preview.DestinationOrigin.LOCAL, 'FooName', true /* isRecent */,
+          print_preview.DestinationConnectionStatus.ONLINE);
+      testDestination.capabilities =
+          print_preview_test_utils.getCddTemplateWithAdvancedSettings(2)
+              .capabilities;
+
+      initializeModel();
+      const defaultTicket =
+          model.createPrintTicket(testDestination, false, false);
+      const expectedDefaultTicket = JSON.stringify({
+        mediaSize: {width_microns: 215900, height_microns: 279400},
+        pageCount: 3,
+        landscape: false,
+        color: testDestination.getNativeColorModel(true),
+        headerFooterEnabled: false,  // Only used in print preview
+        marginsType: print_preview.ticket_items.MarginsTypeValue.DEFAULT,
+        duplex: print_preview_new.DuplexMode.LONG_EDGE,
+        copies: 1,
+        collate: true,
+        shouldPrintBackgrounds: false,
+        shouldPrintSelectionOnly: false,
+        previewModifiable: true,
+        printToPDF: false,
+        printWithCloudPrint: false,
+        printWithPrivet: false,
+        printWithExtension: false,
+        rasterizePDF: false,
+        scaleFactor: 100,
+        pagesPerSheet: 1,
+        dpiHorizontal: 200,
+        dpiVertical: 200,
+        deviceName: 'FooDevice',
+        fitToPageEnabled: false,
+        pageWidth: 612,
+        pageHeight: 792,
+        showSystemDialog: false,
+      });
+      expectEquals(expectedDefaultTicket, defaultTicket);
+
+      // Toggle all the values and create a new print ticket.
+      toggleSettings();
+      const newTicket = model.createPrintTicket(testDestination, false, false);
+      const expectedNewTicket = JSON.stringify({
+        mediaSize: {width_microns: 240000, height_microns: 200000},
+        pageCount: 1,
+        landscape: true,
+        color: testDestination.getNativeColorModel(false),
+        headerFooterEnabled: false,
+        marginsType: print_preview.ticket_items.MarginsTypeValue.CUSTOM,
+        duplex: print_preview_new.DuplexMode.SIMPLEX,
+        copies: 2,
+        collate: false,
+        shouldPrintBackgrounds: true,
+        shouldPrintSelectionOnly: false,  // Only for Print Preview.
+        previewModifiable: true,
+        printToPDF: false,
+        printWithCloudPrint: false,
+        printWithPrivet: false,
+        printWithExtension: false,
+        rasterizePDF: true,
+        scaleFactor: 90,
+        pagesPerSheet: 1,
+        dpiHorizontal: 100,
+        dpiVertical: 100,
+        deviceName: 'FooDevice',
+        fitToPageEnabled: true,
+        pageWidth: 612,
+        pageHeight: 792,
+        showSystemDialog: false,
+        marginsCustom: {
+          marginTop: 100,
+          marginRight: 200,
+          marginBottom: 300,
+          marginLeft: 400,
+        },
+      });
+      expectEquals(expectedNewTicket, newTicket);
+    });
+
+    /**
+     * Tests that toggling each setting results in the expected change to the
+     * cloud job print ticket.
+     */
+    test(assert(TestNames.GetCloudPrintTicket), function() {
+      initializeModel();
+
+      // Create a test cloud destination.
+      const testDestination = new print_preview.Destination(
+          'FooCloudDevice', print_preview.DestinationType.GOOGLE,
+          print_preview.DestinationOrigin.COOKIES, 'FooCloudName',
+          true /* isRecent */,
+          print_preview.DestinationConnectionStatus.ONLINE);
+      testDestination.capabilities =
+          print_preview_test_utils.getCddTemplateWithAdvancedSettings(2)
+              .capabilities;
+
+      const defaultTicket = model.createCloudJobTicket(testDestination);
+      const expectedDefaultTicket = JSON.stringify({
+        version: '1.0',
+        print: {
+          collate: {collate: true},
+          color: {
+            type: testDestination.getSelectedColorOption(true).type,
+          },
+          copies: {copies: 1},
+          duplex: {type: 'LONG_EDGE'},
+          media_size: {
+            width_microns: 215900,
+            height_microns: 279400,
+          },
+          page_orientation: {type: 'PORTRAIT'},
+          dpi: {
+            horizontal_dpi: 200,
+            vertical_dpi: 200,
+          },
+          vendor_ticket_item: [
+            {id: 'paperType', value: 0},
+            {id: 'printArea', value: 4},
+          ],
+        },
+      });
+      expectEquals(expectedDefaultTicket, defaultTicket);
+
+      // Toggle all the values and create a new cloud job ticket.
+      toggleSettings();
+      const newTicket = model.createCloudJobTicket(testDestination);
+      const expectedNewTicket = JSON.stringify({
+        version: '1.0',
+        print: {
+          collate: {collate: false},
+          color: {
+            type: testDestination.getSelectedColorOption(false).type,
+          },
+          copies: {copies: 2},
+          duplex: {type: 'NO_DUPLEX'},
+          media_size: {
+            width_microns: 240000,
+            height_microns: 200000,
+          },
+          page_orientation: {type: 'LANDSCAPE'},
+          dpi: {
+            horizontal_dpi: 100,
+            vertical_dpi: 100,
+          },
+          vendor_ticket_item: [
+            {id: 'paperType', value: 1},
+            {id: 'printArea', value: 6},
+          ],
+        },
+      });
+      expectEquals(expectedNewTicket, newTicket);
+    });
   });
 
   return {
diff --git a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
index d051f5e9..6b2e28b 100644
--- a/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
+++ b/chrome/test/data/webui/print_preview/new_print_preview_ui_browsertest.js
@@ -252,6 +252,7 @@
   get extraLibraries() {
     return super.extraLibraries.concat([
       '../settings/test_util.js',
+      'print_preview_test_utils.js',
       'model_test.js',
     ]);
   }
@@ -266,6 +267,14 @@
   this.runMochaTest(model_test.TestNames.SetStickySettings);
 });
 
+TEST_F('PrintPreviewModelTest', 'GetPrintTicket', function() {
+  this.runMochaTest(model_test.TestNames.GetPrintTicket);
+});
+
+TEST_F('PrintPreviewModelTest', 'GetCloudPrintTicket', function() {
+  this.runMochaTest(model_test.TestNames.GetCloudPrintTicket);
+});
+
 PrintPreviewPreviewGenerationTest = class extends NewPrintPreviewTest {
   /** @override */
   get browsePreload() {
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
index a8c9580..b8a485ef 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
@@ -11,18 +11,18 @@
 
 /**
  * Creates a single item for the list of passwords.
- * @param {string|undefined} url
- * @param {string|undefined} username
- * @param {number|undefined} passwordLength
+ * @param {string=} url
+ * @param {string=} username
+ * @param {number=} passwordLength
+ * @param {number=} index
  * @return {chrome.passwordsPrivate.PasswordUiEntry}
  */
-FakeDataMaker.passwordEntry = function(url, username, passwordLength) {
+FakeDataMaker.passwordEntry = function(url, username, passwordLength, index) {
   // Generate fake data if param is undefined.
   url = url || FakeDataMaker.patternMaker_('www.xxxxxx.com', 16);
   username = username || FakeDataMaker.patternMaker_('user_xxxxx', 16);
   passwordLength = passwordLength || Math.floor(Math.random() * 15) + 3;
-  entryIndex = -1;
-  exceptionIndex = -1;
+  index = index || 0;
 
   return {
     loginPair: {
@@ -34,24 +34,26 @@
       username: username,
     },
     numCharactersInPassword: passwordLength,
-    index: ++entryIndex,
+    index: index,
   };
 };
 
 /**
  * Creates a single item for the list of password exceptions.
- * @param {string|undefined} url
+ * @param {string=} url
+ * @param {number=} index
  * @return {chrome.passwordsPrivate.ExceptionEntry}
  */
-FakeDataMaker.exceptionEntry = function(url) {
+FakeDataMaker.exceptionEntry = function(url, index) {
   url = url || FakeDataMaker.patternMaker_('www.xxxxxx.com', 16);
+  index = index || 0;
   return {
     urls: {
       origin: 'http://' + url + '/login',
       shown: url,
       link: 'http://' + url + '/login',
     },
-    index: ++exceptionIndex,
+    index: index,
   };
 };
 
diff --git a/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js b/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js
index 120fda8..70d08c9 100644
--- a/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_passwords_section_browsertest.js
@@ -59,11 +59,11 @@
    */
   function validatePasswordList(listElement, passwordList) {
     assertEquals(passwordList.length, listElement.items.length);
-    if (passwordList.length > 0) {
+    for (let index = 0; index < passwordList.length; ++index) {
       // The first child is a template, skip and get the real 'first child'.
-      const node = Polymer.dom(listElement).children[1];
+      const node = Polymer.dom(listElement).children[index + 1];
       assert(node);
-      const passwordInfo = passwordList[0];
+      const passwordInfo = passwordList[index];
       assertEquals(
           passwordInfo.loginPair.urls.shown,
           node.$$('#originUrl').textContent.trim());
@@ -290,6 +290,32 @@
       validatePasswordList(passwordsSection.$.passwordList, passwordList);
     });
 
+    // Test verifies that removing one out of two passwords for the same website
+    // will update the elements.
+    test('verifyPasswordListRemoveSameWebsite', function() {
+      const passwordsSection = createPasswordsSection(passwordManager, [], []);
+
+      // Set-up initial list.
+      let passwordList = [
+        FakeDataMaker.passwordEntry('website.com', 'mario', 1, 0),
+        FakeDataMaker.passwordEntry('website.com', 'luigi', 7, 1)
+      ];
+
+      passwordManager.lastCallback.addSavedPasswordListChangedListener(
+          passwordList);
+      Polymer.dom.flush();
+      validatePasswordList(passwordsSection.$.passwordList, passwordList);
+
+      // Simulate '(website.com, mario)' being removed from the list.
+      passwordList =
+          [FakeDataMaker.passwordEntry('website.com', 'luigi', 7, 0)];
+
+      passwordManager.lastCallback.addSavedPasswordListChangedListener(
+          passwordList);
+      Polymer.dom.flush();
+      validatePasswordList(passwordsSection.$.passwordList, passwordList);
+    });
+
     // Test verifies that pressing the 'remove' button will trigger a remove
     // event. Does not actually remove any passwords.
     test('verifyPasswordItemRemoveButton', function(done) {
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.js b/chrome/test/data/webui/settings/sync_account_control_test.js
index 2ce9148..5a8bc9b 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.js
+++ b/chrome/test/data/webui/settings/sync_account_control_test.js
@@ -279,7 +279,7 @@
 
       assertTrue(testElement.$$('#sync-icon-container')
                      .classList.contains('sync-disabled'));
-      assertTrue(!!testElement.$$('[icon=\'settings:sync\']'));
+      assertTrue(!!testElement.$$('[icon=\'cr:sync\']'));
       displayedText = userInfo.querySelector('span:not([hidden])').textContent;
       assertFalse(displayedText.includes('barName'));
       assertFalse(displayedText.includes('fooName'));
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 05b22d807..3266999 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -148,17 +148,6 @@
 }
 #endif  // defined(OS_ANDROID) && !BUILDFLAG(IS_CAST_USING_CMA_BACKEND)
 
-// Gets the URL request context getter for the single Cast browser context.
-// Must be called on the UI thread.
-scoped_refptr<net::URLRequestContextGetter>
-GetRequestContextGetterFromBrowserContext() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  return scoped_refptr<net::URLRequestContextGetter>(
-      content::BrowserContext::GetDefaultStoragePartition(
-          CastBrowserProcess::GetInstance()->browser_context())
-          ->GetURLRequestContext());
-}
-
 }  // namespace
 
 CastContentBrowserClient::CastContentBrowserClient()
@@ -513,15 +502,6 @@
   return locale.empty() ? "en-US" : locale;
 }
 
-void CastContentBrowserClient::GetGeolocationRequestContext(
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        callback) {
-  content::BrowserThread::PostTaskAndReplyWithResult(
-      content::BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&GetRequestContextGetterFromBrowserContext),
-      std::move(callback));
-}
-
 content::QuotaPermissionContext*
 CastContentBrowserClient::CreateQuotaPermissionContext() {
   return new CastQuotaPermissionContext();
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index b027928..e708e762 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -137,9 +137,6 @@
                            content::WebPreferences* prefs) override;
   void ResourceDispatcherHostCreated() override;
   std::string GetApplicationLocale() override;
-  void GetGeolocationRequestContext(
-      base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-          callback) override;
   content::QuotaPermissionContext* CreateQuotaPermissionContext() override;
   void GetQuotaSettings(
       content::BrowserContext* context,
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 50c1eda..31d36995 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-10877.0.0
\ No newline at end of file
+10883.0.0
\ No newline at end of file
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index ec106dd..b3deb38 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -29,7 +29,8 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "url/gurl.h"
 
-using assistant_client::ActionModule;
+using ActionModule = assistant_client::ActionModule;
+using Resolution = assistant_client::ConversationStateListener::Resolution;
 
 namespace api = ::assistant::api;
 
@@ -197,10 +198,12 @@
 }
 
 void AssistantManagerServiceImpl::StartVoiceInteraction() {
+  platform_api_.SetMicState(true);
   assistant_manager_->StartAssistantInteraction();
 }
 
 void AssistantManagerServiceImpl::StopActiveInteraction() {
+  platform_api_.SetMicState(false);
   assistant_manager_->StopAssistantInteraction();
 }
 
@@ -254,16 +257,22 @@
       dismissed_interaction, "DismissNotification", options, [](auto) {});
 }
 
-void AssistantManagerServiceImpl::OnConversationTurnStarted() {
+void AssistantManagerServiceImpl::OnConversationTurnStarted(bool is_mic_open) {
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &AssistantManagerServiceImpl::OnConversationTurnStartedOnMainThread,
-          weak_factory_.GetWeakPtr()));
+          weak_factory_.GetWeakPtr(), is_mic_open));
 }
 
 void AssistantManagerServiceImpl::OnConversationTurnFinished(
-    assistant_client::ConversationStateListener::Resolution resolution) {
+    Resolution resolution) {
+  // TODO(updowndota): Find a better way to handle the edge cases.
+  if (resolution != Resolution::NORMAL_WITH_FOLLOW_ON &&
+      resolution != Resolution::CANCELLED &&
+      resolution != Resolution::BARGE_IN) {
+    platform_api_.SetMicState(false);
+  }
   main_thread_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -551,45 +560,45 @@
   RegisterFallbackMediaHandler();
 }
 
-void AssistantManagerServiceImpl::OnConversationTurnStartedOnMainThread() {
+void AssistantManagerServiceImpl::OnConversationTurnStartedOnMainThread(
+    bool is_mic_open) {
+  // TODO(dmblack): Pipe |is_mic_open| through the OnInteractionStarted event.
   interaction_subscribers_.ForAllPtrs(
       [](auto* ptr) { ptr->OnInteractionStarted(); });
 }
 
 void AssistantManagerServiceImpl::OnConversationTurnFinishedOnMainThread(
-    assistant_client::ConversationStateListener::Resolution resolution) {
+    Resolution resolution) {
   switch (resolution) {
     // Interaction ended normally.
     // Note that TIMEOUT here does not refer to server timeout, but rather mic
     // timeout due to speech inactivity. As this case does not require special
     // UI logic, it is treated here as a normal interaction completion.
-    case assistant_client::ConversationStateListener::Resolution::NORMAL:
-    case assistant_client::ConversationStateListener::Resolution::
-        NORMAL_WITH_FOLLOW_ON:
-    case assistant_client::ConversationStateListener::Resolution::TIMEOUT:
+    case Resolution::NORMAL:
+    case Resolution::NORMAL_WITH_FOLLOW_ON:
+    case Resolution::TIMEOUT:
       interaction_subscribers_.ForAllPtrs([](auto* ptr) {
         ptr->OnInteractionFinished(
             mojom::AssistantInteractionResolution::kNormal);
       });
       break;
     // Interaction ended due to interruption.
-    case assistant_client::ConversationStateListener::Resolution::BARGE_IN:
-    case assistant_client::ConversationStateListener::Resolution::CANCELLED:
+    case Resolution::BARGE_IN:
+    case Resolution::CANCELLED:
       interaction_subscribers_.ForAllPtrs([](auto* ptr) {
         ptr->OnInteractionFinished(
             mojom::AssistantInteractionResolution::kInterruption);
       });
       break;
     // Interaction ended due to multi-device hotword loss.
-    case assistant_client::ConversationStateListener::Resolution::NO_RESPONSE:
+    case Resolution::NO_RESPONSE:
       interaction_subscribers_.ForAllPtrs([](auto* ptr) {
         ptr->OnInteractionFinished(
             mojom::AssistantInteractionResolution::kMultiDeviceHotwordLoss);
       });
       break;
     // Interaction ended due to error.
-    case assistant_client::ConversationStateListener::Resolution::
-        COMMUNICATION_ERROR:
+    case Resolution::COMMUNICATION_ERROR:
       interaction_subscribers_.ForAllPtrs([](auto* ptr) {
         ptr->OnInteractionFinished(
             mojom::AssistantInteractionResolution::kError);
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index 4afe0649..344c4ba 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -108,7 +108,7 @@
   void OnSpeechLevelUpdated(float speech_level) override;
 
   // assistant_client::ConversationStateListener overrides:
-  void OnConversationTurnStarted() override;
+  void OnConversationTurnStarted(bool is_mic_open) override;
   void OnConversationTurnFinished(
       assistant_client::ConversationStateListener::Resolution resolution)
       override;
@@ -158,7 +158,7 @@
       base::RepeatingCallback<void(const std::string&)> callback,
       const std::string& result);
 
-  void OnConversationTurnStartedOnMainThread();
+  void OnConversationTurnStartedOnMainThread(bool is_mic_open);
   void OnConversationTurnFinishedOnMainThread(
       assistant_client::ConversationStateListener::Resolution resolution);
   void OnShowHtmlOnMainThread(const std::string& html);
diff --git a/chromeos/services/assistant/platform/audio_input_provider_impl.cc b/chromeos/services/assistant/platform/audio_input_provider_impl.cc
index 6c80769..76af680e 100644
--- a/chromeos/services/assistant/platform/audio_input_provider_impl.cc
+++ b/chromeos/services/assistant/platform/audio_input_provider_impl.cc
@@ -50,10 +50,12 @@
 }
 
 AudioInputImpl::AudioInputImpl(
-    std::unique_ptr<service_manager::Connector> connector)
+    std::unique_ptr<service_manager::Connector> connector,
+    bool default_on)
     : source_(audio::CreateInputDevice(
           std::move(connector),
           media::AudioDeviceDescription::kDefaultDeviceId)),
+      default_on_(default_on),
       task_runner_(base::ThreadTaskRunnerHandle::Get()),
       weak_factory_(this) {
   DETACH_FROM_SEQUENCE(observer_sequence_checker_);
@@ -112,7 +114,7 @@
     should_start = observers_.size() == 1;
   }
 
-  if (should_start) {
+  if (default_on_ && should_start) {
     // Post to main thread runner to start audio recording. Assistant thread
     // does not have thread context defined in //base and will fail sequence
     // check in AudioCapturerSource::Start().
@@ -138,6 +140,16 @@
   }
 }
 
+void AudioInputImpl::SetMicState(bool mic_open) {
+  DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  if (!default_on_) {
+    if (mic_open)
+      source_->Start();
+    else
+      source_->Stop();
+  }
+}
+
 void AudioInputImpl::StartRecording() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   source_->Start();
@@ -149,8 +161,9 @@
 }
 
 AudioInputProviderImpl::AudioInputProviderImpl(
-    service_manager::Connector* connector)
-    : audio_input_(connector->Clone()) {}
+    service_manager::Connector* connector,
+    bool default_on)
+    : audio_input_(connector->Clone(), default_on) {}
 
 AudioInputProviderImpl::~AudioInputProviderImpl() = default;
 
@@ -163,5 +176,9 @@
   return 0;
 }
 
+void AudioInputProviderImpl::SetMicState(bool mic_open) {
+  audio_input_.SetMicState(mic_open);
+}
+
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_input_provider_impl.h b/chromeos/services/assistant/platform/audio_input_provider_impl.h
index fdbf6d1d..e735e94 100644
--- a/chromeos/services/assistant/platform/audio_input_provider_impl.h
+++ b/chromeos/services/assistant/platform/audio_input_provider_impl.h
@@ -49,8 +49,8 @@
 class AudioInputImpl : public assistant_client::AudioInput,
                        public media::AudioCapturerSource::CaptureCallback {
  public:
-  explicit AudioInputImpl(
-      std::unique_ptr<service_manager::Connector> connector);
+  AudioInputImpl(std::unique_ptr<service_manager::Connector> connector,
+                 bool default_on);
   ~AudioInputImpl() override;
 
   // media::AudioCapturerSource::CaptureCallback overrides:
@@ -69,12 +69,18 @@
   void RemoveObserver(
       assistant_client::AudioInput::Observer* observer) override;
 
+  // Called when the mic state associated with the interaction is changed.
+  void SetMicState(bool mic_open);
+
  private:
   void StartRecording();
   void StopRecording();
 
   scoped_refptr<media::AudioCapturerSource> source_;
 
+  // Should audio input always recording actively.
+  const bool default_on_;
+
   // Guards observers_;
   base::Lock lock_;
   std::vector<assistant_client::AudioInput::Observer*> observers_;
@@ -92,13 +98,17 @@
 
 class AudioInputProviderImpl : public assistant_client::AudioInputProvider {
  public:
-  explicit AudioInputProviderImpl(service_manager::Connector* connector);
+  explicit AudioInputProviderImpl(service_manager::Connector* connector,
+                                  bool default_on);
   ~AudioInputProviderImpl() override;
 
   // assistant_client::AudioInputProvider overrides:
   assistant_client::AudioInput& GetAudioInput() override;
   int64_t GetCurrentAudioTime() override;
 
+  // Called when the mic state associated with the interaction is changed.
+  void SetMicState(bool mic_open);
+
  private:
   AudioInputImpl audio_input_;
 
diff --git a/chromeos/services/assistant/platform/system_provider_impl.cc b/chromeos/services/assistant/platform/system_provider_impl.cc
index 9b1d445..6bb123a 100644
--- a/chromeos/services/assistant/platform/system_provider_impl.cc
+++ b/chromeos/services/assistant/platform/system_provider_impl.cc
@@ -16,12 +16,8 @@
 namespace assistant {
 
 SystemProviderImpl::SystemProviderImpl(
-    device::mojom::BatteryMonitorPtr battery_monitor,
-    bool muted)
-    : battery_monitor_(std::move(battery_monitor)),
-      mic_mute_state_(
-          muted ? assistant_client::MicMuteState::MICROPHONE_OFF
-                : assistant_client::MicMuteState::MICROPHONE_ENABLED) {
+    device::mojom::BatteryMonitorPtr battery_monitor)
+    : battery_monitor_(std::move(battery_monitor)) {
   battery_monitor_->QueryNextStatus(base::BindOnce(
       &SystemProviderImpl::OnBatteryStatus, base::Unretained(this)));
 }
@@ -29,7 +25,8 @@
 SystemProviderImpl::~SystemProviderImpl() = default;
 
 assistant_client::MicMuteState SystemProviderImpl::GetMicMuteState() {
-  return mic_mute_state_;
+  // CRAS input is never muted.
+  return assistant_client::MicMuteState::MICROPHONE_ENABLED;
 }
 
 void SystemProviderImpl::RegisterMicMuteChangeCallback(
diff --git a/chromeos/services/assistant/platform/system_provider_impl.h b/chromeos/services/assistant/platform/system_provider_impl.h
index 4d4843c..c3b035fd 100644
--- a/chromeos/services/assistant/platform/system_provider_impl.h
+++ b/chromeos/services/assistant/platform/system_provider_impl.h
@@ -16,8 +16,7 @@
 
 class SystemProviderImpl : public assistant_client::SystemProvider {
  public:
-  explicit SystemProviderImpl(device::mojom::BatteryMonitorPtr battery_monitor,
-                              bool muted);
+  explicit SystemProviderImpl(device::mojom::BatteryMonitorPtr battery_monitor);
   ~SystemProviderImpl() override;
 
   // assistant_client::SystemProvider implementation:
@@ -36,7 +35,6 @@
 
   device::mojom::BatteryMonitorPtr battery_monitor_;
   device::mojom::BatteryStatusPtr current_battery_status_;
-  const assistant_client::MicMuteState mic_mute_state_;
 
   DISALLOW_COPY_AND_ASSIGN(SystemProviderImpl);
 };
diff --git a/chromeos/services/assistant/platform_api_impl.cc b/chromeos/services/assistant/platform_api_impl.cc
index b07f067b..574f7d4 100644
--- a/chromeos/services/assistant/platform_api_impl.cc
+++ b/chromeos/services/assistant/platform_api_impl.cc
@@ -77,9 +77,9 @@
     service_manager::Connector* connector,
     device::mojom::BatteryMonitorPtr battery_monitor,
     bool enable_hotword)
-    : audio_input_provider_(connector),
+    : audio_input_provider_(connector, enable_hotword),
       audio_output_provider_(CreateLibAssistantConfig(!enable_hotword), this),
-      system_provider_(std::move(battery_monitor), !enable_hotword) {}
+      system_provider_(std::move(battery_monitor)) {}
 
 PlatformApiImpl::~PlatformApiImpl() = default;
 
@@ -111,5 +111,9 @@
   return system_provider_;
 }
 
+void PlatformApiImpl::SetMicState(bool mic_open) {
+  audio_input_provider_.SetMicState(mic_open);
+}
+
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform_api_impl.h b/chromeos/services/assistant/platform_api_impl.h
index d01a6bd8..f2fcf5c3 100644
--- a/chromeos/services/assistant/platform_api_impl.h
+++ b/chromeos/services/assistant/platform_api_impl.h
@@ -48,6 +48,9 @@
   assistant_client::ResourceProvider& GetResourceProvider() override;
   assistant_client::SystemProvider& GetSystemProvider() override;
 
+  // Called when the mic state associated with the interaction is changed.
+  void SetMicState(bool mic_open);
+
  private:
   // ChromeOS does not use auth manager, so we don't yet need to implement a
   // real auth provider.
diff --git a/chromeos/services/multidevice_setup/host_verifier.h b/chromeos/services/multidevice_setup/host_verifier.h
index aef1b949..dfbbc6c 100644
--- a/chromeos/services/multidevice_setup/host_verifier.h
+++ b/chromeos/services/multidevice_setup/host_verifier.h
@@ -13,9 +13,14 @@
 namespace multidevice_setup {
 
 // Verifies that this device can connect to the currently-set MultiDevice host.
-// The verification process consists of creating a Bluetooth connection to the
-// device, performing an authentication handshake, and enabling the per-device
-// features which are supported.
+// In order for a host device to be considered set, its BETTER_TOGETHER_HOST
+// software feature must be enabled, and in order for a host device to be
+// considered verified, at least one of its other host software features must be
+// enabled.
+//
+// HostVerifier waits for that situation to occur and has the ability (via its
+// AttemptVerificationNow() function) to send a tickle message to the phone to
+// ask it to enable its software features.
 class HostVerifier {
  public:
   class Observer {
@@ -26,9 +31,9 @@
 
   virtual ~HostVerifier();
 
-  // Returns whether the host has completed verification; note that if
-  // verification is still in the process of being completed but has not
-  // finished, this function still returns false.
+  // Returns whether verification for the current MultiDevice host device has
+  // completed (see description above). If no MultiDevice host is set at all,
+  // false is returned.
   virtual bool IsHostVerified() = 0;
 
   // Attempts the verification flow; successful completion of the flow is
diff --git a/chromeos/services/multidevice_setup/host_verifier_impl.cc b/chromeos/services/multidevice_setup/host_verifier_impl.cc
index b86c9b9..908fb81 100644
--- a/chromeos/services/multidevice_setup/host_verifier_impl.cc
+++ b/chromeos/services/multidevice_setup/host_verifier_impl.cc
@@ -7,13 +7,48 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
-#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
-#include "chromeos/services/secure_channel/public/cpp/client/secure_channel_client.h"
+#include "chromeos/components/proximity_auth/logging/logging.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 
 namespace chromeos {
 
 namespace multidevice_setup {
 
+namespace {
+
+// Software features which, when enabled, represent a verified host.
+constexpr const cryptauth::SoftwareFeature kPotentialHostFeatures[] = {
+    cryptauth::SoftwareFeature::EASY_UNLOCK_HOST,
+    cryptauth::SoftwareFeature::MAGIC_TETHER_HOST,
+    cryptauth::SoftwareFeature::SMS_CONNECT_HOST};
+
+// Name of the preference containing the time (in milliseconds since Unix
+// epoch) at which a verification attempt should be retried. If the preference
+// value is kTimestampNotSet, no retry is scheduled.
+const char kRetryTimestampPrefName[] =
+    "multidevice_setup.current_retry_timestamp_ms";
+
+// Value set for the kRetryTimestampPrefName preference when no retry attempt is
+// underway (i.e., verification is complete or there is no current host).
+const int64_t kTimestampNotSet = 0;
+
+// Name of the preference containing the time delta (in ms) between the
+// timestamp present in the kRetryTimestampPrefName preference and the attempt
+// before that one. If the value of kRetryTimestampPrefName is kTimestampNotSet,
+// the value at this preference is meaningless.
+const char kLastUsedTimeDeltaMsPrefName[] =
+    "multidevice_setup.last_used_time_delta_ms";
+
+// Delta to set for the first retry.
+constexpr const base::TimeDelta kFirstRetryDelta =
+    base::TimeDelta::FromMinutes(10);
+
+// The multiplier for increasing the backoff timer between retries.
+const double kExponentialBackoffMultiplier = 1.5;
+
+}  // namespace
+
 // static
 HostVerifierImpl::Factory* HostVerifierImpl::Factory::test_factory_ = nullptr;
 
@@ -36,40 +71,171 @@
 std::unique_ptr<HostVerifier> HostVerifierImpl::Factory::BuildInstance(
     HostBackendDelegate* host_backend_delegate,
     device_sync::DeviceSyncClient* device_sync_client,
-    secure_channel::SecureChannelClient* secure_channel_client) {
-  return base::WrapUnique(new HostVerifierImpl(
-      host_backend_delegate, device_sync_client, secure_channel_client));
+    PrefService* pref_service,
+    base::Clock* clock,
+    std::unique_ptr<base::OneShotTimer> timer) {
+  return base::WrapUnique(new HostVerifierImpl(host_backend_delegate,
+                                               device_sync_client, pref_service,
+                                               clock, std::move(timer)));
+}
+
+// static
+void HostVerifierImpl::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterInt64Pref(kRetryTimestampPrefName, kTimestampNotSet);
+  registry->RegisterInt64Pref(kLastUsedTimeDeltaMsPrefName, 0);
 }
 
 HostVerifierImpl::HostVerifierImpl(
     HostBackendDelegate* host_backend_delegate,
     device_sync::DeviceSyncClient* device_sync_client,
-    secure_channel::SecureChannelClient* secure_channel_client)
+    PrefService* pref_service,
+    base::Clock* clock,
+    std::unique_ptr<base::OneShotTimer> timer)
     : host_backend_delegate_(host_backend_delegate),
       device_sync_client_(device_sync_client),
-      secure_channel_client_(secure_channel_client) {
+      pref_service_(pref_service),
+      clock_(clock),
+      timer_(std::move(timer)) {
   host_backend_delegate_->AddObserver(this);
+  device_sync_client_->AddObserver(this);
+
+  UpdateRetryState();
 }
 
 HostVerifierImpl::~HostVerifierImpl() {
   host_backend_delegate_->RemoveObserver(this);
+  device_sync_client_->RemoveObserver(this);
 }
 
 bool HostVerifierImpl::IsHostVerified() {
-  NOTIMPLEMENTED();
+  base::Optional<cryptauth::RemoteDeviceRef> current_host =
+      host_backend_delegate_->GetMultiDeviceHostFromBackend();
+  if (!current_host)
+    return false;
 
-  // Use both |device_sync_client_| and |secure_channel_client_| to prevent
-  // unused field compiler warning.
-  return static_cast<void*>(device_sync_client_) ==
-         static_cast<void*>(secure_channel_client_);
+  // If one or more potential host sofware features is enabled, the host is
+  // considered verified.
+  for (const auto& software_feature : kPotentialHostFeatures) {
+    if (current_host->GetSoftwareFeatureState(software_feature) ==
+        cryptauth::SoftwareFeatureState::kEnabled) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 void HostVerifierImpl::PerformAttemptVerificationNow() {
-  NOTIMPLEMENTED();
+  AttemptHostVerification();
 }
 
 void HostVerifierImpl::OnHostChangedOnBackend() {
-  NOTIMPLEMENTED();
+  UpdateRetryState();
+}
+
+void HostVerifierImpl::OnNewDevicesSynced() {
+  UpdateRetryState();
+}
+
+void HostVerifierImpl::UpdateRetryState() {
+  // If there is no host, verification is not applicable.
+  if (!host_backend_delegate_->GetMultiDeviceHostFromBackend()) {
+    StopTimerAndClearPrefs();
+    return;
+  }
+
+  // If there is a host and it is verified, verification is no longer necessary.
+  if (IsHostVerified()) {
+    bool was_timer_running = timer_->IsRunning();
+    StopTimerAndClearPrefs();
+    if (was_timer_running)
+      NotifyHostVerified();
+    return;
+  }
+
+  // If |timer_| is running, an ongoing retry attempt is in progress.
+  if (timer_->IsRunning())
+    return;
+
+  int64_t timestamp_from_prefs =
+      pref_service_->GetInt64(kRetryTimestampPrefName);
+
+  // If no retry timer was set, set the timer to the initial value and attempt
+  // to verify now.
+  if (timestamp_from_prefs == kTimestampNotSet) {
+    AttemptVerificationWithInitialTimeout();
+    return;
+  }
+
+  base::Time retry_time_from_prefs =
+      base::Time::FromJavaTime(timestamp_from_prefs);
+
+  // If a timeout value was set but has not yet occurred, start the timer.
+  if (clock_->Now() < retry_time_from_prefs) {
+    StartTimer(retry_time_from_prefs);
+    return;
+  }
+
+  AttemptVerificationAfterInitialTimeout(retry_time_from_prefs);
+}
+
+void HostVerifierImpl::StopTimerAndClearPrefs() {
+  timer_->Stop();
+  pref_service_->SetInt64(kRetryTimestampPrefName, kTimestampNotSet);
+  pref_service_->SetInt64(kLastUsedTimeDeltaMsPrefName, 0);
+}
+
+void HostVerifierImpl::AttemptVerificationWithInitialTimeout() {
+  base::Time retry_time = clock_->Now() + kFirstRetryDelta;
+
+  pref_service_->SetInt64(kRetryTimestampPrefName, retry_time.ToJavaTime());
+  pref_service_->SetInt64(kLastUsedTimeDeltaMsPrefName,
+                          kFirstRetryDelta.InMilliseconds());
+
+  StartTimer(retry_time);
+  AttemptHostVerification();
+}
+
+void HostVerifierImpl::AttemptVerificationAfterInitialTimeout(
+    const base::Time& retry_time_from_prefs) {
+  int64_t time_delta_ms = pref_service_->GetInt64(kLastUsedTimeDeltaMsPrefName);
+  DCHECK(time_delta_ms > 0);
+
+  base::Time retry_time = retry_time_from_prefs;
+  while (clock_->Now() >= retry_time) {
+    time_delta_ms *= kExponentialBackoffMultiplier;
+    retry_time += base::TimeDelta::FromMilliseconds(time_delta_ms);
+  }
+
+  pref_service_->SetInt64(kRetryTimestampPrefName, retry_time.ToJavaTime());
+  pref_service_->SetInt64(kLastUsedTimeDeltaMsPrefName, time_delta_ms);
+
+  StartTimer(retry_time);
+  AttemptHostVerification();
+}
+
+void HostVerifierImpl::StartTimer(const base::Time& time_to_fire) {
+  base::Time now = clock_->Now();
+  DCHECK(now < time_to_fire);
+
+  timer_->Start(
+      FROM_HERE, time_to_fire - now /* delay */,
+      base::Bind(&HostVerifierImpl::UpdateRetryState, base::Unretained(this)));
+}
+
+void HostVerifierImpl::AttemptHostVerification() {
+  base::Optional<cryptauth::RemoteDeviceRef> current_host =
+      host_backend_delegate_->GetMultiDeviceHostFromBackend();
+  if (!current_host) {
+    PA_LOG(WARNING) << "HostVerifierImpl::AttemptHostVerification(): Cannot "
+                    << "attempt verification because there is no active host.";
+    return;
+  }
+
+  PA_LOG(INFO) << "HostVerifierImpl::AttemptHostVerification(): Attempting "
+               << "host verification now.";
+  device_sync_client_->FindEligibleDevices(
+      cryptauth::SoftwareFeature::BETTER_TOGETHER_HOST, base::DoNothing());
 }
 
 }  // namespace multidevice_setup
diff --git a/chromeos/services/multidevice_setup/host_verifier_impl.h b/chromeos/services/multidevice_setup/host_verifier_impl.h
index 2c135f3..2fc3e92 100644
--- a/chromeos/services/multidevice_setup/host_verifier_impl.h
+++ b/chromeos/services/multidevice_setup/host_verifier_impl.h
@@ -5,20 +5,20 @@
 #ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_IMPL_H_
 #define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_HOST_VERIFIER_IMPL_H_
 
+#include <memory>
+
 #include "base/macros.h"
+#include "base/time/default_clock.h"
+#include "base/timer/timer.h"
+#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
 #include "chromeos/services/multidevice_setup/host_backend_delegate.h"
 #include "chromeos/services/multidevice_setup/host_verifier.h"
 
+class PrefRegistrySimple;
+class PrefService;
+
 namespace chromeos {
 
-namespace device_sync {
-class DeviceSyncClient;
-}  // namespace device_sync
-
-namespace secure_channel {
-class SecureChannelClient;
-}  // namespace secure_channel
-
 namespace multidevice_setup {
 
 // Concrete HostVerifier implementation, which starts trying to verify a host as
@@ -28,10 +28,9 @@
 // If the MultiDevice host is changed while verification is in progress, the
 // previous verification attempt is canceled and a new attempt begins with the
 // updated device.
-//
-// TODO(khorimoto): Fill out implementation.
 class HostVerifierImpl : public HostVerifier,
-                         public HostBackendDelegate::Observer {
+                         public HostBackendDelegate::Observer,
+                         public device_sync::DeviceSyncClient::Observer {
  public:
   class Factory {
    public:
@@ -41,18 +40,25 @@
     virtual std::unique_ptr<HostVerifier> BuildInstance(
         HostBackendDelegate* host_backend_delegate,
         device_sync::DeviceSyncClient* device_sync_client,
-        secure_channel::SecureChannelClient* secure_channel_client);
+        PrefService* pref_service,
+        base::Clock* clock = base::DefaultClock::GetInstance(),
+        std::unique_ptr<base::OneShotTimer> timer =
+            std::make_unique<base::OneShotTimer>());
 
    private:
     static Factory* test_factory_;
   };
 
+  static void RegisterPrefs(PrefRegistrySimple* registry);
+
   ~HostVerifierImpl() override;
 
  private:
   HostVerifierImpl(HostBackendDelegate* host_backend_delegate,
                    device_sync::DeviceSyncClient* device_sync_client,
-                   secure_channel::SecureChannelClient* secure_channel_client);
+                   PrefService* pref_service,
+                   base::Clock* clock,
+                   std::unique_ptr<base::OneShotTimer> timer);
 
   // HostVerifier:
   bool IsHostVerified() override;
@@ -61,9 +67,22 @@
   // HostBackendDelegate::Observer:
   void OnHostChangedOnBackend() override;
 
+  // device_sync::DeviceSyncClient::Observer:
+  void OnNewDevicesSynced() override;
+
+  void UpdateRetryState();
+  void StopTimerAndClearPrefs();
+  void AttemptVerificationWithInitialTimeout();
+  void AttemptVerificationAfterInitialTimeout(
+      const base::Time& retry_time_from_prefs);
+  void StartTimer(const base::Time& time_to_fire);
+  void AttemptHostVerification();
+
   HostBackendDelegate* host_backend_delegate_;
   device_sync::DeviceSyncClient* device_sync_client_;
-  secure_channel::SecureChannelClient* secure_channel_client_;
+  PrefService* pref_service_;
+  base::Clock* clock_;
+  std::unique_ptr<base::OneShotTimer> timer_;
 
   DISALLOW_COPY_AND_ASSIGN(HostVerifierImpl);
 };
diff --git a/chromeos/services/multidevice_setup/host_verifier_impl_unittest.cc b/chromeos/services/multidevice_setup/host_verifier_impl_unittest.cc
index 5b82a4be..26b5f3d 100644
--- a/chromeos/services/multidevice_setup/host_verifier_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/host_verifier_impl_unittest.cc
@@ -7,54 +7,310 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/test/simple_test_clock.h"
+#include "base/timer/mock_timer.h"
 #include "chromeos/services/device_sync/public/cpp/fake_device_sync_client.h"
 #include "chromeos/services/multidevice_setup/fake_host_backend_delegate.h"
 #include "chromeos/services/multidevice_setup/fake_host_verifier.h"
-#include "chromeos/services/secure_channel/public/cpp/client/fake_secure_channel_client.h"
+#include "components/cryptauth/remote_device_test_util.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
 
 namespace multidevice_setup {
 
+namespace {
+
+const int64_t kTestTimeMs = 1500000000000;
+
+constexpr const cryptauth::SoftwareFeature kPotentialHostSoftwareFeatures[] = {
+    cryptauth::SoftwareFeature::EASY_UNLOCK_HOST,
+    cryptauth::SoftwareFeature::MAGIC_TETHER_HOST,
+    cryptauth::SoftwareFeature::SMS_CONNECT_HOST};
+
+const char kRetryTimestampPrefName[] =
+    "multidevice_setup.current_retry_timestamp_ms";
+const char kLastUsedTimeDeltaMsPrefName[] =
+    "multidevice_setup.last_used_time_delta_ms";
+
+const int64_t kFirstRetryDeltaMs = 10 * 60 * 1000;
+const double kExponentialBackoffMultiplier = 1.5;
+
+enum class HostState { kHostNotSet, kHostSetButNotVerified, kHostVerified };
+
+}  // namespace
+
 class MultiDeviceSetupHostVerifierImplTest : public testing::Test {
  protected:
-  MultiDeviceSetupHostVerifierImplTest() = default;
+  MultiDeviceSetupHostVerifierImplTest()
+      : test_device_(cryptauth::CreateRemoteDeviceRefForTest()) {}
   ~MultiDeviceSetupHostVerifierImplTest() override = default;
 
   // testing::Test:
   void SetUp() override {
     fake_host_backend_delegate_ = std::make_unique<FakeHostBackendDelegate>();
+
     fake_device_sync_client_ =
         std::make_unique<device_sync::FakeDeviceSyncClient>();
-    fake_secure_channel_client_ =
-        std::make_unique<secure_channel::FakeSecureChannelClient>();
+
+    test_pref_service_ =
+        std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+    HostVerifierImpl::RegisterPrefs(test_pref_service_->registry());
+
+    test_clock_ = std::make_unique<base::SimpleTestClock>();
+    test_clock_->SetNow(base::Time::FromJavaTime(kTestTimeMs));
+  }
+
+  void TearDown() override {
+    if (fake_observer_)
+      host_verifier_->RemoveObserver(fake_observer_.get());
+  }
+
+  void CreateVerifier(HostState initial_host_state,
+                      int64_t initial_timer_pref_value = 0,
+                      int64_t initial_time_delta_pref_value = 0) {
+    SetHostState(initial_host_state);
+    test_pref_service_->SetInt64(kRetryTimestampPrefName,
+                                 initial_timer_pref_value);
+    test_pref_service_->SetInt64(kLastUsedTimeDeltaMsPrefName,
+                                 initial_time_delta_pref_value);
+
+    auto mock_timer = std::make_unique<base::MockOneShotTimer>();
+    mock_timer_ = mock_timer.get();
 
     host_verifier_ = HostVerifierImpl::Factory::Get()->BuildInstance(
         fake_host_backend_delegate_.get(), fake_device_sync_client_.get(),
-        fake_secure_channel_client_.get());
+        test_pref_service_.get(), test_clock_.get(), std::move(mock_timer));
 
     fake_observer_ = std::make_unique<FakeHostVerifierObserver>();
     host_verifier_->AddObserver(fake_observer_.get());
   }
 
-  void TearDown() override {
-    host_verifier_->RemoveObserver(fake_observer_.get());
+  void SetHostState(HostState host_state) {
+    for (const auto& feature : kPotentialHostSoftwareFeatures) {
+      GetMutableRemoteDevice(test_device_)->software_features[feature] =
+          host_state == HostState::kHostVerified
+              ? cryptauth::SoftwareFeatureState::kEnabled
+              : cryptauth::SoftwareFeatureState::kSupported;
+    }
+
+    if (host_state == HostState::kHostNotSet)
+      fake_host_backend_delegate_->NotifyHostChangedOnBackend(base::nullopt);
+    else
+      fake_host_backend_delegate_->NotifyHostChangedOnBackend(test_device_);
+
+    fake_device_sync_client_->NotifyNewDevicesSynced();
+  }
+
+  void VerifyState(bool expected_is_verified,
+                   size_t expected_num_verified_events,
+                   int64_t expected_retry_timestamp_value,
+                   int64_t expected_retry_delta_value) {
+    EXPECT_EQ(expected_is_verified, host_verifier_->IsHostVerified());
+    EXPECT_EQ(expected_num_verified_events,
+              fake_observer_->num_host_verifications());
+    EXPECT_EQ(expected_retry_timestamp_value,
+              test_pref_service_->GetInt64(kRetryTimestampPrefName));
+    EXPECT_EQ(expected_retry_delta_value,
+              test_pref_service_->GetInt64(kLastUsedTimeDeltaMsPrefName));
+
+    // If a retry timestamp is set, the timer should be running.
+    EXPECT_EQ(expected_retry_timestamp_value != 0, mock_timer_->IsRunning());
+  }
+
+  void VerifyFindEligibleDevicesCalled() {
+    fake_device_sync_client_->InvokePendingFindEligibleDevicesCallback(
+        base::nullopt /* error_code */,
+        cryptauth::RemoteDeviceRefList() /* eligible_devices */,
+        cryptauth::RemoteDeviceRefList() /* ineligible_devices */);
+  }
+
+  void SimulateTimePassing(const base::TimeDelta& delta,
+                           bool simulate_timeout = false) {
+    test_clock_->Advance(delta);
+
+    if (simulate_timeout)
+      mock_timer_->Fire();
   }
 
  private:
+  cryptauth::RemoteDeviceRef test_device_;
+
   std::unique_ptr<FakeHostVerifierObserver> fake_observer_;
   std::unique_ptr<FakeHostBackendDelegate> fake_host_backend_delegate_;
   std::unique_ptr<device_sync::FakeDeviceSyncClient> fake_device_sync_client_;
-  std::unique_ptr<secure_channel::FakeSecureChannelClient>
-      fake_secure_channel_client_;
+  std::unique_ptr<sync_preferences::TestingPrefServiceSyncable>
+      test_pref_service_;
+  std::unique_ptr<base::SimpleTestClock> test_clock_;
+  base::MockOneShotTimer* mock_timer_ = nullptr;
 
   std::unique_ptr<HostVerifier> host_verifier_;
 
   DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupHostVerifierImplTest);
 };
 
-TEST_F(MultiDeviceSetupHostVerifierImplTest, Test) {}
+TEST_F(MultiDeviceSetupHostVerifierImplTest, StartWithoutHost_SetAndVerify) {
+  CreateVerifier(HostState::kHostNotSet);
+
+  SetHostState(HostState::kHostSetButNotVerified);
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(
+      false /* expected_is_verified */, 0u /* expected_num_verified_events */,
+      kTestTimeMs + kFirstRetryDeltaMs /* expected_retry_timestamp_value */,
+      kFirstRetryDeltaMs /* expected_retry_delta_value */);
+
+  SimulateTimePassing(base::TimeDelta::FromMinutes(1));
+  SetHostState(HostState::kHostVerified);
+  VerifyState(true /* expected_is_verified */,
+              1u /* expected_num_verified_events */,
+              0 /* expected_retry_timestamp_value */,
+              0 /* expected_retry_delta_value */);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierImplTest, StartWithoutHost_Retry) {
+  CreateVerifier(HostState::kHostNotSet);
+
+  SetHostState(HostState::kHostSetButNotVerified);
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(
+      false /* expected_is_verified */, 0u /* expected_num_verified_events */,
+      kTestTimeMs + kFirstRetryDeltaMs /* expected_retry_timestamp_value */,
+      kFirstRetryDeltaMs /* expected_retry_delta_value */);
+
+  // Simulate enough time pasing to time out and retry.
+  SimulateTimePassing(base::TimeDelta::FromMilliseconds(kFirstRetryDeltaMs),
+                      true /* simulate_timeout */);
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(false /* expected_is_verified */,
+              0u /* expected_num_verified_events */,
+              kTestTimeMs + kFirstRetryDeltaMs +
+                  kFirstRetryDeltaMs * kExponentialBackoffMultiplier
+              /* expected_retry_timestamp_value */,
+              kFirstRetryDeltaMs * kExponentialBackoffMultiplier
+              /* expected_retry_delta_value */);
+
+  // Simulate the next retry timeout passing..
+  SimulateTimePassing(base::TimeDelta::FromMilliseconds(
+                          kFirstRetryDeltaMs * kExponentialBackoffMultiplier),
+                      true /* simulate_timeout */);
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(false /* expected_is_verified */,
+              0u /* expected_num_verified_events */,
+              kTestTimeMs + kFirstRetryDeltaMs +
+                  kFirstRetryDeltaMs * kExponentialBackoffMultiplier +
+                  kFirstRetryDeltaMs * kExponentialBackoffMultiplier *
+                      kExponentialBackoffMultiplier
+              /* expected_retry_timestamp_value */,
+              kFirstRetryDeltaMs * kExponentialBackoffMultiplier *
+                  kExponentialBackoffMultiplier
+              /* expected_retry_delta_value */);
+
+  // Succeed.
+  SetHostState(HostState::kHostVerified);
+  VerifyState(true /* expected_is_verified */,
+              1u /* expected_num_verified_events */,
+              0 /* expected_retry_timestamp_value */,
+              0 /* expected_retry_delta_value */);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierImplTest,
+       StartWithUnverifiedHost_NoInitialPrefs) {
+  CreateVerifier(HostState::kHostSetButNotVerified);
+
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(
+      false /* expected_is_verified */, 0u /* expected_num_verified_events */,
+      kTestTimeMs + kFirstRetryDeltaMs /* expected_retry_timestamp_value */,
+      kFirstRetryDeltaMs /* expected_retry_delta_value */);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierImplTest,
+       StartWithUnverifiedHost_InitialPrefs_HasNotPassedRetryTime) {
+  // Simulate starting up the device to find that the retry timer is in 5
+  // minutes.
+  CreateVerifier(HostState::kHostSetButNotVerified,
+                 kTestTimeMs + base::TimeDelta::FromMinutes(5).InMilliseconds()
+                 /* initial_timer_pref_value */,
+                 kFirstRetryDeltaMs /* initial_time_delta_pref_value */);
+
+  SimulateTimePassing(base::TimeDelta::FromMinutes(5),
+                      true /* simulate_timeout */);
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(false /* expected_is_verified */,
+              0u /* expected_num_verified_events */,
+              kTestTimeMs + base::TimeDelta::FromMinutes(5).InMilliseconds() +
+                  kFirstRetryDeltaMs * kExponentialBackoffMultiplier
+              /* expected_retry_timestamp_value */,
+              kFirstRetryDeltaMs * kExponentialBackoffMultiplier
+              /* expected_retry_delta_value */);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierImplTest,
+       StartWithUnverifiedHost_InitialPrefs_AlreadyPassedRetryTime) {
+  // Simulate starting up the device to find that the retry timer had already
+  // fired 5 minutes ago.
+  CreateVerifier(HostState::kHostSetButNotVerified,
+                 kTestTimeMs - base::TimeDelta::FromMinutes(5).InMilliseconds()
+                 /* initial_timer_pref_value */,
+                 kFirstRetryDeltaMs /* initial_time_delta_pref_value */);
+
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(false /* expected_is_verified */,
+              0u /* expected_num_verified_events */,
+              kTestTimeMs - base::TimeDelta::FromMinutes(5).InMilliseconds() +
+                  kFirstRetryDeltaMs * kExponentialBackoffMultiplier
+              /* expected_retry_timestamp_value */,
+              kFirstRetryDeltaMs * kExponentialBackoffMultiplier
+              /* expected_retry_delta_value */);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierImplTest,
+       StartWithUnverifiedHost_InitialPrefs_AlreadyPassedMultipleRetryTimes) {
+  // Simulate starting up the device to find that the retry timer had already
+  // fired 20 minutes ago.
+  CreateVerifier(HostState::kHostSetButNotVerified,
+                 kTestTimeMs - base::TimeDelta::FromMinutes(20).InMilliseconds()
+                 /* initial_timer_pref_value */,
+                 kFirstRetryDeltaMs /* initial_time_delta_pref_value */);
+
+  // Because the first delta is 10 minutes, the second delta is 10 * 1.5 = 15
+  // minutes. In this case, that means that *two* previous timeouts were missed,
+  // so the third one should be scheduled.
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(false /* expected_is_verified */,
+              0u /* expected_num_verified_events */,
+              kTestTimeMs - base::TimeDelta::FromMinutes(20).InMilliseconds() +
+                  kFirstRetryDeltaMs * kExponentialBackoffMultiplier +
+                  kFirstRetryDeltaMs * kExponentialBackoffMultiplier *
+                      kExponentialBackoffMultiplier
+              /* expected_retry_timestamp_value */,
+              kFirstRetryDeltaMs * kExponentialBackoffMultiplier *
+                  kExponentialBackoffMultiplier
+              /* expected_retry_delta_value */);
+}
+
+TEST_F(MultiDeviceSetupHostVerifierImplTest,
+       StartWithVerifiedHost_HostChanges) {
+  CreateVerifier(HostState::kHostVerified);
+  VerifyState(true /* expected_is_verified */,
+              0u /* expected_num_verified_events */,
+              0 /* expected_retry_timestamp_value */,
+              0 /* expected_retry_delta_value */);
+
+  SetHostState(HostState::kHostNotSet);
+  VerifyState(false /* expected_is_verified */,
+              0u /* expected_num_verified_events */,
+              0 /* expected_retry_timestamp_value */,
+              0 /* expected_retry_delta_value */);
+
+  SetHostState(HostState::kHostSetButNotVerified);
+  VerifyFindEligibleDevicesCalled();
+  VerifyState(
+      false /* expected_is_verified */, 0u /* expected_num_verified_events */,
+      kTestTimeMs + kFirstRetryDeltaMs /* expected_retry_timestamp_value */,
+      kFirstRetryDeltaMs /* expected_retry_delta_value */);
+}
 
 }  // namespace multidevice_setup
 
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
index cf85e76..661497d4 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
@@ -64,7 +64,7 @@
       host_verifier_(HostVerifierImpl::Factory::Get()->BuildInstance(
           host_backend_delegate_.get(),
           device_sync_client,
-          secure_channel_client)),
+          pref_service)),
       host_status_provider_(
           HostStatusProviderImpl::Factory::Get()->BuildInstance(
               eligible_host_devices_provider_.get(),
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
index e2b4d3c..7cb0576e 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl_unittest.cc
@@ -136,10 +136,11 @@
   FakeHostVerifierFactory(
       FakeHostBackendDelegateFactory* fake_host_backend_delegate_factory,
       device_sync::FakeDeviceSyncClient* expected_device_sync_client,
-      secure_channel::FakeSecureChannelClient* expected_secure_channel_client)
+      sync_preferences::TestingPrefServiceSyncable*
+          expected_testing_pref_service)
       : fake_host_backend_delegate_factory_(fake_host_backend_delegate_factory),
         expected_device_sync_client_(expected_device_sync_client),
-        expected_secure_channel_client_(expected_secure_channel_client) {}
+        expected_testing_pref_service_(expected_testing_pref_service) {}
 
   ~FakeHostVerifierFactory() override = default;
 
@@ -150,12 +151,14 @@
   std::unique_ptr<HostVerifier> BuildInstance(
       HostBackendDelegate* host_backend_delegate,
       device_sync::DeviceSyncClient* device_sync_client,
-      secure_channel::SecureChannelClient* secure_channel_client) override {
+      PrefService* pref_service,
+      base::Clock* clock,
+      std::unique_ptr<base::OneShotTimer> timer) override {
     EXPECT_FALSE(instance_);
     EXPECT_EQ(fake_host_backend_delegate_factory_->instance(),
               host_backend_delegate);
     EXPECT_EQ(expected_device_sync_client_, device_sync_client);
-    EXPECT_EQ(expected_secure_channel_client_, secure_channel_client);
+    EXPECT_EQ(expected_testing_pref_service_, pref_service);
 
     auto instance = std::make_unique<FakeHostVerifier>();
     instance_ = instance.get();
@@ -164,7 +167,7 @@
 
   FakeHostBackendDelegateFactory* fake_host_backend_delegate_factory_;
   device_sync::FakeDeviceSyncClient* expected_device_sync_client_;
-  secure_channel::FakeSecureChannelClient* expected_secure_channel_client_;
+  sync_preferences::TestingPrefServiceSyncable* expected_testing_pref_service_;
 
   FakeHostVerifier* instance_ = nullptr;
 
@@ -330,7 +333,7 @@
 
     fake_host_verifier_factory_ = std::make_unique<FakeHostVerifierFactory>(
         fake_host_backend_delegate_factory_.get(),
-        fake_device_sync_client_.get(), fake_secure_channel_client_.get());
+        fake_device_sync_client_.get(), test_pref_service_.get());
     HostVerifierImpl::Factory::SetFactoryForTesting(
         fake_host_verifier_factory_.get());
 
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.cc b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
index 829b4ea..9e3816c 100644
--- a/chromeos/services/multidevice_setup/multidevice_setup_service.cc
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
@@ -7,6 +7,7 @@
 #include "chromeos/components/proximity_auth/logging/logging.h"
 #include "chromeos/services/multidevice_setup/account_status_change_delegate_notifier_impl.h"
 #include "chromeos/services/multidevice_setup/host_backend_delegate_impl.h"
+#include "chromeos/services/multidevice_setup/host_verifier_impl.h"
 #include "chromeos/services/multidevice_setup/multidevice_setup_base.h"
 #include "chromeos/services/multidevice_setup/multidevice_setup_initializer.h"
 
@@ -19,6 +20,7 @@
     PrefRegistrySimple* registry) {
   AccountStatusChangeDelegateNotifierImpl::RegisterPrefs(registry);
   HostBackendDelegateImpl::RegisterPrefs(registry);
+  HostVerifierImpl::RegisterPrefs(registry);
 }
 
 MultiDeviceSetupService::MultiDeviceSetupService(
diff --git a/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc b/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc
index 17184ed..94b24fe 100644
--- a/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc
+++ b/components/data_reduction_proxy/content/browser/content_resource_type_provider.cc
@@ -68,4 +68,13 @@
   resource_content_types_.Put(request.url().spec(), content_type);
 }
 
+bool ContentResourceTypeProvider::IsNonContentInitiatedRequest(
+    const net::URLRequest& request) const {
+  const auto* resource_request_info =
+      content::ResourceRequestInfo::ForRequest(&request);
+  return !resource_request_info ||
+         (resource_request_info->GetGlobalRequestID() ==
+          content::GlobalRequestID());
+}
+
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/content/browser/content_resource_type_provider.h b/components/data_reduction_proxy/content/browser/content_resource_type_provider.h
index d4ed06ef20..07f1c0b 100644
--- a/components/data_reduction_proxy/content/browser/content_resource_type_provider.h
+++ b/components/data_reduction_proxy/content/browser/content_resource_type_provider.h
@@ -30,6 +30,8 @@
   void SetContentType(const net::URLRequest& request) override;
   ResourceTypeProvider::ContentType GetContentType(
       const GURL& url) const override;
+  bool IsNonContentInitiatedRequest(
+      const net::URLRequest& request) const override;
 
   // Map that evicts entries lazily based on the recency of being added.
   base::MRUCache<std::string, ResourceTypeProvider::ContentType>
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc
index f61dd41..43fabeb 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_data_use_observer.cc
@@ -44,10 +44,6 @@
   int64_t original_bytes_;
 };
 
-// Hostname used for the other bucket which consists of chrome-services traffic.
-// This should be in sync with the same in DataReductionSiteBreakdownView.java
-const char kOtherHostName[] = "Other";
-
 // static
 const void* const DataUseUserDataBytes::kUserDataKey =
     &DataUseUserDataBytes::kUserDataKey;
@@ -133,12 +129,14 @@
                                 network_bytes, original_bytes));
     }
   } else {
+    // Report the datause that cannot be scoped to a page load to the other
+    // host. These include chrome services, service-worker, Downloads, etc.
     data_reduction_proxy_io_data_->UpdateDataUseForHost(
         network_bytes, original_bytes,
         data_use->traffic_type() ==
                 data_use_measurement::DataUse::TrafficType::USER_TRAFFIC
             ? data_use->url().HostNoBrackets()
-            : kOtherHostName);
+            : util::GetSiteBreakdownOtherHostName());
   }
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
index 107d6a0..75ba84f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.cc
@@ -32,6 +32,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/previews/core/previews_decider.h"
 #include "net/base/load_flags.h"
+#include "net/http/http_request_headers.h"
 #include "net/url_request/http_user_agent_settings.h"
 #include "net/url_request/static_http_user_agent_settings.h"
 #include "net/url_request/url_request_context.h"
@@ -145,6 +146,13 @@
   request_options_.reset(
       new DataReductionProxyRequestOptions(client_, config_.get()));
   request_options_->Init();
+  // It is safe to use base::Unretained here, since it gets executed
+  // synchronously on the IO thread, and |this| outlives the caller (since the
+  // caller is owned by |this|.
+  request_options_->SetUpdateHeaderCallback(
+      base::BindRepeating(&DataReductionProxyIOData::UpdateProxyRequestHeaders,
+                          base::Unretained(this)));
+
   if (use_config_client) {
     // It is safe to use base::Unretained here, since it gets executed
     // synchronously on the IO thread, and |this| outlives the caller (since the
@@ -444,4 +452,12 @@
   previews_decider_ = previews_decider;
 }
 
+void DataReductionProxyIOData::UpdateProxyRequestHeaders(
+    net::HttpRequestHeaders headers) {
+  ui_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DataReductionProxyService::SetProxyRequestHeaders,
+                     service_, std::move(headers)));
+}
+
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
index 7d562ec..e5d8ba2 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h
@@ -150,6 +150,9 @@
   // cleared.
   void OnCacheCleared(const base::Time start, const base::Time end);
 
+  // Forwards proxy authentication headers to the UI thread.
+  void UpdateProxyRequestHeaders(net::HttpRequestHeaders headers);
+
   // Various accessor methods.
   DataReductionProxyConfigurator* configurator() const {
     return configurator_.get();
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
index ef7d102..2be73a8 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate.cc
@@ -570,6 +570,17 @@
       data_use_measurement::DataUseMeasurement::GetContentTypeForRequest(
           request),
       request.traffic_annotation().unique_id_hash_code);
+
+  if (params::IsDataSaverSiteBreakdownUsingPLMEnabled() &&
+      data_reduction_proxy_io_data_ &&
+      data_reduction_proxy_io_data_->resource_type_provider() &&
+      data_reduction_proxy_io_data_->resource_type_provider()
+          ->IsNonContentInitiatedRequest(request)) {
+    // Record non-content initiated traffic to the Other bucket for data saver
+    // site-breakdown.
+    data_reduction_proxy_io_data_->UpdateDataUseForHost(
+        data_used, original_size, util::GetSiteBreakdownOtherHostName());
+  }
 }
 
 void DataReductionProxyNetworkDelegate::AccumulateDataUsage(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
index 2c817e1a..05ad471 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_network_delegate_unittest.cc
@@ -36,6 +36,7 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
+#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
@@ -45,6 +46,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/data_reduction_proxy/core/common/lofi_decider.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
+#include "components/data_reduction_proxy/proto/data_store.pb.h"
 #include "components/previews/core/previews_experiments.h"
 #include "components/previews/core/previews_features.h"
 #include "components/previews/core/test_previews_decider.h"
@@ -287,6 +289,28 @@
   bool on_lofi_response_;
 };
 
+class TestResourceTypeProvider : public ResourceTypeProvider {
+ public:
+  void SetContentType(const net::URLRequest& request) override {}
+
+  ContentType GetContentType(const GURL& url) const override {
+    return ResourceTypeProvider::CONTENT_TYPE_UNKNOWN;
+  }
+
+  bool IsNonContentInitiatedRequest(
+      const net::URLRequest& request) const override {
+    return is_non_content_initiated_request_;
+  }
+
+  void set_is_non_content_initiated_request(
+      bool is_non_content_initiated_request) {
+    is_non_content_initiated_request_ = is_non_content_initiated_request;
+  }
+
+ private:
+  bool is_non_content_initiated_request_ = false;
+};
+
 enum ProxyTestConfig { USE_SECURE_PROXY, USE_INSECURE_PROXY, BYPASS_PROXY };
 
 class DataReductionProxyNetworkDelegateTest : public testing::Test {
@@ -782,6 +806,23 @@
         request, data_reduction_proxy_info, proxy_retry_info, headers);
   }
 
+  void EnableDataUsageReporting() {
+    test_context_->pref_service()->SetBoolean(prefs::kDataUsageReportingEnabled,
+                                              true);
+    // Give the setting notification a chance to propagate.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  size_t GetOtherHostDataUsage() {
+    const auto& data_usage_map = test_context_->data_reduction_proxy_service()
+                                     ->compression_stats()
+                                     ->DataUsageMapForTesting();
+    const auto& it = data_usage_map.find(util::GetSiteBreakdownOtherHostName());
+    if (it != data_usage_map.end())
+      return it->second->data_used();
+    return 0;
+  }
+
   net::MockClientSocketFactory* mock_socket_factory() {
     return mock_socket_factory_.get();
   }
@@ -2244,6 +2285,32 @@
       9 /* UNKNOWN_TRANSFORM_RECEIVED */, 1);
 }
 
+TEST_F(DataReductionProxyNetworkDelegateTest, RecordNonContentToOtherHost) {
+  const std::string response_headers =
+      "HTTP/1.1 200 OK\r\n"
+      "Date: Wed, 28 Nov 2007 09:40:09 GMT\r\n"
+      "Expires: Mon, 24 Nov 2014 12:45:26 GMT\r\n"
+      "Via: 1.1 Chrome-Compression-Proxy\r\n"
+      "\r\n";
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      data_reduction_proxy::features::
+          kDataSaverSiteBreakdownUsingPageLoadMetrics);
+  Init(USE_INSECURE_PROXY, false);
+  EnableDataUsageReporting();
+  auto test_resource_type_provider =
+      std::make_unique<TestResourceTypeProvider>();
+  test_resource_type_provider->set_is_non_content_initiated_request(true);
+  io_data()->set_resource_type_provider(std::move(test_resource_type_provider));
+
+  FetchURLRequest(GURL(kTestURL), nullptr, response_headers,
+                  kResponseContentLength, 0);
+  base::RunLoop().RunUntilIdle();
+  DCHECK_EQ(response_headers.size() + kResponseContentLength,
+            GetOtherHostDataUsage());
+}
+
 }  // namespace
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index fc20bdc..ea80668 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -297,6 +297,12 @@
     headers.push_back(FormatOption(kExperimentsOption, experiment));
 
   header_value_ = base::JoinString(headers, ", ");
+
+  if (update_header_callback_) {
+    net::HttpRequestHeaders headers;
+    headers.SetHeader(chrome_proxy_header(), header_value_);
+    update_header_callback_.Run(std::move(headers));
+  }
 }
 
 std::string DataReductionProxyRequestOptions::GetSessionKeyFromRequestHeaders(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index e0b3561..5f54832 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/optional.h"
@@ -19,6 +20,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
+#include "net/http/http_request_headers.h"
 
 namespace net {
 class HttpRequestHeaders;
@@ -40,6 +42,9 @@
 
 class DataReductionProxyConfig;
 
+typedef base::RepeatingCallback<void(net::HttpRequestHeaders)>
+    UpdateHeaderCallback;
+
 class DataReductionProxyRequestOptions {
  public:
   static bool IsKeySetOnCommandLine();
@@ -74,6 +79,11 @@
   // Sets the credentials for sending to the Data Reduction Proxy.
   void SetSecureSession(const std::string& secure_session);
 
+  // Set the callback to call when the proxy request headers are updated.
+  void SetUpdateHeaderCallback(UpdateHeaderCallback callback) {
+    update_header_callback_ = callback;
+  }
+
   // Retrieves the credentials for sending to the Data Reduction Proxy.
   const std::string& GetSecureSession() const;
 
@@ -166,6 +176,10 @@
   // The page identifier that was last generated for data saver proxy server.
   uint64_t current_page_id_;
 
+  // Callback to expose the chrome_proxy header to the UI thread. Called
+  // whenever the chrome_proxy header value changes. Can be null.
+  UpdateHeaderCallback update_header_callback_;
+
   // Enforce usage on the IO thread.
   base::ThreadChecker thread_checker_;
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
index dcf0efb..9b786ddc 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
@@ -9,11 +9,13 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/command_line.h"
 #include "base/md5.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/field_trial.h"
 #include "base/strings/string16.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -153,6 +155,17 @@
     request_options_->Init();
   }
 
+  void CreateRequestOptionsWithCallback(const std::string& version) {
+    CreateRequestOptions(version);
+    request_options_->SetUpdateHeaderCallback(base::BindRepeating(
+        &DataReductionProxyRequestOptionsTest::UpdateHeaderCallback,
+        base::Unretained(this)));
+  }
+
+  void UpdateHeaderCallback(net::HttpRequestHeaders headers) {
+    callback_headers_ = headers;
+  }
+
   TestDataReductionProxyParams* params() {
     return test_context_->config()->test_params();
   }
@@ -161,6 +174,8 @@
     return request_options_.get();
   }
 
+  net::HttpRequestHeaders callback_headers() { return callback_headers_; }
+
   void VerifyExpectedHeader(const std::string& expected_header,
                             uint64_t page_id) {
     test_context_->RunUntilIdle();
@@ -179,6 +194,7 @@
   base::MessageLoopForIO message_loop_;
   std::unique_ptr<TestDataReductionProxyRequestOptions> request_options_;
   std::unique_ptr<DataReductionProxyTestContext> test_context_;
+  net::HttpRequestHeaders callback_headers_;
 };
 
 TEST_F(DataReductionProxyRequestOptionsTest, AuthHashForSalt) {
@@ -245,6 +261,25 @@
   VerifyExpectedHeader(expected_header, kPageIdValue);
 }
 
+TEST_F(DataReductionProxyRequestOptionsTest, CallsHeaderCallback) {
+  std::string expected_header;
+  SetHeaderExpectations(std::string(), std::string(), kSecureSession,
+                        kClientStr, kExpectedBuild, kExpectedPatch, kPageId,
+                        std::vector<std::string>(), &expected_header);
+
+  CreateRequestOptionsWithCallback(kVersion);
+  request_options()->SetSecureSession(kSecureSession);
+  VerifyExpectedHeader(expected_header, kPageIdValue);
+
+  std::string callback_header;
+  callback_headers().GetHeader(kChromeProxyHeader, &callback_header);
+  // |callback_header| does not include a page id. Since the page id is always
+  // the last element in the header, check that |callback_header| is the prefix
+  // of |expected_header|.
+  EXPECT_TRUE(base::StartsWith(expected_header, callback_header,
+                               base::CompareCase::SENSITIVE));
+}
+
 TEST_F(DataReductionProxyRequestOptionsTest, ParseExperiments) {
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       data_reduction_proxy::switches::kDataReductionProxyExperiment,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
index d7e6810..ae70408 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -21,6 +21,7 @@
 #include "components/data_reduction_proxy/core/browser/db_data_owner.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_storage_delegate.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
+#include "net/http/http_request_headers.h"
 
 class PrefService;
 
@@ -137,6 +138,17 @@
   // cleared.
   void OnCacheCleared(const base::Time start, const base::Time end);
 
+  // Sets |proxy_request_headers_| with a forwarded value from the IO thread.
+  void SetProxyRequestHeaders(net::HttpRequestHeaders headers) {
+    proxy_request_headers_ = headers;
+  }
+
+  // Returns |proxy_request_headers_|. Note: The chrome-proxy header does not
+  // include the page id.
+  const net::HttpRequestHeaders GetProxyRequestHeaders() const {
+    return proxy_request_headers_;
+  }
+
   // Accessor methods.
   DataReductionProxyCompressionStats* compression_stats() const {
     return compression_stats_.get();
@@ -191,6 +203,10 @@
 
   base::ObserverList<DataReductionProxyServiceObserver> observer_list_;
 
+  // Authentication headers for the Data Reduction Proxy, if any. This is
+  // forwarded from the IO thread in PostTask.
+  net::HttpRequestHeaders proxy_request_headers_;
+
   bool initialized_;
 
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
index b67be94..2deca8c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.cc
@@ -35,6 +35,10 @@
 const char kApiKeyName[] = "key";
 #endif
 
+// Hostname used for the other bucket which consists of chrome-services traffic.
+// This should be in sync with the same in DataReductionSiteBreakdownView.java
+const char kOtherHostName[] = "Other";
+
 // Scales |byte_count| by the ratio of |numerator|:|denomenator|.
 int64_t ScaleByteCountByRatio(int64_t byte_count,
                               int64_t numerator,
@@ -284,6 +288,10 @@
   }
 }
 
+const char* GetSiteBreakdownOtherHostName() {
+  return kOtherHostName;
+}
+
 }  // namespace util
 
 namespace protobuf_parser {
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
index 5b3d97b..e8b534e8c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h
@@ -122,6 +122,10 @@
 // Converts net::ProxyServer::Scheme to type ProxyScheme.
 ProxyScheme ConvertNetProxySchemeToProxyScheme(net::ProxyServer::Scheme scheme);
 
+// Returns the hostname used for the other bucket to record datause not scoped
+// to a page load such as chrome-services traffic, service worker, Downloads.
+const char* GetSiteBreakdownOtherHostName();
+
 }  // namespace util
 
 namespace protobuf_parser {
diff --git a/components/data_reduction_proxy/core/common/resource_type_provider.h b/components/data_reduction_proxy/core/common/resource_type_provider.h
index 60ec2faa..a507b7c 100644
--- a/components/data_reduction_proxy/core/common/resource_type_provider.h
+++ b/components/data_reduction_proxy/core/common/resource_type_provider.h
@@ -32,6 +32,12 @@
 
   virtual ContentType GetContentType(const GURL& url) const = 0;
 
+  // Returns if the request is initiated via non-content that cannot be scoped
+  // to a page load. These ResourceInfo-less requests can be issued by chrome
+  // services, service-worker, Downloads, etc.
+  virtual bool IsNonContentInitiatedRequest(
+      const net::URLRequest& request) const = 0;
+
  protected:
   ResourceTypeProvider() {}
 
diff --git a/components/offline_pages/core/model/offline_page_model_taskified.cc b/components/offline_pages/core/model/offline_page_model_taskified.cc
index 6f64980..6b3ffe5e 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified.cc
+++ b/components/offline_pages/core/model/offline_page_model_taskified.cc
@@ -561,6 +561,11 @@
     const OfflinePageItem& offline_page,
     PublishArchiveResult publish_results) {
   if (publish_results.move_result != SavePageResult::SUCCESS) {
+    // Add UMA for the failure reason.
+    UMA_HISTOGRAM_ENUMERATION("OfflinePages.PublishPageResult",
+                              publish_results.move_result,
+                              SavePageResult::RESULT_COUNT);
+
     std::move(save_page_callback).Run(publish_results.move_result, 0LL);
     return;
   }
diff --git a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
index 37a26e20..239a09c 100644
--- a/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
+++ b/components/offline_pages/core/model/offline_page_model_taskified_unittest.cc
@@ -119,6 +119,13 @@
       const std::string& request_origin,
       std::unique_ptr<OfflinePageArchiver> archiver,
       SavePageResult expected_result);
+  // Start a save page simulating a file move failure.
+  int64_t SavePageWithFileMoveFailure(
+      const GURL& url,
+      const ClientId& client_id,
+      const GURL& original_url,
+      const std::string& request_origin,
+      std::unique_ptr<OfflinePageArchiver> archiver);
   // Insert an offline page in to store, it does not rely on the model
   // implementation.
   void InsertPageIntoStore(const OfflinePageItem& offline_page);
@@ -312,6 +319,20 @@
   return offline_id;
 }
 
+int64_t OfflinePageModelTaskifiedTest::SavePageWithFileMoveFailure(
+    const GURL& url,
+    const ClientId& client_id,
+    const GURL& original_url,
+    const std::string& request_origin,
+    std::unique_ptr<OfflinePageArchiver> archiver) {
+  int64_t offline_id = OfflinePageModel::kInvalidOfflineId;
+  base::MockCallback<SavePageCallback> callback;
+
+  SavePageWithCallback(url, client_id, original_url, request_origin,
+                       std::move(archiver), callback.Get());
+  return offline_id;
+}
+
 void OfflinePageModelTaskifiedTest::InsertPageIntoStore(
     const OfflinePageItem& offline_page) {
   store_test_util()->InsertItem(offline_page);
@@ -1261,6 +1282,27 @@
 // This test is affected by https://crbug.com/725685, which only affects windows
 // platform.
 #if defined(OS_WIN)
+#define MAYBE_PublishPageFailure DISABLED_PublishPageFailure
+#else
+#define MAYBE_PublishPageFailure PublishPageFailure
+#endif
+TEST_F(OfflinePageModelTaskifiedTest, MAYBE_PublishPageFailure) {
+  // Save a persistent page that will report failure to be copied to a public
+  // dir.
+  auto archiver = BuildArchiver(kTestUrl, ArchiverResult::SUCCESSFULLY_CREATED);
+  archiver->set_archive_attempt_failure(true);
+  SavePageWithFileMoveFailure(kTestUrl, kTestUserRequestedClientId, GURL(),
+                              kEmptyRequestOrigin, std::move(archiver));
+
+  // Ensure that a histogram is emitted for the failure
+  histogram_tester()->ExpectUniqueSample(
+      "OfflinePages.PublishPageResult",
+      static_cast<int>(SavePageResult::FILE_MOVE_FAILED), 1);
+}
+
+// This test is affected by https://crbug.com/725685, which only affects windows
+// platform.
+#if defined(OS_WIN)
 #define MAYBE_CheckPublishInternalArchive DISABLED_CheckPublishInternalArchive
 #else
 #define MAYBE_CheckPublishInternalArchive CheckPublishInternalArchive
diff --git a/components/offline_pages/core/offline_page_test_archiver.cc b/components/offline_pages/core/offline_page_test_archiver.cc
index 84f060e..b87e12f5 100644
--- a/components/offline_pages/core/offline_page_test_archiver.cc
+++ b/components/offline_pages/core/offline_page_test_archiver.cc
@@ -26,13 +26,15 @@
       size_to_report_(size_to_report),
       create_archive_called_(false),
       publish_archive_called_(false),
+      archive_attempt_failure_(false),
       delayed_(false),
       result_title_(result_title),
       digest_to_report_(digest_to_report),
       task_runner_(task_runner) {}
 
 OfflinePageTestArchiver::~OfflinePageTestArchiver() {
-  EXPECT_TRUE(create_archive_called_ || publish_archive_called_);
+  EXPECT_TRUE(create_archive_called_ || publish_archive_called_ ||
+              archive_attempt_failure_);
 }
 
 void OfflinePageTestArchiver::CreateArchive(
@@ -60,6 +62,10 @@
   publish_archive_result.new_file_path = offline_page.file_path;
   publish_archive_result.download_id = 0;
 
+  if (archive_attempt_failure_) {
+    publish_archive_result.move_result = SavePageResult::FILE_MOVE_FAILED;
+  }
+
   // Note: once the |publish_done_callback| is invoked it is very likely that
   // this instance will be destroyed. So all parameters sent to it must not be
   // bound to the lifetime on this.
diff --git a/components/offline_pages/core/offline_page_test_archiver.h b/components/offline_pages/core/offline_page_test_archiver.h
index 19ab5b0d..6cae827 100644
--- a/components/offline_pages/core/offline_page_test_archiver.h
+++ b/components/offline_pages/core/offline_page_test_archiver.h
@@ -76,6 +76,10 @@
 
   bool create_archive_called() const { return create_archive_called_; }
 
+  void set_archive_attempt_failure(bool fail) {
+    archive_attempt_failure_ = fail;
+  }
+
  private:
   // Not owned. Outlives OfflinePageTestArchiver.
   Observer* observer_;
@@ -87,6 +91,7 @@
   int64_t size_to_report_;
   bool create_archive_called_;
   bool publish_archive_called_;
+  bool archive_attempt_failure_;
   bool delayed_;
   base::string16 result_title_;
   std::string digest_to_report_;
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index 089776fe..fb36902 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -775,6 +775,10 @@
   return res;
 }
 
+bool AutocompleteMatch::IsExceptedFromLineReversal() const {
+  return !!answer && answer->type() == SuggestionAnswer::ANSWER_TYPE_DICTIONARY;
+}
+
 #if DCHECK_IS_ON()
 void AutocompleteMatch::Validate() const {
   ValidateClassifications(contents, contents_class);
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index dff64d40..d16054bf 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -350,6 +350,10 @@
   // See base/trace_event/memory_usage_estimator.h for more info.
   size_t EstimateMemoryUsage() const;
 
+  // Some types of matches (answers for dictionary definitions, e.g.) do not
+  // follow the common rules for reversing lines.
+  bool IsExceptedFromLineReversal() const;
+
   // The provider of this match, used to remember which provider the user had
   // selected when the input changes. This may be NULL, in which case there is
   // no provider (or memory of the user's selection).
diff --git a/components/payments/content/BUILD.gn b/components/payments/content/BUILD.gn
index fa744191..74faebc 100644
--- a/components/payments/content/BUILD.gn
+++ b/components/payments/content/BUILD.gn
@@ -36,6 +36,7 @@
     "//components/payments/mojom",
     "//components/prefs",
     "//components/strings:components_strings_grit",
+    "//components/ukm/content",
     "//components/url_formatter",
     "//content/public/browser",
     "//third_party/blink/public:blink_headers",
@@ -119,6 +120,7 @@
     "//components/strings:components_strings_grit",
     "//components/webdata/common",
     "//content/test:test_support",
+    "//services/metrics/public/cpp:metrics_cpp",
     "//sql",
     "//testing/gtest",
     "//third_party/blink/public:blink_headers",
diff --git a/components/payments/content/DEPS b/components/payments/content/DEPS
index a1853f1b..b57f1c0 100644
--- a/components/payments/content/DEPS
+++ b/components/payments/content/DEPS
@@ -5,12 +5,14 @@
   "+components/keyed_service/core",
   "+components/prefs",
   "+components/strings",
+  "+components/ukm/content",
   "+components/url_formatter",
   "+components/webdata/common",
   "+content/public",
   "+mojo/public/cpp",
   "+net",
   "+services/data_decoder/public/cpp",
+  "+services/metrics/public/cpp",
   "+sql",
   "+third_party/blink/public/common",
   "+third_party/blink/public/platform/modules/payments",
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index a791237..32464225 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -21,6 +21,7 @@
 #include "components/payments/core/payment_instrument.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/prefs/pref_service.h"
+#include "components/ukm/content/source_url_recorder.h"
 #include "components/url_formatter/elide_url.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
@@ -49,8 +50,7 @@
           render_frame_host->GetLastCommittedURL())),
       observer_for_testing_(observer_for_testing),
       journey_logger_(delegate_->IsIncognito(),
-                      web_contents_->GetLastCommittedURL(),
-                      delegate_->GetUkmRecorder()),
+                      ukm::GetSourceIdForWebContentsDocument(web_contents)),
       weak_ptr_factory_(this) {
   // OnConnectionTerminated will be called when the Mojo pipe is closed. This
   // will happen as a result of many renderer-side events (both successful and
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index 3b4c618..d0a298a 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -16,6 +16,7 @@
 #include "components/payments/content/payment_request_spec.h"
 #include "components/payments/content/test_content_payment_request_delegate.h"
 #include "components/payments/core/journey_logger.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/payments/payment_request.mojom.h"
 
@@ -29,8 +30,7 @@
       : num_on_selected_information_changed_called_(0),
         test_payment_request_delegate_(&test_personal_data_manager_),
         journey_logger_(test_payment_request_delegate_.IsIncognito(),
-                        GURL("http://www.test.com"),
-                        test_payment_request_delegate_.GetUkmRecorder()),
+                        ukm::UkmRecorder::GetNewSourceID()),
         address_(autofill::test::GetFullProfile()),
         credit_card_visa_(autofill::test::GetCreditCard()) {
     test_personal_data_manager_.SetAutofillCreditCardEnabled(true);
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc
index b306f49..307337f6 100644
--- a/components/payments/core/journey_logger.cc
+++ b/components/payments/core/journey_logger.cc
@@ -55,13 +55,10 @@
 
 }  // namespace
 
-JourneyLogger::JourneyLogger(bool is_incognito,
-                             const GURL& url,
-                             ukm::UkmRecorder* ukm_recorder)
+JourneyLogger::JourneyLogger(bool is_incognito, ukm::SourceId source_id)
     : is_incognito_(is_incognito),
       events_(EVENT_INITIATED),
-      url_(url),
-      ukm_recorder_(ukm_recorder) {}
+      source_id_(source_id) {}
 
 JourneyLogger::~JourneyLogger() {
   if (WasPaymentRequestTriggered())
@@ -241,16 +238,14 @@
   // Record the events in UMA.
   base::UmaHistogramSparse("PaymentRequest.Events", events_);
 
-  if (!ukm_recorder_ || !url_.is_valid())
+  if (source_id_ == ukm::kInvalidSourceId)
     return;
 
   // Record the events in UKM.
-  ukm::SourceId source_id = ukm_recorder_->GetNewSourceID();
-  ukm_recorder_->UpdateSourceURL(source_id, url_);
-  ukm::builders::PaymentRequest_CheckoutEvents(source_id)
+  ukm::builders::PaymentRequest_CheckoutEvents(source_id_)
       .SetCompletionStatus(completion_status)
       .SetEvents(events_)
-      .Record(ukm_recorder_);
+      .Record(ukm::UkmRecorder::Get());
 }
 
 bool JourneyLogger::WasPaymentRequestTriggered() {
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h
index 85709fc..7db5ce3 100644
--- a/components/payments/core/journey_logger.h
+++ b/components/payments/core/journey_logger.h
@@ -8,11 +8,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "url/gurl.h"
-
-namespace ukm {
-class UkmRecorder;
-}
+#include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace payments {
 
@@ -127,9 +123,7 @@
     NOT_SHOWN_REASON_MAX = 4,
   };
 
-  JourneyLogger(bool is_incognito,
-                const GURL& url,
-                ukm::UkmRecorder* ukm_recorder);
+  JourneyLogger(bool is_incognito, ukm::SourceId source_id);
   ~JourneyLogger();
 
   // Increments the number of selection adds for the specified section.
@@ -232,10 +226,7 @@
   // Accumulates the many events that have happened during the Payment Request.
   int events_;
 
-  const GURL url_;
-
-  // Not owned, will outlive this object.
-  ukm::UkmRecorder* ukm_recorder_;
+  ukm::SourceId source_id_;
 
   DISALLOW_COPY_AND_ASSIGN(JourneyLogger);
 };
diff --git a/components/payments/core/journey_logger_unittest.cc b/components/payments/core/journey_logger_unittest.cc
index ba883f6f..8a30a1a 100644
--- a/components/payments/core/journey_logger_unittest.cc
+++ b/components/payments/core/journey_logger_unittest.cc
@@ -23,8 +23,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentNotCalled_NoShow) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   logger.SetCompleted();
 
@@ -42,8 +41,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndUserAbort) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant does not query CanMakePayment, show the PaymentRequest and the
   // user aborts it.
@@ -66,8 +64,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndOtherAbort) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant does not query CanMakePayment, show the PaymentRequest and
   // there is an abort not initiated by the user.
@@ -90,8 +87,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndComplete) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant does not query CanMakePayment, show the PaymentRequest and the
   // user completes it.
@@ -114,8 +110,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseAndNoShow) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user cannot make payment and the PaymentRequest is not shown.
   logger.SetCanMakePaymentValue(false);
@@ -136,8 +131,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueAndNoShow) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user can make payment and the PaymentRequest is not shown.
   logger.SetCanMakePaymentValue(true);
@@ -158,8 +152,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndUserAbort) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user cannot make payment, the Payment Request is shown but is aborted
   // by the user.
@@ -183,8 +176,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndOtherAbort) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user cannot make payment, the Payment Request is shown but is aborted.
   logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
@@ -207,8 +199,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndComplete) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user cannot make payment, the payment request is shown and is
   // completed.
@@ -232,8 +223,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndUserAbort) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user can make payment, the Payment Request is shown and aborted by the
   // user.
@@ -257,8 +247,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndOtherAbort) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user can make a payment, the request is shown but the transaction is
   // aborted.
@@ -282,8 +271,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndComplete) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The user can make a payment, the request is shown and the user completes
   // the checkout.
@@ -307,8 +295,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_CanMakePayment_IncognitoTab) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/true, ukm::kInvalidSourceId);
 
   // The user can make a payment, the request is shown and the user completes
   // the checkout.
@@ -332,8 +319,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_SuggestionsForEverything_Completed) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -378,8 +364,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_SuggestionsForEverything_UserAborted) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -424,8 +409,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_SuggestionsForEverything_OtherAborted) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -471,8 +455,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_SuggestionsForEverything_Incognito) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/true, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -517,8 +500,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_NoSuggestionsForEverything_Completed) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -563,8 +545,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_NoSuggestionsForEverything_UserAborted) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -609,8 +590,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_NoSuggestionsForEverything_OtherAborted) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -656,8 +636,7 @@
 TEST(JourneyLoggerTest,
      RecordJourneyStatsHistograms_NoSuggestionsForEverything_Incognito) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/true, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -703,8 +682,7 @@
     JourneyLoggerTest,
     RecordJourneyStatsHistograms_NoCompleteSuggestionsForEverything_OtherAborted) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -751,8 +729,7 @@
     JourneyLoggerTest,
     RecordJourneyStatsHistograms_NoCompleteSuggestionsForEverything_SomeComplete_OtherAborted) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -801,8 +778,7 @@
     JourneyLoggerTest,
     RecordJourneyStatsHistograms_CompleteSuggestionsForEverything_OtherAborted) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
-                       /*ukm_recorder=*/nullptr);
+  JourneyLogger logger(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // The merchant only requests payment information.
   logger.SetRequestedInformation(
@@ -849,10 +825,8 @@
 // Requests.
 TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
   base::HistogramTester histogram_tester;
-  JourneyLogger logger1(/*is_incognito=*/false, /*url=*/GURL(""),
-                        /*ukm_recorder=*/nullptr);
-  JourneyLogger logger2(/*is_incognito=*/false, /*url=*/GURL(""),
-                        /*ukm_recorder=*/nullptr);
+  JourneyLogger logger1(/*is_incognito=*/false, ukm::kInvalidSourceId);
+  JourneyLogger logger2(/*is_incognito=*/false, ukm::kInvalidSourceId);
 
   // Make the two loggers have different data.
   logger1.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
@@ -933,8 +907,9 @@
   char test_url[] = "http://www.google.com/";
 
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url),
-                       /*ukm_recorder=*/&ukm_recorder);
+  ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
+  ukm_recorder.UpdateSourceURL(source_id, GURL(test_url));
+  JourneyLogger logger(/*is_incognito=*/true, source_id);
   logger.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/true,
       /*requested_phone=*/false, /*requested_name=*/false);
@@ -983,8 +958,9 @@
   char test_url[] = "http://www.google.com/";
 
   base::HistogramTester histogram_tester;
-  JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url),
-                       /*ukm_recorder=*/&ukm_recorder);
+  ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
+  ukm_recorder.UpdateSourceURL(source_id, GURL(test_url));
+  JourneyLogger logger(/*is_incognito=*/true, source_id);
   logger.SetRequestedInformation(
       /*requested_shipping=*/true, /*requested_email=*/true,
       /*requested_phone=*/false, /*requested_name=*/false);
diff --git a/components/safe_browsing/proto/csd.proto b/components/safe_browsing/proto/csd.proto
index 41a07f0b..1079f03 100644
--- a/components/safe_browsing/proto/csd.proto
+++ b/components/safe_browsing/proto/csd.proto
@@ -705,7 +705,12 @@
   // How this navigation is initiated.
   optional NavigationInitiation navigation_initiation = 10;
 
-  // next available tag number: 11.
+  // Whether we think this entry may have been launched by an external
+  // application. This will have no false negatives, but some false positives
+  // are possible.
+  optional bool maybe_launched_by_external_application = 11;
+
+  // next available tag number: 12.
 }  // End of ReferrerChainEntry
 
 message ClientDownloadResponse {
diff --git a/components/safe_browsing/web_ui/safe_browsing_ui.cc b/components/safe_browsing/web_ui/safe_browsing_ui.cc
index b15e183..c8cbbbae 100644
--- a/components/safe_browsing/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/web_ui/safe_browsing_ui.cc
@@ -567,6 +567,10 @@
   referrer_dict.SetKey("navigation_initiation",
                        base::Value(navigation_initiation));
 
+  referrer_dict.SetKey(
+      "maybe_launched_by_external_application",
+      base::Value(referrer.maybe_launched_by_external_application()));
+
   return std::move(referrer_dict);
 }
 
diff --git a/components/search_engines/BUILD.gn b/components/search_engines/BUILD.gn
index 90e00b4..b5c89d4f 100644
--- a/components/search_engines/BUILD.gn
+++ b/components/search_engines/BUILD.gn
@@ -77,6 +77,7 @@
     "//components/url_formatter",
     "//google_apis",
     "//net",
+    "//services/network/public/cpp",
     "//sql",
     "//third_party/libxml",
     "//ui/base",
@@ -140,6 +141,8 @@
     "//components/sync_preferences:test_support",
     "//components/webdata/common",
     "//net:net",
+    "//services/network:test_support",
+    "//services/network/public/cpp",
     "//sql",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/search_engines/DEPS b/components/search_engines/DEPS
index a5e5859c..5f550b12 100644
--- a/components/search_engines/DEPS
+++ b/components/search_engines/DEPS
@@ -17,6 +17,8 @@
   "+google_apis",
   "+libxml",
   "+net",
+  "+services/network/public/cpp",
+  "+services/network/test",
   "+sql",
   "+ui/base",
   "+ui/gfx",
diff --git a/components/search_engines/template_url_fetcher.cc b/components/search_engines/template_url_fetcher.cc
index 47a8a79..a930d09b 100644
--- a/components/search_engines/template_url_fetcher.cc
+++ b/components/search_engines/template_url_fetcher.cc
@@ -13,10 +13,8 @@
 #include "components/search_engines/template_url_service.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 
 namespace {
 
@@ -46,19 +44,20 @@
 }  // namespace
 
 // RequestDelegate ------------------------------------------------------------
-class TemplateURLFetcher::RequestDelegate : public net::URLFetcherDelegate {
+class TemplateURLFetcher::RequestDelegate {
  public:
-  RequestDelegate(
-      TemplateURLFetcher* fetcher,
-      const base::string16& keyword,
-      const GURL& osdd_url,
-      const GURL& favicon_url,
-      const URLFetcherCustomizeCallback& url_fetcher_customize_callback);
+  RequestDelegate(TemplateURLFetcher* fetcher,
+                  const base::string16& keyword,
+                  const GURL& osdd_url,
+                  const GURL& favicon_url,
+                  const url::Origin& initiator,
+                  network::mojom::URLLoaderFactory* url_loader_factory,
+                  int render_frame_id,
+                  int resource_type);
 
-  // net::URLFetcherDelegate:
   // If data contains a valid OSDD, a TemplateURL is created and added to
   // the TemplateURLService.
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
+  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
 
   // URL of the OSDD.
   GURL url() const { return osdd_url_; }
@@ -70,7 +69,7 @@
   void OnLoaded();
   void AddSearchProvider();
 
-  std::unique_ptr<net::URLFetcher> url_fetcher_;
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
   TemplateURLFetcher* fetcher_;
   std::unique_ptr<TemplateURL> template_url_;
   base::string16 keyword_;
@@ -87,12 +86,11 @@
     const base::string16& keyword,
     const GURL& osdd_url,
     const GURL& favicon_url,
-    const URLFetcherCustomizeCallback& url_fetcher_customize_callback)
-    : url_fetcher_(net::URLFetcher::Create(osdd_url,
-                                           net::URLFetcher::GET,
-                                           this,
-                                           kTrafficAnnotation)),
-      fetcher_(fetcher),
+    const url::Origin& initiator,
+    network::mojom::URLLoaderFactory* url_loader_factory,
+    int render_frame_id,
+    int resource_type)
+    : fetcher_(fetcher),
       keyword_(keyword),
       osdd_url_(osdd_url),
       favicon_url_(favicon_url) {
@@ -107,14 +105,22 @@
     model->Load();
   }
 
-  if (!url_fetcher_customize_callback.is_null())
-    url_fetcher_customize_callback.Run(url_fetcher_.get());
-
-  url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                             net::LOAD_DO_NOT_SAVE_COOKIES |
-                             net::LOAD_DO_NOT_SEND_AUTH_DATA);
-  url_fetcher_->SetRequestContext(fetcher->request_context_.get());
-  url_fetcher_->Start();
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = osdd_url;
+  resource_request->request_initiator = initiator;
+  resource_request->render_frame_id = render_frame_id;
+  resource_request->resource_type = resource_type;
+  resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES |
+                                 net::LOAD_DO_NOT_SAVE_COOKIES |
+                                 net::LOAD_DO_NOT_SEND_AUTH_DATA;
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), kTrafficAnnotation);
+  simple_url_loader_->DownloadToString(
+      url_loader_factory,
+      base::BindOnce(
+          &TemplateURLFetcher::RequestDelegate::OnSimpleLoaderComplete,
+          base::Unretained(this)),
+      50000 /* max_body_size */);
 }
 
 void TemplateURLFetcher::RequestDelegate::OnLoaded() {
@@ -125,27 +131,19 @@
   // WARNING: AddSearchProvider deletes us.
 }
 
-void TemplateURLFetcher::RequestDelegate::OnURLFetchComplete(
-    const net::URLFetcher* source) {
+void TemplateURLFetcher::RequestDelegate::OnSimpleLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
   // Validation checks.
   // Make sure we can still replace the keyword, i.e. the fetch was successful.
-  // If the OSDD file was loaded HTTP, we also have to check the response_code.
-  // For other schemes, e.g. when the OSDD file is bundled with an extension,
-  // the response_code is not applicable and should be -1. Also, ensure that
-  // the returned information results in a valid search URL.
-  std::string data;
-  if (!source->GetStatus().is_success() ||
-      ((source->GetResponseCode() != -1) &&
-        (source->GetResponseCode() != 200)) ||
-      !source->GetResponseAsString(&data)) {
+  if (!response_body) {
     fetcher_->RequestCompleted(this);
     // WARNING: RequestCompleted deletes us.
     return;
   }
 
   template_url_ = TemplateURLParser::Parse(
-      fetcher_->template_url_service_->search_terms_data(), data.data(),
-      data.length(), nullptr);
+      fetcher_->template_url_service_->search_terms_data(),
+      response_body->data(), response_body->length(), nullptr);
   if (!template_url_ ||
       !template_url_->url_ref().SupportsReplacement(
           fetcher_->template_url_service_->search_terms_data())) {
@@ -198,12 +196,8 @@
 
 // TemplateURLFetcher ---------------------------------------------------------
 
-TemplateURLFetcher::TemplateURLFetcher(
-    TemplateURLService* template_url_service,
-    net::URLRequestContextGetter* request_context)
-    : template_url_service_(template_url_service),
-      request_context_(request_context) {
-}
+TemplateURLFetcher::TemplateURLFetcher(TemplateURLService* template_url_service)
+    : template_url_service_(template_url_service) {}
 
 TemplateURLFetcher::~TemplateURLFetcher() {
 }
@@ -212,7 +206,10 @@
     const base::string16& keyword,
     const GURL& osdd_url,
     const GURL& favicon_url,
-    const URLFetcherCustomizeCallback& url_fetcher_customize_callback) {
+    const url::Origin& initiator,
+    network::mojom::URLLoaderFactory* url_loader_factory,
+    int render_frame_id,
+    int resource_type) {
   DCHECK(osdd_url.is_valid());
   DCHECK(!keyword.empty());
 
@@ -236,7 +233,8 @@
   }
 
   requests_.push_back(std::make_unique<RequestDelegate>(
-      this, keyword, osdd_url, favicon_url, url_fetcher_customize_callback));
+      this, keyword, osdd_url, favicon_url, initiator, url_loader_factory,
+      render_frame_id, resource_type));
 }
 
 void TemplateURLFetcher::RequestCompleted(RequestDelegate* request) {
diff --git a/components/search_engines/template_url_fetcher.h b/components/search_engines/template_url_fetcher.h
index 896c755..9944c70 100644
--- a/components/search_engines/template_url_fetcher.h
+++ b/components/search_engines/template_url_fetcher.h
@@ -18,9 +18,14 @@
 class TemplateURL;
 class TemplateURLService;
 
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
+namespace network {
+namespace mojom {
+class URLLoaderFactory;
+}
+}  // namespace network
+
+namespace url {
+class Origin;
 }
 
 // TemplateURLFetcher is responsible for downloading OpenSearch description
@@ -29,12 +34,8 @@
 //
 class TemplateURLFetcher : public KeyedService {
  public:
-  typedef base::Callback<void(
-      net::URLFetcher* url_fetcher)> URLFetcherCustomizeCallback;
-
   // Creates a TemplateURLFetcher.
-  TemplateURLFetcher(TemplateURLService* template_url_service,
-                     net::URLRequestContextGetter* request_context);
+  explicit TemplateURLFetcher(TemplateURLService* template_url_service);
   ~TemplateURLFetcher() override;
 
   // If TemplateURLFetcher is not already downloading the OSDD for osdd_url,
@@ -45,14 +46,13 @@
   // TemplateURL in the model for |keyword|, or we're already downloading an
   // OSDD for this keyword, no download is started.
   //
-  // If |url_fetcher_customize_callback| is not null, it's run after a
-  // URLFetcher is created. This callback can be used to set additional
-  // parameters on the URLFetcher.
-  void ScheduleDownload(
-      const base::string16& keyword,
-      const GURL& osdd_url,
-      const GURL& favicon_url,
-      const URLFetcherCustomizeCallback& url_fetcher_customize_callback);
+  void ScheduleDownload(const base::string16& keyword,
+                        const GURL& osdd_url,
+                        const GURL& favicon_url,
+                        const url::Origin& initiator,
+                        network::mojom::URLLoaderFactory* url_loader_factory,
+                        int render_frame_id,
+                        int resource_type);
 
   // The current number of outstanding requests.
   int requests_count() const { return requests_.size(); }
@@ -69,7 +69,6 @@
   friend class RequestDelegate;
 
   TemplateURLService* template_url_service_;
-  scoped_refptr<net::URLRequestContextGetter> request_context_;
 
   // In progress requests.
   std::vector<std::unique_ptr<RequestDelegate>> requests_;
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index 1d1ca6ac..1246dfd 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -22,6 +22,7 @@
   "+gpu/config",
   "+gpu/ipc",
   "+gpu/skia_bindings",
+  "+gpu/vulkan",
   "+mojo/public/cpp/bindings",
   "+mojo/public/cpp/system",
   "+skia",
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 58d6a05..f9633cf 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -315,12 +315,31 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(recorder_);
 
-  // Convert internal format from GLES2 to platform GL.
-  const auto* version_info = impl_on_gpu_->gl_version_info();
-  metadata.backend_format = GrBackendFormat::MakeGL(
-      gl::GetInternalFormat(version_info,
-                            *metadata.backend_format.getGLFormat()),
-      *metadata.backend_format.getGLTarget());
+  if (!gpu_service_->is_using_vulkan()) {
+    // Convert internal format from GLES2 to platform GL.
+    const auto* version_info = impl_on_gpu_->gl_version_info();
+    metadata.backend_format = GrBackendFormat::MakeGL(
+        gl::GetInternalFormat(version_info,
+                              *metadata.backend_format.getGLFormat()),
+        *metadata.backend_format.getGLTarget());
+  } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+    VkFormat format = VK_FORMAT_UNDEFINED;
+    switch (metadata.color_type) {
+      case kRGBA_8888_SkColorType:
+        format = VK_FORMAT_R8G8B8A8_UNORM;
+        break;
+      case kBGRA_8888_SkColorType:
+        format = VK_FORMAT_B8G8R8A8_UNORM;
+        break;
+      default:
+        NOTREACHED();
+    }
+    metadata.backend_format = GrBackendFormat::MakeVk(format);
+#else
+    NOTREACHED();
+#endif
+  }
 
   DCHECK(!metadata.mailbox.IsZero());
   resource_sync_tokens_.push_back(metadata.sync_token);
@@ -343,10 +362,19 @@
   // supported by Skia.
   YUVResourceMetadata yuv_metadata(std::move(metadatas), yuv_color_space);
 
-  // Convert internal format from GLES2 to platform GL.
-  const auto* version_info = impl_on_gpu_->gl_version_info();
-  auto backend_format = GrBackendFormat::MakeGL(
-      gl::GetInternalFormat(version_info, GL_BGRA8_EXT), GL_TEXTURE_2D);
+  GrBackendFormat backend_format;
+  if (!gpu_service_->is_using_vulkan()) {
+    // Convert internal format from GLES2 to platform GL.
+    const auto* version_info = impl_on_gpu_->gl_version_info();
+    backend_format = GrBackendFormat::MakeGL(
+        gl::GetInternalFormat(version_info, GL_BGRA8_EXT), GL_TEXTURE_2D);
+  } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+    backend_format = GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM);
+#else
+    NOTREACHED();
+#endif
+  }
 
   return PromiseTextureHelper<YUVResourceMetadata>::MakePromiseSkImage(
       this, &recorder_.value(), backend_format, yuv_metadata.size(),
@@ -393,11 +421,21 @@
 
   // TODO(penghuang): Figure out how to choose the right size.
   constexpr size_t kCacheMaxResourceBytes = 90 * 1024 * 1024;
-  const auto* version_info = impl_on_gpu_->gl_version_info();
-  unsigned int texture_storage_format = TextureStorageFormat(format);
-  auto backend_format = GrBackendFormat::MakeGL(
-      gl::GetInternalFormat(version_info, texture_storage_format),
-      GL_TEXTURE_2D);
+
+  GrBackendFormat backend_format;
+  if (!gpu_service_->is_using_vulkan()) {
+    const auto* version_info = impl_on_gpu_->gl_version_info();
+    unsigned int texture_storage_format = TextureStorageFormat(format);
+    backend_format = GrBackendFormat::MakeGL(
+        gl::GetInternalFormat(version_info, texture_storage_format),
+        GL_TEXTURE_2D);
+  } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+    backend_format = GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM);
+#else
+    NOTREACHED();
+#endif
+  }
   auto characterization = gr_context_thread_safe->createCharacterization(
       kCacheMaxResourceBytes, image_info, backend_format, msaa_sample_count,
       kTopLeft_GrSurfaceOrigin, surface_props, mipmap);
@@ -441,14 +479,25 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(recorder_);
 
-  // Convert internal format from GLES2 to platform GL.
-  const auto* version_info = impl_on_gpu_->gl_version_info();
-  unsigned int texture_storage_format = TextureStorageFormat(format);
-  auto backend_format = GrBackendFormat::MakeGL(
-      gl::GetInternalFormat(version_info, texture_storage_format),
-      GL_TEXTURE_2D);
-  SkColorType color_type =
-      ResourceFormatToClosestSkColorType(true /*gpu_compositing */, format);
+  SkColorType color_type = kBGRA_8888_SkColorType;
+  GrBackendFormat backend_format;
+
+  if (!gpu_service_->is_using_vulkan()) {
+    // Convert internal format from GLES2 to platform GL.
+    const auto* version_info = impl_on_gpu_->gl_version_info();
+    unsigned int texture_storage_format = TextureStorageFormat(format);
+    backend_format = GrBackendFormat::MakeGL(
+        gl::GetInternalFormat(version_info, texture_storage_format),
+        GL_TEXTURE_2D);
+    color_type =
+        ResourceFormatToClosestSkColorType(true /*gpu_compositing */, format);
+  } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+    backend_format = GrBackendFormat::MakeVk(VK_FORMAT_B8G8R8A8_UNORM);
+#else
+    NOTREACHED();
+#endif
+  }
   return PromiseTextureHelper<RenderPassId>::MakePromiseSkImage(
       this, &recorder_.value(), backend_format, size,
       mipmap ? GrMipMapped::kYes : GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 44840ec..10c3211 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -17,7 +17,9 @@
 #include "gpu/command_buffer/service/texture_base.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "gpu/config/gpu_preferences.h"
+#include "gpu/ipc/common/gpu_surface_lookup.h"
 #include "gpu/ipc/service/image_transport_surface.h"
+#include "gpu/vulkan/buildflags.h"
 #include "third_party/skia/include/private/SkDeferredDisplayList.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/gl/gl_bindings.h"
@@ -26,6 +28,10 @@
 #include "ui/gl/gl_version_info.h"
 #include "ui/gl/init/gl_factory.h"
 
+#if BUILDFLAG(ENABLE_VULKAN)
+#include "gpu/vulkan/vulkan_implementation.h"
+#endif
+
 namespace viz {
 namespace {
 
@@ -67,52 +73,20 @@
           gpu::CommandBufferNamespace::VIZ_OUTPUT_SURFACE, command_buffer_id_,
           gpu_service_->skia_output_surface_sequence_id());
 
-  if (surface_handle_) {
-    surface_ = gpu::ImageTransportSurface::CreateNativeSurface(
-        weak_ptr_factory_.GetWeakPtr(), surface_handle_, gl::GLSurfaceFormat());
-  } else {
-    // surface_ could be null for pixel tests.
-    surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(1, 1));
-  }
-  DCHECK(surface_);
-
-  gl::GLContext* gl_context = nullptr;
-  if (!gpu_service_->GetGrContextForGLSurface(surface_.get(), &gr_context_,
-                                              &gl_context)) {
-    LOG(FATAL) << "Failed to create GrContext";
-    // TODO(penghuang): handle the failure.
-  }
-  gl_context_ = gl_context;
-  DCHECK(gr_context_);
-  DCHECK(gl_context_);
-
-  if (!gl_context_->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle the failure.
-  }
-
-  gl_version_info_ = gl_context_->GetVersionInfo();
-
-  capabilities_.flipped_output_surface = surface_->FlipsVertically();
-
-  // Get stencil bits from the default frame buffer.
-  auto* current_gl = gl_context_->GetCurrentGL();
-  const auto* version = current_gl->Version;
-  auto* api = current_gl->Api;
-  GLint stencil_bits = 0;
-  if (version->is_desktop_core_profile) {
-    api->glGetFramebufferAttachmentParameterivEXTFn(
-        GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
-        &stencil_bits);
-  } else {
-    api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits);
-  }
-
-  capabilities_.supports_stencil = stencil_bits > 0;
+  if (gpu_service_->is_using_vulkan())
+    InitializeForVulkan();
+  else
+    InitializeForGL();
 }
 
 SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+#if BUILDFLAG(ENABLE_VULKAN)
+  if (vulkan_surface_) {
+    vulkan_surface_->Destroy();
+    vulkan_surface_ = nullptr;
+  }
+#endif
   sync_point_client_state_->Destroy();
 }
 
@@ -132,36 +106,65 @@
         base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(event)));
   }
 
-  if (!gl_context_->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle the failure.
+  if (!gpu_service_->is_using_vulkan()) {
+    if (!gl_context_->MakeCurrent(gl_surface_.get())) {
+      LOG(FATAL) << "Failed to make current.";
+      // TODO(penghuang): Handle the failure.
+    }
+    gl::GLSurface::ColorSpace surface_color_space =
+        color_space == gfx::ColorSpace::CreateSCRGBLinear()
+            ? gl::GLSurface::ColorSpace::SCRGB_LINEAR
+            : gl::GLSurface::ColorSpace::UNSPECIFIED;
+    if (!gl_surface_->Resize(size, device_scale_factor, surface_color_space,
+                             has_alpha)) {
+      LOG(FATAL) << "Failed to resize.";
+      // TODO(penghuang): Handle the failure.
+    }
+    DCHECK(gl_context_->IsCurrent(gl_surface_.get()));
+    DCHECK(gr_context_);
+
+    SkSurfaceProps surface_props =
+        SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
+
+    GrGLFramebufferInfo framebuffer_info;
+    framebuffer_info.fFBOID = 0;
+    framebuffer_info.fFormat =
+        gl_version_info_->is_es ? GL_BGRA8_EXT : GL_RGBA8;
+
+    GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8,
+                                        framebuffer_info);
+
+    sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
+        gr_context_, render_target, kBottomLeft_GrSurfaceOrigin,
+        kBGRA_8888_SkColorType, nullptr, &surface_props);
+    DCHECK(sk_surface_);
+  } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+    if (vulkan_surface_)
+      vulkan_surface_->Destroy();
+    gfx::AcceleratedWidget accelerated_widget = gfx::kNullAcceleratedWidget;
+#if defined(OS_ANDROID)
+    accelerated_widget =
+        gpu::GpuSurfaceLookup::GetInstance()->AcquireNativeWidget(
+            surface_handle_);
+#else
+    accelerated_widget = surface_handle_;
+#endif
+    vulkan_surface_ = gpu_service_->vulkan_context_provider()
+                          ->GetVulkanImplementation()
+                          ->CreateViewSurface(accelerated_widget);
+    if (!vulkan_surface_)
+      LOG(FATAL) << "Failed to create vulkan surface.";
+    if (!vulkan_surface_->Initialize(
+            gpu_service_->vulkan_context_provider()->GetDeviceQueue(),
+            gpu::VulkanSurface::DEFAULT_SURFACE_FORMAT)) {
+      LOG(FATAL) << "Failed to initialize vulkan surface.";
+    }
+    CreateSkSurfaceForVulkan(size);
+#else
+    NOTREACHED();
+#endif
   }
-  gl::GLSurface::ColorSpace surface_color_space =
-      color_space == gfx::ColorSpace::CreateSCRGBLinear()
-          ? gl::GLSurface::ColorSpace::SCRGB_LINEAR
-          : gl::GLSurface::ColorSpace::UNSPECIFIED;
-  if (!surface_->Resize(size, device_scale_factor, surface_color_space,
-                        has_alpha)) {
-    LOG(FATAL) << "Failed to resize.";
-    // TODO(penghuang): Handle the failure.
-  }
-  DCHECK(gl_context_->IsCurrent(surface_.get()));
-  DCHECK(gr_context_);
-
-  SkSurfaceProps surface_props =
-      SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
-
-  GrGLFramebufferInfo framebuffer_info;
-  framebuffer_info.fFBOID = 0;
-  framebuffer_info.fFormat = gl_version_info_->is_es ? GL_BGRA8_EXT : GL_RGBA8;
-
-  GrBackendRenderTarget render_target(size.width(), size.height(), 0, 8,
-                                      framebuffer_info);
-
-  sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
-      gr_context_, render_target, kBottomLeft_GrSurfaceOrigin,
-      kBGRA_8888_SkColorType, nullptr, &surface_props);
-  DCHECK(sk_surface_);
 
   if (characterization) {
     sk_surface_->characterize(characterization);
@@ -177,13 +180,13 @@
   DCHECK(ddl);
   DCHECK(sk_surface_);
 
-  if (!gl_context_->MakeCurrent(surface_.get())) {
+  if (!gpu_service_->is_using_vulkan() &&
+      !gl_context_->MakeCurrent(gl_surface_.get())) {
     LOG(FATAL) << "Failed to make current.";
     // TODO(penghuang): Handle the failure.
   }
 
   PreprocessYUVResources(std::move(yuv_resource_metadatas));
-
   sk_surface_->draw(ddl.get());
   gr_context_->flush();
   sync_point_client_state_->ReleaseFenceSync(sync_fence_release);
@@ -192,14 +195,30 @@
 void SkiaOutputSurfaceImplOnGpu::SwapBuffers(OutputSurfaceFrame frame) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(sk_surface_);
-  if (!gl_context_->MakeCurrent(surface_.get())) {
-    LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle the failure.
+  if (!gpu_service_->is_using_vulkan()) {
+    if (!gl_context_->MakeCurrent(gl_surface_.get())) {
+      LOG(FATAL) << "Failed to make current.";
+      // TODO(penghuang): Handle the failure.
+    }
+    OnSwapBuffers();
+    gl_surface_->SwapBuffers(frame.need_presentation_feedback
+                                 ? buffer_presented_callback_
+                                 : base::DoNothing());
+  } else {
+#if BUILDFLAG(ENABLE_VULKAN)
+    OnSwapBuffers();
+    gpu::SwapBuffersCompleteParams params;
+    params.swap_response.swap_start = base::TimeTicks::Now();
+    params.swap_response.result = vulkan_surface_->SwapBuffers();
+    params.swap_response.swap_end = base::TimeTicks::Now();
+    DidSwapBuffersComplete(params);
+
+    CreateSkSurfaceForVulkan(
+        gfx::Size(sk_surface_->width(), sk_surface_->height()));
+#else
+    NOTREACHED();
+#endif
   }
-  OnSwapBuffers();
-  surface_->SwapBuffers(frame.need_presentation_feedback
-                            ? buffer_presented_callback_
-                            : base::DoNothing());
 }
 
 void SkiaOutputSurfaceImplOnGpu::FinishPaintRenderPass(
@@ -210,9 +229,10 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(ddl);
 
-  if (!gl_context_->MakeCurrent(surface_.get())) {
+  if (!gpu_service_->is_using_vulkan() &&
+      !gl_context_->MakeCurrent(gl_surface_.get())) {
     LOG(FATAL) << "Failed to make current.";
-    // TODO(penghuang): Handle resize failure.
+    // TODO(penghuang): Handle the failure.
   }
 
   PreprocessYUVResources(std::move(yuv_resource_metadatas));
@@ -272,6 +292,11 @@
     const ResourceMetadata& metadata,
     GrBackendTexture* backend_texture) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (gpu_service_->is_using_vulkan()) {
+    // TODO(https://crbug.com/838899): Use SkSurface as raster decoder target.
+    // NOTIMPLEMENTED();
+    return;
+  }
   auto* mailbox_manager = gpu_service_->mailbox_manager();
   auto* texture_base = mailbox_manager->ConsumeTexture(metadata.mailbox);
   if (!texture_base) {
@@ -365,6 +390,56 @@
   return 0;
 }
 
+void SkiaOutputSurfaceImplOnGpu::InitializeForGL() {
+  if (surface_handle_) {
+    gl_surface_ = gpu::ImageTransportSurface::CreateNativeSurface(
+        weak_ptr_factory_.GetWeakPtr(), surface_handle_, gl::GLSurfaceFormat());
+  } else {
+    // surface_ could be null for pixel tests.
+    gl_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size(1, 1));
+  }
+  DCHECK(gl_surface_);
+
+  gl::GLContext* gl_context = nullptr;
+  if (!gpu_service_->GetGrContextForGLSurface(gl_surface_.get(), &gr_context_,
+                                              &gl_context)) {
+    LOG(FATAL) << "Failed to create GrContext";
+    // TODO(penghuang): handle the failure.
+  }
+  gl_context_ = gl_context;
+  DCHECK(gr_context_);
+  DCHECK(gl_context_);
+
+  if (!gl_context_->MakeCurrent(gl_surface_.get())) {
+    LOG(FATAL) << "Failed to make current.";
+    // TODO(penghuang): Handle the failure.
+  }
+
+  gl_version_info_ = gl_context_->GetVersionInfo();
+
+  capabilities_.flipped_output_surface = gl_surface_->FlipsVertically();
+
+  // Get stencil bits from the default frame buffer.
+  auto* current_gl = gl_context_->GetCurrentGL();
+  const auto* version = current_gl->Version;
+  auto* api = current_gl->Api;
+  GLint stencil_bits = 0;
+  if (version->is_desktop_core_profile) {
+    api->glGetFramebufferAttachmentParameterivEXTFn(
+        GL_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE,
+        &stencil_bits);
+  } else {
+    api->glGetIntegervFn(GL_STENCIL_BITS, &stencil_bits);
+  }
+
+  capabilities_.supports_stencil = stencil_bits > 0;
+}
+
+void SkiaOutputSurfaceImplOnGpu::InitializeForVulkan() {
+  gr_context_ = gpu_service_->GetGrContextForVulkan();
+  DCHECK(gr_context_);
+}
+
 void SkiaOutputSurfaceImplOnGpu::BindOrCopyTextureIfNecessary(
     gpu::TextureBase* texture_base) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -392,6 +467,11 @@
 
 void SkiaOutputSurfaceImplOnGpu::PreprocessYUVResources(
     std::vector<YUVResourceMetadata*> yuv_resource_metadatas) {
+  if (gpu_service_->is_using_vulkan()) {
+    // TODO(https://crbug.com/838899): Use VkImage for video.
+    // NOTIMPLEMENTED();
+    return;
+  }
   // Create SkImage for fullfilling YUV promise image, before drawing the ddl.
   // TODO(penghuang): Remove the extra step when Skia supports drawing YUV
   // textures directly.
@@ -440,4 +520,26 @@
   pending_swap_completed_params_.emplace_back(swap_id, pixel_size);
 }
 
+void SkiaOutputSurfaceImplOnGpu::CreateSkSurfaceForVulkan(
+    const gfx::Size& size) {
+#if BUILDFLAG(ENABLE_VULKAN)
+  SkSurfaceProps surface_props =
+      SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
+  auto* swap_chain = vulkan_surface_->GetSwapChain();
+  VkImage vk_image = swap_chain->GetCurrentImage(swap_chain->current_image());
+  GrVkImageInfo vk_image_info;
+  vk_image_info.fImage = vk_image;
+  vk_image_info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
+  vk_image_info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+  vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+  vk_image_info.fFormat = VK_FORMAT_B8G8R8A8_UNORM;
+  vk_image_info.fLevelCount = 1;
+  GrBackendRenderTarget render_target(size.width(), size.height(), 0, 0,
+                                      vk_image_info);
+  sk_surface_ = SkSurface::MakeFromBackendRenderTarget(
+      gr_context_, render_target, kTopLeft_GrSurfaceOrigin,
+      kBGRA_8888_SkColorType, nullptr, &surface_props);
+#endif
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 38d0349..a124875 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -31,6 +31,10 @@
 
 namespace gpu {
 class SyncPointClientState;
+
+#if BUILDFLAG(ENABLE_VULKAN)
+class VulkanSurface;
+#endif
 }
 
 namespace viz {
@@ -140,6 +144,9 @@
   void AddFilter(IPC::MessageFilter* message_filter) override;
   int32_t GetRouteID() const override;
 
+  void InitializeForGL();
+  void InitializeForVulkan();
+
   void BindOrCopyTextureIfNecessary(gpu::TextureBase* texture_base);
   void PreprocessYUVResources(
       std::vector<YUVResourceMetadata*> yuv_resource_metadatas);
@@ -147,6 +154,8 @@
   // Generage the next swap ID and push it to our pending swap ID queues.
   void OnSwapBuffers();
 
+  void CreateSkSurfaceForVulkan(const gfx::Size& size);
+
   const gpu::CommandBufferId command_buffer_id_;
   GpuServiceImpl* const gpu_service_;
   const gpu::SurfaceHandle surface_handle_;
@@ -154,13 +163,17 @@
   BufferPresentedCallback buffer_presented_callback_;
   scoped_refptr<gpu::SyncPointClientState> sync_point_client_state_;
   gpu::GpuPreferences gpu_preferences_;
-  scoped_refptr<gl::GLSurface> surface_;
+  scoped_refptr<gl::GLSurface> gl_surface_;
   sk_sp<SkSurface> sk_surface_;
   GrContext* gr_context_ = nullptr;
   scoped_refptr<gl::GLContext> gl_context_;
   const gl::GLVersionInfo* gl_version_info_ = nullptr;
   OutputSurface::Capabilities capabilities_;
 
+#if BUILDFLAG(ENABLE_VULKAN)
+  std::unique_ptr<gpu::VulkanSurface> vulkan_surface_;
+#endif
+
   // Offscreen surfaces for render passes. It can only be accessed on GPU
   // thread.
   base::flat_map<RenderPassId, sk_sp<SkSurface>> offscreen_surfaces_;
diff --git a/components/viz/service/gl/DEPS b/components/viz/service/gl/DEPS
index 55cd3a7..1e459f8 100644
--- a/components/viz/service/gl/DEPS
+++ b/components/viz/service/gl/DEPS
@@ -8,6 +8,7 @@
   "+gpu/ipc",
   "+gpu/ipc/common",
   "+gpu/ipc/service",
+  "+gpu/vulkan",
   "+ipc",
   "+media/gpu",
   "+media/mojo",
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 3ed52c92..a9e578db 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -18,6 +18,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "components/crash/core/common/crash_key.h"
+#include "components/viz/common/gpu/vulkan_context_provider.h"
+#include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/command_buffer/service/scheduler.h"
@@ -33,6 +35,7 @@
 #include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
+#include "gpu/vulkan/buildflags.h"
 #include "ipc/ipc_channel_handle.h"
 #include "ipc/ipc_sync_channel.h"
 #include "ipc/ipc_sync_message_filter.h"
@@ -135,6 +138,7 @@
     const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
     const base::Optional<gpu::GpuFeatureInfo>&
         gpu_feature_info_for_hardware_gpu,
+    gpu::VulkanImplementation* vulkan_implementation,
     base::OnceClosure exit_callback)
     : main_runner_(base::ThreadTaskRunnerHandle::Get()),
       io_runner_(std::move(io_runner)),
@@ -146,6 +150,9 @@
       gpu_feature_info_(gpu_feature_info),
       gpu_info_for_hardware_gpu_(gpu_info_for_hardware_gpu),
       gpu_feature_info_for_hardware_gpu_(gpu_feature_info_for_hardware_gpu),
+#if BUILDFLAG(ENABLE_VULKAN)
+      vulkan_implementation_(vulkan_implementation),
+#endif
       exit_callback_(std::move(exit_callback)),
       bindings_(std::make_unique<mojo::BindingSet<mojom::GpuService>>()),
       weak_ptr_factory_(this) {
@@ -153,6 +160,16 @@
 #if defined(OS_CHROMEOS)
   protected_buffer_manager_ = new arc::ProtectedBufferManager();
 #endif  // defined(OS_CHROMEOS)
+
+#if BUILDFLAG(ENABLE_VULKAN)
+  if (vulkan_implementation_) {
+    vulkan_context_provider_ =
+        VulkanInProcessContextProvider::Create(vulkan_implementation_);
+    if (!vulkan_context_provider_)
+      DLOG(WARNING) << "Failed to create Vulkan context provider.";
+  }
+#endif
+
   weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
 }
 
@@ -293,6 +310,7 @@
                                               GrContext** gr_context,
                                               gl::GLContext** gl_context) {
   DCHECK(main_runner_->BelongsToCurrentThread());
+  DCHECK(!is_using_vulkan());
   DCHECK(surface);
   DCHECK(gr_context && !*gr_context);
   DCHECK(gl_context && !*gl_context);
@@ -328,6 +346,17 @@
   return !!gr_context;
 }
 
+GrContext* GpuServiceImpl::GetGrContextForVulkan() {
+  DCHECK(main_runner_->BelongsToCurrentThread());
+  DCHECK(is_using_vulkan());
+#if BUILDFLAG(ENABLE_VULKAN)
+  return vulkan_context_provider_->GetGrContext();
+#else
+  NOTREACHED();
+  return nullptr;
+#endif
+}
+
 gpu::ImageFactory* GpuServiceImpl::gpu_image_factory() {
   return gpu_memory_buffer_factory_
              ? gpu_memory_buffer_factory_->AsImageFactory()
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h
index 675534d..31e1b51 100644
--- a/components/viz/service/gl/gpu_service_impl.h
+++ b/components/viz/service/gl/gpu_service_impl.h
@@ -25,6 +25,7 @@
 #include "gpu/ipc/service/gpu_channel_manager_delegate.h"
 #include "gpu/ipc/service/gpu_config.h"
 #include "gpu/ipc/service/x_util.h"
+#include "gpu/vulkan/buildflags.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/viz/privileged/interfaces/gl/gpu_host.mojom.h"
 #include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
@@ -44,6 +45,7 @@
 class GpuWatchdogThread;
 class Scheduler;
 class SyncPointManager;
+class VulkanImplementation;
 }  // namespace gpu
 
 namespace media {
@@ -52,6 +54,8 @@
 
 namespace viz {
 
+class VulkanContextProvider;
+
 // This runs in the GPU process, and communicates with the gpu host (which is
 // the window server) over the mojom APIs. This is responsible for setting up
 // the connection to clients, allocating/free'ing gpu memory etc.
@@ -66,6 +70,7 @@
                  const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu,
                  const base::Optional<gpu::GpuFeatureInfo>&
                      gpu_feature_info_for_hardware_gpu,
+                 gpu::VulkanImplementation* vulkan_implementation,
                  base::OnceClosure exit_callback);
 
   ~GpuServiceImpl() override;
@@ -83,6 +88,8 @@
                                 GrContext** gr_context,
                                 gl::GLContext** gl_context);
 
+  GrContext* GetGrContextForVulkan();
+
   // Notifies the GpuHost to stop using GPU compositing. This should be called
   // in response to an error in the GPU process that occurred after
   // InitializeWithHost() was called, otherwise GpuFeatureInfo should be set
@@ -134,6 +141,16 @@
     return skia_output_surface_sequence_id_;
   }
 
+#if BUILDFLAG(ENABLE_VULKAN)
+  bool is_using_vulkan() const { return !!vulkan_context_provider_; }
+  VulkanContextProvider* vulkan_context_provider() {
+    return vulkan_context_provider_.get();
+  }
+#else
+  bool is_using_vulkan() const { return false; }
+  VulkanContextProvider* vulkan_context_provider() { return nullptr; }
+#endif
+
   void set_oopd_enabled() { oopd_enabled_ = true; }
 
  private:
@@ -267,6 +284,11 @@
   struct GrContextAndGLContext;
   base::flat_map<unsigned long, GrContextAndGLContext> contexts_for_gl_;
 
+#if BUILDFLAG(ENABLE_VULKAN)
+  gpu::VulkanImplementation* vulkan_implementation_;
+  scoped_refptr<VulkanContextProvider> vulkan_context_provider_;
+#endif
+
   // An event that will be signalled when we shutdown. On some platforms it
   // comes from external sources.
   std::unique_ptr<base::WaitableEvent> owned_shutdown_event_;
diff --git a/components/viz/service/gl/gpu_service_impl_unittest.cc b/components/viz/service/gl/gpu_service_impl_unittest.cc
index 7ac7f83..e988567 100644
--- a/components/viz/service/gl/gpu_service_impl_unittest.cc
+++ b/components/viz/service/gl/gpu_service_impl_unittest.cc
@@ -50,9 +50,10 @@
   void SetUp() override {
     ASSERT_TRUE(io_thread_.Start());
     gpu_service_ = std::make_unique<GpuServiceImpl>(
-        gpu::GPUInfo(), /*watchdog_thread=*/nullptr, io_thread_.task_runner(),
+        gpu::GPUInfo(), nullptr /* watchdog_thread */, io_thread_.task_runner(),
         gpu::GpuFeatureInfo(), gpu::GpuPreferences(), gpu::GPUInfo(),
-        gpu::GpuFeatureInfo(), /*exit_callback=*/base::DoNothing());
+        gpu::GpuFeatureInfo(), nullptr /* vulkan_implementation */,
+        base::DoNothing() /* exit_callback */);
   }
 
   void TearDown() override {
diff --git a/components/viz/service/main/viz_main_impl.cc b/components/viz/service/main/viz_main_impl.cc
index c08e4619..2475623 100644
--- a/components/viz/service/main/viz_main_impl.cc
+++ b/components/viz/service/main/viz_main_impl.cc
@@ -170,7 +170,8 @@
       gpu_init_->gpu_info(), gpu_init_->TakeWatchdogThread(), io_task_runner(),
       gpu_init_->gpu_feature_info(), gpu_init_->gpu_preferences(),
       gpu_init_->gpu_info_for_hardware_gpu(),
-      gpu_init_->gpu_feature_info_for_hardware_gpu(), std::move(exit_callback));
+      gpu_init_->gpu_feature_info_for_hardware_gpu(),
+      gpu_init_->vulkan_implementation(), std::move(exit_callback));
   if (dependencies_.create_display_compositor)
     gpu_service_->set_oopd_enabled();
 }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index dbe98ed..dab1edb 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1890,8 +1890,6 @@
       "media/capture/cursor_renderer.h",
       "media/capture/desktop_capture_device.cc",
       "media/capture/desktop_capture_device.h",
-      "media/capture/fake_webcontent_capture_machine.cc",
-      "media/capture/fake_webcontent_capture_machine.h",
       "media/capture/frame_sink_video_capture_device.cc",
       "media/capture/frame_sink_video_capture_device.h",
       "media/capture/web_contents_video_capture_device.cc",
@@ -1901,14 +1899,10 @@
     deps += [ "//third_party/webrtc/modules/desktop_capture" ]
     if (use_aura) {
       sources += [
-        "media/capture/aura_window_capture_machine.cc",
-        "media/capture/aura_window_capture_machine.h",
         "media/capture/aura_window_video_capture_device.cc",
         "media/capture/aura_window_video_capture_device.h",
         "media/capture/cursor_renderer_aura.cc",
         "media/capture/cursor_renderer_aura.h",
-        "media/capture/desktop_capture_device_aura.cc",
-        "media/capture/desktop_capture_device_aura.h",
       ]
     }
     if (is_chromeos) {
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 288e1043..afd8cb0 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -65,9 +65,9 @@
   # Limit visibility into Content Service internals.
   "-services/content",
   "+services/content/public",
+  "+services/content/navigable_contents_delegate.h",
   "+services/content/service.h",
   "+services/content/service_delegate.h",
-  "+services/content/view_delegate.h",
 
   # No inclusion of WebKit from the browser, other than the ones in
   # WebKit/public/{mojom,common}, or the ones that are strictly enum/POD,
diff --git a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
index 1e4481e0..2f95096b 100644
--- a/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
+++ b/content/browser/browsing_data/browsing_data_remover_impl_unittest.cc
@@ -1492,6 +1492,7 @@
 
   GURL domain("https://google.com");
   logging_service->OnHeader(url::Origin::Create(domain),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
 
   ASSERT_EQ(1u, logging_service->GetPolicyOriginsForTesting().size());
@@ -1513,15 +1514,19 @@
 
   GURL domain1("https://google.com");
   logging_service->OnHeader(url::Origin::Create(domain1),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
   GURL domain2("https://host2.com");
   logging_service->OnHeader(url::Origin::Create(domain2),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
   GURL domain3("https://host3.com");
   logging_service->OnHeader(url::Origin::Create(domain3),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
   GURL domain4("https://host4.com");
   logging_service->OnHeader(url::Origin::Create(domain4),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
 
   ASSERT_EQ(4u, logging_service->GetPolicyOriginsForTesting().size());
diff --git a/content/browser/content_service_browsertest.cc b/content/browser/content_service_browsertest.cc
index db6558f..633ff83 100644
--- a/content/browser/content_service_browsertest.cc
+++ b/content/browser/content_service_browsertest.cc
@@ -12,9 +12,9 @@
 #include "content/public/common/content_paths.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/shell/browser/shell.h"
-#include "services/content/public/cpp/view.h"
+#include "services/content/public/cpp/navigable_contents.h"
 #include "services/content/public/mojom/constants.mojom.h"
-#include "services/content/public/mojom/view_factory.mojom.h"
+#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 
 namespace content {
@@ -39,19 +39,19 @@
 };
 
 // Verifies that the embedded Content Service is reachable. Does a basic
-// end-to-end sanity check to also verify that a ContentView client is backed
-// by a WebContents instance in the browser.
+// end-to-end sanity check to also verify that a NavigableContents is backed by
+// a WebContents instance in the browser.
 IN_PROC_BROWSER_TEST_F(ContentServiceBrowserTest, EmbeddedContentService) {
   auto* browser_context = shell()->web_contents()->GetBrowserContext();
   auto* connector = BrowserContext::GetConnectorFor(browser_context);
 
-  content::mojom::ViewFactoryPtr factory;
+  content::mojom::NavigableContentsFactoryPtr factory;
   connector->BindInterface(content::mojom::kServiceName, &factory);
-  auto view = std::make_unique<content::View>(factory.get());
+  auto contents = std::make_unique<content::NavigableContents>(factory.get());
 
   base::RunLoop loop;
-  view->set_did_stop_loading_callback_for_testing(loop.QuitClosure());
-  view->Navigate(embedded_test_server()->GetURL("/hello.html"));
+  contents->set_did_stop_loading_callback_for_testing(loop.QuitClosure());
+  contents->Navigate(embedded_test_server()->GetURL("/hello.html"));
   loop.Run();
 }
 
diff --git a/content/browser/content_service_delegate_impl.cc b/content/browser/content_service_delegate_impl.cc
index fa67d58..40eb806f11 100644
--- a/content/browser/content_service_delegate_impl.cc
+++ b/content/browser/content_service_delegate_impl.cc
@@ -7,29 +7,32 @@
 #include "base/macros.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "services/content/navigable_contents_delegate.h"
 #include "services/content/service.h"
-#include "services/content/view_delegate.h"
 
 namespace content {
 
 namespace {
 
-// Bridge between Content Service view delegation API and a WebContentsImpl.
-class ViewDelegateImpl : public content::ViewDelegate,
-                         public WebContentsObserver {
+// Bridge between Content Service navigable contents delegation API and a
+// WebContentsImpl.
+class NavigableContentsDelegateImpl : public content::NavigableContentsDelegate,
+                                      public WebContentsObserver {
  public:
-  explicit ViewDelegateImpl(BrowserContext* browser_context,
-                            mojom::ViewClient* client)
+  explicit NavigableContentsDelegateImpl(BrowserContext* browser_context,
+                                         mojom::NavigableContentsClient* client)
       : client_(client) {
     WebContents::CreateParams params(browser_context);
     web_contents_ = WebContents::Create(params);
     WebContentsObserver::Observe(web_contents_.get());
   }
 
-  ~ViewDelegateImpl() override { WebContentsObserver::Observe(nullptr); }
+  ~NavigableContentsDelegateImpl() override {
+    WebContentsObserver::Observe(nullptr);
+  }
 
  private:
-  // content::ViewDelegate:
+  // content::NavigableContentsDelegate:
   gfx::NativeView GetNativeView() override {
     return web_contents_->GetNativeView();
   }
@@ -44,9 +47,9 @@
   void DidStopLoading() override { client_->DidStopLoading(); }
 
   std::unique_ptr<WebContents> web_contents_;
-  mojom::ViewClient* const client_;
+  mojom::NavigableContentsClient* const client_;
 
-  DISALLOW_COPY_AND_ASSIGN(ViewDelegateImpl);
+  DISALLOW_COPY_AND_ASSIGN(NavigableContentsDelegateImpl);
 };
 
 }  // namespace
@@ -77,9 +80,11 @@
   service_instances_.erase(service);
 }
 
-std::unique_ptr<content::ViewDelegate>
-ContentServiceDelegateImpl::CreateViewDelegate(mojom::ViewClient* client) {
-  return std::make_unique<ViewDelegateImpl>(browser_context_, client);
+std::unique_ptr<content::NavigableContentsDelegate>
+ContentServiceDelegateImpl::CreateNavigableContentsDelegate(
+    mojom::NavigableContentsClient* client) {
+  return std::make_unique<NavigableContentsDelegateImpl>(browser_context_,
+                                                         client);
 }
 
 }  // namespace content
diff --git a/content/browser/content_service_delegate_impl.h b/content/browser/content_service_delegate_impl.h
index 6c37c19..70b2295f 100644
--- a/content/browser/content_service_delegate_impl.h
+++ b/content/browser/content_service_delegate_impl.h
@@ -36,8 +36,8 @@
  private:
   // content::ContentServiceDelegate:
   void WillDestroyServiceInstance(content::Service* service) override;
-  std::unique_ptr<ViewDelegate> CreateViewDelegate(
-      mojom::ViewClient* client) override;
+  std::unique_ptr<NavigableContentsDelegate> CreateNavigableContentsDelegate(
+      mojom::NavigableContentsClient* client) override;
 
   BrowserContext* const browser_context_;
 
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 9cc32b63..12538a7 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -823,6 +823,35 @@
   Send(new FrameMsg_MediaPlayerActionAt(routing_id_, point_in_view, action));
 }
 
+void RenderFrameHostImpl::CreateNetworkServiceDefaultFactory(
+    network::mojom::URLLoaderFactoryRequest default_factory_request) {
+  network::mojom::URLLoaderFactoryParamsPtr params =
+      network::mojom::URLLoaderFactoryParams::New();
+  params->process_id = GetProcess()->GetID();
+  SiteIsolationPolicy::PopulateURLLoaderFactoryParamsPtrForCORB(params.get());
+
+  auto* context = GetSiteInstance()->GetBrowserContext();
+  GetContentClient()->browser()->WillCreateURLLoaderFactory(
+      context, this, false /* is_navigation */, &default_factory_request);
+  // Keep DevTools proxy lasy, i.e. closest to the network.
+  RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
+      this, false /* is_navigation */, false /* is_download */,
+      &default_factory_request);
+  StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
+      BrowserContext::GetStoragePartition(context, GetSiteInstance()));
+  if (g_create_network_factory_callback_for_test.Get().is_null()) {
+    storage_partition->GetNetworkContext()->CreateURLLoaderFactory(
+        std::move(default_factory_request), std::move(params));
+  } else {
+    network::mojom::URLLoaderFactoryPtr original_factory;
+    storage_partition->GetNetworkContext()->CreateURLLoaderFactory(
+        mojo::MakeRequest(&original_factory), std::move(params));
+    g_create_network_factory_callback_for_test.Get().Run(
+        std::move(default_factory_request), GetProcess()->GetID(),
+        original_factory.PassInterface());
+  }
+}
+
 gfx::NativeView RenderFrameHostImpl::GetNativeView() {
   RenderWidgetHostView* view = render_view_host_->GetWidget()->GetView();
   if (!view)
@@ -4626,40 +4655,21 @@
 
 void RenderFrameHostImpl::CreateNetworkServiceDefaultFactoryAndObserve(
     network::mojom::URLLoaderFactoryRequest default_factory_request) {
-  network::mojom::URLLoaderFactoryParamsPtr params =
-      network::mojom::URLLoaderFactoryParams::New();
-  params->process_id = GetProcess()->GetID();
-  SiteIsolationPolicy::PopulateURLLoaderFactoryParamsPtrForCORB(params.get());
-  network::mojom::URLLoaderFactoryParamsPtr params_for_error_monitoring =
-      params->Clone();
-
-  auto* context = GetSiteInstance()->GetBrowserContext();
-  GetContentClient()->browser()->WillCreateURLLoaderFactory(
-      context, this, false /* is_navigation */, &default_factory_request);
-  // Keep DevTools proxy lasy, i.e. closest to the network.
-  RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-      this, false /* is_navigation */, false /* is_download */,
-      &default_factory_request);
-  StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
-      BrowserContext::GetStoragePartition(context, GetSiteInstance()));
-  if (g_create_network_factory_callback_for_test.Get().is_null()) {
-    storage_partition->GetNetworkContext()->CreateURLLoaderFactory(
-        std::move(default_factory_request), std::move(params));
-  } else {
-    network::mojom::URLLoaderFactoryPtr original_factory;
-    storage_partition->GetNetworkContext()->CreateURLLoaderFactory(
-        mojo::MakeRequest(&original_factory), std::move(params));
-    g_create_network_factory_callback_for_test.Get().Run(
-        std::move(default_factory_request), GetProcess()->GetID(),
-        original_factory.PassInterface());
-  }
+  CreateNetworkServiceDefaultFactory(std::move(default_factory_request));
 
   // Add connection error observer when Network Service is running
   // out-of-process.
-  if (IsOutOfProcessNetworkService()) {
+  if (IsOutOfProcessNetworkService() &&
+      (!network_service_connection_error_handler_holder_ ||
+       network_service_connection_error_handler_holder_.encountered_error())) {
+    StoragePartition* storage_partition = BrowserContext::GetStoragePartition(
+        GetSiteInstance()->GetBrowserContext(), GetSiteInstance());
+    network::mojom::URLLoaderFactoryParamsPtr params =
+        network::mojom::URLLoaderFactoryParams::New();
+    params->process_id = GetProcess()->GetID();
     storage_partition->GetNetworkContext()->CreateURLLoaderFactory(
         mojo::MakeRequest(&network_service_connection_error_handler_holder_),
-        std::move(params_for_error_monitoring));
+        std::move(params));
     network_service_connection_error_handler_holder_
         .set_connection_error_handler(base::BindOnce(
             &RenderFrameHostImpl::UpdateSubresourceLoaderFactories,
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index a52c401..7d84b0f4 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -247,6 +247,8 @@
   void ExecuteMediaPlayerActionAtLocation(
       const gfx::Point&,
       const blink::WebMediaPlayerAction& action) override;
+  void CreateNetworkServiceDefaultFactory(
+      network::mojom::URLLoaderFactoryRequest default_factory_request) override;
 
   // IPC::Sender
   bool Send(IPC::Message* msg) override;
diff --git a/content/browser/media/capture/aura_window_capture_machine.cc b/content/browser/media/capture/aura_window_capture_machine.cc
deleted file mode 100644
index 2f6caae..0000000
--- a/content/browser/media/capture/aura_window_capture_machine.cc
+++ /dev/null
@@ -1,485 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/media/capture/aura_window_capture_machine.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/logging.h"
-#include "base/metrics/histogram_macros.h"
-#include "components/viz/common/frame_sinks/copy_output_request.h"
-#include "components/viz/common/frame_sinks/copy_output_result.h"
-#include "components/viz/common/gl_helper.h"
-#include "content/browser/compositor/image_transport_factory.h"
-#include "content/browser/media/capture/desktop_capture_device_uma_types.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/common/service_manager_connection.h"
-#include "media/base/video_util.h"
-#include "media/capture/content/thread_safe_capture_oracle.h"
-#include "media/capture/content/video_capture_oracle.h"
-#include "media/capture/video_capture_types.h"
-#include "services/device/public/mojom/constants.mojom.h"
-#include "services/device/public/mojom/wake_lock_provider.mojom.h"
-#include "services/service_manager/public/cpp/connector.h"
-#include "skia/ext/image_operations.h"
-#include "ui/aura/client/screen_position_client.h"
-#include "ui/aura/env.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_observer.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/base/cursor/cursors_aura.h"
-#include "ui/compositor/compositor.h"
-#include "ui/compositor/dip_util.h"
-#include "ui/compositor/layer.h"
-
-namespace content {
-
-AuraWindowCaptureMachine::AuraWindowCaptureMachine()
-    : desktop_window_(nullptr),
-      screen_capture_(false),
-      frame_capture_active_(true),
-      weak_factory_(this) {}
-
-AuraWindowCaptureMachine::~AuraWindowCaptureMachine() {}
-
-void AuraWindowCaptureMachine::Start(
-  const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
-  const media::VideoCaptureParams& params,
-  const base::Callback<void(bool)> callback) {
-  // Starts the capture machine asynchronously.
-  BrowserThread::PostTaskAndReplyWithResult(
-      BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(&AuraWindowCaptureMachine::InternalStart,
-                 base::Unretained(this),
-                 oracle_proxy,
-                 params),
-      callback);
-}
-
-bool AuraWindowCaptureMachine::InternalStart(
-    const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
-    const media::VideoCaptureParams& params) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // The window might be destroyed between SetWindow() and Start().
-  if (!desktop_window_)
-    return false;
-
-  // If the associated layer is already destroyed then return failure.
-  ui::Layer* layer = desktop_window_->layer();
-  if (!layer)
-    return false;
-
-  DCHECK(oracle_proxy);
-  oracle_proxy_ = oracle_proxy;
-  capture_params_ = params;
-
-  // Update capture size.
-  UpdateCaptureSize();
-
-  // Start observing compositor updates.
-  aura::WindowTreeHost* const host = desktop_window_->GetHost();
-  ui::Compositor* const compositor = host ? host->compositor() : nullptr;
-  if (!compositor)
-    return false;
-  compositor->AddAnimationObserver(this);
-
-  // Start observing for GL context losses.
-  compositor->context_factory()->AddObserver(this);
-
-  DCHECK(!wake_lock_);
-  // Request Wake Lock. In some testing contexts, the service manager
-  // connection isn't initialized.
-  if (ServiceManagerConnection::GetForProcess()) {
-    service_manager::Connector* connector =
-        ServiceManagerConnection::GetForProcess()->GetConnector();
-    DCHECK(connector);
-    device::mojom::WakeLockProviderPtr wake_lock_provider;
-    connector->BindInterface(device::mojom::kServiceName,
-                             mojo::MakeRequest(&wake_lock_provider));
-    wake_lock_provider->GetWakeLockWithoutContext(
-        device::mojom::WakeLockType::kPreventDisplaySleep,
-        device::mojom::WakeLockReason::kOther, "Aura window or desktop capture",
-        mojo::MakeRequest(&wake_lock_));
-
-    wake_lock_->RequestWakeLock();
-  }
-
-  return true;
-}
-
-void AuraWindowCaptureMachine::Suspend() {
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&AuraWindowCaptureMachine::InternalSuspend,
-                     base::Unretained(this)));
-}
-
-void AuraWindowCaptureMachine::InternalSuspend() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DVLOG(1) << "Suspending frame capture and delivery.";
-  frame_capture_active_ = false;
-}
-
-void AuraWindowCaptureMachine::Resume() {
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&AuraWindowCaptureMachine::InternalResume,
-                     base::Unretained(this)));
-}
-
-void AuraWindowCaptureMachine::InternalResume() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DVLOG(1) << "Resuming frame capture and delivery.";
-  frame_capture_active_ = true;
-  // Whenever capture resumes, capture a refresh frame immediately to make sure
-  // no content updates are missing from the video stream.
-  MaybeCaptureForRefresh();
-}
-
-void AuraWindowCaptureMachine::Stop(const base::Closure& callback) {
-  // Stops the capture machine asynchronously.
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&AuraWindowCaptureMachine::InternalStop,
-                     base::Unretained(this), callback));
-}
-
-void AuraWindowCaptureMachine::InternalStop(const base::Closure& callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // Cancel any and all outstanding callbacks owned by external modules.
-  weak_factory_.InvalidateWeakPtrs();
-
-  if (wake_lock_)
-    wake_lock_->CancelWakeLock();
-  // Stop observing compositor and window events.
-  if (desktop_window_) {
-    if (aura::WindowTreeHost* host = desktop_window_->GetHost()) {
-      if (ui::Compositor* compositor = host->compositor()) {
-        compositor->RemoveAnimationObserver(this);
-        compositor->context_factory()->RemoveObserver(this);
-      }
-    }
-    desktop_window_->RemoveObserver(this);
-    desktop_window_ = nullptr;
-    cursor_renderer_.reset();
-  }
-
-  OnLostResources();
-
-  callback.Run();
-}
-
-void AuraWindowCaptureMachine::MaybeCaptureForRefresh() {
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(
-          &AuraWindowCaptureMachine::Capture,
-          // Use of Unretained() is safe here since this task must run
-          // before InternalStop().
-          base::Unretained(this), base::TimeTicks()));
-}
-
-void AuraWindowCaptureMachine::SetWindow(aura::Window* window) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  DCHECK(!desktop_window_);
-  desktop_window_ = window;
-  cursor_renderer_.reset(
-      new CursorRendererAura(CursorRenderer::CURSOR_DISPLAYED_ALWAYS));
-  cursor_renderer_->SetTargetView(window);
-
-  // Start observing window events.
-  desktop_window_->AddObserver(this);
-
-  // We must store this for the UMA reporting in DidCopyOutput() as
-  // desktop_window_ might be destroyed at that point.
-  screen_capture_ = window->IsRootWindow();
-  IncrementDesktopCaptureCounter(screen_capture_ ? SCREEN_CAPTURER_CREATED
-                                                 : WINDOW_CAPTURER_CREATED);
-}
-
-void AuraWindowCaptureMachine::UpdateCaptureSize() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (oracle_proxy_ && desktop_window_) {
-     ui::Layer* layer = desktop_window_->layer();
-     oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel(
-         layer, layer->bounds().size()));
-  }
-}
-
-void AuraWindowCaptureMachine::Capture(base::TimeTicks event_time) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // Do not capture if the desktop window is already destroyed.
-  if (!desktop_window_)
-    return;
-
-  scoped_refptr<media::VideoFrame> frame;
-  media::ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb;
-
-  // TODO(miu): Need to fix this so the compositor is providing the presentation
-  // timestamps and damage regions, to leverage the frame timestamp rewriting
-  // logic.  http://crbug.com/492839
-  const base::TimeTicks start_time = base::TimeTicks::Now();
-  media::VideoCaptureOracle::Event event;
-  if (event_time.is_null()) {
-    event = media::VideoCaptureOracle::kRefreshRequest;
-    event_time = start_time;
-  } else {
-    event = media::VideoCaptureOracle::kCompositorUpdate;
-  }
-  if (oracle_proxy_->ObserveEventAndDecideCapture(
-          event, gfx::Rect(), event_time, &frame, &capture_frame_cb)) {
-    std::unique_ptr<viz::CopyOutputRequest> request =
-        std::make_unique<viz::CopyOutputRequest>(
-            viz::CopyOutputRequest::ResultFormat::RGBA_TEXTURE,
-            base::BindOnce(&AuraWindowCaptureMachine::DidCopyOutput,
-                           weak_factory_.GetWeakPtr(), std::move(frame),
-                           event_time, start_time, capture_frame_cb));
-    gfx::Rect window_rect = gfx::Rect(desktop_window_->bounds().width(),
-                                      desktop_window_->bounds().height());
-    request->set_area(window_rect);
-    desktop_window_->layer()->RequestCopyOfOutput(std::move(request));
-  }
-}
-
-void AuraWindowCaptureMachine::DidCopyOutput(
-    scoped_refptr<media::VideoFrame> video_frame,
-    base::TimeTicks event_time,
-    base::TimeTicks start_time,
-    const CaptureFrameCallback& capture_frame_cb,
-    std::unique_ptr<viz::CopyOutputResult> result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  static bool first_call = true;
-
-  const bool succeeded = ProcessCopyOutputResponse(
-      video_frame, event_time, capture_frame_cb, std::move(result));
-
-  const base::TimeDelta capture_time = base::TimeTicks::Now() - start_time;
-
-  // The two UMA_ blocks must be put in its own scope since it creates a static
-  // variable which expected constant histogram name.
-  if (screen_capture_) {
-    UMA_HISTOGRAM_TIMES(kUmaScreenCaptureTime, capture_time);
-  } else {
-    UMA_HISTOGRAM_TIMES(kUmaWindowCaptureTime, capture_time);
-  }
-
-  if (first_call) {
-    first_call = false;
-    if (screen_capture_) {
-      IncrementDesktopCaptureCounter(succeeded ? FIRST_SCREEN_CAPTURE_SUCCEEDED
-                                               : FIRST_SCREEN_CAPTURE_FAILED);
-    } else {
-      IncrementDesktopCaptureCounter(succeeded
-                                         ? FIRST_WINDOW_CAPTURE_SUCCEEDED
-                                         : FIRST_WINDOW_CAPTURE_FAILED);
-    }
-  }
-
-  // If ProcessCopyOutputResponse() failed, it will not run |capture_frame_cb|,
-  // so do that now.
-  if (!succeeded)
-    capture_frame_cb.Run(std::move(video_frame), event_time, false);
-}
-
-bool AuraWindowCaptureMachine::ProcessCopyOutputResponse(
-    scoped_refptr<media::VideoFrame> video_frame,
-    base::TimeTicks event_time,
-    const CaptureFrameCallback& capture_frame_cb,
-    std::unique_ptr<viz::CopyOutputResult> result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK_EQ(result->format(), viz::CopyOutputResult::Format::RGBA_TEXTURE);
-
-  if (!desktop_window_) {
-    VLOG(1) << "Ignoring CopyOutputResult: Capture target has gone away.";
-    return false;
-  }
-  if (result->IsEmpty()) {
-    VLOG(1) << "CopyOutputRequest failed: Empty result.";
-    return false;
-  }
-  DCHECK(video_frame);
-
-  // Compute the dest size we want after the letterboxing resize. Make the
-  // coordinates and sizes even because we letterbox in YUV space
-  // (see CopyRGBToVideoFrame). They need to be even for the UV samples to
-  // line up correctly.
-  // The video frame's visible_rect() and the result's size() are both physical
-  // pixels.
-  gfx::Rect region_in_frame = media::ComputeLetterboxRegion(
-      video_frame->visible_rect(), result->size());
-  region_in_frame = gfx::Rect(region_in_frame.x() & ~1,
-                              region_in_frame.y() & ~1,
-                              region_in_frame.width() & ~1,
-                              region_in_frame.height() & ~1);
-  if (region_in_frame.IsEmpty()) {
-    VLOG(1) << "Aborting capture: Computed empty letterboxed content region.";
-    return false;
-  }
-
-  ImageTransportFactory* factory = ImageTransportFactory::GetInstance();
-  viz::GLHelper* gl_helper = factory->GetGLHelper();
-  if (!gl_helper) {
-    VLOG(1) << "Aborting capture: No GLHelper available for YUV readback.";
-    return false;
-  }
-
-  gpu::Mailbox mailbox = result->GetTextureResult()->mailbox;
-  gpu::SyncToken sync_token = result->GetTextureResult()->sync_token;
-  std::unique_ptr<viz::SingleReleaseCallback> release_callback =
-      result->TakeTextureOwnership();
-
-  if (!yuv_readback_pipeline_)
-    yuv_readback_pipeline_ = gl_helper->CreateReadbackPipelineYUV(true, true);
-  viz::GLHelper::ScalerInterface* const scaler =
-      yuv_readback_pipeline_->scaler();
-  const gfx::Vector2d scale_from(result->size().width(),
-                                 result->size().height());
-  const gfx::Vector2d scale_to(region_in_frame.width(),
-                               region_in_frame.height());
-  if (scale_from == scale_to) {
-    if (scaler)
-      yuv_readback_pipeline_->SetScaler(nullptr);
-  } else if (!scaler || !scaler->IsSameScaleRatio(scale_from, scale_to)) {
-    std::unique_ptr<viz::GLHelper::ScalerInterface> fast_scaler =
-        gl_helper->CreateScaler(viz::GLHelper::SCALER_QUALITY_FAST, scale_from,
-                                scale_to, false, false, false);
-    DCHECK(
-        fast_scaler);  // Arguments to CreateScaler() should never be invalid.
-    yuv_readback_pipeline_->SetScaler(std::move(fast_scaler));
-  }
-
-  yuv_readback_pipeline_->ReadbackYUV(
-      mailbox, sync_token, result->size(), gfx::Rect(region_in_frame.size()),
-      video_frame->stride(media::VideoFrame::kYPlane),
-      video_frame->data(media::VideoFrame::kYPlane),
-      video_frame->stride(media::VideoFrame::kUPlane),
-      video_frame->data(media::VideoFrame::kUPlane),
-      video_frame->stride(media::VideoFrame::kVPlane),
-      video_frame->data(media::VideoFrame::kVPlane), region_in_frame.origin(),
-      base::Bind(&CopyOutputFinishedForVideo, weak_factory_.GetWeakPtr(),
-                 event_time, capture_frame_cb, video_frame, region_in_frame,
-                 base::Passed(&release_callback)));
-  media::LetterboxVideoFrame(video_frame.get(), region_in_frame);
-  return true;
-}
-
-using CaptureFrameCallback =
-    media::ThreadSafeCaptureOracle::CaptureFrameCallback;
-
-void AuraWindowCaptureMachine::CopyOutputFinishedForVideo(
-    base::WeakPtr<AuraWindowCaptureMachine> machine,
-    base::TimeTicks event_time,
-    const CaptureFrameCallback& capture_frame_cb,
-    scoped_refptr<media::VideoFrame> target,
-    const gfx::Rect& region_in_frame,
-    std::unique_ptr<viz::SingleReleaseCallback> release_callback,
-    bool result) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  release_callback->Run(gpu::SyncToken(), false);
-
-  // Render the cursor and deliver the captured frame if the
-  // AuraWindowCaptureMachine has not been stopped (i.e., the WeakPtr is
-  // still valid).
-  if (machine) {
-    if (machine->cursor_renderer_ && result)
-      machine->cursor_renderer_->RenderOnVideoFrame(target.get(),
-                                                    region_in_frame, nullptr);
-  } else {
-    VLOG(1) << "Aborting capture: AuraWindowCaptureMachine has gone away.";
-    result = false;
-  }
-
-  capture_frame_cb.Run(std::move(target), event_time, result);
-}
-
-void AuraWindowCaptureMachine::OnWindowBoundsChanged(
-    aura::Window* window,
-    const gfx::Rect& old_bounds,
-    const gfx::Rect& new_bounds,
-    ui::PropertyChangeReason reason) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(desktop_window_ && window == desktop_window_);
-
-  // Post a task to update capture size after first returning to the event loop.
-  BrowserThread::PostTask(
-      BrowserThread::UI, FROM_HERE,
-      base::BindOnce(&AuraWindowCaptureMachine::UpdateCaptureSize,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void AuraWindowCaptureMachine::OnWindowDestroying(aura::Window* window) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  InternalStop(base::DoNothing());
-
-  oracle_proxy_->ReportError(FROM_HERE, "OnWindowDestroying()");
-}
-
-void AuraWindowCaptureMachine::OnWindowAddedToRootWindow(
-    aura::Window* window) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(window == desktop_window_);
-
-  if (aura::WindowTreeHost* host = window->GetHost()) {
-    if (ui::Compositor* compositor = host->compositor())
-      compositor->AddAnimationObserver(this);
-  }
-}
-
-void AuraWindowCaptureMachine::OnWindowRemovingFromRootWindow(
-    aura::Window* window,
-    aura::Window* new_root) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(window == desktop_window_);
-
-  if (aura::WindowTreeHost* host = window->GetHost()) {
-    if (ui::Compositor* compositor = host->compositor()) {
-      compositor->RemoveAnimationObserver(this);
-      compositor->context_factory()->RemoveObserver(this);
-    }
-  }
-}
-
-void AuraWindowCaptureMachine::OnAnimationStep(base::TimeTicks timestamp) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(!timestamp.is_null());
-
-  // HACK: The compositor invokes this observer method to step layer animation
-  // forward. Scheduling frame capture was not the intention, and so invoking
-  // this method does not actually indicate the content has changed. However,
-  // this is the only reliable way to ensure all screen changes are captured, as
-  // of this writing.
-  // http://crbug.com/600031
-  //
-  // TODO(miu): Need a better observer callback interface from the compositor
-  // for this use case. The solution here will always capture frames at the
-  // maximum framerate, which means CPU/GPU is being wasted on redundant
-  // captures and quality/smoothness of animating content will suffer
-  // significantly.
-  // http://crbug.com/492839
-  if (frame_capture_active_)
-    Capture(timestamp);
-}
-
-void AuraWindowCaptureMachine::OnCompositingShuttingDown(
-    ui::Compositor* compositor) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  compositor->RemoveAnimationObserver(this);
-  compositor->context_factory()->RemoveObserver(this);
-}
-
-void AuraWindowCaptureMachine::OnLostResources() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  yuv_readback_pipeline_.reset();
-}
-
-}  // namespace content
diff --git a/content/browser/media/capture/aura_window_capture_machine.h b/content/browser/media/capture/aura_window_capture_machine.h
deleted file mode 100644
index d606741..0000000
--- a/content/browser/media/capture/aura_window_capture_machine.h
+++ /dev/null
@@ -1,150 +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 CONTENT_BROWSER_MEDIA_CAPTURE_AURA_WINDOW_CAPTURE_MACHINE_H_
-#define CONTENT_BROWSER_MEDIA_CAPTURE_AURA_WINDOW_CAPTURE_MACHINE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "content/browser/media/capture/cursor_renderer_aura.h"
-#include "media/capture/content/screen_capture_device_core.h"
-#include "services/device/public/mojom/wake_lock.mojom.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_observer.h"
-#include "ui/base/cursor/cursors_aura.h"
-#include "ui/compositor/compositor.h"
-#include "ui/compositor/compositor_animation_observer.h"
-
-namespace viz {
-class CopyOutputResult;
-class ReadbackYUVInterface;
-}
-
-namespace content {
-
-// AuraWindowCaptureMachine uses the compositor to capture Aura windows.
-//
-// It is used for browser window capture on platforms that use Aura (Windows,
-// Linux, and Chrome OS) and additionally for desktop capture on Chrome OS.
-class AuraWindowCaptureMachine : public media::VideoCaptureMachine,
-                                 public aura::WindowObserver,
-                                 public ui::ContextFactoryObserver,
-                                 public ui::CompositorAnimationObserver {
- public:
-  AuraWindowCaptureMachine();
-  ~AuraWindowCaptureMachine() override;
-
-  // VideoCaptureMachine overrides.
-  void Start(const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
-             const media::VideoCaptureParams& params,
-             const base::Callback<void(bool)> callback) override;
-  void Suspend() override;
-  void Resume() override;
-  void Stop(const base::Closure& callback) override;
-  void MaybeCaptureForRefresh() override;
-
-  // Implements aura::WindowObserver.
-  void OnWindowBoundsChanged(aura::Window* window,
-                             const gfx::Rect& old_bounds,
-                             const gfx::Rect& new_bounds,
-                             ui::PropertyChangeReason reason) override;
-  void OnWindowDestroying(aura::Window* window) override;
-  void OnWindowAddedToRootWindow(aura::Window* window) override;
-  void OnWindowRemovingFromRootWindow(aura::Window* window,
-                                      aura::Window* new_root) override;
-
-  // ui::CompositorAnimationObserver implementation.
-  void OnAnimationStep(base::TimeTicks timestamp) override;
-  void OnCompositingShuttingDown(ui::Compositor* compositor) override;
-
-  // Sets the window to use for capture.
-  void SetWindow(aura::Window* window);
-
- private:
-  bool InternalStart(
-      const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
-      const media::VideoCaptureParams& params);
-  void InternalSuspend();
-  void InternalResume();
-  void InternalStop(const base::Closure& callback);
-
-  // Captures a frame. |event_time| is provided by the compositor, or is null
-  // for refresh requests.
-  void Capture(base::TimeTicks event_time);
-
-  // Update capture size. Must be called on the UI thread.
-  void UpdateCaptureSize();
-
-  using CaptureFrameCallback =
-      media::ThreadSafeCaptureOracle::CaptureFrameCallback;
-
-  // Response callback for cc::Layer::RequestCopyOfOutput().
-  void DidCopyOutput(scoped_refptr<media::VideoFrame> video_frame,
-                     base::TimeTicks event_time,
-                     base::TimeTicks start_time,
-                     const CaptureFrameCallback& capture_frame_cb,
-                     std::unique_ptr<viz::CopyOutputResult> result);
-
-  // A helper which does the real work for DidCopyOutput. Returns true if
-  // succeeded and |capture_frame_cb| will be run at some future point. Returns
-  // false on error, and |capture_frame_cb| should be run by the caller (with
-  // failure status).
-  bool ProcessCopyOutputResponse(scoped_refptr<media::VideoFrame> video_frame,
-                                 base::TimeTicks event_time,
-                                 const CaptureFrameCallback& capture_frame_cb,
-                                 std::unique_ptr<viz::CopyOutputResult> result);
-
-  // ui::ContextFactoryObserver implementation.
-  void OnLostResources() override;
-
-  // Renders the cursor if needed and then delivers the captured frame.
-  static void CopyOutputFinishedForVideo(
-      base::WeakPtr<AuraWindowCaptureMachine> machine,
-      base::TimeTicks event_time,
-      const CaptureFrameCallback& capture_frame_cb,
-      scoped_refptr<media::VideoFrame> target,
-      const gfx::Rect& region_in_frame,
-      std::unique_ptr<viz::SingleReleaseCallback> release_callback,
-      bool result);
-
-  // The window associated with the desktop.
-  aura::Window* desktop_window_;
-
-  // Whether screen capturing or window capture.
-  bool screen_capture_;
-
-  // Makes all the decisions about which frames to copy, and how.
-  scoped_refptr<media::ThreadSafeCaptureOracle> oracle_proxy_;
-
-  // The capture parameters for this capture.
-  media::VideoCaptureParams capture_params_;
-
-  // YUV readback pipeline.
-  std::unique_ptr<viz::ReadbackYUVInterface> yuv_readback_pipeline_;
-
-  // Renders mouse cursor on frame.
-  std::unique_ptr<content::CursorRendererAura> cursor_renderer_;
-
-  // TODO(jiayl): Remove wake_lock_ when there is an API to keep the
-  // screen from sleeping for the drive-by web.
-  device::mojom::WakeLockPtr wake_lock_;
-
-  // False while frame capture has been suspended. All other aspects of the
-  // machine are maintained.
-  bool frame_capture_active_;
-
-  // WeakPtrs are used for the asynchronous capture callbacks passed to external
-  // modules.  They are only valid on the UI thread and become invalidated
-  // immediately when InternalStop() is called to ensure that no more captured
-  // frames will be delivered to the client.
-  base::WeakPtrFactory<AuraWindowCaptureMachine> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(AuraWindowCaptureMachine);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_AURA_WINDOW_CAPTURE_MACHINE_H_
diff --git a/content/browser/media/capture/desktop_capture_device_aura.cc b/content/browser/media/capture/desktop_capture_device_aura.cc
deleted file mode 100644
index a80e012..0000000
--- a/content/browser/media/capture/desktop_capture_device_aura.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/media/capture/desktop_capture_device_aura.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/timer/timer.h"
-#include "content/browser/media/capture/aura_window_capture_machine.h"
-#include "content/browser/media/capture/desktop_capture_device_uma_types.h"
-#include "content/public/browser/browser_thread.h"
-#include "ui/aura/window.h"
-
-namespace content {
-
-namespace {
-
-void SetCaptureSource(AuraWindowCaptureMachine* machine,
-                      const DesktopMediaID& source) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  aura::Window* window = DesktopMediaID::GetAuraWindowById(source);
-  if (window) {
-    machine->SetWindow(window);
-    if (source.type == DesktopMediaID::TYPE_SCREEN) {
-      if (source.audio_share)
-        IncrementDesktopCaptureCounter(SCREEN_CAPTURER_CREATED_WITH_AUDIO);
-      else
-        IncrementDesktopCaptureCounter(SCREEN_CAPTURER_CREATED_WITHOUT_AUDIO);
-    }
-  }
-}
-
-}  // namespace
-
-DesktopCaptureDeviceAura::DesktopCaptureDeviceAura(
-    const DesktopMediaID& source) {
-  AuraWindowCaptureMachine* machine = new AuraWindowCaptureMachine();
-  core_.reset(new media::ScreenCaptureDeviceCore(base::WrapUnique(machine)));
-  // |core_| owns |machine| and deletes it on UI thread so passing the raw
-  // pointer to the UI thread is safe here.
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::BindOnce(&SetCaptureSource, machine, source));
-}
-
-DesktopCaptureDeviceAura::~DesktopCaptureDeviceAura() {
-  DVLOG(2) << "DesktopCaptureDeviceAura@" << this << " destroying.";
-}
-
-// static
-std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDeviceAura::Create(
-    const DesktopMediaID& source) {
-  if (source.aura_id == DesktopMediaID::kNullId)
-    return nullptr;
-  return std::unique_ptr<media::VideoCaptureDevice>(
-      new DesktopCaptureDeviceAura(source));
-}
-
-void DesktopCaptureDeviceAura::AllocateAndStart(
-    const media::VideoCaptureParams& params,
-    std::unique_ptr<Client> client) {
-  DVLOG(1) << "Allocating " << params.requested_format.frame_size.ToString();
-  core_->AllocateAndStart(params, std::move(client));
-}
-
-void DesktopCaptureDeviceAura::RequestRefreshFrame() {
-  core_->RequestRefreshFrame();
-}
-
-void DesktopCaptureDeviceAura::StopAndDeAllocate() {
-  core_->StopAndDeAllocate();
-}
-
-void DesktopCaptureDeviceAura::OnUtilizationReport(int frame_feedback_id,
-                                                   double utilization) {
-  core_->OnConsumerReportingUtilization(frame_feedback_id, utilization);
-}
-
-}  // namespace content
diff --git a/content/browser/media/capture/desktop_capture_device_aura.h b/content/browser/media/capture/desktop_capture_device_aura.h
deleted file mode 100644
index 6eee43a..0000000
--- a/content/browser/media/capture/desktop_capture_device_aura.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_AURA_H_
-#define CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_AURA_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "content/common/content_export.h"
-#include "content/public/browser/desktop_media_id.h"
-#include "media/capture/content/screen_capture_device_core.h"
-#include "media/capture/video/video_capture_device.h"
-
-namespace content {
-
-// An implementation of VideoCaptureDevice that mirrors an Aura window.
-class CONTENT_EXPORT DesktopCaptureDeviceAura
-    : public media::VideoCaptureDevice {
- public:
-  // Creates a VideoCaptureDevice for the Aura desktop.  If |source| does not
-  // reference a registered aura window, returns nullptr instead.
-  static std::unique_ptr<media::VideoCaptureDevice> Create(
-      const DesktopMediaID& source);
-
-  ~DesktopCaptureDeviceAura() override;
-
-  // VideoCaptureDevice implementation.
-  void AllocateAndStart(const media::VideoCaptureParams& params,
-                        std::unique_ptr<Client> client) override;
-  void RequestRefreshFrame() override;
-  void StopAndDeAllocate() override;
-  void OnUtilizationReport(int frame_feedback_id, double utilization) override;
-
- private:
-  explicit DesktopCaptureDeviceAura(const DesktopMediaID& source);
-
-  std::unique_ptr<media::ScreenCaptureDeviceCore> core_;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopCaptureDeviceAura);
-};
-
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_CAPTURE_DEVICE_AURA_H_
diff --git a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc b/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
deleted file mode 100644
index 7de25f8..0000000
--- a/content/browser/media/capture/desktop_capture_device_aura_unittest.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/media/capture/desktop_capture_device_aura.h"
-
-#include <stddef.h>
-#include <stdint.h>
-#include <utility>
-
-#include "base/location.h"
-#include "base/macros.h"
-#include "base/run_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "content/browser/compositor/test/test_image_transport_factory.h"
-#include "content/public/browser/desktop_media_id.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "media/capture/video_capture_types.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/client/window_parenting_client.h"
-#include "ui/aura/test/aura_test_helper.h"
-#include "ui/aura/test/test_window_delegate.h"
-#include "ui/aura/window.h"
-#include "ui/wm/core/default_activation_client.h"
-
-using ::testing::_;
-using ::testing::AnyNumber;
-using ::testing::AtMost;
-using ::testing::DoAll;
-using ::testing::Expectation;
-using ::testing::InvokeWithoutArgs;
-using ::testing::SaveArg;
-
-namespace media {
-class VideoFrame;
-}  // namespace media
-
-namespace content {
-namespace {
-
-const int kFrameRate = 30;
-
-class MockDeviceClient : public media::VideoCaptureDevice::Client {
- public:
-  MOCK_METHOD7(OnIncomingCapturedData,
-               void(const uint8_t* data,
-                    int length,
-                    const media::VideoCaptureFormat& frame_format,
-                    int rotation,
-                    base::TimeTicks reference_time,
-                    base::TimeDelta tiemstamp,
-                    int frame_feedback_id));
-  MOCK_METHOD6(OnIncomingCapturedGfxBuffer,
-               void(gfx::GpuMemoryBuffer* buffer,
-                    const media::VideoCaptureFormat& frame_format,
-                    int clockwise_rotation,
-                    base::TimeTicks reference_time,
-                    base::TimeDelta timestamp,
-                    int frame_feedback_id));
-  MOCK_METHOD0(DoReserveOutputBuffer, void(void));
-  MOCK_METHOD0(DoOnIncomingCapturedBuffer, void(void));
-  MOCK_METHOD0(DoOnIncomingCapturedVideoFrame, void(void));
-  MOCK_METHOD0(DoResurrectLastOutputBuffer, void(void));
-  MOCK_METHOD2(OnError,
-               void(const base::Location& from_here,
-                    const std::string& reason));
-  MOCK_METHOD0(OnStarted, void(void));
-
-  // Trampoline methods to workaround GMOCK problems with std::unique_ptr<>.
-  Buffer ReserveOutputBuffer(const gfx::Size& dimensions,
-                             media::VideoPixelFormat format,
-                             int frame_feedback_id) override {
-    EXPECT_EQ(media::PIXEL_FORMAT_I420, format);
-    DoReserveOutputBuffer();
-    return Buffer();
-  }
-  void OnIncomingCapturedBuffer(Buffer buffer,
-                                const media::VideoCaptureFormat& frame_format,
-                                base::TimeTicks reference_time,
-                                base::TimeDelta timestamp) override {
-    DoOnIncomingCapturedBuffer();
-  }
-  void OnIncomingCapturedBufferExt(
-      Buffer buffer,
-      const media::VideoCaptureFormat& format,
-      base::TimeTicks reference_time,
-      base::TimeDelta timestamp,
-      gfx::Rect visible_rect,
-      const media::VideoFrameMetadata& additional_metadata) override {
-    DoOnIncomingCapturedVideoFrame();
-  }
-  Buffer ResurrectLastOutputBuffer(const gfx::Size& dimensions,
-                                   media::VideoPixelFormat format,
-                                   int frame_feedback_id) override {
-    EXPECT_EQ(media::PIXEL_FORMAT_I420, format);
-    DoResurrectLastOutputBuffer();
-    return Buffer();
-  }
-  double GetBufferPoolUtilization() const override { return 0.0; }
-};
-
-// Test harness that sets up a minimal environment with necessary stubs.
-class DesktopCaptureDeviceAuraTest : public testing::Test {
- public:
-  DesktopCaptureDeviceAuraTest() = default;
-  ~DesktopCaptureDeviceAuraTest() override = default;
-
- protected:
-  void SetUp() override {
-    // The ContextFactory must exist before any Compositors are created.
-    ImageTransportFactory::SetFactory(
-        std::make_unique<TestImageTransportFactory>());
-    helper_.reset(new aura::test::AuraTestHelper());
-    helper_->SetUp(
-        ImageTransportFactory::GetInstance()->GetContextFactory(),
-        ImageTransportFactory::GetInstance()->GetContextFactoryPrivate());
-    new wm::DefaultActivationClient(helper_->root_window());
-    // We need a window to cover desktop area so that DesktopCaptureDeviceAura
-    // can use gfx::NativeWindow::GetWindowAtScreenPoint() to locate the
-    // root window associated with the primary display.
-    gfx::Rect desktop_bounds = root_window()->bounds();
-    window_delegate_.reset(new aura::test::TestWindowDelegate());
-    desktop_window_.reset(new aura::Window(window_delegate_.get()));
-    desktop_window_->Init(ui::LAYER_TEXTURED);
-    desktop_window_->SetBounds(desktop_bounds);
-    aura::client::ParentWindowWithContext(
-        desktop_window_.get(), root_window(), desktop_bounds);
-    desktop_window_->Show();
-  }
-
-  void TearDown() override {
-    helper_->RunAllPendingInMessageLoop();
-    root_window()->RemoveChild(desktop_window_.get());
-    desktop_window_.reset();
-    window_delegate_.reset();
-    helper_->TearDown();
-    base::RunLoop().RunUntilIdle();
-    ImageTransportFactory::Terminate();
-  }
-
-  aura::Window* root_window() { return helper_->root_window(); }
-
- private:
-  TestBrowserThreadBundle thread_bundle_;
-  std::unique_ptr<aura::test::AuraTestHelper> helper_;
-  std::unique_ptr<aura::Window> desktop_window_;
-  std::unique_ptr<aura::test::TestWindowDelegate> window_delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopCaptureDeviceAuraTest);
-};
-
-TEST_F(DesktopCaptureDeviceAuraTest, StartAndStop) {
-  std::unique_ptr<media::VideoCaptureDevice> capture_device =
-      DesktopCaptureDeviceAura::Create(
-          content::DesktopMediaID::RegisterAuraWindow(
-              content::DesktopMediaID::TYPE_SCREEN, root_window()));
-  ASSERT_TRUE(capture_device);
-
-  std::unique_ptr<MockDeviceClient> client(new MockDeviceClient());
-  EXPECT_CALL(*client, OnError(_, _)).Times(0);
-  // |STARTED| is reported asynchronously, which may not be received if capture
-  // is stopped immediately.
-  EXPECT_CALL(*client, OnStarted()).Times(AtMost(1));
-
-  media::VideoCaptureParams capture_params;
-  capture_params.requested_format.frame_size.SetSize(640, 480);
-  capture_params.requested_format.frame_rate = kFrameRate;
-  capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
-  capture_device->AllocateAndStart(capture_params, std::move(client));
-  capture_device->StopAndDeAllocate();
-}
-
-}  // namespace
-}  // namespace content
diff --git a/content/browser/media/capture/fake_webcontent_capture_machine.cc b/content/browser/media/capture/fake_webcontent_capture_machine.cc
deleted file mode 100644
index f18b8835..0000000
--- a/content/browser/media/capture/fake_webcontent_capture_machine.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/media/capture/fake_webcontent_capture_machine.h"
-
-#include "base/logging.h"
-
-namespace content {
-
-FakeWebContentCaptureMachine::FakeWebContentCaptureMachine(
-    bool enable_auto_throttling)
-    : enable_auto_throttling_(enable_auto_throttling) {
-  DVLOG(2) << "FakeWebContentCaptureMachine";
-}
-
-FakeWebContentCaptureMachine::~FakeWebContentCaptureMachine() {
-  DVLOG(2) << "FakeWebContentCaptureMachine@" << this << " destroying.";
-}
-
-void FakeWebContentCaptureMachine::Start(
-    const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
-    const media::VideoCaptureParams& params,
-    const base::Callback<void(bool)> callback) {
-  callback.Run(true);
-}
-void FakeWebContentCaptureMachine::Suspend() {}
-void FakeWebContentCaptureMachine::Resume() {}
-void FakeWebContentCaptureMachine::Stop(const base::Closure& callback) {}
-bool FakeWebContentCaptureMachine::IsAutoThrottlingEnabled() const {
-  return enable_auto_throttling_;
-}
-void FakeWebContentCaptureMachine::MaybeCaptureForRefresh() {}
-
-}  // namespace content
\ No newline at end of file
diff --git a/content/browser/media/capture/fake_webcontent_capture_machine.h b/content/browser/media/capture/fake_webcontent_capture_machine.h
deleted file mode 100644
index 92a5f84..0000000
--- a/content/browser/media/capture/fake_webcontent_capture_machine.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_MEDIA_CAPTURE_FAKE_WEBCONTENT_CAPTURE_MACHINE_H_
-#define CONTENT_BROWSER_MEDIA_CAPTURE_FAKE_WEBCONTENT_CAPTURE_MACHINE_H_
-
-#include "base/callback_helpers.h"
-#include "content/common/content_export.h"
-#include "media/capture/content/screen_capture_device_core.h"
-#include "media/capture/content/thread_safe_capture_oracle.h"
-#include "media/capture/video_capture_types.h"
-
-namespace content {
-
-// An implementation of VideoCaptureDevice that fakes a desktop capturer.
-class CONTENT_EXPORT FakeWebContentCaptureMachine
-    : public media::VideoCaptureMachine {
- public:
-  FakeWebContentCaptureMachine(bool enable_auto_throttling);
-  ~FakeWebContentCaptureMachine() override;
-
-  // VideoCaptureMachine overrides.
-  void Start(const scoped_refptr<media::ThreadSafeCaptureOracle>& oracle_proxy,
-             const media::VideoCaptureParams& params,
-             const base::Callback<void(bool)> callback) override;
-  void Suspend() override;
-  void Resume() override;
-  void Stop(const base::Closure& callback) override;
-  bool IsAutoThrottlingEnabled() const override;
-  void MaybeCaptureForRefresh() override;
-
- private:
-  bool enable_auto_throttling_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeWebContentCaptureMachine);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_MEDIA_CAPTURE_DESKTOP_FAKE_CAPTURE_DEVICE_H_
\ No newline at end of file
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index e30cb00..454ebca 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -183,8 +183,10 @@
   // http://crbug.com/657959.
   std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_impl;
 
+#if BUILDFLAG(ENABLE_VULKAN)
   std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation;
   scoped_refptr<viz::VulkanContextProvider> vulkan_context_provider;
+#endif
  private:
   friend class base::NoDestructor<CompositorDependencies>;
 
@@ -240,6 +242,7 @@
 
 const unsigned int kMaxDisplaySwapBuffers = 1U;
 
+#if BUILDFLAG(ENABLE_VULKAN)
 scoped_refptr<viz::VulkanContextProvider> GetSharedVulkanContextProvider() {
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableVulkan))
@@ -257,6 +260,7 @@
   }
   return context_provider;
 }
+#endif
 
 gpu::SharedMemoryLimits GetCompositorContextSharedMemoryLimits(
     gfx::NativeWindow window) {
@@ -405,10 +409,12 @@
     }
   }
 
+#if BUILDFLAG(ENABLE_VULKAN)
   gpu::VulkanSurface* GetVulkanSurface() override {
     NOTIMPLEMENTED();
     return nullptr;
   }
+#endif
 
   void BindToClient(viz::OutputSurfaceClient* client) override {
     DCHECK(client);
@@ -491,6 +497,7 @@
   base::WeakPtrFactory<AndroidOutputSurface> weak_ptr_factory_;
 };
 
+#if BUILDFLAG(ENABLE_VULKAN)
 class VulkanOutputSurface : public viz::OutputSurface {
  public:
   explicit VulkanOutputSurface(
@@ -600,6 +607,7 @@
 
   DISALLOW_COPY_AND_ASSIGN(VulkanOutputSurface);
 };
+#endif
 
 // TODO(khushalsagar): These are being sent based on the CompositorImpl
 // visiblity which bakes in the assumption that there is a single CompositorImpl
@@ -954,8 +962,10 @@
   if (!host_->IsVisible())
     return;
 
+#if BUILDFLAG(ENABLE_VULKAN)
   if (CreateVulkanOutputSurface())
     return;
+#endif
 
   DCHECK(surface_handle_ != gpu::kNullSurfaceHandle);
   BrowserMainLoop::GetInstance()
@@ -965,6 +975,7 @@
                          weak_factory_.GetWeakPtr()));
 }
 
+#if BUILDFLAG(ENABLE_VULKAN)
 bool CompositorImpl::CreateVulkanOutputSurface() {
   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kEnableVulkan))
@@ -985,6 +996,7 @@
 
   return !!display_;
 }
+#endif
 
 void CompositorImpl::OnGpuChannelEstablished(
     scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) {
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index f61c64dc..ad8f0ea 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -166,7 +166,9 @@
 
   void HandlePendingLayerTreeFrameSinkRequest();
 
+#if BUILDFLAG(ENABLE_VULKAN)
   bool CreateVulkanOutputSurface();
+#endif
   void OnGpuChannelEstablished(
       scoped_refptr<gpu::GpuChannelHost> gpu_channel_host);
   void InitializeDisplay(
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 5b7e2a1..7a862fa 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -1372,8 +1372,15 @@
 
   if (delegated_frame_host_ &&
       delegated_frame_host_->IsPrimarySurfaceEvicted()) {
-    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
-                                base::nullopt);
+    ui::WindowAndroidCompositor* compositor =
+        view_.GetWindowAndroid() ? view_.GetWindowAndroid()->GetCompositor()
+                                 : nullptr;
+    SynchronizeVisualProperties(
+        compositor && compositor->IsDrawingFirstVisibleFrame()
+            ? cc::DeadlinePolicy::UseSpecifiedDeadline(
+                  ui::DelegatedFrameHostAndroid::FirstFrameTimeoutFrames())
+            : cc::DeadlinePolicy::UseDefaultDeadline(),
+        base::nullopt);
   }
 
   host()->WasShown(false /* record_presentation_time */);
@@ -1976,8 +1983,10 @@
     StartObservingRootWindow();
 
   if (resize) {
-    SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
-                                base::nullopt);
+    SynchronizeVisualProperties(
+        cc::DeadlinePolicy::UseSpecifiedDeadline(
+            ui::DelegatedFrameHostAndroid::ResizeTimeoutFrames()),
+        base::nullopt);
   }
 
   if (!touch_selection_controller_) {
@@ -2069,8 +2078,10 @@
 
 void RenderWidgetHostViewAndroid::OnPhysicalBackingSizeChanged() {
   EvictFrameIfNecessary();
-  SynchronizeVisualProperties(cc::DeadlinePolicy::UseDefaultDeadline(),
-                              base::nullopt);
+  SynchronizeVisualProperties(
+      cc::DeadlinePolicy::UseSpecifiedDeadline(
+          ui::DelegatedFrameHostAndroid::ResizeTimeoutFrames()),
+      base::nullopt);
 }
 
 void RenderWidgetHostViewAndroid::OnRootWindowVisibilityChanged(bool visible) {
diff --git a/content/browser/renderer_host/web_database_host_impl.cc b/content/browser/renderer_host/web_database_host_impl.cc
index a27dfea..99612d10 100644
--- a/content/browser/renderer_host/web_database_host_impl.cc
+++ b/content/browser/renderer_host/web_database_host_impl.cc
@@ -91,8 +91,8 @@
   // open handles to them in the database tracker to make sure they're
   // around for as long as needed.
   if (vfs_file_name.empty()) {
-    file = VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
-                                               desired_flags);
+    file = VfsBackend::OpenTempFileInDirectory(
+        db_tracker_->database_directory(), desired_flags);
   } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier,
                                             &database_name, nullptr) &&
              !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier,
diff --git a/content/browser/sandbox_parameters_mac.mm b/content/browser/sandbox_parameters_mac.mm
index e9415b0d..a9227b8 100644
--- a/content/browser/sandbox_parameters_mac.mm
+++ b/content/browser/sandbox_parameters_mac.mm
@@ -10,6 +10,7 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/mac/bundle_locations.h"
+#include "base/mac/foundation_util.h"
 #include "base/mac/mac_util.h"
 #include "base/numerics/checked_math.h"
 #include "base/strings/stringprintf.h"
@@ -63,8 +64,8 @@
   CHECK(client->SetParameter(service_manager::SandboxMac::kSandboxBundlePath,
                              bundle_path));
 
-  NSBundle* bundle = base::mac::OuterBundle();
-  std::string bundle_id = base::SysNSStringToUTF8([bundle bundleIdentifier]);
+  std::string bundle_id = base::mac::BaseBundleID();
+  DCHECK(!bundle_id.empty()) << "base::mac::OuterBundle is unset";
   CHECK(client->SetParameter(
       service_manager::SandboxMac::kSandboxChromeBundleId, bundle_id));
 
diff --git a/content/browser/service_manager/service_manager_context.cc b/content/browser/service_manager/service_manager_context.cc
index 35dba772..2daaf72 100644
--- a/content/browser/service_manager/service_manager_context.cc
+++ b/content/browser/service_manager/service_manager_context.cc
@@ -295,15 +295,6 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceBinaryLauncherFactory);
 };
 
-// Helper to invoke GetGeolocationRequestContext on the currently-set
-// ContentBrowserClient.
-void GetGeolocationRequestContextFromContentClient(
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        callback) {
-  GetContentClient()->browser()->GetGeolocationRequestContext(
-      std::move(callback));
-}
-
 bool ShouldEnableVizService() {
 #if defined(USE_AURA)
   // aura::Env can be null in tests.
@@ -530,7 +521,7 @@
   device_info.factory = base::Bind(
       &device::CreateDeviceService, device_blocking_task_runner,
       service_manager_thread_task_runner_,
-      base::BindRepeating(&GetGeolocationRequestContextFromContentClient),
+      GetContentClient()->browser()->GetSystemSharedURLLoaderFactory(),
       GetContentClient()->browser()->GetGeolocationApiKey(),
       GetContentClient()->browser()->ShouldUseGmsCoreGeolocationProvider(),
       base::Bind(&WakeLockContextHost::GetNativeViewForContext),
@@ -541,7 +532,7 @@
   device_info.factory = base::Bind(
       &device::CreateDeviceService, device_blocking_task_runner,
       service_manager_thread_task_runner_,
-      base::BindRepeating(&GetGeolocationRequestContextFromContentClient),
+      GetContentClient()->browser()->GetSystemSharedURLLoaderFactory(),
       GetContentClient()->browser()->GetGeolocationApiKey(),
       base::Bind(&ContentBrowserClient::OverrideSystemLocationProvider,
                  base::Unretained(GetContentClient()->browser())));
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 7537896e..aee47ae 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -921,11 +921,20 @@
 }
 #endif  // defined(USE_AURA)
 
+#if defined(OS_CHROMEOS)
+// Times out flakily on Chrome OS. crbug.com/833380
+#define MAYBE_CancelWheelScrollBubblingOnWheelTargetDeletion \
+  DISABLED_CancelWheelScrollBubblingOnWheelTargetDeletion
+#else
+#define MAYBE_CancelWheelScrollBubblingOnWheelTargetDeletion \
+  CancelWheelScrollBubblingOnWheelTargetDeletion
+#endif
+
 // Tests that wheel scroll bubbling gets cancelled when the wheel target view
 // gets destroyed in the middle of a wheel scroll seqeunce. This happens in
 // cases like overscroll navigation from inside an oopif.
 IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
-                       CancelWheelScrollBubblingOnWheelTargetDeletion) {
+                       MAYBE_CancelWheelScrollBubblingOnWheelTargetDeletion) {
   ui::GestureConfiguration::GetInstance()->set_scroll_debounce_interval_in_ms(
       0);
   GURL main_url(embedded_test_server()->GetURL(
@@ -1471,7 +1480,7 @@
 // and is needed for trackpad scrolling on Chromebooks.
 #if defined(USE_AURA)
 
-#if defined(THREAD_SANITIZER)
+#if defined(THREAD_SANITIZER) || defined(OS_CHROMEOS)
 // Flaky: https://crbug.com/833380
 #define MAYBE_ScrollEventToOOPIF DISABLED_ScrollEventToOOPIF
 #else
@@ -1618,9 +1627,10 @@
 
 // Test that mouse events are being routed to the correct RenderWidgetHostView
 // based on coordinates.
-#if defined(THREAD_SANITIZER)
+#if defined(THREAD_SANITIZER) || defined(OS_CHROMEOS)
 // The test times out often on TSAN bot.
 // https://crbug.com/591170.
+// Also times out flakily on Chrome OS. crbug.com/833380
 #define MAYBE_SurfaceHitTestTest DISABLED_SurfaceHitTestTest
 #else
 #define MAYBE_SurfaceHitTestTest SurfaceHitTestTest
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 2138a5fb..f592dc9 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -217,7 +217,6 @@
     "navigation_subresource_loader_params.h",
     "net/record_load_histograms.cc",
     "net/record_load_histograms.h",
-    "net/url_fetcher.cc",
     "net/url_request_service_worker_data.cc",
     "net/url_request_service_worker_data.h",
     "net/url_request_user_data.cc",
diff --git a/content/common/net/url_fetcher.cc b/content/common/net/url_fetcher.cc
deleted file mode 100644
index 9af15ee..0000000
--- a/content/common/net/url_fetcher.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2012 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 "content/public/common/url_fetcher.h"
-
-#include "base/bind.h"
-#include "content/common/net/url_request_user_data.h"
-#include "net/url_request/url_fetcher.h"
-
-namespace content {
-
-namespace {
-
-std::unique_ptr<base::SupportsUserData::Data> CreateURLRequestUserData(
-    int render_process_id,
-    int render_frame_id) {
-  return std::make_unique<URLRequestUserData>(render_process_id,
-                                              render_frame_id);
-}
-
-}  // namespace
-
-void AssociateURLFetcherWithRenderFrame(
-    net::URLFetcher* url_fetcher,
-    const base::Optional<url::Origin>& initiator,
-    int render_process_id,
-    int render_frame_id) {
-  url_fetcher->SetInitiator(initiator);
-  url_fetcher->SetURLRequestUserData(
-      URLRequestUserData::kUserDataKey,
-      base::Bind(&CreateURLRequestUserData, render_process_id,
-                 render_frame_id));
-}
-
-}  // namespace content
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index a332235..c14c0d3 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -85,7 +85,7 @@
           "testing_api"
         ],
         "cdm": [ "media:cdm" ],
-        "content": [ "view" ],
+        "content": [ "navigation" ],
         "content_gpu": [ "browser" ],
         "content_plugin": [ "browser" ],
         "content_renderer": [ "browser" ],
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index ac20bf65b..bef2c6a 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -370,10 +370,9 @@
   return nullptr;
 }
 
-void ContentBrowserClient::GetGeolocationRequestContext(
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        callback) {
-  std::move(callback).Run(scoped_refptr<net::URLRequestContextGetter>(nullptr));
+scoped_refptr<network::SharedURLLoaderFactory>
+ContentBrowserClient::GetSystemSharedURLLoaderFactory() {
+  return nullptr;
 }
 
 std::string ContentBrowserClient::GetGeolocationApiKey() {
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index d765060..88e9ab2 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -87,6 +87,10 @@
 class ScopedInterfaceEndpointHandle;
 }
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace service_manager {
 class Identity;
 class Service;
@@ -582,14 +586,11 @@
   virtual std::unique_ptr<device::LocationProvider>
   OverrideSystemLocationProvider();
 
-  // Allows the embedder to provide a URLRequestContextGetter to use for network
-  // geolocation queries.
-  // * May be called from any thread. A URLRequestContextGetter is then provided
-  //   by invoking |callback| on the calling thread.
-  // * Default implementation provides nullptr URLRequestContextGetter.
-  virtual void GetGeolocationRequestContext(
-      base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-          callback);
+  // Returns a SharedURLLoaderFactory attached to the system network context.
+  // Must be called on the UI thread. The default implementation returns
+  // nullptr.
+  virtual scoped_refptr<network::SharedURLLoaderFactory>
+  GetSystemSharedURLLoaderFactory();
 
   // Allows an embedder to provide a Google API Key to use for network
   // geolocation queries.
diff --git a/content/public/browser/gpu_utils.cc b/content/public/browser/gpu_utils.cc
index fd8d511b..78d1c989 100644
--- a/content/public/browser/gpu_utils.cc
+++ b/content/public/browser/gpu_utils.cc
@@ -95,6 +95,9 @@
   gpu_preferences.disable_oop_rasterization =
       command_line->HasSwitch(switches::kDisableOopRasterization);
 
+  gpu_preferences.enable_vulkan =
+      command_line->HasSwitch(switches::kEnableVulkan);
+
   // Some of these preferences are set or adjusted in
   // GpuDataManagerImplPrivate::AppendGpuCommandLine.
   return gpu_preferences;
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 74b1304..5a85535 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -15,6 +15,7 @@
 #include "content/public/common/file_chooser_params.h"
 #include "ipc/ipc_listener.h"
 #include "ipc/ipc_sender.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
 #include "third_party/blink/public/mojom/page/page_visibility_state.mojom.h"
 #include "third_party/blink/public/platform/web_sudden_termination_disabler_type.h"
@@ -314,6 +315,10 @@
       const gfx::Point& location,
       const blink::WebMediaPlayerAction& action) = 0;
 
+  // Creates a Network Service-backed factory from appropriate |NetworkContext|.
+  virtual void CreateNetworkServiceDefaultFactory(
+      network::mojom::URLLoaderFactoryRequest default_factory_request) = 0;
+
  private:
   // This interface should only be implemented inside content.
   friend class RenderFrameHostImpl;
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 19f059c..f0cad2d 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -217,7 +217,6 @@
     "three_d_api_types.h",
     "url_constants.cc",
     "url_constants.h",
-    "url_fetcher.h",
     "url_loader_throttle.cc",
     "url_loader_throttle.h",
     "url_utils.cc",
diff --git a/content/public/common/url_fetcher.h b/content/public/common/url_fetcher.h
deleted file mode 100644
index bc9f1436..0000000
--- a/content/public/common/url_fetcher.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_PUBLIC_COMMON_URL_FETCHER_H_
-#define CONTENT_PUBLIC_COMMON_URL_FETCHER_H_
-
-#include "base/optional.h"
-#include "content/common/content_export.h"
-
-namespace net {
-class URLFetcher;
-}  // namespace
-
-namespace url {
-class Origin;
-}  // namespace
-
-namespace content {
-
-// Mark URLRequests started by the URLFetcher to stem from the given render
-// frame.
-CONTENT_EXPORT void AssociateURLFetcherWithRenderFrame(
-    net::URLFetcher* url_fetcher,
-    const base::Optional<url::Origin>& initiator,
-    int render_process_id,
-    int render_frame_id);
-
-}  // namespace content
-
-#endif  // CONTENT_PUBLIC_COMMON_URL_FETCHER_H_
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc
index 2a7eaf53..3b3afff 100644
--- a/content/public/test/test_launcher.cc
+++ b/content/public/test/test_launcher.cc
@@ -57,6 +57,7 @@
 #include "services/service_manager/sandbox/win/sandbox_win.h"
 #elif defined(OS_MACOSX)
 #include "base/mac/scoped_nsautorelease_pool.h"
+#include "sandbox/mac/seatbelt_exec.h"
 #endif
 
 namespace content {
@@ -614,6 +615,13 @@
 
   params.instance = GetModuleHandle(NULL);
   params.sandbox_info = &sandbox_info;
+#elif defined(OS_MACOSX)
+  sandbox::SeatbeltExecServer::CreateFromArgumentsResult seatbelt =
+      sandbox::SeatbeltExecServer::CreateFromArguments(
+          command_line->GetProgram().value().c_str(), argc, argv);
+  if (seatbelt.sandbox_required) {
+    CHECK(seatbelt.server->InitializeSandbox());
+  }
 #elif !defined(OS_ANDROID)
   params.argc = argc;
   params.argv = const_cast<const char**>(argv);
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink.cc b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
index eece5a8f..24c569e 100644
--- a/content/renderer/android/synchronous_layer_tree_frame_sink.cc
+++ b/content/renderer/android/synchronous_layer_tree_frame_sink.cc
@@ -86,10 +86,12 @@
   void BindFramebuffer() override {}
   void SetDrawRectangle(const gfx::Rect& rect) override {}
   void SwapBuffers(viz::OutputSurfaceFrame frame) override {}
+#if BUILDFLAG(ENABLE_VULKAN)
   gpu::VulkanSurface* GetVulkanSurface() override {
     NOTIMPLEMENTED();
     return nullptr;
   }
+#endif
   void Reshape(const gfx::Size& size,
                float scale_factor,
                const gfx::ColorSpace& color_space,
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
index 44dac7b..0e810df 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc
@@ -160,6 +160,23 @@
           const_cast<uint8_t*>(yuv_buffer->DataV()), elapsed_timestamp);
       break;
     }
+    case webrtc::VideoFrameBuffer::Type::kI010: {
+      webrtc::I010BufferInterface* yuv_buffer = buffer->GetI010();
+      // WebRTC defines I010 data as uint16 whereas Chromium uses uint8 for all
+      // video formats, so conversion and cast is needed.
+      video_frame = media::VideoFrame::WrapExternalYuvData(
+          media::PIXEL_FORMAT_YUV420P10, size, gfx::Rect(size), size,
+          yuv_buffer->StrideY() * 2, yuv_buffer->StrideU() * 2,
+          yuv_buffer->StrideV() * 2,
+          const_cast<uint8_t*>(
+              reinterpret_cast<const uint8_t*>(yuv_buffer->DataY())),
+          const_cast<uint8_t*>(
+              reinterpret_cast<const uint8_t*>(yuv_buffer->DataU())),
+          const_cast<uint8_t*>(
+              reinterpret_cast<const uint8_t*>(yuv_buffer->DataV())),
+          elapsed_timestamp);
+      break;
+    }
     default:
       NOTREACHED();
   }
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index cea3cbe..af63abe 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -636,160 +636,6 @@
       RTCStatsCollectorCallbackImpl::Create(main_thread, std::move(callback)));
 }
 
-class PeerConnectionUMAObserver : public webrtc::UMAObserver {
- public:
-  PeerConnectionUMAObserver() {}
-  ~PeerConnectionUMAObserver() override {}
-  void IncrementEnumCounter(webrtc::PeerConnectionEnumCounterType counter_type,
-                            int counter,
-                            int counter_max) override {
-    switch (counter_type) {
-      case webrtc::kEnumCounterAddressFamily:
-        UMA_HISTOGRAM_EXACT_LINEAR("WebRTC.PeerConnection.IPMetrics", counter,
-                                   counter_max);
-        break;
-      case webrtc::kEnumCounterIceCandidatePairTypeUdp:
-        UMA_HISTOGRAM_EXACT_LINEAR(
-            "WebRTC.PeerConnection.CandidatePairType_UDP", counter,
-            counter_max);
-        break;
-      case webrtc::kEnumCounterIceCandidatePairTypeTcp:
-        UMA_HISTOGRAM_EXACT_LINEAR(
-            "WebRTC.PeerConnection.CandidatePairType_TCP", counter,
-            counter_max);
-        break;
-      case webrtc::kEnumCounterDtlsHandshakeError:
-        UMA_HISTOGRAM_EXACT_LINEAR("WebRTC.PeerConnection.DtlsHandshakeError",
-                                   counter, counter_max);
-        break;
-      case webrtc::kEnumCounterIceRestart:
-        UMA_HISTOGRAM_EXACT_LINEAR("WebRTC.PeerConnection.IceRestartState",
-                                   counter, counter_max);
-        break;
-      case webrtc::kEnumCounterIceRegathering:
-        UMA_HISTOGRAM_EXACT_LINEAR("WebRTC.PeerConnection.IceRegatheringReason",
-                                   counter, counter_max);
-        break;
-      case webrtc::kEnumCounterKeyProtocol:
-        UMA_HISTOGRAM_ENUMERATION(
-            "WebRTC.PeerConnection.KeyProtocol",
-            static_cast<webrtc::KeyExchangeProtocolType>(counter),
-            webrtc::kEnumCounterKeyProtocolMax);
-        break;
-      case webrtc::kEnumCounterSdpSemanticNegotiated:
-        UMA_HISTOGRAM_ENUMERATION(
-            "WebRTC.PeerConnection.SdpSemanticNegotiated",
-            static_cast<webrtc::SdpSemanticNegotiated>(counter),
-            webrtc::kSdpSemanticNegotiatedMax);
-        break;
-      case webrtc::kEnumCounterKeyProtocolMediaType:
-        UMA_HISTOGRAM_ENUMERATION(
-            "WebRTC.PeerConnection.KeyProtocolByMedia",
-            static_cast<webrtc::KeyExchangeProtocolMedia>(counter),
-            webrtc::kEnumCounterKeyProtocolMediaTypeMax);
-        break;
-      case webrtc::kEnumCounterSdpFormatReceived:
-        UMA_HISTOGRAM_ENUMERATION(
-            "WebRTC.PeerConnection.SdpFormatReceived",
-            static_cast<webrtc::SdpFormatReceived>(counter),
-            webrtc::kSdpFormatReceivedMax);
-        break;
-      default:
-        // The default clause is expected to be reached when new enum types are
-        // added. Log, but only once.
-        {
-          static bool log_once = false;
-          LOG_IF(ERROR, !log_once) << "New WebRTC enum counter found: "
-                                   << static_cast<int>(counter_type);
-          log_once = true;
-        }
-        break;
-    }
-  }
-
-  void IncrementSparseEnumCounter(
-      webrtc::PeerConnectionEnumCounterType counter_type,
-      int counter) override {
-    switch (counter_type) {
-      case webrtc::kEnumCounterAudioSrtpCipher:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SrtpCryptoSuite.Audio",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterAudioSslCipher:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SslCipherSuite.Audio",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterVideoSrtpCipher:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SrtpCryptoSuite.Video",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterVideoSslCipher:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SslCipherSuite.Video",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterDataSrtpCipher:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SrtpCryptoSuite.Data",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterDataSslCipher:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SslCipherSuite.Data",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterSrtpUnprotectError:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SrtpUnprotectError",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterSrtcpUnprotectError:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.SrtcpUnprotectError",
-                                 counter);
-        break;
-      case webrtc::kEnumCounterUsagePattern:
-        base::UmaHistogramSparse("WebRTC.PeerConnection.UsagePattern", counter);
-        break;
-      default:
-        // The default clause is expected to be reached when new enum types are
-        // added. Log, but only once.
-        {
-          static bool log_once = false;
-          LOG_IF(ERROR, !log_once) << "New WebRTC sparse enum counter found: "
-                                   << static_cast<int>(counter_type);
-          log_once = true;
-        }
-        break;
-    }
-  }
-
-  void AddHistogramSample(webrtc::PeerConnectionUMAMetricsName type,
-                          int value) override {
-    // Runs on libjingle's signaling thread.
-    switch (type) {
-      case webrtc::kTimeToConnect:
-        UMA_HISTOGRAM_MEDIUM_TIMES(
-            "WebRTC.PeerConnection.TimeToConnect",
-            base::TimeDelta::FromMilliseconds(value));
-        break;
-      case webrtc::kNetworkInterfaces_IPv4:
-        UMA_HISTOGRAM_COUNTS_100("WebRTC.PeerConnection.IPv4Interfaces",
-                                 value);
-        break;
-      case webrtc::kNetworkInterfaces_IPv6:
-        UMA_HISTOGRAM_COUNTS_100("WebRTC.PeerConnection.IPv6Interfaces",
-                                 value);
-        break;
-      default:
-        // The default clause is expected to be reached when new enum types are
-        // added. Log, but only once.
-        {
-          static bool log_once = false;
-          LOG_IF(ERROR, !log_once) << "New WebRTC histogram counter found: "
-                                   << static_cast<int>(type);
-          log_once = true;
-        }
-        break;
-    }
-  }
-};
-
 void ConvertOfferOptionsToWebrtcOfferOptions(
     const blink::WebRTCOfferOptions& options,
     webrtc::PeerConnectionInterface::RTCOfferAnswerOptions* output) {
@@ -1316,9 +1162,6 @@
                                                      options, frame_);
   }
 
-  uma_observer_ = new rtc::RefCountedObject<PeerConnectionUMAObserver>();
-  native_peer_connection_->RegisterUMAObserver(uma_observer_.get());
-
   UMA_HISTOGRAM_ENUMERATION(
       "WebRTC.PeerConnection.SdpSemanticRequested",
       GetSdpSemanticRequested(server_configuration.sdp_semantics),
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.h b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
index 0865b854..07562e8c 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.h
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.h
@@ -300,7 +300,6 @@
   // To make sure the observers are released after native_peer_connection_,
   // they have to come first.
   scoped_refptr<Observer> peer_connection_observer_;
-  scoped_refptr<webrtc::UMAObserver> uma_observer_;
 
   // |native_peer_connection_| is the libjingle native PeerConnection object.
   scoped_refptr<webrtc::PeerConnectionInterface> native_peer_connection_;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index cb17517..401eca8 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -483,7 +483,10 @@
   }
 
   if (is_mac) {
-    deps += [ "//ui/accelerated_widget_mac" ]
+    deps += [
+      "//sandbox/mac:seatbelt",
+      "//ui/accelerated_widget_mac",
+    ]
   }
 }
 
@@ -1954,10 +1957,6 @@
     if (is_mac) {
       sources += [ "../browser/media/capture/cursor_renderer_mac_unittest.mm" ]
     }
-    if (is_chromeos) {
-      sources +=
-          [ "../browser/media/capture/desktop_capture_device_aura_unittest.cc" ]
-    }
   }
 
   if (is_linux) {
diff --git a/device/fido/fake_fido_discovery_unittest.cc b/device/fido/fake_fido_discovery_unittest.cc
index 9858dcb..d3a8c37 100644
--- a/device/fido/fake_fido_discovery_unittest.cc
+++ b/device/fido/fake_fido_discovery_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "device/fido/fido_test_data.h"
 #include "device/fido/mock_fido_device.h"
 #include "device/fido/mock_fido_discovery_observer.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -117,8 +118,15 @@
 
   auto device0 = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device0, GetId()).WillOnce(::testing::Return("device0"));
-  EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_));
+  device0->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
+  base::RunLoop device0_done;
+  EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_))
+      .WillOnce(testing::InvokeWithoutArgs(
+          [&device0_done]() { device0_done.Quit(); }));
   discovery.AddDevice(std::move(device0));
+  device0_done.Run();
   ::testing::Mock::VerifyAndClearExpectations(&observer);
 
   EXPECT_CALL(observer, DiscoveryStarted(&discovery, true));
@@ -127,8 +135,15 @@
 
   auto device1 = std::make_unique<MockFidoDevice>();
   EXPECT_CALL(*device1, GetId()).WillOnce(::testing::Return("device1"));
-  EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_));
+  device1->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
+  base::RunLoop device1_done;
+  EXPECT_CALL(observer, DeviceAdded(&discovery, ::testing::_))
+      .WillOnce(testing::InvokeWithoutArgs(
+          [&device1_done]() { device1_done.Quit(); }));
   discovery.AddDevice(std::move(device1));
+  device1_done.Run();
   ::testing::Mock::VerifyAndClearExpectations(&observer);
 }
 
diff --git a/device/fido/fido_authenticator.h b/device/fido/fido_authenticator.h
index 1eddfe5..258c9bf6 100644
--- a/device/fido/fido_authenticator.h
+++ b/device/fido/fido_authenticator.h
@@ -16,7 +16,7 @@
 
 namespace device {
 
-class AuthenticatorSelectionCriteria;
+class AuthenticatorSupportedOptions;
 class CtapGetAssertionRequest;
 class CtapMakeCredentialRequest;
 
@@ -36,13 +36,13 @@
   virtual ~FidoAuthenticator() = default;
 
   virtual void MakeCredential(
-      AuthenticatorSelectionCriteria authenticator_selection_criteria,
       CtapMakeCredentialRequest request,
       MakeCredentialCallback callback) = 0;
   virtual void GetAssertion(CtapGetAssertionRequest request,
                             GetAssertionCallback callback) = 0;
   virtual void Cancel() = 0;
   virtual std::string GetId() const = 0;
+  virtual const AuthenticatorSupportedOptions& Options() const = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FidoAuthenticator);
diff --git a/device/fido/fido_device.cc b/device/fido/fido_device.cc
index 1646e652..e3f448c 100644
--- a/device/fido/fido_device.cc
+++ b/device/fido/fido_device.cc
@@ -6,11 +6,56 @@
 
 #include <utility>
 
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "device/base/features.h"
+#include "device/fido/ctap_empty_authenticator_request.h"
+#include "device/fido/device_response_converter.h"
+#include "device/fido/fido_constants.h"
+
 namespace device {
 
 FidoDevice::FidoDevice() = default;
 FidoDevice::~FidoDevice() = default;
 
+void FidoDevice::DiscoverSupportedProtocolAndDeviceInfo(
+    base::OnceClosure done) {
+  if (base::FeatureList::IsEnabled(kNewCtap2Device)) {
+    // Set the protocol version to CTAP2 for the purpose of sending the GetInfo
+    // request. The correct value will be set in the callback based on the
+    // device response.
+    supported_protocol_ = ProtocolVersion::kCtap;
+    DeviceTransact(AuthenticatorGetInfoRequest().Serialize(),
+                   base::BindOnce(&FidoDevice::OnDeviceInfoReceived,
+                                  GetWeakPtr(), std::move(done)));
+  } else {
+    supported_protocol_ = ProtocolVersion::kU2f;
+    std::move(done).Run();
+  }
+}
+
+bool FidoDevice::SupportedProtocolIsInitialized() {
+  return (supported_protocol_ == ProtocolVersion::kU2f && !device_info_) ||
+         (supported_protocol_ == ProtocolVersion::kCtap && device_info_);
+}
+
+void FidoDevice::OnDeviceInfoReceived(
+    base::OnceClosure done,
+    base::Optional<std::vector<uint8_t>> response) {
+  state_ = FidoDevice::State::kReady;
+
+  base::Optional<AuthenticatorGetInfoResponse> get_info_response =
+      response ? ReadCTAPGetInfoResponse(*response) : base::nullopt;
+  if (!get_info_response || !base::ContainsKey(get_info_response->versions(),
+                                               ProtocolVersion::kCtap)) {
+    supported_protocol_ = ProtocolVersion::kU2f;
+  } else {
+    supported_protocol_ = ProtocolVersion::kCtap;
+    device_info_ = std::move(*get_info_response);
+  }
+  std::move(done).Run();
+}
+
 void FidoDevice::SetDeviceInfo(AuthenticatorGetInfoResponse device_info) {
   device_info_ = std::move(device_info);
 }
diff --git a/device/fido/fido_device.h b/device/fido/fido_device.h
index 28ffef7..defeec5 100644
--- a/device/fido/fido_device.h
+++ b/device/fido/fido_device.h
@@ -21,6 +21,11 @@
 namespace device {
 
 // Device abstraction for an individual CTAP1.0/CTAP2.0 device.
+//
+// Devices are instantiated with an unknown protocol version. Users should call
+// |DiscoverSupportedProtocolAndDeviceInfo| to determine a device's
+// capabilities and initialize the instance accordingly. Instances returned by
+// |FidoDiscovery| are already fully initialized.
 class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
  public:
   using WinkCallback = base::OnceClosure;
@@ -41,11 +46,18 @@
   virtual void Cancel() = 0;
   virtual std::string GetId() const = 0;
 
-  void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
+  // Sends a speculative AuthenticatorGetInfo request to determine whether the
+  // device supports the CTAP2 protocol, and initializes supported_protocol_
+  // and device_info_ according to the result (unless the
+  // device::kNewCtap2Device feature is off, in which case U2F is assumed).
+  void DiscoverSupportedProtocolAndDeviceInfo(base::OnceClosure done);
+  // Returns whether supported_protocol has been correctly initialized (usually
+  // by calling DiscoverSupportedProtocolAndDeviceInfo).
+  bool SupportedProtocolIsInitialized();
+  // TODO(martinkr): Rename to "SetSupportedProtocolForTesting".
   void set_supported_protocol(ProtocolVersion supported_protocol) {
     supported_protocol_ = supported_protocol;
   }
-  void set_state(State state) { state_ = state; }
 
   ProtocolVersion supported_protocol() const { return supported_protocol_; }
   const base::Optional<AuthenticatorGetInfoResponse>& device_info() const {
@@ -56,6 +68,10 @@
  protected:
   virtual base::WeakPtr<FidoDevice> GetWeakPtr() = 0;
 
+  void OnDeviceInfoReceived(base::OnceClosure done,
+                            base::Optional<std::vector<uint8_t>> response);
+  void SetDeviceInfo(AuthenticatorGetInfoResponse device_info);
+
   State state_ = State::kInit;
   ProtocolVersion supported_protocol_ = ProtocolVersion::kUnknown;
   base::Optional<AuthenticatorGetInfoResponse> device_info_;
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index c8aa469..93071ce 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -6,7 +6,8 @@
 
 #include <utility>
 
-#include "device/fido/authenticator_selection_criteria.h"
+#include "base/logging.h"
+#include "device/fido/authenticator_supported_options.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/fido_device.h"
@@ -16,19 +17,18 @@
 namespace device {
 
 FidoDeviceAuthenticator::FidoDeviceAuthenticator(FidoDevice* device)
-    : device_(device) {}
+    : device_(device) {
+  DCHECK(device_->SupportedProtocolIsInitialized());
+}
 FidoDeviceAuthenticator::~FidoDeviceAuthenticator() = default;
 
-void FidoDeviceAuthenticator::MakeCredential(
-    AuthenticatorSelectionCriteria authenticator_selection_criteria,
-    CtapMakeCredentialRequest request,
-    MakeCredentialCallback callback) {
+void FidoDeviceAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
+                                             MakeCredentialCallback callback) {
   DCHECK(!task_);
   // TODO(martinkr): Change FidoTasks to take all request parameters by const
   // reference, so we can avoid copying these from the RequestHandler.
-  task_ = std::make_unique<MakeCredentialTask>(
-      device_, std::move(request), std::move(authenticator_selection_criteria),
-      std::move(callback));
+  task_ = std::make_unique<MakeCredentialTask>(device_, std::move(request),
+                                               std::move(callback));
 }
 
 void FidoDeviceAuthenticator::GetAssertion(CtapGetAssertionRequest request,
@@ -48,6 +48,21 @@
   return device_->GetId();
 }
 
+const AuthenticatorSupportedOptions& FidoDeviceAuthenticator::Options() const {
+  static const AuthenticatorSupportedOptions default_options;
+  switch (device_->supported_protocol()) {
+    case ProtocolVersion::kU2f:
+      return default_options;
+    case ProtocolVersion::kCtap:
+      DCHECK(device_->device_info()) << "uninitialized device";
+      return device_->device_info()->options();
+    case ProtocolVersion::kUnknown:
+      NOTREACHED() << "uninitialized device";
+  }
+  NOTREACHED();
+  return default_options;
+}
+
 void FidoDeviceAuthenticator::SetTaskForTesting(
     std::unique_ptr<FidoTask> task) {
   task_ = std::move(task);
diff --git a/device/fido/fido_device_authenticator.h b/device/fido/fido_device_authenticator.h
index 29db9d81..ec62fef 100644
--- a/device/fido/fido_device_authenticator.h
+++ b/device/fido/fido_device_authenticator.h
@@ -15,7 +15,6 @@
 
 namespace device {
 
-class AuthenticatorSelectionCriteria;
 class CtapGetAssertionRequest;
 class CtapMakeCredentialRequest;
 class FidoDevice;
@@ -32,13 +31,13 @@
 
   // FidoAuthenticator:
   void MakeCredential(
-      AuthenticatorSelectionCriteria authenticator_selection_criteria,
       CtapMakeCredentialRequest request,
       MakeCredentialCallback callback) override;
   void GetAssertion(CtapGetAssertionRequest request,
                     GetAssertionCallback callback) override;
   void Cancel() override;
   std::string GetId() const override;
+  const AuthenticatorSupportedOptions& Options() const override;
 
  protected:
   void OnCtapMakeCredentialResponseReceived(
diff --git a/device/fido/fido_discovery.cc b/device/fido/fido_discovery.cc
index 299bbf05..98d4f67 100644
--- a/device/fido/fido_discovery.cc
+++ b/device/fido/fido_discovery.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "build/build_config.h"
 #include "device/fido/fido_ble_discovery.h"
 #include "device/fido/fido_device.h"
@@ -67,7 +68,7 @@
 }
 
 FidoDiscovery::FidoDiscovery(FidoTransportProtocol transport)
-    : transport_(transport) {}
+    : transport_(transport), weak_factory_(this) {}
 
 FidoDiscovery::~FidoDiscovery() = default;
 
@@ -132,9 +133,15 @@
 bool FidoDiscovery::AddDevice(std::unique_ptr<FidoDevice> device) {
   std::string device_id = device->GetId();
   const auto result = devices_.emplace(std::move(device_id), std::move(device));
-  if (result.second)
-    NotifyDeviceAdded(result.first->second.get());
-  return result.second;
+  if (!result.second) {
+    return false;  // Duplicate device id.
+  }
+  FidoDevice* device_ptr = result.first->second.get();
+  // Determine the device protocol version before notifying observers.
+  device_ptr->DiscoverSupportedProtocolAndDeviceInfo(
+      base::BindOnce(&FidoDiscovery::NotifyDeviceAdded,
+                     weak_factory_.GetWeakPtr(), device_ptr));
+  return true;
 }
 
 bool FidoDiscovery::RemoveDevice(base::StringPiece device_id) {
diff --git a/device/fido/fido_discovery.h b/device/fido/fido_discovery.h
index a3c31b5..68aadcc9 100644
--- a/device/fido/fido_discovery.h
+++ b/device/fido/fido_discovery.h
@@ -49,6 +49,11 @@
     // before the client of FidoDiscovery calls FidoDiscovery::Start(). However,
     // for devices already known to the system at that point, DeviceAdded()
     // might already be called to reported already known devices.
+    //
+    // The supplied FidoDevice instance is guaranteed to have its protocol
+    // version initialized. I.e., FidoDiscovery calls
+    // FidoDevice::DiscoverSupportedProtocolAndDeviceInfo() before notifying
+    // the Observer.
     virtual void DeviceAdded(FidoDiscovery* discovery, FidoDevice* device) = 0;
     virtual void DeviceRemoved(FidoDiscovery* discovery,
                                FidoDevice* device) = 0;
@@ -112,6 +117,7 @@
 
   const FidoTransportProtocol transport_;
   State state_ = State::kIdle;
+  base::WeakPtrFactory<FidoDiscovery> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(FidoDiscovery);
 };
diff --git a/device/fido/fido_discovery_unittest.cc b/device/fido/fido_discovery_unittest.cc
index 51b873e..922c559 100644
--- a/device/fido/fido_discovery_unittest.cc
+++ b/device/fido/fido_discovery_unittest.cc
@@ -7,6 +7,9 @@
 #include <utility>
 
 #include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "device/fido/fido_test_data.h"
 #include "device/fido/mock_fido_device.h"
 #include "device/fido/mock_fido_discovery_observer.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -90,6 +93,7 @@
 }
 
 TEST(FidoDiscoveryTest, TestAddRemoveDevices) {
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy);
   MockFidoDiscoveryObserver observer;
   discovery.set_observer(&observer);
@@ -98,18 +102,37 @@
   // Expect successful insertion.
   auto device0 = std::make_unique<MockFidoDevice>();
   auto* device0_raw = device0.get();
-  EXPECT_CALL(observer, DeviceAdded(&discovery, device0_raw));
+  device0->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
+  base::RunLoop device0_done;
+  EXPECT_CALL(observer, DeviceAdded(&discovery, device0_raw))
+      .WillOnce(testing::InvokeWithoutArgs(
+          [&device0_done]() { device0_done.Quit(); }));
   EXPECT_CALL(*device0, GetId()).WillOnce(Return("device0"));
   EXPECT_TRUE(discovery.AddDevice(std::move(device0)));
+  device0_done.Run();
   ::testing::Mock::VerifyAndClearExpectations(&observer);
+  // Device should have been initialized as a CTAP device.
+  EXPECT_EQ(ProtocolVersion::kCtap, device0_raw->supported_protocol());
+  EXPECT_TRUE(device0_raw->device_info());
 
-  // // Expect successful insertion.
+  // Expect successful insertion.
+  base::RunLoop device1_done;
   auto device1 = std::make_unique<MockFidoDevice>();
   auto* device1_raw = device1.get();
-  EXPECT_CALL(observer, DeviceAdded(&discovery, device1_raw));
+  device1->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+  EXPECT_CALL(observer, DeviceAdded(&discovery, device1_raw))
+      .WillOnce(testing::InvokeWithoutArgs(
+          [&device1_done]() { device1_done.Quit(); }));
   EXPECT_CALL(*device1, GetId()).WillOnce(Return("device1"));
   EXPECT_TRUE(discovery.AddDevice(std::move(device1)));
+  device1_done.Run();
   ::testing::Mock::VerifyAndClearExpectations(&observer);
+  // Device should have been initialized as a U2F device.
+  EXPECT_EQ(ProtocolVersion::kU2f, device1_raw->supported_protocol());
+  EXPECT_FALSE(device1_raw->device_info());
 
   // Inserting a device with an already present id should be prevented.
   auto device1_dup = std::make_unique<MockFidoDevice>();
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index 09a7905..7adbfdd 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -92,10 +92,6 @@
 void FidoRequestHandlerBase::DeviceAdded(FidoDiscovery* discovery,
                                          FidoDevice* device) {
   DCHECK(!base::ContainsKey(active_authenticators(), device->GetId()));
-  // All devices are initially assumed to support CTAP protocol and thus
-  // AuthenticatorGetInfo command is sent to all connected devices. If device
-  // errors out, then it is assumed to support U2F protocol.
-  device->set_supported_protocol(ProtocolVersion::kCtap);
   AddAuthenticator(CreateAuthenticatorFromDevice(device));
 }
 
diff --git a/device/fido/fido_request_handler_unittest.cc b/device/fido/fido_request_handler_unittest.cc
index a8fddbe0..c497d0a 100644
--- a/device/fido/fido_request_handler_unittest.cc
+++ b/device/fido/fido_request_handler_unittest.cc
@@ -180,6 +180,8 @@
   discovery()->WaitForCallToStartAndSimulateSuccess();
 
   auto device = std::make_unique<MockFidoDevice>();
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
   EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
   // Device returns success response.
   device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
@@ -200,12 +202,16 @@
   discovery()->WaitForCallToStartAndSimulateSuccess();
 
   auto device0 = std::make_unique<MockFidoDevice>();
-  device0->set_supported_protocol(ProtocolVersion::kCtap);
+  device0->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
   device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
   EXPECT_CALL(*device0, Cancel());
   auto device1 = std::make_unique<MockFidoDevice>();
-  device1->set_supported_protocol(ProtocolVersion::kCtap);
+  device1->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
   device1->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
   EXPECT_CALL(*device1, Cancel());
@@ -224,7 +230,9 @@
 
   // Represents a connected device that hangs without a response.
   auto device0 = std::make_unique<MockFidoDevice>();
-  device0->set_supported_protocol(ProtocolVersion::kCtap);
+  device0->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
   // Device is unresponsive and cancel command is invoked afterwards.
   device0->ExpectRequestAndDoNotRespond(std::vector<uint8_t>());
@@ -232,7 +240,9 @@
 
   // Represents a connected device that response successfully.
   auto device1 = std::make_unique<MockFidoDevice>();
-  device1->set_supported_protocol(ProtocolVersion::kCtap);
+  device1->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
   device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
                                        CreateFakeSuccessDeviceResponse());
@@ -255,7 +265,9 @@
   // Represents a connected device that responds successfully after small time
   // delay.
   auto device0 = std::make_unique<MockFidoDevice>();
-  device0->set_supported_protocol(ProtocolVersion::kCtap);
+  device0->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
   device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
                                        CreateFakeSuccessDeviceResponse(),
@@ -264,7 +276,9 @@
   // Represents a device that returns a success response after a longer time
   // delay.
   auto device1 = std::make_unique<MockFidoDevice>();
-  device1->set_supported_protocol(ProtocolVersion::kCtap);
+  device1->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
   device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
                                        CreateFakeSuccessDeviceResponse(),
@@ -294,6 +308,9 @@
   // Represents a connected device that immediately responds with a processing
   // error.
   auto device0 = std::make_unique<MockFidoDevice>();
+  device0->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
   device0->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
                                        CreateFakeDeviceProcesssingError());
@@ -301,6 +318,9 @@
   // Represents a device that returns an UP verified failure response after a
   // small time delay.
   auto device1 = std::make_unique<MockFidoDevice>();
+  device1->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
   device1->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
                                        CreateFakeUserPresenceVerifiedError(),
@@ -309,7 +329,9 @@
   // Represents a device that returns an UP verified failure response after a
   // big time delay.
   auto device2 = std::make_unique<MockFidoDevice>();
-  device2->set_supported_protocol(ProtocolVersion::kCtap);
+  device2->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
   EXPECT_CALL(*device2, GetId()).WillRepeatedly(testing::Return("device2"));
   device2->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
                                        CreateFakeDeviceProcesssingError(),
@@ -333,7 +355,7 @@
   // A platform authenticator usually wouldn't usually use a FidoDevice, but
   // that's not the point of the test here. The test is only trying to ensure
   // the authenticator gets injected and used.
-  auto device = std::make_unique<MockFidoDevice>();
+  auto device = MockFidoDevice::MakeCtap();
   EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
   // Device returns success response.
   device->ExpectRequestAndRespondWith(std::vector<uint8_t>(),
diff --git a/device/fido/fido_task.cc b/device/fido/fido_task.cc
index 5debc7c..80ba6505 100644
--- a/device/fido/fido_task.cc
+++ b/device/fido/fido_task.cc
@@ -9,14 +9,13 @@
 #include "base/bind.h"
 #include "base/stl_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "device/fido/ctap_empty_authenticator_request.h"
-#include "device/fido/device_response_converter.h"
 #include "device/fido/fido_constants.h"
 
 namespace device {
 
 FidoTask::FidoTask(FidoDevice* device) : device_(device), weak_factory_(this) {
   DCHECK(device_);
+  DCHECK(device_->SupportedProtocolIsInitialized());
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&FidoTask::StartTask, weak_factory_.GetWeakPtr()));
@@ -31,35 +30,4 @@
   device()->Cancel();
 }
 
-void FidoTask::GetAuthenticatorInfo(base::OnceClosure ctap_callback,
-                                    base::OnceClosure u2f_callback) {
-  // When AuthenticatorInfo command is sent to authenticators, we first assume
-  // that the connected device is a CTAP2 device.
-  device_->set_supported_protocol(ProtocolVersion::kCtap);
-  device()->DeviceTransact(
-      AuthenticatorGetInfoRequest().Serialize(),
-      base::BindOnce(&FidoTask::OnAuthenticatorInfoReceived,
-                     weak_factory_.GetWeakPtr(), std::move(ctap_callback),
-                     std::move(u2f_callback)));
-}
-
-void FidoTask::OnAuthenticatorInfoReceived(
-    base::OnceClosure ctap_callback,
-    base::OnceClosure u2f_callback,
-    base::Optional<std::vector<uint8_t>> response) {
-  device()->set_state(FidoDevice::State::kReady);
-
-  base::Optional<AuthenticatorGetInfoResponse> get_info_response;
-  if (!response || !(get_info_response = ReadCTAPGetInfoResponse(*response)) ||
-      !base::ContainsKey(get_info_response->versions(),
-                         ProtocolVersion::kCtap)) {
-    device()->set_supported_protocol(ProtocolVersion::kU2f);
-    std::move(u2f_callback).Run();
-    return;
-  }
-
-  device()->SetDeviceInfo(std::move(*get_info_response));
-  std::move(ctap_callback).Run();
-}
-
 }  // namespace device
diff --git a/device/fido/fido_task.h b/device/fido/fido_task.h
index 773b145b..b2ca94b 100644
--- a/device/fido/fido_task.h
+++ b/device/fido/fido_task.h
@@ -13,7 +13,6 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/optional.h"
 #include "device/fido/fido_device.h"
 
 namespace device {
@@ -42,27 +41,12 @@
   // Asynchronously initiates CTAP request operation for a single device.
   virtual void StartTask() = 0;
 
-  // Invokes the AuthenticatorGetInfo method on |device_|. If successful and a
-  // well formed response is received, then |device_| is deemed to support CTAP
-  // protocol and |ctap_callback| is invoked, which sends CBOR encoded command
-  // to the authenticator. For all failure cases, |device_| is assumed to
-  // support the U2F protocol as FidoDiscovery selects only devices that support
-  // either the U2F or CTAP protocols during discovery. Therefore |u2f_callback|
-  // is invoked, which sends APDU encoded request to authenticator.
-  void GetAuthenticatorInfo(base::OnceClosure ctap_callback,
-                            base::OnceClosure u2f_callback);
-
   FidoDevice* device() const {
     DCHECK(device_);
     return device_;
   }
 
  private:
-  void OnAuthenticatorInfoReceived(
-      base::OnceClosure ctap_callback,
-      base::OnceClosure u2f_callback,
-      base::Optional<std::vector<uint8_t>> response);
-
   FidoDevice* const device_;
   base::WeakPtrFactory<FidoTask> weak_factory_;
 
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index c97955e..ed8267e 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -38,19 +38,24 @@
   }
 
   std::unique_ptr<GetAssertionRequestHandler> CreateGetAssertionHandler() {
-    ForgeNextHidDiscovery();
-
-    CtapGetAssertionRequest request_param(test_data::kRelyingPartyId,
-                                          test_data::kClientDataHash);
-    request_param.SetAllowList(
+    CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                    test_data::kClientDataHash);
+    request.SetAllowList(
         {{CredentialType::kPublicKey,
           fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
 
+    return CreateGetAssertionHandlerWithRequest(std::move(request));
+  }
+
+  std::unique_ptr<GetAssertionRequestHandler>
+  CreateGetAssertionHandlerWithRequest(CtapGetAssertionRequest request) {
+    ForgeNextHidDiscovery();
+
     return std::make_unique<GetAssertionRequestHandler>(
         nullptr /* connector */,
         base::flat_set<FidoTransportProtocol>(
             {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
-        std::move(request_param), get_assertion_cb_.callback());
+        std::move(request), get_assertion_cb_.callback());
   }
 
   void InitFeatureListAndDisableCtapFlag() {
@@ -138,4 +143,47 @@
   EXPECT_TRUE(request_handler->is_complete());
 }
 
+TEST_F(FidoGetAssertionHandlerTest, TestIncompatibleUserVerificationSetting) {
+  auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
+                                         test_data::kClientDataHash);
+  request.SetUserVerification(UserVerificationRequirement::kRequired);
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestGetInfoResponseWithoutUvSupport);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
+TEST_F(FidoGetAssertionHandlerTest,
+       TestU2fSignRequestWithUserVerificationRequired) {
+  auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
+                                         test_data::kClientDataHash);
+  request.SetAllowList(
+      {{CredentialType::kPublicKey,
+        fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
+  request.SetUserVerification(UserVerificationRequirement::kRequired);
+  auto request_handler =
+      CreateGetAssertionHandlerWithRequest(std::move(request));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(get_assertion_callback().was_called());
+}
+
 }  // namespace device
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index 6e9ce8f5..dcf5aa0 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -51,10 +51,54 @@
 
 GetAssertionRequestHandler::~GetAssertionRequestHandler() = default;
 
+namespace {
+
+// Checks UserVerificationRequirement enum passed from the relying party is
+// compatible with the authenticator, and updates the request to the
+// "effective" user verification requirement.
+// https://w3c.github.io/webauthn/#effective-user-verification-requirement-for-assertion
+bool CheckUserVerificationCompatible(FidoAuthenticator* authenticator,
+                                     CtapGetAssertionRequest* request) {
+  const auto uv_availability =
+      authenticator->Options().user_verification_availability();
+
+  switch (request->user_verification()) {
+    case UserVerificationRequirement::kRequired:
+      return uv_availability ==
+             AuthenticatorSupportedOptions::UserVerificationAvailability::
+                 kSupportedAndConfigured;
+
+    case UserVerificationRequirement::kDiscouraged:
+      return true;
+
+    case UserVerificationRequirement::kPreferred:
+      if (uv_availability ==
+          AuthenticatorSupportedOptions::UserVerificationAvailability::
+              kSupportedAndConfigured) {
+        request->SetUserVerification(UserVerificationRequirement::kRequired);
+      } else {
+        request->SetUserVerification(UserVerificationRequirement::kDiscouraged);
+      }
+      return true;
+  }
+
+  NOTREACHED();
+  return false;
+}
+
+}  // namespace
+
 void GetAssertionRequestHandler::DispatchRequest(
     FidoAuthenticator* authenticator) {
+  // The user verification field of the request may be adjusted to the
+  // authenticator, so we need to make a copy.
+  CtapGetAssertionRequest request_copy = request_;
+  if (!CheckUserVerificationCompatible(authenticator, &request_copy)) {
+    return;
+  }
+
   authenticator->GetAssertion(
-      request_,
+      std::move(request_copy),
       base::BindOnce(&GetAssertionRequestHandler::OnAuthenticatorResponse,
                      weak_factory_.GetWeakPtr(), authenticator));
 }
diff --git a/device/fido/get_assertion_task.cc b/device/fido/get_assertion_task.cc
index 7cae6313..e381b64 100644
--- a/device/fido/get_assertion_task.cc
+++ b/device/fido/get_assertion_task.cc
@@ -43,23 +43,15 @@
 GetAssertionTask::~GetAssertionTask() = default;
 
 void GetAssertionTask::StartTask() {
-  if (base::FeatureList::IsEnabled(kNewCtap2Device)) {
-    GetAuthenticatorInfo(
-        base::BindOnce(&GetAssertionTask::GetAssertion,
-                       weak_factory_.GetWeakPtr()),
-        base::BindOnce(&GetAssertionTask::U2fSign, weak_factory_.GetWeakPtr()));
+  if (base::FeatureList::IsEnabled(kNewCtap2Device) &&
+      device()->supported_protocol() == ProtocolVersion::kCtap) {
+    GetAssertion();
   } else {
     U2fSign();
   }
 }
 
 void GetAssertionTask::GetAssertion() {
-  if (!CheckUserVerificationCompatible()) {
-    std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             base::nullopt);
-    return;
-  }
-
   sign_operation_ =
       std::make_unique<Ctap2DeviceOperation<CtapGetAssertionRequest,
                                             AuthenticatorGetAssertionResponse>>(
@@ -72,13 +64,7 @@
 
 void GetAssertionTask::U2fSign() {
   DCHECK(!device()->device_info());
-  device()->set_supported_protocol(ProtocolVersion::kU2f);
-
-  if (!CheckUserVerificationCompatible()) {
-    std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             base::nullopt);
-    return;
-  }
+  DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
 
   sign_operation_ = std::make_unique<U2fSignOperation>(
       device(), request_,
@@ -134,6 +120,8 @@
     return;
   }
 
+  // TODO(martinkr): CheckRpIdHash invocation needs to move into the Request
+  // handler. See https://crbug.com/863988.
   if (!device_response || !device_response->CheckRpIdHash(request_.rp_id()) ||
       !CheckRequirementsOnReturnedCredentialId(*device_response) ||
       !CheckRequirementsOnReturnedUserEntities(*device_response)) {
@@ -145,37 +133,4 @@
   std::move(callback_).Run(response_code, std::move(device_response));
 }
 
-bool GetAssertionTask::CheckUserVerificationCompatible() {
-  if (!device()->device_info()) {
-    return request_.user_verification() !=
-           UserVerificationRequirement::kRequired;
-  }
-
-  const auto uv_availability =
-      device()->device_info()->options().user_verification_availability();
-
-  switch (request_.user_verification()) {
-    case UserVerificationRequirement::kRequired:
-      return uv_availability ==
-             AuthenticatorSupportedOptions::UserVerificationAvailability::
-                 kSupportedAndConfigured;
-
-    case UserVerificationRequirement::kDiscouraged:
-      return true;
-
-    case UserVerificationRequirement::kPreferred:
-      if (uv_availability ==
-          AuthenticatorSupportedOptions::UserVerificationAvailability::
-              kSupportedAndConfigured) {
-        request_.SetUserVerification(UserVerificationRequirement::kRequired);
-      } else {
-        request_.SetUserVerification(UserVerificationRequirement::kDiscouraged);
-      }
-      return true;
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 }  // namespace device
diff --git a/device/fido/get_assertion_task.h b/device/fido/get_assertion_task.h
index cc8d666..b993d84 100644
--- a/device/fido/get_assertion_task.h
+++ b/device/fido/get_assertion_task.h
@@ -67,17 +67,6 @@
   bool CheckRequirementsOnReturnedCredentialId(
       const AuthenticatorGetAssertionResponse& response);
 
-  // Checks UserVerificationRequirement enum passed from the relying party
-  // is compatible with the authenticator using the following logic:
-  //  - If UserVerificationRequirement is set to kRequired, user verification
-  //    option parameter should be set to true.
-  //  - If UserVerificationRequirement is set to kPreferred, user verification
-  //    option is set to true only if the authenticator supports UV.
-  //  - If UserVerificationRequirement is set to kDiscouraged, user verification
-  //    is set to false.
-  // https://w3c.github.io/webauthn/#enumdef-userverificationrequirement
-  bool CheckUserVerificationCompatible();
-
   void OnCtapGetAssertionResponseReceived(
       CtapDeviceResponseCode response_code,
       base::Optional<AuthenticatorGetAssertionResponse> device_response);
diff --git a/device/fido/get_assertion_task_unittest.cc b/device/fido/get_assertion_task_unittest.cc
index fbc5792..87599e5 100644
--- a/device/fido/get_assertion_task_unittest.cc
+++ b/device/fido/get_assertion_task_unittest.cc
@@ -17,6 +17,7 @@
 #include "device/base/features.h"
 #include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/ctap_get_assertion_request.h"
+#include "device/fido/device_response_converter.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_test_data.h"
@@ -29,7 +30,6 @@
 using ::testing::_;
 
 namespace device {
-
 namespace {
 
 using TestGetAssertionTaskCallbackReceiver =
@@ -37,13 +37,9 @@
         CtapDeviceResponseCode,
         base::Optional<AuthenticatorGetAssertionResponse>>;
 
-}  // namespace
-
 class FidoGetAssertionTaskTest : public testing::Test {
  public:
-  FidoGetAssertionTaskTest() {
-    scoped_feature_list_.emplace();
-  }
+  FidoGetAssertionTaskTest() { scoped_feature_list_.emplace(); }
 
   TestGetAssertionTaskCallbackReceiver& get_assertion_callback_receiver() {
     return cb_;
@@ -61,10 +57,7 @@
 };
 
 TEST_F(FidoGetAssertionTaskTest, TestGetAssertionSuccess) {
-  auto device = std::make_unique<MockFidoDevice>();
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
+  auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorGetAssertion,
       test_data::kTestGetAssertionResponse);
@@ -83,14 +76,10 @@
   EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
             get_assertion_callback_receiver().status());
   EXPECT_TRUE(get_assertion_callback_receiver().value());
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
-  EXPECT_TRUE(device->device_info());
 }
 
 TEST_F(FidoGetAssertionTaskTest, TestU2fSignSuccess) {
-  auto device = std::make_unique<MockFidoDevice>();
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+  auto device = MockFidoDevice::MakeU2f();
   device->ExpectRequestAndRespondWith(
       test_data::kU2fCheckOnlySignCommandApdu,
       test_data::kApduEncodedNoErrorSignResponse);
@@ -112,8 +101,6 @@
   EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
             get_assertion_callback_receiver().status());
   EXPECT_TRUE(get_assertion_callback_receiver().value());
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
-  EXPECT_FALSE(device->device_info());
 }
 
 TEST_F(FidoGetAssertionTaskTest, TestSignSuccessWithFake) {
@@ -133,6 +120,9 @@
           std::move(private_key),
           fido_parsing_utils::CreateSHA256Hash(test_data::kRelyingPartyId),
           42 /* counter */));
+  test::TestCallbackReceiver<> done;
+  device->DiscoverSupportedProtocolAndDeviceInfo(done.callback());
+  done.WaitForCallback();
 
   auto task = std::make_unique<GetAssertionTask>(
       device.get(), std::move(request_param),
@@ -163,7 +153,7 @@
 
 TEST_F(FidoGetAssertionTaskTest, TestU2fSignWithoutFlag) {
   RemoveCtapFlag();
-  auto device = std::make_unique<MockFidoDevice>();
+  auto device = MockFidoDevice::MakeU2f();
   device->ExpectRequestAndRespondWith(
       test_data::kU2fCheckOnlySignCommandApdu,
       test_data::kApduEncodedNoErrorSignResponse);
@@ -185,18 +175,12 @@
   EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
             get_assertion_callback_receiver().status());
   EXPECT_TRUE(get_assertion_callback_receiver().value());
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
-  EXPECT_FALSE(device->device_info());
 }
 
 // Tests a scenario where the authenticator responds with credential ID that
 // is not included in the allowed list.
 TEST_F(FidoGetAssertionTaskTest, TestGetAssertionInvalidCredential) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
+  auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorGetAssertion,
       test_data::kTestGetAssertionResponse);
@@ -211,18 +195,12 @@
   EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
             get_assertion_callback_receiver().status());
   EXPECT_FALSE(get_assertion_callback_receiver().value());
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
-  EXPECT_TRUE(device->device_info());
 }
 
 // Tests a scenario where authenticator responds without user entity in its
 // response but client is expecting a resident key credential.
 TEST_F(FidoGetAssertionTaskTest, TestGetAsserionIncorrectUserEntity) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
+  auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorGetAssertion,
       test_data::kTestGetAssertionResponse);
@@ -234,19 +212,13 @@
       get_assertion_callback_receiver().callback());
 
   get_assertion_callback_receiver().WaitForCallback();
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
-  EXPECT_TRUE(device->device_info());
   EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
             get_assertion_callback_receiver().status());
   EXPECT_FALSE(get_assertion_callback_receiver().value());
 }
 
 TEST_F(FidoGetAssertionTaskTest, TestGetAsserionIncorrectRpIdHash) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
+  auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorGetAssertion,
       test_data::kTestGetAssertionResponseWithIncorrectRpIdHash);
@@ -258,19 +230,13 @@
       get_assertion_callback_receiver().callback());
 
   get_assertion_callback_receiver().WaitForCallback();
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
-  EXPECT_TRUE(device->device_info());
   EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
             get_assertion_callback_receiver().status());
   EXPECT_FALSE(get_assertion_callback_receiver().value());
 }
 
 TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
+  auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorGetAssertion, base::nullopt);
 
@@ -281,54 +247,6 @@
       get_assertion_callback_receiver().callback());
 
   get_assertion_callback_receiver().WaitForCallback();
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
-  EXPECT_TRUE(device->device_info());
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            get_assertion_callback_receiver().status());
-  EXPECT_FALSE(get_assertion_callback_receiver().value());
-}
-
-TEST_F(FidoGetAssertionTaskTest, TestIncompatibleUserVerificationSetting) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestGetInfoResponseWithoutUvSupport);
-
-  auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
-                                         test_data::kClientDataHash);
-  request.SetUserVerification(UserVerificationRequirement::kRequired);
-
-  auto task = std::make_unique<GetAssertionTask>(
-      device.get(), std::move(request),
-      get_assertion_callback_receiver().callback());
-
-  get_assertion_callback_receiver().WaitForCallback();
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kCtap);
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            get_assertion_callback_receiver().status());
-  EXPECT_FALSE(get_assertion_callback_receiver().value());
-}
-
-TEST_F(FidoGetAssertionTaskTest,
-       TestU2fSignRequestWithUserVerificationRequired) {
-  auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
-                                         test_data::kClientDataHash);
-  request.SetAllowList(
-      {{CredentialType::kPublicKey,
-        fido_parsing_utils::Materialize(test_data::kU2fSignKeyHandle)}});
-  request.SetUserVerification(UserVerificationRequirement::kRequired);
-
-  auto device = std::make_unique<MockFidoDevice>();
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
-  auto task = std::make_unique<GetAssertionTask>(
-      device.get(), std::move(request),
-      get_assertion_callback_receiver().callback());
-
-  get_assertion_callback_receiver().WaitForCallback();
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
   EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
             get_assertion_callback_receiver().status());
   EXPECT_FALSE(get_assertion_callback_receiver().value());
@@ -338,9 +256,7 @@
   auto request = CtapGetAssertionRequest(test_data::kRelyingPartyId,
                                          test_data::kClientDataHash);
 
-  auto device = std::make_unique<MockFidoDevice>();
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+  auto device = MockFidoDevice::MakeU2f();
   device->ExpectRequestAndRespondWith(
       test_data::kU2fFakeRegisterCommand,
       test_data::kApduEncodedNoErrorSignResponse);
@@ -350,10 +266,10 @@
       get_assertion_callback_receiver().callback());
 
   get_assertion_callback_receiver().WaitForCallback();
-  EXPECT_EQ(device->supported_protocol(), ProtocolVersion::kU2f);
   EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrCredentialNotValid,
             get_assertion_callback_receiver().status());
   EXPECT_FALSE(get_assertion_callback_receiver().value());
 }
 
+}  // namespace
 }  // namespace device
diff --git a/device/fido/mac/authenticator.h b/device/fido/mac/authenticator.h
index 4804525..5030f64 100644
--- a/device/fido/mac/authenticator.h
+++ b/device/fido/mac/authenticator.h
@@ -37,13 +37,13 @@
 
   // FidoAuthenticator
   void MakeCredential(
-      AuthenticatorSelectionCriteria authenticator_selection_criteria,
       CtapMakeCredentialRequest request,
       MakeCredentialCallback callback) override;
   void GetAssertion(CtapGetAssertionRequest request,
                     GetAssertionCallback callback) override;
   void Cancel() override;
   std::string GetId() const override;
+  const AuthenticatorSupportedOptions& Options() const override;
 
  private:
   TouchIdAuthenticator(std::string keychain_access_group,
diff --git a/device/fido/mac/authenticator.mm b/device/fido/mac/authenticator.mm
index acf005d..76f9d482 100644
--- a/device/fido/mac/authenticator.mm
+++ b/device/fido/mac/authenticator.mm
@@ -7,11 +7,12 @@
 #import <LocalAuthentication/LocalAuthentication.h>
 
 #include "base/feature_list.h"
+#include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/optional.h"
 #include "base/strings/string_piece.h"
 #include "device/base/features.h"
-#include "device/fido/authenticator_selection_criteria.h"
+#include "device/fido/authenticator_supported_options.h"
 #include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/fido_constants.h"
@@ -57,10 +58,8 @@
 
 TouchIdAuthenticator::~TouchIdAuthenticator() = default;
 
-void TouchIdAuthenticator::MakeCredential(
-    AuthenticatorSelectionCriteria authenticator_selection_criteria,
-    CtapMakeCredentialRequest request,
-    MakeCredentialCallback callback) {
+void TouchIdAuthenticator::MakeCredential(CtapMakeCredentialRequest request,
+                                          MakeCredentialCallback callback) {
   if (__builtin_available(macOS 10.12.2, *)) {
     DCHECK(!operation_);
     operation_ = std::make_unique<MakeCredentialOperation>(
@@ -97,6 +96,27 @@
   return "TouchIdAuthenticator";
 }
 
+namespace {
+
+AuthenticatorSupportedOptions TouchIdAuthenticatorOptions() {
+  AuthenticatorSupportedOptions options;
+  options.SetIsPlatformDevice(true);
+  options.SetSupportsResidentKey(true);
+  options.SetUserVerificationAvailability(
+      AuthenticatorSupportedOptions::UserVerificationAvailability::
+          kSupportedAndConfigured);
+  options.SetUserPresenceRequired(true);
+  return options;
+}
+
+}  // namespace
+
+const AuthenticatorSupportedOptions& TouchIdAuthenticator::Options() const {
+  static const AuthenticatorSupportedOptions options =
+      TouchIdAuthenticatorOptions();
+  return options;
+}
+
 TouchIdAuthenticator::TouchIdAuthenticator(std::string keychain_access_group,
                                            std::string metadata_secret)
     : keychain_access_group_(std::move(keychain_access_group)),
diff --git a/device/fido/mac/browsing_data_deletion_unittest.mm b/device/fido/mac/browsing_data_deletion_unittest.mm
index 7950267..b6acba5 100644
--- a/device/fido/mac/browsing_data_deletion_unittest.mm
+++ b/device/fido/mac/browsing_data_deletion_unittest.mm
@@ -14,7 +14,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "device/base/features.h"
-#include "device/fido/authenticator_selection_criteria.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/mac/authenticator.h"
@@ -141,8 +140,7 @@
     TestCallbackReceiver<CtapDeviceResponseCode,
                          base::Optional<AuthenticatorMakeCredentialResponse>>
         callback_receiver;
-    authenticator->MakeCredential(AuthenticatorSelectionCriteria(),
-                                  MakeRequest(), callback_receiver.callback());
+    authenticator->MakeCredential(MakeRequest(), callback_receiver.callback());
     callback_receiver.WaitForCallback();
     auto result = callback_receiver.TakeResult();
     return std::get<0>(result) == CtapDeviceResponseCode::kSuccess;
diff --git a/device/fido/make_credential_handler_unittest.cc b/device/fido/make_credential_handler_unittest.cc
index f884b1be..f5b277d 100644
--- a/device/fido/make_credential_handler_unittest.cc
+++ b/device/fido/make_credential_handler_unittest.cc
@@ -42,6 +42,13 @@
   }
 
   std::unique_ptr<MakeCredentialRequestHandler> CreateMakeCredentialHandler() {
+    return CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+        AuthenticatorSelectionCriteria());
+  }
+
+  std::unique_ptr<MakeCredentialRequestHandler>
+  CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+      AuthenticatorSelectionCriteria authenticator_selection_criteria) {
     ForgeNextHidDiscovery();
     PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
     PublicKeyCredentialUserEntity user(
@@ -57,8 +64,8 @@
         nullptr,
         base::flat_set<FidoTransportProtocol>(
             {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
-        std::move(request_parameter), AuthenticatorSelectionCriteria(),
-        cb_.callback());
+        std::move(request_parameter),
+        std::move(authenticator_selection_criteria), cb_.callback());
   }
 
   void InitFeatureListAndDisableCtapFlag() {
@@ -134,4 +141,181 @@
   EXPECT_TRUE(request_handler->is_complete());
 }
 
+TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithUserVerificationRequired) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kRequired));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+       U2fRegisterWithPlatformDeviceRequirement) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kPlatform,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kPreferred));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, U2fRegisterWithResidentKeyRequirement) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+              true /* require_resident_key */,
+              UserVerificationRequirement::kPreferred));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+       UserVerificationAuthenticatorSelectionCriteria) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kRequired));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestGetInfoResponseWithoutUvSupport);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+       PlatformDeviceAuthenticatorSelectionCriteria) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kPlatform,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kRequired));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestGetInfoResponseCrossPlatformDevice);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+       ResidentKeyAuthenticatorSelectionCriteria) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+              true /* require_resident_key */,
+              UserVerificationRequirement::kPreferred));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestGetInfoResponseWithoutResidentKeySupport);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest,
+       SatisfyAllAuthenticatorSelectionCriteria) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::
+                  kPlatform,
+              true /* require_resident_key */,
+              UserVerificationRequirement::kRequired));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestAuthenticatorGetInfoResponse);
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorMakeCredential,
+      test_data::kTestMakeCredentialResponse);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  callback().WaitForCallback();
+  EXPECT_EQ(FidoReturnCode::kSuccess, callback().status());
+}
+
+TEST_F(FidoMakeCredentialHandlerTest, IncompatibleUserVerificationSetting) {
+  auto request_handler =
+      CreateMakeCredentialHandlerWithAuthenticatorSelectionCriteria(
+          AuthenticatorSelectionCriteria(
+              AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
+              false /* require_resident_key */,
+              UserVerificationRequirement::kRequired));
+  discovery()->WaitForCallToStartAndSimulateSuccess();
+
+  auto device = std::make_unique<MockFidoDevice>();
+  EXPECT_CALL(*device, GetId()).WillRepeatedly(testing::Return("device0"));
+  device->ExpectCtap2CommandAndRespondWith(
+      CtapRequestCommand::kAuthenticatorGetInfo,
+      test_data::kTestGetInfoResponseWithoutUvSupport);
+
+  discovery()->AddDevice(std::move(device));
+
+  scoped_task_environment_.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(callback().was_called());
+}
+
 }  // namespace device
diff --git a/device/fido/make_credential_request_handler.cc b/device/fido/make_credential_request_handler.cc
index abe433e..804bb75 100644
--- a/device/fido/make_credential_request_handler.cc
+++ b/device/fido/make_credential_request_handler.cc
@@ -47,10 +47,58 @@
 
 MakeCredentialRequestHandler::~MakeCredentialRequestHandler() = default;
 
+namespace {
+
+bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
+    FidoAuthenticator* authenticator,
+    const AuthenticatorSelectionCriteria& authenticator_selection_criteria,
+    CtapMakeCredentialRequest* request) {
+  using AuthenticatorAttachment =
+      AuthenticatorSelectionCriteria::AuthenticatorAttachment;
+  using UvAvailability =
+      AuthenticatorSupportedOptions::UserVerificationAvailability;
+
+  const auto& options = authenticator->Options();
+  if ((authenticator_selection_criteria.authenticator_attachement() ==
+           AuthenticatorAttachment::kPlatform &&
+       !options.is_platform_device()) ||
+      (authenticator_selection_criteria.authenticator_attachement() ==
+           AuthenticatorAttachment::kCrossPlatform &&
+       options.is_platform_device())) {
+    return false;
+  }
+
+  if (authenticator_selection_criteria.require_resident_key() &&
+      !options.supports_resident_key()) {
+    return false;
+  }
+
+  const auto& user_verification_requirement =
+      authenticator_selection_criteria.user_verification_requirement();
+  if (user_verification_requirement == UserVerificationRequirement::kRequired) {
+    request->SetUserVerificationRequired(true);
+  }
+
+  return user_verification_requirement !=
+             UserVerificationRequirement::kRequired ||
+         options.user_verification_availability() ==
+             UvAvailability::kSupportedAndConfigured;
+}
+
+}  // namespace
+
 void MakeCredentialRequestHandler::DispatchRequest(
     FidoAuthenticator* authenticator) {
-  return authenticator->MakeCredential(
-      authenticator_selection_criteria_, request_parameter_,
+  // The user verification field of the request may be adjusted to the
+  // authenticator, so we need to make a copy.
+  CtapMakeCredentialRequest request_copy = request_parameter_;
+  if (!CheckIfAuthenticatorSelectionCriteriaAreSatisfied(
+          authenticator, authenticator_selection_criteria_, &request_copy)) {
+    return;
+  }
+
+  authenticator->MakeCredential(
+      std::move(request_copy),
       base::BindOnce(&MakeCredentialRequestHandler::OnAuthenticatorResponse,
                      weak_factory_.GetWeakPtr(), authenticator));
 }
diff --git a/device/fido/make_credential_task.cc b/device/fido/make_credential_task.cc
index 8f7cbc3..d609836 100644
--- a/device/fido/make_credential_task.cc
+++ b/device/fido/make_credential_task.cc
@@ -19,35 +19,24 @@
 MakeCredentialTask::MakeCredentialTask(
     FidoDevice* device,
     CtapMakeCredentialRequest request_parameter,
-    AuthenticatorSelectionCriteria authenticator_selection_criteria,
     MakeCredentialTaskCallback callback)
     : FidoTask(device),
       request_parameter_(std::move(request_parameter)),
-      authenticator_selection_criteria_(
-          std::move(authenticator_selection_criteria)),
       callback_(std::move(callback)),
       weak_factory_(this) {}
 
 MakeCredentialTask::~MakeCredentialTask() = default;
 
 void MakeCredentialTask::StartTask() {
-  if (base::FeatureList::IsEnabled(kNewCtap2Device)) {
-    GetAuthenticatorInfo(base::BindOnce(&MakeCredentialTask::MakeCredential,
-                                        weak_factory_.GetWeakPtr()),
-                         base::BindOnce(&MakeCredentialTask::U2fRegister,
-                                        weak_factory_.GetWeakPtr()));
+  if (base::FeatureList::IsEnabled(kNewCtap2Device) &&
+      device()->supported_protocol() == ProtocolVersion::kCtap) {
+    MakeCredential();
   } else {
     U2fRegister();
   }
 }
 
 void MakeCredentialTask::MakeCredential() {
-  if (!CheckIfAuthenticatorSelectionCriteriaAreSatisfied()) {
-    std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
-                             base::nullopt);
-    return;
-  }
-
   register_operation_ = std::make_unique<Ctap2DeviceOperation<
       CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
       device(), request_parameter_,
@@ -58,10 +47,7 @@
 }
 
 void MakeCredentialTask::U2fRegister() {
-  device()->set_supported_protocol(ProtocolVersion::kU2f);
-
-  if (!CheckIfAuthenticatorSelectionCriteriaAreSatisfied() ||
-      !IsConvertibleToU2fRegisterCommand(request_parameter_)) {
+  if (!IsConvertibleToU2fRegisterCommand(request_parameter_)) {
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
                              base::nullopt);
     return;
@@ -82,6 +68,8 @@
     return;
   }
 
+  // TODO(martinkr): CheckRpIdHash invocation needs to move into the Request
+  // handler. See https://crbug.com/863988.
   if (!response_data ||
       !response_data->CheckRpIdHash(request_parameter_.rp().rp_id())) {
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
@@ -92,48 +80,4 @@
   std::move(callback_).Run(return_code, std::move(response_data));
 }
 
-bool MakeCredentialTask::CheckIfAuthenticatorSelectionCriteriaAreSatisfied() {
-  using AuthenticatorAttachment =
-      AuthenticatorSelectionCriteria::AuthenticatorAttachment;
-  using UvAvailability =
-      AuthenticatorSupportedOptions::UserVerificationAvailability;
-
-  // U2F authenticators are non-platform devices that do not support resident
-  // key or user verification.
-  const auto& device_info = device()->device_info();
-  if (!device_info) {
-    return !authenticator_selection_criteria_.require_resident_key() &&
-           authenticator_selection_criteria_.user_verification_requirement() !=
-               UserVerificationRequirement::kRequired &&
-           authenticator_selection_criteria_.authenticator_attachement() !=
-               AuthenticatorAttachment::kPlatform;
-  }
-
-  const auto& options = device_info->options();
-  if ((authenticator_selection_criteria_.authenticator_attachement() ==
-           AuthenticatorAttachment::kPlatform &&
-       !options.is_platform_device()) ||
-      (authenticator_selection_criteria_.authenticator_attachement() ==
-           AuthenticatorAttachment::kCrossPlatform &&
-       options.is_platform_device())) {
-    return false;
-  }
-
-  if (authenticator_selection_criteria_.require_resident_key() &&
-      !options.supports_resident_key()) {
-    return false;
-  }
-
-  const auto user_verification_requirement =
-      authenticator_selection_criteria_.user_verification_requirement();
-  if (user_verification_requirement == UserVerificationRequirement::kRequired) {
-    request_parameter_.SetUserVerificationRequired(true);
-  }
-
-  return user_verification_requirement !=
-             UserVerificationRequirement::kRequired ||
-         options.user_verification_availability() ==
-             UvAvailability::kSupportedAndConfigured;
-}
-
 }  // namespace device
diff --git a/device/fido/make_credential_task.h b/device/fido/make_credential_task.h
index 47fcb99..fc8ae97 100644
--- a/device/fido/make_credential_task.h
+++ b/device/fido/make_credential_task.h
@@ -16,7 +16,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "device/fido/authenticator_make_credential_response.h"
-#include "device/fido/authenticator_selection_criteria.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/device_operation.h"
 #include "device/fido/fido_constants.h"
@@ -37,7 +36,6 @@
 
   MakeCredentialTask(FidoDevice* device,
                      CtapMakeCredentialRequest request_parameter,
-                     AuthenticatorSelectionCriteria authenticator_criteria,
                      MakeCredentialTaskCallback callback);
   ~MakeCredentialTask() override;
 
@@ -51,14 +49,7 @@
       CtapDeviceResponseCode return_code,
       base::Optional<AuthenticatorMakeCredentialResponse> response_data);
 
-  // Invoked after retrieving response to AuthenticatorGetInfo request. Filters
-  // out authenticators based on |authenticator_selection_criteria_| constraints
-  // provided by the relying party. If |device_| does not satisfy the
-  // constraints, then this request is silently dropped.
-  bool CheckIfAuthenticatorSelectionCriteriaAreSatisfied();
-
   CtapMakeCredentialRequest request_parameter_;
-  AuthenticatorSelectionCriteria authenticator_selection_criteria_;
   std::unique_ptr<RegisterOperation> register_operation_;
   MakeCredentialTaskCallback callback_;
   base::WeakPtrFactory<MakeCredentialTask> weak_factory_;
diff --git a/device/fido/make_credential_task_unittest.cc b/device/fido/make_credential_task_unittest.cc
index b97aa9b..ee16b84 100644
--- a/device/fido/make_credential_task_unittest.cc
+++ b/device/fido/make_credential_task_unittest.cc
@@ -14,6 +14,7 @@
 #include "device/base/features.h"
 #include "device/fido/authenticator_make_credential_response.h"
 #include "device/fido/ctap_make_credential_request.h"
+#include "device/fido/device_response_converter.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_test_data.h"
@@ -35,13 +36,9 @@
         CtapDeviceResponseCode,
         base::Optional<AuthenticatorMakeCredentialResponse>>;
 
-}  // namespace
-
 class FidoMakeCredentialTaskTest : public testing::Test {
  public:
-  FidoMakeCredentialTaskTest() {
-    scoped_feature_list_.emplace();
-  }
+  FidoMakeCredentialTaskTest() { scoped_feature_list_.emplace(); }
 
   std::unique_ptr<MakeCredentialTask> CreateMakeCredentialTask(
       FidoDevice* device) {
@@ -54,23 +51,7 @@
             test_data::kClientDataHash, std::move(rp), std::move(user),
             PublicKeyCredentialParams(
                 std::vector<PublicKeyCredentialParams::CredentialInfo>(1))),
-        AuthenticatorSelectionCriteria(), callback_receiver_.callback());
-  }
-
-  std::unique_ptr<MakeCredentialTask>
-  CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      FidoDevice* device,
-      AuthenticatorSelectionCriteria criteria) {
-    PublicKeyCredentialRpEntity rp(test_data::kRelyingPartyId);
-    PublicKeyCredentialUserEntity user(
-        fido_parsing_utils::Materialize(test_data::kUserId));
-    return std::make_unique<MakeCredentialTask>(
-        device,
-        CtapMakeCredentialRequest(
-            test_data::kClientDataHash, std::move(rp), std::move(user),
-            PublicKeyCredentialParams(
-                std::vector<PublicKeyCredentialParams::CredentialInfo>(1))),
-        std::move(criteria), callback_receiver_.callback());
+        callback_receiver_.callback());
   }
 
   void RemoveCtapFlag() {
@@ -89,11 +70,7 @@
 };
 
 TEST_F(FidoMakeCredentialTaskTest, MakeCredentialSuccess) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
+  auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorMakeCredential,
       test_data::kTestMakeCredentialResponse);
@@ -110,6 +87,9 @@
 
 TEST_F(FidoMakeCredentialTaskTest, TestRegisterSuccessWithFake) {
   auto device = std::make_unique<VirtualCtap2Device>();
+  test::TestCallbackReceiver<> done_init;
+  device->DiscoverSupportedProtocolAndDeviceInfo(done_init.callback());
+  done_init.WaitForCallback();
   const auto task = CreateMakeCredentialTask(device.get());
   make_credential_callback_receiver().WaitForCallback();
 
@@ -124,11 +104,7 @@
 }
 
 TEST_F(FidoMakeCredentialTaskTest, MakeCredentialWithIncorrectRpIdHash) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
+  auto device = MockFidoDevice::MakeCtap();
   device->ExpectCtap2CommandAndRespondWith(
       CtapRequestCommand::kAuthenticatorMakeCredential,
       test_data::kTestMakeCredentialResponseWithIncorrectRpIdHash);
@@ -141,10 +117,7 @@
 }
 
 TEST_F(FidoMakeCredentialTaskTest, FallbackToU2fRegisterSuccess) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
+  auto device = MockFidoDevice::MakeU2f();
   device->ExpectRequestAndRespondWith(
       test_data::kU2fRegisterCommandApdu,
       test_data::kApduEncodedNoErrorRegisterResponse);
@@ -159,7 +132,7 @@
 
 TEST_F(FidoMakeCredentialTaskTest, TestDefaultU2fRegisterOperationWithoutFlag) {
   RemoveCtapFlag();
-  auto device = std::make_unique<MockFidoDevice>();
+  auto device = MockFidoDevice::MakeU2f();
   device->ExpectRequestAndRespondWith(
       test_data::kU2fRegisterCommandApdu,
       test_data::kApduEncodedNoErrorRegisterResponse);
@@ -171,182 +144,5 @@
             make_credential_callback_receiver().status());
 }
 
-TEST_F(FidoMakeCredentialTaskTest, U2fRegisterWithUserVerificationRequired) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
-          false /* require_resident_key */,
-          UserVerificationRequirement::kRequired));
-  make_credential_callback_receiver().WaitForCallback();
-
-  EXPECT_EQ(ProtocolVersion::kU2f, device->supported_protocol());
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            make_credential_callback_receiver().status());
-}
-
-TEST_F(FidoMakeCredentialTaskTest, U2fRegisterWithPlatformDeviceRequirement) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kPlatform,
-          false /* require_resident_key */,
-          UserVerificationRequirement::kPreferred));
-  make_credential_callback_receiver().WaitForCallback();
-
-  EXPECT_EQ(ProtocolVersion::kU2f, device->supported_protocol());
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            make_credential_callback_receiver().status());
-}
-
-TEST_F(FidoMakeCredentialTaskTest, U2fRegisterWithResidentKeyRequirement) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo, base::nullopt);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
-          true /* require_resident_key */,
-          UserVerificationRequirement::kPreferred));
-  make_credential_callback_receiver().WaitForCallback();
-
-  EXPECT_EQ(ProtocolVersion::kU2f, device->supported_protocol());
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            make_credential_callback_receiver().status());
-}
-
-TEST_F(FidoMakeCredentialTaskTest,
-       UserVerificationAuthenticatorSelectionCriteria) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestGetInfoResponseWithoutUvSupport);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
-          false /* require_resident_key */,
-          UserVerificationRequirement::kRequired));
-  make_credential_callback_receiver().WaitForCallback();
-
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            make_credential_callback_receiver().status());
-  EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
-  EXPECT_TRUE(device->device_info());
-  EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::
-                kSupportedButNotConfigured,
-            device->device_info()->options().user_verification_availability());
-}
-
-TEST_F(FidoMakeCredentialTaskTest,
-       PlatformDeviceAuthenticatorSelectionCriteria) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestGetInfoResponseCrossPlatformDevice);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kPlatform,
-          false /* require_resident_key */,
-          UserVerificationRequirement::kPreferred));
-  make_credential_callback_receiver().WaitForCallback();
-
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            make_credential_callback_receiver().status());
-  EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
-  EXPECT_TRUE(device->device_info());
-  EXPECT_FALSE(device->device_info()->options().is_platform_device());
-}
-
-TEST_F(FidoMakeCredentialTaskTest, ResidentKeyAuthenticatorSelectionCriteria) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestGetInfoResponseWithoutResidentKeySupport);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
-          true /* require_resident_key */,
-          UserVerificationRequirement::kPreferred));
-  make_credential_callback_receiver().WaitForCallback();
-
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            make_credential_callback_receiver().status());
-  EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
-  EXPECT_TRUE(device->device_info());
-  EXPECT_FALSE(device->device_info()->options().supports_resident_key());
-}
-
-TEST_F(FidoMakeCredentialTaskTest, SatisfyAllAuthenticatorSelectionCriteria) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestAuthenticatorGetInfoResponse);
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorMakeCredential,
-      test_data::kTestMakeCredentialResponse);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kPlatform,
-          true /* require_resident_key */,
-          UserVerificationRequirement::kRequired));
-  make_credential_callback_receiver().WaitForCallback();
-
-  EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
-            make_credential_callback_receiver().status());
-  EXPECT_TRUE(make_credential_callback_receiver().value());
-  EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
-  EXPECT_TRUE(device->device_info());
-  const auto& device_options = device->device_info()->options();
-  EXPECT_TRUE(device_options.is_platform_device());
-  EXPECT_TRUE(device_options.supports_resident_key());
-  EXPECT_EQ(AuthenticatorSupportedOptions::UserVerificationAvailability::
-                kSupportedAndConfigured,
-            device_options.user_verification_availability());
-}
-
-TEST_F(FidoMakeCredentialTaskTest, IncompatibleUserVerificationSetting) {
-  auto device = std::make_unique<MockFidoDevice>();
-
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetInfo,
-      test_data::kTestGetInfoResponseWithoutUvSupport);
-
-  const auto task = CreateMakeCredentialTaskWithAuthenticatorSelectionCriteria(
-      device.get(),
-      AuthenticatorSelectionCriteria(
-          AuthenticatorSelectionCriteria::AuthenticatorAttachment::kAny,
-          false /* require_resident_key */,
-          UserVerificationRequirement::kRequired));
-  make_credential_callback_receiver().WaitForCallback();
-  EXPECT_EQ(ProtocolVersion::kCtap, device->supported_protocol());
-  EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
-            make_credential_callback_receiver().status());
-  EXPECT_FALSE(make_credential_callback_receiver().value());
-}
-
+}  // namespace
 }  // namespace device
diff --git a/device/fido/mock_fido_device.cc b/device/fido/mock_fido_device.cc
index 714950a..cf499d5b 100644
--- a/device/fido/mock_fido_device.cc
+++ b/device/fido/mock_fido_device.cc
@@ -10,18 +10,49 @@
 #include "base/location.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/apdu/apdu_response.h"
+#include "device/fido/device_response_converter.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "device/fido/fido_test_data.h"
 
 namespace device {
 
+namespace {
+AuthenticatorGetInfoResponse DefaultAuthenticatorInfo() {
+  return *ReadCTAPGetInfoResponse(test_data::kTestAuthenticatorGetInfoResponse);
+}
+}  // namespace
+
+// static
+std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeU2f() {
+  return std::make_unique<MockFidoDevice>(ProtocolVersion::kU2f, base::nullopt);
+}
+
+// static
+std::unique_ptr<MockFidoDevice> MockFidoDevice::MakeCtap(
+    base::Optional<AuthenticatorGetInfoResponse> device_info) {
+  if (!device_info) {
+    device_info = DefaultAuthenticatorInfo();
+  }
+  return std::make_unique<MockFidoDevice>(ProtocolVersion::kCtap,
+                                          std::move(*device_info));
+}
+
 // Matcher to compare the fist byte of the incoming requests.
 MATCHER_P(IsCtap2Command, expected_command, "") {
   return !arg.empty() && arg[0] == base::strict_cast<uint8_t>(expected_command);
 }
 
 MockFidoDevice::MockFidoDevice() : weak_factory_(this) {}
+MockFidoDevice::MockFidoDevice(
+    ProtocolVersion protocol_version,
+    base::Optional<AuthenticatorGetInfoResponse> device_info)
+    : MockFidoDevice() {
+  set_supported_protocol(protocol_version);
+  if (device_info) {
+    SetDeviceInfo(std::move(*device_info));
+  }
+}
 MockFidoDevice::~MockFidoDevice() = default;
 
 void MockFidoDevice::TryWink(WinkCallback cb) {
diff --git a/device/fido/mock_fido_device.h b/device/fido/mock_fido_device.h
index cabfea9..0df85aa9 100644
--- a/device/fido/mock_fido_device.h
+++ b/device/fido/mock_fido_device.h
@@ -23,7 +23,13 @@
 
 class MockFidoDevice : public FidoDevice {
  public:
+  static std::unique_ptr<MockFidoDevice> MakeU2f();
+  static std::unique_ptr<MockFidoDevice> MakeCtap(
+      base::Optional<AuthenticatorGetInfoResponse> device_info = base::nullopt);
+
   MockFidoDevice();
+  MockFidoDevice(ProtocolVersion protocol_version,
+                 base::Optional<AuthenticatorGetInfoResponse> device_info);
   ~MockFidoDevice() override;
 
   // TODO(crbug.com/729950): Remove these workarounds once support for move-only
diff --git a/device/vr/android/gvr/gvr_delegate_provider.h b/device/vr/android/gvr/gvr_delegate_provider.h
index 2e5f84f..9bd7708 100644
--- a/device/vr/android/gvr/gvr_delegate_provider.h
+++ b/device/vr/android/gvr/gvr_delegate_provider.h
@@ -23,7 +23,7 @@
   virtual void StartWebXRPresentation(
       mojom::VRDisplayInfoPtr display_info,
       mojom::XRDeviceRuntimeSessionOptionsPtr options,
-      device::mojom::VRDisplayHost::RequestSessionCallback callback) = 0;
+      base::OnceCallback<void(device::mojom::XRSessionPtr)> callback) = 0;
   virtual void ExitWebVRPresent() = 0;
   virtual void OnListeningForActivateChanged(bool listening) = 0;
 
diff --git a/device/vr/android/gvr/gvr_device.cc b/device/vr/android/gvr/gvr_device.cc
index de2c12bc..20166c65 100644
--- a/device/vr/android/gvr/gvr_device.cc
+++ b/device/vr/android/gvr/gvr_device.cc
@@ -186,27 +186,25 @@
   }
 
   if (options->immersive) {
-    if (options->immersive) {
-      // StartWebXRPresentation is async as we may trigger a DON (Device ON)
-      // flow that pauses Chrome.
-      delegate_provider->StartWebXRPresentation(
-          GetVRDisplayInfo(), std::move(options),
-          base::BindOnce(&GvrDevice::OnRequestSessionResult,
-                         weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-    } else {
-      // TODO(https://crbug.com/695937): This should be NOTREACHED() once
-      // orientation device handles non-immersive VR sessions.
-      // TODO(https://crbug.com/842025): Handle this when RequestSession is
-      // called for non-immersive sessions.
-      NOTREACHED();
-    }
+    // StartWebXRPresentation is async as we may trigger a DON (Device ON) flow
+    // that pauses Chrome.
+    delegate_provider->StartWebXRPresentation(
+        GetVRDisplayInfo(), std::move(options),
+        base::BindOnce(&GvrDevice::OnStartPresentResult,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  } else {
+    // TODO(https://crbug.com/695937): This should be NOTREACHED() once
+    // orientation device handles non-immersive VR sessions.
+    // TODO(https://crbug.com/842025): Handle this when RequestSession is called
+    // for non-immersive sessions.
+    NOTREACHED();
   }
 }
 
-void GvrDevice::OnRequestSessionResult(
+void GvrDevice::OnStartPresentResult(
     mojom::XRRuntime::RequestSessionCallback callback,
-    mojom::XRPresentationConnectionPtr connection) {
-  if (!connection) {
+    mojom::XRSessionPtr session) {
+  if (!session || !session->connection) {
     std::move(callback).Run(nullptr, nullptr);
     return;
   }
@@ -225,7 +223,8 @@
       base::BindOnce(&GvrDevice::OnPresentingControllerMojoConnectionError,
                      base::Unretained(this)));
 
-  std::move(callback).Run(std::move(connection), std::move(session_controller));
+  std::move(callback).Run(std::move(session->connection),
+                          std::move(session_controller));
 }
 
 // XRSessionController
diff --git a/device/vr/android/gvr/gvr_device.h b/device/vr/android/gvr/gvr_device.h
index ead1ab4..6061fcbe 100644
--- a/device/vr/android/gvr/gvr_device.h
+++ b/device/vr/android/gvr/gvr_device.h
@@ -44,8 +44,8 @@
   void OnMagicWindowFrameDataRequest(
       mojom::VRMagicWindowProvider::GetFrameDataCallback callback) override;
 
-  void OnRequestSessionResult(mojom::XRRuntime::RequestSessionCallback callback,
-                              mojom::XRPresentationConnectionPtr connection);
+  void OnStartPresentResult(mojom::XRRuntime::RequestSessionCallback callback,
+                            mojom::XRSessionPtr session);
 
   // XRSessionController
   void SetFrameDataRestricted(bool restricted) override;
diff --git a/device/vr/public/mojom/isolated_xr_service.mojom b/device/vr/public/mojom/isolated_xr_service.mojom
index 5b33778..3e4c7955 100644
--- a/device/vr/public/mojom/isolated_xr_service.mojom
+++ b/device/vr/public/mojom/isolated_xr_service.mojom
@@ -69,9 +69,9 @@
   // Attempt to start a "magic window" session.  Magic window sessions allow
   // Clients to obtain poses (device position and orientation), but rendering
   // goes through the standard Chrome compositor.
-  RequestMagicWindowSession(device.mojom.VRMagicWindowProvider& session,
-                            device.mojom.XRSessionController& controller) =>
-      (bool success);
+  RequestMagicWindowSession() =>
+      (device.mojom.VRMagicWindowProvider? session,
+       device.mojom.XRSessionController? controller);
 
   // The browser may register for changes to a device. Initial VRDisplayInfo
   // will immediately be returned to the listener to prevent races.
diff --git a/device/vr/public/mojom/vr_service.mojom b/device/vr/public/mojom/vr_service.mojom
index a5b87a7..f87b684 100644
--- a/device/vr/public/mojom/vr_service.mojom
+++ b/device/vr/public/mojom/vr_service.mojom
@@ -40,6 +40,13 @@
   bool use_legacy_webvr_render_path;
 };
 
+// TODO(offenwanger) Rearrange these two interfaces to merge duplicate
+// functionality.
+struct XRSession {
+  VRMagicWindowProvider? magic_window_provider;
+  XRPresentationConnection? connection;
+};
+
 struct XRPresentationConnection {
   VRSubmitFrameClient& client_request;
   VRPresentationProvider provider;
@@ -237,8 +244,7 @@
 };
 
 interface VRServiceClient {
-  OnDisplayConnected(VRMagicWindowProvider magic_window_provider,
-                     VRDisplayHost display, VRDisplayClient& request,
+  OnDisplayConnected(VRDisplayHost display, VRDisplayClient& request,
                      VRDisplayInfo display_info);
 };
 
@@ -285,7 +291,7 @@
   // reflect WebXR.
   RequestSession(
     XRSessionOptions options,
-    bool triggered_by_displayactive) => (XRPresentationConnection? connection);
+    bool triggered_by_displayactive) => (XRSession? session);
   SupportsSession(XRSessionOptions options) => (bool supports_session);
 
   ExitPresent();
diff --git a/device/vr/test/fake_vr_service_client.cc b/device/vr/test/fake_vr_service_client.cc
index 9644610..a46445f 100644
--- a/device/vr/test/fake_vr_service_client.cc
+++ b/device/vr/test/fake_vr_service_client.cc
@@ -13,7 +13,6 @@
 FakeVRServiceClient::~FakeVRServiceClient() {}
 
 void FakeVRServiceClient::OnDisplayConnected(
-    mojom::VRMagicWindowProviderPtr magic_window_provider,
     mojom::VRDisplayHostPtr display,
     mojom::VRDisplayClientRequest request,
     mojom::VRDisplayInfoPtr displayInfo) {
diff --git a/device/vr/test/fake_vr_service_client.h b/device/vr/test/fake_vr_service_client.h
index 1524f3b..74fd128 100644
--- a/device/vr/test/fake_vr_service_client.h
+++ b/device/vr/test/fake_vr_service_client.h
@@ -19,8 +19,7 @@
   FakeVRServiceClient(mojom::VRServiceClientRequest request);
   ~FakeVRServiceClient() override;
 
-  void OnDisplayConnected(mojom::VRMagicWindowProviderPtr magic_window_provider,
-                          mojom::VRDisplayHostPtr display,
+  void OnDisplayConnected(mojom::VRDisplayHostPtr display,
                           mojom::VRDisplayClientRequest request,
                           mojom::VRDisplayInfoPtr displayInfo) override;
   void SetLastDeviceId(unsigned int id);
diff --git a/device/vr/vr_device_base.cc b/device/vr/vr_device_base.cc
index 686f5eb3..1c7703a 100644
--- a/device/vr/vr_device_base.cc
+++ b/device/vr/vr_device_base.cc
@@ -111,12 +111,12 @@
 }
 
 void VRDeviceBase::RequestMagicWindowSession(
-    mojom::VRMagicWindowProviderRequest provider_request,
-    mojom::XRSessionControllerRequest controller_request,
     mojom::XRRuntime::RequestMagicWindowSessionCallback callback) {
+  mojom::VRMagicWindowProviderPtr provider;
+  mojom::XRSessionControllerPtr controller;
   magic_window_sessions_.push_back(std::make_unique<VRDisplayImpl>(
-      this, std::move(provider_request), std::move(controller_request)));
-  std::move(callback).Run(true);
+      this, mojo::MakeRequest(&provider), mojo::MakeRequest(&controller)));
+  std::move(callback).Run(std::move(provider), std::move(controller));
 }
 
 void VRDeviceBase::EndMagicWindowSession(VRDisplayImpl* session) {
diff --git a/device/vr/vr_device_base.h b/device/vr/vr_device_base.h
index 370b0c9..04f6c506 100644
--- a/device/vr/vr_device_base.h
+++ b/device/vr/vr_device_base.h
@@ -85,8 +85,6 @@
 
   // XRRuntime
   void RequestMagicWindowSession(
-      mojom::VRMagicWindowProviderRequest provider_request,
-      mojom::XRSessionControllerRequest controller_request,
       mojom::XRRuntime::RequestMagicWindowSessionCallback callback) override;
 
   mojom::XRRuntimeEventListenerPtr listener_;
diff --git a/docs/sublime_ide.md b/docs/sublime_ide.md
index 1e692d3..b0b904ae 100644
--- a/docs/sublime_ide.md
+++ b/docs/sublime_ide.md
@@ -553,6 +553,36 @@
   ]
 ```
 
+### More detailed stack traces
+
+Chrome's default stack traces don't have full file paths so Sublime can't
+parse them. You can enable more detailed stack traces and use F4 to step right
+to the crashing line of code.
+
+First, add `print_unsymbolized_stack_traces = true` to your gn args, and make
+sure you have debug symbols enabled too (`symbol_level = 2`). Then, pipe
+Chrome's stderr through the asan_symbolize.py script. Here's a suitable build
+variant for Linux (with tweaked file_regex):
+
+```json
+{
+  "name": "Build and run with asan_symbolize",
+  "cmd": "ninja -j 1000 -C out/Debug chrome && out/Debug/chrome 2>&1 | ./tools/valgrind/asan/asan_symbolize.py",
+  "shell": true,
+  "file_regex": "(?:^|[)] )[.\\\\/]*([a-z]?:?[\\w.\\\\/]+)[(:]([0-9]+)[,:]?([0-9]+)?[)]?:?(.*)$"
+}
+```
+
+You can test it by visiting chrome://crash. You should be able to step through
+each line in the resulting stacktrace with F4. You can also get a stack trace
+without crashing like so:
+
+```c++
+#include "base/debug/stack_trace.h"
+[...]
+base::debug::StackTrace().Print();
+```
+
 ### Assigning builds to keyboard shortcuts
 
 To assign a build to a keyboard shortcut, select `Preferences > Key Bindings -
diff --git a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
index fd3092d..cbf8b12 100644
--- a/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
+++ b/extensions/browser/api/guest_view/web_view/web_view_internal_api.cc
@@ -20,7 +20,6 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/stop_find_action.h"
-#include "content/public/common/url_fetcher.h"
 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
 #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h"
 #include "extensions/common/api/web_view_internal.h"
diff --git a/extensions/browser/api/web_request/web_request_info.cc b/extensions/browser/api/web_request/web_request_info.cc
index 5ad45a4..e1cbcbd 100644
--- a/extensions/browser/api/web_request/web_request_info.cc
+++ b/extensions/browser/api/web_request/web_request_info.cc
@@ -23,6 +23,8 @@
 #include "net/log/net_log_with_source.h"
 #include "net/url_request/url_request.h"
 #include "services/network/public/cpp/resource_response.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/url_loader.h"
 
 namespace keys = extension_web_request_api_constants;
 
@@ -257,6 +259,16 @@
     web_request_type = ToWebRequestResourceType(type.value());
     is_async = info->IsAsync();
     resource_context = info->GetContext();
+  } else if (auto* url_loader = network::URLLoader::ForRequest(*url_request)) {
+    // This is reached only in the SimpleURLLoader case (since network service
+    // is disabled if we're in this constructor). Only set the IDs if they're
+    // non-zero, since almost all requests come from the browser and aren't
+    // associated with a frame. In the case that the browser wants this
+    // SimpleURLLoader associated with a frame, the process ID will be non-zero.
+    if (url_loader->GetProcessId() != network::mojom::kBrowserProcessId) {
+      render_process_id = url_loader->GetProcessId();
+      frame_id = url_loader->GetRenderFrameId();
+    }
   } else {
     // There may be basic process and frame info associated with the request
     // even when |info| is null. Attempt to grab it as a last ditch effort. If
diff --git a/extensions/browser/blob_reader.cc b/extensions/browser/blob_reader.cc
index 6df12eb..f4f4156 100644
--- a/extensions/browser/blob_reader.cc
+++ b/extensions/browser/blob_reader.cc
@@ -72,6 +72,8 @@
 
 void BlobReader::OnDataComplete() {
   data_complete_ = true;
+  if (!blob_data_)
+    blob_data_ = std::make_unique<std::string>();
   if (blob_length_)
     Succeeded();
 }
diff --git a/gpu/config/gpu_preferences.h b/gpu/config/gpu_preferences.h
index b0936f66..9261ac2 100644
--- a/gpu/config/gpu_preferences.h
+++ b/gpu/config/gpu_preferences.h
@@ -204,6 +204,9 @@
   // send a background/suspend signal.
   bool watchdog_starts_backgrounded = false;
 
+  // Use Vulkan for rasterization and display compositing.
+  bool enable_vulkan = false;
+
   // Please update gpu_preferences_unittest.cc when making additions or
   // changes to this struct.
 };
diff --git a/gpu/config/gpu_preferences_unittest.cc b/gpu/config/gpu_preferences_unittest.cc
index 67e9ff7..d5493e2 100644
--- a/gpu/config/gpu_preferences_unittest.cc
+++ b/gpu/config/gpu_preferences_unittest.cc
@@ -74,6 +74,7 @@
             right.use_gpu_fences_for_overlay_planes);
   EXPECT_EQ(left.watchdog_starts_backgrounded,
             right.watchdog_starts_backgrounded);
+  EXPECT_EQ(left.enable_vulkan, right.enable_vulkan);
 }
 
 }  // namespace
@@ -150,6 +151,7 @@
     GPU_PREFERENCES_FIELD(disable_oop_rasterization, true)
     GPU_PREFERENCES_FIELD(use_gpu_fences_for_overlay_planes, true)
     GPU_PREFERENCES_FIELD(watchdog_starts_backgrounded, true)
+    GPU_PREFERENCES_FIELD(enable_vulkan, true)
 
     input_prefs.texture_target_exception_list.emplace_back(
         gfx::BufferUsage::SCANOUT, gfx::BufferFormat::RGBA_8888);
diff --git a/gpu/ipc/common/gpu_preferences.mojom b/gpu/ipc/common/gpu_preferences.mojom
index 37f32a8..b467a10 100644
--- a/gpu/ipc/common/gpu_preferences.mojom
+++ b/gpu/ipc/common/gpu_preferences.mojom
@@ -64,4 +64,5 @@
   bool disable_oop_rasterization;
   bool use_gpu_fences_for_overlay_planes;
   bool watchdog_starts_backgrounded;
+  bool enable_vulkan;
 };
diff --git a/gpu/ipc/common/gpu_preferences_struct_traits.h b/gpu/ipc/common/gpu_preferences_struct_traits.h
index 6bcf6536..265911c4 100644
--- a/gpu/ipc/common/gpu_preferences_struct_traits.h
+++ b/gpu/ipc/common/gpu_preferences_struct_traits.h
@@ -121,6 +121,7 @@
     out->use_gpu_fences_for_overlay_planes =
         prefs.use_gpu_fences_for_overlay_planes();
     out->watchdog_starts_backgrounded = prefs.watchdog_starts_backgrounded();
+    out->enable_vulkan = prefs.enable_vulkan();
     return true;
   }
 
@@ -261,6 +262,9 @@
   static bool watchdog_starts_backgrounded(const gpu::GpuPreferences& prefs) {
     return prefs.watchdog_starts_backgrounded;
   }
+  static bool enable_vulkan(const gpu::GpuPreferences& prefs) {
+    return prefs.enable_vulkan;
+  }
 };
 
 }  // namespace mojo
diff --git a/gpu/ipc/service/BUILD.gn b/gpu/ipc/service/BUILD.gn
index b226989..834eab22f 100644
--- a/gpu/ipc/service/BUILD.gn
+++ b/gpu/ipc/service/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//testing/test.gni")
 import("//build/config/ui.gni")
+import("//gpu/vulkan/features.gni")
 if (is_mac) {
   import("//build/config/mac/mac_sdk.gni")
 }
@@ -64,6 +65,7 @@
     "//gpu/command_buffer/service:gles2",
     "//gpu/config",
     "//gpu/ipc/common",
+    "//gpu/vulkan:buildflags",
   ]
   libs = []
   ldflags = []
@@ -129,6 +131,9 @@
   if (is_fuchsia) {
     sources += [ "image_transport_surface_fuchsia.cc" ]
   }
+  if (enable_vulkan) {
+    deps += [ "//gpu/vulkan/init" ]
+  }
 }
 
 source_set("test_support") {
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 1c8818d6..eb2da596 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -30,6 +30,7 @@
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager_delegate.h"
 #include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+#include "third_party/skia/include/core/SkGraphics.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_share_group.h"
 #include "ui/gl/gl_version_info.h"
@@ -298,6 +299,8 @@
     raster_decoder_context_state_->context_lost = true;
     raster_decoder_context_state_.reset();
   }
+
+  SkGraphics::PurgeAllCaches();
 }
 #endif
 
@@ -307,6 +310,9 @@
         base::MemoryPressureListener::MemoryPressureLevel::
             MEMORY_PRESSURE_LEVEL_CRITICAL);
   }
+
+  // Release all skia caching when the application is backgrounded.
+  SkGraphics::PurgeAllCaches();
 }
 
 void GpuChannelManager::HandleMemoryPressure(
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 98a1a44..4ba6ee24 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -38,6 +38,11 @@
 #include "ui/gl/gl_surface_egl.h"
 #endif
 
+#if BUILDFLAG(ENABLE_VULKAN)
+#include "gpu/vulkan/init/vulkan_factory.h"
+#include "gpu/vulkan/vulkan_implementation.h"
+#endif
+
 namespace gpu {
 
 namespace {
@@ -172,6 +177,20 @@
 #endif  // OS_WIN
   }
 
+#if BUILDFLAG(ENABLE_VULKAN)
+  if (gpu_preferences_.enable_vulkan) {
+    vulkan_implementation_ = gpu::CreateVulkanImplementation();
+    if (!vulkan_implementation_ ||
+        !vulkan_implementation_->InitializeVulkanInstance()) {
+      DLOG(WARNING) << "Failed to create and initialize Vulkan implementation.";
+      vulkan_implementation_ = nullptr;
+    }
+    gpu_preferences_.enable_vulkan = !!vulkan_implementation_;
+  }
+#else
+  gpu_preferences_.enable_vulkan = false;
+#endif
+
   sandbox_helper_->PreSandboxStartup();
 
   bool attempted_startsandbox = false;
diff --git a/gpu/ipc/service/gpu_init.h b/gpu/ipc/service/gpu_init.h
index a19f6cc..39ed71f 100644
--- a/gpu/ipc/service/gpu_init.h
+++ b/gpu/ipc/service/gpu_init.h
@@ -12,6 +12,7 @@
 #include "gpu/config/gpu_preferences.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
 #include "gpu/ipc/service/gpu_watchdog_thread.h"
+#include "gpu/vulkan/buildflags.h"
 
 namespace base {
 class CommandLine;
@@ -19,6 +20,8 @@
 
 namespace gpu {
 
+class VulkanImplementation;
+
 class GPU_IPC_SERVICE_EXPORT GpuSandboxHelper {
  public:
   virtual ~GpuSandboxHelper() = default;
@@ -60,6 +63,13 @@
     return std::move(watchdog_thread_);
   }
   bool init_successful() const { return init_successful_; }
+#if BUILDFLAG(ENABLE_VULKAN)
+  VulkanImplementation* vulkan_implementation() {
+    return vulkan_implementation_.get();
+  }
+#else
+  VulkanImplementation* vulkan_implementation() { return nullptr; }
+#endif
 
  private:
   GpuSandboxHelper* sandbox_helper_ = nullptr;
@@ -74,6 +84,10 @@
   base::Optional<GPUInfo> gpu_info_for_hardware_gpu_;
   base::Optional<GpuFeatureInfo> gpu_feature_info_for_hardware_gpu_;
 
+#if BUILDFLAG(ENABLE_VULKAN)
+  std::unique_ptr<VulkanImplementation> vulkan_implementation_;
+#endif
+
   void AdjustInfoToSwiftShader();
 
   DISALLOW_COPY_AND_ASSIGN(GpuInit);
diff --git a/gpu/vulkan/features.gni b/gpu/vulkan/features.gni
index 150f6156..1c11576f 100644
--- a/gpu/vulkan/features.gni
+++ b/gpu/vulkan/features.gni
@@ -9,4 +9,10 @@
 declare_args() {
   # Enable experimental vulkan backend.
   enable_vulkan = (is_linux && use_x11) || is_android
+
+  # We want to temporarily disable Vulkan on Android to give us time to
+  # investigate ways to reduce its binary size.
+  if (is_android) {
+    enable_vulkan = false
+  }
 }
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 7adec62..4080ded 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -78,6 +78,8 @@
   triggers: "Android FYI Release (Nexus 6P)"
   triggers: "Android FYI Release (Nexus 9)"
   triggers: "Android FYI dEQP Release (Nexus 5X)"
+  triggers: "Android N5 Swarm"
+  triggers: "Android N5X Swarm"
   triggers: "Android Release (Nexus 5X)"
   triggers: "Android arm Builder (dbg)"
   triggers: "Android arm64 Builder (dbg)"
@@ -87,6 +89,7 @@
   triggers: "Cast Android (dbg)"
   triggers: "Cast Audio Linux"
   triggers: "Cast Linux"
+  triggers: "ChromeOS Swarm"
   triggers: "Chromium Linux Goma GCE Staging"
   triggers: "Chromium Linux Goma RBE Staging (clobber)"
   triggers: "Chromium Linux Goma RBE Staging (dbg) (clobber)"
@@ -152,6 +155,7 @@
   triggers: "Linux Clang Analyzer"
   triggers: "Linux FYI GPU TSAN Release"
   triggers: "Linux MSan Builder"
+  triggers: "Linux Swarm"
   triggers: "Linux TSan Builder"
   triggers: "Linux Viz"
   triggers: "Linux remote_run Builder"
@@ -172,6 +176,7 @@
   triggers: "Mac FYI GPU ASAN Release"
   triggers: "Mac Goma Canary (clobber)"
   triggers: "Mac Goma Canary LocalOutputCache"
+  triggers: "Mac Swarm"
   triggers: "Mac deterministic (dbg)"
   triggers: "Mac deterministic"
   triggers: "Mac"
@@ -203,6 +208,7 @@
   triggers: "Win7 Builder Goma Canary"
   triggers: "WinMSVC64 Goma Canary"
   triggers: "Windows Clang deterministic"
+  triggers: "Windows Swarm"
   triggers: "Windows deterministic"
   triggers: "android-kitkat-arm-rel"
   triggers: "android-marshmallow-arm64-rel"
diff --git a/ios/chrome/browser/experimental_flags.mm b/ios/chrome/browser/experimental_flags.mm
index eb914622..2a5fe05 100644
--- a/ios/chrome/browser/experimental_flags.mm
+++ b/ios/chrome/browser/experimental_flags.mm
@@ -143,7 +143,7 @@
 }
 
 bool IsSettingsUIRebootEnabled() {
-  return base::FeatureList::IsEnabled(kCollectionsUIReboot);
+  return base::FeatureList::IsEnabled(kUIRefreshPhase1);
 }
 
 }  // namespace experimental_flags
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn
index e3c48928..588b77ad 100644
--- a/ios/chrome/browser/payments/BUILD.gn
+++ b/ios/chrome/browser/payments/BUILD.gn
@@ -44,6 +44,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/autofill",
     "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/metrics",
     "//ios/chrome/browser/signin",
     "//ios/web",
     "//net",
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index f9ae6baa..76495d9e2 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -31,6 +31,7 @@
 #include "ios/chrome/browser/autofill/address_normalizer_factory.h"
 #include "ios/chrome/browser/autofill/validation_rules_storage_factory.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/metrics/ukm_url_recorder.h"
 #import "ios/chrome/browser/payments/ios_payment_instrument.h"
 #import "ios/chrome/browser/payments/payment_request_util.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
@@ -96,7 +97,8 @@
       selected_payment_method_(nullptr),
       selected_shipping_option_(nullptr),
       profile_comparator_(GetApplicationLocale(), *this),
-      journey_logger_(IsIncognito(), GetLastCommittedURL(), GetUkmRecorder()),
+      journey_logger_(IsIncognito(),
+                      ukm::GetSourceIdForWebStateDocument(web_state)),
       payment_instruments_ready_(false),
       ios_instrument_finder_(
           GetApplicationContext()->GetSharedURLLoaderFactory(),
diff --git a/media/base/bind_to_current_loop.h b/media/base/bind_to_current_loop.h
index 8c07621..de69a63a 100644
--- a/media/base/bind_to_current_loop.h
+++ b/media/base/bind_to_current_loop.h
@@ -13,18 +13,22 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 
-// This is a helper utility for base::Bind()ing callbacks to the current
-// MessageLoop. The typical use is when |a| (of class |A|) wants to hand a
+// This is a helper utility for base::Bind()ing callbacks to a given
+// TaskRunner. The typical use is when |a| (of class |A|) wants to hand a
 // callback such as base::Bind(&A::AMethod, a) to |b|, but needs to ensure that
-// when |b| executes the callback, it does so on |a|'s current MessageLoop.
+// when |b| executes the callback, it does so on |a|'s task_runner's
+// MessageLoop.
 //
 // Typical usage: request to be called back on the current thread:
 // other->StartAsyncProcessAndCallMeBack(
-//    media::BindToCurrentLoop(base::BindOnce(&MyClass::MyMethod, this)));
+//    media::BindToLoop(task_runner, base::BindOnce(&MyClass::MyMethod, this)));
 //
-// media::BindToCurrentLoop returns the same type of callback to the given
+// media::BindToLoop returns the same type of callback to the given
 // callback. I.e. it returns a RepeatingCallback for a given RepeatingCallback,
 // and returns OnceCallback for a given OnceCallback.
+//
+// The function BindToCurrentLoop is shorthand to bind to the calling function's
+// current MessageLoop.
 
 namespace media {
 namespace internal {
@@ -81,7 +85,8 @@
 }  // namespace internal
 
 template <typename... Args>
-inline base::RepeatingCallback<void(Args...)> BindToCurrentLoop(
+inline base::RepeatingCallback<void(Args...)> BindToLoop(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
     base::RepeatingCallback<void(Args...)> cb) {
   using CallbackType = base::RepeatingCallback<void(Args...)>;
   using Helper = internal::TrampolineHelper<CallbackType>;
@@ -89,12 +94,12 @@
   RunnerType run = &Helper::Run;
   // TODO(tzik): Propagate FROM_HERE from the caller.
   return base::BindRepeating(
-      run, std::make_unique<Helper>(
-               FROM_HERE, base::ThreadTaskRunnerHandle::Get(), std::move(cb)));
+      run, std::make_unique<Helper>(FROM_HERE, task_runner, std::move(cb)));
 }
 
 template <typename... Args>
-inline base::OnceCallback<void(Args...)> BindToCurrentLoop(
+inline base::OnceCallback<void(Args...)> BindToLoop(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
     base::OnceCallback<void(Args...)> cb) {
   using CallbackType = base::OnceCallback<void(Args...)>;
   using Helper = internal::TrampolineHelper<CallbackType>;
@@ -102,8 +107,19 @@
   RunnerType run = &Helper::Run;
   // TODO(tzik): Propagate FROM_HERE from the caller.
   return base::BindOnce(
-      run, std::make_unique<Helper>(
-               FROM_HERE, base::ThreadTaskRunnerHandle::Get(), std::move(cb)));
+      run, std::make_unique<Helper>(FROM_HERE, task_runner, std::move(cb)));
+}
+
+template <typename... Args>
+inline base::RepeatingCallback<void(Args...)> BindToCurrentLoop(
+    base::RepeatingCallback<void(Args...)> cb) {
+  return BindToLoop(base::ThreadTaskRunnerHandle::Get(), std::move(cb));
+}
+
+template <typename... Args>
+inline base::OnceCallback<void(Args...)> BindToCurrentLoop(
+    base::OnceCallback<void(Args...)> cb) {
+  return BindToLoop(base::ThreadTaskRunnerHandle::Get(), std::move(cb));
 }
 
 }  // namespace media
diff --git a/media/blink/DEPS b/media/blink/DEPS
index c30001fa..efda6347 100644
--- a/media/blink/DEPS
+++ b/media/blink/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+cc/layers/layer.h",
+  "+cc/layers/surface_layer.h",
   "+cc/layers/video_frame_provider.h",
   "+cc/layers/video_layer.h",
   "+components/scheduler",  # Only allowed in tests.
diff --git a/media/blink/video_frame_compositor.cc b/media/blink/video_frame_compositor.cc
index 99c2fb99..6b25bcb0 100644
--- a/media/blink/video_frame_compositor.cc
+++ b/media/blink/video_frame_compositor.cc
@@ -9,6 +9,7 @@
 #include "base/time/default_tick_clock.h"
 #include "base/trace_event/auto_open_close_event.h"
 #include "base/trace_event/trace_event.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/media_switches.h"
 #include "media/base/video_frame.h"
 #include "media/blink/webmediaplayer_params.h"
@@ -46,9 +47,23 @@
     task_runner_->PostTask(
         FROM_HERE, base::Bind(&VideoFrameCompositor::InitializeSubmitter,
                               weak_ptr_factory_.GetWeakPtr()));
+    update_submission_state_callback_ = media::BindToLoop(
+        task_runner_,
+        base::BindRepeating(&VideoFrameCompositor::UpdateSubmissionState,
+                            weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
+cc::UpdateSubmissionStateCB
+VideoFrameCompositor::GetUpdateSubmissionStateCallback() {
+  return update_submission_state_callback_;
+}
+
+void VideoFrameCompositor::UpdateSubmissionState(bool state) {
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  submitter_->UpdateSubmissionState(state);
+}
+
 void VideoFrameCompositor::InitializeSubmitter() {
   DCHECK(task_runner_->BelongsToCurrentThread());
   submitter_->Initialize(this);
@@ -63,7 +78,7 @@
 }
 
 void VideoFrameCompositor::EnableSubmission(
-    const viz::FrameSinkId& id,
+    const viz::SurfaceId& id,
     media::VideoRotation rotation,
     blink::WebFrameSinkDestroyedCallback frame_sink_destroyed_callback) {
   DCHECK(task_runner_->BelongsToCurrentThread());
diff --git a/media/blink/video_frame_compositor.h b/media/blink/video_frame_compositor.h
index f0b49b5..f3959f8 100644
--- a/media/blink/video_frame_compositor.h
+++ b/media/blink/video_frame_compositor.h
@@ -15,6 +15,7 @@
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
+#include "cc/layers/surface_layer.h"
 #include "cc/layers/video_frame_provider.h"
 #include "media/base/video_renderer_sink.h"
 #include "media/blink/media_blink_export.h"
@@ -29,7 +30,7 @@
 }
 
 namespace viz {
-class FrameSinkId;
+class SurfaceId;
 }
 
 namespace media {
@@ -76,10 +77,13 @@
   // called before destruction starts.
   ~VideoFrameCompositor() override;
 
+  // Can be called from any thread.
+  cc::UpdateSubmissionStateCB GetUpdateSubmissionStateCallback();
+
   // Signals the VideoFrameSubmitter to prepare to receive BeginFrames and
   // submit video frames given by VideoFrameCompositor.
   virtual void EnableSubmission(
-      const viz::FrameSinkId& id,
+      const viz::SurfaceId& id,
       media::VideoRotation rotation,
       blink::WebFrameSinkDestroyedCallback frame_sink_destroyed_callback);
 
@@ -150,9 +154,10 @@
   // Ran on the |task_runner_| to initalize |submitter_|;
   void InitializeSubmitter();
 
+  // Signals the VideoFrameSubmitter to stop submitting frames.
+  void UpdateSubmissionState(bool);
+
   // Indicates whether the endpoint for the VideoFrame exists.
-  // TODO(lethalantidote): Update this function to read creation/destruction
-  // signals of the SurfaceLayerImpl.
   bool IsClientSinkAvailable();
 
   // Called on the compositor thread in response to Start() or Stop() calls;
@@ -201,6 +206,7 @@
   base::TimeDelta last_interval_;
   base::TimeTicks last_background_render_;
   OnNewProcessedFrameCB new_processed_frame_cb_;
+  cc::UpdateSubmissionStateCB update_submission_state_callback_;
 
   // Set on the compositor thread, but also read on the media thread.
   base::Lock current_frame_lock_;
diff --git a/media/blink/video_frame_compositor_unittest.cc b/media/blink/video_frame_compositor_unittest.cc
index 8585ce9..237be8d 100644
--- a/media/blink/video_frame_compositor_unittest.cc
+++ b/media/blink/video_frame_compositor_unittest.cc
@@ -28,11 +28,12 @@
   // blink::WebVideoFrameSubmitter implementation.
   void StopUsingProvider() override {}
   MOCK_METHOD2(EnableSubmission,
-               void(viz::FrameSinkId, blink::WebFrameSinkDestroyedCallback));
+               void(viz::SurfaceId, blink::WebFrameSinkDestroyedCallback));
   MOCK_METHOD0(StartRendering, void());
   MOCK_METHOD0(StopRendering, void());
   MOCK_METHOD1(Initialize, void(cc::VideoFrameProvider*));
   MOCK_METHOD1(SetRotation, void(media::VideoRotation));
+  MOCK_METHOD1(UpdateSubmissionState, void(bool));
   void DidReceiveFrame() override { ++did_receive_frame_count_; }
 
   int did_receive_frame_count() { return did_receive_frame_count_; }
@@ -69,8 +70,8 @@
       base::RunLoop().RunUntilIdle();
       EXPECT_CALL(*submitter_,
                   SetRotation(Eq(media::VideoRotation::VIDEO_ROTATION_90)));
-      EXPECT_CALL(*submitter_, EnableSubmission(Eq(viz::FrameSinkId(1, 1)), _));
-      compositor_->EnableSubmission(viz::FrameSinkId(1, 1),
+      EXPECT_CALL(*submitter_, EnableSubmission(Eq(viz::SurfaceId()), _));
+      compositor_->EnableSubmission(viz::SurfaceId(),
                                     media::VideoRotation::VIDEO_ROTATION_90,
                                     base::BindRepeating([] {}));
     }
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index d1a69db..49b1395 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1630,13 +1630,15 @@
     } else {
       DCHECK(!bridge_);
 
-      bridge_ = std::move(create_bridge_callback_).Run(this);
+      bridge_ = std::move(create_bridge_callback_)
+                    .Run(this, compositor_->GetUpdateSubmissionStateCallback());
       bridge_->CreateSurfaceLayer();
+
       vfc_task_runner_->PostTask(
           FROM_HERE,
           base::BindOnce(
               &VideoFrameCompositor::EnableSubmission,
-              base::Unretained(compositor_.get()), bridge_->GetFrameSinkId(),
+              base::Unretained(compositor_.get()), bridge_->GetSurfaceId(),
               pipeline_metadata_.video_decoder_config.video_rotation(),
               BindToCurrentLoop(base::BindRepeating(
                   &WebMediaPlayerImpl::OnFrameSinkDestroyed, AsWeakPtr()))));
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 3872617..5af1fefd 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -24,6 +24,7 @@
 #include "base/timer/elapsed_timer.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
+#include "cc/layers/surface_layer.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "media/base/media_observer.h"
 #include "media/base/media_tracks.h"
@@ -866,7 +867,8 @@
   bool surface_layer_for_video_enabled_ = false;
 
   base::OnceCallback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
-      blink::WebSurfaceLayerBridgeObserver*)>
+      blink::WebSurfaceLayerBridgeObserver*,
+      cc::UpdateSubmissionStateCB)>
       create_bridge_callback_;
 
   base::CancelableOnceCallback<void(base::TimeTicks)> frame_time_report_cb_;
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 5d59dba..eb1bbe3 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -294,10 +294,10 @@
  public:
   MOCK_CONST_METHOD0(GetCcLayer, cc::Layer*());
   MOCK_CONST_METHOD0(GetFrameSinkId, const viz::FrameSinkId&());
+  MOCK_CONST_METHOD0(GetSurfaceId, const viz::SurfaceId&());
   MOCK_METHOD0(ClearSurfaceId, void());
   MOCK_METHOD1(SetContentsOpaque, void(bool));
   MOCK_METHOD0(CreateSurfaceLayer, void());
-  MOCK_CONST_METHOD0(GetSurfaceId, const viz::SurfaceId&());
 };
 
 class MockVideoFrameCompositor : public VideoFrameCompositor {
@@ -311,7 +311,7 @@
   void SetOnNewProcessedFrameCallback(OnNewProcessedFrameCB cb) override {}
   MOCK_METHOD0(GetCurrentFrameAndUpdateIfStale, scoped_refptr<VideoFrame>());
   MOCK_METHOD3(EnableSubmission,
-               void(const viz::FrameSinkId&,
+               void(const viz::SurfaceId&,
                     media::VideoRotation,
                     blink::WebFrameSinkDestroyedCallback));
 };
@@ -379,9 +379,8 @@
         kMaxKeyframeDistanceToDisableBackgroundVideo,
         kMaxKeyframeDistanceToDisableBackgroundVideoMSE, false, false,
         std::move(provider),
-        base::BindRepeating(
-            &WebMediaPlayerImplTest::CreateMockSurfaceLayerBridge,
-            base::Unretained(this)),
+        base::BindOnce(&WebMediaPlayerImplTest::CreateMockSurfaceLayerBridge,
+                       base::Unretained(this)),
         viz::TestContextProvider::Create(),
         base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo));
 
@@ -416,7 +415,8 @@
 
  protected:
   std::unique_ptr<blink::WebSurfaceLayerBridge> CreateMockSurfaceLayerBridge(
-      blink::WebSurfaceLayerBridgeObserver*) {
+      blink::WebSurfaceLayerBridgeObserver*,
+      cc::UpdateSubmissionStateCB) {
     return std::move(surface_layer_bridge_);
   }
 
@@ -768,8 +768,8 @@
   if (base::FeatureList::IsEnabled(kUseSurfaceLayerForVideo)) {
     EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
     EXPECT_CALL(client_, SetCcLayer(_)).Times(0);
-    EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
-        .WillOnce(ReturnRef(frame_sink_id_));
+    EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId())
+        .WillOnce(ReturnRef(surface_id_));
     EXPECT_CALL(*compositor_, EnableSubmission(_, _, _));
     EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
   }
@@ -1166,7 +1166,7 @@
 
   if (base::FeatureList::IsEnabled(media::kUseSurfaceLayerForVideo)) {
     EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer()).Times(0);
-    EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId()).Times(0);
+    EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId()).Times(0);
     EXPECT_CALL(*compositor_, EnableSubmission(_, _, _)).Times(0);
   }
 
@@ -1184,8 +1184,8 @@
   if (base::FeatureList::IsEnabled(kUseSurfaceLayerForVideo)) {
     EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
     EXPECT_CALL(client_, SetCcLayer(_)).Times(0);
-    EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
-        .WillOnce(ReturnRef(frame_sink_id_));
+    EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId())
+        .WillOnce(ReturnRef(surface_id_));
     EXPECT_CALL(*compositor_, EnableSubmission(_, _, _));
     EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
   } else {
@@ -1211,8 +1211,8 @@
   if (base::FeatureList::IsEnabled(kUseSurfaceLayerForVideo)) {
     EXPECT_CALL(client_, SetCcLayer(_)).Times(0);
     EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
-    EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
-        .WillOnce(ReturnRef(frame_sink_id_));
+    EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId())
+        .WillOnce(ReturnRef(surface_id_));
     EXPECT_CALL(*compositor_, EnableSubmission(_, _, _));
     EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
   } else {
@@ -1239,8 +1239,8 @@
   if (base::FeatureList::IsEnabled(kUseSurfaceLayerForVideo)) {
     EXPECT_CALL(client_, SetCcLayer(_)).Times(0);
     EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
-    EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
-        .WillOnce(ReturnRef(frame_sink_id_));
+    EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId())
+        .WillOnce(ReturnRef(surface_id_));
     EXPECT_CALL(*compositor_, EnableSubmission(_, _, _));
     EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
   } else {
@@ -1315,8 +1315,8 @@
   if (base::FeatureList::IsEnabled(kUseSurfaceLayerForVideo)) {
     EXPECT_CALL(client_, SetCcLayer(_)).Times(0);
     EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
-    EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
-        .WillOnce(ReturnRef(frame_sink_id_));
+    EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId())
+        .WillOnce(ReturnRef(surface_id_));
     EXPECT_CALL(*compositor_, EnableSubmission(_, _, _));
     EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
   } else {
@@ -1353,8 +1353,8 @@
 
   EXPECT_CALL(client_, SetCcLayer(_)).Times(0);
   EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
-  EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
-      .WillOnce(ReturnRef(frame_sink_id_));
+  EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId())
+      .WillOnce(ReturnRef(surface_id_));
   EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
   EXPECT_CALL(*compositor_, EnableSubmission(_, _, _));
 
@@ -1392,10 +1392,8 @@
   InitializeWebMediaPlayerImpl();
 
   EXPECT_CALL(*surface_layer_bridge_ptr_, CreateSurfaceLayer());
-  EXPECT_CALL(*surface_layer_bridge_ptr_, GetFrameSinkId())
-      .WillOnce(ReturnRef(frame_sink_id_));
   EXPECT_CALL(*surface_layer_bridge_ptr_, GetSurfaceId())
-      .WillOnce(ReturnRef(surface_id_));
+      .WillRepeatedly(ReturnRef(surface_id_));
   EXPECT_CALL(*compositor_, EnableSubmission(_, _, _));
   EXPECT_CALL(*surface_layer_bridge_ptr_, SetContentsOpaque(false));
 
diff --git a/media/blink/webmediaplayer_params.cc b/media/blink/webmediaplayer_params.cc
index 9b6a730..0436885 100644
--- a/media/blink/webmediaplayer_params.cc
+++ b/media/blink/webmediaplayer_params.cc
@@ -29,7 +29,8 @@
     bool embedded_media_experience_enabled,
     mojom::MediaMetricsProviderPtr metrics_provider,
     base::OnceCallback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
-        blink::WebSurfaceLayerBridgeObserver*)> create_bridge_callback,
+        blink::WebSurfaceLayerBridgeObserver*,
+        cc::UpdateSubmissionStateCB)> create_bridge_callback,
     scoped_refptr<viz::ContextProvider> context_provider,
     bool use_surface_layer_for_video)
     : defer_load_cb_(defer_load_cb),
diff --git a/media/blink/webmediaplayer_params.h b/media/blink/webmediaplayer_params.h
index 256532d80..0793f19 100644
--- a/media/blink/webmediaplayer_params.h
+++ b/media/blink/webmediaplayer_params.h
@@ -13,6 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "cc/layers/surface_layer.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "media/base/media_log.h"
 #include "media/base/media_observer.h"
@@ -81,7 +82,8 @@
       bool embedded_media_experience_enabled,
       mojom::MediaMetricsProviderPtr metrics_provider,
       base::OnceCallback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
-          blink::WebSurfaceLayerBridgeObserver*)> bridge_callback,
+          blink::WebSurfaceLayerBridgeObserver*,
+          cc::UpdateSubmissionStateCB)> bridge_callback,
       scoped_refptr<viz::ContextProvider> context_provider,
       bool use_surface_layer_for_video);
 
@@ -152,7 +154,8 @@
   }
 
   base::OnceCallback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
-      blink::WebSurfaceLayerBridgeObserver*)>
+      blink::WebSurfaceLayerBridgeObserver*,
+      cc::UpdateSubmissionStateCB)>
   create_bridge_callback() {
     return std::move(create_bridge_callback_);
   }
@@ -185,7 +188,8 @@
   const bool embedded_media_experience_enabled_;
   mojom::MediaMetricsProviderPtr metrics_provider_;
   base::OnceCallback<std::unique_ptr<blink::WebSurfaceLayerBridge>(
-      blink::WebSurfaceLayerBridgeObserver*)>
+      blink::WebSurfaceLayerBridgeObserver*,
+      cc::UpdateSubmissionStateCB)>
       create_bridge_callback_;
   scoped_refptr<viz::ContextProvider> context_provider_;
   bool use_surface_layer_for_video_;
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 9d9b3d14..b6cf674 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -550,6 +550,12 @@
       "jpeg_decode_accelerator_unittest.cc",
       "test/video_accelerator_unittest_helpers.h",
     ]
+    data = [
+      "//media/test/data/peach_pi-1280x720.jpg",
+      "//media/test/data/peach_pi-40x23.jpg",
+      "//media/test/data/peach_pi-41x22.jpg",
+      "//media/test/data/peach_pi-41x23.jpg",
+    ]
     if (use_x11) {
       deps += [ "//ui/gfx/x" ]
     }
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index efb9234..952c9a7 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -872,14 +872,16 @@
         size_t bytes_per_row = viz::ResourceSizes::CheckedWidthInBytes<size_t>(
             video_frame->coded_size().width(), viz::ResourceFormat::RGBA_8888);
         size_t needed_size = bytes_per_row * video_frame->coded_size().height();
-        if (upload_pixels_.size() < needed_size) {
-          // Clear before resizing to avoid memcpy.
-          upload_pixels_.clear();
-          upload_pixels_.resize(needed_size);
+        if (upload_pixels_size_ < needed_size) {
+          // Free the existing data first so that the memory can be reused,
+          // if possible. Note that the new array is purposely not initialized.
+          upload_pixels_.reset();
+          upload_pixels_.reset(new uint8_t[needed_size]);
+          upload_pixels_size_ = needed_size;
         }
 
         PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
-            video_frame.get(), &upload_pixels_[0], bytes_per_row);
+            video_frame.get(), upload_pixels_.get(), bytes_per_row);
 
         // Copy pixels into texture.
         auto* gl = context_provider_->ContextGL();
@@ -889,7 +891,7 @@
         gl->TexSubImage2D(
             hardware_resource->texture_target(), 0, 0, 0, plane_size.width(),
             plane_size.height(), GLDataFormat(viz::ResourceFormat::RGBA_8888),
-            GLDataType(viz::ResourceFormat::RGBA_8888), &upload_pixels_[0]);
+            GLDataType(viz::ResourceFormat::RGBA_8888), upload_pixels_.get());
       }
       plane_resource->SetUniqueId(video_frame->unique_id(), 0);
     }
@@ -997,10 +999,12 @@
       // Avoid malloc for each frame/plane if possible.
       const size_t needed_size =
           upload_image_stride * resource_size_pixels.height();
-      if (upload_pixels_.size() < needed_size) {
-        // Clear before resizing to avoid memcpy.
-        upload_pixels_.clear();
-        upload_pixels_.resize(needed_size);
+      if (upload_pixels_size_ < needed_size) {
+        // Free the existing data first so that the memory can be reused,
+        // if possible. Note that the new array is purposely not initialized.
+        upload_pixels_.reset();
+        upload_pixels_.reset(new uint8_t[needed_size]);
+        upload_pixels_size_ = needed_size;
       }
 
       if (plane_resource_format == viz::LUMINANCE_F16) {
@@ -1017,7 +1021,7 @@
         const int scale = 0x10000 >> (bits_per_channel - 8);
         libyuv::Convert16To8Plane(
             reinterpret_cast<uint16_t*>(video_frame->data(i)),
-            video_stride_bytes / 2, upload_pixels_.data(), upload_image_stride,
+            video_stride_bytes / 2, upload_pixels_.get(), upload_image_stride,
             scale, bytes_per_row, resource_size_pixels.height());
       } else {
         // Make a copy to reconcile stride, size and format being equal.
@@ -1025,12 +1029,12 @@
         DCHECK(plane_resource_format == viz::LUMINANCE_8 ||
                plane_resource_format == viz::RED_8);
         libyuv::CopyPlane(video_frame->data(i), video_stride_bytes,
-                          upload_pixels_.data(), upload_image_stride,
+                          upload_pixels_.get(), upload_image_stride,
                           resource_size_pixels.width(),
                           resource_size_pixels.height());
       }
 
-      pixels = &upload_pixels_[0];
+      pixels = upload_pixels_.get();
     }
 
     // Copy pixels into texture. TexSubImage2D() is applicable because
diff --git a/media/renderers/video_resource_updater.h b/media/renderers/video_resource_updater.h
index 4a4e6a5..9ea3f50 100644
--- a/media/renderers/video_resource_updater.h
+++ b/media/renderers/video_resource_updater.h
@@ -196,7 +196,8 @@
   uint32_t next_plane_resource_id_ = 1;
 
   // Temporary pixel buffer when converting between formats.
-  std::vector<uint8_t> upload_pixels_;
+  std::unique_ptr<uint8_t[]> upload_pixels_;
+  size_t upload_pixels_size_ = 0;
 
   VideoFrameResourceType frame_resource_type_;
 
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
index e3345922..e6297a1 100644
--- a/net/disk_cache/simple/simple_entry_impl.cc
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -391,8 +391,10 @@
     return net::ERR_INVALID_ARGUMENT;
   }
 
-  // TODO(felipeg): Optimization: Add support for truly parallel read
-  // operations.
+  // If this is the only operation, bypass the queue, and also see if there is
+  // in-memory data to handle it synchronously. In principle, multiple reads can
+  // be parallelized, but past studies have shown that parallelizable ones
+  // happen <1% of the time, so it's probably not worth the effort.
   bool alone_in_queue =
       pending_operations_.size() == 0 && state_ == STATE_READY;
 
@@ -402,8 +404,7 @@
   }
 
   pending_operations_.push(SimpleEntryOperation::ReadOperation(
-      this, stream_index, offset, buf_len, buf, std::move(callback),
-      alone_in_queue));
+      this, stream_index, offset, buf_len, buf, std::move(callback)));
   RunNextOperationIfNeeded();
   return net::ERR_IO_PENDING;
 }
@@ -453,7 +454,8 @@
   // actually run the write operation that sets the stream size. It also
   // prevents from previous possibly-conflicting writes that could be stacked
   // in the |pending_operations_|. We could optimize this for when we have
-  // only read operations enqueued.
+  // only read operations enqueued, but past studies have shown that that such
+  // parallelizable cases are very rare.
   const bool optimistic =
       (use_optimistic_operations_ && state_ == STATE_READY &&
        pending_operations_.size() == 0);
@@ -567,7 +569,6 @@
   // measured, but the ownership of SimpleSynchronousEntry isn't straightforward
   return sizeof(SimpleSynchronousEntry) +
          base::trace_event::EstimateMemoryUsage(pending_operations_) +
-         base::trace_event::EstimateMemoryUsage(executing_operation_) +
          (stream_0_data_ ? stream_0_data_->capacity() : 0) +
          (stream_1_prefetch_data_ ? stream_1_prefetch_data_->capacity() : 0);
 }
@@ -665,13 +666,11 @@
         CloseInternal();
         break;
       case SimpleEntryOperation::TYPE_READ:
-        RecordReadIsParallelizable(*operation);
         ReadDataInternal(/* sync_possible= */ false, operation->index(),
                          operation->offset(), operation->buf(),
                          operation->length(), operation->ReleaseCallback());
         break;
       case SimpleEntryOperation::TYPE_WRITE:
-        RecordWriteDependencyType(*operation);
         WriteDataInternal(operation->index(), operation->offset(),
                           operation->buf(), operation->length(),
                           operation->ReleaseCallback(), operation->truncate());
@@ -697,10 +696,6 @@
       default:
         NOTREACHED();
     }
-    // The operation is kept for histograms. Makes sure it does not leak
-    // resources.
-    executing_operation_.swap(operation);
-    executing_operation_->ReleaseReferences();
     // |this| may have been deleted.
   }
 }
@@ -1538,77 +1533,6 @@
   return file_size;
 }
 
-void SimpleEntryImpl::RecordReadIsParallelizable(
-    const SimpleEntryOperation& operation) const {
-  if (!executing_operation_)
-    return;
-  // Used in histograms, please only add entries at the end.
-  enum ReadDependencyType {
-    // READ_STANDALONE = 0, Deprecated.
-    READ_FOLLOWS_READ = 1,
-    READ_FOLLOWS_CONFLICTING_WRITE = 2,
-    READ_FOLLOWS_NON_CONFLICTING_WRITE = 3,
-    READ_FOLLOWS_OTHER = 4,
-    READ_ALONE_IN_QUEUE = 5,
-    READ_DEPENDENCY_TYPE_MAX = 6,
-  };
-
-  ReadDependencyType type = READ_FOLLOWS_OTHER;
-  if (operation.alone_in_queue()) {
-    type = READ_ALONE_IN_QUEUE;
-  } else if (executing_operation_->type() == SimpleEntryOperation::TYPE_READ) {
-    type = READ_FOLLOWS_READ;
-  } else if (executing_operation_->type() == SimpleEntryOperation::TYPE_WRITE) {
-    if (executing_operation_->ConflictsWith(operation))
-      type = READ_FOLLOWS_CONFLICTING_WRITE;
-    else
-      type = READ_FOLLOWS_NON_CONFLICTING_WRITE;
-  }
-  SIMPLE_CACHE_UMA(ENUMERATION,
-                   "ReadIsParallelizable", cache_type_,
-                   type, READ_DEPENDENCY_TYPE_MAX);
-}
-
-void SimpleEntryImpl::RecordWriteDependencyType(
-    const SimpleEntryOperation& operation) const {
-  if (!executing_operation_)
-    return;
-  // Used in histograms, please only add entries at the end.
-  enum WriteDependencyType {
-    WRITE_OPTIMISTIC = 0,
-    WRITE_FOLLOWS_CONFLICTING_OPTIMISTIC = 1,
-    WRITE_FOLLOWS_NON_CONFLICTING_OPTIMISTIC = 2,
-    WRITE_FOLLOWS_CONFLICTING_WRITE = 3,
-    WRITE_FOLLOWS_NON_CONFLICTING_WRITE = 4,
-    WRITE_FOLLOWS_CONFLICTING_READ = 5,
-    WRITE_FOLLOWS_NON_CONFLICTING_READ = 6,
-    WRITE_FOLLOWS_OTHER = 7,
-    WRITE_DEPENDENCY_TYPE_MAX = 8,
-  };
-
-  WriteDependencyType type = WRITE_FOLLOWS_OTHER;
-  if (operation.optimistic()) {
-    type = WRITE_OPTIMISTIC;
-  } else if (executing_operation_->type() == SimpleEntryOperation::TYPE_READ ||
-             executing_operation_->type() == SimpleEntryOperation::TYPE_WRITE) {
-    bool conflicting = executing_operation_->ConflictsWith(operation);
-
-    if (executing_operation_->type() == SimpleEntryOperation::TYPE_READ) {
-      type = conflicting ? WRITE_FOLLOWS_CONFLICTING_READ
-                         : WRITE_FOLLOWS_NON_CONFLICTING_READ;
-    } else if (executing_operation_->optimistic()) {
-      type = conflicting ? WRITE_FOLLOWS_CONFLICTING_OPTIMISTIC
-                         : WRITE_FOLLOWS_NON_CONFLICTING_OPTIMISTIC;
-    } else {
-      type = conflicting ? WRITE_FOLLOWS_CONFLICTING_WRITE
-                         : WRITE_FOLLOWS_NON_CONFLICTING_WRITE;
-    }
-  }
-  SIMPLE_CACHE_UMA(ENUMERATION,
-                   "WriteDependencyType", cache_type_,
-                   type, WRITE_DEPENDENCY_TYPE_MAX);
-}
-
 int SimpleEntryImpl::ReadFromBuffer(net::GrowableIOBuffer* in_buf,
                                     int offset,
                                     int buf_len,
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h
index c1323e7..f00f6902 100644
--- a/net/disk_cache/simple/simple_entry_impl.h
+++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -330,10 +330,6 @@
 
   int64_t GetDiskUsage() const;
 
-  // Used to report histograms.
-  void RecordReadIsParallelizable(const SimpleEntryOperation& operation) const;
-  void RecordWriteDependencyType(const SimpleEntryOperation& operation) const;
-
   // Completes a read from the stream data kept in memory, logging metrics
   // and updating metadata. Returns the # of bytes read successfully.
   // This asumes the caller has already range-checked offset and buf_len
@@ -424,8 +420,6 @@
 
   net::NetLogWithSource net_log_;
 
-  std::unique_ptr<SimpleEntryOperation> executing_operation_;
-
   // Unlike other streams, stream 0 data is read from the disk when the entry is
   // opened, and then kept in memory. All read/write operations on stream 0
   // affect the |stream_0_data_| buffer. When the entry is closed,
diff --git a/net/disk_cache/simple/simple_entry_operation.cc b/net/disk_cache/simple/simple_entry_operation.cc
index 5e48343..e0c964e 100644
--- a/net/disk_cache/simple/simple_entry_operation.cc
+++ b/net/disk_cache/simple/simple_entry_operation.cc
@@ -13,27 +13,6 @@
 
 namespace disk_cache {
 
-namespace {
-
-bool IsReadWriteType(unsigned int type) {
-  return type == SimpleEntryOperation::TYPE_READ ||
-         type == SimpleEntryOperation::TYPE_WRITE ||
-         type == SimpleEntryOperation::TYPE_READ_SPARSE ||
-         type == SimpleEntryOperation::TYPE_WRITE_SPARSE;
-}
-
-bool IsReadType(unsigned type) {
-  return type == SimpleEntryOperation::TYPE_READ ||
-         type == SimpleEntryOperation::TYPE_READ_SPARSE;
-}
-
-bool IsSparseType(unsigned type) {
-  return type == SimpleEntryOperation::TYPE_READ_SPARSE ||
-         type == SimpleEntryOperation::TYPE_WRITE_SPARSE;
-}
-
-}  // anonymous namespace
-
 SimpleEntryOperation::SimpleEntryOperation(SimpleEntryOperation&& other)
     : entry_(std::move(other.entry_)),
       buf_(std::move(other.buf_)),
@@ -47,8 +26,7 @@
       have_index_(other.have_index_),
       index_(other.index_),
       truncate_(other.truncate_),
-      optimistic_(other.optimistic_),
-      alone_in_queue_(other.alone_in_queue_) {}
+      optimistic_(other.optimistic_) {}
 
 SimpleEntryOperation::~SimpleEntryOperation() = default;
 
@@ -59,8 +37,7 @@
     net::CompletionOnceCallback callback,
     Entry** out_entry) {
   return SimpleEntryOperation(entry, NULL, std::move(callback), out_entry, 0, 0,
-                              0, NULL, TYPE_OPEN, have_index, 0, false, false,
-                              false);
+                              0, NULL, TYPE_OPEN, have_index, 0, false, false);
 }
 
 // static
@@ -70,7 +47,7 @@
     net::CompletionOnceCallback callback,
     Entry** out_entry) {
   return SimpleEntryOperation(entry, NULL, std::move(callback), out_entry, 0, 0,
-                              0, NULL, TYPE_CREATE, have_index, 0, false, false,
+                              0, NULL, TYPE_CREATE, have_index, 0, false,
                               false);
 }
 
@@ -78,8 +55,7 @@
 SimpleEntryOperation SimpleEntryOperation::CloseOperation(
     SimpleEntryImpl* entry) {
   return SimpleEntryOperation(entry, NULL, CompletionOnceCallback(), NULL, 0, 0,
-                              0, NULL, TYPE_CLOSE, false, 0, false, false,
-                              false);
+                              0, NULL, TYPE_CLOSE, false, 0, false, false);
 }
 
 // static
@@ -89,11 +65,10 @@
     int offset,
     int length,
     net::IOBuffer* buf,
-    CompletionOnceCallback callback,
-    bool alone_in_queue) {
+    CompletionOnceCallback callback) {
   return SimpleEntryOperation(entry, buf, std::move(callback), NULL, offset, 0,
                               length, NULL, TYPE_READ, false, index, false,
-                              false, alone_in_queue);
+                              false);
 }
 
 // static
@@ -108,7 +83,7 @@
     CompletionOnceCallback callback) {
   return SimpleEntryOperation(entry, buf, std::move(callback), NULL, offset, 0,
                               length, NULL, TYPE_WRITE, false, index, truncate,
-                              optimistic, false);
+                              optimistic);
 }
 
 // static
@@ -120,7 +95,7 @@
     CompletionOnceCallback callback) {
   return SimpleEntryOperation(entry, buf, std::move(callback), NULL, 0,
                               sparse_offset, length, NULL, TYPE_READ_SPARSE,
-                              false, 0, false, false, false);
+                              false, 0, false, false);
 }
 
 // static
@@ -132,7 +107,7 @@
     CompletionOnceCallback callback) {
   return SimpleEntryOperation(entry, buf, std::move(callback), NULL, 0,
                               sparse_offset, length, NULL, TYPE_WRITE_SPARSE,
-                              false, 0, false, false, false);
+                              false, 0, false, false);
 }
 
 // static
@@ -142,9 +117,9 @@
     int length,
     int64_t* out_start,
     CompletionOnceCallback callback) {
-  return SimpleEntryOperation(
-      entry, NULL, std::move(callback), NULL, 0, sparse_offset, length,
-      out_start, TYPE_GET_AVAILABLE_RANGE, false, 0, false, false, false);
+  return SimpleEntryOperation(entry, NULL, std::move(callback), NULL, 0,
+                              sparse_offset, length, out_start,
+                              TYPE_GET_AVAILABLE_RANGE, false, 0, false, false);
 }
 
 // static
@@ -161,53 +136,9 @@
   const int index = 0;
   const bool truncate = false;
   const bool optimistic = false;
-  const bool alone_in_queue = false;
-  return SimpleEntryOperation(entry, buf, std::move(callback), out_entry,
-                              offset, sparse_offset, length, out_start,
-                              TYPE_DOOM, have_index, index, truncate,
-                              optimistic, alone_in_queue);
-}
-
-bool SimpleEntryOperation::ConflictsWith(
-    const SimpleEntryOperation& other_op) const {
-  EntryOperationType other_type = other_op.type();
-
-  // Non-read/write operations conflict with everything.
-  if (!IsReadWriteType(type_) || !IsReadWriteType(other_type))
-    return true;
-
-  // Reads (sparse or otherwise) conflict with nothing.
-  if (IsReadType(type_) && IsReadType(other_type))
-    return false;
-
-  // Sparse and non-sparse operations do not conflict with each other.
-  if (IsSparseType(type_) != IsSparseType(other_type)) {
-    return false;
-  }
-
-  // There must be two read/write operations, at least one must be a write, and
-  // they must be either both non-sparse or both sparse.  Compare the streams
-  // and offsets to see whether they overlap.
-
-  if (IsSparseType(type_)) {
-    int64_t end = sparse_offset_ + length_;
-    int64_t other_op_end = other_op.sparse_offset() + other_op.length();
-    return sparse_offset_ < other_op_end && other_op.sparse_offset() < end;
-  }
-
-  if (index_ != other_op.index_)
-    return false;
-  int end = (type_ == TYPE_WRITE && truncate_) ? INT_MAX : offset_ + length_;
-  int other_op_end = (other_op.type() == TYPE_WRITE && other_op.truncate())
-                         ? INT_MAX
-                         : other_op.offset() + other_op.length();
-  return offset_ < other_op_end && other_op.offset() < end;
-}
-
-void SimpleEntryOperation::ReleaseReferences() {
-  callback_ = CompletionOnceCallback();
-  buf_ = NULL;
-  entry_ = NULL;
+  return SimpleEntryOperation(
+      entry, buf, std::move(callback), out_entry, offset, sparse_offset, length,
+      out_start, TYPE_DOOM, have_index, index, truncate, optimistic);
 }
 
 SimpleEntryOperation::SimpleEntryOperation(SimpleEntryImpl* entry,
@@ -222,8 +153,7 @@
                                            bool have_index,
                                            int index,
                                            bool truncate,
-                                           bool optimistic,
-                                           bool alone_in_queue)
+                                           bool optimistic)
     : entry_(entry),
       buf_(buf),
       callback_(std::move(callback)),
@@ -236,7 +166,6 @@
       have_index_(have_index),
       index_(index),
       truncate_(truncate),
-      optimistic_(optimistic),
-      alone_in_queue_(alone_in_queue) {}
+      optimistic_(optimistic) {}
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_entry_operation.h b/net/disk_cache/simple/simple_entry_operation.h
index 15aa52f..564195ff 100644
--- a/net/disk_cache/simple/simple_entry_operation.h
+++ b/net/disk_cache/simple/simple_entry_operation.h
@@ -55,8 +55,7 @@
                                             int offset,
                                             int length,
                                             net::IOBuffer* buf,
-                                            CompletionOnceCallback callback,
-                                            bool alone_in_queue);
+                                            CompletionOnceCallback callback);
   static SimpleEntryOperation WriteOperation(SimpleEntryImpl* entry,
                                              int index,
                                              int offset,
@@ -86,11 +85,6 @@
   static SimpleEntryOperation DoomOperation(SimpleEntryImpl* entry,
                                             CompletionOnceCallback callback);
 
-  bool ConflictsWith(const SimpleEntryOperation& other_op) const;
-  // Releases all references. After calling this operation, SimpleEntryOperation
-  // will only hold POD members.
-  void ReleaseReferences();
-
   EntryOperationType type() const {
     return static_cast<EntryOperationType>(type_);
   }
@@ -106,7 +100,6 @@
   net::IOBuffer* buf() { return buf_.get(); }
   bool truncate() const { return truncate_; }
   bool optimistic() const { return optimistic_; }
-  bool alone_in_queue() const { return alone_in_queue_; }
 
  private:
   SimpleEntryOperation(SimpleEntryImpl* entry,
@@ -121,8 +114,7 @@
                        bool have_index,
                        int index,
                        bool truncate,
-                       bool optimistic,
-                       bool alone_in_queue);
+                       bool optimistic);
 
   // This ensures entry will not be deleted until the operation has ran.
   scoped_refptr<SimpleEntryImpl> entry_;
@@ -148,8 +140,6 @@
   // Used only in write operations.
   const bool truncate_;
   const bool optimistic_;
-  // Used only in SimpleCache.ReadIsParallelizable histogram.
-  const bool alone_in_queue_;
 };
 
 }  // namespace disk_cache
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 5a6fec7..1fbe018 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -1251,7 +1251,6 @@
     { "name": "s-c.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "security-carpet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sherbers.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "tittelbach.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tomfisher.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wunderlist.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "zotero.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -11964,7 +11963,6 @@
     { "name": "natenom.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ncpc.gov", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "netnodes.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "nepustil.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nerdtime.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "netsoins.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "netronix.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -13785,7 +13783,6 @@
     { "name": "gopokego.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gospelofmark.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "goto.world", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "gottcode.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "gracethrufaith.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "grademymac.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "grandwailea.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -14813,7 +14810,6 @@
     { "name": "webduck.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "webeconomia.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "weblate.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "webmail.info", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "webmandesign.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "webtropia.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "weddywood.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -15943,14 +15939,12 @@
     { "name": "mstiles92.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "migeeks.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "map4jena.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "muevetumundo.com.mx", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mistreaded.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "msh100.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "megamisja.pl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "muspla.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mkoppmann.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "manage4all.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "multimarques.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mainframeserver.space", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "miyoshi-kikaku.co.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mikek.work", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -15984,7 +15978,6 @@
     { "name": "mostlyharmless.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ndmath.club", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mihnea.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "naturesharvestbread.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "megumico.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nanch.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "muonium.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -16107,7 +16100,6 @@
     { "name": "pepsicoemployeepreferencesurvey.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nnote.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "open-mx.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "paradiselost.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "partyvan.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "opensource-cms.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "parckwart.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -16129,11 +16121,9 @@
     { "name": "pe-kyousai.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "papygeek.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "photoancestry.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "peetah.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "npm.li", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "perezdecastro.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pe-bank.co.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "paper-republic.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paul-kerebel.pro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pnut.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paulshir.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -27037,7 +27027,6 @@
     { "name": "patsytoforyou.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paulbramhall.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paulbunyanmls.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "paulscustomauto.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paulus-foto.pl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pautadiaria.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pavando.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28390,7 +28379,6 @@
     { "name": "7f-wgg.cf", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "7733445.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "44scc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "00881919.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "5kraceforals.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "22scc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "55scc.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -28765,7 +28753,6 @@
     { "name": "arfad.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "beyond-infinity.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "beraru.tk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bewegungsfluss.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "birdbrowser.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bestwarezone.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "biletua.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -29879,65 +29866,30 @@
     { "name": "herzig.cc", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hansmund.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "heribe-maruo.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71806.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71801.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71812.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "headlinepublishing.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg718.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71803.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71802.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71809.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71807.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71835.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "heimatverein-eitensheim.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg0088.vip", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71811.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71819.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71815.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "helber-it-services.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hickorywinecellar.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71836.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71833.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hermanbrouwer.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hingle.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71822.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71858.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71805.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71813.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hg71839.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hindmanfuneralhomes.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "herrderzeit.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hitrek.ml", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71857.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71837.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hiraku.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71852.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71851.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71860.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "holytransaction.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hga8687.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "goiaspropaganda.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71856.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hga8685.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71863.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hl7999.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hightower.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hga8587.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hga8758.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hatarisecurity.co.ke", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71861.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "highland-webcams.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "houraiteahouse.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hga8689.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "holodeck.us", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hokify.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "happyagain.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hokify.at", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hga8586.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hokify.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hga8757.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "highlegshop.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hg71850.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "honeyhaw.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hollo.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hootworld.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -30954,7 +30906,6 @@
     { "name": "pdfconvert.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "noc.wang", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "party-kneipe-bar.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "patadanabouca.pw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paf-events.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pawelnazaruk.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paranoidpenguin.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -32046,7 +31997,6 @@
     { "name": "wesayyesprogram.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "widdleguy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "whilsttraveling.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "wellies.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wasielewski.com.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "werhatunsverraten.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "wertheimer-burgrock.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -33626,7 +33576,6 @@
     { "name": "nba2k.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nba2k.live", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nba2k.download", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "murmel.it", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "myliveupdates.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "microbiote-insectes-vecteurs.group", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nba2k.tw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -35567,7 +35516,6 @@
     { "name": "pagedesignhub.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pagedesignpro.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pagedesignshop.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "paichai.space", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "passionatehorsemanship.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "pastorsuico.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "paul-bronski.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -40933,7 +40881,6 @@
     { "name": "fluteandpianoteaching.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flygon.pink", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "formadmin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "freespace.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "freizeitplaza.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fuckyoupaypal.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fwdx.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41933,7 +41880,6 @@
     { "name": "546802.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "598598598.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "788da.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "7bwin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "81uc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "8888esb.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "8901178.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41967,7 +41913,6 @@
     { "name": "aceanswering.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "acroso.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "actom.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "actom.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "adamcoffee.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "adhd-inattentive.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "admin-forms.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -42770,7 +42715,6 @@
     { "name": "fireworkcoaching.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fitfitup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fiveboosts.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "fixvoltage.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flamero.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "flangaapis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fleurenplume.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -50387,12 +50331,6 @@
     { "name": "btsapem.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bttorj45.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bullshitmail.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "by2230.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "by2238.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "by2239.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "by2251.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "by2253.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "by2254.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "bytesign.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cad-noerdlingen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "cangku.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -50647,7 +50585,6 @@
     { "name": "iambozboz.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iankmusic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ibaq.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ibwin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "iftarsaati.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ifttl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ignacjanskiednimlodziezy.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -53293,7 +53230,6 @@
     { "name": "windsorite.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "workmart.mx", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "wumai-p.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "www-hg718.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xiecongan.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "xrwracing-france.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yassine-ayari.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54055,7 +53991,6 @@
     { "name": "taoways.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tasks.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "tearoomlints.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "techcenturion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "techniclab.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "techniclab.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "technik-boeckmann.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54211,6 +54146,358 @@
     { "name": "zlaty-tyden.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zlatytyden.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zq789.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1288fc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "12photos.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "20zq.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "319k3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3ank.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5518k3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "5986fc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "64970.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "6616fc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "6666yh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "72ty.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "74th.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "82ty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8688fc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8818k3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "8989k3.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "a0print.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "absolutehosting.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ace.media", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acquistareviagragenericoitalia.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "afeefzarapackages.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "albertonplumber24-7.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "allthecryptonews.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "alquiaga.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amcangroup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amyfoundhermann.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "andrewbdesign.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "anegabawa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "angelesydemonios.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "animan.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aoaprograms.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aperim.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "autoschadeschreuder.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "axtudo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "balilingo.ooo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "banes.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bardiel.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "behindthethrills.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "benjaminkopelke.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "benz-hikaku.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bettertechinterviews.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bielefailed.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bigbendguide.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blockchain.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blogdeyugioh.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bluemtnrentalmanagement.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blyth.me.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bondank.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "brazenfol.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "breitband.bz.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bryansmith.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bscc.support", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bsdlab.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "busit.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "busuttil.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bwfc.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "c3wien.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "campbellapplianceheatingandair.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cashbook.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "catchhimandkeephim.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "catl.st", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ccavenue.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cccwien.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ceoptique.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ch47f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chaoswars.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clad.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "clangwarnings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "classroomconductor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "colorfuldots.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "consultimedia.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cordep.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "createme.com.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cromefire.myds.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cyberexplained.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "damghaem.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danielgorr.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danielt.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "danslan.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "deepz.pt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "degosoft.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "digital1world.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dipalma.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dissieux.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drchrislivingston.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dsanraffleshangbai.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "duocircle.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dwi-sued.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dziary.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "e-colle.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ecacollege.nsw.edu.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eduroam.uy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "eihaikyo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elbohlyart.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elevatoraptitudetest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emperola.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emporioonline.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "erperium.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "escape2rooms.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "estate360.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "estudioamazonico.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "etresmant.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "everpcpc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "exsora.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "exultcosmetics.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ezzhole.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fedvan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fee-hosting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fjordboge.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "flyingspaghettimonsterdonationsfund.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "freesoft-board.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "froehliche-hessen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "fulfilmentcrowd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "futuretimes.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gallifreyapp.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "garage-abri-chalet.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gcgeeks.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "genome.gov", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gigin.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "go-dutch.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "godaxen.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "golighthouse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gratisgamecards.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "guidebook.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hamcocc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "happndin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "havetherelationshipyouwant.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "herbhuang.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hotels4teams.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hrbl.lc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "html.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "humorcaliente.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hurleyhomestead.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ibericaderedes.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ickerseashop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "icmshoptrend.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ico500.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iliastsi.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imanageproducts.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "imedes.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "immobilien-zirm.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "importsagt.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "indoorcomfortteam.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infranotes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ing-buero-junk.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "internacao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iochen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iomedia.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iowaschoolofbeauty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ipad.li", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iran-geo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iranjeunesse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "islamonline.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "it-service24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "izaakbeekman.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jadchaar.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jericamacmillan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jeroldirvin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jessicahrehor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jisha.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "joaoaugusto.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jockbusuttil.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jockbusuttil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jockbusuttil.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jodaniels.photography", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jordhy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "josephbleroy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jsnfwlr.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "k7azx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kaany.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kantv1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kissmycreative.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kolbeck.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kzar.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "labtest.ltd", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lai.is", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lgbt.ventures", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lgbtqventures.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lgbtventures.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lifegrip.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "linzyjx.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "litebit.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "livesheep.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lovelychalets-peisey.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luan.ma", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lykai.ca", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "maggie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "malezan.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "manuel7espejo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "markholden.guru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "marycliffpress.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mcgaccountancy.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "med360.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meeco.kr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "megasystem.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "menhera.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mentesemprendedoras.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meo.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "messenger.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "michael.band", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "michaelband.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "michaelhrehor.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mollaretsmeningitis.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moneybird.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monzo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monzo.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mr-designer-oman.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "muell-weg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "munduch.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "munwr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "napisdata.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nazigol.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nebenbeiblog.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nevergreen.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nexril.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nextcloud.nerdpol.ovh", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nhgteam.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ninverse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "noofficewalls.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nootronerd.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nosqlzoo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nutpanda.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nwuss.okinawa", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nylevemusic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nyuusannkinn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "omar.yt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "omlmetal.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "openre.site", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "orientravelmacas.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "parav.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paul-schmidt.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "paybook.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pdfpassword.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pdfpasswort.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pearlsenroses.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pesyun.cn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "physicpezeshki.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pictureguy.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "plumbingman.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "polkhealthforanewyou.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pos.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "precept.uk.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pregono.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prioritylawyers.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "protobetatest.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "puq.moe", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "q-technologies.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "qicomidadeverdade.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "radyabkhodro.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "railtoo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rajivshah.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "restaurantmaan.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rexskz.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "riku.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "robotkvarnen.se", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "romatrip.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ronniegane.kiwi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rossmacphee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rustralasia.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saastopankki.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sac-shop.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sacrome.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saleduck.co.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saleduck.co.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saleduck.com.my", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saleduck.com.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saleduck.com.sg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saleduck.com.vn", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "samtalen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sannesfotklinikk.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saol.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sattamatkachart.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sattamatkadpboss.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sattamatkamobi.mobi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scroll.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "secretsdujeu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "semsec.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sexoyrelax.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sgsp.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shadowstack.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shugo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "siel.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sioeckes.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sjsmith.id.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sl899.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sl998.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "slate.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "solentbasketball.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spacinov.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spacivox.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spectreattack.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ss.lazio.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "starinvestors.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stijncrevits.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "strafensau.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stroeerdigital.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "studiopop.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stumeta2019.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sudo-i.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "superstropdas.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "swiftpk.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "swisstechassociation.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tabhui.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tambre.ee", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tamposign.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teengirl.pub", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tehrankey.ir", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theguitarcompany.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thehairrepublic.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "timeless-photostudio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tkirch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tobiaspahlings.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tomy.icu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tradik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "travis.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trucchibellezza.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trustocean.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tugers.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tunity.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tunnelbear.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tvipper.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "twinztech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uhappy30.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uhurl.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unp.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "urbansurvival.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vedma-praktik.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vegetariantokyo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "viljatori.fi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vir2.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webalert.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "werbeagentur.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "willvision.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wood-crafted.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wood-crafted.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "workforce.co.tz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wpsmackdown.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wtpdive.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xendo.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xiaocg.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn----zmcaltpp1mdh16i.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--fs5ak3f.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--gfrrli-yxa.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--hgbk4a00a.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--kckd0bd4a8tp27yee2e.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--mgbpkc7fz3awhe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xuanmeishe.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "youngpeopleunited.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "youtuberis.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zengdong.ren", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zny.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zone403.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/network_error_logging/network_error_logging_service.cc b/net/network_error_logging/network_error_logging_service.cc
index 34cf8900..762b185 100644
--- a/net/network_error_logging/network_error_logging_service.cc
+++ b/net/network_error_logging/network_error_logging_service.cc
@@ -61,6 +61,8 @@
 const char kApplicationPhase[] = "application";
 const char kConnectionPhase[] = "connection";
 const char kDnsPhase[] = "dns";
+
+const char kDnsAddressChangedType[] = "dns.address_changed";
 const char kHttpErrorType[] = "http.error";
 
 const struct {
@@ -147,6 +149,8 @@
   REMOVED = 12,
   SET = 13,
 
+  DISCARDED_MISSING_REMOTE_ENDPOINT = 14,
+
   MAX
 };
 
@@ -188,7 +192,9 @@
 
   // NetworkErrorLoggingService implementation:
 
-  void OnHeader(const url::Origin& origin, const std::string& value) override {
+  void OnHeader(const url::Origin& origin,
+                const IPAddress& received_ip_address,
+                const std::string& value) override {
     // NEL is only available to secure origins, so don't permit insecure origins
     // to set policies.
     if (!origin.GetURL().SchemeIsCryptographic()) {
@@ -197,6 +203,7 @@
     }
 
     OriginPolicy policy;
+    policy.received_ip_address = received_ip_address;
     HeaderOutcome outcome =
         ParseHeader(value, tick_clock_->NowTicks(), &policy);
     RecordHeaderOutcome(outcome);
@@ -217,7 +224,7 @@
     MaybeAddWildcardPolicy(origin, &inserted.first->second);
   }
 
-  void OnRequest(const RequestDetails& details) override {
+  void OnRequest(RequestDetails details) override {
     if (!reporting_service_) {
       RecordRequestOutcome(RequestOutcome::DISCARDED_NO_REPORTING_SERVICE);
       return;
@@ -270,6 +277,18 @@
       return;
     }
 
+    // If the server that handled the request is different than the server that
+    // delivered the NEL policy (as determined by their IP address), then we
+    // have to "downgrade" the NEL report, so that it only includes information
+    // about DNS resolution.
+    if (phase_string != kDnsPhase && details.server_ip.IsValid() &&
+        details.server_ip != policy->received_ip_address) {
+      phase_string = kDnsPhase;
+      type_string = kDnsAddressChangedType;
+      details.elapsed_time = base::TimeDelta();
+      details.status_code = 0;
+    }
+
     // include_subdomains policies are only allowed to report on DNS resolution
     // errors.
     if (phase_string != kDnsPhase && policy->include_subdomains) {
@@ -351,6 +370,8 @@
  private:
   // NEL Policy set by an origin.
   struct OriginPolicy {
+    IPAddress received_ip_address;
+
     // Reporting API endpoint group to which reports should be sent.
     std::string report_to;
 
@@ -594,6 +615,12 @@
 
 // static
 void NetworkErrorLoggingService::
+    RecordHeaderDiscardedForMissingRemoteEndpoint() {
+  RecordHeaderOutcome(HeaderOutcome::DISCARDED_MISSING_REMOTE_ENDPOINT);
+}
+
+// static
+void NetworkErrorLoggingService::
     RecordRequestDiscardedForNoNetworkErrorLoggingService() {
   RecordRequestOutcome(
       RequestOutcome::DISCARDED_NO_NETWORK_ERROR_LOGGING_SERVICE);
diff --git a/net/network_error_logging/network_error_logging_service.h b/net/network_error_logging/network_error_logging_service.h
index b024b31..2354a8c 100644
--- a/net/network_error_logging/network_error_logging_service.h
+++ b/net/network_error_logging/network_error_logging_service.h
@@ -90,6 +90,7 @@
   static void RecordHeaderDiscardedForNoNetworkErrorLoggingService();
   static void RecordHeaderDiscardedForInvalidSSLInfo();
   static void RecordHeaderDiscardedForCertStatusError();
+  static void RecordHeaderDiscardedForMissingRemoteEndpoint();
 
   static void RecordRequestDiscardedForNoNetworkErrorLoggingService();
 
@@ -98,16 +99,24 @@
 
   virtual ~NetworkErrorLoggingService();
 
-  // Ingests a "NEL:" header received from |orogin| with normalized value
-  // |value|. May or may not actually set a policy for that origin.
+  // Ingests a "NEL:" header received for |origin| from |received_ip_address|
+  // with normalized value |value|. May or may not actually set a policy for
+  // that origin.
   virtual void OnHeader(const url::Origin& origin,
+                        const IPAddress& received_ip_address,
                         const std::string& value) = 0;
 
   // Considers queueing a network error report for the request described in
-  // |details|. Note that Network Error Logging can report a fraction of
-  // successful requests as well (to calculate error rates), so this should be
-  // called on *all* requests.
-  virtual void OnRequest(const RequestDetails& details) = 0;
+  // |details|.  The contents of |details| might be changed, depending on the
+  // NEL policy associated with the request's origin.  Note that |details| is
+  // passed by value, so that it doesn't need to be copied in this function if
+  // it needs to be changed.  Consider using std::move to pass this parameter if
+  // the caller doesn't need to access it after this method call.
+  //
+  // Note that Network Error Logging can report a fraction of successful
+  // requests as well (to calculate error rates), so this should be called on
+  // *all* requests.
+  virtual void OnRequest(RequestDetails details) = 0;
 
   // Removes browsing data (origin policies) associated with any origin for
   // which |origin_filter| returns true.
diff --git a/net/network_error_logging/network_error_logging_service_unittest.cc b/net/network_error_logging/network_error_logging_service_unittest.cc
index 438bf7f..fdff16a9c 100644
--- a/net/network_error_logging/network_error_logging_service_unittest.cc
+++ b/net/network_error_logging/network_error_logging_service_unittest.cc
@@ -129,13 +129,16 @@
     reporting_service_.reset();
   }
 
-  NetworkErrorLoggingService::RequestDetails
-  MakeRequestDetails(GURL url, Error error_type, int status_code = 0) {
+  NetworkErrorLoggingService::RequestDetails MakeRequestDetails(
+      GURL url,
+      Error error_type,
+      int status_code = 0,
+      IPAddress server_ip = IPAddress()) {
     NetworkErrorLoggingService::RequestDetails details;
 
     details.uri = url;
     details.referrer = kReferrer_;
-    details.server_ip = IPAddress::IPv4AllZeros();
+    details.server_ip = server_ip.IsValid() ? server_ip : kServerIP_;
     details.status_code = status_code;
     details.elapsed_time = base::TimeDelta::FromSeconds(1);
     details.type = error_type;
@@ -154,6 +157,8 @@
   const GURL kUrlSubdomain_ = GURL("https://subdomain.example.com/path");
   const GURL kUrlDifferentHost_ = GURL("https://example2.com/path");
 
+  const IPAddress kServerIP_ = IPAddress(192, 168, 0, 1);
+  const IPAddress kOtherServerIP_ = IPAddress(192, 168, 0, 2);
   const url::Origin kOrigin_ = url::Origin::Create(kUrl_);
   const url::Origin kOriginDifferentPort_ =
       url::Origin::Create(kUrlDifferentPort_);
@@ -199,7 +204,7 @@
 TEST_F(NetworkErrorLoggingServiceTest, NoReportingService) {
   DestroyReportingService();
 
-  service()->OnHeader(kOrigin_, kHeader_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 }
@@ -208,7 +213,7 @@
   const GURL kInsecureUrl("http://insecure.com/");
   const url::Origin kInsecureOrigin = url::Origin::Create(kInsecureUrl);
 
-  service()->OnHeader(kInsecureOrigin, kHeader_);
+  service()->OnHeader(kInsecureOrigin, kServerIP_, kHeader_);
 
   service()->OnRequest(
       MakeRequestDetails(kInsecureUrl, ERR_CONNECTION_REFUSED));
@@ -223,7 +228,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, JsonTooLong) {
-  service()->OnHeader(kOrigin_, kHeaderTooLong_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderTooLong_);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
@@ -231,7 +236,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, JsonTooDeep) {
-  service()->OnHeader(kOrigin_, kHeaderTooDeep_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderTooDeep_);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
@@ -241,7 +246,7 @@
 TEST_F(NetworkErrorLoggingServiceTest, SuccessReportQueued) {
   static const std::string kHeaderSuccessFraction1 =
       "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":1.0}";
-  service()->OnHeader(kOrigin_, kHeaderSuccessFraction1);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, OK));
 
@@ -260,7 +265,7 @@
   // TODO(juliatuttle): Extract these constants.
   ExpectDictDoubleValue(1.0, *body,
                         NetworkErrorLoggingService::kSamplingFractionKey);
-  base::ExpectDictStringValue("0.0.0.0", *body,
+  base::ExpectDictStringValue(kServerIP_.ToString(), *body,
                               NetworkErrorLoggingService::kServerIpKey);
   base::ExpectDictStringValue("", *body,
                               NetworkErrorLoggingService::kProtocolKey);
@@ -277,7 +282,7 @@
 TEST_F(NetworkErrorLoggingServiceTest, FailureReportQueued) {
   static const std::string kHeaderFailureFraction1 =
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":1.0}";
-  service()->OnHeader(kOrigin_, kHeaderFailureFraction1);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction1);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
@@ -296,7 +301,7 @@
   // TODO(juliatuttle): Extract these constants.
   ExpectDictDoubleValue(1.0, *body,
                         NetworkErrorLoggingService::kSamplingFractionKey);
-  base::ExpectDictStringValue("0.0.0.0", *body,
+  base::ExpectDictStringValue(kServerIP_.ToString(), *body,
                               NetworkErrorLoggingService::kServerIpKey);
   base::ExpectDictStringValue("", *body,
                               NetworkErrorLoggingService::kProtocolKey);
@@ -313,7 +318,7 @@
 TEST_F(NetworkErrorLoggingServiceTest, HttpErrorReportQueued) {
   static const std::string kHeaderFailureFraction1 =
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":1.0}";
-  service()->OnHeader(kOrigin_, kHeaderFailureFraction1);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction1);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, OK, 504));
 
@@ -332,7 +337,7 @@
   // TODO(juliatuttle): Extract these constants.
   ExpectDictDoubleValue(1.0, *body,
                         NetworkErrorLoggingService::kSamplingFractionKey);
-  base::ExpectDictStringValue("0.0.0.0", *body,
+  base::ExpectDictStringValue(kServerIP_.ToString(), *body,
                               NetworkErrorLoggingService::kServerIpKey);
   base::ExpectDictStringValue("", *body,
                               NetworkErrorLoggingService::kProtocolKey);
@@ -346,10 +351,152 @@
                               NetworkErrorLoggingService::kTypeKey);
 }
 
-TEST_F(NetworkErrorLoggingServiceTest, MaxAge0) {
-  service()->OnHeader(kOrigin_, kHeader_);
+TEST_F(NetworkErrorLoggingServiceTest, SuccessReportDowngraded) {
+  static const std::string kHeaderSuccessFraction1 =
+      "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":1.0}";
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1);
 
-  service()->OnHeader(kOrigin_, kHeaderMaxAge0_);
+  service()->OnRequest(MakeRequestDetails(kUrl_, OK, 200, kOtherServerIP_));
+
+  ASSERT_EQ(1u, reports().size());
+  EXPECT_EQ(kUrl_, reports()[0].url);
+  EXPECT_EQ(kGroup_, reports()[0].group);
+  EXPECT_EQ(kType_, reports()[0].type);
+  EXPECT_EQ(0, reports()[0].depth);
+
+  const base::DictionaryValue* body;
+  ASSERT_TRUE(reports()[0].body->GetAsDictionary(&body));
+  base::ExpectDictStringValue(kUrl_.spec(), *body,
+                              NetworkErrorLoggingService::kUriKey);
+  base::ExpectDictStringValue(kReferrer_.spec(), *body,
+                              NetworkErrorLoggingService::kReferrerKey);
+  ExpectDictDoubleValue(1.0, *body,
+                        NetworkErrorLoggingService::kSamplingFractionKey);
+  base::ExpectDictStringValue(kOtherServerIP_.ToString(), *body,
+                              NetworkErrorLoggingService::kServerIpKey);
+  base::ExpectDictStringValue("", *body,
+                              NetworkErrorLoggingService::kProtocolKey);
+  base::ExpectDictIntegerValue(0, *body,
+                               NetworkErrorLoggingService::kStatusCodeKey);
+  base::ExpectDictIntegerValue(0, *body,
+                               NetworkErrorLoggingService::kElapsedTimeKey);
+  base::ExpectDictStringValue("dns", *body,
+                              NetworkErrorLoggingService::kPhaseKey);
+  base::ExpectDictStringValue("dns.address_changed", *body,
+                              NetworkErrorLoggingService::kTypeKey);
+}
+
+TEST_F(NetworkErrorLoggingServiceTest, FailureReportDowngraded) {
+  static const std::string kHeaderSuccessFraction1 =
+      "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":1.0}";
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1);
+
+  service()->OnRequest(
+      MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED, 200, kOtherServerIP_));
+
+  ASSERT_EQ(1u, reports().size());
+  EXPECT_EQ(kUrl_, reports()[0].url);
+  EXPECT_EQ(kGroup_, reports()[0].group);
+  EXPECT_EQ(kType_, reports()[0].type);
+  EXPECT_EQ(0, reports()[0].depth);
+
+  const base::DictionaryValue* body;
+  ASSERT_TRUE(reports()[0].body->GetAsDictionary(&body));
+  base::ExpectDictStringValue(kUrl_.spec(), *body,
+                              NetworkErrorLoggingService::kUriKey);
+  base::ExpectDictStringValue(kReferrer_.spec(), *body,
+                              NetworkErrorLoggingService::kReferrerKey);
+  ExpectDictDoubleValue(1.0, *body,
+                        NetworkErrorLoggingService::kSamplingFractionKey);
+  base::ExpectDictStringValue(kOtherServerIP_.ToString(), *body,
+                              NetworkErrorLoggingService::kServerIpKey);
+  base::ExpectDictStringValue("", *body,
+                              NetworkErrorLoggingService::kProtocolKey);
+  base::ExpectDictIntegerValue(0, *body,
+                               NetworkErrorLoggingService::kStatusCodeKey);
+  base::ExpectDictIntegerValue(0, *body,
+                               NetworkErrorLoggingService::kElapsedTimeKey);
+  base::ExpectDictStringValue("dns", *body,
+                              NetworkErrorLoggingService::kPhaseKey);
+  base::ExpectDictStringValue("dns.address_changed", *body,
+                              NetworkErrorLoggingService::kTypeKey);
+}
+
+TEST_F(NetworkErrorLoggingServiceTest, HttpErrorReportDowngraded) {
+  static const std::string kHeaderSuccessFraction1 =
+      "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":1.0}";
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1);
+
+  service()->OnRequest(MakeRequestDetails(kUrl_, OK, 504, kOtherServerIP_));
+
+  ASSERT_EQ(1u, reports().size());
+  EXPECT_EQ(kUrl_, reports()[0].url);
+  EXPECT_EQ(kGroup_, reports()[0].group);
+  EXPECT_EQ(kType_, reports()[0].type);
+  EXPECT_EQ(0, reports()[0].depth);
+
+  const base::DictionaryValue* body;
+  ASSERT_TRUE(reports()[0].body->GetAsDictionary(&body));
+  base::ExpectDictStringValue(kUrl_.spec(), *body,
+                              NetworkErrorLoggingService::kUriKey);
+  base::ExpectDictStringValue(kReferrer_.spec(), *body,
+                              NetworkErrorLoggingService::kReferrerKey);
+  ExpectDictDoubleValue(1.0, *body,
+                        NetworkErrorLoggingService::kSamplingFractionKey);
+  base::ExpectDictStringValue(kOtherServerIP_.ToString(), *body,
+                              NetworkErrorLoggingService::kServerIpKey);
+  base::ExpectDictStringValue("", *body,
+                              NetworkErrorLoggingService::kProtocolKey);
+  base::ExpectDictIntegerValue(0, *body,
+                               NetworkErrorLoggingService::kStatusCodeKey);
+  base::ExpectDictIntegerValue(0, *body,
+                               NetworkErrorLoggingService::kElapsedTimeKey);
+  base::ExpectDictStringValue("dns", *body,
+                              NetworkErrorLoggingService::kPhaseKey);
+  base::ExpectDictStringValue("dns.address_changed", *body,
+                              NetworkErrorLoggingService::kTypeKey);
+}
+
+TEST_F(NetworkErrorLoggingServiceTest, DNSFailureReportNotDowngraded) {
+  static const std::string kHeaderSuccessFraction1 =
+      "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":1.0}";
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1);
+
+  service()->OnRequest(
+      MakeRequestDetails(kUrl_, ERR_NAME_NOT_RESOLVED, 0, kOtherServerIP_));
+
+  ASSERT_EQ(1u, reports().size());
+  EXPECT_EQ(kUrl_, reports()[0].url);
+  EXPECT_EQ(kGroup_, reports()[0].group);
+  EXPECT_EQ(kType_, reports()[0].type);
+  EXPECT_EQ(0, reports()[0].depth);
+
+  const base::DictionaryValue* body;
+  ASSERT_TRUE(reports()[0].body->GetAsDictionary(&body));
+  base::ExpectDictStringValue(kUrl_.spec(), *body,
+                              NetworkErrorLoggingService::kUriKey);
+  base::ExpectDictStringValue(kReferrer_.spec(), *body,
+                              NetworkErrorLoggingService::kReferrerKey);
+  ExpectDictDoubleValue(1.0, *body,
+                        NetworkErrorLoggingService::kSamplingFractionKey);
+  base::ExpectDictStringValue(kOtherServerIP_.ToString(), *body,
+                              NetworkErrorLoggingService::kServerIpKey);
+  base::ExpectDictStringValue("", *body,
+                              NetworkErrorLoggingService::kProtocolKey);
+  base::ExpectDictIntegerValue(0, *body,
+                               NetworkErrorLoggingService::kStatusCodeKey);
+  base::ExpectDictIntegerValue(1000, *body,
+                               NetworkErrorLoggingService::kElapsedTimeKey);
+  base::ExpectDictStringValue("dns", *body,
+                              NetworkErrorLoggingService::kPhaseKey);
+  base::ExpectDictStringValue("dns.name_not_resolved", *body,
+                              NetworkErrorLoggingService::kTypeKey);
+}
+
+TEST_F(NetworkErrorLoggingServiceTest, MaxAge0) {
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderMaxAge0_);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED));
 
@@ -359,7 +506,7 @@
 TEST_F(NetworkErrorLoggingServiceTest, SuccessFraction0) {
   static const std::string kHeaderSuccessFraction0 =
       "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":0.0}";
-  service()->OnHeader(kOrigin_, kHeaderSuccessFraction0);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction0);
 
   // Each network error has a 0% chance of being reported.  Fire off several and
   // verify that no reports are produced.
@@ -376,7 +523,7 @@
   static const std::string kHeaderSuccessFractionHalf =
       "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":0.5,"
       "\"failure_fraction\":0.25}";
-  service()->OnHeader(kOrigin_, kHeaderSuccessFractionHalf);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFractionHalf);
 
   // Each network error has a 50% chance of being reported.  Fire off several
   // and verify that some requests were reported and some weren't.  (We can't
@@ -407,7 +554,7 @@
 TEST_F(NetworkErrorLoggingServiceTest, FailureFraction0) {
   static const std::string kHeaderFailureFraction0 =
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":0.0}";
-  service()->OnHeader(kOrigin_, kHeaderFailureFraction0);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFraction0);
 
   // Each network error has a 0% chance of being reported.  Fire off several and
   // verify that no reports are produced.
@@ -424,7 +571,7 @@
   static const std::string kHeaderFailureFractionHalf =
       "{\"report_to\":\"group\",\"max_age\":86400,\"failure_fraction\":0.5,"
       "\"success_fraction\":0.25}";
-  service()->OnHeader(kOrigin_, kHeaderFailureFractionHalf);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderFailureFractionHalf);
 
   // Each network error has a 50% chance of being reported.  Fire off several
   // and verify that some requests were reported and some weren't.  (We can't
@@ -452,7 +599,7 @@
 
 TEST_F(NetworkErrorLoggingServiceTest,
        ExcludeSubdomainsDoesntMatchDifferentPort) {
-  service()->OnHeader(kOrigin_, kHeader_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
   service()->OnRequest(
       MakeRequestDetails(kUrlDifferentPort_, ERR_CONNECTION_REFUSED));
@@ -461,7 +608,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, ExcludeSubdomainsDoesntMatchSubdomain) {
-  service()->OnHeader(kOrigin_, kHeader_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_CONNECTION_REFUSED));
@@ -470,7 +617,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, IncludeSubdomainsMatchesDifferentPort) {
-  service()->OnHeader(kOrigin_, kHeaderIncludeSubdomains_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
   service()->OnRequest(
       MakeRequestDetails(kUrlDifferentPort_, ERR_NAME_NOT_RESOLVED));
@@ -480,7 +627,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, IncludeSubdomainsMatchesSubdomain) {
-  service()->OnHeader(kOrigin_, kHeaderIncludeSubdomains_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_NAME_NOT_RESOLVED));
@@ -490,7 +637,7 @@
 
 TEST_F(NetworkErrorLoggingServiceTest,
        IncludeSubdomainsDoesntMatchSuperdomain) {
-  service()->OnHeader(kOriginSubdomain_, kHeaderIncludeSubdomains_);
+  service()->OnHeader(kOriginSubdomain_, kServerIP_, kHeaderIncludeSubdomains_);
 
   service()->OnRequest(MakeRequestDetails(kUrl_, ERR_NAME_NOT_RESOLVED));
 
@@ -499,7 +646,7 @@
 
 TEST_F(NetworkErrorLoggingServiceTest,
        IncludeSubdomainsDoesntReportConnectionError) {
-  service()->OnHeader(kOrigin_, kHeaderIncludeSubdomains_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_CONNECTION_REFUSED));
@@ -509,7 +656,7 @@
 
 TEST_F(NetworkErrorLoggingServiceTest,
        IncludeSubdomainsDoesntReportApplicationError) {
-  service()->OnHeader(kOrigin_, kHeaderIncludeSubdomains_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
   service()->OnRequest(
       MakeRequestDetails(kUrlSubdomain_, ERR_INVALID_HTTP_RESPONSE));
@@ -518,7 +665,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, IncludeSubdomainsDoesntReportSuccess) {
-  service()->OnHeader(kOrigin_, kHeaderIncludeSubdomains_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderIncludeSubdomains_);
 
   service()->OnRequest(MakeRequestDetails(kUrlSubdomain_, OK));
 
@@ -526,7 +673,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, RemoveAllBrowsingData) {
-  service()->OnHeader(kOrigin_, kHeader_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
   service()->RemoveAllBrowsingData();
 
@@ -536,8 +683,8 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, RemoveSomeBrowsingData) {
-  service()->OnHeader(kOrigin_, kHeader_);
-  service()->OnHeader(kOriginDifferentHost_, kHeader_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
+  service()->OnHeader(kOriginDifferentHost_, kServerIP_, kHeader_);
 
   service()->RemoveBrowsingData(
       base::BindRepeating([](const GURL& origin) -> bool {
@@ -555,7 +702,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, Nested) {
-  service()->OnHeader(kOrigin_, kHeader_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
   NetworkErrorLoggingService::RequestDetails details =
       MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED);
@@ -569,7 +716,7 @@
 }
 
 TEST_F(NetworkErrorLoggingServiceTest, NestedTooDeep) {
-  service()->OnHeader(kOrigin_, kHeader_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeader_);
 
   NetworkErrorLoggingService::RequestDetails details =
       MakeRequestDetails(kUrl_, ERR_CONNECTION_REFUSED);
@@ -586,9 +733,9 @@
 
   static const std::string kHeaderSuccessFraction1 =
       "{\"report_to\":\"group\",\"max_age\":86400,\"success_fraction\":1.0}";
-  service()->OnHeader(kOrigin_, kHeaderSuccessFraction1);
-  service()->OnHeader(kOriginDifferentHost_, kHeader_);
-  service()->OnHeader(kOriginSubdomain_, kHeaderIncludeSubdomains_);
+  service()->OnHeader(kOrigin_, kServerIP_, kHeaderSuccessFraction1);
+  service()->OnHeader(kOriginDifferentHost_, kServerIP_, kHeader_);
+  service()->OnHeader(kOriginSubdomain_, kServerIP_, kHeaderIncludeSubdomains_);
 
   base::Value actual = service()->StatusAsValue();
   std::unique_ptr<base::Value> expected = base::test::ParseJson(R"json(
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index b18ea93..7ef5874 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -1210,7 +1210,7 @@
     details.reporting_upload_depth = 0;
   }
 
-  service->OnRequest(details);
+  service->OnRequest(std::move(details));
 }
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index af57b41ed..ff0bb02 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -953,7 +953,14 @@
     return;
   }
 
-  service->OnHeader(url::Origin::Create(request_info_.url), value);
+  IPEndPoint endpoint;
+  if (!GetRemoteEndpoint(&endpoint)) {
+    NetworkErrorLoggingService::RecordHeaderDiscardedForMissingRemoteEndpoint();
+    return;
+  }
+
+  service->OnHeader(url::Origin::Create(request_info_.url), endpoint.address(),
+                    value);
 }
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index a231d7d..ec987ea 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <algorithm>
 #include <utility>
 
 // This must be before Windows headers
@@ -7631,7 +7632,17 @@
     Header() = default;
     ~Header() = default;
 
+    // Returns whether the |received_ip_address| field matches any of the
+    // addresses in |address_list|.
+    bool MatchesAddressList(const AddressList& address_list) const {
+      return std::any_of(address_list.begin(), address_list.end(),
+                         [this](const IPEndPoint& endpoint) {
+                           return endpoint.address() == received_ip_address;
+                         });
+    }
+
     url::Origin origin;
+    IPAddress received_ip_address;
     std::string value;
   };
 
@@ -7642,15 +7653,18 @@
 
   ~TestNetworkErrorLoggingService() override = default;
 
-  void OnHeader(const url::Origin& origin, const std::string& value) override {
+  void OnHeader(const url::Origin& origin,
+                const IPAddress& received_ip_address,
+                const std::string& value) override {
     Header header;
     header.origin = origin;
+    header.received_ip_address = received_ip_address;
     header.value = value;
     headers_.push_back(header);
   }
 
-  void OnRequest(const RequestDetails& details) override {
-    errors_.push_back(details);
+  void OnRequest(RequestDetails details) override {
+    errors_.push_back(std::move(details));
   }
 
   void RemoveBrowsingData(const base::RepeatingCallback<bool(const GURL&)>&
@@ -7743,6 +7757,9 @@
 
   ASSERT_EQ(1u, nel_service.headers().size());
   EXPECT_EQ(url::Origin::Create(request_url), nel_service.headers()[0].origin);
+  AddressList address_list;
+  EXPECT_TRUE(https_test_server.GetAddressList(&address_list));
+  EXPECT_TRUE(nel_service.headers()[0].MatchesAddressList(address_list));
   EXPECT_EQ("foo", nel_service.headers()[0].value);
 }
 
diff --git a/remoting/protocol/webrtc_transport.cc b/remoting/protocol/webrtc_transport.cc
index 5e48c07..04b0f94 100644
--- a/remoting/protocol/webrtc_transport.cc
+++ b/remoting/protocol/webrtc_transport.cc
@@ -52,6 +52,13 @@
 // XML namespace for the transport elements.
 const char kTransportNamespace[] = "google:remoting:webrtc";
 
+// Bitrate cap applied to relay connections. This is done to prevent
+// large amounts of packet loss, since the Google TURN/relay server drops
+// packets to limit the connection to ~10Mbps. The rate-limiting behavior works
+// badly with WebRTC's bandwidth-estimation, which results in the host process
+// trying to send frames too rapidly over the connection.
+constexpr int kMaxBitrateOnRelayKbps = 8000;
+
 #if !defined(NDEBUG)
 // Command line switch used to disable signature verification.
 // TODO(sergeyu): Remove this flag.
@@ -742,6 +749,32 @@
   bool is_relay =
       local_candidate_type == "relay" || remote_candidate_type == "relay";
   VLOG(0) << "Relay connection: " << (is_relay ? "true" : "false");
+
+  if (is_relay) {
+    auto senders = peer_connection()->GetSenders();
+    for (rtc::scoped_refptr<webrtc::RtpSenderInterface> sender : senders) {
+      // x-google-max-bitrate is only set for video codecs in the SDP exchange.
+      // So avoid setting a very large bitrate cap on the audio sender.
+      if (sender->media_type() != cricket::MediaType::MEDIA_TYPE_VIDEO) {
+        continue;
+      }
+
+      webrtc::RtpParameters parameters = sender->GetParameters();
+      if (parameters.encodings.empty()) {
+        LOG(ERROR) << "No encodings found for sender " << sender->id();
+        continue;
+      }
+
+      if (parameters.encodings.size() != 1) {
+        LOG(ERROR) << "Unexpected number of encodings ("
+                   << parameters.encodings.size() << ") for sender "
+                   << sender->id();
+      }
+
+      parameters.encodings[0].max_bitrate_bps = kMaxBitrateOnRelayKbps * 1000;
+      sender->SetParameters(parameters);
+    }
+  }
 }
 
 void WebrtcTransport::EnsurePendingTransportInfoMessage() {
diff --git a/services/content/BUILD.gn b/services/content/BUILD.gn
index bd57f62a..f2fbe6f 100644
--- a/services/content/BUILD.gn
+++ b/services/content/BUILD.gn
@@ -12,17 +12,17 @@
   ]
 
   public = [
+    "navigable_contents_delegate.h",
     "service.h",
     "service_delegate.h",
-    "view_delegate.h",
   ]
 
   sources = [
+    "navigable_contents_factory_impl.cc",
+    "navigable_contents_factory_impl.h",
+    "navigable_contents_impl.cc",
+    "navigable_contents_impl.h",
     "service.cc",
-    "view_factory_impl.cc",
-    "view_factory_impl.h",
-    "view_impl.cc",
-    "view_impl.h",
   ]
 
   public_deps = [
diff --git a/services/content/manifest.json b/services/content/manifest.json
index 19bba2ee..c47c4be 100644
--- a/services/content/manifest.json
+++ b/services/content/manifest.json
@@ -4,11 +4,12 @@
   "interface_provider_specs": {
     "service_manager:connector": {
       "provides": {
-        // The |view| capability allows a service to access embeddable, navigable content views.
-        // For now, access to this capability should be restricted only to services which are
-        // at least as trusted as the Chrome browser process (e.g., Ash).
-        "view": [
-          "content.mojom.ViewFactory"
+        // The |navigation| capability allows a service to acquire embeddable,
+        // navigable contents. for now, access to this capability should be
+        // restricted only to services which are at least as trusted as the
+        // Chrome browser process (e.g., Ash).
+        "navigation": [
+          "content.mojom.NavigableContentsFactory"
         ]
       }
     }
diff --git a/services/content/navigable_contents_delegate.h b/services/content/navigable_contents_delegate.h
new file mode 100644
index 0000000..9e8208b
--- /dev/null
+++ b/services/content/navigable_contents_delegate.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_CONTENT_NAVIGABLE_CONTENTS_DELEGATE_H_
+#define SERVICES_CONTENT_NAVIGABLE_CONTENTS_DELEGATE_H_
+
+#include "ui/gfx/native_widget_types.h"
+
+class GURL;
+
+namespace content {
+
+// A virtual interface which must be implemented as a backing for
+// NavigableContentsImpl instances.
+//
+// This is the primary interface by which the Content Service delegates
+// NavigableContentsImpl behavior out to WebContentsImpl in src/content. As such
+// it is a transitional API which will be removed as soon as WebContentsImpl
+// itself can be fully migrated into Content Service.
+//
+// Each instance of this interface is constructed by the ContentServiceDelegate
+// implementation and owned by a NavigableContentsImpl.
+class NavigableContentsDelegate {
+ public:
+  virtual ~NavigableContentsDelegate() {}
+
+  // Returns a NativeView that can be embedded into a client application's
+  // window tree to display the web contents navigated by the delegate's
+  // NavigableContents.
+  virtual gfx::NativeView GetNativeView() = 0;
+
+  // Navigates the content object to a new URL.
+  virtual void Navigate(const GURL& url) = 0;
+};
+
+}  // namespace content
+
+#endif  // SERVICES_CONTENT_NAVIGABLE_CONTENTS_DELEGATE_H_
diff --git a/services/content/navigable_contents_factory_impl.cc b/services/content/navigable_contents_factory_impl.cc
new file mode 100644
index 0000000..179164b
--- /dev/null
+++ b/services/content/navigable_contents_factory_impl.cc
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/content/navigable_contents_factory_impl.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "services/content/navigable_contents_impl.h"
+#include "services/content/service.h"
+
+namespace content {
+
+NavigableContentsFactoryImpl::NavigableContentsFactoryImpl(
+    Service* service,
+    mojom::NavigableContentsFactoryRequest request)
+    : service_(service), binding_(this, std::move(request)) {
+  binding_.set_connection_error_handler(
+      base::BindOnce(&Service::RemoveNavigableContentsFactory,
+                     base::Unretained(service_), this));
+}
+
+NavigableContentsFactoryImpl::~NavigableContentsFactoryImpl() = default;
+
+void NavigableContentsFactoryImpl::CreateContents(
+    mojom::NavigableContentsParamsPtr params,
+    mojom::NavigableContentsRequest request,
+    mojom::NavigableContentsClientPtr client) {
+  service_->AddNavigableContents(std::make_unique<NavigableContentsImpl>(
+      service_, std::move(params), std::move(request), std::move(client)));
+}
+
+}  // namespace content
diff --git a/services/content/navigable_contents_factory_impl.h b/services/content/navigable_contents_factory_impl.h
new file mode 100644
index 0000000..0232781
--- /dev/null
+++ b/services/content/navigable_contents_factory_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_CONTENT_NAVIGABLE_CONTENTS_FACTORY_IMPL_H_
+#define SERVICES_CONTENT_NAVIGABLE_CONTENTS_FACTORY_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
+
+namespace content {
+
+class Service;
+
+// An implementation of the NavigableContentsFactory which backs every connected
+// NavigableContentsFactory interface in a Content Service client. This creates
+// instances of NavigableContentsImpl to fulfill |CreateContents()| requests.
+//
+// Instances of this class, and of the NavigableContentsImpls it creates, are
+// all managed by the Service instance.
+class NavigableContentsFactoryImpl : public mojom::NavigableContentsFactory {
+ public:
+  NavigableContentsFactoryImpl(Service* service,
+                               mojom::NavigableContentsFactoryRequest request);
+  ~NavigableContentsFactoryImpl() override;
+
+ private:
+  // mojom::NavigableContentsFactory:
+  void CreateContents(mojom::NavigableContentsParamsPtr params,
+                      mojom::NavigableContentsRequest request,
+                      mojom::NavigableContentsClientPtr client) override;
+
+  Service* const service_;
+  mojo::Binding<mojom::NavigableContentsFactory> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigableContentsFactoryImpl);
+};
+
+}  // namespace content
+
+#endif  // SERVICES_CONTENT_NAVIGABLE_CONTENTS_FACTORY_IMPL_H_
diff --git a/services/content/view_impl.cc b/services/content/navigable_contents_impl.cc
similarity index 62%
rename from services/content/view_impl.cc
rename to services/content/navigable_contents_impl.cc
index 208616a..df8ce6c9 100644
--- a/services/content/view_impl.cc
+++ b/services/content/navigable_contents_impl.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "services/content/view_impl.h"
+#include "services/content/navigable_contents_impl.h"
 
 #include "base/bind.h"
+#include "services/content/navigable_contents_delegate.h"
 #include "services/content/public/cpp/buildflags.h"
 #include "services/content/service.h"
 #include "services/content/service_delegate.h"
-#include "services/content/view_delegate.h"
 
 #if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
 #include "ui/aura/window.h"                                 // nogncheck
@@ -17,17 +17,19 @@
 
 namespace content {
 
-ViewImpl::ViewImpl(Service* service,
-                   mojom::ViewParamsPtr params,
-                   mojom::ViewRequest request,
-                   mojom::ViewClientPtr client)
+NavigableContentsImpl::NavigableContentsImpl(
+    Service* service,
+    mojom::NavigableContentsParamsPtr params,
+    mojom::NavigableContentsRequest request,
+    mojom::NavigableContentsClientPtr client)
     : service_(service),
       binding_(this, std::move(request)),
       client_(std::move(client)),
-      delegate_(service_->delegate()->CreateViewDelegate(client_.get())),
+      delegate_(
+          service_->delegate()->CreateNavigableContentsDelegate(client_.get())),
       native_content_view_(delegate_->GetNativeView()) {
   binding_.set_connection_error_handler(base::BindRepeating(
-      &Service::RemoveView, base::Unretained(service_), this));
+      &Service::RemoveNavigableContents, base::Unretained(service_), this));
 
 #if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
   if (native_content_view_) {
@@ -38,14 +40,22 @@
 #endif
 }
 
-ViewImpl::~ViewImpl() = default;
+NavigableContentsImpl::~NavigableContentsImpl() = default;
 
-void ViewImpl::PrepareToEmbed(PrepareToEmbedCallback callback) {
+void NavigableContentsImpl::Navigate(const GURL& url) {
+  // Ignore non-HTTP/HTTPS requests for now.
+  if (!url.SchemeIsHTTPOrHTTPS())
+    return;
+
+  delegate_->Navigate(url);
+}
+
+void NavigableContentsImpl::CreateView(CreateViewCallback callback) {
 #if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
   if (remote_view_provider_) {
     remote_view_provider_->GetEmbedToken(
-        base::BindOnce(&ViewImpl::OnEmbedTokenReceived, base::Unretained(this),
-                       std::move(callback)));
+        base::BindOnce(&NavigableContentsImpl::OnEmbedTokenReceived,
+                       base::Unretained(this), std::move(callback)));
     return;
   }
 #endif
@@ -54,16 +64,9 @@
   std::move(callback).Run(base::UnguessableToken::Create());
 }
 
-void ViewImpl::Navigate(const GURL& url) {
-  // Ignore non-HTTP/HTTPS requests for now.
-  if (!url.SchemeIsHTTPOrHTTPS())
-    return;
-
-  delegate_->Navigate(url);
-}
-
-void ViewImpl::OnEmbedTokenReceived(PrepareToEmbedCallback callback,
-                                    const base::UnguessableToken& token) {
+void NavigableContentsImpl::OnEmbedTokenReceived(
+    CreateViewCallback callback,
+    const base::UnguessableToken& token) {
 #if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
   if (native_content_view_)
     native_content_view_->Show();
diff --git a/services/content/navigable_contents_impl.h b/services/content/navigable_contents_impl.h
new file mode 100644
index 0000000..38b0b1f
--- /dev/null
+++ b/services/content/navigable_contents_impl.h
@@ -0,0 +1,60 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_CONTENT_NAVIGABLE_CONTENTS_IMPL_H_
+#define SERVICES_CONTENT_NAVIGABLE_CONTENTS_IMPL_H_
+
+#include "base/macros.h"
+#include "base/unguessable_token.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/content/public/cpp/buildflags.h"
+#include "services/content/public/mojom/navigable_contents.mojom.h"
+#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace views {
+class RemoteViewProvider;
+}
+
+namespace content {
+
+class Service;
+class NavigableContentsDelegate;
+
+// This is the state which backs an individual NavigableContents owned by some
+// client of the Content Service. In terms of the classical Content API, this is
+// roughly analogous to a WebContentsImpl.
+class NavigableContentsImpl : public mojom::NavigableContents {
+ public:
+  NavigableContentsImpl(Service* service,
+                        mojom::NavigableContentsParamsPtr params,
+                        mojom::NavigableContentsRequest request,
+                        mojom::NavigableContentsClientPtr client);
+  ~NavigableContentsImpl() override;
+
+ private:
+  // mojom::NavigableContents:
+  void Navigate(const GURL& url) override;
+  void CreateView(CreateViewCallback callback) override;
+
+  void OnEmbedTokenReceived(CreateViewCallback callback,
+                            const base::UnguessableToken& token);
+
+  Service* const service_;
+
+  mojo::Binding<mojom::NavigableContents> binding_;
+  mojom::NavigableContentsClientPtr client_;
+  std::unique_ptr<NavigableContentsDelegate> delegate_;
+  gfx::NativeView native_content_view_;
+
+#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
+  std::unique_ptr<views::RemoteViewProvider> remote_view_provider_;
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(NavigableContentsImpl);
+};
+
+}  // namespace content
+
+#endif  // SERVICES_CONTENT_NAVIGABLE_CONTENTS_IMPL_H_
diff --git a/services/content/public/cpp/BUILD.gn b/services/content/public/cpp/BUILD.gn
index 5a031ff..1926c73 100644
--- a/services/content/public/cpp/BUILD.gn
+++ b/services/content/public/cpp/BUILD.gn
@@ -15,11 +15,11 @@
   output_name = "content_service_cpp"
 
   public = [
-    "view.h",
+    "navigable_contents.h",
   ]
 
   sources = [
-    "view.cc",
+    "navigable_contents.cc",
   ]
 
   defines = [ "IS_CONTENT_SERVICE_CPP_IMPL" ]
diff --git a/services/content/public/cpp/navigable_contents.cc b/services/content/public/cpp/navigable_contents.cc
new file mode 100644
index 0000000..2fd041ed
--- /dev/null
+++ b/services/content/public/cpp/navigable_contents.cc
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/content/public/cpp/navigable_contents.h"
+
+#include "services/content/public/cpp/buildflags.h"
+
+#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
+#include "services/ui/public/interfaces/window_tree_constants.mojom.h"  // nogncheck
+#include "ui/views/layout/fill_layout.h"                // nogncheck
+#include "ui/views/mus/remote_view/remote_view_host.h"  // nogncheck
+#include "ui/views/view.h"                              // nogncheck
+#endif
+
+namespace content {
+
+NavigableContents::NavigableContents(mojom::NavigableContentsFactory* factory)
+    : client_binding_(this) {
+  mojom::NavigableContentsClientPtr client;
+  client_binding_.Bind(mojo::MakeRequest(&client));
+  factory->CreateContents(mojom::NavigableContentsParams::New(),
+                          mojo::MakeRequest(&contents_), std::move(client));
+}
+
+NavigableContents::~NavigableContents() = default;
+
+views::View* NavigableContents::GetView() {
+#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
+  if (!view_) {
+    view_ = std::make_unique<views::View>();
+    view_->set_owned_by_client();
+    view_->SetLayoutManager(std::make_unique<views::FillLayout>());
+
+    DCHECK(!remote_view_host_);
+    remote_view_host_ = new views::RemoteViewHost;
+    view_->AddChildView(remote_view_host_);
+
+    contents_->CreateView(base::BindOnce(
+        &NavigableContents::OnEmbedTokenReceived, base::Unretained(this)));
+  }
+  return view_.get();
+#else
+  return nullptr;
+#endif
+}
+
+void NavigableContents::Navigate(const GURL& url) {
+  contents_->Navigate(url);
+}
+
+void NavigableContents::DidStopLoading() {
+  if (did_stop_loading_callback_)
+    did_stop_loading_callback_.Run();
+}
+
+void NavigableContents::OnEmbedTokenReceived(
+    const base::UnguessableToken& token) {
+#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
+  const uint32_t kEmbedFlags = ui::mojom::kEmbedFlagEmbedderInterceptsEvents |
+                               ui::mojom::kEmbedFlagEmbedderControlsVisibility;
+  remote_view_host_->EmbedUsingToken(token, kEmbedFlags, base::DoNothing());
+#endif  // defined(USE_AURA)
+}
+
+}  // namespace content
diff --git a/services/content/public/cpp/navigable_contents.h b/services/content/public/cpp/navigable_contents.h
new file mode 100644
index 0000000..57a5d26
--- /dev/null
+++ b/services/content/public/cpp/navigable_contents.h
@@ -0,0 +1,77 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_CONTENT_PUBLIC_CPP_NAVIGABLE_CONTENTS_H_
+#define SERVICES_CONTENT_PUBLIC_CPP_NAVIGABLE_CONTENTS_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/content/public/cpp/buildflags.h"
+#include "services/content/public/mojom/navigable_contents.mojom.h"
+#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
+
+namespace views {
+class RemoteViewHost;
+class View;
+}  // namespace views
+
+namespace content {
+
+// A NavigableContents controls a single dedicated instance of a top-level,
+// navigable content frame hosted by the Content Service. In addition to
+// maintaining its own navigation state, a NavigableContents may be used to
+// acquire an embeddable Views widget to display renderered content within a
+// client application's own UI.
+class COMPONENT_EXPORT(CONTENT_SERVICE_CPP) NavigableContents
+    : public mojom::NavigableContentsClient {
+ public:
+  // Constructs a new NavigableContents using |factory|.
+  explicit NavigableContents(mojom::NavigableContentsFactory* factory);
+  ~NavigableContents() override;
+
+  // Returns a View which renders this NavigableContents's currently navigated
+  // contents. This widget can be parented and displayed anywhere within the
+  // application's own window tree.
+  //
+  // Note that this View is created lazily on first call, and by default
+  // NavigableContents does not otherwise create or manipulate UI objects.
+  views::View* GetView();
+
+  // Begins an attempt to asynchronously navigate this NavigableContents to
+  // |url|.
+  void Navigate(const GURL& url);
+
+  void set_did_stop_loading_callback_for_testing(
+      base::RepeatingClosure callback) {
+    did_stop_loading_callback_ = std::move(callback);
+  }
+
+ private:
+  // mojom::NavigableContentsClient:
+  void DidStopLoading() override;
+
+  void OnEmbedTokenReceived(const base::UnguessableToken& token);
+
+  mojom::NavigableContentsPtr contents_;
+  mojo::Binding<mojom::NavigableContentsClient> client_binding_;
+
+#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
+  // This NavigableContents's View. Only initialized if |GetView()| is called,
+  // and only on platforms which support View embedding via Aura.
+  std::unique_ptr<views::View> view_;
+  views::RemoteViewHost* remote_view_host_ = nullptr;
+#endif
+
+  base::RepeatingClosure did_stop_loading_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(NavigableContents);
+};
+
+}  // namespace content
+
+#endif  // SERVICES_CONTENT_PUBLIC_CPP_NAVIGABLE_CONTENTS_H_
diff --git a/services/content/public/cpp/view.cc b/services/content/public/cpp/view.cc
deleted file mode 100644
index 15574a2..0000000
--- a/services/content/public/cpp/view.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/content/public/cpp/view.h"
-
-#include "services/content/public/cpp/buildflags.h"
-
-#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
-#include "services/ui/public/interfaces/window_tree_constants.mojom.h"  // nogncheck
-#include "ui/views/layout/fill_layout.h"                // nogncheck
-#include "ui/views/mus/remote_view/remote_view_host.h"  // nogncheck
-#include "ui/views/view.h"                              // nogncheck
-#endif
-
-namespace content {
-
-View::View(mojom::ViewFactory* factory) : client_binding_(this) {
-  mojom::ViewClientPtr client;
-  client_binding_.Bind(mojo::MakeRequest(&client));
-  factory->CreateView(mojom::ViewParams::New(), mojo::MakeRequest(&view_),
-                      std::move(client));
-}
-
-View::~View() = default;
-
-views::View* View::CreateUI() {
-  view_->PrepareToEmbed(
-      base::BindOnce(&View::OnEmbedTokenReceived, base::Unretained(this)));
-#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
-  DCHECK(!ui_view_);
-  ui_view_ = std::make_unique<views::View>();
-  ui_view_->set_owned_by_client();
-  ui_view_->SetLayoutManager(std::make_unique<views::FillLayout>());
-
-  DCHECK(!remote_view_host_);
-  remote_view_host_ = new views::RemoteViewHost;
-  ui_view_->AddChildView(remote_view_host_);
-  return ui_view_.get();
-#else
-  return nullptr;
-#endif
-}
-
-void View::Navigate(const GURL& url) {
-  view_->Navigate(url);
-}
-
-void View::DidStopLoading() {
-  if (did_stop_loading_callback_)
-    did_stop_loading_callback_.Run();
-}
-
-void View::OnEmbedTokenReceived(const base::UnguessableToken& token) {
-#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
-  const uint32_t kEmbedFlags = ui::mojom::kEmbedFlagEmbedderInterceptsEvents |
-                               ui::mojom::kEmbedFlagEmbedderControlsVisibility;
-  remote_view_host_->EmbedUsingToken(token, kEmbedFlags, base::DoNothing());
-#endif  // defined(USE_AURA)
-}
-
-}  // namespace content
diff --git a/services/content/public/cpp/view.h b/services/content/public/cpp/view.h
deleted file mode 100644
index 9a62f2b..0000000
--- a/services/content/public/cpp/view.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_CONTENT_PUBLIC_CPP_VIEW_H_
-#define SERVICES_CONTENT_PUBLIC_CPP_VIEW_H_
-
-#include <memory>
-
-#include "base/callback.h"
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/content/public/cpp/buildflags.h"
-#include "services/content/public/mojom/view.mojom.h"
-#include "services/content/public/mojom/view_factory.mojom.h"
-
-namespace views {
-class RemoteViewHost;
-class View;
-}  // namespace views
-
-namespace content {
-
-// A View is a navigable, top-level view of web content which applications can
-// embed within their own UI.
-//
-// A View does not need to be used only for displaying visually renderable web
-// contents, so by default it has no graphical presence. Call |CreateUI()| to
-// get a usable Views widget which displays the navigated web contents and which
-// can be attached to an existing window tree.
-class COMPONENT_EXPORT(CONTENT_SERVICE_CPP) View : public mojom::ViewClient {
- public:
-  // Constructs a new View using |factory|.
-  explicit View(mojom::ViewFactory* factory);
-  ~View() override;
-
-  // Initialize's this View for use within a window tree. Returns the
-  // corresponding |views::View|, which the caller may parent to some other
-  // widget. This widget will display the web content navigated by the Content
-  // Service on this View's behalf.
-  views::View* CreateUI();
-
-  // Begins an attempt to asynchronously navigate this View to |url|.
-  void Navigate(const GURL& url);
-
-  void set_did_stop_loading_callback_for_testing(
-      base::RepeatingClosure callback) {
-    did_stop_loading_callback_ = std::move(callback);
-  }
-
- private:
-  // mojom::ViewClient:
-  void DidStopLoading() override;
-
-  void OnEmbedTokenReceived(const base::UnguessableToken& token);
-
-  mojom::ViewPtr view_;
-  mojo::Binding<mojom::ViewClient> client_binding_;
-
-#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
-  // This View's node in the client's window tree. Non-existent by default, but
-  // may be initialized by calling InitializeUI.
-  std::unique_ptr<views::View> ui_view_;
-  views::RemoteViewHost* remote_view_host_ = nullptr;
-#endif
-
-  base::RepeatingClosure did_stop_loading_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(View);
-};
-
-}  // namespace content
-
-#endif  // SERVICES_CONTENT_PUBLIC_CPP_VIEW_H_
diff --git a/services/content/public/mojom/BUILD.gn b/services/content/public/mojom/BUILD.gn
index bbcac09..8308295 100644
--- a/services/content/public/mojom/BUILD.gn
+++ b/services/content/public/mojom/BUILD.gn
@@ -13,8 +13,8 @@
 
   sources = [
     "constants.mojom",
-    "view.mojom",
-    "view_factory.mojom",
+    "navigable_contents.mojom",
+    "navigable_contents_factory.mojom",
   ]
 
   public_deps = [
diff --git a/services/content/public/mojom/navigable_contents.mojom b/services/content/public/mojom/navigable_contents.mojom
new file mode 100644
index 0000000..dd9b5e1a
--- /dev/null
+++ b/services/content/public/mojom/navigable_contents.mojom
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "mojo/public/mojom/base/unguessable_token.mojom";
+import "url/mojom/url.mojom";
+
+// The primary interface an application uses to drive a top-level, navigable
+// content object. Typically this would correspond to e.g. a browser tab, but
+// it is not strictly necessary that the contents have any graphical presence
+// within the client application.
+interface NavigableContents {
+  // Initiates a navigation to |url|.
+  Navigate(url.mojom.Url url);
+
+  // Creates a visual representation of the navigated contents, which is
+  // maintained by the Content Service. Responds with a |embed_token| which can
+  // be given to Mus in order to authorize embedding of that visual
+  // representation within the client application's own window tree.
+  CreateView() => (mojo_base.mojom.UnguessableToken embed_token);
+};
+
+// A client interface used by the Content Service to push contents-scoped events
+// back to the application.
+interface NavigableContentsClient {
+  // Notifies the client that the NavigableContents has stopped loading
+  // resources pertaining to a prior navigation request.
+  DidStopLoading();
+};
diff --git a/services/content/public/mojom/navigable_contents_factory.mojom b/services/content/public/mojom/navigable_contents_factory.mojom
new file mode 100644
index 0000000..b79c633
--- /dev/null
+++ b/services/content/public/mojom/navigable_contents_factory.mojom
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module content.mojom;
+
+import "services/content/public/mojom/navigable_contents.mojom";
+
+// Parameters used to configure a newly created NavigableContents.
+struct NavigableContentsParams {};
+
+// NavigableContentsFactory is the primary interface through which a new
+// NavigableContents interface is bound to a new concrete navigable contents
+// within the Content Service.
+interface NavigableContentsFactory {
+  // Creates a new NavigableContents configured according to |params|. |request|
+  // is bound to the contents implementation, and |client| is used to push
+  // notifications of events relevant to the state of that context throughout
+  // its lifetime.
+  CreateContents(NavigableContentsParams params,
+                 NavigableContents& request,
+                 NavigableContentsClient client);
+};
diff --git a/services/content/public/mojom/view.mojom b/services/content/public/mojom/view.mojom
deleted file mode 100644
index b19b720e..0000000
--- a/services/content/public/mojom/view.mojom
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module content.mojom;
-
-import "mojo/public/mojom/base/unguessable_token.mojom";
-import "url/mojom/url.mojom";
-
-// The primary interface driving a navigable content view embedded by an
-// application which wants to display web contents.
-interface View {
-  // Prepares the visible content frame associated with this View to be embedded
-  // within another Mus window tree. The response's |token| can be passed to the
-  // desired embedder once received.
-  PrepareToEmbed() => (mojo_base.mojom.UnguessableToken token);
-
-  // Initiates a navigation to |url|.
-  Navigate(url.mojom.Url url);
-};
-
-// A client interface used by the Content Service to push view-scoped events
-// back to the application.
-interface ViewClient {
-  // Notifies the client that the View has stopped loading resources pertaining
-  // to a navigation.
-  DidStopLoading();
-};
diff --git a/services/content/public/mojom/view_factory.mojom b/services/content/public/mojom/view_factory.mojom
deleted file mode 100644
index fd7d98c..0000000
--- a/services/content/public/mojom/view_factory.mojom
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module content.mojom;
-
-import "services/content/public/mojom/view.mojom";
-
-// Parameters used to configure a newly created View.
-struct ViewParams {};
-
-// ViewFactory is the primary interface through which a new View interface is
-// bound to a new concrete content view.
-interface ViewFactory {
-  // Creates a new content view configured according to |params|. |request| is
-  // bound to the view implementation, and |client| is used to push
-  // notifications of events relevant to the state of the new view throughout
-  // its lifetime.
-  CreateView(ViewParams params, View& request, ViewClient client);
-};
diff --git a/services/content/service.cc b/services/content/service.cc
index dcbb091..f8be8865 100644
--- a/services/content/service.cc
+++ b/services/content/service.cc
@@ -9,19 +9,20 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/binding.h"
-#include "services/content/public/mojom/view_factory.mojom.h"
+#include "services/content/navigable_contents_factory_impl.h"
+#include "services/content/navigable_contents_impl.h"
+#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
 #include "services/content/service_delegate.h"
-#include "services/content/view_factory_impl.h"
-#include "services/content/view_impl.h"
 #include "services/service_manager/public/cpp/service_context.h"
 
 namespace content {
 
 Service::Service(ServiceDelegate* delegate) : delegate_(delegate) {
   binders_.AddInterface(base::BindRepeating(
-      [](Service* service, mojom::ViewFactoryRequest request) {
-        service->AddViewFactory(
-            std::make_unique<ViewFactoryImpl>(service, std::move(request)));
+      [](Service* service, mojom::NavigableContentsFactoryRequest request) {
+        service->AddNavigableContentsFactory(
+            std::make_unique<NavigableContentsFactoryImpl>(service,
+                                                           std::move(request)));
       },
       this));
 }
@@ -33,31 +34,34 @@
 void Service::ForceQuit() {
   // Ensure that all bound interfaces are disconnected and no further interface
   // requests will be handled.
-  view_factories_.clear();
-  views_.clear();
-  binders_.RemoveInterface<mojom::ViewFactory>();
+  navigable_contents_factories_.clear();
+  navigable_contents_.clear();
+  binders_.RemoveInterface<mojom::NavigableContentsFactory>();
 
   // Force-disconnect from the Service Mangager. Under normal circumstances
   // (i.e. in non-test code), the call below destroys |this|.
   context()->QuitNow();
 }
 
-void Service::AddViewFactory(std::unique_ptr<ViewFactoryImpl> factory) {
+void Service::AddNavigableContentsFactory(
+    std::unique_ptr<NavigableContentsFactoryImpl> factory) {
   auto* raw_factory = factory.get();
-  view_factories_.emplace(raw_factory, std::move(factory));
+  navigable_contents_factories_.emplace(raw_factory, std::move(factory));
 }
 
-void Service::RemoveViewFactory(ViewFactoryImpl* factory) {
-  view_factories_.erase(factory);
+void Service::RemoveNavigableContentsFactory(
+    NavigableContentsFactoryImpl* factory) {
+  navigable_contents_factories_.erase(factory);
 }
 
-void Service::AddView(std::unique_ptr<ViewImpl> view) {
-  auto* raw_view = view.get();
-  views_.emplace(raw_view, std::move(view));
+void Service::AddNavigableContents(
+    std::unique_ptr<NavigableContentsImpl> contents) {
+  auto* raw_contents = contents.get();
+  navigable_contents_.emplace(raw_contents, std::move(contents));
 }
 
-void Service::RemoveView(ViewImpl* view) {
-  views_.erase(view);
+void Service::RemoveNavigableContents(NavigableContentsImpl* contents) {
+  navigable_contents_.erase(contents);
 }
 
 void Service::OnBindInterface(const service_manager::BindSourceInfo& source,
diff --git a/services/content/service.h b/services/content/service.h
index 2a38a16..551cbd1 100644
--- a/services/content/service.h
+++ b/services/content/service.h
@@ -14,8 +14,8 @@
 namespace content {
 
 class ServiceDelegate;
-class ViewFactoryImpl;
-class ViewImpl;
+class NavigableContentsFactoryImpl;
+class NavigableContentsImpl;
 
 // The core Service implementation of the Content Service. This takes
 // responsibility for owning top-level state for an instance of the service,
@@ -38,14 +38,15 @@
   void ForceQuit();
 
  private:
-  friend class ViewFactoryImpl;
-  friend class ViewImpl;
+  friend class NavigableContentsFactoryImpl;
+  friend class NavigableContentsImpl;
 
-  void AddViewFactory(std::unique_ptr<ViewFactoryImpl> factory);
-  void RemoveViewFactory(ViewFactoryImpl* factory);
+  void AddNavigableContentsFactory(
+      std::unique_ptr<NavigableContentsFactoryImpl> factory);
+  void RemoveNavigableContentsFactory(NavigableContentsFactoryImpl* factory);
 
-  void AddView(std::unique_ptr<ViewImpl> view);
-  void RemoveView(ViewImpl* view);
+  void AddNavigableContents(std::unique_ptr<NavigableContentsImpl> contents);
+  void RemoveNavigableContents(NavigableContentsImpl* contents);
 
   // service_manager::Service:
   void OnBindInterface(const service_manager::BindSourceInfo& source,
@@ -55,8 +56,11 @@
   ServiceDelegate* const delegate_;
   service_manager::BinderRegistry binders_;
 
-  std::map<ViewFactoryImpl*, std::unique_ptr<ViewFactoryImpl>> view_factories_;
-  std::map<ViewImpl*, std::unique_ptr<ViewImpl>> views_;
+  std::map<NavigableContentsFactoryImpl*,
+           std::unique_ptr<NavigableContentsFactoryImpl>>
+      navigable_contents_factories_;
+  std::map<NavigableContentsImpl*, std::unique_ptr<NavigableContentsImpl>>
+      navigable_contents_;
 
   DISALLOW_COPY_AND_ASSIGN(Service);
 };
diff --git a/services/content/service_delegate.h b/services/content/service_delegate.h
index 7d96dccb..650a2e1 100644
--- a/services/content/service_delegate.h
+++ b/services/content/service_delegate.h
@@ -5,12 +5,12 @@
 #ifndef SERVICES_CONTENT_SERVICE_DELEGATE_H_
 #define SERVICES_CONTENT_SERVICE_DELEGATE_H_
 
-#include "services/content/public/mojom/view.mojom.h"
+#include "services/content/public/mojom/navigable_contents.mojom.h"
 
 namespace content {
 
+class NavigableContentsDelegate;
 class Service;
-class ViewDelegate;
 
 // This is a delegate interface which allows the Content Service implementation
 // to delegate out to private src/content code without a circular dependency
@@ -28,12 +28,12 @@
   // delegate) is about to be destroyed.
   virtual void WillDestroyServiceInstance(Service* service) = 0;
 
-  // Constructs a new ViewDelegate implementation to back a new ContentViewImpl
-  // instance, servicing a client's ContentView. |client| is a ViewClient
-  // interface the implementation can use to communicate with the client of this
-  // view.
-  virtual std::unique_ptr<ViewDelegate> CreateViewDelegate(
-      mojom::ViewClient* client) = 0;
+  // Constructs a new NavigableContentsDelegate implementation to back a new
+  // NavigableContentsImpl instance, servicing a client's NavigableContents.
+  // |client| is a NavigableContentsClient interface the implementation can use
+  // to communicate with the client of this contents.
+  virtual std::unique_ptr<NavigableContentsDelegate>
+  CreateNavigableContentsDelegate(mojom::NavigableContentsClient* client) = 0;
 };
 
 };  // namespace content
diff --git a/services/content/service_unittest.cc b/services/content/service_unittest.cc
index 928ee1f..d0e08da 100644
--- a/services/content/service_unittest.cc
+++ b/services/content/service_unittest.cc
@@ -11,11 +11,11 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "mojo/public/cpp/bindings/binding.h"
+#include "services/content/navigable_contents_delegate.h"
 #include "services/content/public/mojom/constants.mojom.h"
-#include "services/content/public/mojom/view.mojom.h"
-#include "services/content/public/mojom/view_factory.mojom.h"
+#include "services/content/public/mojom/navigable_contents.mojom.h"
+#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
 #include "services/content/service_delegate.h"
-#include "services/content/view_delegate.h"
 #include "services/service_manager/public/cpp/test/test_connector_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -23,22 +23,22 @@
 namespace content {
 namespace {
 
-class TestViewClient : public mojom::ViewClient {
+class TestNavigableContentsClient : public mojom::NavigableContentsClient {
  public:
-  TestViewClient() = default;
-  ~TestViewClient() override = default;
+  TestNavigableContentsClient() = default;
+  ~TestNavigableContentsClient() override = default;
 
  private:
-  // mojom::ViewClient:
+  // mojom::NavigableContentsClient:
   void DidStopLoading() override {}
 
-  DISALLOW_COPY_AND_ASSIGN(TestViewClient);
+  DISALLOW_COPY_AND_ASSIGN(TestNavigableContentsClient);
 };
 
-class TestViewDelegate : public ViewDelegate {
+class TestNavigableContentsDelegate : public NavigableContentsDelegate {
  public:
-  TestViewDelegate() = default;
-  ~TestViewDelegate() override = default;
+  TestNavigableContentsDelegate() = default;
+  ~TestNavigableContentsDelegate() override = default;
 
   const GURL& last_navigated_url() const { return last_navigated_url_; }
 
@@ -46,7 +46,7 @@
     navigation_callback_ = std::move(callback);
   }
 
-  // ViewDelegate:
+  // NavigableContentsDelegate:
   void Navigate(const GURL& url) override {
     last_navigated_url_ = url;
     if (navigation_callback_)
@@ -59,7 +59,7 @@
   GURL last_navigated_url_;
   base::RepeatingClosure navigation_callback_;
 
-  DISALLOW_COPY_AND_ASSIGN(TestViewDelegate);
+  DISALLOW_COPY_AND_ASSIGN(TestNavigableContentsDelegate);
 };
 
 class TestServiceDelegate : public ServiceDelegate {
@@ -67,25 +67,25 @@
   TestServiceDelegate() = default;
   ~TestServiceDelegate() override = default;
 
-  void set_view_delegate_created_callback(
-      base::RepeatingCallback<void(TestViewDelegate*)> callback) {
-    view_delegate_created_callback_ = std::move(callback);
+  void set_navigable_contents_delegate_created_callback(
+      base::RepeatingCallback<void(TestNavigableContentsDelegate*)> callback) {
+    navigable_contents_delegate_created_callback_ = std::move(callback);
   }
 
   // ServiceDelegate:
   void WillDestroyServiceInstance(Service* service) override {}
 
-  std::unique_ptr<ViewDelegate> CreateViewDelegate(
-      mojom::ViewClient* client) override {
-    auto object = std::make_unique<TestViewDelegate>();
-    if (view_delegate_created_callback_)
-      view_delegate_created_callback_.Run(object.get());
-    return object;
+  std::unique_ptr<NavigableContentsDelegate> CreateNavigableContentsDelegate(
+      mojom::NavigableContentsClient* client) override {
+    auto delegate = std::make_unique<TestNavigableContentsDelegate>();
+    if (navigable_contents_delegate_created_callback_)
+      navigable_contents_delegate_created_callback_.Run(delegate.get());
+    return delegate;
   }
 
  private:
-  base::RepeatingCallback<void(TestViewDelegate*)>
-      view_delegate_created_callback_;
+  base::RepeatingCallback<void(TestNavigableContentsDelegate*)>
+      navigable_contents_delegate_created_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(TestServiceDelegate);
 };
@@ -119,38 +119,39 @@
   DISALLOW_COPY_AND_ASSIGN(ContentServiceTest);
 };
 
-TEST_F(ContentServiceTest, ViewCreation) {
-  mojom::ViewFactoryPtr factory;
+TEST_F(ContentServiceTest, NavigableContentsCreation) {
+  mojom::NavigableContentsFactoryPtr factory;
   BindInterface(mojo::MakeRequest(&factory));
 
   base::RunLoop loop;
 
-  TestViewDelegate* view_delegate = nullptr;
-  delegate().set_view_delegate_created_callback(
-      base::BindLambdaForTesting([&](TestViewDelegate* delegate) {
-        EXPECT_FALSE(view_delegate);
-        view_delegate = delegate;
+  TestNavigableContentsDelegate* navigable_contents_delegate = nullptr;
+  delegate().set_navigable_contents_delegate_created_callback(
+      base::BindLambdaForTesting([&](TestNavigableContentsDelegate* delegate) {
+        EXPECT_FALSE(navigable_contents_delegate);
+        navigable_contents_delegate = delegate;
         loop.Quit();
       }));
 
-  mojom::ViewPtr view;
-  TestViewClient client_impl;
-  mojom::ViewClientPtr client;
-  mojo::Binding<mojom::ViewClient> client_binding(&client_impl,
-                                                  mojo::MakeRequest(&client));
-  factory->CreateView(mojom::ViewParams::New(), mojo::MakeRequest(&view),
-                      std::move(client));
+  mojom::NavigableContentsPtr contents;
+  TestNavigableContentsClient client_impl;
+  mojom::NavigableContentsClientPtr client;
+  mojo::Binding<mojom::NavigableContentsClient> client_binding(
+      &client_impl, mojo::MakeRequest(&client));
+  factory->CreateContents(mojom::NavigableContentsParams::New(),
+                          mojo::MakeRequest(&contents), std::move(client));
   loop.Run();
 
   base::RunLoop navigation_loop;
-  ASSERT_TRUE(view_delegate);
-  view_delegate->set_navigation_callback(navigation_loop.QuitClosure());
+  ASSERT_TRUE(navigable_contents_delegate);
+  navigable_contents_delegate->set_navigation_callback(
+      navigation_loop.QuitClosure());
 
   const GURL kTestUrl("https://example.com/");
-  view->Navigate(kTestUrl);
+  contents->Navigate(kTestUrl);
   navigation_loop.Run();
 
-  EXPECT_EQ(kTestUrl, view_delegate->last_navigated_url());
+  EXPECT_EQ(kTestUrl, navigable_contents_delegate->last_navigated_url());
 }
 
 }  // namespace
diff --git a/services/content/simple_browser/manifest.json b/services/content/simple_browser/manifest.json
index 5d8332a..44ca599 100644
--- a/services/content/simple_browser/manifest.json
+++ b/services/content/simple_browser/manifest.json
@@ -9,7 +9,7 @@
         "app": []
       },
       "requires": {
-        "content": [ "view" ],
+        "content": [ "navigation" ],
         "font_service": [ "font_service" ],
         "ui": [ "app" ]
       }
diff --git a/services/content/simple_browser/window.cc b/services/content/simple_browser/window.cc
index 4dc31d9d..4b9b5cf 100644
--- a/services/content/simple_browser/window.cc
+++ b/services/content/simple_browser/window.cc
@@ -8,9 +8,9 @@
 #include <utility>
 
 #include "base/strings/utf_string_conversions.h"
-#include "services/content/public/cpp/view.h"
+#include "services/content/public/cpp/navigable_contents.h"
 #include "services/content/public/mojom/constants.mojom.h"
-#include "services/content/public/mojom/view_factory.mojom.h"
+#include "services/content/public/mojom/navigable_contents_factory.mojom.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
@@ -35,11 +35,12 @@
     AddChildView(location_bar_);
 
     connector_->BindInterface(content::mojom::kServiceName,
-                              MakeRequest(&view_factory_));
-    view_ = std::make_unique<content::View>(view_factory_.get());
-    content_area_ = view_->CreateUI();
-    content_area_->SetBorder(views::CreateSolidBorder(2, SK_ColorGREEN));
-    AddChildView(content_area_);
+                              MakeRequest(&navigable_contents_factory_));
+    navigable_contents_ = std::make_unique<content::NavigableContents>(
+        navigable_contents_factory_.get());
+    content_view_ = navigable_contents_->GetView();
+    content_view_->SetBorder(views::CreateSolidBorder(2, SK_ColorGREEN));
+    AddChildView(content_view_);
   }
 
   ~SimpleBrowserUI() override = default;
@@ -56,9 +57,9 @@
     location_bar_bounds.Inset(5, 0);
     location_bar_->SetBoundsRect(location_bar_bounds);
 
-    gfx::Rect content_area_bounds = GetLocalBounds();
-    content_area_bounds.Inset(5, 25, 5, 5);
-    content_area_->SetBoundsRect(content_area_bounds);
+    gfx::Rect content_view_bounds = GetLocalBounds();
+    content_view_bounds.Inset(5, 25, 5, 5);
+    content_view_->SetBoundsRect(content_view_bounds);
   }
 
   gfx::Size CalculatePreferredSize() const override {
@@ -71,19 +72,21 @@
     if (key_event.type() != ui::ET_KEY_PRESSED)
       return false;
 
-    if (key_event.key_code() == ui::VKEY_RETURN)
-      view_->Navigate(GURL(base::UTF16ToUTF8(location_bar_->text())));
+    if (key_event.key_code() == ui::VKEY_RETURN) {
+      navigable_contents_->Navigate(
+          GURL(base::UTF16ToUTF8(location_bar_->text())));
+    }
 
     return false;
   }
 
   service_manager::Connector* const connector_;
 
-  content::mojom::ViewFactoryPtr view_factory_;
-  std::unique_ptr<content::View> view_;
+  content::mojom::NavigableContentsFactoryPtr navigable_contents_factory_;
+  std::unique_ptr<content::NavigableContents> navigable_contents_;
 
   views::Textfield* location_bar_;
-  views::View* content_area_;
+  views::View* content_view_;
 
   DISALLOW_COPY_AND_ASSIGN(SimpleBrowserUI);
 };
diff --git a/services/content/view_delegate.h b/services/content/view_delegate.h
deleted file mode 100644
index 5c3364b..0000000
--- a/services/content/view_delegate.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_CONTENT_VIEW_DELEGATE_H_
-#define SERVICES_CONTENT_VIEW_DELEGATE_H_
-
-#include "ui/gfx/native_widget_types.h"
-
-class GURL;
-
-namespace content {
-
-// A virtual interface which must be implemented as a backing for ViewImpl
-// instances.
-//
-// This is the primary interface by which the Content Service delegates ViewImpl
-// behavior out to WebContentsImpl in src/content. As such it is a transitional
-// API which will be removed as soon as WebContentsImpl itself can be fully
-// migrated into Content Service.
-//
-// Each instance of this interface is constructed by the ContentServiceDelegate
-// implementation and owned by a ViewImpl.
-class ViewDelegate {
- public:
-  virtual ~ViewDelegate() {}
-
-  // Returns a NativeView that can be embedded into a client application's
-  // window tree to display the web contents navigated by the delegate's View.
-  virtual gfx::NativeView GetNativeView() = 0;
-
-  // Navigates the content object to a new URL.
-  virtual void Navigate(const GURL& url) = 0;
-};
-
-}  // namespace content
-
-#endif  // SERVICES_CONTENT_VIEW_DELEGATE_H_
diff --git a/services/content/view_factory_impl.cc b/services/content/view_factory_impl.cc
deleted file mode 100644
index cb1b74b..0000000
--- a/services/content/view_factory_impl.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "services/content/view_factory_impl.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "services/content/service.h"
-#include "services/content/view_impl.h"
-
-namespace content {
-
-ViewFactoryImpl::ViewFactoryImpl(Service* service,
-                                 mojom::ViewFactoryRequest request)
-    : service_(service), binding_(this, std::move(request)) {
-  binding_.set_connection_error_handler(base::BindOnce(
-      &Service::RemoveViewFactory, base::Unretained(service_), this));
-}
-
-ViewFactoryImpl::~ViewFactoryImpl() = default;
-
-void ViewFactoryImpl::CreateView(mojom::ViewParamsPtr params,
-                                 mojom::ViewRequest request,
-                                 mojom::ViewClientPtr client) {
-  service_->AddView(std::make_unique<ViewImpl>(
-      service_, std::move(params), std::move(request), std::move(client)));
-}
-
-}  // namespace content
diff --git a/services/content/view_factory_impl.h b/services/content/view_factory_impl.h
deleted file mode 100644
index 9c44c3ac..0000000
--- a/services/content/view_factory_impl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_CONTENT_VIEW_FACTORY_IMPL_H_
-#define SERVICES_CONTENT_VIEW_FACTORY_IMPL_H_
-
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/content/public/mojom/view_factory.mojom.h"
-
-namespace content {
-
-class Service;
-
-class ViewFactoryImpl : public mojom::ViewFactory {
- public:
-  ViewFactoryImpl(Service* service, mojom::ViewFactoryRequest request);
-  ~ViewFactoryImpl() override;
-
- private:
-  // mojom::ViewFactory:
-  void CreateView(mojom::ViewParamsPtr params,
-                  mojom::ViewRequest request,
-                  mojom::ViewClientPtr client) override;
-
-  Service* const service_;
-  mojo::Binding<mojom::ViewFactory> binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(ViewFactoryImpl);
-};
-
-}  // namespace content
-
-#endif  // SERVICES_CONTENT_VIEW_FACTORY_IMPL_H_
diff --git a/services/content/view_impl.h b/services/content/view_impl.h
deleted file mode 100644
index dfac642..0000000
--- a/services/content/view_impl.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SERVICES_CONTENT_VIEW_IMPL_H_
-#define SERVICES_CONTENT_VIEW_IMPL_H_
-
-#include "base/macros.h"
-#include "base/unguessable_token.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/content/public/cpp/buildflags.h"
-#include "services/content/public/mojom/view.mojom.h"
-#include "services/content/public/mojom/view_factory.mojom.h"
-#include "ui/gfx/native_widget_types.h"
-
-namespace views {
-class RemoteViewProvider;
-}
-
-namespace content {
-
-class Service;
-class ViewDelegate;
-
-class ViewImpl : public mojom::View {
- public:
-  ViewImpl(Service* service,
-           mojom::ViewParamsPtr params,
-           mojom::ViewRequest request,
-           mojom::ViewClientPtr client);
-  ~ViewImpl() override;
-
- private:
-  // mojom::View:
-  void PrepareToEmbed(PrepareToEmbedCallback callback) override;
-  void Navigate(const GURL& url) override;
-
-  void OnEmbedTokenReceived(PrepareToEmbedCallback callback,
-                            const base::UnguessableToken& token);
-
-  Service* const service_;
-
-  mojo::Binding<mojom::View> binding_;
-  mojom::ViewClientPtr client_;
-  std::unique_ptr<ViewDelegate> delegate_;
-  gfx::NativeView native_content_view_;
-
-#if BUILDFLAG(ENABLE_AURA_CONTENT_VIEW_EMBEDDING)
-  std::unique_ptr<views::RemoteViewProvider> remote_view_provider_;
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(ViewImpl);
-};
-
-}  // namespace content
-
-#endif  // SERVICES_CONTENT_VIEW_IMPL_H_
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn
index ecbd41c2..83dce91 100644
--- a/services/device/BUILD.gn
+++ b/services/device/BUILD.gn
@@ -117,6 +117,7 @@
     "//services/device/public/cpp/power_monitor",
     "//services/device/public/mojom",
     "//services/device/wake_lock",
+    "//services/network:test_support",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -237,6 +238,7 @@
     "//net:test_support",
     "//services/device/public/cpp/geolocation",
     "//services/device/public/mojom:constants",
+    "//services/network:test_support",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp:service_test_support",
   ]
diff --git a/services/device/DEPS b/services/device/DEPS
index ae6faf02..35d0c5e4 100644
--- a/services/device/DEPS
+++ b/services/device/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+device",
   "+jni",
-  "+net/url_request",
+  "+services/network/public/cpp",
+  "+services/network/test",
   "+ui/gfx/native_widget_types.h",
 ]
diff --git a/services/device/device_service.cc b/services/device/device_service.cc
index 44cacfed..0c55aa9 100644
--- a/services/device/device_service.cc
+++ b/services/device/device_service.cc
@@ -24,6 +24,7 @@
 #include "services/device/serial/serial_io_handler_impl.h"
 #include "services/device/time_zone_monitor/time_zone_monitor.h"
 #include "services/device/wake_lock/wake_lock_provider.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "ui/gfx/native_widget_types.h"
 
 #if defined(OS_ANDROID)
@@ -47,35 +48,33 @@
 std::unique_ptr<service_manager::Service> CreateDeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    const GeolocationProvider::RequestContextProducer
-        geolocation_request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& geolocation_api_key,
     bool use_gms_core_location_provider,
     const WakeLockContextCallback& wake_lock_context_callback,
     const CustomLocationProviderCallback& custom_location_provider_callback,
     const base::android::JavaRef<jobject>& java_nfc_delegate) {
   GeolocationProviderImpl::SetGeolocationConfiguration(
-      geolocation_request_context_producer, geolocation_api_key,
+      url_loader_factory, geolocation_api_key,
       custom_location_provider_callback, use_gms_core_location_provider);
   return std::make_unique<DeviceService>(
       std::move(file_task_runner), std::move(io_task_runner),
-      std::move(geolocation_request_context_producer), geolocation_api_key,
+      std::move(url_loader_factory), geolocation_api_key,
       wake_lock_context_callback, java_nfc_delegate);
 }
 #else
 std::unique_ptr<service_manager::Service> CreateDeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    const GeolocationProvider::RequestContextProducer
-        geolocation_request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& geolocation_api_key,
     const CustomLocationProviderCallback& custom_location_provider_callback) {
   GeolocationProviderImpl::SetGeolocationConfiguration(
-      geolocation_request_context_producer, geolocation_api_key,
+      url_loader_factory, geolocation_api_key,
       custom_location_provider_callback);
   return std::make_unique<DeviceService>(
       std::move(file_task_runner), std::move(io_task_runner),
-      std::move(geolocation_request_context_producer), geolocation_api_key);
+      std::move(url_loader_factory), geolocation_api_key);
 }
 #endif
 
@@ -83,15 +82,13 @@
 DeviceService::DeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    const GeolocationProvider::RequestContextProducer
-        geolocation_request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& geolocation_api_key,
     const WakeLockContextCallback& wake_lock_context_callback,
     const base::android::JavaRef<jobject>& java_nfc_delegate)
     : file_task_runner_(std::move(file_task_runner)),
       io_task_runner_(std::move(io_task_runner)),
-      geolocation_request_context_producer_(
-          geolocation_request_context_producer),
+      url_loader_factory_(std::move(url_loader_factory)),
       geolocation_api_key_(geolocation_api_key),
       wake_lock_context_callback_(wake_lock_context_callback),
       java_interface_provider_initialized_(false) {
@@ -101,13 +98,11 @@
 DeviceService::DeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    const GeolocationProvider::RequestContextProducer
-        geolocation_request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& geolocation_api_key)
     : file_task_runner_(std::move(file_task_runner)),
       io_task_runner_(std::move(io_task_runner)),
-      geolocation_request_context_producer_(
-          geolocation_request_context_producer),
+      url_loader_factory_(std::move(url_loader_factory)),
       geolocation_api_key_(geolocation_api_key) {}
 #endif
 
@@ -267,7 +262,7 @@
   if (!public_ip_address_geolocation_provider_) {
     public_ip_address_geolocation_provider_ =
         std::make_unique<PublicIpAddressGeolocationProvider>(
-            geolocation_request_context_producer_, geolocation_api_key_);
+            url_loader_factory_, geolocation_api_key_);
   }
 
   public_ip_address_geolocation_provider_->Bind(std::move(request));
diff --git a/services/device/device_service.h b/services/device/device_service.h
index 12146f3..2eae9a4 100644
--- a/services/device/device_service.h
+++ b/services/device/device_service.h
@@ -51,6 +51,10 @@
 class SingleThreadTaskRunner;
 }
 
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
 namespace device {
 
 #if !defined(OS_ANDROID)
@@ -68,8 +72,7 @@
 std::unique_ptr<service_manager::Service> CreateDeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    GeolocationProvider::RequestContextProducer
-        geolocation_request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& geolocation_api_key,
     bool use_gms_core_location_provider,
     const WakeLockContextCallback& wake_lock_context_callback,
@@ -79,8 +82,7 @@
 std::unique_ptr<service_manager::Service> CreateDeviceService(
     scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-    GeolocationProvider::RequestContextProducer
-        geolocation_request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& geolocation_api_key,
     const CustomLocationProviderCallback& custom_location_provider_callback);
 #endif
@@ -88,19 +90,19 @@
 class DeviceService : public service_manager::Service {
  public:
 #if defined(OS_ANDROID)
-  DeviceService(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
-                scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-                GeolocationProvider::RequestContextProducer
-                    geolocation_request_context_producer,
-                const std::string& geolocation_api_key,
-                const WakeLockContextCallback& wake_lock_context_callback,
-                const base::android::JavaRef<jobject>& java_nfc_delegate);
+  DeviceService(
+      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& geolocation_api_key,
+      const WakeLockContextCallback& wake_lock_context_callback,
+      const base::android::JavaRef<jobject>& java_nfc_delegate);
 #else
-  DeviceService(scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
-                scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-                GeolocationProvider::RequestContextProducer
-                    geolocation_request_context_producer,
-                const std::string& geolocation_api_key);
+  DeviceService(
+      scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& geolocation_api_key);
 #endif
   ~DeviceService() override;
 
@@ -157,9 +159,8 @@
   std::unique_ptr<TimeZoneMonitor> time_zone_monitor_;
   scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
-  GeolocationProvider::RequestContextProducer
-      geolocation_request_context_producer_;
   const std::string geolocation_api_key_;
   WakeLockContextCallback wake_lock_context_callback_;
 
diff --git a/services/device/device_service_test_base.cc b/services/device/device_service_test_base.cc
index 0acf452..0e7a7f12 100644
--- a/services/device/device_service_test_base.cc
+++ b/services/device/device_service_test_base.cc
@@ -6,14 +6,15 @@
 
 #include <memory>
 
+#include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "net/url_request/url_request_test_util.h"
 #include "services/device/device_service.h"
 #include "services/device/public/cpp/geolocation/location_provider.h"
 #include "services/device/public/mojom/constants.mojom.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "services/service_manager/public/mojom/service_factory.mojom.h"
@@ -24,17 +25,6 @@
 
 const char kTestServiceName[] = "device_unittests";
 
-// Simple request context producer that immediately produces a
-// TestURLRequestContextGetter.
-void TestRequestContextProducer(
-    const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        response_callback) {
-  std::move(response_callback)
-      .Run(base::MakeRefCounted<net::TestURLRequestContextGetter>(
-          network_task_runner));
-}
-
 // Simply return a nullptr which means no CustomLocationProvider from embedder.
 std::unique_ptr<LocationProvider> GetCustomLocationProviderForTest() {
   return nullptr;
@@ -47,10 +37,12 @@
   explicit ServiceTestClient(
       service_manager::test::ServiceTest* test,
       scoped_refptr<base::SingleThreadTaskRunner> file_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
       : service_manager::test::ServiceTestClient(test),
         file_task_runner_(std::move(file_task_runner)),
-        io_task_runner_(std::move(io_task_runner)) {
+        io_task_runner_(std::move(io_task_runner)),
+        url_loader_factory_(std::move(url_loader_factory)) {
     registry_.AddInterface<service_manager::mojom::ServiceFactory>(
         base::Bind(&ServiceTestClient::Create, base::Unretained(this)));
   }
@@ -71,18 +63,16 @@
 #if defined(OS_ANDROID)
       device_service_context_.reset(new service_manager::ServiceContext(
           CreateDeviceService(
-              file_task_runner_, io_task_runner_,
-              base::Bind(&TestRequestContextProducer, io_task_runner_),
+              file_task_runner_, io_task_runner_, url_loader_factory_,
               kTestGeolocationApiKey, false, wake_lock_context_callback_,
-              base::Bind(&GetCustomLocationProviderForTest), nullptr),
+              base::BindRepeating(&GetCustomLocationProviderForTest), nullptr),
           std::move(request)));
 #else
       device_service_context_.reset(new service_manager::ServiceContext(
           CreateDeviceService(
-              file_task_runner_, io_task_runner_,
-              base::Bind(&TestRequestContextProducer, io_task_runner_),
+              file_task_runner_, io_task_runner_, url_loader_factory_,
               kTestGeolocationApiKey,
-              base::Bind(&GetCustomLocationProviderForTest)),
+              base::BindRepeating(&GetCustomLocationProviderForTest)),
           std::move(request)));
 #endif
     }
@@ -97,10 +87,13 @@
   mojo::BindingSet<service_manager::mojom::ServiceFactory>
       service_factory_bindings_;
   std::unique_ptr<service_manager::ServiceContext> device_service_context_;
-  scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
-  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+  const scoped_refptr<base::SingleThreadTaskRunner> file_task_runner_;
+  const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+  const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   WakeLockContextCallback wake_lock_context_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceTestClient);
 };
 
 }  // namespace
@@ -118,8 +111,10 @@
 
 std::unique_ptr<service_manager::Service>
 DeviceServiceTestBase::CreateService() {
-  return std::make_unique<ServiceTestClient>(this, file_thread_.task_runner(),
-                                             io_thread_.task_runner());
+  return std::make_unique<ServiceTestClient>(
+      this, file_thread_.task_runner(), io_thread_.task_runner(),
+      base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+          &test_url_loader_factory_));
 }
 
 }  // namespace device
diff --git a/services/device/device_service_test_base.h b/services/device/device_service_test_base.h
index 27938538..e7e35a7 100644
--- a/services/device/device_service_test_base.h
+++ b/services/device/device_service_test_base.h
@@ -7,6 +7,7 @@
 
 #include "base/macros.h"
 #include "base/threading/thread.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "services/service_manager/public/cpp/service_test.h"
 
 namespace device {
@@ -22,6 +23,7 @@
  protected:
   base::Thread file_thread_;
   base::Thread io_thread_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
 
  private:
   // service_manager::test::ServiceTest:
diff --git a/services/device/geolocation/BUILD.gn b/services/device/geolocation/BUILD.gn
index 03e7230..0a139b7 100644
--- a/services/device/geolocation/BUILD.gn
+++ b/services/device/geolocation/BUILD.gn
@@ -75,6 +75,7 @@
   public_deps = [
     "//services/device/public/cpp/geolocation",
     "//services/device/public/mojom",
+    "//services/network/public/cpp",
   ]
   if (is_android) {
     sources -= [
diff --git a/services/device/geolocation/DEPS b/services/device/geolocation/DEPS
index d3e3a8c..0687f47a 100644
--- a/services/device/geolocation/DEPS
+++ b/services/device/geolocation/DEPS
@@ -2,6 +2,9 @@
   "+chromeos",
   "+dbus",
   "+jni",
-  "+net",
+  "+net/base",
+  "+net/traffic_annotation",
+  "+services/network/public/cpp",
+  "+services/network/test",
   "+third_party/cros_system_api/dbus",
 ]
diff --git a/services/device/geolocation/geolocation_provider.h b/services/device/geolocation/geolocation_provider.h
index 1201e06..04ce5a1 100644
--- a/services/device/geolocation/geolocation_provider.h
+++ b/services/device/geolocation/geolocation_provider.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/callback_list.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/device/public/mojom/geoposition.mojom.h"
 
 namespace device {
@@ -33,11 +32,6 @@
  public:
   static GeolocationProvider* GetInstance();
 
-  // Callback type for a function that asynchronously produces a
-  // URLRequestContextGetter.
-  using RequestContextProducer = base::RepeatingCallback<void(
-      base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>)>;
-
   typedef base::Callback<void(const mojom::Geoposition&)>
       LocationUpdateCallback;
   typedef base::CallbackList<void(const mojom::Geoposition&)>::Subscription
diff --git a/services/device/geolocation/geolocation_provider_impl.cc b/services/device/geolocation/geolocation_provider_impl.cc
index 0482c486..49b700a4 100644
--- a/services/device/geolocation/geolocation_provider_impl.cc
+++ b/services/device/geolocation/geolocation_provider_impl.cc
@@ -18,6 +18,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "services/device/geolocation/location_arbitrator.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/jni_android.h"
@@ -29,8 +30,8 @@
 namespace {
 base::LazyInstance<CustomLocationProviderCallback>::Leaky
     g_custom_location_provider_callback = LAZY_INSTANCE_INITIALIZER;
-base::LazyInstance<GeolocationProvider::RequestContextProducer>::Leaky
-    g_request_context_producer = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<std::unique_ptr<network::SharedURLLoaderFactoryInfo>>::Leaky
+    g_url_loader_factory_info = LAZY_INSTANCE_INITIALIZER;
 base::LazyInstance<std::string>::Leaky g_api_key = LAZY_INSTANCE_INITIALIZER;
 }  // namespace
 
@@ -41,11 +42,12 @@
 
 // static
 void GeolocationProviderImpl::SetGeolocationConfiguration(
-    const GeolocationProvider::RequestContextProducer request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& api_key,
     const CustomLocationProviderCallback& custom_location_provider_getter,
     bool use_gms_core_location_provider) {
-  g_request_context_producer.Get() = request_context_producer;
+  if (url_loader_factory)
+    g_url_loader_factory_info.Get() = url_loader_factory->Clone();
   g_api_key.Get() = api_key;
   g_custom_location_provider_callback.Get() = custom_location_provider_getter;
   if (use_gms_core_location_provider) {
@@ -226,9 +228,15 @@
   LocationProvider::LocationProviderUpdateCallback callback = base::Bind(
       &GeolocationProviderImpl::OnLocationUpdate, base::Unretained(this));
 
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
+  if (g_url_loader_factory_info.Get()) {
+    url_loader_factory = network::SharedURLLoaderFactory::Create(
+        std::move(g_url_loader_factory_info.Get()));
+  }
+
   arbitrator_ = std::make_unique<LocationArbitrator>(
-      g_custom_location_provider_callback.Get(),
-      g_request_context_producer.Get(), g_api_key.Get());
+      g_custom_location_provider_callback.Get(), std::move(url_loader_factory),
+      g_api_key.Get());
   arbitrator_->SetUpdateCallback(callback);
 }
 
diff --git a/services/device/geolocation/geolocation_provider_impl.h b/services/device/geolocation/geolocation_provider_impl.h
index b4564e4..79cf0af 100644
--- a/services/device/geolocation/geolocation_provider_impl.h
+++ b/services/device/geolocation/geolocation_provider_impl.h
@@ -26,6 +26,10 @@
 class SingleThreadTaskRunner;
 }  // namespace base
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace device {
 
 // Callback that returns the embedder's custom location provider. This callback
@@ -56,16 +60,14 @@
 
   // Optional: Provide global configuration to Geolocation. Should be called
   // before using Init() on the singleton GetInstance().
-  // |request_context_producer| : a callback to produce a request context for
-  // network geolocation requests.
+  // |url_loader_factory| : a factory to use for network geolocation requests.
   // |api_key| : a Google API key for network geolocation requests.
   // |custom_location_provider_getter| : a callback which returns a custom
   // location provider from embedder.
   // |use_gms_core_location_provider| : For android only, a flag indicates
   // whether using the GMS core location provider.
   static void SetGeolocationConfiguration(
-      const GeolocationProvider::RequestContextProducer
-          request_context_producer,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& api_key,
       const CustomLocationProviderCallback& custom_location_provider_getter,
       bool use_gms_core_location_provider = false);
diff --git a/services/device/geolocation/geolocation_service_unittest.cc b/services/device/geolocation/geolocation_service_unittest.cc
index e727e98..18b19ae3 100644
--- a/services/device/geolocation/geolocation_service_unittest.cc
+++ b/services/device/geolocation/geolocation_service_unittest.cc
@@ -3,13 +3,13 @@
 // found in the LICENSE file.
 
 #include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
 #include "build/build_config.h"
 #if defined(OS_CHROMEOS)
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/network/geolocation_handler.h"
 #endif
 #include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "net/url_request/test_url_fetcher_factory.h"
 #include "services/device/device_service_test_base.h"
 #include "services/device/geolocation/geolocation_provider_impl.h"
 #include "services/device/geolocation/network_location_request.h"
@@ -30,39 +30,6 @@
   std::move(quit_closure).Run();
 }
 
-// Observer that waits until a TestURLFetcher with the specified fetcher_id
-// starts, after which it is made available through .fetcher().
-class TestURLFetcherObserver : public net::TestURLFetcher::DelegateForTests {
- public:
-  explicit TestURLFetcherObserver(int expected_fetcher_id)
-      : expected_fetcher_id_(expected_fetcher_id) {
-    factory_.SetDelegateForTests(this);
-  }
-  virtual ~TestURLFetcherObserver() {}
-
-  void Wait() { loop_.Run(); }
-
-  net::TestURLFetcher* fetcher() { return fetcher_; }
-
-  // net::TestURLFetcher::DelegateForTests:
-  void OnRequestStart(int fetcher_id) override {
-    if (fetcher_id == expected_fetcher_id_) {
-      fetcher_ = factory_.GetFetcherByID(fetcher_id);
-      fetcher_->SetDelegateForTests(nullptr);
-      factory_.SetDelegateForTests(nullptr);
-      loop_.Quit();
-    }
-  }
-  void OnChunkUpload(int fetcher_id) override {}
-  void OnRequestEnd(int fetcher_id) override {}
-
- private:
-  const int expected_fetcher_id_;
-  net::TestURLFetcher* fetcher_ = nullptr;
-  net::TestURLFetcherFactory factory_;
-  base::RunLoop loop_;
-};
-
 class GeolocationServiceUnitTest : public DeviceServiceTestBase {
  public:
   GeolocationServiceUnitTest() = default;
@@ -118,23 +85,20 @@
 // detected in a scan: https://crbug.com/767300.
 #else
 TEST_F(GeolocationServiceUnitTest, UrlWithApiKey) {
-  // Unique ID (derived from Gerrit CL number):
-  device::NetworkLocationRequest::url_fetcher_id_for_tests = 675023;
+  base::RunLoop loop;
+  test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
+      [&loop](const network::ResourceRequest& request) {
+        // Verify the full URL including a fake Google API key.
+        std::string expected_url =
+            "https://www.googleapis.com/geolocation/v1/geolocate?key=";
+        expected_url.append(kTestGeolocationApiKey);
 
-  // Intercept the URLFetcher from network geolocation request.
-  TestURLFetcherObserver observer(
-      device::NetworkLocationRequest::url_fetcher_id_for_tests);
+        if (request.url == expected_url)
+          loop.Quit();
+      }));
 
   geolocation_->SetHighAccuracy(true);
-  observer.Wait();
-  DCHECK(observer.fetcher());
-
-  // Verify full URL including a fake Google API key.
-  std::string expected_url =
-      "https://www.googleapis.com/geolocation/v1/"
-      "geolocate?key=";
-  expected_url.append(kTestGeolocationApiKey);
-  EXPECT_EQ(expected_url, observer.fetcher()->GetOriginalURL());
+  loop.Run();
 }
 #endif
 
diff --git a/services/device/geolocation/location_arbitrator.cc b/services/device/geolocation/location_arbitrator.cc
index a1a07a3..21ea659 100644
--- a/services/device/geolocation/location_arbitrator.cc
+++ b/services/device/geolocation/location_arbitrator.cc
@@ -15,6 +15,7 @@
 #include "services/device/geolocation/network_location_provider.h"
 #include "services/device/geolocation/wifi_polling_policy.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace device {
 
@@ -25,10 +26,10 @@
 
 LocationArbitrator::LocationArbitrator(
     const CustomLocationProviderCallback& custom_location_provider_getter,
-    GeolocationProvider::RequestContextProducer request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& api_key)
     : custom_location_provider_getter_(custom_location_provider_getter),
-      request_context_producer_(request_context_producer),
+      url_loader_factory_(url_loader_factory),
       api_key_(api_key),
       position_provider_(nullptr),
       is_permission_granted_(false),
@@ -64,18 +65,7 @@
 
   if (providers_.empty()) {
     RegisterSystemProvider();
-
-    // Request a URLRequestContextGetter to use for network geolocation.
-    if (!request_context_producer_.is_null()) {
-      // Note: .Reset() will cancel any previous callback.
-      request_context_response_callback_.Reset(
-          base::Bind(&LocationArbitrator::OnRequestContextResponse,
-                     base::Unretained(this)));
-      // Invoke callback to obtain a URL request context.
-      request_context_producer_.Run(
-          request_context_response_callback_.callback());
-      return;
-    }
+    RegisterNetworkProvider();
   }
   DoStartProviders();
 }
@@ -105,16 +95,6 @@
   is_running_ = false;
 }
 
-void LocationArbitrator::OnRequestContextResponse(
-    scoped_refptr<net::URLRequestContextGetter> context_getter) {
-  if (context_getter != nullptr) {
-    // Create a NetworkLocationProvider using the provided request context.
-    RegisterProvider(
-        NewNetworkLocationProvider(std::move(context_getter), api_key_));
-  }
-  DoStartProviders();
-}
-
 void LocationArbitrator::RegisterProvider(
     std::unique_ptr<LocationProvider> provider) {
   if (!provider)
@@ -137,6 +117,13 @@
   RegisterProvider(std::move(provider));
 }
 
+void LocationArbitrator::RegisterNetworkProvider() {
+  if (!url_loader_factory_)
+    return;
+
+  RegisterProvider(NewNetworkLocationProvider(url_loader_factory_, api_key_));
+}
+
 void LocationArbitrator::OnLocationUpdate(
     const LocationProvider* provider,
     const mojom::Geoposition& new_position) {
@@ -162,15 +149,15 @@
 
 std::unique_ptr<LocationProvider>
 LocationArbitrator::NewNetworkLocationProvider(
-    scoped_refptr<net::URLRequestContextGetter> context,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& api_key) {
-  DCHECK(context != nullptr);
+  DCHECK(url_loader_factory);
 #if defined(OS_ANDROID)
   // Android uses its own SystemLocationProvider.
   return nullptr;
 #else
-  return std::make_unique<NetworkLocationProvider>(std::move(context), api_key,
-                                                   this);
+  return std::make_unique<NetworkLocationProvider>(
+      std::move(url_loader_factory), api_key, this);
 #endif
 }
 
diff --git a/services/device/geolocation/location_arbitrator.h b/services/device/geolocation/location_arbitrator.h
index 750de88..5d89450 100644
--- a/services/device/geolocation/location_arbitrator.h
+++ b/services/device/geolocation/location_arbitrator.h
@@ -14,15 +14,14 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/device/geolocation/geolocation_provider_impl.h"
 #include "services/device/geolocation/network_location_provider.h"
 #include "services/device/public/cpp/geolocation/location_provider.h"
 #include "services/device/public/mojom/geoposition.mojom.h"
 #include "url/gurl.h"
 
-namespace net {
-class URLRequestContextGetter;
+namespace network {
+class SharedURLLoaderFactory;
 }
 
 namespace device {
@@ -42,8 +41,7 @@
   // LocationArbitrator uses the default system location provider.
   LocationArbitrator(
       const CustomLocationProviderCallback& custom_location_provider_getter,
-      const GeolocationProvider::RequestContextProducer
-          request_context_producer,
+      const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& api_key);
   ~LocationArbitrator() override;
 
@@ -66,7 +64,7 @@
   // These functions are useful for injection of dependencies in derived
   // testing classes.
   virtual std::unique_ptr<LocationProvider> NewNetworkLocationProvider(
-      scoped_refptr<net::URLRequestContextGetter> context,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& api_key);
   virtual std::unique_ptr<LocationProvider> NewSystemLocationProvider();
   virtual base::Time GetTimeNow() const;
@@ -78,6 +76,7 @@
   // deleted on error (e.g. it fails to start).
   void RegisterProvider(std::unique_ptr<LocationProvider> provider);
   void RegisterSystemProvider();
+  void RegisterNetworkProvider();
 
   // Tells all registered providers to start.
   // If |providers_| is empty, immediately provides
@@ -85,10 +84,6 @@
   // |arbitrator_update_callback_|.
   void DoStartProviders();
 
-  // Response callback for request_context_callback_.
-  void OnRequestContextResponse(
-      scoped_refptr<net::URLRequestContextGetter> context_getter);
-
   // Gets called when a provider has a new position.
   void OnLocationUpdate(const LocationProvider* provider,
                         const mojom::Geoposition& new_position);
@@ -101,16 +96,11 @@
                            bool from_same_provider) const;
 
   const CustomLocationProviderCallback custom_location_provider_getter_;
-  const GeolocationProvider::RequestContextProducer request_context_producer_;
+  const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   const std::string api_key_;
 
   LocationProvider::LocationProviderUpdateCallback arbitrator_update_callback_;
 
-  // CancelableCallback to prevent OnRequestContextReponse from being called
-  // multiple times in case request_context_callback_ is invoked multiple times.
-  base::CancelableCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-      request_context_response_callback_;
-
   std::vector<std::unique_ptr<LocationProvider>> providers_;
   bool enable_high_accuracy_;
   // The provider which supplied the current |position_|
diff --git a/services/device/geolocation/location_arbitrator_unittest.cc b/services/device/geolocation/location_arbitrator_unittest.cc
index 2a998c3..08c61f5 100644
--- a/services/device/geolocation/location_arbitrator_unittest.cc
+++ b/services/device/geolocation/location_arbitrator_unittest.cc
@@ -10,11 +10,11 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/test/scoped_task_environment.h"
-#include "net/url_request/url_request_test_util.h"
 #include "services/device/geolocation/fake_location_provider.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
 #include "services/device/public/cpp/geolocation/location_provider.h"
 #include "services/device/public/mojom/geoposition.mojom.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -77,37 +77,16 @@
   SetPositionFix(provider, 51.0, -0.1, 400);
 }
 
-// Simple request context producer that immediately produces a
-// TestURLRequestContextGetter.
-void TestRequestContextProducer(
-    const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        response_callback) {
-  std::move(response_callback)
-      .Run(base::MakeRefCounted<net::TestURLRequestContextGetter>(
-          network_task_runner));
-}
-
 }  // namespace
 
-// Simple request context producer that immediately produces a nullptr
-// URLRequestContextGetter, indicating that network geolocation should not be
-// used.
-void NullRequestContextProducer(
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        response_callback) {
-  std::move(response_callback)
-      .Run(scoped_refptr<net::URLRequestContextGetter>(nullptr));
-}
-
 class TestingLocationArbitrator : public LocationArbitrator {
  public:
   TestingLocationArbitrator(
       const LocationProviderUpdateCallback& callback,
       const CustomLocationProviderCallback& provider_getter,
-      GeolocationProvider::RequestContextProducer request_context_producer)
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
       : LocationArbitrator(provider_getter,
-                           request_context_producer,
+                           std::move(url_loader_factory),
                            std::string() /* api_key */),
         cell_(nullptr),
         gps_(nullptr) {
@@ -117,7 +96,7 @@
   base::Time GetTimeNow() const override { return GetTimeNowForTest(); }
 
   std::unique_ptr<LocationProvider> NewNetworkLocationProvider(
-      scoped_refptr<net::URLRequestContextGetter> context,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& api_key) override {
     cell_ = new FakeLocationProvider;
     return base::WrapUnique(cell_);
@@ -140,17 +119,20 @@
 
 class GeolocationLocationArbitratorTest : public testing::Test {
  protected:
-  GeolocationLocationArbitratorTest() : observer_(new MockLocationObserver) {}
+  GeolocationLocationArbitratorTest()
+      : observer_(new MockLocationObserver),
+        url_loader_factory_(new network::TestSharedURLLoaderFactory()) {}
 
-  // Initializes |arbitrator_| with the specified |provider|, which may be null.
+  // Initializes |arbitrator_| with the specified |url_loader_factory|, which
+  // may be null.
   void InitializeArbitrator(
       const CustomLocationProviderCallback& provider_getter,
-      GeolocationProvider::RequestContextProducer request_context_producer) {
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
     const LocationProvider::LocationProviderUpdateCallback callback =
-        base::Bind(&MockLocationObserver::OnLocationUpdate,
-                   base::Unretained(observer_.get()));
-    arbitrator_.reset(new TestingLocationArbitrator(callback, provider_getter,
-                                                    request_context_producer));
+        base::BindRepeating(&MockLocationObserver::OnLocationUpdate,
+                            base::Unretained(observer_.get()));
+    arbitrator_.reset(new TestingLocationArbitrator(
+        callback, provider_getter, std::move(url_loader_factory)));
   }
 
   // testing::Test
@@ -179,12 +161,13 @@
   const std::unique_ptr<MockLocationObserver> observer_;
   std::unique_ptr<TestingLocationArbitrator> arbitrator_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 };
 
 // Basic test of the text fixture.
 TEST_F(GeolocationLocationArbitratorTest, CreateDestroy) {
-  InitializeArbitrator(base::Bind(&GetCustomLocationProviderForTest, nullptr),
-                       base::Bind(&NullRequestContextProducer));
+  InitializeArbitrator(
+      base::BindRepeating(&GetCustomLocationProviderForTest, nullptr), nullptr);
   EXPECT_TRUE(arbitrator_);
   arbitrator_.reset();
   SUCCEED();
@@ -192,8 +175,8 @@
 
 // Tests OnPermissionGranted().
 TEST_F(GeolocationLocationArbitratorTest, OnPermissionGranted) {
-  InitializeArbitrator(base::Bind(&GetCustomLocationProviderForTest, nullptr),
-                       base::Bind(&NullRequestContextProducer));
+  InitializeArbitrator(
+      base::BindRepeating(&GetCustomLocationProviderForTest, nullptr), nullptr);
   EXPECT_FALSE(arbitrator_->HasPermissionBeenGrantedForTest());
   arbitrator_->OnPermissionGranted();
   EXPECT_TRUE(arbitrator_->HasPermissionBeenGrantedForTest());
@@ -207,9 +190,8 @@
 // providers and system location provider.
 TEST_F(GeolocationLocationArbitratorTest, NormalUsage) {
   InitializeArbitrator(
-      base::Bind(&GetCustomLocationProviderForTest, nullptr),
-      base::Bind(&TestRequestContextProducer,
-                 scoped_task_environment_.GetMainThreadTaskRunner()));
+      base::BindRepeating(&GetCustomLocationProviderForTest, nullptr),
+      url_loader_factory_);
   ASSERT_TRUE(arbitrator_);
 
   EXPECT_FALSE(cell());
@@ -244,9 +226,9 @@
 TEST_F(GeolocationLocationArbitratorTest, CustomSystemProviderOnly) {
   auto provider = std::make_unique<FakeLocationProvider>();
   FakeLocationProvider* fake_location_provider = provider.get();
-  InitializeArbitrator(
-      base::Bind(&GetCustomLocationProviderForTest, base::Passed(&provider)),
-      base::Bind(&NullRequestContextProducer));
+  InitializeArbitrator(base::BindRepeating(&GetCustomLocationProviderForTest,
+                                           base::Passed(&provider)),
+                       nullptr);
   ASSERT_TRUE(arbitrator_);
 
   EXPECT_FALSE(cell());
@@ -281,10 +263,9 @@
        CustomSystemAndDefaultNetworkProviders) {
   auto provider = std::make_unique<FakeLocationProvider>();
   FakeLocationProvider* fake_location_provider = provider.get();
-  InitializeArbitrator(
-      base::Bind(&GetCustomLocationProviderForTest, base::Passed(&provider)),
-      base::Bind(&TestRequestContextProducer,
-                 scoped_task_environment_.GetMainThreadTaskRunner()));
+  InitializeArbitrator(base::BindRepeating(&GetCustomLocationProviderForTest,
+                                           base::Passed(&provider)),
+                       url_loader_factory_);
   ASSERT_TRUE(arbitrator_);
 
   EXPECT_FALSE(cell());
@@ -318,9 +299,8 @@
 // observer.
 TEST_F(GeolocationLocationArbitratorTest, SetObserverOptions) {
   InitializeArbitrator(
-      base::Bind(&GetCustomLocationProviderForTest, nullptr),
-      base::Bind(&TestRequestContextProducer,
-                 scoped_task_environment_.GetMainThreadTaskRunner()));
+      base::BindRepeating(&GetCustomLocationProviderForTest, nullptr),
+      url_loader_factory_);
   arbitrator_->StartProvider(false);
   ASSERT_TRUE(cell());
   ASSERT_TRUE(gps());
@@ -338,9 +318,8 @@
 // multiple sources, with varying accuracy, across a period of time.
 TEST_F(GeolocationLocationArbitratorTest, Arbitration) {
   InitializeArbitrator(
-      base::Bind(&GetCustomLocationProviderForTest, nullptr),
-      base::Bind(&TestRequestContextProducer,
-                 scoped_task_environment_.GetMainThreadTaskRunner()));
+      base::BindRepeating(&GetCustomLocationProviderForTest, nullptr),
+      url_loader_factory_);
   arbitrator_->StartProvider(false);
   ASSERT_TRUE(cell());
   ASSERT_TRUE(gps());
@@ -419,9 +398,8 @@
 // it has stopped and then restarted (crbug.com/240956).
 TEST_F(GeolocationLocationArbitratorTest, TwoOneShotsIsNewPositionBetter) {
   InitializeArbitrator(
-      base::Bind(&GetCustomLocationProviderForTest, nullptr),
-      base::Bind(&TestRequestContextProducer,
-                 scoped_task_environment_.GetMainThreadTaskRunner()));
+      base::BindRepeating(&GetCustomLocationProviderForTest, nullptr),
+      url_loader_factory_);
   arbitrator_->StartProvider(false);
   ASSERT_TRUE(cell());
   ASSERT_TRUE(gps());
diff --git a/services/device/geolocation/network_location_provider.cc b/services/device/geolocation/network_location_provider.cc
index 5b12f45..c2f1ccd 100644
--- a/services/device/geolocation/network_location_provider.cc
+++ b/services/device/geolocation/network_location_provider.cc
@@ -13,8 +13,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_request_context_getter.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace device {
 namespace {
@@ -101,7 +101,7 @@
 
 // NetworkLocationProvider
 NetworkLocationProvider::NetworkLocationProvider(
-    scoped_refptr<net::URLRequestContextGetter> url_context_getter,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& api_key,
     LastPositionCache* last_position_cache)
     : wifi_data_provider_manager_(nullptr),
@@ -113,7 +113,7 @@
       is_permission_granted_(false),
       is_new_data_available_(false),
       request_(new NetworkLocationRequest(
-          std::move(url_context_getter),
+          std::move(url_loader_factory),
           api_key,
           base::Bind(&NetworkLocationProvider::OnLocationResponse,
                      base::Unretained(this)))),
diff --git a/services/device/geolocation/network_location_provider.h b/services/device/geolocation/network_location_provider.h
index e14937b2..75878bb 100644
--- a/services/device/geolocation/network_location_provider.h
+++ b/services/device/geolocation/network_location_provider.h
@@ -73,9 +73,10 @@
     CacheAgeList cache_age_list_;  // Oldest first.
   };
 
-  NetworkLocationProvider(scoped_refptr<net::URLRequestContextGetter> context,
-                          const std::string& api_key,
-                          LastPositionCache* last_position_cache);
+  NetworkLocationProvider(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& api_key,
+      LastPositionCache* last_position_cache);
   ~NetworkLocationProvider() override;
 
   // LocationProvider implementation
diff --git a/services/device/geolocation/network_location_provider_unittest.cc b/services/device/geolocation/network_location_provider_unittest.cc
index 39124171..139b36da 100644
--- a/services/device/geolocation/network_location_provider_unittest.cc
+++ b/services/device/geolocation/network_location_provider_unittest.cc
@@ -21,11 +21,12 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_status.h"
 #include "services/device/geolocation/location_arbitrator.h"
 #include "services/device/geolocation/wifi_data_provider.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace device {
@@ -131,10 +132,8 @@
 
   LocationProvider* CreateProvider(bool set_permission_granted,
                                    const std::string& api_key = std::string()) {
-    // No URLContextGetter needed: The request within the provider is tested
-    // directly using TestURLFetcherFactory.
     LocationProvider* provider = new NetworkLocationProvider(
-        nullptr, api_key, last_position_cache_.get());
+        shared_url_loader_factory_, api_key, last_position_cache_.get());
     if (set_permission_granted)
       provider->OnPermissionGranted();
     return provider;
@@ -142,7 +141,10 @@
 
  protected:
   GeolocationNetworkProviderTest()
-      : wifi_data_provider_(MockWifiDataProvider::CreateInstance()),
+      : shared_url_loader_factory_(
+            new network::WeakWrapperSharedURLLoaderFactory(
+                &test_url_loader_factory_)),
+        wifi_data_provider_(MockWifiDataProvider::CreateInstance()),
         last_position_cache_(std::make_unique<TestLastPositionCache>()) {
     // TODO(joth): Really these should be in SetUp, not here, but they take no
     // effect on Mac OS Release builds if done there. I kid not. Figure out why.
@@ -150,14 +152,8 @@
         MockWifiDataProvider::GetInstance);
   }
 
-  // Returns the current url fetcher (if any) and advances the id ready for the
-  // next test step.
-  net::TestURLFetcher* get_url_fetcher_and_advance_id() {
-    net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(
-        NetworkLocationRequest::url_fetcher_id_for_tests);
-    if (fetcher)
-      ++NetworkLocationRequest::url_fetcher_id_for_tests;
-    return fetcher;
+  ~GeolocationNetworkProviderTest() override {
+    shared_url_loader_factory_->Detach();
   }
 
   static int IndexToChannel(int index) { return index + 4; }
@@ -244,15 +240,18 @@
     return testing::AssertionSuccess();
   }
 
-  // Checks that |request| contains valid JSON upload data. The Wifi access
-  // points specified in the JSON are validated against the first
+  // Checks that current pending request contains valid JSON upload data. The
+  // WiFi access points specified in the JSON are validated against the first
   // |expected_wifi_aps| access points, starting from position
   // |wifi_start_index|, that are generated by CreateReferenceWifiScanDataJson.
-  void CheckRequestIsValid(const net::TestURLFetcher& request,
-                           int expected_wifi_aps,
-                           int wifi_start_index) {
-    const std::string& upload_data = request.upload_data();
+  void CheckRequestIsValid(int expected_wifi_aps, int wifi_start_index) {
+    ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+    const network::TestURLLoaderFactory::PendingRequest& pending_request =
+        test_url_loader_factory_.pending_requests()->back();
+    EXPECT_FALSE(pending_request.client.encountered_error());
+    std::string upload_data = network::GetUploadData(pending_request.request);
     ASSERT_FALSE(upload_data.empty());
+
     std::string json_parse_error_msg;
     std::unique_ptr<base::Value> parsed_json =
         base::JSONReader::ReadAndReturnError(upload_data, base::JSON_PARSE_RFC,
@@ -291,7 +290,9 @@
   }
 
   const base::MessageLoop main_message_loop_;
-  const net::TestURLFetcherFactory url_fetcher_factory_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  const scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
+      shared_url_loader_factory_;
   const scoped_refptr<MockWifiDataProvider> wifi_data_provider_;
   std::unique_ptr<NetworkLocationProvider::LastPositionCache>
       last_position_cache_;
@@ -312,9 +313,11 @@
   const std::string api_key = "";
   std::unique_ptr<LocationProvider> provider(CreateProvider(true, api_key));
   provider->StartProvider(false);
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
-  EXPECT_FALSE(fetcher->GetOriginalURL().has_query());
+
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+  const GURL& request_url =
+      test_url_loader_factory_.pending_requests()->back().request.url;
+  EXPECT_FALSE(request_url.has_query());
 }
 
 // Tests that, with non-empty api_key, a "key" query string parameter is
@@ -323,10 +326,12 @@
   const std::string api_key = "something";
   std::unique_ptr<LocationProvider> provider(CreateProvider(true, api_key));
   provider->StartProvider(false);
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
-  EXPECT_TRUE(fetcher->GetOriginalURL().has_query());
-  EXPECT_TRUE(fetcher->GetOriginalURL().query_piece().starts_with("key="));
+
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+  const GURL& request_url =
+      test_url_loader_factory_.pending_requests()->back().request.url;
+  EXPECT_TRUE(request_url.has_query());
+  EXPECT_TRUE(request_url.query_piece().starts_with("key="));
 }
 
 // Tests that, after StartProvider(), a TestURLFetcher can be extracted,
@@ -334,9 +339,8 @@
 TEST_F(GeolocationNetworkProviderTest, StartProvider) {
   std::unique_ptr<LocationProvider> provider(CreateProvider(true));
   provider->StartProvider(false);
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
-  CheckRequestIsValid(*fetcher, 0, 0);
+
+  CheckRequestIsValid(0, 0);
 }
 
 // Tests that, with a very large number of access points, the set of access
@@ -348,14 +352,16 @@
   const int kFirstScanAps = 20;
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
   base::RunLoop().RunUntilIdle();
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
+
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+  const std::string& request_url =
+      test_url_loader_factory_.pending_requests()->back().request.url.spec();
   // The request url should have been shortened to less than 2048 characters
   // in length by not including access points with the lowest signal strength
   // in the request.
-  EXPECT_LT(fetcher->GetOriginalURL().spec().size(), size_t(2048));
+  EXPECT_LT(request_url.size(), size_t(2048));
   // Expect only 16 out of 20 original access points.
-  CheckRequestIsValid(*fetcher, 16, 4);
+  CheckRequestIsValid(16, 4);
 }
 
 // Tests that the provider issues the right requests, and provides the right
@@ -371,19 +377,18 @@
 TEST_F(GeolocationNetworkProviderTest, MultipleWifiScansComplete) {
   std::unique_ptr<LocationProvider> provider(CreateProvider(true));
   provider->StartProvider(false);
-
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
 
   // 1. Complete the network request with bad position fix.
+  const std::string& request_url_1 =
+      test_url_loader_factory_.pending_requests()->back().request.url.spec();
   const char* kNoFixNetworkResponse =
       "{"
       "  \"status\": \"ZERO_RESULTS\""
       "}";
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);  // OK
-  fetcher->SetResponseString(kNoFixNetworkResponse);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.AddResponse(request_url_1, kNoFixNetworkResponse);
+  base::RunLoop().RunUntilIdle();
+  test_url_loader_factory_.ClearResponses();
 
   mojom::Geoposition position = provider->GetPosition();
   EXPECT_FALSE(ValidateGeoposition(position));
@@ -392,12 +397,14 @@
   const int kFirstScanAps = 6;
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
   base::RunLoop().RunUntilIdle();
-  fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
+
   // The request should have the wifi data.
-  CheckRequestIsValid(*fetcher, kFirstScanAps, 0);
+  CheckRequestIsValid(kFirstScanAps, 0);
 
   // 3. Send a reply with good position fix.
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+  const std::string& request_url_2 =
+      test_url_loader_factory_.pending_requests()->back().request.url.spec();
   const char* kReferenceNetworkResponse =
       "{"
       "  \"accuracy\": 1200.4,"
@@ -406,10 +413,10 @@
       "    \"lng\": -0.1"
       "  }"
       "}";
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);  // OK
-  fetcher->SetResponseString(kReferenceNetworkResponse);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.AddResponse(request_url_2,
+                                       kReferenceNetworkResponse);
+  base::RunLoop().RunUntilIdle();
+  test_url_loader_factory_.ClearResponses();
 
   position = provider->GetPosition();
   EXPECT_EQ(51.0, position.latitude);
@@ -423,8 +430,7 @@
   const int kSecondScanAps = kFirstScanAps - 1;
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(kSecondScanAps));
   base::RunLoop().RunUntilIdle();
-  fetcher = get_url_fetcher_and_advance_id();
-  EXPECT_FALSE(fetcher);
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
 
   position = provider->GetPosition();
   EXPECT_EQ(51.0, position.latitude);
@@ -435,15 +441,16 @@
   const int kThirdScanAps = kFirstScanAps * 2 + 1;
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(kThirdScanAps));
   base::RunLoop().RunUntilIdle();
-  fetcher = get_url_fetcher_and_advance_id();
-  EXPECT_TRUE(fetcher);
-  CheckRequestIsValid(*fetcher, kThirdScanAps, 0);
+  CheckRequestIsValid(kThirdScanAps, 0);
 
   // 6. ...reply with a network error.
-  fetcher->set_status(net::URLRequestStatus::FromError(net::ERR_FAILED));
-  fetcher->set_response_code(200);  // should be ignored
-  fetcher->SetResponseString(std::string());
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+  const GURL& request_url_3 =
+      test_url_loader_factory_.pending_requests()->back().request.url;
+  test_url_loader_factory_.AddResponse(
+      request_url_3, network::ResourceResponseHead(), std::string(),
+      network::URLLoaderCompletionStatus(net::ERR_FAILED));
+  base::RunLoop().RunUntilIdle();
 
   // Error means we now no longer have a fix.
   position = provider->GetPosition();
@@ -452,7 +459,7 @@
   // 7. Wifi scan returns to original set: should be serviced from cache.
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(get_url_fetcher_and_advance_id());  // No new request created.
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
 
   position = provider->GetPosition();
   EXPECT_EQ(51.0, position.latitude);
@@ -471,14 +478,14 @@
   provider->SetUpdateCallback(listener.callback);
 
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(get_url_fetcher_and_advance_id())
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending())
       << "Network request should not be created right away on startup when "
          "wifi data has not yet arrived";
 
   // Now Wifi data becomes available.
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(1));
   base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(get_url_fetcher_and_advance_id());
+  EXPECT_EQ(1, test_url_loader_factory_.NumPending());
 }
 
 // Tests that, even if a request is already in flight, new wifi data results in
@@ -487,14 +494,12 @@
   // Send initial request with empty data
   std::unique_ptr<LocationProvider> provider(CreateProvider(true));
   provider->StartProvider(false);
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  EXPECT_TRUE(fetcher);
+  CheckRequestIsValid(0, 0);
 
   // Now wifi data arrives; new request should be sent.
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(4));
   base::RunLoop().RunUntilIdle();
-  fetcher = get_url_fetcher_and_advance_id();
-  EXPECT_TRUE(fetcher);
+  CheckRequestIsValid(4, 0);
 }
 
 // Tests that, if user geolocation permission hasn't been granted during
@@ -503,12 +508,10 @@
 TEST_F(GeolocationNetworkProviderTest, NetworkRequestDeferredForPermission) {
   std::unique_ptr<LocationProvider> provider(CreateProvider(false));
   provider->StartProvider(false);
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  EXPECT_FALSE(fetcher);
-  provider->OnPermissionGranted();
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
 
-  fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
+  provider->OnPermissionGranted();
+  EXPECT_EQ(1, test_url_loader_factory_.NumPending());
 }
 
 // Tests that, even if new Wifi data arrives, the provider doesn't initiate its
@@ -517,22 +520,15 @@
        NetworkRequestWithWifiDataDeferredForPermission) {
   std::unique_ptr<LocationProvider> provider(CreateProvider(false));
   provider->StartProvider(false);
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  EXPECT_FALSE(fetcher);
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
 
   static const int kScanCount = 4;
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(kScanCount));
   base::RunLoop().RunUntilIdle();
-
-  fetcher = get_url_fetcher_and_advance_id();
-  EXPECT_FALSE(fetcher);
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
 
   provider->OnPermissionGranted();
-
-  fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
-
-  CheckRequestIsValid(*fetcher, kScanCount, 0);
+  CheckRequestIsValid(kScanCount, 0);
 }
 
 // Tests that the provider's position cache correctly caches each item, and
@@ -580,12 +576,13 @@
   const int kFirstScanAps = 6;
   wifi_data_provider_->SetData(CreateReferenceWifiScanData(kFirstScanAps));
   base::RunLoop().RunUntilIdle();
-  net::TestURLFetcher* fetcher = get_url_fetcher_and_advance_id();
-  ASSERT_TRUE(fetcher);
   // The request should have the wifi data.
-  CheckRequestIsValid(*fetcher, kFirstScanAps, 0);
+  CheckRequestIsValid(kFirstScanAps, 0);
 
   // Send a reply with good position fix.
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+  const std::string& request_url =
+      test_url_loader_factory_.pending_requests()->back().request.url.spec();
   const char* kReferenceNetworkResponse =
       "{"
       "  \"accuracy\": 1200.4,"
@@ -594,10 +591,8 @@
       "    \"lng\": -0.1"
       "  }"
       "}";
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);  // OK
-  fetcher->SetResponseString(kReferenceNetworkResponse);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.AddResponse(request_url, kReferenceNetworkResponse);
+  base::RunLoop().RunUntilIdle();
 
   // The provider should return the position as the current best estimate.
   position = provider->GetPosition();
@@ -747,7 +742,7 @@
   EXPECT_FALSE(ValidateGeoposition(listener.last_position));
 
   // Check that there is no pending network request.
-  EXPECT_FALSE(get_url_fetcher_and_advance_id());
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
 
   // Simulate no new wifi data.
   wifi_data_provider_->set_got_data(false);
@@ -763,7 +758,7 @@
   EXPECT_FALSE(ValidateGeoposition(listener.last_position));
 
   // Check that a network request is pending.
-  EXPECT_TRUE(get_url_fetcher_and_advance_id());
+  EXPECT_EQ(1, test_url_loader_factory_.NumPending());
 }
 
 }  // namespace device
diff --git a/services/device/geolocation/network_location_request.cc b/services/device/geolocation/network_location_request.cc
index deff99ce..c5cd5bd 100644
--- a/services/device/geolocation/network_location_request.cc
+++ b/services/device/geolocation/network_location_request.cc
@@ -23,11 +23,10 @@
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_request_context_getter.h"
-#include "net/url_request/url_request_status.h"
 #include "services/device/geolocation/location_arbitrator.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 
 namespace device {
 namespace {
@@ -87,9 +86,9 @@
 
 // Attempts to extract a position from the response. Detects and indicates
 // various failure cases.
-void GetLocationFromResponse(bool http_post_result,
+void GetLocationFromResponse(int net_error,
                              int status_code,
-                             const std::string& response_body,
+                             std::unique_ptr<std::string> response_body,
                              const base::Time& wifi_timestamp,
                              const GURL& server_url,
                              mojom::Geoposition* position);
@@ -105,15 +104,13 @@
                  base::DictionaryValue* request);
 }  // namespace
 
-int NetworkLocationRequest::url_fetcher_id_for_tests = 0;
-
 NetworkLocationRequest::NetworkLocationRequest(
-    scoped_refptr<net::URLRequestContextGetter> context,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& api_key,
     LocationResponseCallback callback)
-    : url_context_(std::move(context)),
+    : url_loader_factory_(std::move(url_loader_factory)),
       api_key_(api_key),
-      location_response_callback_(callback) {}
+      location_response_callback_(std::move(callback)) {}
 
 NetworkLocationRequest::~NetworkLocationRequest() = default;
 
@@ -123,10 +120,10 @@
     const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) {
   RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_START);
   RecordUmaAccessPoints(wifi_data.access_point_data.size());
-  if (url_fetcher_ != NULL) {
+  if (url_loader_) {
     DVLOG(1) << "NetworkLocationRequest : Cancelling pending request";
     RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_REQUEST_CANCEL);
-    url_fetcher_.reset();
+    url_loader_.reset();
   }
   wifi_data_ = wifi_data;
   wifi_timestamp_ = wifi_timestamp;
@@ -147,41 +144,48 @@
         policy {
           cookies_allowed: NO
       })");
-  const GURL request_url = FormRequestURL(api_key_);
-  DCHECK(request_url.is_valid());
-  url_fetcher_ =
-      net::URLFetcher::Create(url_fetcher_id_for_tests, request_url,
-                              net::URLFetcher::POST, this, traffic_annotation);
-  url_fetcher_->SetRequestContext(url_context_.get());
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->method = "POST";
+  resource_request->url = FormRequestURL(api_key_);
+  DCHECK(resource_request->url.is_valid());
+  resource_request->load_flags =
+      net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
+      net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
+      net::LOAD_DO_NOT_SEND_AUTH_DATA;
+
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 traffic_annotation);
+
   std::string upload_data;
   FormUploadData(wifi_data, wifi_timestamp, &upload_data);
-  url_fetcher_->SetUploadData("application/json", upload_data);
-  url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE |
-                             net::LOAD_DO_NOT_SAVE_COOKIES |
-                             net::LOAD_DO_NOT_SEND_COOKIES |
-                             net::LOAD_DO_NOT_SEND_AUTH_DATA);
+  url_loader_->AttachStringForUpload(upload_data, "application/json");
 
   request_start_time_ = base::TimeTicks::Now();
-  url_fetcher_->Start();
+  url_loader_->DownloadToString(
+      url_loader_factory_.get(),
+      base::BindOnce(&NetworkLocationRequest::OnRequestComplete,
+                     base::Unretained(this)),
+      1024 * 1024 /* 1 MiB */);
   return true;
 }
 
-void NetworkLocationRequest::OnURLFetchComplete(const net::URLFetcher* source) {
-  DCHECK_EQ(url_fetcher_.get(), source);
+void NetworkLocationRequest::OnRequestComplete(
+    std::unique_ptr<std::string> data) {
+  int net_error = url_loader_->NetError();
 
-  net::URLRequestStatus status = source->GetStatus();
-  int response_code = source->GetResponseCode();
+  int response_code = 0;
+  if (url_loader_->ResponseInfo())
+    response_code = url_loader_->ResponseInfo()->headers->response_code();
   RecordUmaResponseCode(response_code);
 
   mojom::Geoposition position;
-  std::string data;
-  source->GetResponseAsString(&data);
-  GetLocationFromResponse(status.is_success(), response_code, data,
-                          wifi_timestamp_, source->GetURL(), &position);
-  const bool server_error =
-      !status.is_success() || (response_code >= 500 && response_code < 600);
-  url_fetcher_.reset();
+  GetLocationFromResponse(net_error, response_code, std::move(data),
+                          wifi_timestamp_, url_loader_->GetFinalURL(),
+                          &position);
 
+  bool server_error =
+      net_error != net::OK || (response_code >= 500 && response_code < 600);
   if (!server_error) {
     const base::TimeDelta request_time =
         base::TimeTicks::Now() - request_start_time_;
@@ -191,6 +195,8 @@
                                base::TimeDelta::FromSeconds(10), 100);
   }
 
+  url_loader_.reset();
+
   DVLOG(1) << "NetworkLocationRequest::OnURLFetchComplete() : run callback.";
   location_response_callback_.Run(position, server_error, wifi_data_);
 }
@@ -294,9 +300,9 @@
           << position->error_message;
 }
 
-void GetLocationFromResponse(bool http_post_result,
+void GetLocationFromResponse(int net_error,
                              int status_code,
-                             const std::string& response_body,
+                             std::unique_ptr<std::string> response_body,
                              const base::Time& wifi_timestamp,
                              const GURL& server_url,
                              mojom::Geoposition* position) {
@@ -304,11 +310,12 @@
 
   // HttpPost can fail for a number of reasons. Most likely this is because
   // we're offline, or there was no response.
-  if (!http_post_result) {
+  if (net_error != net::OK) {
     FormatPositionError(server_url, "No response received", position);
     RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_EMPTY);
     return;
   }
+
   if (status_code != 200) {  // HTTP OK.
     std::string message = "Returned error code ";
     message += base::IntToString(status_code);
@@ -316,14 +323,17 @@
     RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_NOT_OK);
     return;
   }
+
   // We use the timestamp from the wifi data that was used to generate
   // this position fix.
-  if (!ParseServerResponse(response_body, wifi_timestamp, position)) {
+  DCHECK(response_body);
+  if (!ParseServerResponse(*response_body, wifi_timestamp, position)) {
     // We failed to parse the repsonse.
     FormatPositionError(server_url, "Response was malformed", position);
     RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_MALFORMED);
     return;
   }
+
   // The response was successfully parsed, but it may not be a valid
   // position fix.
   if (!ValidateGeoposition(*position)) {
@@ -332,6 +342,7 @@
     RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_INVALID_FIX);
     return;
   }
+
   RecordUmaEvent(NETWORK_LOCATION_REQUEST_EVENT_RESPONSE_SUCCESS);
 }
 
diff --git a/services/device/geolocation/network_location_request.h b/services/device/geolocation/network_location_request.h
index c544960..885abf39 100644
--- a/services/device/geolocation/network_location_request.h
+++ b/services/device/geolocation/network_location_request.h
@@ -11,39 +11,38 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string16.h"
-#include "net/url_request/url_fetcher_delegate.h"
 #include "services/device/geolocation/wifi_data_provider.h"
 #include "services/device/public/mojom/geoposition.mojom.h"
 #include "url/gurl.h"
 
 namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-
 struct PartialNetworkTrafficAnnotationTag;
 }  // namespace net
 
+namespace network {
+class SharedURLLoaderFactory;
+class SimpleURLLoader;
+}  // namespace network
+
 namespace device {
 
 // Takes wifi data and sends it to a server to get a position fix.
 // It performs formatting of the request and interpretation of the response.
-class NetworkLocationRequest : private net::URLFetcherDelegate {
+class NetworkLocationRequest {
  public:
-  // ID passed to URLFetcher::Create(). Used for testing.
-  static int url_fetcher_id_for_tests;
-
   // Called when a new geo position is available. The second argument indicates
   // whether there was a server error or not. It is true when there was a
   // server or network error - either no response or a 500 error code.
   using LocationResponseCallback =
-      base::Callback<void(const mojom::Geoposition& /* position */,
-                          bool /* server_error */,
-                          const WifiData& /* wifi_data */)>;
+      base::RepeatingCallback<void(const mojom::Geoposition& /* position */,
+                                   bool /* server_error */,
+                                   const WifiData& /* wifi_data */)>;
 
-  NetworkLocationRequest(scoped_refptr<net::URLRequestContextGetter> context,
-                         const std::string& api_key,
-                         LocationResponseCallback callback);
-  ~NetworkLocationRequest() override;
+  NetworkLocationRequest(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      const std::string& api_key,
+      LocationResponseCallback callback);
+  ~NetworkLocationRequest();
 
   // Makes a new request using the specified |wifi_data|. Returns true if the
   // new request was successfully started. In all cases, any currently pending
@@ -56,16 +55,15 @@
                    const net::PartialNetworkTrafficAnnotationTag&
                        partial_traffic_annotation);
 
-  bool is_request_pending() const { return url_fetcher_ != NULL; }
+  bool is_request_pending() const { return bool(url_loader_); }
 
  private:
-  // net::URLFetcherDelegate
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
+  void OnRequestComplete(std::unique_ptr<std::string> data);
 
-  const scoped_refptr<net::URLRequestContextGetter> url_context_;
+  const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   const std::string api_key_;
   const LocationResponseCallback location_response_callback_;
-  std::unique_ptr<net::URLFetcher> url_fetcher_;
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
 
   // Keep a copy of the data sent in the request, so we can refer back to it
   // when the response arrives.
diff --git a/services/device/geolocation/public_ip_address_geolocation_provider.cc b/services/device/geolocation/public_ip_address_geolocation_provider.cc
index 207556e..2ad162e 100644
--- a/services/device/geolocation/public_ip_address_geolocation_provider.cc
+++ b/services/device/geolocation/public_ip_address_geolocation_provider.cc
@@ -5,18 +5,19 @@
 #include "services/device/geolocation/public_ip_address_geolocation_provider.h"
 
 #include "services/device/geolocation/public_ip_address_geolocator.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace device {
 
 PublicIpAddressGeolocationProvider::PublicIpAddressGeolocationProvider(
-    GeolocationProvider::RequestContextProducer request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& api_key) {
   // Bind sequence_checker_ to the initialization sequence.
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   public_ip_address_location_notifier_ =
       std::make_unique<PublicIpAddressLocationNotifier>(
-          request_context_producer, api_key);
+          std::move(url_loader_factory), api_key);
 }
 
 PublicIpAddressGeolocationProvider::~PublicIpAddressGeolocationProvider() {}
diff --git a/services/device/geolocation/public_ip_address_geolocation_provider.h b/services/device/geolocation/public_ip_address_geolocation_provider.h
index 8888c95..06dd4fa6 100644
--- a/services/device/geolocation/public_ip_address_geolocation_provider.h
+++ b/services/device/geolocation/public_ip_address_geolocation_provider.h
@@ -29,10 +29,9 @@
     : public mojom::PublicIpAddressGeolocationProvider {
  public:
   // Initialize PublicIpAddressGeolocationProvider using the specified Google
-  // |api_key| and a URL request context produced by |request_context_producer|
-  // for network location requests.
+  // |api_key| and |url_loader_factory| for network location requests.
   PublicIpAddressGeolocationProvider(
-      GeolocationProvider::RequestContextProducer request_context_producer,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& api_key);
   ~PublicIpAddressGeolocationProvider() override;
 
diff --git a/services/device/geolocation/public_ip_address_geolocator_unittest.cc b/services/device/geolocation/public_ip_address_geolocator_unittest.cc
index 2449938..795a3325 100644
--- a/services/device/geolocation/public_ip_address_geolocator_unittest.cc
+++ b/services/device/geolocation/public_ip_address_geolocator_unittest.cc
@@ -5,11 +5,13 @@
 #include "services/device/geolocation/public_ip_address_geolocator.h"
 
 #include "base/run_loop.h"
+#include "base/strings/string_util.h"
 #include "base/test/scoped_task_environment.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/public/cpp/bindings/strong_binding_set.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -18,29 +20,18 @@
 
 const char kTestGeolocationApiKey[] = "";
 
-// Simple request context producer that immediately produces a
-// TestURLRequestContextGetter.
-void TestRequestContextProducer(
-    const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        response_callback) {
-  std::move(response_callback)
-      .Run(base::MakeRefCounted<net::TestURLRequestContextGetter>(
-          network_task_runner));
-}
-
 class PublicIpAddressGeolocatorTest : public testing::Test {
  public:
   PublicIpAddressGeolocatorTest()
       : scoped_task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::IO) {
     notifier_.reset(new PublicIpAddressLocationNotifier(
-        base::Bind(&TestRequestContextProducer,
-                   scoped_task_environment_.GetMainThreadTaskRunner()),
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_),
         kTestGeolocationApiKey));
   }
 
-  ~PublicIpAddressGeolocatorTest() override = default;
+  ~PublicIpAddressGeolocatorTest() override {}
 
  protected:
   void SetUp() override {
@@ -107,73 +98,33 @@
   // The object under test.
   mojom::GeolocationPtr public_ip_address_geolocator_;
 
-  // PublicIpAddressGeolocator implementation.
-  // std::unique_ptr<PublicIpAddressGeolocator> impl_;
+  // Test URLLoaderFactory for handling requests to the geolocation API.
+  network::TestURLLoaderFactory test_url_loader_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PublicIpAddressGeolocatorTest);
 };
 
-// Observer that waits until a TestURLFetcher with the specified fetcher_id
-// starts, after which it is made available through .fetcher().
-class TestURLFetcherObserver : public net::TestURLFetcher::DelegateForTests {
- public:
-  explicit TestURLFetcherObserver(int expected_fetcher_id)
-      : expected_fetcher_id_(expected_fetcher_id) {
-    factory_.SetDelegateForTests(this);
-  }
-  virtual ~TestURLFetcherObserver() {}
-
-  void Wait() { loop_.Run(); }
-
-  net::TestURLFetcher* fetcher() { return fetcher_; }
-
-  // net::TestURLFetcher::DelegateForTests:
-  void OnRequestStart(int fetcher_id) override {
-    if (fetcher_id == expected_fetcher_id_) {
-      fetcher_ = factory_.GetFetcherByID(fetcher_id);
-      fetcher_->SetDelegateForTests(nullptr);
-      factory_.SetDelegateForTests(nullptr);
-      loop_.Quit();
-    }
-  }
-  void OnChunkUpload(int fetcher_id) override {}
-  void OnRequestEnd(int fetcher_id) override {}
-
- private:
-  const int expected_fetcher_id_;
-  net::TestURLFetcher* fetcher_ = nullptr;
-  net::TestURLFetcherFactory factory_;
-  base::RunLoop loop_;
-};
-
 // Basic test of a client invoking QueryNextPosition.
 TEST_F(PublicIpAddressGeolocatorTest, BindAndQuery) {
-  // Intercept the URLFetcher from network geolocation request.
-  TestURLFetcherObserver observer(
-      device::NetworkLocationRequest::url_fetcher_id_for_tests);
-
-  // Invoke QueryNextPosition and wait for a URLFetcher.
+  // Invoke QueryNextPosition.
   base::RunLoop loop;
   QueryNextPosition(loop.QuitClosure());
-  observer.Wait();
-  DCHECK(observer.fetcher());
 
-  // Issue a valid response to the URLFetcher.
-  observer.fetcher()->set_url(observer.fetcher()->GetOriginalURL());
-  observer.fetcher()->set_status(net::URLRequestStatus());
-  observer.fetcher()->set_response_code(200);
-  observer.fetcher()->SetResponseString(R"({
+  ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+  const std::string& request_url =
+      test_url_loader_factory_.pending_requests()->back().request.url.spec();
+  EXPECT_TRUE(
+      base::StartsWith("https://www.googleapis.com/geolocation/v1/geolocate",
+                       request_url, base::CompareCase::SENSITIVE));
+
+  // Issue a valid response.
+  test_url_loader_factory_.AddResponse(request_url, R"({
         "accuracy": 100.0,
         "location": {
           "lat": 10.0,
           "lng": 20.0
         }
-      })");
-  scoped_task_environment_.GetMainThreadTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&net::URLFetcherDelegate::OnURLFetchComplete,
-                     base::Unretained(observer.fetcher()->delegate()),
-                     base::Unretained(observer.fetcher())));
+      })", net::HTTP_OK);
 
   // Wait for QueryNextPosition to return.
   loop.Run();
diff --git a/services/device/geolocation/public_ip_address_location_notifier.cc b/services/device/geolocation/public_ip_address_location_notifier.cc
index 906bf98..207a08f8 100644
--- a/services/device/geolocation/public_ip_address_location_notifier.cc
+++ b/services/device/geolocation/public_ip_address_location_notifier.cc
@@ -6,6 +6,7 @@
 
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/device/geolocation/wifi_data.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace device {
 
@@ -17,11 +18,11 @@
 }  // namespace
 
 PublicIpAddressLocationNotifier::PublicIpAddressLocationNotifier(
-    GeolocationProvider::RequestContextProducer request_context_producer,
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& api_key)
     : network_changed_since_last_request_(true),
       api_key_(api_key),
-      request_context_producer_(request_context_producer),
+      url_loader_factory_(url_loader_factory),
       network_traffic_annotation_tag_(nullptr),
       weak_ptr_factory_(this) {
   // Subscribe to notifications of changes in network configuration.
@@ -98,21 +99,11 @@
 void PublicIpAddressLocationNotifier::MakeNetworkLocationRequest() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   network_changed_since_last_request_ = false;
-  // Obtain URL request context using provided producer callback, then continue
-  // request in MakeNetworkLocationRequestWithRequestContext.
-  request_context_producer_.Run(base::BindOnce(
-      &PublicIpAddressLocationNotifier::MakeNetworkLocationRequestWithContext,
-      weak_ptr_factory_.GetWeakPtr()));
-}
-
-void PublicIpAddressLocationNotifier::MakeNetworkLocationRequestWithContext(
-    scoped_refptr<net::URLRequestContextGetter> context_getter) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!context_getter)
+  if (!url_loader_factory_)
     return;
 
   network_location_request_ = std::make_unique<NetworkLocationRequest>(
-      std::move(context_getter), api_key_,
+      url_loader_factory_, api_key_,
       base::BindRepeating(
           &PublicIpAddressLocationNotifier::OnNetworkLocationResponse,
           weak_ptr_factory_.GetWeakPtr()));
diff --git a/services/device/geolocation/public_ip_address_location_notifier.h b/services/device/geolocation/public_ip_address_location_notifier.h
index 5272f50e..c304a61 100644
--- a/services/device/geolocation/public_ip_address_location_notifier.h
+++ b/services/device/geolocation/public_ip_address_location_notifier.h
@@ -31,11 +31,10 @@
 class PublicIpAddressLocationNotifier
     : public net::NetworkChangeNotifier::NetworkChangeObserver {
  public:
-  // Creates a notifier that uses the specified Google |api_key| and a URL
-  // request context produced by |request_context_producer| for network location
-  // requests.
+  // Creates a notifier that uses the specified Google |api_key| and
+  // |url_loader_factory| for network location requests.
   PublicIpAddressLocationNotifier(
-      GeolocationProvider::RequestContextProducer request_context_producer,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& api_key);
   ~PublicIpAddressLocationNotifier() override;
 
@@ -69,15 +68,9 @@
   // if any clients are waiting.
   void ReactToNetworkChange();
 
-  // Begins a network location request, by first obtaining a
-  // URLRequestContextGetter, then continuing in
-  // MakeNetworkLocationRequestWithContext.
-  void MakeNetworkLocationRequest();
-
-  // Creates network_location_request_ and starts the network request, which
+  // Creates |network_location_request_| and starts the network request, which
   // will invoke OnNetworkLocationResponse when done.
-  void MakeNetworkLocationRequestWithContext(
-      scoped_refptr<net::URLRequestContextGetter> context_getter);
+  void MakeNetworkLocationRequest();
 
   // Completion callback for network_location_request_.
   void OnNetworkLocationResponse(const mojom::Geoposition& position,
@@ -98,8 +91,8 @@
   // Google API key for network geolocation requests.
   const std::string api_key_;
 
-  // Callback to produce a URL request context for network geolocation requests.
-  const GeolocationProvider::RequestContextProducer request_context_producer_;
+  // SharedURLLoaderFactory for network geolocation requests.
+  const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   // Used to make calls to the Maps geolocate API.
   // Empty unless a call is currently in progress.
diff --git a/services/device/geolocation/public_ip_address_location_notifier_unittest.cc b/services/device/geolocation/public_ip_address_location_notifier_unittest.cc
index a4b8ce0..12bb806c 100644
--- a/services/device/geolocation/public_ip_address_location_notifier_unittest.cc
+++ b/services/device/geolocation/public_ip_address_location_notifier_unittest.cc
@@ -9,27 +9,15 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "net/url_request/url_request_test_util.h"
+#include "services/device/device_service_test_base.h"
 #include "services/device/public/cpp/geolocation/geoposition.h"
 #include "services/device/public/mojom/geoposition.mojom.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace device {
-namespace {
-// Simple request context producer that immediately produces a
-// TestURLRequestContextGetter.
-void TestRequestContextProducer(
-    const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner,
-    base::OnceCallback<void(scoped_refptr<net::URLRequestContextGetter>)>
-        response_callback) {
-  std::move(response_callback)
-      .Run(base::MakeRefCounted<net::TestURLRequestContextGetter>(
-          network_task_runner));
-}
-
-}  // namespace
 
 class PublicIpAddressLocationNotifierTest : public testing::Test {
  protected:
@@ -64,31 +52,28 @@
       : mock_time_task_runner_(
             base::MakeRefCounted<base::TestMockTimeTaskRunner>(
                 base::TestMockTimeTaskRunner::Type::kBoundToThread)),
-        mock_time_scoped_context_(mock_time_task_runner_.get()),
         network_change_notifier_(net::NetworkChangeNotifier::CreateMock()),
         notifier_(
-            base::Bind(&TestRequestContextProducer, mock_time_task_runner_),
-            std::string() /* api_key */) {}
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_),
+            kTestGeolocationApiKey) {}
 
-  // Returns the current TestURLFetcher (if any) and advances the test fetcher
-  // id for disambiguation from subsequent tests.
-  net::TestURLFetcher* GetTestUrlFetcher() {
-    net::TestURLFetcher* const fetcher = url_fetcher_factory_.GetFetcherByID(
-        NetworkLocationRequest::url_fetcher_id_for_tests);
-    if (fetcher)
-      ++NetworkLocationRequest::url_fetcher_id_for_tests;
-    return fetcher;
-  }
+  ~PublicIpAddressLocationNotifierTest() override {}
 
   // Gives a valid JSON reponse to the specified URLFetcher.
   // For disambiguation purposes, the specified |latitude| is included in the
   // response.
-  void RespondToFetchWithLatitude(net::TestURLFetcher* const fetcher,
-                                  const float latitude) {
+  void RespondToFetchWithLatitude(const float latitude) {
+    ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+    const std::string& request_url =
+        test_url_loader_factory_.pending_requests()->back().request.url.spec();
+    std::string expected_url =
+        "https://www.googleapis.com/geolocation/v1/"
+        "geolocate?key=";
+    expected_url.append(kTestGeolocationApiKey);
+    EXPECT_EQ(expected_url, request_url);
+
     // Issue a valid response including the specified latitude.
-    fetcher->set_url(fetcher->GetOriginalURL());
-    fetcher->set_status(net::URLRequestStatus());
-    fetcher->set_response_code(200);
     const char kNetworkResponseFormatString[] =
         R"({
             "accuracy": 100.0,
@@ -97,16 +82,28 @@
               "lng": 90.0
             }
           })";
-    fetcher->SetResponseString(
-        base::StringPrintf(kNetworkResponseFormatString, latitude));
-    fetcher->delegate()->OnURLFetchComplete(fetcher);
+    std::string body =
+        base::StringPrintf(kNetworkResponseFormatString, latitude);
+    test_url_loader_factory_.AddResponse(request_url, body, net::HTTP_OK);
+    mock_time_task_runner_->RunUntilIdle();
+    test_url_loader_factory_.ClearResponses();
   }
 
-  void RespondToFetchWithServerError(net::TestURLFetcher* const fetcher) {
-    fetcher->set_url(fetcher->GetOriginalURL());
-    fetcher->set_status(net::URLRequestStatus());
-    fetcher->set_response_code(500);
-    fetcher->delegate()->OnURLFetchComplete(fetcher);
+  void RespondToFetchWithServerError() {
+    ASSERT_EQ(1, test_url_loader_factory_.NumPending());
+    const std::string& request_url =
+        test_url_loader_factory_.pending_requests()->back().request.url.spec();
+
+    std::string expected_url =
+        "https://www.googleapis.com/geolocation/v1/"
+        "geolocate?key=";
+    expected_url.append(kTestGeolocationApiKey);
+    EXPECT_EQ(expected_url, request_url);
+
+    test_url_loader_factory_.AddResponse(request_url, std::string(),
+                                         net::HTTP_INTERNAL_SERVER_ERROR);
+    mock_time_task_runner_->RunUntilIdle();
+    test_url_loader_factory_.ClearResponses();
   }
 
   // Expects a non-empty and valid Geoposition, including the specified
@@ -123,15 +120,15 @@
     EXPECT_THAT(position->error_code,
                 mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE);
   }
+
   // Use a TaskRunner on which we can fast-forward time.
   const scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner_;
-  const base::TestMockTimeTaskRunner::ScopedContext mock_time_scoped_context_;
 
   // notifier_ requires a NetworkChangeNotifier to exist.
   std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
 
-  // Intercept URL fetchers.
-  net::TestURLFetcherFactory url_fetcher_factory_;
+  // Test URLLoaderFactory for handling requests to the geolocation API.
+  network::TestURLLoaderFactory test_url_loader_factory_;
 
   // The object under test.
   PublicIpAddressLocationNotifier notifier_;
@@ -144,10 +141,10 @@
   notifier_.QueryNextPosition(base::Time::Now(),
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query.MakeCallback());
+
   // Expect a URL fetch & send a valid response.
-  net::TestURLFetcher* const fetcher = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher, 1.0f);
+  RespondToFetchWithLatitude(1.0f);
+
   // Expect the query to return.
   ExpectValidPosition(query.position(), 1.0f);
 }
@@ -160,9 +157,7 @@
   TestPositionQuery query_1;
   notifier_.QueryNextPosition(time, PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query_1.MakeCallback());
-  net::TestURLFetcher* const fetcher = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher, 1.0f);
+  RespondToFetchWithLatitude(1.0f);
   ExpectValidPosition(query_1.position(), 1.0f);
 
   // Second query for an earlier time.
@@ -171,7 +166,7 @@
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query_2.MakeCallback());
   // Expect a cached result, so no new network request.
-  EXPECT_THAT(GetTestUrlFetcher(), testing::IsNull());
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
   // Expect the same result as query_1.
   ExpectValidPosition(query_2.position(), 1.0f);
 }
@@ -185,9 +180,7 @@
   notifier_.QueryNextPosition(base::Time::Now(),
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query_1.MakeCallback());
-  net::TestURLFetcher* const fetcher = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher, 1.0f);
+  RespondToFetchWithLatitude(1.0f);
   ExpectValidPosition(query_1.position(), 1.0f);
 
   // Second query seeking a position newer than the result of query_1.
@@ -196,7 +189,7 @@
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query_2.MakeCallback());
   // Expect no network request or callback.
-  EXPECT_THAT(GetTestUrlFetcher(), testing::IsNull());
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
   EXPECT_FALSE(query_2.position().has_value());
 
   // Fake a network change notification.
@@ -206,9 +199,7 @@
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
 
   // Now expect a network request and query_2 to return.
-  net::TestURLFetcher* const fetcher_2 = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher_2, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher_2, 2.0f);
+  RespondToFetchWithLatitude(2.0f);
   ExpectValidPosition(query_2.position(), 2.0f);
 }
 
@@ -221,9 +212,7 @@
   notifier_.QueryNextPosition(base::Time::Now(),
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query_1.MakeCallback());
-  net::TestURLFetcher* const fetcher = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher, 1.0f);
+  RespondToFetchWithLatitude(1.0f);
   ExpectValidPosition(query_1.position(), 1.0f);
 
   // Second query seeking a position newer than the result of query_1.
@@ -232,7 +221,7 @@
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query_2.MakeCallback());
   // Expect no network request or callback since network has not changed.
-  EXPECT_THAT(GetTestUrlFetcher(), testing::IsNull());
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
   EXPECT_FALSE(query_2.position().has_value());
 
   // Fake several consecutive network changes notification.
@@ -242,16 +231,14 @@
     mock_time_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(5));
   }
   // Expect still no network request or callback.
-  EXPECT_THAT(GetTestUrlFetcher(), testing::IsNull());
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
   EXPECT_FALSE(query_2.position().has_value());
 
   // Wait longer.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
 
   // Now expect a network request & query_2 to return.
-  net::TestURLFetcher* const fetcher_2 = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher_2, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher_2, 2.0f);
+  RespondToFetchWithLatitude(2.0f);
   ExpectValidPosition(query_2.position(), 2.0f);
 }
 
@@ -262,9 +249,7 @@
   notifier_.QueryNextPosition(base::Time::Now(),
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query_1.MakeCallback());
-  net::TestURLFetcher* const fetcher = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher, 1.0f);
+  RespondToFetchWithLatitude(1.0f);
   ExpectValidPosition(query_1.position(), 1.0f);
 
   // Multiple queries seeking positions newer than the result of query_1.
@@ -278,7 +263,7 @@
                               query_3.MakeCallback());
 
   // Expect no network requests or callback since network has not changed.
-  EXPECT_THAT(GetTestUrlFetcher(), testing::IsNull());
+  EXPECT_EQ(0, test_url_loader_factory_.NumPending());
   EXPECT_FALSE(query_2.position().has_value());
   EXPECT_FALSE(query_3.position().has_value());
 
@@ -289,9 +274,7 @@
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
 
   // Now expect a network request & fake a valid response.
-  net::TestURLFetcher* const fetcher_2 = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher_2, testing::NotNull());
-  RespondToFetchWithLatitude(fetcher_2, 2.0f);
+  RespondToFetchWithLatitude(2.0f);
   // Expect all queries to now return.
   ExpectValidPosition(query_2.position(), 2.0f);
   ExpectValidPosition(query_3.position(), 2.0f);
@@ -305,9 +288,7 @@
                               PARTIAL_TRAFFIC_ANNOTATION_FOR_TESTS,
                               query.MakeCallback());
   // Expect a URL fetch & send a valid response.
-  net::TestURLFetcher* const fetcher = GetTestUrlFetcher();
-  EXPECT_THAT(fetcher, testing::NotNull());
-  RespondToFetchWithServerError(fetcher);
+  RespondToFetchWithServerError();
   // Expect the query to return.
   ExpectError(query.position());
 }
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 72fa406..3aaaab47 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -1784,6 +1784,7 @@
 
   GURL domain("https://google.com");
   logging_service->OnHeader(url::Origin::Create(domain),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
 
   ASSERT_EQ(1u, logging_service->GetPolicyOriginsForTesting().size());
@@ -1808,9 +1809,11 @@
 
   GURL domain1("https://google.com");
   logging_service->OnHeader(url::Origin::Create(domain1),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
   GURL domain2("https://chromium.org");
   logging_service->OnHeader(url::Origin::Create(domain2),
+                            net::IPAddress(192, 168, 0, 1),
                             "{\"report_to\":\"group\",\"max_age\":86400}");
 
   ASSERT_EQ(2u, logging_service->GetPolicyOriginsForTesting().size());
diff --git a/services/ui/gpu_host/gpu_host_unittest.cc b/services/ui/gpu_host/gpu_host_unittest.cc
index 87be309..9bc7d7a 100644
--- a/services/ui/gpu_host/gpu_host_unittest.cc
+++ b/services/ui/gpu_host/gpu_host_unittest.cc
@@ -56,6 +56,7 @@
                      gpu::GpuPreferences(),
                      base::nullopt,
                      base::nullopt,
+                     nullptr /* vulkan_implementation */,
                      /*exit_callback=*/base::DoNothing()) {}
 
 }  // namespace
diff --git a/storage/browser/database/database_tracker.h b/storage/browser/database/database_tracker.h
index 2daecebb..e8863de2 100644
--- a/storage/browser/database/database_tracker.h
+++ b/storage/browser/database/database_tracker.h
@@ -93,7 +93,7 @@
         const base::string16& database_name) = 0;
 
    protected:
-    virtual ~Observer() {}
+    virtual ~Observer() = default;
   };
 
   DatabaseTracker(const base::FilePath& profile_path,
@@ -121,7 +121,9 @@
 
   void CloseTrackerDatabaseAndClearCaches();
 
-  const base::FilePath& DatabaseDirectory() const { return db_dir_; }
+  // Thread-safe getter.
+  const base::FilePath& database_directory() const { return db_dir_; }
+
   base::FilePath GetFullDBFilePath(const std::string& origin_identifier,
                                    const base::string16& database_name);
 
@@ -130,7 +132,7 @@
   virtual bool GetAllOriginIdentifiers(std::vector<std::string>* origin_ids);
   virtual bool GetAllOriginsInfo(std::vector<OriginInfo>* origins_info);
 
-  // Safe to call on any thread.
+  // Thread-safe getter.
   storage::QuotaManagerProxy* quota_manager_proxy() const {
     return quota_manager_proxy_.get();
   }
@@ -281,7 +283,12 @@
   bool force_keep_session_state_ = false;
   bool shutting_down_ = false;
   const base::FilePath profile_path_;
+
+  // Can be accessed from any thread via database_directory().
+  //
+  // Thread-safety argument: The member is immutable.
   const base::FilePath db_dir_;
+
   std::unique_ptr<sql::Connection> db_;
   std::unique_ptr<DatabasesTable> databases_table_;
   std::unique_ptr<sql::MetaTable> meta_table_;
@@ -294,9 +301,12 @@
   PendingDeletionCallbacks deletion_callbacks_;
 
   // Apps and Extensions can have special rights.
-  scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
+  const scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy_;
 
-  scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
+  // Can be accessed from any thread via quota_manager_proxy().
+  //
+  // Thread-safety argument: The reference is immutable.
+  const scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy_;
 
   // The database tracker thread we're supposed to run file IO on.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/storage/browser/database/database_tracker_unittest.cc b/storage/browser/database/database_tracker_unittest.cc
index daed560..f213778c 100644
--- a/storage/browser/database/database_tracker_unittest.cc
+++ b/storage/browser/database/database_tracker_unittest.cc
@@ -218,10 +218,10 @@
     tracker->DatabaseOpened(kOrigin2, kDB3, kDescription, 0, &database_size);
 
     EXPECT_TRUE(base::CreateDirectory(
-        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+        tracker->database_directory().Append(base::FilePath::FromUTF16Unsafe(
             tracker->GetOriginDirectory(kOrigin1)))));
     EXPECT_TRUE(base::CreateDirectory(
-        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+        tracker->database_directory().Append(base::FilePath::FromUTF16Unsafe(
             tracker->GetOriginDirectory(kOrigin2)))));
     EXPECT_EQ(
         1, base::WriteFile(tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
@@ -247,12 +247,12 @@
     result = callback.GetResult(result);
     EXPECT_EQ(net::OK, result);
     EXPECT_FALSE(
-        base::PathExists(tracker->DatabaseDirectory().AppendASCII(kOrigin1)));
+        base::PathExists(tracker->database_directory().AppendASCII(kOrigin1)));
 
     // Recreate db1.
     tracker->DatabaseOpened(kOrigin1, kDB1, kDescription, 0, &database_size);
     EXPECT_TRUE(base::CreateDirectory(
-        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+        tracker->database_directory().Append(base::FilePath::FromUTF16Unsafe(
             tracker->GetOriginDirectory(kOrigin1)))));
     EXPECT_EQ(
         1, base::WriteFile(tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
@@ -281,7 +281,7 @@
     result = callback.GetResult(result);
     EXPECT_EQ(net::OK, result);
     EXPECT_FALSE(
-        base::PathExists(tracker->DatabaseDirectory().AppendASCII(kOrigin1)));
+        base::PathExists(tracker->database_directory().AppendASCII(kOrigin1)));
     EXPECT_TRUE(base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB2)));
     EXPECT_TRUE(base::PathExists(tracker->GetFullDBFilePath(kOrigin2, kDB3)));
 
@@ -339,10 +339,10 @@
     // Write some data to each file and check that the listeners are
     // called with the appropriate values.
     EXPECT_TRUE(base::CreateDirectory(
-        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+        tracker->database_directory().Append(base::FilePath::FromUTF16Unsafe(
             tracker->GetOriginDirectory(kOrigin1)))));
     EXPECT_TRUE(base::CreateDirectory(
-        tracker->DatabaseDirectory().Append(base::FilePath::FromUTF16Unsafe(
+        tracker->database_directory().Append(base::FilePath::FromUTF16Unsafe(
             tracker->GetOriginDirectory(kOrigin2)))));
     EXPECT_EQ(
         1, base::WriteFile(tracker->GetFullDBFilePath(kOrigin1, kDB1), "a", 1));
diff --git a/storage/common/database/database_connections.cc b/storage/common/database/database_connections.cc
index ace4dc4..479b45b 100644
--- a/storage/common/database/database_connections.cc
+++ b/storage/common/database/database_connections.cc
@@ -71,8 +71,12 @@
 int64_t DatabaseConnections::GetOpenDatabaseSize(
     const std::string& origin_identifier,
     const base::string16& database_name) const {
-  DCHECK(IsDatabaseOpened(origin_identifier, database_name));
-  return connections_[origin_identifier][database_name].second;
+  OriginConnections::const_iterator origin_it =
+      connections_.find(origin_identifier);
+  DCHECK(origin_it != connections_.end()) << "Database not opened";
+  DBConnections::const_iterator it = origin_it->second.find(database_name);
+  DCHECK(it != origin_it->second.end()) << "Database not opened";
+  return it->second.second;
 }
 
 void DatabaseConnections::SetOpenDatabaseSize(
diff --git a/storage/common/database/database_connections.h b/storage/common/database/database_connections.h
index ff46934..79d6b94 100644
--- a/storage/common/database/database_connections.h
+++ b/storage/common/database/database_connections.h
@@ -38,7 +38,7 @@
   std::vector<std::pair<std::string, base::string16>> RemoveConnections(
       const DatabaseConnections& connections);
 
-  // Database sizes can be kept only if IsDatabaseOpened returns true.
+  // Can be called only if IsDatabaseOpened would have returned true.
   int64_t GetOpenDatabaseSize(const std::string& origin_identifier,
                               const base::string16& database_name) const;
   void SetOpenDatabaseSize(const std::string& origin_identifier,
@@ -49,10 +49,12 @@
   std::vector<std::pair<std::string, base::string16>> ListConnections() const;
 
  private:
-  // Mapping from name to <openCount, size>
-  typedef std::map<base::string16, std::pair<int, int64_t>> DBConnections;
-  typedef std::map<std::string, DBConnections> OriginConnections;
-  mutable OriginConnections connections_;  // mutable for GetOpenDatabaseSize
+  // Maps database names to (open count, database size).
+  using DBConnections = std::map<base::string16, std::pair<int, int64_t>>;
+  // Maps origin identifiers to DBConnections.
+  using OriginConnections = std::map<std::string, DBConnections>;
+
+  OriginConnections connections_;
 
   // Returns true if the last connection was removed.
   bool RemoveConnectionsHelper(const std::string& origin_identifier,
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index e4a75204..6c2e0d08 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -209,7 +209,6 @@
 -ExtensionWebRequestApiTest.WebRequestDeclarative2
 -ExtensionWebRequestApiTest.WebRequestDiceHeaderProtection
 -ExtensionWebRequestApiTest.WebRequestTypes
--ExtensionWebRequestApiTest.WebRequestTestOSDD
 # Note WebRequestUnloadImmediately is disabled on Linux
 -ExtensionWebRequestApiTest.WebRequestUnloadImmediately
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 2f1389597..89c5f2a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1326,6 +1326,21 @@
             ]
         }
     ],
+    "DetectingHeavyPages": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DetectingHeavyPages"
+                    ]
+                }
+            ]
+        }
+    ],
     "DownloadHomeMoreButton": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 979f0cea..9b5217e 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -490,7 +490,6 @@
 crbug.com/591099 fast/block/float/nopaint-after-layer-destruction.html [ Failure ]
 crbug.com/591099 fast/block/float/nopaint-after-layer-destruction2.html [ Failure ]
 crbug.com/591099 fast/block/float/overlapping-floats-paint-hittest-order-1.html [ Failure ]
-crbug.com/591099 fast/block/line-layout/floats-do-not-fit-on-line.html [ Failure ]
 crbug.com/591099 fast/block/positioning/positioned-child-inside-relative-positioned-anonymous-block.html [ Failure ]
 crbug.com/591099 fast/borders/bidi-002.html [ Failure ]
 crbug.com/859497 fast/borders/bidi-009a.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 3db4991..c3e6976 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -343,7 +343,6 @@
 crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/basic/truncation-rtl.html [ Failure ]
 
 ### virtual/layout_ng/fast/block/float
-crbug.com/810335 virtual/layout_ng/fast/block/float/003.html [ Failure ]
 crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/float/020.html [ Failure ]
 crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/float/026.html [ Failure ]
 crbug.com/635619 [ Mac ] virtual/layout_ng/fast/block/float/028.html [ Failure ]
@@ -819,7 +818,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/mixed-opacity-test.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/mixed-positioning-stacking-order.html [ Failure ]
 crbug.com/714962 virtual/layout_ng_experimental/fast/multicol/multicol-becomes-abspos-crash.html [ Failure ]
-crbug.com/626703 virtual/layout_ng_experimental/fast/multicol/multicol-becomes-paged-auto-height.html [ Crash ]
+crbug.com/626703 virtual/layout_ng_experimental/fast/multicol/multicol-becomes-paged-auto-height.html [ Crash Pass ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/multicol-with-child-renderLayer-for-input.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-3-multicols-fixed-height.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/nested-auto-height-extra-block-inbetween.html [ Failure ]
@@ -1025,7 +1024,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/widows-and-orphans.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/widows.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/auto-height-with-break.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/auto-height.html [ Crash ]
+crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/auto-height.html [ Crash Pass ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/break-in-paged-overflow.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/caret-range-outside-paged-x-rtl-vertical-rl.html [ Failure Crash ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/caret-range-outside-paged-x-rtl.html [ Failure ]
@@ -1045,8 +1044,8 @@
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/div-y-vertical-lr-rtl.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/div-y-vertical-rl-ltr.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/div-y-vertical-rl-rtl.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/first-letter-inherit-all-crash.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/modal-dialog-crash.html [ Crash ]
+crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/first-letter-inherit-all-crash.html [ Crash Pass ]
+crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/modal-dialog-crash.html [ Crash Pass ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/multicol.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/paged-x-column-gap.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/fast/pagination/repeating-thead-tfoot-paged-x.html [ Failure ]
@@ -1154,8 +1153,8 @@
 crbug.com/591099 virtual/layout_ng_experimental/printing/css2.1/page-break-after-004.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/css2.1/page-break-before-000.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/fixed-positioned-but-static-headers-and-footers.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/fixed-positioned-child-repeats-even-when-html-and-body-are-zero-height.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/printing/fixed-positioned-child-shouldnt-print.html [ Crash ]
+crbug.com/591099 virtual/layout_ng_experimental/printing/fixed-positioned-child-repeats-even-when-html-and-body-are-zero-height.html [ Crash Pass ]
+crbug.com/591099 virtual/layout_ng_experimental/printing/fixed-positioned-child-shouldnt-print.html [ Crash Pass ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/fixed-positioned-headers-and-footers-clipped.html [ Failure ]
 crbug.com/824918 virtual/layout_ng_experimental/printing/fixed-positioned-headers-and-footers-inside-transform.html [ Crash Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/printing/fixed-positioned-headers-and-footers-larger-than-page.html [ Failure ]
@@ -4630,3 +4629,5 @@
 crbug.com/860706 [ Mac ] virtual/gpu/fast/canvas/canvas-blending-image-over-image.html [ Timeout Pass ]
 crbug.com/860706 [ Mac ] virtual/gpu/fast/canvas/color-space/canvas-createImageBitmap-rec2020.html [ Timeout Pass ]
 crbug.com/860706 [ Mac ] virtual/gpu/fast/canvas/canvas-blending-color-over-pattern.html [ Timeout Pass ]
+crbug.com/860706 [ Mac ] virtual/gpu/fast/canvas/canvas-blending-pattern-over-pattern.html [ Timeout Pass ]
+crbug.com/860706 [ Mac ] virtual/gpu/fast/canvas/canvas-shadow-source-in.html [ Timeout Pass ]
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index b960978..58a2271 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -9735,6 +9735,18 @@
      {}
     ]
    ],
+   "css/CSS2/floats/floats-line-wrap-shifted-001.html": [
+    [
+     "/css/CSS2/floats/floats-line-wrap-shifted-001.html",
+     [
+      [
+       "/css/CSS2/floats/floats-line-wrap-shifted-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/CSS2/floats/floats-placement-vertical-001a.xht": [
     [
      "/css/CSS2/floats/floats-placement-vertical-001a.xht",
@@ -107420,6 +107432,11 @@
      {}
     ]
    ],
+   "css/CSS2/floats/floats-line-wrap-shifted-001-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/CSS2/floats/floats-placement-vertical-001-ref.xht": [
     [
      {}
@@ -158305,6 +158322,11 @@
      {}
     ]
    ],
+   "interfaces/csp-embedded-enforcement.idl": [
+    [
+     {}
+    ]
+   ],
    "interfaces/css-animations.idl": [
     [
      {}
@@ -185636,6 +185658,12 @@
      {}
     ]
    ],
+   "content-security-policy/embedded-enforcement/idlharness.window.js": [
+    [
+     "/content-security-policy/embedded-enforcement/idlharness.window.html",
+     {}
+    ]
+   ],
    "content-security-policy/embedded-enforcement/iframe-csp-attribute.html": [
     [
      "/content-security-policy/embedded-enforcement/iframe-csp-attribute.html",
@@ -276667,6 +276695,10 @@
    "70aeb617f5d580917b385346ba629e035f062c32",
    "testharness"
   ],
+  "content-security-policy/embedded-enforcement/idlharness.window.js": [
+   "e009d639fbb4ede1085c365038fb79d1e0625143",
+   "testharness"
+  ],
   "content-security-policy/embedded-enforcement/iframe-csp-attribute.html": [
    "d5a253732352f46d33c1a58d1a3183a88daa3a75",
    "testharness"
@@ -282647,6 +282679,14 @@
    "f9b30873ba1da1309d17f2ae6f0777656521e5b2",
    "reftest"
   ],
+  "css/CSS2/floats/floats-line-wrap-shifted-001-ref.html": [
+   "97d01b457c2032e59de20eb768aa025607e9b046",
+   "support"
+  ],
+  "css/CSS2/floats/floats-line-wrap-shifted-001.html": [
+   "1e5c1373145ea0f217fa4cf354eeae5883b629af",
+   "reftest"
+  ],
   "css/CSS2/floats/floats-placement-vertical-001-ref.xht": [
    "219c3d13a6859b58907f35df0a5602ba215a0335",
    "support"
@@ -377491,6 +377531,10 @@
    "29a0bcde9ddd6629c7bf05757cea45c831fe9a6b",
    "support"
   ],
+  "interfaces/csp-embedded-enforcement.idl": [
+   "57d276da4bf41e5f47dd903411a748c3c6ebfd79",
+   "support"
+  ],
   "interfaces/css-animations.idl": [
    "804917872e1fd13cb9edaee693d4e011fcd7b79e",
    "support"
@@ -377680,7 +377724,7 @@
    "support"
   ],
   "interfaces/performance-timeline.idl": [
-   "57f26fe863d12c7672905d185e9a2c7ab9cb98a0",
+   "d9e73d5bb30f757c743f6bcff4aad6de4efc633c",
    "support"
   ],
   "interfaces/permissions.idl": [
@@ -377836,7 +377880,7 @@
    "support"
   ],
   "interfaces/webxr.idl": [
-   "f12a5bab0506e9e45cd06e29bf3acc15f7c2e8df",
+   "cc9199634844a31a06e4d25c4095a7193d308c90",
    "support"
   ],
   "interfaces/worklets.idl": [
@@ -388332,7 +388376,7 @@
    "testharness"
   ],
   "performance-timeline/idlharness.any.js": [
-   "0a3ea0b532a1634008b776489b7409b348952d6f",
+   "842da267dbe611c9f1505ca93e6ff799f25d7ee5",
    "testharness"
   ],
   "performance-timeline/performanceentry-tojson.any.js": [
@@ -413560,7 +413604,7 @@
    "support"
   ],
   "webxr/interfaces.https-expected.txt": [
-   "61c3134ea7352340f3eaa5ccc78701a64bd1d201",
+   "d7d20b425b896e27e9dd6991937e4f452341a279",
    "support"
   ],
   "webxr/interfaces.https.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/embedded-enforcement/idlharness.window.js b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/embedded-enforcement/idlharness.window.js
new file mode 100644
index 0000000..38fa663
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/embedded-enforcement/idlharness.window.js
@@ -0,0 +1,21 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+// https://w3c.github.io/webappsec-csp/embedded/
+
+'use strict';
+
+promise_test(async () => {
+  const idl = await fetch('/interfaces/csp-embedded-enforcement.idl').then(r => r.text());
+  const html = await fetch('/interfaces/html.idl').then(r => r.text());
+  const dom = await fetch('/interfaces/dom.idl').then(r => r.text());
+
+  const idl_array = new IdlArray();
+  idl_array.add_idls(idl);
+  idl_array.add_dependency_idls(html);
+  idl_array.add_dependency_idls(dom);
+  idl_array.add_objects({
+    HTMLIFrameElement: ['document.createElement("iframe")'],
+  });
+  idl_array.test();
+}, 'csp-embedded-enforcement IDL');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/csp-embedded-enforcement.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/csp-embedded-enforcement.idl
new file mode 100644
index 0000000..7d752eb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/csp-embedded-enforcement.idl
@@ -0,0 +1,8 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content of this file was automatically extracted from the
+// "Content Security Policy: Embedded Enforcement" spec.
+// See: https://w3c.github.io/webappsec-csp/embedded/
+
+partial interface HTMLIFrameElement {
+  [CEReactions] attribute DOMString csp;
+};
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/performance-timeline.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/performance-timeline.idl
index 857be8c..a8b78fb 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/performance-timeline.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/performance-timeline.idl
@@ -4,37 +4,33 @@
 // See: https://w3c.github.io/performance-timeline/
 
 partial interface Performance {
-    PerformanceEntryList getEntries();
-    PerformanceEntryList getEntriesByType(DOMString type);
-    PerformanceEntryList getEntriesByName(DOMString name,
-                                          optional DOMString type);
-};
-typedef sequence<PerformanceEntry> PerformanceEntryList;
+  PerformanceEntryList getEntries();
+  PerformanceEntryList getEntriesByType(DOMString type);
+  PerformanceEntryList getEntriesByName(DOMString name, optional DOMString type);
+};typedef sequence<PerformanceEntry> PerformanceEntryList;
 [Exposed=(Window,Worker)]
 interface PerformanceEntry {
-    readonly attribute DOMString           name;
-    readonly attribute DOMString           entryType;
-    readonly attribute DOMHighResTimeStamp startTime;
-    readonly attribute DOMHighResTimeStamp duration;
-    [Default] object toJSON();
+  readonly    attribute DOMString name;
+  readonly    attribute DOMString entryType;
+  readonly    attribute DOMHighResTimeStamp startTime;
+  readonly    attribute DOMHighResTimeStamp duration;
+  [Default] object toJSON();
 };
 callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries,
                                              PerformanceObserver observer);
-[Constructor(PerformanceObserverCallback callback),
- Exposed=(Window,Worker)]
+[Constructor(PerformanceObserverCallback callback), Exposed=(Window,Worker)]
 interface PerformanceObserver {
-    void                 observe(PerformanceObserverInit options);
-    void                 disconnect();
-    PerformanceEntryList takeRecords();
+  void observe(PerformanceObserverInit options);
+  void disconnect();
+  PerformanceEntryList takeRecords();
 };
 dictionary PerformanceObserverInit {
-    required sequence<DOMString> entryTypes;
-             boolean             buffered = false;
+  required sequence<DOMString> entryTypes;
+  boolean buffered = false;
 };
 [Exposed=(Window,Worker)]
 interface PerformanceObserverEntryList {
-    PerformanceEntryList getEntries();
-    PerformanceEntryList getEntriesByType(DOMString type);
-    PerformanceEntryList getEntriesByName(DOMString name,
-                                          optional DOMString type);
+  PerformanceEntryList getEntries();
+  PerformanceEntryList getEntriesByType(DOMString type);
+  PerformanceEntryList getEntriesByName(DOMString name, optional DOMString type);
 };
diff --git a/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/idlharness.any.js b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/idlharness.any.js
index b15a667..66e75cc 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/idlharness.any.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/performance-timeline/idlharness.any.js
@@ -6,21 +6,22 @@
 
 'use strict';
 
-promise_test(async () => {
-  const idl_array = new IdlArray();
-  const idl = await fetch("/interfaces/performance-timeline.idl").then(r => r.text());
-  const dom = await fetch("/interfaces/dom.idl").then(r => r.text());
-  const hrtime = await fetch("/interfaces/hr-time.idl").then(r => r.text());
+idl_test(
+  ['performance-timeline'],
+  ['hr-time', 'dom'],
+  idl_array => {
+    try {
+      const callback = (e, o) => {};
+      self.observerEntry = new ResizeObserverEntry(callback);
+    } catch (e) {
+      // Will be surfaced when entry is undefined below.
+    }
 
-  // create first mark
-  self.performance.mark("mark");
-
-  idl_array.add_idls(idl);
-  idl_array.add_dependency_idls(hrtime);
-  idl_array.add_dependency_idls(dom);
-  idl_array.add_objects({
-    Performance: ["performance"],
-    PerformanceMark: [self.performance.getEntriesByName("mark")[0]],
-  });
-  idl_array.test();
-}, "Test IDL implementation of performance-timeline API");
+    idl_array.add_objects({
+      Performance: ['performance'],
+      // PerformanceEntry implicitly tested in user-timing.
+      ResizeObserverEntry: ['observerEntry'],
+    });
+  },
+  'Test IDL implementation of performance-timeline API'
+);
diff --git a/third_party/WebKit/LayoutTests/fast/canvas-api/canvas-textMetrics-advances.html b/third_party/WebKit/LayoutTests/fast/canvas-api/canvas-textMetrics-advances.html
new file mode 100644
index 0000000..5739540
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/canvas-api/canvas-textMetrics-advances.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<head>
+<meta charset="UTF-8">
+<style>
+@font-face {
+  font-family: Libertine;
+  src: url('../../third_party/Libertine/LinLibertine_R.woff');
+}
+</style>
+</head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+
+const kAligns = [ "left", "right", "center", "start", "end" ];
+
+const kTexts = [
+  { text: "Hello", rtl: false },
+  { text: "傳統是假的", rtl: false },
+  { text: "フェミニズム", rtl: false },
+  { text: "ليس", rtl: true },
+  { text: "\u202EHello", rtl: true },
+  // TODO(davidqu): Fix the following edge cases:
+  //{ text: "الله‎", rtl: true }, // Special ligatures.
+  //{ text: "ليس في اسمنا", rtl: true }, // RTL only works for single "words".
+  //{ text: "\u202EHello World", rtl: true },
+  //{ text: "🏁", rtl: false }, // One glyph with two "characters".
+  //{ text: "एक आम भाषा", rtl: false }, // Special post-modifying characters.
+  //{ text: "a\u0301", rtl: true }, // Combining diacritical marks
+]
+
+function forEachExample(fn) {
+  for (const ex of kTexts) {
+    for (const align of kAligns) {
+      fn(ex, align);
+    }
+  }
+}
+
+function isNonDecreasing(array) {
+  for (var i = 1; i < array.length; i++) {
+    if (array[i] < array[i-1]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+function getTextMetrics(ctx, text, align="left", direction="ltr") {
+  ctx.font = '25px Libertine';
+  ctx.textAlign = align;
+  ctx.direction = direction;
+  return ctx.measureText(text);
+}
+
+function testEmptyStringReturnsEmptyAdvances(ctx) {
+  const tm = getTextMetrics(ctx, "");
+  assert_array_equals(tm.advances, [], "Empty string must return empty advances");
+}
+
+function testAllPositive(ctx) {
+  forEachExample((ex, align) => {
+      const tm = getTextMetrics(ctx, ex.text, align, ex.rtl ? "rtl" : "ltr", );
+      assert_true(tm.advances.every(function isPositive(i) {return i >= 0; }),
+        "Advances must be all positive (" + ex.text + ")");
+  });
+}
+
+function testNonDecreasing(ctx) {
+  forEachExample((ex, align) => {
+      const tm = getTextMetrics(ctx, ex.text, align, ex.rtl ? "rtl" : "ltr");
+      assert_true(isNonDecreasing(tm.advances),
+       "Advances must be non-decreasing (" + ex.text + ")");
+  });
+}
+
+function testLastAdvanceLessThanWith(ctx) {
+  forEachExample((ex, align) => {
+      const tm = getTextMetrics(ctx, ex.text, align, ex.rtl ? "rtl" : "ltr");
+      assert_less_than(tm.advances.slice(-1)[0], tm.width,
+        "Last advance must be strictly less than total width (" + ex.text + ")");
+  });
+}
+
+function testAdvances(ctx) {
+  testEmptyStringReturnsEmptyAdvances(ctx);
+  testAllPositive(ctx);
+  testNonDecreasing(ctx);
+  testLastAdvanceLessThanWith(ctx);
+}
+
+async_test(t => {
+  var canvas = document.createElement('canvas');
+  canvas.width = 100;
+  canvas.height = 100;
+  var ctx = canvas.getContext('2d');
+  // Kick off loading of the font
+  ctx.font = '50px Libertine';
+  ctx.fillText(" ", 0, 0);
+  document.fonts.ready.then(t.step_func_done(testAdvances(ctx)));
+}, "Test TextMetrics advances.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-within-composited-scroller-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-within-composited-scroller-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win/paint/invalidation/selection/selection-within-composited-scroller-expected.txt
rename to third_party/WebKit/LayoutTests/paint/invalidation/selection/selection-within-composited-scroller-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/image-selection-highlight-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/image-selection-highlight-expected.txt
deleted file mode 100644
index f2c5242..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/overflow/image-selection-highlight-expected.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 212x19
-          text run at (0,0) width 212: "This tests for a regression against "
-        LayoutInline {I} at (0,0) size 767x39
-          LayoutInline {A} at (0,0) size 348x19 [color=#0000EE]
-            LayoutText {#text} at (212,0) size 348x19
-              text run at (212,0) width 348: "http://bugzilla.opendarwin.org/show_bug.cgi?id=6673"
-          LayoutText {#text} at (559,0) size 767x39
-            text run at (559,0) width 5: " "
-            text run at (563,0) width 204: "Selection highlight doesn't scroll"
-            text run at (0,20) width 369: "along with an image contained in an overflow:scroll block"
-        LayoutText {#text} at (368,20) size 5x19
-          text run at (368,20) width 5: "."
-      LayoutBlockFlow {P} at (0,56) size 784x40
-        LayoutText {#text} at (0,0) size 748x39
-          text run at (0,0) width 412: "There should be one contiguous highlight from \x{201C}elit\x{201D} to \x{201C}Etiam\x{201D}, "
-          text run at (412,0) width 336: "including the orange square, and no other highlighted"
-          text run at (0,20) width 36: "areas."
-      LayoutBlockFlow {HR} at (0,112) size 784x2 [border: (1px inset #EEEEEE)]
-layer at (8,130) size 100x200 clip at (8,130) size 85x200 scrollY 40.00 scrollHeight 320
-  LayoutBlockFlow {DIV} at (0,122) size 100x200
-    LayoutText {#text} at (0,0) size 84x119
-      text run at (0,0) width 84: "Lorem ipsum"
-      text run at (0,20) width 51: "dolor sit"
-      text run at (0,40) width 34: "amet,"
-      text run at (0,60) width 78: "consectetuer"
-      text run at (0,80) width 64: "adipiscing"
-      text run at (0,100) width 27: "elit. "
-    LayoutImage {IMG} at (27,105) size 10x10
-    LayoutText {#text} at (37,100) size 83x219
-      text run at (37,100) width 4: " "
-      text run at (41,100) width 37: "Etiam"
-      text run at (0,120) width 57: "et ipsum."
-      text run at (0,140) width 31: "Nam"
-      text run at (0,160) width 78: "consectetuer"
-      text run at (0,180) width 81: "mi eget velit."
-      text run at (0,200) width 83: "Sed nec risus"
-      text run at (0,220) width 60: "vitae felis"
-      text run at (0,240) width 39: "auctor"
-      text run at (0,260) width 53: "ultricies."
-      text run at (0,280) width 79: "Pellentesque"
-      text run at (0,300) width 54: "aliquet..."
-selection start: position 58 of child 0 {#text} of child 7 {DIV} of body
-selection end:   position 11 of child 2 {#text} of child 7 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/atsui-partial-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/atsui-partial-selection-expected.txt
deleted file mode 100644
index 49b33e9..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/atsui-partial-selection-expected.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x576
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 52x19
-          text run at (0,0) width 52: "Test for "
-        LayoutInline {I} at (0,0) size 749x39
-          LayoutInline {A} at (0,0) size 299x19 [color=#0000EE]
-            LayoutText {#text} at (51,0) size 299x19
-              text run at (51,0) width 299: "http://bugs.webkit.org/show_bug.cgi?id=11124"
-          LayoutText {#text} at (349,0) size 749x39
-            text run at (349,0) width 5: " "
-            text run at (353,0) width 396: "REGRESSION (r14297): No drag image for partially-selected"
-            text run at (0,20) width 79: "complex text"
-        LayoutText {#text} at (79,20) size 4x19
-          text run at (79,20) width 4: "."
-      LayoutBlockFlow {P} at (0,56) size 784x20
-        LayoutText {#text} at (0,0) size 144x19
-          text run at (0,0) width 144: "This should look like \x{201C}"
-        LayoutInline {SPAN} at (0,0) size 84x19 [color=#008000]
-          LayoutText {#text} at (144,0) size 84x19
-            text run at (144,0) width 84: "Lore\x{300}m ipsum"
-        LayoutText {#text} at (228,0) size 15x19
-          text run at (228,0) width 15: "\x{201D}: "
-        LayoutInline {SPAN} at (0,0) size 88x19
-          LayoutText {#text} at (243,0) size 88x19
-            text run at (243,0) width 88: " Lore\x{300}m ipsum"
-        LayoutText {#text} at (0,0) size 0x0
-selection start: position 1 of child 0 {#text} of child 3 {SPAN} of child 2 {P} of body
-selection end:   position 13 of child 0 {#text} of child 3 {SPAN} of child 2 {P} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/emphasis-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/emphasis-expected.txt
deleted file mode 100644
index f6f6e42..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/emphasis-expected.txt
+++ /dev/null
@@ -1,198 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x554
-  LayoutBlockFlow {HTML} at (0,0) size 800x554
-    LayoutBlockFlow {BODY} at (8,8) size 784x538
-      LayoutBlockFlow {DIV} at (4,4) size 366x146 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,3) size 270x27
-          text run at (3,3) width 270: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 213x27
-          LayoutText {#text} at (3,45) size 213x27
-            text run at (3,45) width 213: "consectetur adipiscing"
-        LayoutText {#text} at (216,45) size 130x27
-          text run at (216,45) width 6: " "
-          text run at (222,45) width 124: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 109x27
-          LayoutText {#text} at (3,73) size 109x27
-            text run at (3,73) width 109: "odio sapien"
-        LayoutText {#text} at (112,73) size 330x69
-          text run at (112,73) width 12: ", "
-          text run at (124,73) width 209: "lobortis eu iaculis vel,"
-          text run at (3,115) width 207: "scelerisque nec dolor."
-      LayoutText {#text} at (374,119) size 6x27
-        text run at (374,119) width 6: " "
-      LayoutBlockFlow {DIV} at (384,4) size 366x146 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,3) size 270x27
-          text run at (3,3) width 270: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 213x27
-          LayoutText {#text} at (3,45) size 213x27
-            text run at (3,45) width 213: "consectetur adipiscing"
-        LayoutText {#text} at (216,45) size 130x27
-          text run at (216,45) width 6: " "
-          text run at (222,45) width 124: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 109x27
-          LayoutText {#text} at (3,73) size 109x27
-            text run at (3,73) width 109: "odio sa\x{300}pien"
-        LayoutText {#text} at (112,73) size 330x69
-          text run at (112,73) width 12: ", "
-          text run at (124,73) width 209: "lobortis eu iaculis vel,"
-          text run at (3,115) width 207: "scelerisque nec dolor."
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {DIV} at (4,167) size 366x161 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,18) size 70x27
-          text run at (3,18) width 70: "Lorem "
-        LayoutInline {SPAN} at (0,0) size 57x27
-          LayoutText {#text} at (73,18) size 57x27
-            text run at (73,18) width 57: "ipsum"
-        LayoutText {#text} at (130,18) size 6x27
-          text run at (130,18) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 50x27
-          LayoutText {#text} at (136,18) size 50x27
-            text run at (136,18) width 50: "dolor"
-        LayoutText {#text} at (186,18) size 6x27
-          text run at (186,18) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 22x27
-          LayoutText {#text} at (192,18) size 22x27
-            text run at (192,18) width 22: "sit"
-        LayoutText {#text} at (214,18) size 6x27
-          text run at (214,18) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 47x27
-          LayoutText {#text} at (220,18) size 47x27
-            text run at (220,18) width 47: "amet"
-        LayoutText {#text} at (267,18) size 6x27
-          text run at (267,18) width 6: ","
-        LayoutInline {SPAN} at (0,0) size 111x27
-          LayoutText {#text} at (3,60) size 111x27
-            text run at (3,60) width 111: "consectetur"
-        LayoutText {#text} at (114,60) size 6x27
-          text run at (114,60) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 96x27
-          LayoutText {#text} at (120,60) size 96x27
-            text run at (120,60) width 96: "adipiscing"
-        LayoutText {#text} at (216,60) size 6x27
-          text run at (216,60) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 30x27
-          LayoutText {#text} at (222,60) size 30x27
-            text run at (222,60) width 30: "elit"
-        LayoutText {#text} at (252,60) size 12x27
-          text run at (252,60) width 12: ". "
-        LayoutInline {SPAN} at (0,0) size 82x27
-          LayoutText {#text} at (264,60) size 82x27
-            text run at (264,60) width 82: "Aliquam"
-        LayoutText {#text} at (346,60) size 6x27
-          text run at (346,60) width 6: ","
-        LayoutInline {SPAN} at (0,0) size 42x27
-          LayoutText {#text} at (3,102) size 42x27
-            text run at (3,102) width 42: "odio"
-        LayoutText {#text} at (45,102) size 6x27
-          text run at (45,102) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 61x27
-          LayoutText {#text} at (51,102) size 61x27
-            text run at (51,102) width 61: "sapien"
-        LayoutText {#text} at (112,102) size 12x27
-          text run at (112,102) width 12: ", "
-        LayoutInline {SPAN} at (0,0) size 72x27
-          LayoutText {#text} at (124,102) size 72x27
-            text run at (124,102) width 72: "lobortis"
-        LayoutText {#text} at (196,102) size 330x55
-          text run at (196,102) width 6: " "
-          text run at (202,102) width 131: "eu iaculis vel,"
-          text run at (3,130) width 207: "scelerisque nec dolor."
-      LayoutText {#text} at (374,297) size 6x27
-        text run at (374,297) width 6: " "
-      LayoutBlockFlow {DIV} at (384,158) size 366x156 [border: (3px solid #000000)]
-        LayoutText {#text} at (18,3) size 27x70
-          text run at (18,3) width 70: "Lorem "
-        LayoutInline {SPAN} at (0,0) size 27x57
-          LayoutText {#text} at (18,73) size 27x57
-            text run at (18,73) width 57: "ipsum"
-        LayoutText {#text} at (0,0) size 0x0
-        LayoutInline {SPAN} at (0,0) size 27x50
-          LayoutText {#text} at (60,3) size 27x50
-            text run at (60,3) width 50: "dolor"
-        LayoutText {#text} at (60,53) size 27x6
-          text run at (60,53) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 27x22
-          LayoutText {#text} at (60,59) size 27x22
-            text run at (60,59) width 22: "sit"
-        LayoutText {#text} at (60,81) size 27x6
-          text run at (60,81) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 27x47
-          LayoutText {#text} at (60,87) size 27x47
-            text run at (60,87) width 47: "amet"
-        LayoutText {#text} at (60,134) size 27x6
-          text run at (60,134) width 6: ","
-        LayoutInline {SPAN} at (0,0) size 27x111
-          LayoutText {#text} at (99,3) size 27x111
-            text run at (99,3) width 111: "consectetur"
-        LayoutText {#text} at (0,0) size 0x0
-        LayoutInline {SPAN} at (0,0) size 27x96
-          LayoutText {#text} at (141,3) size 27x96
-            text run at (141,3) width 96: "adipiscing"
-        LayoutText {#text} at (141,99) size 27x6
-          text run at (141,99) width 6: " "
-        LayoutInline {SPAN} at (0,0) size 27x30
-          LayoutText {#text} at (141,105) size 27x30
-            text run at (141,105) width 30: "elit"
-        LayoutText {#text} at (141,135) size 27x6
-          text run at (141,135) width 6: "."
-        LayoutInline {SPAN} at (0,0) size 27x82
-          LayoutText {#text} at (183,3) size 27x82
-            text run at (183,3) width 82: "Aliquam"
-        LayoutText {#text} at (183,85) size 27x12
-          text run at (183,85) width 12: ", "
-        LayoutInline {SPAN} at (0,0) size 27x42
-          LayoutText {#text} at (183,97) size 27x42
-            text run at (183,97) width 42: "odio"
-        LayoutText {#text} at (0,0) size 0x0
-        LayoutInline {SPAN} at (0,0) size 27x61
-          LayoutText {#text} at (225,3) size 27x61
-            text run at (225,3) width 61: "sapien"
-        LayoutText {#text} at (225,64) size 27x12
-          text run at (225,64) width 12: ", "
-        LayoutInline {SPAN} at (0,0) size 27x72
-          LayoutText {#text} at (225,76) size 27x72
-            text run at (225,76) width 72: "lobortis"
-        LayoutText {#text} at (253,3) size 83x146
-          text run at (253,3) width 131: "eu iaculis vel,"
-          text run at (281,3) width 146: "scelerisque nec"
-          text run at (309,3) width 55: "dolor."
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutBlockFlow {DIV} at (4,336) size 366x198 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,13) size 270x27
-          text run at (3,13) width 270: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 213x27
-          LayoutText {#text} at (3,61) size 213x27
-            text run at (3,61) width 213: "consectetur adipiscing"
-        LayoutText {#text} at (216,61) size 130x27
-          text run at (216,61) width 6: " "
-          text run at (222,61) width 124: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 109x27
-          LayoutText {#text} at (3,109) size 109x27
-            text run at (3,109) width 109: "odio sapien"
-        LayoutText {#text} at (112,109) size 330x75
-          text run at (112,109) width 12: ", "
-          text run at (124,109) width 209: "lobortis eu iaculis vel,"
-          text run at (3,157) width 207: "scelerisque nec dolor."
-      LayoutText {#text} at (374,493) size 6x27
-        text run at (374,493) width 6: " "
-      LayoutBlockFlow {DIV} at (384,378) size 366x146 [border: (3px solid #000000)]
-        LayoutText {#text} at (3,3) size 270x27
-          text run at (3,3) width 270: "Lorem ipsum dolor sit amet,"
-        LayoutInline {SPAN} at (0,0) size 213x27
-          LayoutText {#text} at (3,45) size 213x27
-            text run at (3,45) width 213: "consectetur adipiscing"
-        LayoutText {#text} at (216,45) size 130x27
-          text run at (216,45) width 6: " "
-          text run at (222,45) width 124: "elit. Aliquam"
-        LayoutInline {SPAN} at (0,0) size 109x27
-          LayoutText {#text} at (3,73) size 109x27
-            text run at (3,73) width 109: "odio sapien"
-        LayoutText {#text} at (112,73) size 330x69
-          text run at (112,73) width 12: ", "
-          text run at (124,73) width 209: "lobortis eu iaculis vel,"
-          text run at (3,115) width 207: "scelerisque nec dolor."
-      LayoutText {#text} at (0,0) size 0x0
-      LayoutText {#text} at (0,0) size 0x0
-selection start: position 10 of child 0 {#text} of child 1 {SPAN} of child 10 {DIV} of body
-selection end:   position 7 of child 0 {#text} of child 3 {SPAN} of child 10 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/justified-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/justified-selection-expected.txt
deleted file mode 100644
index d7f8b8e8..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/justified-selection-expected.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 106x19
-          text run at (0,0) width 106: "Test for revision "
-        LayoutInline {A} at (0,0) size 49x19 [color=#0000EE]
-          LayoutText {#text} at (105,0) size 49x19
-            text run at (105,0) width 49: "#20574"
-        LayoutText {#text} at (153,0) size 5x19
-          text run at (153,0) width 5: "."
-      LayoutBlockFlow {P} at (0,36) size 784x20
-        LayoutText {#text} at (0,0) size 251x19
-          text run at (0,0) width 251: "The two blue boxes should be identical."
-      LayoutBlockFlow {DIV} at (0,72) size 106x46 [border: (3px solid #0000FF)]
-        LayoutText {#text} at (3,3) size 10x19
-          text run at (3,3) width 10: "L"
-        LayoutInline {SPAN} at (0,0) size 40x19 [color=#008000] [bgcolor=#FFFF00]
-          LayoutText {#text} at (13,3) size 40x19
-            text run at (13,3) width 40: "o r"
-        LayoutText {#text} at (53,3) size 100x39
-          text run at (53,3) width 50: "e mi"
-          text run at (3,23) width 67: "psumdolor"
-      LayoutBlockFlow (anonymous) at (0,118) size 784x20
-        LayoutBR {BR} at (0,0) size 0x19
-      LayoutBlockFlow {DIV} at (0,138) size 106x46 [border: (3px solid #0000FF)]
-        LayoutText {#text} at (3,3) size 100x39
-          text run at (3,3) width 100: "Lo re mi"
-          text run at (3,23) width 67: "psumdolor"
-selection start: position 1 of child 0 {#text} of child 8 {DIV} of body
-selection end:   position 4 of child 0 {#text} of child 8 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/selection-multiple-runs-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/selection-multiple-runs-expected.txt
deleted file mode 100644
index 2cc01c4e..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/selection-multiple-runs-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x244
-  LayoutBlockFlow {HTML} at (0,0) size 800x244
-    LayoutBlockFlow {BODY} at (8,30) size 784x206
-      LayoutBlockFlow {P} at (0,0) size 784x70
-        LayoutText {#text} at (0,0) size 766x69
-          text run at (0,0) width 766: "The selection should be painted correctly from and including \x{732B}"
-          text run at (0,35) width 579: "until and including C, then from right to left for "
-          text run at (579,35) width 39 RTL: "\x{627}\x{644}\x{639}\x{631}"
-          text run at (618,35) width 8: "."
-      LayoutBlockFlow {DIV} at (0,100) size 784x53
-        LayoutText {#text} at (0,7) size 415x34
-          text run at (0,7) width 351: "\x{543E}\x{8F29}\x{306F}\x{732B}\x{3067} \x{92E}\x{93E}\x{928}\x{915} \x{939}\x{93F}\x{928}\x{94D}\x{926}\x{940}ABC"
-          text run at (351,7) width 64 RTL: "\x{627}\x{644}\x{639}\x{631}\x{628}\x{64A}\x{629}"
-      LayoutBlockFlow {DIV} at (0,153) size 784x53
-        LayoutText {#text} at (0,7) size 415x34
-          text run at (0,7) width 351: "\x{543E}\x{8F29}\x{306F}\x{732B}\x{3067} \x{92E}\x{93E}\x{928}\x{915} \x{939}\x{93F}\x{928}\x{94D}\x{926}\x{940}ABC"
-          text run at (351,7) width 64 RTL: "\x{627}\x{644}\x{639}\x{631}\x{628}\x{64A}\x{629}"
-selection start: position 3 of child 0 {#text} of child 5 {DIV} of body
-selection end:   position 24 of child 0 {#text} of child 5 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/should-use-atsui-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/should-use-atsui-expected.txt
deleted file mode 100644
index f199fc6..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection/should-use-atsui-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutBlockFlow {HTML} at (0,0) size 800x600
-    LayoutBlockFlow {BODY} at (8,8) size 784x584
-      LayoutBlockFlow {P} at (0,0) size 784x40
-        LayoutText {#text} at (0,0) size 52x19
-          text run at (0,0) width 52: "Test for "
-        LayoutInline {I} at (0,0) size 734x39
-          LayoutText {#text} at (51,0) size 734x39
-            text run at (51,0) width 683: "http://bugzilla.opendarwin.org/show_bug.cgi?id=6132 Incorrect selection highlighting for ATSUI text when"
-            text run at (0,20) width 176: "selected range is \"CG-safe\""
-        LayoutText {#text} at (176,20) size 4x19
-          text run at (176,20) width 4: "."
-      LayoutBlockFlow {P} at (0,56) size 784x20
-        LayoutText {#text} at (0,0) size 708x19
-          text run at (0,0) width 708: "The word \x{201C}dolor\x{201D} below should be highlighted in its entirety. The highlight should not extend beyond that word."
-      LayoutBlockFlow {HR} at (0,92) size 784x2 [border: (1px inset #EEEEEE)]
-      LayoutBlockFlow (anonymous) at (0,102) size 784x20
-        LayoutInline {SPAN} at (0,0) size 173x19
-          LayoutText {#text} at (0,0) size 173x19
-            text run at (0,0) width 173: "Lo\x{308}re\x{300}m ipsum dolor sit amet"
-        LayoutText {#text} at (0,0) size 0x0
-selection start: position 14 of child 0 {#text} of child 7 {SPAN} of body
-selection end:   position 19 of child 0 {#text} of child 7 {SPAN} of body
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/japanese-rl-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/japanese-rl-selection-expected.txt
deleted file mode 100644
index 7cc5f34e61..0000000
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/writing-mode/japanese-rl-selection-expected.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (418,0) size 382x600
-  LayoutBlockFlow {HTML} at (0,0) size 382x600 [border: (10px solid #800000)]
-    LayoutBlockFlow {BODY} at (18,18) size 346x564 [border: (5px solid #000000)]
-      LayoutBlockFlow {DIV} at (5,105) size 336x400
-        LayoutText {#text} at (0,0) size 335x399
-          text run at (0,0) width 399: "\x{305B}\x{3063}\x{304B}\x{304F}\x{898B}\x{3064}\x{3051}\x{305F}\x{3059}\x{3070}\x{3089}\x{3057}\x{3044}\x{8A18}\x{4E8B}\x{304C}\x{3069}\x{3053}\x{306B}"
-          text run at (24,0) width 399: "\x{3042}\x{3063}\x{305F}\x{304B}\x{5FD8}\x{308C}\x{3066}\x{3057}\x{307E}\x{3063}\x{305F}\x{7D4C}\x{9A13}\x{306F}\x{3042}\x{308A}\x{307E}\x{3059}\x{304B}"
-          text run at (48,0) width 399: "\x{306A}\x{3089}\x{30BF}\x{30A4}\x{30C8}\x{30EB}\x{3068}\x{30A2}\x{30C9}\x{30EC}\x{30B9}\x{3060}\x{3051}\x{3067}\x{306A}\x{304F}\x{3001}\x{8A2A}\x{554F}"
-          text run at (72,0) width 399: "\x{3057}\x{305F}\x{30A6}\x{30A7}\x{30D6}\x{30DA}\x{30FC}\x{30B8}\x{306E}\x{30B3}\x{30F3}\x{30C6}\x{30F3}\x{30C4}\x{304B}\x{3089}\x{3082}\x{691C}\x{7D22}"
-          text run at (96,0) width 399: "\x{3059}\x{308B}\x{3053}\x{3068}\x{304C}\x{3067}\x{304D}\x{307E}\x{3059}\x{3002}\x{305B}\x{3063}\x{304B}\x{304F}\x{898B}\x{3064}\x{3051}\x{305F}\x{3059}"
-          text run at (120,0) width 399: "\x{3070}\x{3089}\x{3057}\x{3044}\x{8A18}\x{4E8B}\x{304C}\x{3069}\x{3053}\x{306B}\x{3042}\x{3063}\x{305F}\x{304B}\x{5FD8}\x{308C}\x{3066}\x{3057}\x{307E}"
-          text run at (144,0) width 383: "\x{3063}\x{305F}\x{7D4C}\x{9A13}\x{306F}\x{3042}\x{308A}\x{307E}\x{3059}\x{304B} \x{306A}\x{3089}\x{30BF}\x{30A4}\x{30C8}\x{30EB}\x{3068}\x{30A2}"
-          text run at (168,0) width 399: "\x{30C9}\x{30EC}\x{30B9}\x{3060}\x{3051}\x{3067}\x{306A}\x{304F}\x{3001}\x{8A2A}\x{554F}\x{3057}\x{305F}\x{30A6}\x{30A7}\x{30D6}\x{30DA}\x{30FC}\x{30B8}"
-          text run at (192,0) width 399: "\x{306E}\x{30B3}\x{30F3}\x{30C6}\x{30F3}\x{30C4}\x{304B}\x{3089}\x{3082}\x{691C}\x{7D22}\x{3059}\x{308B}\x{3053}\x{3068}\x{304C}\x{3067}\x{304D}\x{307E}"
-          text run at (216,0) width 399: "\x{3059}\x{3002}\x{8A2A}\x{554F}\x{3057}\x{305F}\x{30A6}\x{30A7}\x{30D6}\x{30DA}\x{30FC}\x{30B8}\x{306E}\x{30B3}\x{30F3}\x{30C6}\x{30F3}\x{30C4}\x{304B}"
-          text run at (240,0) width 399: "\x{3089}\x{3082}\x{691C}\x{7D22}\x{3059}\x{308B}\x{3053}\x{3068}\x{304C}\x{3067}\x{304D}\x{307E}\x{3059}\x{3002}\x{305B}\x{3063}\x{304B}\x{304F}\x{898B}"
-          text run at (264,0) width 399: "\x{3064}\x{3051}\x{305F}\x{3059}\x{3070}\x{3089}\x{3057}\x{3044}\x{8A18}\x{4E8B}\x{304C}\x{3069}\x{3053}\x{306B}\x{3042}\x{3063}\x{305F}\x{304B}\x{5FD8}"
-          text run at (288,0) width 383: "\x{308C}\x{3066}\x{3057}\x{307E}\x{3063}\x{305F}\x{7D4C}\x{9A13}\x{306F}\x{3042}\x{308A}\x{307E}\x{3059}\x{304B} \x{306A}\x{3089}\x{30BF}\x{30A4}"
-          text run at (312,0) width 315: "\x{30C8}\x{30EB}\x{3068}\x{30A2}\x{30C9}\x{30EC}\x{30B9}\x{3060}\x{3051}\x{3067}\x{306A}\x{304F}\x{3001}\x{8A2A}\x{554F}"
-selection start: position 5 of child 0 {#text} of child 1 {DIV} of body
-selection end:   position 252 of child 0 {#text} of child 1 {DIV} of body
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt
new file mode 100644
index 0000000..3caa47b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt
@@ -0,0 +1,13 @@
+This tests clicking on the left of RTL text puts the caret at the end of the line.
+
+PASS: on ך לכ, caret is at 4 initially
+PASS: on ך לכ, caret is at 2 after moving upwards once
+PASS: on כ ככ כככ, caret is at 8 initially
+PASS: on כ ככ כככ, caret is at 5 after moving upwards once
+PASS: on כ ככ כככ, caret is at 2 after moving upwards twice
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 18 initially
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 14 after moving upwards once
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 10 after moving upwards twice
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 6 after moving upwards 3 times
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 3 after moving upwards 4 times
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/editing/selection/offset-from-point-complex-scripts-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/editing/selection/offset-from-point-complex-scripts-expected.txt
new file mode 100644
index 0000000..e4ad1bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/editing/selection/offset-from-point-complex-scripts-expected.txt
@@ -0,0 +1 @@
+0 4 3 2 6
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/selection-painted-separately-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/selection-painted-separately-expected.png
new file mode 100644
index 0000000..ab30ea5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/selection-painted-separately-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt
new file mode 100644
index 0000000..a16d129
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt
@@ -0,0 +1,10 @@
+This tests clicking on the left of RTL text puts the caret at the end of the line.
+
+PASS: on ך לכ, caret is at 4 initially
+PASS: on ך לכ, caret is at 2 after moving upwards once
+PASS: on כ ככ כככ, caret is at 8 initially
+PASS: on כ ככ כככ, caret is at 5 after moving upwards once
+PASS: on כ ככ כככ, caret is at 2 after moving upwards twice
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 18 initially
+FAIL: on גכ יגכ יגכ יגכ יגכ, caret is at 15 after moving upwards once but expected at 14
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/editing/selection/offset-from-point-complex-scripts-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/editing/selection/offset-from-point-complex-scripts-expected.txt
new file mode 100644
index 0000000..50a789e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/editing/selection/offset-from-point-complex-scripts-expected.txt
@@ -0,0 +1 @@
+0 5 4 3 2 6
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/selection/selection-painted-separately-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/selection/selection-painted-separately-expected.png
new file mode 100644
index 0000000..e64b0af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/fast/text/selection/selection-painted-separately-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-retina/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt
new file mode 100644
index 0000000..3caa47b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/editing/selection/modify-up-on-rtl-wrapping-text-expected.txt
@@ -0,0 +1,13 @@
+This tests clicking on the left of RTL text puts the caret at the end of the line.
+
+PASS: on ך לכ, caret is at 4 initially
+PASS: on ך לכ, caret is at 2 after moving upwards once
+PASS: on כ ככ כככ, caret is at 8 initially
+PASS: on כ ככ כככ, caret is at 5 after moving upwards once
+PASS: on כ ככ כככ, caret is at 2 after moving upwards twice
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 18 initially
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 14 after moving upwards once
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 10 after moving upwards twice
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 6 after moving upwards 3 times
+PASS: on גכ יגכ יגכ יגכ יגכ, caret is at 3 after moving upwards 4 times
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/editing/selection/offset-from-point-complex-scripts-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-retina/editing/selection/offset-from-point-complex-scripts-expected.txt
new file mode 100644
index 0000000..e4ad1bd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/editing/selection/offset-from-point-complex-scripts-expected.txt
@@ -0,0 +1 @@
+0 4 3 2 6
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/atsui-partial-selection-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/atsui-partial-selection-expected.png
new file mode 100644
index 0000000..6e90dbd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/atsui-partial-selection-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/emphasis-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/emphasis-expected.png
new file mode 100644
index 0000000..af48c0db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/emphasis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/justified-selection-at-edge-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/justified-selection-at-edge-expected.png
new file mode 100644
index 0000000..0254daf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/justified-selection-at-edge-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/justified-selection-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/justified-selection-expected.png
new file mode 100644
index 0000000..ead45108
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/justified-selection-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/selection-multiple-runs-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/selection-multiple-runs-expected.png
new file mode 100644
index 0000000..831359be
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/selection-multiple-runs-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/selection-painted-separately-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/selection-painted-separately-expected.png
new file mode 100644
index 0000000..ab30ea5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-retina/fast/text/selection/selection-painted-separately-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/editing/selection/replaced-boundaries-1-expected.png b/third_party/WebKit/LayoutTests/platform/win7/editing/selection/replaced-boundaries-1-expected.png
new file mode 100644
index 0000000..1b850154
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/editing/selection/replaced-boundaries-1-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/color-emoji-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/color-emoji-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/color-emoji-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/color-emoji-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/complex-preferred-logical-widths-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/complex-preferred-logical-widths-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/complex-preferred-logical-widths-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/complex-preferred-logical-widths-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/drawBidiText-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/drawBidiText-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/drawBidiText-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/drawBidiText-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/drawBidiText-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/drawBidiText-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/drawBidiText-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/drawBidiText-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/emoticons-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/emoticons-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/emoticons-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/emoticons-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/emphasis-complex-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/emphasis-complex-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/emphasis-complex-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/fallback-traits-fixup-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/fallback-traits-fixup-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/fallback-traits-fixup-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/fallback-traits-fixup-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-fallback-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/font-fallback-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-fallback-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/font-fallback-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-fallback-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/font-fallback-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/font-fallback-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/font-fallback-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/bidi-listbox-atsui-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/international/bidi-listbox-atsui-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/bidi-listbox-atsui-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/international/bidi-listbox-atsui-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/bidi-listbox-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/international/bidi-listbox-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/bidi-listbox-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/international/bidi-listbox-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/hindi-spacing-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/international/hindi-spacing-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/hindi-spacing-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/international/hindi-spacing-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/lang-glyph-cache-separation-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/international/lang-glyph-cache-separation-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/lang-glyph-cache-separation-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/international/lang-glyph-cache-separation-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/unicode-bidi-plaintext-in-textarea-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/international/unicode-bidi-plaintext-in-textarea-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/international/unicode-bidi-plaintext-in-textarea-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/international/unicode-bidi-plaintext-in-textarea-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/justify-ideograph-complex-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/justify-ideograph-complex-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/justify-ideograph-complex-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/justify-ideograph-complex-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/justify-ideograph-simple-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/justify-ideograph-simple-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/justify-ideograph-simple-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/justify-ideograph-simple-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/justify-ideograph-vertical-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/justify-ideograph-vertical-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/justify-ideograph-vertical-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/justify-ideograph-vertical-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/mac-system-ui-trak-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/mac-system-ui-trak-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/mac-system-ui-trak-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/mac-system-ui-trak-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/midword-break-before-surrogate-pair-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/midword-break-before-surrogate-pair-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/midword-break-before-surrogate-pair-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/midword-break-before-surrogate-pair-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/selection/atsui-partial-selection-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/atsui-partial-selection-expected.png
new file mode 100644
index 0000000..6e90dbd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/atsui-partial-selection-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/selection/emphasis-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/emphasis-expected.png
new file mode 100644
index 0000000..af48c0db
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/emphasis-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-nested-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-nested-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-nested-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-nested-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-nested-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-nested-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/flexbox-selection-nested-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/selection/flexbox-selection-nested-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/selection/justified-selection-at-edge-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/justified-selection-at-edge-expected.png
new file mode 100644
index 0000000..0254daf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/justified-selection-at-edge-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/selection/justified-selection-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/justified-selection-expected.png
new file mode 100644
index 0000000..ead45108
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/justified-selection-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/khmer-selection-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/khmer-selection-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/khmer-selection-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/selection/khmer-selection-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/mixed-directionality-selection-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/mixed-directionality-selection-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/selection/mixed-directionality-selection-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/selection/mixed-directionality-selection-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/selection/selection-multiple-runs-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/selection-multiple-runs-expected.png
new file mode 100644
index 0000000..831359be
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/selection-multiple-runs-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/selection/text-selection-inline-block-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/text-selection-inline-block-expected.png
new file mode 100644
index 0000000..9bb2c7a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/text-selection-inline-block-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/selection/text-selection-inline-block-rtl-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/text-selection-inline-block-rtl-expected.png
new file mode 100644
index 0000000..656183c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/win7/paint/selection/text-selection-inline-block-rtl-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/textIteratorNilRenderer-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/textIteratorNilRenderer-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/textIteratorNilRenderer-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/textIteratorNilRenderer-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/textIteratorNilRenderer-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/textIteratorNilRenderer-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/textIteratorNilRenderer-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/textIteratorNilRenderer-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/unicode-fallback-font-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/unicode-fallback-font-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/unicode-fallback-font-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/unicode-fallback-font-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/unicode-fallback-font-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/unicode-fallback-font-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/unicode-fallback-font-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/unicode-fallback-font-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/updateNewFont-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/updateNewFont-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/updateNewFont-expected.png
rename to third_party/WebKit/LayoutTests/platform/win7/paint/updateNewFont-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/updateNewFont-expected.txt b/third_party/WebKit/LayoutTests/platform/win7/paint/updateNewFont-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac-mac10.11/fast/text/updateNewFont-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win7/paint/updateNewFont-expected.txt
diff --git a/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js b/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js
index d3c0460..c9abd5b 100644
--- a/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js
+++ b/third_party/WebKit/LayoutTests/vr/resources/mock-vr-service.js
@@ -21,11 +21,20 @@
         device.mojom.VRDisplayFrameTransportMethod.SUBMIT_AS_MAILBOX_HOLDER;
     options.waitForTransferNotification = true;
     options.waitForRenderNotification = true;
+
+    let magicWindowPtr = new device.mojom.VRMagicWindowProviderPtr();
+    let magicWindowRequest = mojo.makeRequest(magicWindowPtr);
+    let magicWindowBinding = new mojo.Binding(
+        device.mojom.VRMagicWindowProvider, this, magicWindowRequest);
+
     return Promise.resolve({
-      connection: {
-        clientRequest: request,
-        provider: provider,
-        transportOptions: options
+      session: {
+        connection: {
+          clientRequest: request,
+          provider: provider,
+          transportOptions: options
+        },
+        magicWindowProvider: magicWindowPtr
       }
     });
   }
@@ -80,14 +89,9 @@
     let displayBinding =
         new mojo.Binding(device.mojom.VRDisplayHost, this, displayRequest);
 
-    let magicWindowPtr = new device.mojom.VRMagicWindowProviderPtr();
-    let magicWindowRequest = mojo.makeRequest(magicWindowPtr);
-    let magicWindowBinding = new mojo.Binding(
-        device.mojom.VRMagicWindowProvider, this, magicWindowRequest);
-
     let clientRequest = mojo.makeRequest(this.displayClient_);
-    this.service_.client_.onDisplayConnected(magicWindowPtr, displayPtr,
-        clientRequest, this.displayInfo_);
+    this.service_.client_.onDisplayConnected(
+        displayPtr, clientRequest, this.displayInfo_);
   }
 }
 
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index 4a3113cc..541998aad 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -7097,6 +7097,7 @@
     getter actualBoundingBoxDescent
     getter actualBoundingBoxLeft
     getter actualBoundingBoxRight
+    getter advances
     getter alphabeticBaseline
     getter emHeightAscent
     getter emHeightDescent
diff --git a/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js b/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
index 2be7b2c..997141c 100644
--- a/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
+++ b/third_party/WebKit/LayoutTests/xr/resources/xr-device-mocking.js
@@ -313,14 +313,25 @@
       options.waitForRenderNotification = true;
 
       let connection;
-      if (result.supportsSession)
+      if (result.supportsSession) {
         connection = {
           clientRequest: this.presentation_provider_.getClientRequest(),
           provider: this.presentation_provider_.bindProvider(sessionOptions),
           transportOptions: options
         };
 
-      return Promise.resolve({connection: connection});
+        let magicWindowPtr = new device.mojom.VRMagicWindowProviderPtr();
+        let magicWindowRequest = mojo.makeRequest(magicWindowPtr);
+        let magicWindowBinding = new mojo.Binding(
+            device.mojom.VRMagicWindowProvider, this, magicWindowRequest);
+
+        return Promise.resolve({
+          session:
+              {connection: connection, magicWindowProvider: magicWindowPtr}
+        });
+      } else {
+        return Promise.resolve({session: null});
+      }
     });
   }
 
@@ -409,14 +420,9 @@
     let displayBinding =
         new mojo.Binding(device.mojom.VRDisplayHost, this, displayRequest);
 
-    let magicWindowPtr = new device.mojom.VRMagicWindowProviderPtr();
-    let magicWindowRequest = mojo.makeRequest(magicWindowPtr);
-    let magicWindowBinding = new mojo.Binding(
-        device.mojom.VRMagicWindowProvider, this, magicWindowRequest);
-
     let clientRequest = mojo.makeRequest(this.displayClient_);
     this.service_.client_.onDisplayConnected(
-        magicWindowPtr, displayPtr, clientRequest, this.displayInfo_);
+        displayPtr, clientRequest, this.displayInfo_);
   }
 
   addInputSource(input_source) {
diff --git a/third_party/blink/public/platform/web_surface_layer_bridge.h b/third_party/blink/public/platform/web_surface_layer_bridge.h
index ed484b0..2b3d981 100644
--- a/third_party/blink/public/platform/web_surface_layer_bridge.h
+++ b/third_party/blink/public/platform/web_surface_layer_bridge.h
@@ -5,6 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_SURFACE_LAYER_BRIDGE_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_SURFACE_LAYER_BRIDGE_H_
 
+#include <memory>
+
+#include "cc/layers/surface_layer.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_layer_tree_view.h"
@@ -22,7 +25,7 @@
   virtual void UnregisterContentsLayer(cc::Layer*) = 0;
 
   // Called when a SurfaceLayer is activated.
-  virtual void OnSurfaceIdUpdated(viz::SurfaceId surface_id){};
+  virtual void OnSurfaceIdUpdated(viz::SurfaceId surface_id) {}
 };
 
 // Maintains and exposes the SurfaceLayer.
@@ -30,14 +33,15 @@
  public:
   static std::unique_ptr<WebSurfaceLayerBridge> Create(
       WebLayerTreeView*,
-      WebSurfaceLayerBridgeObserver*);
+      WebSurfaceLayerBridgeObserver*,
+      cc::UpdateSubmissionStateCB);
   virtual ~WebSurfaceLayerBridge();
   virtual cc::Layer* GetCcLayer() const = 0;
   virtual const viz::FrameSinkId& GetFrameSinkId() const = 0;
+  virtual const viz::SurfaceId& GetSurfaceId() const = 0;
   virtual void ClearSurfaceId() = 0;
   virtual void SetContentsOpaque(bool) = 0;
   virtual void CreateSurfaceLayer() = 0;
-  virtual const viz::SurfaceId& GetSurfaceId() const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/platform/web_video_frame_submitter.h b/third_party/blink/public/platform/web_video_frame_submitter.h
index 6b69062..b3eb085 100644
--- a/third_party/blink/public/platform/web_video_frame_submitter.h
+++ b/third_party/blink/public/platform/web_video_frame_submitter.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_VIDEO_FRAME_SUBMITTER_H_
 
 #include "cc/layers/video_frame_provider.h"
-#include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/surface_id.h"
 #include "media/base/video_rotation.h"
 #include "third_party/blink/public/platform/web_common.h"
 
@@ -46,10 +46,14 @@
   virtual void SetRotation(media::VideoRotation) = 0;
 
   // Prepares the compositor frame sink to accept frames by providing
-  // a FrameSinkId. The callback is to be used when on context loss to prevent
+  // a SurfaceId. The callback is to be used when on context loss to prevent
   // the submitter from continuing to submit frames with invalid resources.
-  virtual void EnableSubmission(viz::FrameSinkId,
+  virtual void EnableSubmission(viz::SurfaceId,
                                 WebFrameSinkDestroyedCallback) = 0;
+
+  // Updates whether we should submit frames or not based on whether the video
+  // is visible on screen.
+  virtual void UpdateSubmissionState(bool) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 355d039..5f8f288 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -39,7 +39,6 @@
 #include "build/build_config.h"
 #include "cc/layers/picture_layer.h"
 #include "third_party/blink/public/mojom/page/page_visibility_state.mojom-blink.h"
-#include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/web_float_point.h"
 #include "third_party/blink/public/platform/web_image.h"
 #include "third_party/blink/public/platform/web_input_event.h"
@@ -94,6 +93,7 @@
 #include "third_party/blink/renderer/core/frame/browser_controls.h"
 #include "third_party/blink/renderer/core/frame/event_handler_registry.h"
 #include "third_party/blink/renderer/core/frame/fullscreen_controller.h"
+#include "third_party/blink/renderer/core/frame/link_highlights.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
@@ -139,7 +139,6 @@
 #include "third_party/blink/renderer/core/page/validation_message_client_impl.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h"
-#include "third_party/blink/renderer/core/paint/link_highlight_impl.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
@@ -148,7 +147,6 @@
 #include "third_party/blink/renderer/platform/cursor.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
-#include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_mutator_client.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_mutator_impl.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
@@ -379,11 +377,6 @@
 
 WebViewImpl::~WebViewImpl() {
   DCHECK(!page_);
-
-  // Each highlight uses m_owningWebViewImpl->m_linkHighlightsTimeline
-  // in destructor. m_linkHighlightsTimeline might be destroyed earlier
-  // than m_linkHighlights.
-  DCHECK(link_highlights_.IsEmpty());
 }
 
 ValidationMessageClient* WebViewImpl::GetValidationMessageClient() const {
@@ -602,8 +595,7 @@
     case WebInputEvent::kGestureTapCancel:
     case WebInputEvent::kGestureTap:
     case WebInputEvent::kGestureLongPress:
-      for (size_t i = 0; i < link_highlights_.size(); ++i)
-        link_highlights_[i]->StartHighlightAnimationIfNeeded();
+      GetPage()->GetLinkHighlights().StartHighlightAnimationIfNeeded();
       break;
     default:
       break;
@@ -651,8 +643,7 @@
                 RoundedIntSize(targeted_event.GetHitTestResult().LocalPoint());
 
             EnableTapHighlights(highlight_nodes);
-            for (size_t i = 0; i < link_highlights_.size(); ++i)
-              link_highlights_[i]->StartHighlightAnimationIfNeeded();
+            GetPage()->GetLinkHighlights().StartHighlightAnimationIfNeeded();
             event_result = WebInputEventResult::kHandledSystem;
             event_cancelled = true;
             break;
@@ -1245,30 +1236,7 @@
 
 void WebViewImpl::EnableTapHighlights(
     HeapVector<Member<Node>>& highlight_nodes) {
-  if (highlight_nodes.IsEmpty())
-    return;
-
-  // Always clear any existing highlight when this is invoked, even if we
-  // don't get a new target to highlight.
-  link_highlights_.clear();
-
-  for (size_t i = 0; i < highlight_nodes.size(); ++i) {
-    Node* node = highlight_nodes[i];
-
-    if (!node || !node->GetLayoutObject())
-      continue;
-
-    Color highlight_color =
-        node->GetLayoutObject()->Style()->TapHighlightColor();
-    // Safari documentation for -webkit-tap-highlight-color says if the
-    // specified color has 0 alpha, then tap highlighting is disabled.
-    // http://developer.apple.com/library/safari/#documentation/appleapplications/reference/safaricssref/articles/standardcssproperties.html
-    if (!highlight_color.Alpha())
-      continue;
-
-    link_highlights_.push_back(LinkHighlightImpl::Create(node, this));
-  }
-
+  GetPage()->GetLinkHighlights().SetTapHighlights(highlight_nodes);
   UpdateAllLifecyclePhases();
 }
 
@@ -1766,8 +1734,8 @@
 
   // TODO(chrishtr): link highlights don't currently paint themselves, it's
   // still driven by cc.  Fix this.
-  for (size_t i = 0; i < link_highlights_.size(); ++i)
-    link_highlights_[i]->UpdateGeometry();
+  // TODO(pdr): Move this to LocalFrameView::UpdateLifecyclePhasesInternal.
+  GetPage()->GetLinkHighlights().UpdateGeometry();
 
   if (LocalFrameView* view = MainFrameImpl()->GetFrameView()) {
     LocalFrame* frame = MainFrameImpl()->GetFrame();
@@ -2118,12 +2086,6 @@
 }
 
 void WebViewImpl::WillCloseLayerTreeView() {
-  if (link_highlights_timeline_) {
-    link_highlights_.clear();
-    DetachCompositorAnimationTimeline(link_highlights_timeline_.get());
-    link_highlights_timeline_.reset();
-  }
-
   if (layer_tree_view_)
     GetPage()->WillCloseLayerTreeView(*layer_tree_view_, nullptr);
 
@@ -3157,7 +3119,8 @@
   GetPage()->GetVisualViewport().MainFrameDidChangeSize();
 
   // Make sure link highlight from previous page is cleared.
-  link_highlights_.clear();
+  // TODO(pdr): Move this to Page::DidCommitLoad.
+  GetPage()->GetLinkHighlights().ResetForPageNavigation();
   if (!MainFrameImpl())
     return;
 }
@@ -3448,18 +3411,6 @@
     client_->WidgetClient()->ScheduleAnimation();
 }
 
-void WebViewImpl::AttachCompositorAnimationTimeline(
-    CompositorAnimationTimeline* timeline) {
-  if (animation_host_)
-    animation_host_->AddTimeline(*timeline);
-}
-
-void WebViewImpl::DetachCompositorAnimationTimeline(
-    CompositorAnimationTimeline* timeline) {
-  if (animation_host_)
-    animation_host_->RemoveTimeline(*timeline);
-}
-
 void WebViewImpl::InitializeLayerTreeView() {
   if (client_) {
     layer_tree_view_ = client_->InitializeLayerTreeView();
@@ -3485,11 +3436,6 @@
   // hit this code and then delete allowsBrokenNullLayerTreeView.
   DCHECK(layer_tree_view_ || !client_ ||
          client_->WidgetClient()->AllowsBrokenNullLayerTreeView());
-
-  if (Platform::Current()->IsThreadedAnimationEnabled() && layer_tree_view_) {
-    link_highlights_timeline_ = CompositorAnimationTimeline::Create();
-    AttachCompositorAnimationTimeline(link_highlights_timeline_.get());
-  }
 }
 
 void WebViewImpl::ApplyViewportDeltas(
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index f35cbf2..1126c8c 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -56,7 +56,6 @@
 #include "third_party/blink/renderer/core/page/event_with_hit_test_results.h"
 #include "third_party/blink/renderer/core/page/page_widget_delegate.h"
 #include "third_party/blink/renderer/core/page/scoped_page_pauser.h"
-#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
 #include "third_party/blink/renderer/platform/geometry/int_point.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
@@ -78,7 +77,6 @@
 class DevToolsEmulator;
 class Frame;
 class FullscreenController;
-class LinkHighlightImpl;
 class PageOverlay;
 class PageScaleConstraintsSet;
 class PaintLayerCompositor;
@@ -340,9 +338,6 @@
   GraphicsLayer* RootGraphicsLayer();
   void RegisterViewportLayersWithCompositor();
   PaintLayerCompositor* Compositor() const;
-  CompositorAnimationTimeline* LinkHighlightsTimeline() const {
-    return link_highlights_timeline_.get();
-  }
 
   PageScheduler* Scheduler() const override;
   void SetVisibilityState(mojom::PageVisibilityState, bool) override;
@@ -397,12 +392,6 @@
   bool HasHorizontalScrollbar();
   bool HasVerticalScrollbar();
 
-  // Exposed for tests.
-  unsigned NumLinkHighlights() { return link_highlights_.size(); }
-  LinkHighlightImpl* GetLinkHighlight(int i) {
-    return link_highlights_[i].get();
-  }
-
   WebSettingsImpl* SettingsImpl();
 
   // Returns the bounding box of the block type node touched by the WebPoint.
@@ -540,8 +529,6 @@
 
   void SetRootGraphicsLayer(GraphicsLayer*);
   void SetRootLayer(scoped_refptr<cc::Layer>);
-  void AttachCompositorAnimationTimeline(CompositorAnimationTimeline*);
-  void DetachCompositorAnimationTimeline(CompositorAnimationTimeline*);
 
   LocalFrame* FocusedLocalFrameInWidget() const;
   LocalFrame* FocusedLocalFrameAvailableForIme() const;
@@ -652,8 +639,6 @@
   GraphicsLayer* visual_viewport_container_layer_;
   bool matches_heuristics_for_gpu_rasterization_;
 
-  Vector<std::unique_ptr<LinkHighlightImpl>> link_highlights_;
-  std::unique_ptr<CompositorAnimationTimeline> link_highlights_timeline_;
   std::unique_ptr<FullscreenController> fullscreen_controller_;
 
   WebPoint last_tap_disambiguation_best_candidate_position_;
diff --git a/third_party/blink/renderer/core/frame/BUILD.gn b/third_party/blink/renderer/core/frame/BUILD.gn
index 9343b9ac..faf80c78 100644
--- a/third_party/blink/renderer/core/frame/BUILD.gn
+++ b/third_party/blink/renderer/core/frame/BUILD.gn
@@ -78,6 +78,8 @@
     "intervention_report_body.h",
     "layout_subtree_root_list.cc",
     "layout_subtree_root_list.h",
+    "link_highlights.cc",
+    "link_highlights.h",
     "local_dom_window.cc",
     "local_dom_window.h",
     "local_frame.cc",
diff --git a/third_party/blink/renderer/core/frame/frame_view.h b/third_party/blink/renderer/core/frame/frame_view.h
index 429a2e0..3ea14499 100644
--- a/third_party/blink/renderer/core/frame/frame_view.h
+++ b/third_party/blink/renderer/core/frame/frame_view.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_VIEW_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FRAME_VIEW_H_
 
+#include "third_party/blink/renderer/core/dom/document_lifecycle.h"
 #include "third_party/blink/renderer/core/frame/embedded_content_view.h"
 
 namespace blink {
@@ -14,7 +15,8 @@
 class CORE_EXPORT FrameView : public EmbeddedContentView {
  public:
   ~FrameView() override = default;
-  virtual void UpdateViewportIntersectionsForSubtree() = 0;
+  virtual void UpdateViewportIntersectionsForSubtree(
+      DocumentLifecycle::LifecycleState) = 0;
 
   virtual bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const = 0;
   virtual bool HasIntrinsicSizingInfo() const = 0;
diff --git a/third_party/blink/renderer/core/frame/link_highlights.cc b/third_party/blink/renderer/core/frame/link_highlights.cc
new file mode 100644
index 0000000..34f1ffce
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/link_highlights.cc
@@ -0,0 +1,108 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/frame/link_highlights.h"
+
+#include <memory>
+
+#include "cc/layers/picture_layer.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/platform/web_layer_tree_view.h"
+#include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
+#include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/core/paint/link_highlight_impl.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_host.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
+
+namespace blink {
+
+LinkHighlights::LinkHighlights(Page& owner) : page_(&owner) {}
+
+LinkHighlights::~LinkHighlights() {
+  RemoveAllHighlights();
+}
+
+void LinkHighlights::Trace(blink::Visitor* visitor) {
+  visitor->Trace(page_);
+}
+
+void LinkHighlights::RemoveAllHighlights() {
+  if (timeline_) {
+    for (auto& highlight : link_highlights_)
+      timeline_->AnimationDestroyed(*highlight);
+  }
+  link_highlights_.clear();
+}
+
+void LinkHighlights::ResetForPageNavigation() {
+  RemoveAllHighlights();
+}
+
+void LinkHighlights::SetTapHighlights(
+    HeapVector<Member<Node>>& highlight_nodes) {
+  // Always clear any existing highlight when this is invoked, even if we
+  // don't get a new target to highlight.
+  RemoveAllHighlights();
+
+  for (size_t i = 0; i < highlight_nodes.size(); ++i) {
+    Node* node = highlight_nodes[i];
+
+    if (!node || !node->GetLayoutObject())
+      continue;
+
+    Color highlight_color =
+        node->GetLayoutObject()->Style()->TapHighlightColor();
+    // Safari documentation for -webkit-tap-highlight-color says if the
+    // specified color has 0 alpha, then tap highlighting is disabled.
+    // http://developer.apple.com/library/safari/#documentation/appleapplications/reference/safaricssref/articles/standardcssproperties.html
+    if (!highlight_color.Alpha())
+      continue;
+
+    link_highlights_.push_back(LinkHighlightImpl::Create(node));
+    if (timeline_)
+      timeline_->AnimationAttached(*link_highlights_.back());
+  }
+}
+
+void LinkHighlights::UpdateGeometry() {
+  for (auto& highlight : link_highlights_)
+    highlight->UpdateGeometry();
+}
+
+LocalFrame* LinkHighlights::MainFrame() const {
+  return GetPage().MainFrame() && GetPage().MainFrame()->IsLocalFrame()
+             ? GetPage().DeprecatedLocalMainFrame()
+             : nullptr;
+}
+
+void LinkHighlights::StartHighlightAnimationIfNeeded() {
+  for (auto& highlight : link_highlights_)
+    highlight->StartHighlightAnimationIfNeeded();
+
+  if (auto* local_frame = MainFrame())
+    GetPage().GetChromeClient().ScheduleAnimation(local_frame->View());
+}
+
+void LinkHighlights::LayerTreeViewInitialized(
+    WebLayerTreeView& layer_tree_view) {
+  if (Platform::Current()->IsThreadedAnimationEnabled()) {
+    timeline_ = CompositorAnimationTimeline::Create();
+    animation_host_ = std::make_unique<CompositorAnimationHost>(
+        layer_tree_view.CompositorAnimationHost());
+    animation_host_->AddTimeline(*timeline_);
+  }
+}
+
+void LinkHighlights::WillCloseLayerTreeView(WebLayerTreeView& layer_tree_view) {
+  RemoveAllHighlights();
+  if (timeline_) {
+    animation_host_->RemoveTimeline(*timeline_);
+    timeline_.reset();
+  }
+  animation_host_ = nullptr;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/link_highlights.h b/third_party/blink/renderer/core/frame/link_highlights.h
new file mode 100644
index 0000000..2eb1e31
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/link_highlights.h
@@ -0,0 +1,66 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LINK_HIGHLIGHTS_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LINK_HIGHLIGHTS_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+class Page;
+class LinkHighlightImpl;
+class CompositorAnimationHost;
+class CompositorAnimationTimeline;
+class WebLayerTreeView;
+class LocalFrame;
+
+class CORE_EXPORT LinkHighlights final
+    : public GarbageCollectedFinalized<LinkHighlights> {
+ public:
+  static LinkHighlights* Create(Page& page) { return new LinkHighlights(page); }
+  virtual ~LinkHighlights();
+
+  virtual void Trace(blink::Visitor*);
+
+  void ResetForPageNavigation();
+
+  void SetTapHighlights(HeapVector<Member<Node>>&);
+
+  void UpdateGeometry();
+
+  void StartHighlightAnimationIfNeeded();
+
+  void LayerTreeViewInitialized(WebLayerTreeView&);
+  void WillCloseLayerTreeView(WebLayerTreeView&);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(LinkHighlightImplTest, verifyWebViewImplIntegration);
+  FRIEND_TEST_ALL_PREFIXES(LinkHighlightImplTest, resetDuringNodeRemoval);
+  FRIEND_TEST_ALL_PREFIXES(LinkHighlightImplTest, resetLayerTreeView);
+  FRIEND_TEST_ALL_PREFIXES(LinkHighlightImplTest, multipleHighlights);
+
+  explicit LinkHighlights(Page&);
+
+  void RemoveAllHighlights();
+
+  LocalFrame* MainFrame() const;
+
+  Page& GetPage() const {
+    DCHECK(page_);
+    return *page_;
+  }
+
+  Member<Page> page_;
+  Vector<std::unique_ptr<LinkHighlightImpl>> link_highlights_;
+  std::unique_ptr<CompositorAnimationHost> animation_host_;
+  std::unique_ptr<CompositorAnimationTimeline> timeline_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_LINK_HIGHLIGHTS_H_
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index d53247c..9844b6b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2379,7 +2379,8 @@
       &current_update_lifecycle_phases_target_state_, target_state);
 
   if (ShouldThrottleRendering()) {
-    UpdateThrottlingStatusForSubtree();
+    UpdateViewportIntersectionsForSubtree(
+        std::min(target_state, DocumentLifecycle::kCompositingClean));
     return Lifecycle().GetState() == target_state;
   }
 
@@ -2409,7 +2410,7 @@
   }
 
   if (target_state == DocumentLifecycle::kLayoutClean) {
-    UpdateThrottlingStatusForSubtree();
+    UpdateViewportIntersectionsForSubtree(target_state);
     return Lifecycle().GetState() == target_state;
   }
 
@@ -2419,9 +2420,7 @@
   // OOPIF local frame roots that are throttled can return now that layout
   // is clean and intersection observations can be calculated.
   if (ShouldThrottleRendering()) {
-    if (target_state == DocumentLifecycle::kPaintClean)
-      UpdateViewportIntersectionsForSubtree();
-    UpdateThrottlingStatusForSubtree();
+    UpdateViewportIntersectionsForSubtree(target_state);
     return Lifecycle().GetState() == target_state;
   }
 
@@ -2442,8 +2441,6 @@
       frame_view.allows_layout_invalidation_after_layout_clean_ = false;
     });
 
-    bool should_update_intersection_observations = false;
-
     {
       TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
                            "SetLayerTreeId", TRACE_EVENT_SCOPE_THREAD, "data",
@@ -2467,13 +2464,6 @@
             *this);
       }
 
-      // Save off this value now, since PrePaintTreeWalk will clear out
-      // the NeedsPaintPropertyUpdate bits.
-      should_update_intersection_observations =
-          layout_view->NeedsPaintPropertyUpdate() ||
-          layout_view->DescendantNeedsPaintPropertyUpdate() ||
-          needs_intersection_observation_;
-
       if (target_state >= DocumentLifecycle::kPrePaintClean) {
         UpdateCompositedSelectionIfNeeded();
 
@@ -2523,14 +2513,6 @@
         }
       }
 
-      if (should_update_intersection_observations) {
-        TRACE_EVENT0("blink,benchmark",
-                     "LocalFrameView::UpdateViewportIntersectionsForSubtree");
-        SCOPED_UMA_AND_UKM_TIMER("Blink.IntersectionObservation.UpdateTime",
-                                 UkmMetricNames::kIntersectionObservation);
-        UpdateViewportIntersectionsForSubtree();
-      }
-
       DCHECK(!frame_->Selection().NeedsLayoutSelectionUpdate());
       DCHECK(ShouldThrottleRendering() ||
              (frame_->GetDocument()->Printing() &&
@@ -2544,7 +2526,13 @@
     });
   }
 
-  UpdateThrottlingStatusForSubtree();
+  {
+    TRACE_EVENT0("blink,benchmark",
+                 "LocalFrameView::UpdateViewportIntersectionsForSubtree");
+    SCOPED_UMA_AND_UKM_TIMER("Blink.IntersectionObservation.UpdateTime",
+                             UkmMetricNames::kIntersectionObservation);
+    UpdateViewportIntersectionsForSubtree(target_state);
+  }
 
   return Lifecycle().GetState() == target_state;
 }
@@ -2593,7 +2581,6 @@
   {
     SCOPED_UMA_AND_UKM_TIMER("Blink.PrePaint.UpdateTime",
                              UkmMetricNames::kPrePaint);
-
     PrePaintTreeWalk().WalkTree(*this);
   }
 
@@ -3880,7 +3867,8 @@
     CollectAnnotatedRegions(*curr, regions);
 }
 
-void LocalFrameView::UpdateViewportIntersectionsForSubtree() {
+void LocalFrameView::UpdateViewportIntersectionsForSubtree(
+    DocumentLifecycle::LifecycleState target_state) {
   // TODO(dcheng): Since LocalFrameView tree updates are deferred, FrameViews
   // might still be in the LocalFrameView hierarchy even though the associated
   // Document is already detached. Investigate if this check and a similar check
@@ -3889,29 +3877,19 @@
   if (!GetFrame().GetDocument()->IsActive())
     return;
 
-  RecordDeferredLoadingStats();
-  if (!NeedsLayout()) {
-    // Notify javascript IntersectionObservers
-    if (GetFrame().GetDocument()->GetIntersectionObserverController()) {
-      GetFrame()
-          .GetDocument()
-          ->GetIntersectionObserverController()
-          ->ComputeTrackedIntersectionObservations();
+  if (target_state == DocumentLifecycle::kPaintClean) {
+    RecordDeferredLoadingStats();
+    if (!NeedsLayout()) {
+      // Notify javascript IntersectionObservers
+      if (GetFrame().GetDocument()->GetIntersectionObserverController()) {
+        GetFrame()
+            .GetDocument()
+            ->GetIntersectionObserverController()
+            ->ComputeTrackedIntersectionObservations();
+      }
     }
   }
 
-  for (Frame* child = frame_->Tree().FirstChild(); child;
-       child = child->Tree().NextSibling()) {
-    child->View()->UpdateViewportIntersectionsForSubtree();
-  }
-
-  needs_intersection_observation_ = false;
-}
-
-void LocalFrameView::UpdateThrottlingStatusForSubtree() {
-  if (!GetFrame().GetDocument()->IsActive())
-    return;
-
   // Don't throttle display:none frames (see updateRenderThrottlingStatus).
   HTMLFrameOwnerElement* owner_element = frame_->DeprecatedLocalOwner();
   if (hidden_for_throttling_ && owner_element &&
@@ -3923,9 +3901,11 @@
                                  kDontNotifyChildren);
   }
 
-  ForAllChildLocalFrameViews([](LocalFrameView& child_view) {
-    child_view.UpdateThrottlingStatusForSubtree();
-  });
+  for (Frame* child = frame_->Tree().FirstChild(); child;
+       child = child->Tree().NextSibling()) {
+    child->View()->UpdateViewportIntersectionsForSubtree(target_state);
+  }
+  needs_intersection_observation_ = false;
 }
 
 void LocalFrameView::UpdateRenderThrottlingStatusForTesting() {
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 30519b5..5c4d983 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -734,8 +734,8 @@
   template <typename Function>
   void ForAllNonThrottledLocalFrameViews(const Function&);
 
-  void UpdateViewportIntersectionsForSubtree() override;
-  void UpdateThrottlingStatusForSubtree();
+  void UpdateViewportIntersectionsForSubtree(
+      DocumentLifecycle::LifecycleState) override;
 
   void NotifyResizeObservers();
 
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc
index 47f1be0..f3f61fa1 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -63,9 +63,12 @@
   return view;
 }
 
-void RemoteFrameView::UpdateViewportIntersectionsForSubtree() {
+void RemoteFrameView::UpdateViewportIntersectionsForSubtree(
+    DocumentLifecycle::LifecycleState target_state) {
   if (!remote_frame_->OwnerLayoutObject())
     return;
+  if (target_state < DocumentLifecycle::kPaintClean)
+    return;
 
   LocalFrameView* local_root_view =
       ToLocalFrame(remote_frame_->Tree().Parent())->LocalFrameRoot().View();
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.h b/third_party/blink/renderer/core/frame/remote_frame_view.h
index 25b94e7..1f6fba0e 100644
--- a/third_party/blink/renderer/core/frame/remote_frame_view.h
+++ b/third_party/blink/renderer/core/frame/remote_frame_view.h
@@ -56,7 +56,8 @@
   void Show() override;
   void SetParentVisible(bool) override;
 
-  void UpdateViewportIntersectionsForSubtree() override;
+  void UpdateViewportIntersectionsForSubtree(
+      DocumentLifecycle::LifecycleState) override;
 
   bool GetIntrinsicSizingInfo(IntrinsicSizingInfo&) const override;
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 2478cce..d22074bd 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -29,7 +29,9 @@
 
 #include <math.h>
 
+#include <limits>
 #include <memory>
+#include <utility>
 
 #include "base/location.h"
 #include "base/numerics/checked_math.h"
@@ -1427,8 +1429,8 @@
   if (frame) {
     layer_tree_view =
         frame->GetPage()->GetChromeClient().GetWebLayerTreeView(frame);
-    surface_layer_bridge_ =
-        std::make_unique<::blink::SurfaceLayerBridge>(layer_tree_view, this);
+    surface_layer_bridge_ = std::make_unique<::blink::SurfaceLayerBridge>(
+        layer_tree_view, this, base::DoNothing());
     // Creates a placeholder layer first before Surface is created.
     surface_layer_bridge_->CreateSolidColorLayer();
   }
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.cc b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
index 1639dce..864bfe6 100644
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.cc
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/html/canvas/text_metrics.h"
+#include "third_party/blink/renderer/platform/fonts/character_range.h"
 
 namespace blink {
 
@@ -64,6 +65,12 @@
   FloatRect bbox = font.BoundingBox(text_run);
   const FontMetrics& font_metrics = font_data->GetFontMetrics();
 
+  Vector<CharacterRange> ranges = font.IndividualCharacterRanges(text_run);
+  advances_.resize(ranges.size());
+  for (unsigned i = 0; i < ranges.size(); i++) {
+    advances_[i] = ranges[i].start;
+  }
+
   // x direction
   width_ = bbox.Width();
 
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.h b/third_party/blink/renderer/core/html/canvas/text_metrics.h
index c132aa2..51fc4e388 100644
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.h
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.h
@@ -51,6 +51,7 @@
   }
 
   double width() const { return width_; }
+  const Vector<double>& advances() const { return advances_; }
   double actualBoundingBoxLeft() const { return actual_bounding_box_left_; }
   double actualBoundingBoxRight() const { return actual_bounding_box_right_; }
   double fontBoundingBoxAscent() const { return font_bounding_box_ascent_; }
@@ -77,6 +78,7 @@
 
   // x-direction
   double width_ = 0.0;
+  Vector<double> advances_;
   double actual_bounding_box_left_ = 0.0;
   double actual_bounding_box_right_ = 0.0;
 
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.idl b/third_party/blink/renderer/core/html/canvas/text_metrics.idl
index e84a831..bcaa5e2 100644
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.idl
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.idl
@@ -29,6 +29,7 @@
 interface TextMetrics {
     // x-direction
     readonly attribute float width; // advance width
+    [RuntimeEnabled=ExtendedTextMetrics] readonly attribute FrozenArray<double> advances;
     [RuntimeEnabled=ExtendedTextMetrics] readonly attribute double actualBoundingBoxLeft;
     [RuntimeEnabled=ExtendedTextMetrics] readonly attribute double actualBoundingBoxRight;
 
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index 6389fbd..bb8a4d45 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -683,7 +683,9 @@
         if (LineDirectionPointFitsInBox(point_line_direction.ToInt(), box,
                                         should_affinity_be_downstream)) {
           return CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(
-              box, box->OffsetForPosition(point_line_direction),
+              box,
+              box->OffsetForPosition(point_line_direction, IncludePartialGlyphs,
+                                     BreakGlyphs),
               should_affinity_be_downstream);
         }
       }
@@ -697,7 +699,9 @@
                                 should_affinity_be_downstream);
     return CreatePositionWithAffinityForBoxAfterAdjustingOffsetForBiDi(
         last_box,
-        last_box->OffsetForPosition(point_line_direction) + last_box->Start(),
+        last_box->OffsetForPosition(point_line_direction, IncludePartialGlyphs,
+                                    BreakGlyphs) +
+            last_box->Start(),
         should_affinity_be_downstream);
   }
   return CreatePositionWithAffinity(0);
diff --git a/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h b/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h
index a1ccf5b4..9a3994b 100644
--- a/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h
+++ b/third_party/blink/renderer/core/layout/line/breaking_context_inline_headers.h
@@ -866,7 +866,8 @@
   x_pos_to_break += LayoutUnit::Epsilon();
   if (run.Rtl())
     x_pos_to_break = word_measurement.width - x_pos_to_break;
-  len = font.OffsetForPosition(run, x_pos_to_break, false);
+  len = font.OffsetForPosition(run, x_pos_to_break, OnlyFullGlyphs,
+                               DontBreakGlyphs);
   int end = start + len;
   if (len) {
     end = break_iterator.PreviousBreakOpportunity(end, start);
@@ -908,8 +909,9 @@
   TextRun run = ConstructTextRun(font, text, start, len, style);
   run.SetTabSize(!collapse_white_space_, style.GetTabSize());
   run.SetXPos(width_.CurrentWidth());
-  unsigned max_prefix_length =
-      font.OffsetForPosition(run, max_prefix_width, false);
+  // TODO(fserb): Check if this need to be BreakGlyphs.
+  unsigned max_prefix_length = font.OffsetForPosition(
+      run, max_prefix_width, OnlyFullGlyphs, DontBreakGlyphs);
   if (max_prefix_length < Hyphenation::kMinimumPrefixLength)
     return false;
 
diff --git a/third_party/blink/renderer/core/layout/line/inline_text_box.cc b/third_party/blink/renderer/core/layout/line/inline_text_box.cc
index 17d3cca7..80dca35b5 100644
--- a/third_party/blink/renderer/core/layout/line/inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_text_box.cc
@@ -424,7 +424,10 @@
     // more accurate position in rtl text.
     // TODO(crbug.com/722043: This doesn't always give the best results.
     bool ltr = IsLeftToRightDirection();
-    int offset = OffsetForPosition(ellipsis_x, !ltr);
+    int offset = OffsetForPosition(ellipsis_x,
+                                   ltr ? OnlyFullGlyphs : IncludePartialGlyphs,
+                                   DontBreakGlyphs);
+
     // Full truncation is only necessary when we're flowing left-to-right.
     if (flow_is_ltr && offset == 0 && ltr == flow_is_ltr) {
       // No characters should be laid out.  Set ourselves to full truncation and
@@ -625,7 +628,8 @@
 }
 
 int InlineTextBox::OffsetForPosition(LayoutUnit line_offset,
-                                     bool include_partial_glyphs) const {
+                                     IncludePartialGlyphsOption partial_glyphs,
+                                     BreakGlyphsOption break_glyphs) const {
   if (IsLineBreak())
     return 0;
 
@@ -639,7 +643,7 @@
   const Font& font = style.GetFont();
   return font.OffsetForPosition(ConstructTextRun(style),
                                 (line_offset - LogicalLeft()).ToFloat(),
-                                include_partial_glyphs);
+                                partial_glyphs, break_glyphs);
 }
 
 LayoutUnit InlineTextBox::PositionForOffset(int offset) const {
diff --git a/third_party/blink/renderer/core/layout/line/inline_text_box.h b/third_party/blink/renderer/core/layout/line/inline_text_box.h
index cace2c5..fb34617 100644
--- a/third_party/blink/renderer/core/layout/line/inline_text_box.h
+++ b/third_party/blink/renderer/core/layout/line/inline_text_box.h
@@ -206,7 +206,8 @@
 
  public:
   virtual int OffsetForPosition(LayoutUnit x,
-                                bool include_partial_glyphs = true) const;
+                                IncludePartialGlyphsOption,
+                                BreakGlyphsOption) const;
   virtual LayoutUnit PositionForOffset(int offset) const;
 
   // Returns false for offset after line break.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 6025b3e0d..5b21a32 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -251,9 +251,10 @@
                           IsLtr(line_info->BaseDirection()) ? 0 : 1, box);
   }
 
-  if (line_box_.IsEmpty() && !container_builder_.UnpositionedListMarker()) {
-    return;  // The line was empty.
-  }
+  // We can return early if we don't have any children (and don't need to
+  // create a line-box for a list marker, etc).
+  if (line_box_.IsEmpty() && line_info->IsEmptyLine())
+    return;
 
   box_states_->OnEndPlaceItems(&line_box_, baseline_type_);
 
@@ -284,12 +285,7 @@
 
   // Handle out-of-flow positioned objects. They need inline offsets for their
   // static positions.
-  if (!PlaceOutOfFlowObjects(*line_info, line_box_metrics) &&
-      !container_builder_.UnpositionedListMarker()) {
-    // If we have out-of-flow objects but nothing else, we don't have line box
-    // metrics nor BFC offset. Exit early.
-    return;
-  }
+  PlaceOutOfFlowObjects(*line_info, line_box_metrics);
 
   // Even if we have something in-flow, it may just be empty items that
   // shouldn't trigger creation of a line. Exit now if that's the case.
@@ -438,10 +434,9 @@
 
 // Place all out-of-flow objects in |line_box_| and clear them.
 // @return whether |line_box_| has any in-flow fragments.
-bool NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects(
+void NGInlineLayoutAlgorithm::PlaceOutOfFlowObjects(
     const NGLineInfo& line_info,
     const NGLineHeightMetrics& line_box_metrics) {
-  bool has_fragments = false;
   for (NGLineBoxFragmentBuilder::Child& child : line_box_) {
     if (LayoutObject* box = child.out_of_flow_positioned_box) {
       // The static position is at the line-top. Ignore the block_offset.
@@ -471,11 +466,8 @@
 
       child.out_of_flow_positioned_box = child.out_of_flow_containing_box =
           nullptr;
-    } else if (!has_fragments) {
-      has_fragments = child.HasFragment();
     }
   }
-  return has_fragments;
 }
 
 // Place a list marker.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h
index 017544f..4fcf7b1 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_layout_algorithm.h
@@ -72,7 +72,7 @@
   void PlaceLayoutResult(NGInlineItemResult*,
                          NGInlineBoxState*,
                          LayoutUnit inline_offset = LayoutUnit());
-  bool PlaceOutOfFlowObjects(const NGLineInfo&, const NGLineHeightMetrics&);
+  void PlaceOutOfFlowObjects(const NGLineInfo&, const NGLineHeightMetrics&);
   void PlaceListMarker(const NGInlineItem&,
                        NGInlineItemResult*,
                        const NGLineInfo&);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
index 05934ae6..2593375 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.cc
@@ -202,9 +202,16 @@
     result.CheckConsistency();
 #endif
 
+  // We should create a line-box when:
+  //  - We have an item which needs a line box (text, etc).
+  //  - A list-marker is present, and it would be the last line or last line
+  //    before a forced new-line.
+  //  - During min/max content sizing (to correctly determine the line width).
+  //
   // TODO(kojii): There are cases where we need to PlaceItems() without creating
   // line boxes. These cases need to be reviewed.
   if (ShouldCreateLineBox(line_info->Results()) ||
+      (has_list_marker_ && line_info->IsLastLine()) ||
       mode_ != NGLineBreakerMode::kContent)
     ComputeLineLocation(line_info);
   else
@@ -284,7 +291,7 @@
       MoveToNextOf(item);
     } else if (item.Type() == NGInlineItem::kListMarker) {
       NGInlineItemResult* item_result = AddItem(item, item_results);
-      item_result->should_create_line_box = true;
+      has_list_marker_ = true;
       DCHECK(!item_result->can_break_after);
       MoveToNextOf(item);
     } else {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
index 103ad53..d223f4d 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
@@ -221,6 +221,9 @@
   // https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk
   bool in_line_height_quirks_mode_ = false;
 
+  // True when the line we are breaking has a list marker.
+  bool has_list_marker_ = false;
+
   bool ignore_floats_ = false;
 };
 
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
index 3ded252..7c70596 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.cc
@@ -192,9 +192,9 @@
   DCHECK(TextShapeResult());
   const LayoutUnit& point_in_line_direction =
       Style().IsHorizontalWritingMode() ? point.left : point.top;
-  const bool include_partial_glyphs = true;
   return TextShapeResult()->OffsetForPosition(point_in_line_direction.ToFloat(),
-                                              include_partial_glyphs) +
+                                              IncludePartialGlyphs,
+                                              BreakGlyphs) +
          StartOffset();
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
index c579125..876df62 100644
--- a/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
+++ b/third_party/blink/renderer/core/layout/ng/list/ng_unpositioned_list_marker.cc
@@ -64,7 +64,16 @@
   // Compute the baseline of the child content.
   NGLineHeightMetrics content_metrics;
   if (content.IsLineBox()) {
-    content_metrics = ToNGPhysicalLineBoxFragment(content).Metrics();
+    const NGPhysicalLineBoxFragment& line_box =
+        ToNGPhysicalLineBoxFragment(content);
+
+    // If this child is an empty line-box, the list marker should be aligned
+    // with the next non-empty line box produced. (This can occur with floats
+    // producing empty line-boxes).
+    if (line_box.Children().IsEmpty() && !line_box.BreakToken()->IsFinished())
+      return false;
+
+    content_metrics = line_box.Metrics();
   } else {
     NGBoxFragment content_fragment(space.GetWritingMode(),
                                    ToNGPhysicalBoxFragment(content));
diff --git a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc
index dfaacf6..6965c06 100644
--- a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.cc
@@ -57,7 +57,9 @@
     next_box->DirtyLineBoxes();
 }
 
-int SVGInlineTextBox::OffsetForPosition(LayoutUnit, bool) const {
+int SVGInlineTextBox::OffsetForPosition(LayoutUnit,
+                                        IncludePartialGlyphsOption,
+                                        BreakGlyphsOption) const {
   // SVG doesn't use the standard offset <-> position selection system, as it's
   // not suitable for SVGs complex needs. Vertical text selection, inline boxes
   // spanning multiple lines (contrary to HTML, etc.)
@@ -80,11 +82,10 @@
   if (fragment.AffectedByTextLength())
     position /= fragment.length_adjust_scale;
 
-  const bool include_partial_glyphs = true;
   TextRun text_run = ConstructTextRun(line_layout_item.StyleRef(), fragment);
   return fragment.character_offset - Start() +
          line_layout_item.ScaledFont().OffsetForPosition(
-             text_run, position, include_partial_glyphs);
+             text_run, position, IncludePartialGlyphs, BreakGlyphs);
 }
 
 LayoutUnit SVGInlineTextBox::PositionForOffset(int) const {
diff --git a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h
index ced60f1..00cbed81 100644
--- a/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h
+++ b/third_party/blink/renderer/core/layout/svg/line/svg_inline_text_box.h
@@ -39,7 +39,8 @@
   void SetLogicalHeight(LayoutUnit height) { logical_height_ = height; }
 
   int OffsetForPosition(LayoutUnit x,
-                        bool include_partial_glyphs = true) const override;
+                        IncludePartialGlyphsOption,
+                        BreakGlyphsOption) const override;
   LayoutUnit PositionForOffset(int offset) const override;
 
   void Paint(const PaintInfo&,
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index c22147f..5f98429 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/core/frame/dom_timer.h"
 #include "third_party/blink/renderer/core/frame/event_handler_registry.h"
 #include "third_party/blink/renderer/core/frame/frame_console.h"
+#include "third_party/blink/renderer/core/frame/link_highlights.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/page_scale_constraints.h"
@@ -162,6 +163,7 @@
       visual_viewport_(VisualViewport::Create(*this)),
       overscroll_controller_(
           OverscrollController::Create(GetVisualViewport(), GetChromeClient())),
+      link_highlights_(LinkHighlights::Create(*this)),
       plugin_data_(nullptr),
       opened_by_dom_(false),
       tab_key_cycles_through_elements_(true),
@@ -262,6 +264,10 @@
   return *overscroll_controller_;
 }
 
+LinkHighlights& Page::GetLinkHighlights() {
+  return *link_highlights_;
+}
+
 DOMRectList* Page::NonFastScrollableRectsForTesting(const LocalFrame* frame) {
   // Update lifecycle to kPrePaintClean.  This includes the compositing update
   // and ScrollingCoordinator::UpdateAfterPaint, which computes the non-fast
@@ -725,6 +731,7 @@
   visitor->Trace(global_root_scroller_controller_);
   visitor->Trace(visual_viewport_);
   visitor->Trace(overscroll_controller_);
+  visitor->Trace(link_highlights_);
   visitor->Trace(main_frame_);
   visitor->Trace(plugin_data_);
   visitor->Trace(validation_message_client_);
@@ -739,12 +746,14 @@
                                     LocalFrameView* view) {
   if (GetScrollingCoordinator())
     GetScrollingCoordinator()->LayerTreeViewInitialized(layer_tree_view, view);
+  GetLinkHighlights().LayerTreeViewInitialized(layer_tree_view);
 }
 
 void Page::WillCloseLayerTreeView(WebLayerTreeView& layer_tree_view,
                                   LocalFrameView* view) {
   if (scrolling_coordinator_)
     scrolling_coordinator_->WillCloseLayerTreeView(layer_tree_view, view);
+  GetLinkHighlights().WillCloseLayerTreeView(layer_tree_view);
 }
 
 void Page::WillBeDestroyed() {
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index c2cc3cc..3245d4c2 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -59,6 +59,7 @@
 class DragController;
 class FocusController;
 class Frame;
+class LinkHighlights;
 class OverscrollController;
 struct PageScaleConstraints;
 class PageScaleConstraintsSet;
@@ -216,6 +217,8 @@
   VisualViewport& GetVisualViewport();
   const VisualViewport& GetVisualViewport() const;
 
+  LinkHighlights& GetLinkHighlights();
+
   OverscrollController& GetOverscrollController();
   const OverscrollController& GetOverscrollController() const;
 
@@ -363,6 +366,7 @@
       global_root_scroller_controller_;
   const Member<VisualViewport> visual_viewport_;
   const Member<OverscrollController> overscroll_controller_;
+  const Member<LinkHighlights> link_highlights_;
 
   Member<PluginData> plugin_data_;
 
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.cc b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
index 45511ba..683971b 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.cc
@@ -38,10 +38,10 @@
 #include "third_party/blink/public/web/blink.h"
 #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
 #include "third_party/blink/renderer/core/dom/node.h"
-#include "third_party/blink/renderer/core/exported/web_settings_impl.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
@@ -68,15 +68,12 @@
 
 namespace blink {
 
-std::unique_ptr<LinkHighlightImpl> LinkHighlightImpl::Create(
-    Node* node,
-    WebViewImpl* owning_web_view) {
-  return base::WrapUnique(new LinkHighlightImpl(node, owning_web_view));
+std::unique_ptr<LinkHighlightImpl> LinkHighlightImpl::Create(Node* node) {
+  return base::WrapUnique(new LinkHighlightImpl(node));
 }
 
-LinkHighlightImpl::LinkHighlightImpl(Node* node, WebViewImpl* owning_web_view)
+LinkHighlightImpl::LinkHighlightImpl(Node* node)
     : node_(node),
-      owning_web_view_(owning_web_view),
       current_graphics_layer_(nullptr),
       is_scrolling_graphics_layer_(false),
       geometry_needs_update_(false),
@@ -84,15 +81,12 @@
       start_time_(CurrentTimeTicks()),
       unique_id_(NewUniqueObjectId()) {
   DCHECK(node_);
-  DCHECK(owning_web_view);
   content_layer_ = cc::PictureLayer::Create(this);
   content_layer_->SetTransformOrigin(FloatPoint3D());
 
   compositor_animation_ = CompositorAnimation::Create();
   DCHECK(compositor_animation_);
   compositor_animation_->SetAnimationDelegate(this);
-  if (owning_web_view_->LinkHighlightsTimeline())
-    owning_web_view_->LinkHighlightsTimeline()->AnimationAttached(*this);
 
   CompositorElementId element_id =
       CompositorElementIdFromUniqueObjectId(unique_id_);
@@ -106,8 +100,6 @@
 LinkHighlightImpl::~LinkHighlightImpl() {
   if (compositor_animation_->IsElementAttached())
     compositor_animation_->DetachElement();
-  if (owning_web_view_->LinkHighlightsTimeline())
-    owning_web_view_->LinkHighlightsTimeline()->AnimationDestroyed(*this);
   compositor_animation_->SetAnimationDelegate(nullptr);
   compositor_animation_.reset();
 
@@ -228,7 +220,9 @@
     // links: these should ideally be merged into a single rect before creating
     // the path, but that's another CL.
     if (quads.size() == 1 && transformed_quad.IsRectilinear() &&
-        !owning_web_view_->SettingsImpl()->MockGestureTapHighlightsEnabled()) {
+        !node_->GetDocument()
+             .GetSettings()
+             ->GetMockGestureTapHighlightsEnabled()) {
       FloatSize rect_rounding_radii(3, 3);
       new_path.AddRoundedRect(transformed_quad.BoundingBox(),
                               rect_rounding_radii);
@@ -326,7 +320,6 @@
   compositor_animation_->AddKeyframeModel(std::move(keyframe_model));
 
   Invalidate();
-  owning_web_view_->MainFrameImpl()->FrameWidgetImpl()->ScheduleAnimation();
 }
 
 void LinkHighlightImpl::ClearGraphicsLayerLinkHighlightPointer() {
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl.h b/third_party/blink/renderer/core/paint/link_highlight_impl.h
index 9c4a76d8f..2158cf7 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl.h
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl.h
@@ -50,14 +50,13 @@
 class GraphicsLayer;
 class LayoutBoxModelObject;
 class Node;
-class WebViewImpl;
 
 class CORE_EXPORT LinkHighlightImpl final : public LinkHighlight,
                                             public cc::ContentLayerClient,
                                             public CompositorAnimationDelegate,
                                             public CompositorAnimationClient {
  public:
-  static std::unique_ptr<LinkHighlightImpl> Create(Node*, WebViewImpl*);
+  static std::unique_ptr<LinkHighlightImpl> Create(Node*);
   ~LinkHighlightImpl() override;
 
   void StartHighlightAnimationIfNeeded();
@@ -88,7 +87,7 @@
   }
 
  private:
-  LinkHighlightImpl(Node*, WebViewImpl*);
+  LinkHighlightImpl(Node*);
 
   void ReleaseResources();
   void ComputeQuads(const Node&, Vector<FloatQuad>&) const;
@@ -104,7 +103,6 @@
   Path path_;
 
   Persistent<Node> node_;
-  WebViewImpl* owning_web_view_;
   GraphicsLayer* current_graphics_layer_;
   bool is_scrolling_graphics_layer_;
   std::unique_ptr<CompositorAnimation> compositor_animation_;
diff --git a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
index b24337a..1739a05 100644
--- a/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
+++ b/third_party/blink/renderer/core/paint/link_highlight_impl_test.cc
@@ -40,11 +40,13 @@
 #include "third_party/blink/renderer/core/events/web_input_event_conversion.h"
 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
+#include "third_party/blink/renderer/core/frame/link_highlights.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/input/event_handler.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/touch_disambiguation.h"
+#include "third_party/blink/renderer/platform/animation/compositor_animation_timeline.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
@@ -104,26 +106,34 @@
   web_view_impl->EnableTapHighlightAtPoint(
       GetTargetedEvent(web_view_impl, touch_event));
 
-  EXPECT_TRUE(web_view_impl->GetLinkHighlight(0));
-  EXPECT_TRUE(web_view_impl->GetLinkHighlight(0)->Layer());
+  const auto& highlights =
+      web_view_impl->GetPage()->GetLinkHighlights().link_highlights_;
+  EXPECT_TRUE(highlights.at(0));
+  EXPECT_TRUE(highlights.at(0)->Layer());
 
   // Find a target inside a scrollable div
   touch_event.SetPositionInWidget(WebFloatPoint(20, 100));
   web_view_impl->EnableTapHighlightAtPoint(
       GetTargetedEvent(web_view_impl, touch_event));
-  ASSERT_TRUE(web_view_impl->GetLinkHighlight(0));
+  ASSERT_TRUE(highlights.at(0));
+
+  // Enesure the timeline was added to a host.
+  EXPECT_TRUE(!!web_view_impl->GetPage()
+                    ->GetLinkHighlights()
+                    .timeline_->GetAnimationTimeline()
+                    ->animation_host());
 
   // Don't highlight if no "hand cursor"
   touch_event.SetPositionInWidget(
       WebFloatPoint(20, 220));  // An A-link with cross-hair cursor.
   web_view_impl->EnableTapHighlightAtPoint(
       GetTargetedEvent(web_view_impl, touch_event));
-  ASSERT_EQ(0U, web_view_impl->NumLinkHighlights());
+  ASSERT_EQ(0U, highlights.size());
 
   touch_event.SetPositionInWidget(WebFloatPoint(20, 260));  // A text input box.
   web_view_impl->EnableTapHighlightAtPoint(
       GetTargetedEvent(web_view_impl, touch_event));
-  ASSERT_EQ(0U, web_view_impl->NumLinkHighlights());
+  ASSERT_EQ(0U, highlights.size());
 
   Platform::Current()
       ->GetURLLoaderMockFactory()
@@ -152,16 +162,17 @@
   ASSERT_TRUE(touch_node);
 
   web_view_impl->EnableTapHighlightAtPoint(targeted_event);
-  ASSERT_TRUE(web_view_impl->GetLinkHighlight(0));
+  const auto& highlights = web_view_impl->GetPage()->GetLinkHighlights();
+  ASSERT_TRUE(highlights.link_highlights_.at(0));
 
   GraphicsLayer* highlight_layer =
-      web_view_impl->GetLinkHighlight(0)->CurrentGraphicsLayerForTesting();
+      highlights.link_highlights_.at(0)->CurrentGraphicsLayerForTesting();
   ASSERT_TRUE(highlight_layer);
   EXPECT_TRUE(highlight_layer->GetLinkHighlight(0));
 
   touch_node->remove(IGNORE_EXCEPTION_FOR_TESTING);
   web_view_impl->UpdateAllLifecyclePhases();
-  ASSERT_EQ(0U, highlight_layer->NumLinkHighlights());
+  EXPECT_EQ(0U, highlight_layer->NumLinkHighlights());
 
   Platform::Current()
       ->GetURLLoaderMockFactory()
@@ -191,10 +202,12 @@
   ASSERT_TRUE(touch_node);
 
   web_view_impl->EnableTapHighlightAtPoint(targeted_event);
-  ASSERT_TRUE(web_view_impl->GetLinkHighlight(0));
+  const auto& highlights =
+      web_view_impl->GetPage()->GetLinkHighlights().link_highlights_;
+  ASSERT_TRUE(highlights.at(0));
 
   GraphicsLayer* highlight_layer =
-      web_view_impl->GetLinkHighlight(0)->CurrentGraphicsLayerForTesting();
+      highlights.at(0)->CurrentGraphicsLayerForTesting();
   ASSERT_TRUE(highlight_layer);
   EXPECT_TRUE(highlight_layer->GetLinkHighlight(0));
 
@@ -228,7 +241,9 @@
                        good_targets, highlight_nodes);
 
   web_view_impl->EnableTapHighlights(highlight_nodes);
-  EXPECT_EQ(2U, web_view_impl->NumLinkHighlights());
+  const auto& highlights =
+      web_view_impl->GetPage()->GetLinkHighlights().link_highlights_;
+  EXPECT_EQ(2U, highlights.size());
 
   Platform::Current()
       ->GetURLLoaderMockFactory()
diff --git a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
index d4c09dc..63f4af4 100644
--- a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
@@ -710,9 +710,6 @@
   // layout, but should still unthrottle the frame.
   frame_element->setAttribute(styleAttr, "transform: translateY(0px)");
   CompositeFrame();  // Unthrottle the frame.
-
-  EXPECT_FALSE(
-      frame_element->contentDocument()->View()->ShouldThrottleRendering());
   CompositeFrame();  // Handle the pending visual update of the unthrottled
                      // frame.
   EXPECT_EQ(DocumentLifecycle::kPaintClean,
diff --git a/third_party/blink/renderer/modules/vr/vr_controller.cc b/third_party/blink/renderer/modules/vr/vr_controller.cc
index da0e2fb..6805adad 100644
--- a/third_party/blink/renderer/modules/vr/vr_controller.cc
+++ b/third_party/blink/renderer/modules/vr/vr_controller.cc
@@ -60,13 +60,11 @@
 // here. Upon calling SetClient in the constructor we should receive one call
 // for each VRDisplay that was already connected at the time.
 void VRController::OnDisplayConnected(
-    device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider,
     device::mojom::blink::VRDisplayHostPtr display,
     device::mojom::blink::VRDisplayClientRequest request,
     device::mojom::blink::VRDisplayInfoPtr display_info) {
   VRDisplay* vr_display =
-      new VRDisplay(navigator_vr_, std::move(magic_window_provider),
-                    std::move(display), std::move(request));
+      new VRDisplay(navigator_vr_, std::move(display), std::move(request));
   vr_display->Update(display_info);
   vr_display->OnConnected();
   vr_display->FocusChanged();
diff --git a/third_party/blink/renderer/modules/vr/vr_controller.h b/third_party/blink/renderer/modules/vr/vr_controller.h
index 5d5fae9..b91a0e5 100644
--- a/third_party/blink/renderer/modules/vr/vr_controller.h
+++ b/third_party/blink/renderer/modules/vr/vr_controller.h
@@ -34,8 +34,7 @@
   void GetDisplays(ScriptPromiseResolver*);
   void SetListeningForActivate(bool);
 
-  void OnDisplayConnected(device::mojom::blink::VRMagicWindowProviderPtr,
-                          device::mojom::blink::VRDisplayHostPtr,
+  void OnDisplayConnected(device::mojom::blink::VRDisplayHostPtr,
                           device::mojom::blink::VRDisplayClientRequest,
                           device::mojom::blink::VRDisplayInfoPtr) override;
 
diff --git a/third_party/blink/renderer/modules/vr/vr_display.cc b/third_party/blink/renderer/modules/vr/vr_display.cc
index 73ea3f76..a221211 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.cc
+++ b/third_party/blink/renderer/modules/vr/vr_display.cc
@@ -95,16 +95,25 @@
 
 VRDisplay::VRDisplay(
     NavigatorVR* navigator_vr,
-    device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider,
     device::mojom::blink::VRDisplayHostPtr display,
     device::mojom::blink::VRDisplayClientRequest request)
     : PausableObject(navigator_vr->GetDocument()),
       navigator_vr_(navigator_vr),
       capabilities_(new VRDisplayCapabilities()),
-      magic_window_provider_(std::move(magic_window_provider)),
       display_(std::move(display)),
       display_client_binding_(this, std::move(request)) {
   PauseIfNeeded();  // Initialize SuspendabaleObject.
+
+  // Request a non-exclusive session to provide magic window.
+  device::mojom::blink::XRSessionOptionsPtr options =
+      device::mojom::blink::XRSessionOptions::New();
+  options->immersive = false;
+  // Set in_on_display_activate to true, this will prevent the request present
+  // from being logged.
+  // TODO(offenwanger): clean up the logging when refactors are complete.
+  display_->RequestSession(std::move(options), true,
+                           WTF::Bind(&VRDisplay::OnMagicWindowRequestReturned,
+                                     WrapPersistent(this)));
 }
 
 VRDisplay::~VRDisplay() = default;
@@ -227,7 +236,26 @@
   if (display_blurred_)
     return;
 
-  if (!is_presenting_) {
+  if (is_presenting_) {
+    DCHECK(vr_presentation_provider_.is_bound());
+
+    if (pending_presenting_vsync_)
+      return;
+
+    pending_magic_window_vsync_ = false;
+    pending_presenting_vsync_ = true;
+    vr_presentation_provider_->GetFrameData(
+        WTF::Bind(&VRDisplay::OnPresentingVSync, WrapWeakPersistent(this)));
+
+    DVLOG(2) << __FUNCTION__ << " done: pending_presenting_vsync_="
+             << pending_presenting_vsync_;
+  } else {
+    // Check if magic_window_provider_, if not then we are not fully
+    // initialized, or we do not support magic window, so don't request the
+    // vsync. If and when magic_window_provider_ is set it will run this code
+    // again.
+    if (!magic_window_provider_)
+      return;
     if (pending_magic_window_vsync_)
       return;
     magic_window_vsync_waiting_for_pose_.Reset();
@@ -239,20 +267,7 @@
         doc->RequestAnimationFrame(new VRDisplayFrameRequestCallback(this));
     DVLOG(2) << __FUNCTION__ << " done: pending_magic_window_vsync_="
              << pending_magic_window_vsync_;
-    return;
   }
-  DCHECK(vr_presentation_provider_.is_bound());
-
-  if (pending_presenting_vsync_)
-    return;
-
-  pending_magic_window_vsync_ = false;
-  pending_presenting_vsync_ = true;
-  vr_presentation_provider_->GetFrameData(
-      WTF::Bind(&VRDisplay::OnPresentingVSync, WrapWeakPersistent(this)));
-
-  DVLOG(2) << __FUNCTION__
-           << " done: pending_presenting_vsync_=" << pending_presenting_vsync_;
 }
 
 int VRDisplay::requestAnimationFrame(V8FrameRequestCallback* callback) {
@@ -485,19 +500,19 @@
 }
 
 void VRDisplay::OnRequestSessionReturned(
-    device::mojom::blink::XRPresentationConnectionPtr connection) {
+    device::mojom::blink::XRSessionPtr session) {
   pending_present_request_ = false;
-  if (connection) {
-    vr_presentation_provider_.Bind(std::move(connection->provider));
+  if (session && session->connection) {
+    vr_presentation_provider_.Bind(std::move(session->connection->provider));
     vr_presentation_provider_.set_connection_error_handler(
         WTF::Bind(&VRDisplay::OnPresentationProviderConnectionError,
                   WrapWeakPersistent(this)));
 
     frame_transport_ = new XRFrameTransport();
     frame_transport_->BindSubmitFrameClient(
-        std::move(connection->client_request));
+        std::move(session->connection->client_request));
     frame_transport_->SetTransportOptions(
-        std::move(connection->transport_options));
+        std::move(session->connection->transport_options));
 
     this->BeginPresent();
   } else {
@@ -512,6 +527,16 @@
   }
 }
 
+void VRDisplay::OnMagicWindowRequestReturned(
+    device::mojom::blink::XRSessionPtr session) {
+  if (!session || !session->magic_window_provider) {
+    // System does not support any kind of magic window.
+    return;
+  }
+  magic_window_provider_.Bind(std::move(session->magic_window_provider));
+  RequestVSync();
+}
+
 ScriptPromise VRDisplay::exitPresent(ScriptState* script_state) {
   DVLOG(1) << __FUNCTION__;
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
diff --git a/third_party/blink/renderer/modules/vr/vr_display.h b/third_party/blink/renderer/modules/vr/vr_display.h
index 17d6d01..bfd14698 100644
--- a/third_party/blink/renderer/modules/vr/vr_display.h
+++ b/third_party/blink/renderer/modules/vr/vr_display.h
@@ -108,7 +108,6 @@
   friend class VRController;
 
   VRDisplay(NavigatorVR*,
-            device::mojom::blink::VRMagicWindowProviderPtr,
             device::mojom::blink::VRDisplayHostPtr,
             device::mojom::blink::VRDisplayClientRequest);
 
@@ -124,8 +123,8 @@
   VRController* Controller();
 
  private:
-  void OnRequestSessionReturned(
-      device::mojom::blink::XRPresentationConnectionPtr connection);
+  void OnRequestSessionReturned(device::mojom::blink::XRSessionPtr session);
+  void OnMagicWindowRequestReturned(device::mojom::blink::XRSessionPtr session);
 
   void OnConnected();
   void OnDisconnected();
@@ -219,6 +218,7 @@
   bool did_log_requestPresent_ = false;
 
   device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider_;
+
   device::mojom::blink::VRDisplayHostPtr display_;
 
   bool present_image_needs_copy_ = false;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_destination_node.h b/third_party/blink/renderer/modules/webaudio/audio_destination_node.h
index 6267e9f..c143f86 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_destination_node.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_destination_node.h
@@ -64,9 +64,20 @@
   virtual double SampleRate() const = 0;
   virtual unsigned long MaxChannelCount() const = 0;
 
+  void ContextDestroyed() { is_execution_context_destroyed_ = true; }
+  bool IsExecutionContextDestroyed() const {
+    return is_execution_context_destroyed_;
+  }
+
  protected:
   // The number of sample frames processed by the destination so far.
   size_t current_sample_frame_;
+
+ private:
+  // True if the execution context is being destroyed.  If this is true, the
+  // destination ndoe must avoid checking for or accessing the execution
+  // context.
+  bool is_execution_context_destroyed_ = false;
 };
 
 // -----------------------------------------------------------------------------
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
index 1b6fa474..f30bd88 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.cc
@@ -120,6 +120,14 @@
 
   if (destination_node_) {
     destination_node_->Handler().Initialize();
+    // TODO(crbug.com/863951).  The audio thread needs some things from the
+    // destination handler like the currentTime.  But the audio thread
+    // shouldn't access the |destination_node_| since it's an Oilpan object.
+    // Thus, get the destination handler, a non-oilpan object, so we can get
+    // the items directly from the handler instead of through the destination
+    // node.
+    destination_handler_ = &destination_node_->GetAudioDestinationHandler();
+
     // The AudioParams in the listener need access to the destination node, so
     // only create the listener if the destination node exists.
     listener_ = AudioListener::Create(*this);
@@ -160,6 +168,7 @@
 }
 
 void BaseAudioContext::ContextDestroyed(ExecutionContext*) {
+  destination()->GetAudioDestinationHandler().ContextDestroyed();
   Uninitialize();
 }
 
diff --git a/third_party/blink/renderer/modules/webaudio/base_audio_context.h b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
index 9da78b3..f55e32c4 100644
--- a/third_party/blink/renderer/modules/webaudio/base_audio_context.h
+++ b/third_party/blink/renderer/modules/webaudio/base_audio_context.h
@@ -127,16 +127,12 @@
   AudioDestinationNode* destination() const;
 
   size_t CurrentSampleFrame() const {
-    return destination_node_->GetAudioDestinationHandler().CurrentSampleFrame();
+    return destination_handler_->CurrentSampleFrame();
   }
 
-  double currentTime() const {
-    return destination_node_->GetAudioDestinationHandler().CurrentTime();
-  }
+  double currentTime() const { return destination_handler_->CurrentTime(); }
 
-  float sampleRate() const {
-    return destination_node_->GetAudioDestinationHandler().SampleRate();
-  }
+  float sampleRate() const { return destination_handler_->SampleRate(); }
 
   String state() const;
   AudioContextState ContextState() const { return context_state_; }
@@ -445,6 +441,9 @@
 
   AudioIOPosition output_position_;
 
+  // The handler associated with the above |destination_node_|.
+  scoped_refptr<AudioDestinationHandler> destination_handler_;
+
   Member<AudioWorklet> audio_worklet_;
 
   // In order to update some information (e.g. current frame) in
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
index 3e8126d..673121b 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.cc
@@ -265,6 +265,12 @@
 
   render_thread_.reset();
 
+  // If the execution context has been destroyed, there's no where to
+  // send the notification, so just return.
+  if (IsExecutionContextDestroyed()) {
+    return;
+  }
+
   // The OfflineAudioContext might be gone.
   if (Context() && Context()->GetExecutionContext())
     Context()->FireCompletionEvent();
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index 1b4aace..58c4a8a 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -127,13 +127,12 @@
 // here. Upon calling SetClient in the constructor we should receive one call
 // for each XRDevice that was already connected at the time.
 void XR::OnDisplayConnected(
-    device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider,
     device::mojom::blink::VRDisplayHostPtr display,
     device::mojom::blink::VRDisplayClientRequest client_request,
     device::mojom::blink::VRDisplayInfoPtr display_info) {
   XRDevice* xr_device =
-      new XRDevice(this, std::move(magic_window_provider), std::move(display),
-                   std::move(client_request), std::move(display_info));
+      new XRDevice(this, std::move(display), std::move(client_request),
+                   std::move(display_info));
 
   devices_.push_back(xr_device);
 
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h
index 19c80ba..4c6efa8c 100644
--- a/third_party/blink/renderer/modules/xr/xr.h
+++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -37,8 +37,7 @@
   ScriptPromise requestDevice(ScriptState*);
 
   // XRServiceClient overrides.
-  void OnDisplayConnected(device::mojom::blink::VRMagicWindowProviderPtr,
-                          device::mojom::blink::VRDisplayHostPtr,
+  void OnDisplayConnected(device::mojom::blink::VRDisplayHostPtr,
                           device::mojom::blink::VRDisplayClientRequest,
                           device::mojom::blink::VRDisplayInfoPtr) override;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_device.cc b/third_party/blink/renderer/modules/xr/xr_device.cc
index 1a883f2..42656d7 100644
--- a/third_party/blink/renderer/modules/xr/xr_device.cc
+++ b/third_party/blink/renderer/modules/xr/xr_device.cc
@@ -40,12 +40,10 @@
 
 XRDevice::XRDevice(
     XR* xr,
-    device::mojom::blink::VRMagicWindowProviderPtr magic_window_provider,
     device::mojom::blink::VRDisplayHostPtr display,
     device::mojom::blink::VRDisplayClientRequest client_request,
     device::mojom::blink::VRDisplayInfoPtr display_info)
     : xr_(xr),
-      magic_window_provider_(std::move(magic_window_provider)),
       display_(std::move(display)),
       display_client_binding_(this, std::move(client_request)) {
   SetXRDisplayInfo(std::move(display_info));
@@ -208,14 +206,17 @@
 void XRDevice::OnRequestSessionReturned(
     ScriptPromiseResolver* resolver,
     const XRSessionCreationOptions& options,
-    device::mojom::blink::XRPresentationConnectionPtr connection) {
-  if (!connection) {
+    device::mojom::blink::XRSessionPtr session_ptr) {
+  if (!session_ptr) {
     DOMException* exception = DOMException::Create(
         DOMExceptionCode::kNotAllowedError, kRequestFailed);
     resolver->Reject(exception);
     return;
   }
 
+  if (session_ptr->magic_window_provider)
+    magic_window_provider_.Bind(std::move(session_ptr->magic_window_provider));
+
   XRPresentationContext* output_context = nullptr;
   if (options.hasOutputContext()) {
     output_context = options.outputContext();
@@ -232,7 +233,8 @@
   sessions_.insert(session);
 
   if (options.immersive()) {
-    frameProvider()->BeginImmersiveSession(session, std::move(connection));
+    frameProvider()->BeginImmersiveSession(session,
+                                           std::move(session_ptr->connection));
   }
 
   resolver->Resolve(session);
diff --git a/third_party/blink/renderer/modules/xr/xr_device.h b/third_party/blink/renderer/modules/xr/xr_device.h
index ac7df4f..f4db8e21 100644
--- a/third_party/blink/renderer/modules/xr/xr_device.h
+++ b/third_party/blink/renderer/modules/xr/xr_device.h
@@ -28,7 +28,6 @@
 
  public:
   XRDevice(XR*,
-           device::mojom::blink::VRMagicWindowProviderPtr,
            device::mojom::blink::VRDisplayHostPtr,
            device::mojom::blink::VRDisplayClientRequest,
            device::mojom::blink::VRDisplayInfoPtr);
@@ -84,10 +83,9 @@
 
   const char* checkSessionSupport(const XRSessionCreationOptions&) const;
 
-  void OnRequestSessionReturned(
-      ScriptPromiseResolver* resolver,
-      const XRSessionCreationOptions& options,
-      device::mojom::blink::XRPresentationConnectionPtr connection);
+  void OnRequestSessionReturned(ScriptPromiseResolver* resolver,
+                                const XRSessionCreationOptions& options,
+                                device::mojom::blink::XRSessionPtr session);
   void OnSupportsSessionReturned(ScriptPromiseResolver* resolver,
                                  bool supports_session);
 
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index ce5e8c46..3fefb14 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -200,8 +200,10 @@
 void XRFrameProvider::ScheduleNonImmersiveFrame() {
   TRACE_EVENT0("gpu", __FUNCTION__);
   DCHECK(!immersive_session_)
-      << "Scheduling should be done via the immersive session if present.";
-  DCHECK(device_->xrMagicWindowProviderPtr());
+      << "Scheduling should be done via the exclusive session if present.";
+  DCHECK(device_->xrMagicWindowProviderPtr())
+      << "If there is no exclusive session, it should be impossible to "
+         "schedule a frame without a MagicWindowProvider.";
 
   if (pending_non_immersive_vsync_)
     return;
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 45fc93c..ee19530 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1732,6 +1732,7 @@
     "feature_policy/feature_policy_test.cc",
     "fonts/android/font_cache_android_test.cc",
     "fonts/bitmap_glyphs_blacklist_test.cc",
+    "fonts/cursor_position_test.cc",
     "fonts/font_cache_test.cc",
     "fonts/font_description_test.cc",
     "fonts/font_family_test.cc",
@@ -1972,6 +1973,7 @@
     "testing/blink_perf_test_suite.cc",
     "testing/blink_perf_test_suite.h",
     "testing/run_all_perf_tests.cc",
+    "testing/shape_result_perf_test.cc",
     "testing/shaping_line_breaker_perf_test.cc",
   ]
 
diff --git a/third_party/blink/renderer/platform/exported/web_font.cc b/third_party/blink/renderer/platform/exported/web_font.cc
index 1d1c3d7..4e1dd3c 100644
--- a/third_party/blink/renderer/platform/exported/web_font.cc
+++ b/third_party/blink/renderer/platform/exported/web_font.cc
@@ -108,7 +108,8 @@
 }
 
 int WebFont::OffsetForPosition(const WebTextRun& run, float position) const {
-  return private_->GetFont().OffsetForPosition(run, position, true);
+  return private_->GetFont().OffsetForPosition(
+      run, position, IncludePartialGlyphs, DontBreakGlyphs);
 }
 
 WebFloatRect WebFont::SelectionRectForText(const WebTextRun& run,
diff --git a/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc b/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc
index e434b44..8910562d 100644
--- a/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/exported/web_surface_layer_bridge.cc
@@ -11,8 +11,10 @@
 
 std::unique_ptr<WebSurfaceLayerBridge> WebSurfaceLayerBridge::Create(
     WebLayerTreeView* layer_tree_view,
-    WebSurfaceLayerBridgeObserver* observer) {
-  return std::make_unique<SurfaceLayerBridge>(layer_tree_view, observer);
+    WebSurfaceLayerBridgeObserver* observer,
+    cc::UpdateSubmissionStateCB update_submission_state_callback) {
+  return std::make_unique<SurfaceLayerBridge>(
+      layer_tree_view, observer, std::move(update_submission_state_callback));
 }
 
 WebSurfaceLayerBridge::~WebSurfaceLayerBridge() = default;
diff --git a/third_party/blink/renderer/platform/fonts/cursor_position_test.cc b/third_party/blink/renderer/platform/fonts/cursor_position_test.cc
new file mode 100644
index 0000000..2ffd9a8
--- /dev/null
+++ b/third_party/blink/renderer/platform/fonts/cursor_position_test.cc
@@ -0,0 +1,457 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/platform/file_path_conversion.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+using blink::test::CreateTestFont;
+
+namespace blink {
+
+class CursorPositionTest : public ::testing::Test {
+ public:
+  enum FontName {
+    ahem,
+    amiri,
+    megalopolis,
+    roboto,
+  };
+
+  float GetWidth(FontName font_name,
+                 const String& text,
+                 bool ltr,
+                 int start = 0,
+                 int end = -1) {
+    FontDescription::VariantLigatures ligatures(
+        FontDescription::kEnabledLigaturesState);
+    Font font = CreateTestFont("TestFont",
+                               test::PlatformTestDataPath(font_path[font_name]),
+                               100, &ligatures);
+    TextRun text_run(
+        text, /* xpos */ 0, /* expansion */ 0,
+        TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
+        ltr ? TextDirection::kLtr : TextDirection::kRtl, false);
+
+    if (end == -1)
+      end = text_run.length();
+    DCHECK_GE(start, 0);
+    DCHECK_LE(start, static_cast<int>(text_run.length()));
+    DCHECK_GE(end, -1);
+    DCHECK_LE(end, static_cast<int>(text_run.length()));
+    FloatRect rect =
+        font.SelectionRectForText(text_run, FloatPoint(), 12, start, end);
+    return rect.Width();
+  }
+
+  int GetCharacter(FontName font_name,
+                   const String& text,
+                   bool ltr,
+                   float position,
+                   bool partial) {
+    FontDescription::VariantLigatures ligatures(
+        FontDescription::kEnabledLigaturesState);
+    Font font = CreateTestFont("TestFont",
+                               test::PlatformTestDataPath(font_path[font_name]),
+                               100, &ligatures);
+    TextRun text_run(
+        text, /* xpos */ 0, /* expansion */ 0,
+        TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
+        ltr ? TextDirection::kLtr : TextDirection::kRtl, false);
+
+    return font.OffsetForPosition(
+        text_run, position, partial ? IncludePartialGlyphs : OnlyFullGlyphs,
+        BreakGlyphs);
+  }
+
+ private:
+  std::map<FontName, String> font_path = {
+      {ahem, "Ahem.woff"},
+      {amiri, "third_party/Amiri/amiri_arabic.woff2"},
+      {megalopolis, "third_party/MEgalopolis/MEgalopolisExtra.woff"},
+      {roboto, "third_party/Roboto/roboto-regular.woff2"},
+  };
+};
+
+TEST_F(CursorPositionTest, LTRMouse) {
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 0, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 0, true), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 10, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 10, true), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 60, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 60, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 100, false), 1);
+  EXPECT_EQ(GetCharacter(ahem, "X", true, 100, true), 1);
+
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 10, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 10, true), 0);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 60, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 60, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 100, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 100, false), 1);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 125, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 125, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 151, false), 1);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 151, true), 2);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 175, false), 1);
+  EXPECT_EQ(GetCharacter(ahem, "XXX", true, 175, true), 2);
+}
+
+TEST_F(CursorPositionTest, LTRLigatureMouse) {
+  const float kFUWidth = GetWidth(megalopolis, "FU", true);
+  const float kRAWidth = GetWidth(megalopolis, "RA", true);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 4 - 1, false),
+            0);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 4 - 1, true), 0);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 4 + 1, false),
+            0);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 4 + 1, true), 1);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 2 - 1, false),
+            0);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 2 - 1, true), 1);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 2 + 1, false),
+            1);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth / 2 + 1, true), 1);
+
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "FURA", true, kFUWidth * 3 / 4 - 1, false), 1);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth * 3 / 4 - 1, true),
+            1);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "FURA", true, kFUWidth * 3 / 4 + 1, false), 1);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth * 3 / 4 + 1, true),
+            2);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth - 1, false), 1);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth - 1, true), 2);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + 1, false), 2);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + 1, true), 2);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 4 - 1,
+                         false),
+            2);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 4 - 1,
+                         true),
+            2);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 4 + 1,
+                         false),
+            2);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 4 + 1,
+                         true),
+            3);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 2 - 1,
+                         false),
+            2);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 2 - 1,
+                         true),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 2 + 1,
+                         false),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth / 2 + 1,
+                         true),
+            3);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true,
+                         kFUWidth + kRAWidth * 3 / 4 - 1, false),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true,
+                         kFUWidth + kRAWidth * 3 / 4 - 1, true),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true,
+                         kFUWidth + kRAWidth * 3 / 4 + 1, false),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "FURA", true,
+                         kFUWidth + kRAWidth * 3 / 4 + 1, true),
+            4);
+
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth - 1, false),
+      3);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth - 1, true),
+      4);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth + 1, false),
+      4);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "FURA", true, kFUWidth + kRAWidth + 1, true),
+      4);
+}
+
+TEST_F(CursorPositionTest, RTLMouse) {
+  // The widths below are from the final shaped version, not from the single
+  // characters. They were extracted with "hb-shape --font-size=100"
+
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 0, false), 1);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 0, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 10, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 10, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 49, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 49, true), 1);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 51, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 51, true), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 60, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 60, true), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 100, false), 0);
+  EXPECT_EQ(GetCharacter(ahem, "X", false, 100, true), 0);
+
+  const float kAloneTaWidth = GetWidth(amiri, u"ت", false);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, 0, false), 1);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, 0, true), 1);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, kAloneTaWidth / 4, false), 0);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, kAloneTaWidth / 4, true), 1);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, kAloneTaWidth * 2 / 3, false), 0);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, kAloneTaWidth * 2 / 3, true), 0);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, 2 * kAloneTaWidth, false), 0);
+  EXPECT_EQ(GetCharacter(amiri, u"ت", false, 2 * kAloneTaWidth, true), 0);
+
+  const float kAboveTaWidth = 10;
+  const float kAboveKhaWidth = 55;
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, 0, false), 2);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, 0, true), 2);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, kAboveTaWidth / 4, false), 1);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, kAboveTaWidth / 4, true), 2);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, kAboveTaWidth * 2 / 3, false), 1);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, kAboveTaWidth * 2 / 3, true), 1);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, kAboveTaWidth + 1, false), 0);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false, kAboveTaWidth + 1, true), 1);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         kAboveTaWidth + kAboveKhaWidth / 4, false),
+            0);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         kAboveTaWidth + kAboveKhaWidth / 4, true),
+            1);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         kAboveTaWidth + kAboveKhaWidth * 2 / 3, false),
+            0);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         kAboveTaWidth + kAboveKhaWidth * 2 / 3, true),
+            0);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         kAboveTaWidth + kAboveKhaWidth + 1, false),
+            0);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         kAboveTaWidth + kAboveKhaWidth + 1, true),
+            0);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         2 * (kAboveTaWidth + kAboveKhaWidth), false),
+            0);
+  EXPECT_EQ(GetCharacter(amiri, u"تخ", false,
+                         2 * (kAboveTaWidth + kAboveKhaWidth), true),
+            0);
+}
+
+TEST_F(CursorPositionTest, RTLLigatureMouse) {
+  const float kFUWidth = GetWidth(megalopolis, "FU", true);
+  const float kRAWidth = GetWidth(megalopolis, "RA", true);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 4 - 1, false),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 4 - 1, true),
+            4);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 4 + 1, false),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 4 + 1, true),
+            3);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 2 - 1, false),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 2 - 1, true),
+            3);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 2 + 1, false),
+            2);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth / 2 + 1, true),
+            3);
+
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth * 3 / 4 - 1, false), 2);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth * 3 / 4 - 1, true), 3);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth * 3 / 4 + 1, false), 2);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth * 3 / 4 + 1, true), 2);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth - 1, false), 2);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth - 1, true), 2);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth + 1, false), 1);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false, kFUWidth + 1, true), 2);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 4 - 1, false),
+            1);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 4 - 1, true),
+            2);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 4 + 1, false),
+            1);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 4 + 1, true),
+            1);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 2 - 1, false),
+            1);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 2 - 1, true),
+            1);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 2 + 1, false),
+            0);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth / 2 + 1, true),
+            1);
+
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth * 3 / 4 - 1, false),
+            0);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth * 3 / 4 - 1, true),
+            1);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth * 3 / 4 + 1, false),
+            0);
+  EXPECT_EQ(GetCharacter(megalopolis, "ARUF", false,
+                         kFUWidth + kRAWidth * 3 / 4 + 1, true),
+            0);
+
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth + kRAWidth - 1, false),
+      0);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth + kRAWidth - 1, true),
+      0);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth + kRAWidth + 1, false),
+      0);
+  EXPECT_EQ(
+      GetCharacter(megalopolis, "ARUF", false, kFUWidth + kRAWidth + 1, true),
+      0);
+}
+
+TEST_F(CursorPositionTest, LTRText) {
+  EXPECT_EQ(GetWidth(ahem, "X", true, 0, 1), 100);
+
+  EXPECT_EQ(GetWidth(ahem, "XXX", true, 0, 1), 100);
+  EXPECT_EQ(GetWidth(ahem, "XXX", true, 0, 2), 200);
+  EXPECT_EQ(GetWidth(ahem, "XXX", true, 0, 3), 300);
+  EXPECT_EQ(GetWidth(ahem, "XXX", true, 1, 2), 100);
+  EXPECT_EQ(GetWidth(ahem, "XXX", true, 1, 3), 200);
+  EXPECT_EQ(GetWidth(ahem, "XXX", true, 2, 3), 100);
+}
+
+TEST_F(CursorPositionTest, LTRLigature) {
+  const float kFUWidth = GetWidth(megalopolis, "FU", true);
+  const float kRAWidth = GetWidth(megalopolis, "RA", true);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 0, 1), kFUWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 0, 2), kFUWidth, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 0, 3),
+              kFUWidth + kRAWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 0, 4), kFUWidth + kRAWidth,
+              1.0);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 1, 2), kFUWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 1, 3),
+              kFUWidth / 2 + kRAWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 1, 4),
+              kFUWidth / 2 + kRAWidth, 1.0);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 2, 3), kRAWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 2, 4), kRAWidth, 1.0);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "FURA", true, 3, 4), kRAWidth / 2, 1.0);
+
+  const float kFFIWidth = GetWidth(roboto, "ffi", true);
+  const float kFFWidth = GetWidth(roboto, "ff", true);
+  const float kIWidth = GetWidth(roboto, u"î", true);
+
+  EXPECT_NEAR(GetWidth(roboto, "ffi", true, 0, 1), kFFIWidth / 3.0, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, "ffi", true, 0, 2), kFFIWidth * 2.0 / 3.0, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, "ffi", true, 0, 3), kFFIWidth, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, "ffi", true, 1, 2), kFFIWidth / 3.0, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, "ffi", true, 1, 3), kFFIWidth * 2.0 / 3.0, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, "ffi", true, 2, 3), kFFIWidth / 3.0, 1.0);
+
+  EXPECT_NEAR(GetWidth(roboto, u"ffî", true, 0, 1), kFFWidth / 2.0, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, u"ffî", true, 0, 2), kFFWidth, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, u"ffî", true, 0, 3), kFFWidth + kIWidth, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, u"ffî", true, 1, 2), kFFWidth / 2.0, 1.0);
+  EXPECT_NEAR(GetWidth(roboto, u"ffî", true, 1, 3), kFFWidth / 2.0 + kIWidth,
+              1.0);
+  EXPECT_NEAR(GetWidth(roboto, u"ffî", true, 2, 3), kIWidth, 1.0);
+}
+
+TEST_F(CursorPositionTest, RTLText) {
+  // The widths below are from the final shaped version, not from the single
+  // characters. They were extracted with "hb-shape --font-size=100"
+
+  EXPECT_EQ(GetWidth(amiri, u"ت", false, 0, 1), 93);
+
+  const float kAboveKhaWidth = 55;
+  const float kAboveTaWidth = 10;
+  EXPECT_NEAR(GetWidth(amiri, u"تخ", false, 0, 1), kAboveKhaWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"تخ", false, 0, 2),
+              kAboveKhaWidth + kAboveTaWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"تخ", false, 1, 2), kAboveTaWidth, 1.0);
+
+  const float kTaWidth = 75;
+  const float kKhaWidth = 7;
+  const float kLamWidth = 56;
+  const float kAlifWidth = 22;
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 0, 1), kAlifWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 0, 2), kAlifWidth + kLamWidth,
+              1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 0, 3),
+              kAlifWidth + kLamWidth + kKhaWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 0, 4),
+              kAlifWidth + kLamWidth + kKhaWidth + kTaWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 1, 2), kLamWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 1, 3), kLamWidth + kKhaWidth,
+              1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 1, 4),
+              kLamWidth + kKhaWidth + kTaWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 2, 3), kKhaWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 2, 4), kKhaWidth + kTaWidth, 1.0);
+  EXPECT_NEAR(GetWidth(amiri, u"الخط", false, 3, 4), kTaWidth, 1.0);
+
+  const float kMeemWidth = GetWidth(amiri, u"م", false);
+  EXPECT_EQ(GetWidth(amiri, u"مَ", false, 0, 1), kMeemWidth);
+  EXPECT_EQ(GetWidth(amiri, u"مَ", false, 0, 2), kMeemWidth);
+  EXPECT_EQ(GetWidth(amiri, u"مَ", false, 1, 2), kMeemWidth);
+}
+
+TEST_F(CursorPositionTest, RTLLigature) {
+  const float kFUWidth = GetWidth(megalopolis, "FU", true);
+  const float kRAWidth = GetWidth(megalopolis, "RA", true);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 0, 1), kRAWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 0, 2), kRAWidth, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 0, 3),
+              kRAWidth + kFUWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 0, 4), kRAWidth + kFUWidth,
+              1.0);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 1, 2), kRAWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 1, 3),
+              kRAWidth / 2 + kFUWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 1, 4),
+              kRAWidth / 2 + kFUWidth, 1.0);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 2, 3), kFUWidth / 2, 1.0);
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 2, 4), kFUWidth, 1.0);
+
+  EXPECT_NEAR(GetWidth(megalopolis, "ARUF", false, 3, 4), kFUWidth / 2, 1.0);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font.cc b/third_party/blink/renderer/platform/fonts/font.cc
index 6c1378c..140c9e34 100644
--- a/third_party/blink/renderer/platform/fonts/font.cc
+++ b/third_party/blink/renderer/platform/fonts/font.cc
@@ -390,10 +390,11 @@
 
 int Font::OffsetForPosition(const TextRun& run,
                             float x_float,
-                            bool include_partial_glyphs) const {
+                            IncludePartialGlyphsOption partial_glyphs,
+                            BreakGlyphsOption break_glyphs) const {
   FontCachePurgePreventer purge_preventer;
   CachingWordShaper shaper(*this);
-  return shaper.OffsetForPosition(run, x_float, include_partial_glyphs);
+  return shaper.OffsetForPosition(run, x_float, partial_glyphs, break_glyphs);
 }
 
 ShapeCache* Font::GetShapeCache() const {
diff --git a/third_party/blink/renderer/platform/fonts/font.h b/third_party/blink/renderer/platform/fonts/font.h
index d2fad11..de3a27c 100644
--- a/third_party/blink/renderer/platform/fonts/font.h
+++ b/third_party/blink/renderer/platform/fonts/font.h
@@ -143,7 +143,8 @@
 
   int OffsetForPosition(const TextRun&,
                         float position,
-                        bool include_partial_glyphs) const;
+                        IncludePartialGlyphsOption,
+                        BreakGlyphsOption) const;
   FloatRect SelectionRectForText(const TextRun&,
                                  const FloatPoint&,
                                  int h,
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
index 0006c16d..41f5fa9c 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
@@ -80,13 +80,15 @@
   return total_width;
 }
 
-int CachingWordShaper::OffsetForPosition(const TextRun& run,
-                                         float target_x,
-                                         bool include_partial_glyphs) {
+int CachingWordShaper::OffsetForPosition(
+    const TextRun& run,
+    float target_x,
+    IncludePartialGlyphsOption partial_glyphs,
+    BreakGlyphsOption break_glyphs) {
   ShapeResultBuffer buffer;
   ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
 
-  return buffer.OffsetForPosition(run, target_x, include_partial_glyphs);
+  return buffer.OffsetForPosition(run, target_x, partial_glyphs, break_glyphs);
 }
 
 void CachingWordShaper::FillResultBuffer(const TextRunPaintInfo& run_info,
@@ -101,7 +103,7 @@
   ShapeResultBuffer buffer;
   float total_width = ShapeResultsForRun(GetShapeCache(), &font_, run, &buffer);
 
-  return buffer.GetCharacterRange(run.Direction(), total_width, from, to);
+  return buffer.GetCharacterRange(total_width, run.Direction(), from, to);
 }
 
 Vector<CharacterRange> CachingWordShaper::IndividualCharacterRanges(
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
index ab719be..1adfe22 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.h
@@ -55,7 +55,8 @@
               FloatRect* glyph_bounds);
   int OffsetForPosition(const TextRun&,
                         float target_x,
-                        bool include_partial_glyphs);
+                        IncludePartialGlyphsOption,
+                        BreakGlyphsOption);
 
   void FillResultBuffer(const TextRunPaintInfo&, ShapeResultBuffer*);
   CharacterRange GetCharacterRange(const TextRun&, unsigned from, unsigned to);
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc b/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc
index 0a23914..f9e4c5c 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc
@@ -48,6 +48,7 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
 #include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
 #include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
 #include "third_party/blink/renderer/platform/wtf/compiler.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
@@ -337,10 +338,13 @@
   // Here we need to specify glyph positions.
   BufferSlice next_slice;
   for (const BufferSlice* current_slice = &slice;;) {
+    Vector<unsigned> graphemes;
+    GraphemesClusterList(text_, current_slice->start_character_index,
+                         current_slice->num_characters, &graphemes);
     ShapeResult::RunInfo* run = new ShapeResult::RunInfo(
         current_font, direction, canvas_rotation, script,
         current_slice->start_character_index, current_slice->num_glyphs,
-        current_slice->num_characters);
+        current_slice->num_characters, graphemes);
     shape_result->InsertRun(base::WrapUnique(run),
                             current_slice->start_glyph_index,
                             current_slice->num_glyphs, range_data->buffer);
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc
index bd338bf0..33f63fd 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc
@@ -673,16 +673,24 @@
   Font ahem = CreateAhem(10);
   scoped_refptr<ShapeResult> result =
       SplitRun(shaper.Shape(&ahem, TextDirection::kLtr), 2);
-  EXPECT_EQ(data.offset_ltr, result->OffsetForPosition(data.position, false));
-  EXPECT_EQ(data.hit_test_ltr, result->OffsetForPosition(data.position, true));
+  EXPECT_EQ(data.offset_ltr,
+            result->OffsetForPosition(data.position, OnlyFullGlyphs,
+                                      DontBreakGlyphs));
+  EXPECT_EQ(data.hit_test_ltr,
+            result->OffsetForPosition(data.position, IncludePartialGlyphs,
+                                      DontBreakGlyphs));
   EXPECT_EQ(data.fit_ltr_ltr,
             result->OffsetToFit(data.position, TextDirection::kLtr));
   EXPECT_EQ(data.fit_ltr_rtl,
             result->OffsetToFit(data.position, TextDirection::kRtl));
 
   result = SplitRun(shaper.Shape(&ahem, TextDirection::kRtl), 3);
-  EXPECT_EQ(data.offset_rtl, result->OffsetForPosition(data.position, false));
-  EXPECT_EQ(data.hit_test_rtl, result->OffsetForPosition(data.position, true));
+  EXPECT_EQ(data.offset_rtl,
+            result->OffsetForPosition(data.position, OnlyFullGlyphs,
+                                      DontBreakGlyphs));
+  EXPECT_EQ(data.hit_test_rtl,
+            result->OffsetForPosition(data.position, IncludePartialGlyphs,
+                                      DontBreakGlyphs));
   EXPECT_EQ(data.fit_rtl_ltr,
             result->OffsetToFit(data.position, TextDirection::kLtr));
   EXPECT_EQ(data.fit_rtl_rtl,
@@ -728,14 +736,15 @@
 
 // A Value-Parameterized Test class to test OffsetForPosition() with
 // |include_partial_glyphs| parameter.
-class IncludePartialGlyphs : public HarfBuzzShaperTest,
-                             public testing::WithParamInterface<bool> {};
+class IncludePartialGlyphsTest : public HarfBuzzShaperTest,
+                                 public ::testing::WithParamInterface<bool> {};
 
 INSTANTIATE_TEST_CASE_P(OffsetForPositionTest,
-                        IncludePartialGlyphs,
-                        testing::Bool());
+                        IncludePartialGlyphsTest,
+                        ::testing::Bool());
 
-TEST_P(IncludePartialGlyphs, OffsetForPositionMatchesPositionForOffsetLatin) {
+TEST_P(IncludePartialGlyphsTest,
+       OffsetForPositionMatchesPositionForOffsetLatin) {
   String string = To16Bit("Hello World!", 12);
   TextDirection direction = TextDirection::kLtr;
 
@@ -743,35 +752,39 @@
   scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
 
   bool include_partial_glyphs = GetParam();
-  EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0),
-                                          include_partial_glyphs));
-  EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1),
-                                          include_partial_glyphs));
-  EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2),
-                                          include_partial_glyphs));
-  EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3),
-                                          include_partial_glyphs));
-  EXPECT_EQ(4u, result->OffsetForPosition(result->PositionForOffset(4),
-                                          include_partial_glyphs));
-  EXPECT_EQ(5u, result->OffsetForPosition(result->PositionForOffset(5),
-                                          include_partial_glyphs));
-  EXPECT_EQ(6u, result->OffsetForPosition(result->PositionForOffset(6),
-                                          include_partial_glyphs));
-  EXPECT_EQ(7u, result->OffsetForPosition(result->PositionForOffset(7),
-                                          include_partial_glyphs));
-  EXPECT_EQ(8u, result->OffsetForPosition(result->PositionForOffset(8),
-                                          include_partial_glyphs));
-  EXPECT_EQ(9u, result->OffsetForPosition(result->PositionForOffset(9),
-                                          include_partial_glyphs));
+  IncludePartialGlyphsOption partial =
+      include_partial_glyphs ? IncludePartialGlyphs : OnlyFullGlyphs;
+
+  EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(4u, result->OffsetForPosition(result->PositionForOffset(4), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(5u, result->OffsetForPosition(result->PositionForOffset(5), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(6u, result->OffsetForPosition(result->PositionForOffset(6), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(7u, result->OffsetForPosition(result->PositionForOffset(7), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(8u, result->OffsetForPosition(result->PositionForOffset(8), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(9u, result->OffsetForPosition(result->PositionForOffset(9), partial,
+                                          DontBreakGlyphs));
   EXPECT_EQ(10u, result->OffsetForPosition(result->PositionForOffset(10),
-                                           include_partial_glyphs));
+                                           partial, DontBreakGlyphs));
   EXPECT_EQ(11u, result->OffsetForPosition(result->PositionForOffset(11),
-                                           include_partial_glyphs));
+                                           partial, DontBreakGlyphs));
   EXPECT_EQ(12u, result->OffsetForPosition(result->PositionForOffset(12),
-                                           include_partial_glyphs));
+                                           partial, DontBreakGlyphs));
 }
 
-TEST_P(IncludePartialGlyphs, OffsetForPositionMatchesPositionForOffsetArabic) {
+TEST_P(IncludePartialGlyphsTest,
+       OffsetForPositionMatchesPositionForOffsetArabic) {
   UChar arabic_string[] = {0x628, 0x64A, 0x629};
   TextDirection direction = TextDirection::kRtl;
 
@@ -779,36 +792,43 @@
   scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
 
   bool include_partial_glyphs = GetParam();
-  EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0),
-                                          include_partial_glyphs));
-  EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1),
-                                          include_partial_glyphs));
-  EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2),
-                                          include_partial_glyphs));
-  EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3),
-                                          include_partial_glyphs));
+  IncludePartialGlyphsOption partial =
+      include_partial_glyphs ? IncludePartialGlyphs : OnlyFullGlyphs;
+
+  EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3), partial,
+                                          DontBreakGlyphs));
 }
 
-TEST_P(IncludePartialGlyphs, OffsetForPositionMatchesPositionForOffsetMixed) {
+TEST_P(IncludePartialGlyphsTest,
+       OffsetForPositionMatchesPositionForOffsetMixed) {
   UChar mixed_string[] = {0x628, 0x64A, 0x629, 0xE20, 0x65E5, 0x62};
   HarfBuzzShaper shaper(String(mixed_string, 6));
   scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
 
   bool include_partial_glyphs = GetParam();
-  EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0),
-                                          include_partial_glyphs));
-  EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1),
-                                          include_partial_glyphs));
-  EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2),
-                                          include_partial_glyphs));
-  EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3),
-                                          include_partial_glyphs));
-  EXPECT_EQ(4u, result->OffsetForPosition(result->PositionForOffset(4),
-                                          include_partial_glyphs));
-  EXPECT_EQ(5u, result->OffsetForPosition(result->PositionForOffset(5),
-                                          include_partial_glyphs));
-  EXPECT_EQ(6u, result->OffsetForPosition(result->PositionForOffset(6),
-                                          include_partial_glyphs));
+  IncludePartialGlyphsOption partial =
+      include_partial_glyphs ? IncludePartialGlyphs : OnlyFullGlyphs;
+
+  EXPECT_EQ(0u, result->OffsetForPosition(result->PositionForOffset(0), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(1u, result->OffsetForPosition(result->PositionForOffset(1), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(2u, result->OffsetForPosition(result->PositionForOffset(2), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(3u, result->OffsetForPosition(result->PositionForOffset(3), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(4u, result->OffsetForPosition(result->PositionForOffset(4), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(5u, result->OffsetForPosition(result->PositionForOffset(5), partial,
+                                          DontBreakGlyphs));
+  EXPECT_EQ(6u, result->OffsetForPosition(result->PositionForOffset(6), partial,
+                                          DontBreakGlyphs));
 }
 
 TEST_F(HarfBuzzShaperTest, CachedOffsetPositionMappingForOffsetLatin) {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index d3316a8..88e9c01 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -101,90 +101,221 @@
   return XPositionForOffset(offset, adjust_mid_cluster);
 }
 
+unsigned ShapeResult::RunInfo::NumGraphemes(unsigned start,
+                                            unsigned end) const {
+  if (graphemes_.size() == 0 || start >= num_characters_)
+    return 0;
+  DCHECK_LT(start, end);
+  DCHECK_LE(end, num_characters_);
+  return graphemes_[end - 1] - graphemes_[start] + 1;
+}
+
+// XPositionForOffset returns the X position (in layout space) from the
+// beginning of the run to the beginning of the cluster of glyphs for X
+// character.
+// For RTL, beginning means the right most side of the cluster.
+// Characters may spawn multiple glyphs.
+// In the case that multiple characters form a Unicode grapheme cluster, we
+// distribute the width of the grapheme cluster among the number of cursor
+// positions returned by cursor-based TextBreakIterator.
 float ShapeResult::RunInfo::XPositionForOffset(
     unsigned offset,
     AdjustMidCluster adjust_mid_cluster) const {
   DCHECK_LE(offset, num_characters_);
   const unsigned num_glyphs = glyph_data_.size();
-  unsigned glyph_index = 0;
-  float position = 0;
-  if (Rtl()) {
-    while (glyph_index < num_glyphs &&
-           glyph_data_[glyph_index].character_index > offset) {
-      position += glyph_data_[glyph_index].advance;
-      ++glyph_index;
-    }
-    // If |glyph_index| is at the end, the glyph for |offset| is missing, along
-    // with all glyphs before it. We can't adjust position to the start
-    // direction.
-    if (glyph_index == num_glyphs)
-      return position;
-    // Adjust offset if it's not on the cluster boundary. In RTL, this means
-    // that the adjusted position is the left side of the character.
-    if (adjust_mid_cluster == AdjustMidCluster::kToEnd &&
-        glyph_data_[glyph_index].character_index < offset) {
-      return position;
-    }
-    // For RTL, we need to return the right side boundary of the character.
-    // Add advance of glyphs which are part of the character.
-    while (glyph_index < num_glyphs - 1 &&
-           glyph_data_[glyph_index].character_index ==
-               glyph_data_[glyph_index + 1].character_index) {
-      position += glyph_data_[glyph_index].advance;
-      ++glyph_index;
-    }
-    position += glyph_data_[glyph_index].advance;
-  } else {
-    while (glyph_index < num_glyphs &&
-           glyph_data_[glyph_index].character_index < offset) {
-      position += glyph_data_[glyph_index].advance;
-      ++glyph_index;
-    }
-    // Adjust offset if it's not on the cluster boundary.
-    if (adjust_mid_cluster == AdjustMidCluster::kToStart && glyph_index &&
-        (glyph_index < num_glyphs ? glyph_data_[glyph_index].character_index
-                                  : num_characters_) > offset) {
-      offset = glyph_data_[--glyph_index].character_index;
-      for (; glyph_data_[glyph_index].character_index == offset;
-           --glyph_index) {
-        position -= glyph_data_[glyph_index].advance;
-        if (!glyph_index)
-          break;
+
+  // In this context, a glyph sequence is a sequence of glyphs that shares the
+  // same character_index and therefore represent the same interval of source
+  // characters. glyph_sequence_start marks the character index at the beginning
+  // of the interval of characters for which this glyph sequence was formed as
+  // the result of shaping; glyph_sequence_end marks the end of the interval of
+  // characters for which this glyph sequence was formed. [glyph_sequence_start,
+  // glyph_sequence_end) is inclusive on the start for the range of characters
+  // of the current sequence we are visiting.
+  unsigned glyph_sequence_start = 0;
+  unsigned glyph_sequence_end = num_characters_;
+  // the advance of the current glyph sequence.
+  float glyph_sequence_advance = 0.0;
+  // the accumulated advance up to the current glyph sequence.
+  float accumulated_position = 0;
+
+  if (!Rtl()) {
+    for (unsigned i = 0; i < num_glyphs; ++i) {
+      unsigned current_glyph_char_index = glyph_data_[i].character_index;
+      // If this glyph is still part of the same glyph sequence for the grapheme
+      // cluster at character index glyph_sequence_start, add its advance to the
+      // glyph_sequence's advance.
+      if (glyph_sequence_start == current_glyph_char_index) {
+        glyph_sequence_advance += glyph_data_[i].advance;
+        continue;
       }
+
+      // We are about to move out of a glyph sequence that contains offset, so
+      // the current glyph sequence is the one we are looking for.
+      if (glyph_sequence_start <= offset && offset < current_glyph_char_index) {
+        glyph_sequence_end = current_glyph_char_index;
+        break;
+      }
+
+      glyph_sequence_start = current_glyph_char_index;
+      // Since we always update glyph_sequence_end when we break, set this to
+      // last_character in case this is the final iteration of the loop.
+      glyph_sequence_end = num_characters_;
+      accumulated_position += glyph_sequence_advance;
+      glyph_sequence_advance = glyph_data_[i].advance;
+    }
+
+  } else {
+    glyph_sequence_start = glyph_sequence_end = num_characters_;
+
+    for (unsigned i = 0; i < num_glyphs; ++i) {
+      unsigned current_glyph_char_index = glyph_data_[i].character_index;
+      // If this glyph is still part of the same glyph sequence for the grapheme
+      // cluster at character index glyph_sequence_start, add its advance to the
+      // glyph_sequence's advance.
+      if (glyph_sequence_start == current_glyph_char_index) {
+        glyph_sequence_advance += glyph_data_[i].advance;
+        continue;
+      }
+
+      // We are about to move out of a glyph sequence that contains offset, so
+      // the current glyph sequence is the one we are looking for.
+      if (glyph_sequence_start <= offset && offset < glyph_sequence_end) {
+        break;
+      }
+
+      glyph_sequence_end = glyph_sequence_start;
+      glyph_sequence_start = current_glyph_char_index;
+      accumulated_position += glyph_sequence_advance;
+      glyph_sequence_advance = glyph_data_[i].advance;
     }
   }
-  return position;
+
+  // This is the character position inside the glyph sequence.
+  unsigned pos = offset - glyph_sequence_start;
+
+  // We calculate the number of Unicode grapheme clusters (actually cursor
+  // position stops) on the subset of characters. We use this to divide
+  // glyph_sequence_advance by the number of unicode grapheme clusters this
+  // glyph sequence was shaped for, and thus linearly interpolate the cursor
+  // position based on accumulated position and a fraction of
+  // glyph_sequence_advance.
+  unsigned graphemes = NumGraphemes(glyph_sequence_start, glyph_sequence_end);
+  if (graphemes > 1) {
+    DCHECK_GE(glyph_sequence_end, glyph_sequence_start);
+    unsigned size = glyph_sequence_end - glyph_sequence_start;
+    unsigned place = graphemes * pos / size;
+    pos -= place;
+    glyph_sequence_advance = glyph_sequence_advance / graphemes;
+    if (Rtl()) {
+      accumulated_position += glyph_sequence_advance * (graphemes - place - 1);
+    } else {
+      accumulated_position += glyph_sequence_advance * place;
+    }
+  }
+
+  // Re-adapt based on adjust_mid_cluster. On LTR, if we want AdjustToEnd and
+  // offset is not at the beginning, we need to jump to the right side of the
+  // grapheme. On RTL, if we want AdjustToStart and offset is not at the end, we
+  // need to jump to the left side of the grapheme.
+  if (!Rtl() && adjust_mid_cluster == AdjustMidCluster::kToEnd && pos != 0) {
+    accumulated_position += glyph_sequence_advance;
+  } else if (Rtl() && adjust_mid_cluster == AdjustMidCluster::kToEnd &&
+             pos != 0) {
+    accumulated_position -= glyph_sequence_advance;
+  }
+
+  if (Rtl()) {
+    // For RTL, we return the right side.
+    accumulated_position += glyph_sequence_advance;
+  }
+
+  return accumulated_position;
 }
 
+// In some ways, CharacterIndexForXPosition is the reverse of
+// XPositionForOffset. Given a target pixel distance on screen space, returns a
+// character index for the end of the interval that would be included within
+// that space. @break_glyphs_option controls wether we use grapheme information
+// to break glyphs into grapheme clusters and return character that are a part
+// of a glyph.
 void ShapeResult::RunInfo::CharacterIndexForXPosition(
     float target_x,
+    BreakGlyphsOption break_glyphs_option,
     GlyphIndexResult* result) const {
   DCHECK(target_x >= 0 && target_x <= width_);
   const unsigned num_glyphs = glyph_data_.size();
-  float current_x = 0;
-  unsigned glyph_index = 0;
 
-  while (true) {
-    unsigned current_character_index = glyph_data_[glyph_index].character_index;
-    float current_advance = glyph_data_[glyph_index].advance;
-    unsigned next_glyph_index = glyph_index + 1;
-    while (next_glyph_index < num_glyphs &&
-           current_character_index ==
-               glyph_data_[next_glyph_index].character_index)
-      current_advance += glyph_data_[next_glyph_index++].advance;
-    float next_x = current_x + current_advance;
-    if (target_x < next_x || next_glyph_index == num_glyphs) {
-      result->glyph_index = glyph_index;
-      result->next_glyph_index = next_glyph_index;
-      result->character_index = current_character_index;
-      result->origin_x = current_x;
-      result->advance = current_advance;
-      return;
-    }
-    current_x = next_x;
-    glyph_index = next_glyph_index;
+  result->origin_x = 0;
+  unsigned glyph_sequence_start = 0;
+  unsigned glyph_sequence_end = num_characters_;
+  result->advance = 0.0;
+
+  // on RTL, we start on the last index.
+  if (Rtl()) {
+    glyph_sequence_start = glyph_sequence_end = num_characters_;
   }
-  NOTREACHED();
+
+  for (unsigned i = 0; i < num_glyphs; ++i) {
+    unsigned current_glyph_char_index = glyph_data_[i].character_index;
+    // If the glyph is part of the same sequence, we just accumulate the
+    // advance.
+    if (glyph_sequence_start == current_glyph_char_index) {
+      result->advance += glyph_data_[i].advance;
+      continue;
+    }
+
+    // Since we are about to move to the next sequence of glyphs, check if
+    // the target falls inside it, if it does, we found our sequence.
+    if (result->origin_x + result->advance > target_x) {
+      if (!Rtl()) {
+        glyph_sequence_end = current_glyph_char_index;
+      }
+      break;
+    }
+
+    // Move to the next sequence, update accumulated_x.
+    if (Rtl()) {
+      // Notice that on RTL, as we move to our next sequence, we already know
+      // both bounds. Nonetheless, we still need to move forward so we can
+      // capture all glyphs of this sequence.
+      glyph_sequence_end = glyph_sequence_start;
+    }
+    glyph_sequence_start = current_glyph_char_index;
+    result->origin_x += result->advance;
+    result->advance = glyph_data_[i].advance;
+  }
+
+  // At this point, we have [glyph_sequence_start, glyph_sequence_end)
+  // representing a sequence of glyphs, of size glyph_sequence_advance. We
+  // linearly interpolate how much space each character takes, and reduce the
+  // sequence to only match the character size.
+  if (break_glyphs_option == BreakGlyphs) {
+    int graphemes = NumGraphemes(glyph_sequence_start, glyph_sequence_end);
+    if (graphemes > 1) {
+      float unit_size = result->advance / graphemes;
+      unsigned step = floor((target_x - result->origin_x) / unit_size);
+      unsigned glyph_length = glyph_sequence_end - glyph_sequence_start;
+      unsigned final_size = floor(glyph_length / graphemes);
+      result->origin_x += unit_size * step;
+      if (!Rtl()) {
+        glyph_sequence_start += step;
+        glyph_sequence_end = glyph_sequence_start + final_size;
+      } else {
+        glyph_sequence_end -= step;
+        glyph_sequence_start = glyph_sequence_end - final_size;
+      }
+      result->advance = unit_size;
+    }
+  }
+
+  if (!Rtl()) {
+    result->left_character_index = glyph_sequence_start;
+    result->right_character_index = glyph_sequence_end;
+  } else {
+    result->left_character_index = glyph_sequence_end;
+    result->right_character_index = glyph_sequence_start;
+  }
 }
 
 void HarfBuzzRunGlyphData::SetGlyphAndPositions(uint16_t glyph_id,
@@ -322,107 +453,111 @@
   return StartIndexForResult();
 }
 
-// Returns the offset of the character of |result| for LTR.
-unsigned ShapeResult::OffsetLtr(const GlyphIndexResult& result) const {
-  DCHECK(IsLtr(Direction()));
-  return result.characters_on_left_runs + result.character_index;
-}
-
-// Returns the offset of the character of |result| for RTL.
-unsigned ShapeResult::OffsetRtl(const GlyphIndexResult& result, float x) const {
-  DCHECK(IsRtl(Direction()));
-  if (!result.IsInRun())
-    return NumCharacters() - result.characters_on_left_runs;
-  // In RTL, the boundary belongs to the left character. This subtle difference
-  // allows round trips between OffsetForPoint and PointForOffset.
-  if (UNLIKELY(x == result.origin_x))
-    return OffsetLeftRtl(result);
-  return NumCharacters() - result.characters_on_left_runs -
-         runs_[result.run_index]->num_characters_ + result.character_index;
-}
-
-// Returns the offset of the character on the right of |result| for LTR.
-unsigned ShapeResult::OffsetRightLtr(const GlyphIndexResult& result) const {
-  DCHECK(IsLtr(Direction()));
-  if (result.run_index >= runs_.size())
-    return NumCharacters();
-  const RunInfo& run = *runs_[result.run_index];
-  return result.characters_on_left_runs +
-         (result.next_glyph_index < run.glyph_data_.size()
-              ? run.glyph_data_[result.next_glyph_index].character_index
-              : run.num_characters_);
-}
-
-// Returns the offset of the character on the left of |result| for RTL.
-unsigned ShapeResult::OffsetLeftRtl(const GlyphIndexResult& result) const {
-  DCHECK(IsRtl(Direction()));
-  if (!result.glyph_index)
-    return NumCharacters() - result.characters_on_left_runs;
-  const RunInfo& run = *runs_[result.run_index];
-  return NumCharacters() - result.characters_on_left_runs -
-         run.num_characters_ +
-         run.glyph_data_[result.glyph_index - 1].character_index;
-}
-
 // If the position is outside of the result, returns the start or the end offset
 // depends on the position.
 void ShapeResult::OffsetForPosition(float target_x,
+                                    BreakGlyphsOption break_glyphs_option,
                                     GlyphIndexResult* result) const {
-  if (target_x <= 0)
+  if (target_x <= 0) {
+    if (Rtl()) {
+      result->left_character_index = result->right_character_index =
+          NumCharacters();
+    }
     return;
+  }
 
-  unsigned characters_so_far = 0;
+  unsigned characters_so_far = Rtl() ? NumCharacters() : 0;
   float current_x = 0;
+
   for (unsigned i = 0; i < runs_.size(); ++i) {
     const RunInfo* run = runs_[i].get();
     if (!run)
       continue;
+    if (Rtl())
+      characters_so_far -= runs_[i]->num_characters_;
     float next_x = current_x + run->width_;
     float offset_for_run = target_x - current_x;
     if (offset_for_run >= 0 && offset_for_run < run->width_) {
       // The x value in question is within this script run.
-      run->CharacterIndexForXPosition(offset_for_run, result);
+      run->CharacterIndexForXPosition(offset_for_run, break_glyphs_option,
+                                      result);
       result->run_index = i;
       result->characters_on_left_runs = characters_so_far;
+      if (Rtl()) {
+        result->left_character_index =
+            characters_so_far + result->left_character_index;
+        result->right_character_index =
+            characters_so_far + result->right_character_index;
+        DCHECK_LE(result->left_character_index, NumCharacters() + 1);
+        DCHECK_LE(result->right_character_index, NumCharacters());
+      } else {
+        result->left_character_index += characters_so_far;
+        result->right_character_index += characters_so_far;
+        DCHECK_LE(result->left_character_index, NumCharacters());
+        DCHECK_LE(result->right_character_index, NumCharacters() + 1);
+      }
       result->origin_x += current_x;
-      DCHECK_LE(result->characters_on_left_runs + result->character_index,
-                NumCharacters());
       return;
     }
-    characters_so_far += run->num_characters_;
+    if (!Rtl())
+      characters_so_far += run->num_characters_;
     current_x = next_x;
   }
 
-  result->run_index = runs_.size();
-  result->characters_on_left_runs = characters_so_far;
-}
-
-unsigned ShapeResult::OffsetForPosition(float x) const {
-  GlyphIndexResult result;
-  OffsetForPosition(x, &result);
-  return IsLtr(Direction()) ? OffsetLtr(result) : OffsetRtl(result, x);
-}
-
-unsigned ShapeResult::OffsetForHitTest(float x) const {
-  GlyphIndexResult result;
-  OffsetForPosition(x, &result);
-  if (IsLtr(Direction())) {
-    if (result.IsInRun() && x > result.origin_x + result.advance / 2)
-      return OffsetRightLtr(result);
-    return OffsetLtr(result);
+  if (Rtl()) {
+    result->left_character_index = 0;
+    result->right_character_index = 0;
+  } else {
+    result->left_character_index += characters_so_far;
+    result->right_character_index += characters_so_far;
   }
-  if (result.IsInRun() && x <= result.origin_x + result.advance / 2)
-    return OffsetLeftRtl(result);
-  return OffsetRtl(result, x);
+
+  result->run_index = runs_.size() - 1;
+  result->characters_on_left_runs = characters_so_far;
+
+  DCHECK_LE(result->left_character_index, NumCharacters());
+  DCHECK_LE(result->right_character_index, NumCharacters() + 1);
+}
+
+unsigned ShapeResult::OffsetForPosition(
+    float x,
+    BreakGlyphsOption break_glyphs_option) const {
+  GlyphIndexResult result;
+  OffsetForPosition(x, break_glyphs_option, &result);
+
+  // For LTR, the offset is always the left one.
+  if (!Rtl())
+    return result.left_character_index;
+
+  // For RTL the offset is the right one, except that the interval is open
+  // on other side. So in case we are exactly at the boundary, we return the
+  // left index.
+  if (x == result.origin_x)
+    return result.left_character_index;
+  return result.right_character_index;
+}
+
+unsigned ShapeResult::OffsetForHitTest(
+    float x,
+    BreakGlyphsOption break_glyphs_option) const {
+  GlyphIndexResult result;
+  OffsetForPosition(x, break_glyphs_option, &result);
+
+  if (x - result.origin_x <= result.advance / 2)
+    return result.left_character_index;
+  return result.right_character_index;
 }
 
 unsigned ShapeResult::OffsetToFit(float x, TextDirection line_direction) const {
   GlyphIndexResult result;
-  OffsetForPosition(x, &result);
-  if (IsLtr(line_direction)) {
-    return IsLtr(Direction()) ? OffsetLtr(result) : OffsetLeftRtl(result);
-  }
-  return IsRtl(Direction()) ? OffsetRtl(result, x) : OffsetRightLtr(result);
+  OffsetForPosition(x, DontBreakGlyphs, &result);
+
+  if (IsLtr(line_direction))
+    return result.left_character_index;
+
+  if (x == result.origin_x && IsRtl(Direction()))
+    return result.left_character_index;
+  return result.right_character_index;
 }
 
 float ShapeResult::PositionForOffset(
@@ -799,21 +934,24 @@
 // synthesize a run without glyphs.
 void ShapeResult::InsertRunForIndex(unsigned start_character_index) {
   DCHECK(runs_.IsEmpty());
+  // TODO(fserb): do we need the proper graphemes?
+  Vector<unsigned> graphemes;
   runs_.push_back(std::make_unique<RunInfo>(
       primary_font_.get(), !Rtl() ? HB_DIRECTION_LTR : HB_DIRECTION_RTL,
       CanvasRotationInVertical::kRegular, HB_SCRIPT_UNKNOWN,
-      start_character_index, 0, num_characters_));
+      start_character_index, 0, num_characters_, graphemes));
 }
 
 ShapeResult::RunInfo* ShapeResult::InsertRunForTesting(
     unsigned start_index,
     unsigned num_characters,
     TextDirection direction,
-    Vector<uint16_t> safe_break_offsets) {
+    Vector<uint16_t> safe_break_offsets,
+    Vector<unsigned> graphemes) {
   std::unique_ptr<RunInfo> run = std::make_unique<ShapeResult::RunInfo>(
       nullptr, IsLtr(direction) ? HB_DIRECTION_LTR : HB_DIRECTION_RTL,
       CanvasRotationInVertical::kRegular, HB_SCRIPT_COMMON, start_index,
-      num_characters, num_characters);
+      num_characters, num_characters, std::move(graphemes));
   unsigned i = 0;
   for (auto& glyph_data : run->glyph_data_)
     glyph_data.SetGlyphAndPositions(0, i++, 0, FloatSize(), false);
@@ -1049,9 +1187,12 @@
   const SimpleFontData* font_data = font->PrimaryFont();
   // Tab characters are always LTR or RTL, not TTB, even when
   // isVerticalAnyUpright().
+  // We don't pass proper graphemes for tabulation.
+  Vector<unsigned> graphemes;
   std::unique_ptr<ShapeResult::RunInfo> run = std::make_unique<RunInfo>(
       font_data, text_run.Rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR,
-      CanvasRotationInVertical::kRegular, HB_SCRIPT_COMMON, 0, count, count);
+      CanvasRotationInVertical::kRegular, HB_SCRIPT_COMMON, 0, count, count,
+      graphemes);
   float position = text_run.XPos() + position_offset;
   float start_position = position;
   for (unsigned i = 0; i < count; i++) {
@@ -1213,7 +1354,7 @@
   // TODO(layout-dev): Remove once CharacterPositionData::OffsetForPosition
   // properly supports RTL.
   if (Rtl())
-    return OffsetForPosition(x);
+    return OffsetForPosition(x, DontBreakGlyphs);
 
   DCHECK(character_position_);
   return character_position_->OffsetForPosition(x);
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
index 0342539..926faed 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.h
@@ -67,6 +67,22 @@
   unsigned safe_to_break_before : 1;
 };
 
+// There are two options for how OffsetForPosition behaves:
+// IncludePartialGlyphs - decides what to do when the position hits more than
+// 50% of the glyph. If enabled, we count that glyph, if disable we don't.
+enum IncludePartialGlyphsOption {
+  OnlyFullGlyphs,
+  IncludePartialGlyphs,
+};
+
+// BreakGlyphs - allows OffsetForPosition to consider graphemes separations
+// inside a glyph. It allows the function to return a point inside a glyph when
+// multiple graphemes share a glyph (for example, in a ligature)
+enum BreakGlyphsOption {
+  DontBreakGlyphs,
+  BreakGlyphs,
+};
+
 class PLATFORM_EXPORT ShapeResult : public RefCounted<ShapeResult> {
  public:
   static scoped_refptr<ShapeResult> Create(const Font* font,
@@ -124,18 +140,21 @@
 
   // Returns the offset, relative to StartIndexForResult, whose (origin,
   // origin+advance) contains |x|.
-  unsigned OffsetForPosition(float x) const;
-
+  unsigned OffsetForPosition(float x, BreakGlyphsOption) const;
   // Returns the offset whose glyph boundary is nearest to |x|. Depends on
   // whether |x| is on the left-half or the right-half of the glyph, it
   // determines the left-boundary or the right-boundary, then computes the
   // offset from the bidi direction.
-  unsigned OffsetForHitTest(float x) const;
+  unsigned OffsetForHitTest(float x, BreakGlyphsOption) const;
   // Returns the offset that can fit to between |x| and the left or the right
   // edge. The side of the edge is determined by |line_direction|.
   unsigned OffsetToFit(float x, TextDirection line_direction) const;
-  unsigned OffsetForPosition(float x, bool include_partial_glyphs) const {
-    return !include_partial_glyphs ? OffsetForPosition(x) : OffsetForHitTest(x);
+  unsigned OffsetForPosition(float x,
+                             IncludePartialGlyphsOption include_partial_glyphs,
+                             BreakGlyphsOption break_glyphs_option) const {
+    return include_partial_glyphs == OnlyFullGlyphs
+               ? OffsetForPosition(x, break_glyphs_option)
+               : OffsetForHitTest(x, break_glyphs_option);
   }
 
   // Returns the position for a given offset, relative to StartIndexForResult.
@@ -198,7 +217,8 @@
   RunInfo* InsertRunForTesting(unsigned start_index,
                                unsigned num_characters,
                                TextDirection,
-                               Vector<uint16_t> safe_break_offsets = {});
+                               Vector<uint16_t> safe_break_offsets = {},
+                               Vector<unsigned> graphemes = {});
 #if DCHECK_IS_ON()
   void CheckConsistency() const;
 #endif
@@ -221,26 +241,21 @@
     unsigned run_index = 0;
     // The total number of characters of runs_[0..run_index - 1].
     unsigned characters_on_left_runs = 0;
-    unsigned character_index = 0;
-    unsigned glyph_index = 0;
-    // |next_glyph_index| may not be |glyph_index| + 1 when a cluster is of
-    // multiple glyphs; i.e., ligatures or combining glyphs.
-    unsigned next_glyph_index = 0;
+
+    // Those are the left and right character indexes of the group of glyphs
+    // that were selected by OffsetForPosition.
+    unsigned left_character_index = 0;
+    unsigned right_character_index = 0;
+
     // The glyph origin of the glyph.
     float origin_x = 0;
     // The advance of the glyph.
     float advance = 0;
-
-    // True if the position was found on a run. False otherwise.
-    bool IsInRun() const { return next_glyph_index; }
   };
 
-  unsigned OffsetLtr(const GlyphIndexResult&) const;
-  unsigned OffsetRtl(const GlyphIndexResult&, float x) const;
-  unsigned OffsetRightLtr(const GlyphIndexResult&) const;
-  unsigned OffsetLeftRtl(const GlyphIndexResult&) const;
-
-  void OffsetForPosition(float target_x, GlyphIndexResult*) const;
+  void OffsetForPosition(float target_x,
+                         BreakGlyphsOption,
+                         GlyphIndexResult*) const;
 
   // Helper class storing a map between offsets and x-positions.
   // Unlike the RunInfo and GlyphData structures in ShapeResult, which operates
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc
index 4aaab9f3..1800d807 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
 
 #include "third_party/blink/renderer/platform/fonts/character_range.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
@@ -27,6 +28,13 @@
   return GetCharacterRangeInternal(results, direction, total_width, from, to);
 }
 
+CharacterRange ShapeResultBuffer::GetCharacterRange(float total_width,
+                                                    TextDirection direction,
+                                                    unsigned from,
+                                                    unsigned to) const {
+  return GetCharacterRangeInternal(results_, direction, total_width, from, to);
+}
+
 CharacterRange ShapeResultBuffer::GetCharacterRangeInternal(
     const Vector<scoped_refptr<const ShapeResult>, 64>& results,
     TextDirection direction,
@@ -123,13 +131,6 @@
   return CharacterRange(to_x, from_x, -min_y, max_y);
 }
 
-CharacterRange ShapeResultBuffer::GetCharacterRange(TextDirection direction,
-                                                    float total_width,
-                                                    unsigned from,
-                                                    unsigned to) const {
-  return GetCharacterRangeInternal(results_, direction, total_width, from, to);
-}
-
 void ShapeResultBuffer::AddRunInfoRanges(const ShapeResult::RunInfo& run_info,
                                          float offset,
                                          Vector<CharacterRange>& ranges) {
@@ -172,9 +173,11 @@
   return ranges;
 }
 
-int ShapeResultBuffer::OffsetForPosition(const TextRun& run,
-                                         float target_x,
-                                         bool include_partial_glyphs) const {
+int ShapeResultBuffer::OffsetForPosition(
+    const TextRun& run,
+    float target_x,
+    IncludePartialGlyphsOption partial_glyphs,
+    BreakGlyphsOption break_glyphs) const {
   unsigned total_offset;
   if (run.Rtl()) {
     total_offset = run.length();
@@ -184,8 +187,8 @@
         continue;
       total_offset -= word_result->NumCharacters();
       if (target_x >= 0 && target_x <= word_result->Width()) {
-        int offset_for_word =
-            word_result->OffsetForPosition(target_x, include_partial_glyphs);
+        int offset_for_word = word_result->OffsetForPosition(
+            target_x, partial_glyphs, break_glyphs);
         return total_offset + offset_for_word;
       }
       target_x -= word_result->Width();
@@ -195,8 +198,8 @@
     for (const auto& word_result : results_) {
       if (!word_result)
         continue;
-      int offset_for_word =
-          word_result->OffsetForPosition(target_x, include_partial_glyphs);
+      int offset_for_word = word_result->OffsetForPosition(
+          target_x, partial_glyphs, break_glyphs);
       DCHECK_GE(offset_for_word, 0);
       total_offset += offset_for_word;
       if (target_x >= 0 && target_x <= word_result->Width())
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h
index f1092b5..ace7dcd 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h
@@ -33,11 +33,12 @@
 
   bool HasVerticalOffsets() const { return has_vertical_offsets_; }
 
-  int OffsetForPosition(const TextRun&,
+  int OffsetForPosition(const TextRun& run,
                         float target_x,
-                        bool include_partial_glyphs) const;
-  CharacterRange GetCharacterRange(TextDirection,
-                                   float total_width,
+                        IncludePartialGlyphsOption,
+                        BreakGlyphsOption) const;
+  CharacterRange GetCharacterRange(float total_width,
+                                   TextDirection,
                                    unsigned from,
                                    unsigned to) const;
   Vector<CharacterRange> IndividualCharacterRanges(TextDirection,
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
index 5d479ea0..3609c766 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h
@@ -37,6 +37,7 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -72,12 +73,14 @@
           hb_script_t script,
           unsigned start_index,
           unsigned num_glyphs,
-          unsigned num_characters)
+          unsigned num_characters,
+          Vector<unsigned> graphemes)
       : font_data_(const_cast<SimpleFontData*>(font)),
         direction_(dir),
         canvas_rotation_(canvas_rotation),
         script_(script),
         glyph_data_(num_glyphs),
+        graphemes_(graphemes),
         start_index_(start_index),
         num_characters_(num_characters),
         width_(0.0f) {}
@@ -88,6 +91,7 @@
         canvas_rotation_(other.canvas_rotation_),
         script_(other.script_),
         glyph_data_(other.glyph_data_),
+        graphemes_(other.graphemes_),
         start_index_(other.start_index_),
         num_characters_(other.num_characters_),
         width_(other.width_) {}
@@ -100,12 +104,21 @@
   unsigned PreviousSafeToBreakOffset(unsigned) const;
   float XPositionForVisualOffset(unsigned, AdjustMidCluster) const;
   float XPositionForOffset(unsigned, AdjustMidCluster) const;
-  void CharacterIndexForXPosition(float, GlyphIndexResult*) const;
+  void CharacterIndexForXPosition(float,
+                                  BreakGlyphsOption,
+                                  GlyphIndexResult*) const;
+  void SetGlyphAndPositions(unsigned index,
+                            uint16_t glyph_id,
+                            float advance,
+                            float offset_x,
+                            float offset_y);
 
   size_t GlyphToCharacterIndex(size_t i) const {
     return start_index_ + glyph_data_[i].character_index;
   }
 
+  unsigned NumGraphemes(unsigned start, unsigned end) const;
+
   // For memory reporting.
   size_t ByteSize() const {
     return sizeof(this) + glyph_data_.size() * sizeof(HarfBuzzRunGlyphData);
@@ -159,9 +172,18 @@
     auto glyphs = FindGlyphDataRange(start, end);
     unsigned number_of_glyphs = std::distance(glyphs.begin, glyphs.end);
 
+    Vector<unsigned> sub_graphemes;
+    if (graphemes_.size()) {
+      sub_graphemes.resize(number_of_characters);
+      for (unsigned i = 0; i < number_of_characters; ++i) {
+        sub_graphemes[i] = graphemes_[start + i];
+      }
+    }
+
     auto run = std::make_unique<RunInfo>(
         font_data_.get(), direction_, canvas_rotation_, script_,
-        start_index_ + start, number_of_glyphs, number_of_characters);
+        start_index_ + start, number_of_glyphs, number_of_characters,
+        std::move(sub_graphemes));
 
     static_assert(base::is_trivially_copyable<HarfBuzzRunGlyphData>::value,
                   "HarfBuzzRunGlyphData should be trivially copyable");
@@ -270,6 +292,11 @@
   CanvasRotationInVertical canvas_rotation_;
   hb_script_t script_;
   Vector<HarfBuzzRunGlyphData> glyph_data_;
+
+  // graphemes_[i] is the number of graphemes up to (and including) the ith
+  // character in the run.
+  Vector<unsigned> graphemes_;
+
   unsigned start_index_;
   unsigned num_characters_;
   float width_;
diff --git a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
index 949bcd8..3aa75b4d 100644
--- a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/graphics/surface_layer_bridge.h"
 
+#include <utility>
+
 #include "base/feature_list.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/solid_color_layer.h"
@@ -21,9 +23,13 @@
 
 namespace blink {
 
-SurfaceLayerBridge::SurfaceLayerBridge(WebLayerTreeView* layer_tree_view,
-                                       WebSurfaceLayerBridgeObserver* observer)
+SurfaceLayerBridge::SurfaceLayerBridge(
+    WebLayerTreeView* layer_tree_view,
+    WebSurfaceLayerBridgeObserver* observer,
+    cc::UpdateSubmissionStateCB update_submission_state_callback)
     : observer_(observer),
+      update_submission_state_callback_(
+          std::move(update_submission_state_callback)),
       binding_(this),
       frame_sink_id_(Platform::Current()->GenerateFrameSinkId()),
       parent_frame_sink_id_(layer_tree_view ? layer_tree_view->GetFrameSinkId()
@@ -118,7 +124,7 @@
 }
 
 void SurfaceLayerBridge::CreateSurfaceLayer() {
-  surface_layer_ = cc::SurfaceLayer::Create();
+  surface_layer_ = cc::SurfaceLayer::Create(update_submission_state_callback_);
 
   // This surface_id is essentially just a placeholder for the real one we will
   // get in OnFirstSurfaceActivation. We need it so that we properly get a
@@ -127,8 +133,12 @@
       frame_sink_id_,
       parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId());
 
+  surface_layer_->SetPrimarySurfaceId(current_surface_id_,
+                                      cc::DeadlinePolicy::UseDefaultDeadline());
+
   surface_layer_->SetStretchContentToFillBounds(true);
   surface_layer_->SetIsDrawable(true);
+  surface_layer_->SetMayContainVideo(true);
 
   if (observer_) {
     observer_->RegisterContentsLayer(surface_layer_.get());
@@ -138,8 +148,4 @@
   surface_layer_->SetContentsOpaque(false);
 }
 
-const viz::SurfaceId& SurfaceLayerBridge::GetSurfaceId() const {
-  return current_surface_id_;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
index afb53d3..120b32a6 100644
--- a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
@@ -34,7 +34,10 @@
     : public blink::mojom::blink::EmbeddedFrameSinkClient,
       public WebSurfaceLayerBridge {
  public:
-  SurfaceLayerBridge(WebLayerTreeView*, WebSurfaceLayerBridgeObserver*);
+  SurfaceLayerBridge(
+      WebLayerTreeView*,
+      WebSurfaceLayerBridgeObserver*,
+      cc::UpdateSubmissionStateCB update_submission_state_callback);
   ~SurfaceLayerBridge() override;
 
   void CreateSolidColorLayer();
@@ -48,7 +51,10 @@
   void ClearSurfaceId() override;
   void SetContentsOpaque(bool) override;
   void CreateSurfaceLayer() override;
-  const viz::SurfaceId& GetSurfaceId() const override;
+
+  const viz::SurfaceId& GetSurfaceId() const override {
+    return current_surface_id_;
+  }
 
  private:
   scoped_refptr<cc::SurfaceLayer> surface_layer_;
@@ -56,6 +62,7 @@
 
   // The |observer_| handles unregistering the contents layer on its own.
   WebSurfaceLayerBridgeObserver* observer_;
+  cc::UpdateSubmissionStateCB update_submission_state_callback_;
   viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
   mojo::Binding<blink::mojom::blink::EmbeddedFrameSinkClient> binding_;
 
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index d1a112314..906e20e 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/platform/graphics/video_frame_submitter.h"
 
+#include <vector>
+
 #include "base/task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
@@ -11,7 +13,6 @@
 #include "cc/scheduler/video_frame_controller.h"
 #include "components/viz/common/resources/resource_id.h"
 #include "components/viz/common/resources/returned_resource.h"
-#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "media/base/video_frame.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
@@ -50,16 +51,29 @@
 }
 
 void VideoFrameSubmitter::EnableSubmission(
-    viz::FrameSinkId id,
+    viz::SurfaceId id,
     WebFrameSinkDestroyedCallback frame_sink_destroyed_callback) {
   // TODO(lethalantidote): Set these fields earlier in the constructor. Will
   // need to construct VideoFrameSubmitter later in order to do this.
-  frame_sink_id_ = id;
+  surface_id_ = id;
   frame_sink_destroyed_callback_ = frame_sink_destroyed_callback;
   if (resource_provider_->IsInitialized())
     StartSubmitting();
 }
 
+void VideoFrameSubmitter::UpdateSubmissionState(bool should_submit) {
+  should_submit_ = should_submit;
+  UpdateSubmissionStateInternal();
+}
+
+void VideoFrameSubmitter::UpdateSubmissionStateInternal() {
+  if (compositor_frame_sink_) {
+    compositor_frame_sink_->SetNeedsBeginFrame(is_rendering_ && should_submit_);
+    if (should_submit_)
+      SubmitSingleFrame();
+  }
+}
+
 void VideoFrameSubmitter::StopUsingProvider() {
   DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
   if (is_rendering_)
@@ -72,12 +86,8 @@
   DCHECK(is_rendering_);
   DCHECK(provider_);
 
-  if (compositor_frame_sink_) {
-    // Push out final frame.
-    SubmitSingleFrame();
-    compositor_frame_sink_->SetNeedsBeginFrame(false);
-  }
   is_rendering_ = false;
+  UpdateSubmissionStateInternal();
 }
 
 void VideoFrameSubmitter::SubmitSingleFrame() {
@@ -89,11 +99,13 @@
   viz::BeginFrameAck current_begin_frame_ack =
       viz::BeginFrameAck::CreateManualAckWithDamage();
   scoped_refptr<media::VideoFrame> video_frame = provider_->GetCurrentFrame();
-  base::SequencedTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&VideoFrameSubmitter::SubmitFrame,
-                                weak_ptr_factory_.GetWeakPtr(),
-                                current_begin_frame_ack, video_frame));
-  provider_->PutCurrentFrame();
+  if (video_frame) {
+    base::SequencedTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::BindOnce(&VideoFrameSubmitter::SubmitFrame,
+                                  weak_ptr_factory_.GetWeakPtr(),
+                                  current_begin_frame_ack, video_frame));
+    provider_->PutCurrentFrame();
+  }
 }
 
 void VideoFrameSubmitter::DidReceiveFrame() {
@@ -110,9 +122,10 @@
 void VideoFrameSubmitter::StartRendering() {
   DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
   DCHECK(!is_rendering_);
-  if (compositor_frame_sink_)
-    compositor_frame_sink_->SetNeedsBeginFrame(true);
   is_rendering_ = true;
+
+  if (compositor_frame_sink_)
+    compositor_frame_sink_->SetNeedsBeginFrame(is_rendering_ && should_submit_);
 }
 
 void VideoFrameSubmitter::Initialize(cc::VideoFrameProvider* provider) {
@@ -149,13 +162,13 @@
     resource_provider_->Initialize(nullptr, this);
   }
 
-  if (frame_sink_id_.is_valid())
+  if (surface_id_.is_valid())
     StartSubmitting();
 }
 
 void VideoFrameSubmitter::StartSubmitting() {
   DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  DCHECK(frame_sink_id_.is_valid());
+  DCHECK(surface_id_.is_valid());
 
   mojom::blink::EmbeddedFrameSinkProviderPtr provider;
   Platform::Current()->GetInterfaceProvider()->GetInterface(
@@ -164,25 +177,13 @@
   viz::mojom::blink::CompositorFrameSinkClientPtr client;
   binding_.Bind(mojo::MakeRequest(&client));
   provider->CreateCompositorFrameSink(
-      frame_sink_id_, std::move(client),
+      surface_id_.frame_sink_id(), std::move(client),
       mojo::MakeRequest(&compositor_frame_sink_));
 
   compositor_frame_sink_.set_connection_error_handler(base::BindOnce(
       &VideoFrameSubmitter::OnContextLost, base::Unretained(this)));
 
-  if (is_rendering_)
-    compositor_frame_sink_->SetNeedsBeginFrame(true);
-
-  scoped_refptr<media::VideoFrame> video_frame = provider_->GetCurrentFrame();
-  if (video_frame) {
-    viz::BeginFrameAck current_begin_frame_ack =
-        viz::BeginFrameAck::CreateManualAckWithDamage();
-    base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&VideoFrameSubmitter::SubmitFrame,
-                                  weak_ptr_factory_.GetWeakPtr(),
-                                  current_begin_frame_ack, video_frame));
-    provider_->PutCurrentFrame();
-  }
+  UpdateSubmissionStateInternal();
 }
 
 void VideoFrameSubmitter::SubmitFrame(
@@ -190,10 +191,7 @@
     scoped_refptr<media::VideoFrame> video_frame) {
   TRACE_EVENT0("media", "VideoFrameSubmitter::SubmitFrame");
   DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-
-  // The context may have been destroyed between the call being scheduled and it
-  // running. The lack of compositor_frame_sink_ is used as a sign.
-  if (!compositor_frame_sink_)
+  if (!compositor_frame_sink_ || !should_submit_)
     return;
 
   viz::CompositorFrame compositor_frame;
@@ -219,15 +217,9 @@
                                           &compositor_frame.resource_list);
   compositor_frame.render_pass_list.push_back(std::move(render_pass));
 
-  if (compositor_frame.size_in_pixels() != current_size_in_pixels_) {
-    parent_local_surface_id_allocator_.GenerateId();
-    current_size_in_pixels_ = compositor_frame.size_in_pixels();
-  }
-
   // TODO(lethalantidote): Address third/fourth arg in SubmitCompositorFrame.
   compositor_frame_sink_->SubmitCompositorFrame(
-      parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(),
-      std::move(compositor_frame), nullptr, 0);
+      surface_id_.local_surface_id(), std::move(compositor_frame), nullptr, 0);
   resource_provider_->ReleaseFrameResources();
 
   waiting_for_compositor_ack_ = true;
@@ -236,7 +228,8 @@
 void VideoFrameSubmitter::OnBeginFrame(const viz::BeginFrameArgs& args) {
   TRACE_EVENT0("media", "VideoFrameSubmitter::OnBeginFrame");
   DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  viz::BeginFrameAck current_begin_frame_ack(args, false);
+  viz::BeginFrameAck current_begin_frame_ack =
+      viz::BeginFrameAck(args.source_id, args.sequence_number, false);
   if (args.type == viz::BeginFrameArgs::MISSED) {
     compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack);
     return;
@@ -280,14 +273,18 @@
   }
   waiting_for_compositor_ack_ = false;
 
-  resource_provider_->OnContextLost();
-
   // |compositor_frame_sink_| should be reset last.
   compositor_frame_sink_.reset();
 
+  resource_provider_->OnContextLost();
   context_provider_callback_.Run(
       base::BindOnce(&VideoFrameSubmitter::OnReceivedContextProvider,
                      weak_ptr_factory_.GetWeakPtr()));
+
+  // We need to trigger another submit so that surface_id's get propagated
+  // correctly. If we don't, we don't get any more signals to update the
+  // submission state.
+  should_submit_ = true;
 }
 
 void VideoFrameSubmitter::DidReceiveCompositorFrameAck(
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
index 0f8d3934..aa95cf0 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.h
@@ -5,12 +5,14 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_SUBMITTER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_VIDEO_FRAME_SUBMITTER_H_
 
+#include <memory>
+#include <utility>
+
 #include "base/memory/weak_ptr.h"
 #include "base/threading/thread_checker.h"
 #include "components/viz/client/shared_bitmap_reporter.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "components/viz/common/resources/shared_bitmap.h"
-#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom-blink.h"
@@ -37,7 +39,7 @@
 
   ~VideoFrameSubmitter() override;
 
-  bool Rendering() { return is_rendering_; };
+  bool Rendering() { return is_rendering_; }
   cc::VideoFrameProvider* Provider() { return provider_; }
   mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient>* Binding() {
     return &binding_;
@@ -45,6 +47,7 @@
   void SetSink(viz::mojom::blink::CompositorFrameSinkPtr* sink) {
     compositor_frame_sink_ = std::move(*sink);
   }
+  void SetSurfaceId(viz::SurfaceId id) { surface_id_ = id; }
 
   void OnReceivedContextProvider(
       bool,
@@ -59,8 +62,8 @@
   // WebVideoFrameSubmitter implementation.
   void Initialize(cc::VideoFrameProvider*) override;
   void SetRotation(media::VideoRotation) override;
-  void EnableSubmission(viz::FrameSinkId,
-                        WebFrameSinkDestroyedCallback) override;
+  void EnableSubmission(viz::SurfaceId, WebFrameSinkDestroyedCallback) override;
+  void UpdateSubmissionState(bool) override;
 
   // viz::ContextLostObserver implementation.
   void OnContextLost() override;
@@ -83,8 +86,11 @@
 
  private:
   FRIEND_TEST_ALL_PREFIXES(VideoFrameSubmitterTest, ContextLostDuringSubmit);
+  FRIEND_TEST_ALL_PREFIXES(VideoFrameSubmitterTest,
+                           ShouldSubmitPreventsSubmission);
 
   void StartSubmitting();
+  void UpdateSubmissionStateInternal();
   void SubmitFrame(const viz::BeginFrameAck&, scoped_refptr<media::VideoFrame>);
 
   // Pulls frame and submits it to compositor.
@@ -97,16 +103,16 @@
   scoped_refptr<ui::ContextProviderCommandBuffer> context_provider_;
   viz::mojom::blink::CompositorFrameSinkPtr compositor_frame_sink_;
   mojo::Binding<viz::mojom::blink::CompositorFrameSinkClient> binding_;
-  viz::ParentLocalSurfaceIdAllocator parent_local_surface_id_allocator_;
   WebContextProviderCallback context_provider_callback_;
   std::unique_ptr<VideoFrameResourceProvider> resource_provider_;
   WebFrameSinkDestroyedCallback frame_sink_destroyed_callback_;
-  viz::FrameSinkId frame_sink_id_;
+  viz::SurfaceId surface_id_;
   bool waiting_for_compositor_ack_ = false;
 
   bool is_rendering_;
+  // If we are not on screen, we should not submit.
+  bool should_submit_ = false;
   media::VideoRotation rotation_;
-  gfx::Size current_size_in_pixels_;
 
   THREAD_CHECKER(media_thread_checker_);
 
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
index addcc897..4512c5f 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter_test.cc
@@ -157,7 +157,15 @@
     viz::mojom::blink::CompositorFrameSinkRequest request =
         mojo::MakeRequest(&submitter_sink);
     sink_ = std::make_unique<StrictMock<MockCompositorFrameSink>>(&request);
+
+    // By setting the submission state before we set the sink, we can make
+    // testing easier without having to worry about the first sent frame.
+    submitter_->UpdateSubmissionState(true);
     submitter_->SetSink(&submitter_sink);
+    submitter_->SetSurfaceId(viz::SurfaceId(
+        viz::FrameSinkId(1, 1),
+        viz::LocalSurfaceId(11,
+                            base::UnguessableToken::Deserialize(0x111111, 0))));
   }
 
  protected:
@@ -262,6 +270,44 @@
   scoped_task_environment_.RunUntilIdle();
 }
 
+TEST_F(VideoFrameSubmitterTest, ShouldSubmitPreventsSubmission) {
+  MakeSubmitter();
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(*sink_, SetNeedsBeginFrame(false));
+  submitter_->UpdateSubmissionState(false);
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(*sink_, SetNeedsBeginFrame(false));
+  submitter_->StartRendering();
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(*sink_, SetNeedsBeginFrame(true));
+  EXPECT_CALL(*provider_, GetCurrentFrame())
+      .WillOnce(Return(media::VideoFrame::CreateFrame(
+          media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+          gfx::Size(8, 8), base::TimeDelta())));
+  EXPECT_CALL(*sink_, DoSubmitCompositorFrame(_, _));
+  EXPECT_CALL(*provider_, PutCurrentFrame());
+  EXPECT_CALL(*resource_provider_, AppendQuads(_, _, _));
+  EXPECT_CALL(*resource_provider_, PrepareSendToParent(_, _));
+  EXPECT_CALL(*resource_provider_, ReleaseFrameResources());
+  submitter_->UpdateSubmissionState(true);
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(*sink_, SetNeedsBeginFrame(false));
+  submitter_->UpdateSubmissionState(false);
+  scoped_task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(*provider_, GetCurrentFrame())
+      .WillOnce(Return(media::VideoFrame::CreateFrame(
+          media::PIXEL_FORMAT_YV12, gfx::Size(8, 8), gfx::Rect(gfx::Size(8, 8)),
+          gfx::Size(8, 8), base::TimeDelta())));
+  EXPECT_CALL(*provider_, PutCurrentFrame());
+
+  submitter_->SubmitSingleFrame();
+}
+
 TEST_F(VideoFrameSubmitterTest, RotationInformationPassedToResourceProvider) {
   // Check to see if rotation is communicated pre-rendering.
   MakeSubmitter();
diff --git a/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
new file mode 100644
index 0000000..008e697d
--- /dev/null
+++ b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/time/time.h"
+#include "cc/base/lap_timer.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+#include "third_party/blink/renderer/platform/fonts/font.h"
+#include "third_party/blink/renderer/platform/fonts/font_description.h"
+#include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+
+using blink::test::CreateTestFont;
+
+namespace blink {
+
+static const int kTimeLimitMillis = 3000;
+static const int kWarmupRuns = 10000;
+static const int kTimeCheckInterval = 1000000;
+
+class ShapeResultPerfTest {
+ public:
+  enum FontName {
+    ahem,
+    amiri,
+    megalopolis,
+    roboto,
+  };
+
+  ShapeResultPerfTest()
+      : timer(kWarmupRuns,
+              base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+              kTimeCheckInterval) {}
+
+ protected:
+  TextRun SetupFont(FontName font_name, const String& text, bool ltr) {
+    FontDescription::VariantLigatures ligatures(
+        FontDescription::kEnabledLigaturesState);
+    font = CreateTestFont("TestFont",
+                          test::PlatformTestDataPath(font_path[font_name]), 100,
+                          &ligatures);
+
+    return TextRun(
+        text, /* xpos */ 0, /* expansion */ 0,
+        TextRun::kAllowTrailingExpansion | TextRun::kForbidLeadingExpansion,
+        ltr ? TextDirection::kLtr : TextDirection::kRtl, false);
+  }
+
+  Font font;
+
+  std::map<FontName, String> font_path = {
+      {ahem, "Ahem.woff"},
+      {amiri, "third_party/Amiri/amiri_arabic.woff2"},
+      {megalopolis, "third_party/MEgalopolis/MEgalopolisExtra.woff"},
+      {roboto, "third_party/Roboto/roboto-regular.woff2"},
+  };
+
+  cc::LapTimer timer;
+};
+
+class OffsetForPositionPerfTest : public ShapeResultPerfTest,
+                                  public testing::TestWithParam<float> {
+ public:
+  void OffsetForPosition(TextRun& run,
+                         IncludePartialGlyphsOption partial,
+                         BreakGlyphsOption breakopt) {
+    timer.Reset();
+    float position = GetParam();
+    do {
+      font.OffsetForPosition(run, position, partial, breakopt);
+      timer.NextLap();
+    } while (!timer.HasTimeLimitExpired());
+  }
+};
+
+class CharacterRangePerfTest : public ShapeResultPerfTest,
+                               public testing::TestWithParam<int> {
+ public:
+  void GetCharacter(TextRun& run) {
+    timer.Reset();
+    int endpos = GetParam();
+    do {
+      font.SelectionRectForText(run, FloatPoint(), 100, 0, endpos);
+      timer.NextLap();
+    } while (!timer.HasTimeLimitExpired());
+  }
+};
+
+TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionFullBreak) {
+  TextRun run = SetupFont(ahem, "FURACOLO", true);
+  OffsetForPosition(run, OnlyFullGlyphs, BreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " LTR full break", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionFullDontBreak) {
+  TextRun run = SetupFont(ahem, "FURACOLO", true);
+  OffsetForPosition(run, OnlyFullGlyphs, DontBreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " LTR full", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionIncludePartialBreak) {
+  TextRun run = SetupFont(ahem, "FURACOLO", true);
+  OffsetForPosition(run, IncludePartialGlyphs, BreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " LTR partial break", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(OffsetForPositionPerfTest, LTROffsetForPositionIncludePartialDontBreak) {
+  TextRun run = SetupFont(ahem, "FURACOLO", true);
+  OffsetForPosition(run, IncludePartialGlyphs, DontBreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " LTR partial", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionFullBreak) {
+  TextRun run = SetupFont(ahem, "OLOCARUF", false);
+  OffsetForPosition(run, OnlyFullGlyphs, BreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " RTL full break", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionFullDontBreak) {
+  TextRun run = SetupFont(ahem, "OLOCARUF", false);
+  OffsetForPosition(run, OnlyFullGlyphs, DontBreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " RTL full", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionIncludePartialBreak) {
+  TextRun run = SetupFont(ahem, "OLOCARUF", false);
+  OffsetForPosition(run, IncludePartialGlyphs, BreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " RTL partial break", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(OffsetForPositionPerfTest, RTLOffsetForPositionIncludePartialDontBreak) {
+  TextRun run = SetupFont(ahem, "OLOCARUF", false);
+  OffsetForPosition(run, IncludePartialGlyphs, DontBreakGlyphs);
+  perf_test::PrintResult("OffsetForPositionPerfTest", " RTL partial", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+INSTANTIATE_TEST_CASE_P(OffsetForPosition,
+                        OffsetForPositionPerfTest,
+                        testing::Values(0, 10, 60, 100, 200, 350));
+
+TEST_P(CharacterRangePerfTest, LTRCharacterForPosition) {
+  TextRun run = SetupFont(ahem, "FURACOLO", true);
+  GetCharacter(run);
+  perf_test::PrintResult("CharacterRangePerfTest", " LTR", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+TEST_P(CharacterRangePerfTest, RTLCharacterForPosition) {
+  TextRun run = SetupFont(ahem, "OLOCARUF", false);
+  GetCharacter(run);
+  perf_test::PrintResult("CharacterRangePerfTest", " RTL", "",
+                         timer.LapsPerSecond(), "runs/s", true);
+}
+
+INSTANTIATE_TEST_CASE_P(CharacterRange,
+                        CharacterRangePerfTest,
+                        testing::Values(0, 1, 2, 4, 8));
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.cc b/third_party/blink/renderer/platform/text/text_break_iterator.cc
index 2373b2f..bc6d101 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.cc
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.cc
@@ -53,8 +53,7 @@
   return num;
 }
 
-void GraphemesClusterList(const UChar* text,
-                          unsigned text_length,
+void GraphemesClusterList(String text,
                           unsigned start,
                           unsigned length,
                           Vector<unsigned>* graphemes) {
@@ -62,8 +61,8 @@
   if (!length)
     return;
 
-  DCHECK_LE(static_cast<unsigned>(start + length), text_length);
-  NonSharedCharacterBreakIterator it(&text[start], length);
+  String substring = text.Substring(start, length);
+  NonSharedCharacterBreakIterator it(substring);
 
   int cursor_pos = it.Next();
   unsigned count = 0;
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator.h b/third_party/blink/renderer/platform/text/text_break_iterator.h
index a578d46..958e3f8 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator.h
+++ b/third_party/blink/renderer/platform/text/text_break_iterator.h
@@ -365,8 +365,7 @@
 
 // Returns a list of graphemes cluster at each character using character break
 // rules.
-PLATFORM_EXPORT void GraphemesClusterList(const UChar* text,
-                                          unsigned text_length,
+PLATFORM_EXPORT void GraphemesClusterList(String text,
                                           unsigned start,
                                           unsigned length,
                                           Vector<unsigned>* graphemes);
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator_test.cc b/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
index 1c70153..fd23955 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
+++ b/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
@@ -75,8 +75,7 @@
                                         unsigned start,
                                         unsigned length) {
     Vector<unsigned> result;
-    ::blink::GraphemesClusterList(input.Characters16(), input.length(), start,
-                                  length, &result);
+    ::blink::GraphemesClusterList(input, start, length, &result);
     return result;
   }
 
diff --git a/tools/clang/scripts/run_tool.py b/tools/clang/scripts/run_tool.py
index ad85dd3..82ae64d 100755
--- a/tools/clang/scripts/run_tool.py
+++ b/tools/clang/scripts/run_tool.py
@@ -299,9 +299,12 @@
       sys.stderr.write('\n')
     done_count = self.__success_count + self.__failed_count
     percentage = (float(done_count) / len(self.__compdb_entries)) * 100
-    sys.stderr.write(
-        'Processed %d files with %s tool (%d failures) [%.2f%%]\r' %
-        (done_count, self.__toolname, self.__failed_count, percentage))
+    # Only output progress for every 100th entry, to make log files easier to
+    # inspect.
+    if done_count % 100 == 0 or done_count == len(self.__compdb_entries):
+      sys.stderr.write(
+          'Processed %d files with %s tool (%d failures) [%.2f%%]\r' %
+          (done_count, self.__toolname, self.__failed_count, percentage))
 
 
 def main():
diff --git a/tools/code_coverage/test_suite.txt b/tools/code_coverage/test_suite.txt
index 900f1b5..abe7e57 100644
--- a/tools/code_coverage/test_suite.txt
+++ b/tools/code_coverage/test_suite.txt
@@ -45,7 +45,6 @@
 interactive_ui_tests
 ipc_tests
 jingle_unittests
-keyboard_unittests
 latency_unittests
 leveldb_service_unittests
 libjingle_xmpp_unittests
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 35c98ad0f..a5d378e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -28183,6 +28183,7 @@
   <int value="-474806100" label="DataReductionProxyMainMenu:enabled"/>
   <int value="-474322576" label="disable-quick-unlock-pin"/>
   <int value="-473087416" label="DragTabsInTabletMode:enabled"/>
+  <int value="-472014137" label="HomepageTile:disabled"/>
   <int value="-472013317" label="WebRTC-H264WithOpenH264FFmpeg:disabled"/>
   <int value="-471405972" label="ViewsProfileChooser:disabled"/>
   <int value="-471085510" label="MultiDeviceApi:enabled"/>
@@ -28199,6 +28200,7 @@
   <int value="-435914745" label="ClipboardContentSetting:disabled"/>
   <int value="-430360431" label="disable-password-generation"/>
   <int value="-428599163" label="NTPDownloadSuggestions:enabled"/>
+  <int value="-426815606" label="HomepageTile:enabled"/>
   <int value="-424701311" label="SignedHTTPExchange:disabled"/>
   <int value="-418868128" label="enable-experimental-web-platform-features"/>
   <int value="-410852857" label="ImprovedA2HS:disabled"/>
@@ -32503,6 +32505,7 @@
   <int value="11" label="Discarded: report-to not a string"/>
   <int value="12" label="Removed (max-age = 0)"/>
   <int value="13" label="Set (max-age &gt; 0)"/>
+  <int value="14" label="Discarded: missing remote endpoint"/>
 </enum>
 
 <enum name="NetNetworkErrorLoggingRequestOutcome">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f6ec479..4d1f563 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -40218,7 +40218,7 @@
 </histogram>
 
 <histogram name="Media.EME.EncryptionScheme.Initial.Audio"
-    units="MediaEncryptionScheme">
+    enum="MediaEncryptionScheme">
   <owner>jrummell@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -40228,7 +40228,7 @@
 </histogram>
 
 <histogram name="Media.EME.EncryptionScheme.Initial.Video"
-    units="MediaEncryptionScheme">
+    enum="MediaEncryptionScheme">
   <owner>jrummell@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -63923,6 +63923,16 @@
   </summary>
 </histogram>
 
+<histogram name="OfflinePages.PublishPageResult"
+    enum="OfflinePagesSavePageResult">
+  <owner>petewil@chromium.org</owner>
+  <summary>
+    Result of publishing downloaded page. CANCELLED means the page to publish
+    isn't found. ALREADY_EXISTS means the page have already been published
+    before.
+  </summary>
+</histogram>
+
 <histogram name="OfflinePages.RedirectResult" enum="OfflinePagesRedirectResult">
   <obsolete>
     Deprecated 8/2016. Use OfflinePages.RequestResult instead.
@@ -93802,6 +93812,9 @@
 
 <histogram base="true" name="SimpleCache.ReadIsParallelizable"
     enum="SimpleCacheReadParallelizable">
+  <obsolete>
+    Removed 2018-07-02. See https://crrev.com/c/1122706
+  </obsolete>
   <owner>morlovich@chromium.org</owner>
   <summary>
     For each Read operation, whether it could have been issued in parallel of a
@@ -93951,6 +93964,9 @@
 
 <histogram base="true" name="SimpleCache.WriteDependencyType"
     enum="SimpleCacheWriteDependencyType">
+  <obsolete>
+    Removed 2018-07-02. See https://crrev.com/c/1122706
+  </obsolete>
   <owner>morlovich@chromium.org</owner>
   <summary>
     Shows whether a write operation depends on the previous operation in queue
diff --git a/tools/perf/core/upload_results_to_perf_dashboard.py b/tools/perf/core/upload_results_to_perf_dashboard.py
index ad82a6ce..2bff331 100755
--- a/tools/perf/core/upload_results_to_perf_dashboard.py
+++ b/tools/perf/core/upload_results_to_perf_dashboard.py
@@ -12,6 +12,7 @@
 import optparse
 import re
 import sys
+import time
 import urllib
 
 from core import results_dashboard
@@ -71,12 +72,16 @@
   is_reference_build = 'reference' in options.name
   stripped_test_name = options.name.replace('.reference', '')
 
-  return results_dashboard.MakeHistogramSetWithDiagnostics(
+  begin_time = time.time()
+  hs = results_dashboard.MakeHistogramSetWithDiagnostics(
       options.results_file, stripped_test_name,
       options.configuration_name, options.buildername, options.buildnumber,
       revisions, is_reference_build,
       perf_dashboard_machine_group=options.perf_dashboard_machine_group)
-
+  end_time = time.time()
+  print 'Duration of adding diagnostics for %s: %d seconds' % (
+      stripped_test_name, end_time - begin_time)
+  return hs
 
 def _CreateParser():
   # Parse options
diff --git a/tools/perf/page_sets/data/rendering_desktop.json b/tools/perf/page_sets/data/rendering_desktop.json
index f7abf07..4782916 100644
--- a/tools/perf/page_sets/data/rendering_desktop.json
+++ b/tools/perf/page_sets/data/rendering_desktop.json
@@ -78,6 +78,81 @@
         "google_image_search": {
             "DEFAULT": "top_25_007.wprgo"
         },
+	"accu_weather_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "amazon_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "blogspot_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "booking.com_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "cnn_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "ebay_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "espn_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "facebook_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "gmail_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_calendar_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_docs_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_image_search_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_plus_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_web_search_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "linkedin_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "pinterest_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "techcrunch_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "twitch_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "twitter_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "wikipedia_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "wordpress_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_answers_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_news_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_sports_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "youtube_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
 	"maps_move": {
             "DEFAULT": "key_desktop_move_cases_000.wprgo"
         },
diff --git a/tools/perf/page_sets/data/rendering_mobile.json b/tools/perf/page_sets/data/rendering_mobile.json
index 2007270..0c6bf453 100644
--- a/tools/perf/page_sets/data/rendering_mobile.json
+++ b/tools/perf/page_sets/data/rendering_mobile.json
@@ -276,7 +276,7 @@
         "linkedin_mobile_sync_scroll": {
             "DEFAULT": "key_mobile_sites_002.wprgo"
         },
-        "google_docs": {
+	"google_docs": {
             "DEFAULT": "top_25_009.wprgo"
         },
         "google_docs_desktop_gpu_raster": {
@@ -432,6 +432,156 @@
         "google_image_search_desktop_gpu_raster": {
             "DEFAULT": "top_25_007.wprgo"
         },
+	"accu_weather_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "accu_weather_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "twitch_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "twitch_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+	"google_docs_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_docs_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "facebook_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "facebook_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "wikipedia_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "wikipedia_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_answers_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_answers_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "booking.com_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "booking.com_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "wordpress_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "wordpress_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "espn_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "espn_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "blogspot_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "blogspot_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_news_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_news_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "pinterest_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "pinterest_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_sports_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "yahoo_sports_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "techcrunch_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "techcrunch_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "amazon_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "amazon_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "cnn_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "cnn_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "ebay_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "ebay_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "linkedin_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "linkedin_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "youtube_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "youtube_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "gmail_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "gmail_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_plus_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_plus_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "twitter_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "twitter_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_web_search_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_web_search_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_calendar_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_calendar_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_image_search_2018": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
+        "google_image_search_2018_desktop_gpu_raster": {
+            "DEFAULT": "top_25_012.wprgo"
+        },
 	"filter_terrain_svg": {
             "DEFAULT": "tough_filters_cases_002.wprgo"
         },
diff --git a/tools/perf/page_sets/data/top_25_012.wprgo.sha1 b/tools/perf/page_sets/data/top_25_012.wprgo.sha1
index 217f848..92eb6273 100644
--- a/tools/perf/page_sets/data/top_25_012.wprgo.sha1
+++ b/tools/perf/page_sets/data/top_25_012.wprgo.sha1
@@ -1 +1 @@
-bd503b282964e34176b6c617d84d38aee20aa07c
\ No newline at end of file
+371b648115b68061656913d0f01a8cd651bc755b
\ No newline at end of file
diff --git a/tools/perf/page_sets/login_helpers/google_login.py b/tools/perf/page_sets/login_helpers/google_login.py
index 84bd47e..9540886 100644
--- a/tools/perf/page_sets/login_helpers/google_login.py
+++ b/tools/perf/page_sets/login_helpers/google_login.py
@@ -29,9 +29,9 @@
     'document.querySelector("%s") !== null' % (_PASSWORD_SELECTOR))
 
 
-def LoginGoogleAccount(action_runner,
-                       credential='googletest',  # Recommended credential.
-                       credentials_path=login_utils.DEFAULT_CREDENTIAL_PATH):
+def BaseLoginGoogle(action_runner,
+                    credential='googletest',  # Recommended credential.
+                    credentials_path=login_utils.DEFAULT_CREDENTIAL_PATH):
   """Logs in into Google account.
 
   This function navigates the tab into Google's login page and logs in a user
@@ -73,4 +73,19 @@
 
   login_utils.InputWithSelector(action_runner, password, _PASSWORD_SELECTOR)
   action_runner.ClickElement(selector=_SIGNIN_SELECTOR)
+
+
+def LoginGoogleAccount(action_runner,
+                       credential='googletest',  # Recommended credential.
+                       credentials_path=login_utils.DEFAULT_CREDENTIAL_PATH):
+  """ Login for old UI """
+  BaseLoginGoogle(action_runner, credential, credentials_path)
   action_runner.WaitForElement(text='My Account')
+
+
+def NewLoginGoogleAccount(action_runner,
+                          credential='googletest',  # Recommended credential.
+                          credentials_path=login_utils.DEFAULT_CREDENTIAL_PATH):
+  """ Login for new UI """
+  BaseLoginGoogle(action_runner, credential, credentials_path)
+  action_runner.WaitForElement(text='Google Account')
diff --git a/tools/perf/page_sets/rendering/top_real_world_desktop.py b/tools/perf/page_sets/rendering/top_real_world_desktop.py
index f9bdb10..bd6429e2 100644
--- a/tools/perf/page_sets/rendering/top_real_world_desktop.py
+++ b/tools/perf/page_sets/rendering/top_real_world_desktop.py
@@ -4,6 +4,7 @@
 from telemetry.page import shared_page_state
 
 from page_sets.login_helpers import google_login
+from page_sets.login_helpers import linkedin_login
 from page_sets.rendering import rendering_story
 from page_sets.rendering import story_tags
 
@@ -54,6 +55,27 @@
     action_runner.WaitForElement(text='Next')
 
 
+class GoogleWebSearch2018Page(TopRealWorldDesktopPage):
+  """ Why: top google property; a google tab is often open """
+  BASE_NAME = 'google_web_search_2018'
+  URL = 'https://www.google.com/#hl=en&q=barack+obama'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(GoogleWebSearch2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(GoogleWebSearch2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement(text='Next')
+
+
 class GoogleImageSearchPage(TopRealWorldDesktopPage):
   """ Why: tough image case; top google properties """
   BASE_NAME = 'google_image_search'
@@ -75,6 +97,26 @@
     super(GoogleImageSearchPage, self).RunNavigateSteps(action_runner)
 
 
+class GoogleImageSearch2018Page(TopRealWorldDesktopPage):
+  """ Why: tough image case; top google properties """
+  BASE_NAME = 'google_image_search_2018'
+  URL = 'https://www.google.com/search?q=cats&tbm=isch'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(GoogleImageSearch2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(GoogleImageSearch2018Page, self).RunNavigateSteps(action_runner)
+
+
 class GmailPage(TopRealWorldDesktopPage):
   """ Why: productivity, top google properties """
   ABSTRACT_STORY = True
@@ -177,6 +219,27 @@
     action_runner.WaitForElement(text='Home')
 
 
+class GooglePlus2018Page(TopRealWorldDesktopPage):
+  """ Why: social; top google property; Public profile; infinite scrolls """
+  BASE_NAME = 'google_plus_2018'
+  URL = 'https://plus.google.com/110031535020051778989/posts'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(GooglePlus2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(GooglePlus2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement(text='Posts')
+
+
 class YoutubePage(TopRealWorldDesktopPage):
   """ Why: #3 (Alexa global) """
   BASE_NAME = 'youtube'
@@ -199,6 +262,27 @@
     action_runner.Wait(2)
 
 
+class Youtube2018Page(TopRealWorldDesktopPage):
+  """ Why: #3 (Alexa global) """
+  BASE_NAME = 'youtube_2018'
+  URL = 'http://www.youtube.com'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Youtube2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(Youtube2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement(selector='#buttons')
+
+
 class BlogspotPage(TopRealWorldDesktopPage):
   """ Why: #11 (Alexa global), google property; some blogger layouts have
   infinite scroll but more interesting """
@@ -221,6 +305,28 @@
     action_runner.WaitForElement(text='accessibility')
 
 
+class Blogspot2018Page(TopRealWorldDesktopPage):
+  """ Why: #11 (Alexa global), google property; some blogger layouts have
+  infinite scroll but more interesting """
+  BASE_NAME = 'blogspot_2018'
+  URL = 'http://googlewebmastercentral.blogspot.com/'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Blogspot2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(Blogspot2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement('div[class="searchBox"]')
+
+
 class WordpressPage(TopRealWorldDesktopPage):
   """ Why: #18 (Alexa global), Picked an interesting post """
   BASE_NAME = 'wordpress'
@@ -246,6 +352,31 @@
     )
 
 
+class Wordpress2018Page(TopRealWorldDesktopPage):
+  """ Why: #18 (Alexa global), Picked an interesting post """
+  BASE_NAME = 'wordpress_2018'
+  # pylint: disable=line-too-long
+  URL = 'http://en.blog.wordpress.com/2012/09/04/freshly-pressed-editors-picks-for-august-2012/'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Wordpress2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(Wordpress2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement(
+        # pylint: disable=line-too-long
+        'a[href="https://en.blog.wordpress.com/2012/08/30/new-themes-able-and-sight/"]'
+    )
+
+
 class FacebookPage(TopRealWorldDesktopPage):
   """ Why: top social,Public profile """
   BASE_NAME = 'facebook'
@@ -267,6 +398,27 @@
     action_runner.WaitForElement(text='Videos')
 
 
+class Facebook2018Page(TopRealWorldDesktopPage):
+  """ Why: top social,Public profile """
+  BASE_NAME = 'facebook_2018'
+  URL = 'https://www.facebook.com/barackobama'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Facebook2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(Facebook2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement(text='Videos')
+
+
 class LinkedinPage(TopRealWorldDesktopPage):
   """ Why: #12 (Alexa global), Public profile. """
   BASE_NAME = 'linkedin'
@@ -284,6 +436,27 @@
         extra_browser_args=extra_browser_args)
 
 
+class Linkedin2018Page(TopRealWorldDesktopPage):
+  """ Why: #12 (Alexa global), Public profile. """
+  BASE_NAME = 'linkedin_2018'
+  URL = 'http://www.linkedin.com/in/linustorvalds'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Linkedin2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    linkedin_login.LoginDesktopAccount(action_runner, 'linkedin')
+    super(Linkedin2018Page, self).RunNavigateSteps(action_runner)
+
+
 class WikipediaPage(TopRealWorldDesktopPage):
   """ Why: #6 (Alexa) most visited worldwide,Picked an interesting page. """
   BASE_NAME = 'wikipedia'
@@ -301,6 +474,23 @@
         extra_browser_args=extra_browser_args)
 
 
+class Wikipedia2018Page(TopRealWorldDesktopPage):
+  """ Why: #6 (Alexa) most visited worldwide,Picked an interesting page. """
+  BASE_NAME = 'wikipedia_2018'
+  URL = 'http://en.wikipedia.org/wiki/Wikipedia'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Wikipedia2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+
 class TwitterPage(TopRealWorldDesktopPage):
   """ Why: #8 (Alexa global),Picked an interesting page """
   BASE_NAME = 'twitter'
@@ -322,6 +512,27 @@
     action_runner.Wait(2)
 
 
+class Twitter2018Page(TopRealWorldDesktopPage):
+  """ Why: #8 (Alexa global),Picked an interesting page """
+  BASE_NAME = 'twitter_2018'
+  URL = 'https://twitter.com/katyperry'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Twitter2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunNavigateSteps(self, action_runner):
+    super(Twitter2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement(selector='.ProfileNav')
+
+
 class PinterestPage(TopRealWorldDesktopPage):
   """ Why: #37 (Alexa global) """
   BASE_NAME = 'pinterest'
@@ -339,6 +550,23 @@
         extra_browser_args=extra_browser_args)
 
 
+class Pinterest2018Page(TopRealWorldDesktopPage):
+  """ Why: #37 (Alexa global) """
+  BASE_NAME = 'pinterest_2018'
+  URL = 'https://www.pinterest.com/search/pins/?q=flowers&rs=typed'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Pinterest2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+
 class ESPNPage(TopRealWorldDesktopPage):
   """ Why: #1 sports """
   ABSTRACT_STORY = True
@@ -373,6 +601,23 @@
         extra_browser_args=extra_browser_args)
 
 
+class AccuWeather2018Page(TopRealWorldDesktopPage):
+  """ Why: #2 weather according to Alexa """
+  BASE_NAME = 'accu_weather_2018'
+  URL = 'https://www.accuweather.com/en/us/new-york-ny/10017/weather-forecast/349727'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(AccuWeather2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+
 class YahooGamesPage(TopRealWorldDesktopPage):
   """ Why: #1 games according to Alexa (with actual games in it) """
   BASE_NAME = 'yahoo_games'
@@ -394,6 +639,32 @@
     action_runner.Wait(2)
 
 
+class Twitch2018Page(TopRealWorldDesktopPage):
+  """ Why: #1 games according to Alexa  """
+  BASE_NAME = 'twitch_2018'
+  URL = 'https://www.twitch.tv'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(Twitch2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+  def RunPageInteractions(self, action_runner):
+    action_runner.WaitForElement(selector='#mantle_skin')
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPageToElement(selector='.footer')
+      if self.story_set.scroll_forever:
+        while True:
+          action_runner.ScrollPage(direction='up')
+          action_runner.ScrollPage(direction='down')
+
+
 class GmailSmoothPage(GmailPage):
   """ Why: productivity, top google properties """
   BASE_NAME = 'gmail'
@@ -419,6 +690,30 @@
               element_function='window.__scrollableElementForTelemetry')
 
 
+class Gmail2018SmoothPage(TopRealWorldDesktopPage):
+  """ Why: productivity, top google properties """
+  BASE_NAME = 'gmail_2018'
+  URL = 'https://mail.google.com/mail/'
+
+  def RunNavigateSteps(self, action_runner):
+    google_login.NewLoginGoogleAccount(action_runner, 'googletest')
+    super(Gmail2018SmoothPage, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForJavaScriptCondition(
+        'window.gmonkey !== undefined &&'
+        'document.getElementById("gb") !== null')
+
+  def RunPageInteractions(self, action_runner):
+    action_runner.WaitForElement(selector='.Tm.aeJ')
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(selector='.Tm.aeJ')
+      if self.story_set.scroll_forever:
+        while True:
+          action_runner.ScrollElement(
+              direction='up', selector='.Tm.aeJ')
+          action_runner.ScrollElement(
+              direction='down', selector='.Tm.aeJ')
+
+
 class GoogleCalendarSmoothPage(GoogleCalendarPage):
   """ Why: productivity, top google properties """
   BASE_NAME='google_calendar'
@@ -435,6 +730,37 @@
               direction='down', selector='#scrolltimedeventswk')
 
 
+class GoogleCalendar2018SmoothPage(TopRealWorldDesktopPage):
+  """ Why: productivity, top google properties """
+  BASE_NAME='google_calendar_2018'
+  URL='https://www.google.com/calendar/'
+
+  def RunNavigateSteps(self, action_runner):
+    google_login.NewLoginGoogleAccount(action_runner, 'googletest')
+    super(GoogleCalendar2018SmoothPage, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement('span[class~="sm8sCf"]')
+    action_runner.ExecuteJavaScript("""
+        (function() {
+          var elem = document.createElement('meta');
+          elem.name='viewport';
+          elem.content='initial-scale=1';
+          document.body.appendChild(elem);
+        })();""")
+    action_runner.Wait(1)
+
+
+  def RunPageInteractions(self, action_runner):
+    action_runner.WaitForElement('span[class~="sm8sCf"]')
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(selector='#YPCqFe')
+      if self.story_set.scroll_forever:
+        while True:
+          action_runner.ScrollElement(
+              direction='up', selector='#YPCqFe')
+          action_runner.ScrollElement(
+              direction='down', selector='#YPCqFe')
+
+
 class GoogleDocSmoothPage(GoogleDocPage):
   """ Why: productivity, top google properties; Sample doc in the link """
   BASE_NAME='google_docs'
@@ -451,6 +777,29 @@
               direction='down', selector='.kix-appview-editor')
 
 
+class GoogleDoc2018SmoothPage(TopRealWorldDesktopPage):
+  """ Why: productivity, top google properties; Sample doc in the link """
+  # pylint: disable=line-too-long
+  URL = 'https://docs.google.com/document/d/1X-IKNjtEnx-WW5JIKRLsyhz5sbsat3mfTpAPUSX3_s4/view'
+  BASE_NAME='google_docs_2018'
+
+  def RunNavigateSteps(self, action_runner):
+    super(GoogleDoc2018SmoothPage, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForJavaScriptCondition(
+        'document.getElementsByClassName("kix-appview-editor").length')
+
+  def RunPageInteractions(self, action_runner):
+    action_runner.WaitForElement(selector='#printButton')
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollElement(selector='.kix-appview-editor')
+      if self.story_set.scroll_forever:
+        while True:
+          action_runner.ScrollElement(
+              direction='up', selector='.kix-appview-editor')
+          action_runner.ScrollElement(
+              direction='down', selector='.kix-appview-editor')
+
+
 class ESPNSmoothPage(ESPNPage):
   """ Why: #1 sports """
   BASE_NAME='espn'
@@ -465,18 +814,45 @@
           action_runner.ScrollPage(direction='down', left_start_ratio=0.1)
 
 
+class ESPN2018SmoothPage(TopRealWorldDesktopPage):
+  """ Why: #1 sports """
+  BASE_NAME='espn_2018'
+  URL = 'http://espn.go.com'
+
+  def RunPageInteractions(self, action_runner):
+    action_runner.WaitForElement(selector='#global-scoreboard')
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage(left_start_ratio=0.1)
+      if self.story_set.scroll_forever:
+        while True:
+          action_runner.ScrollPage(direction='up', left_start_ratio=0.1)
+          action_runner.ScrollPage(direction='down', left_start_ratio=0.1)
+
+
 class YahooNewsPage(TopRealWorldDesktopPage):
   """Why: #1 news worldwide (Alexa global)"""
   BASE_NAME = 'yahoo_news'
   URL = 'http://news.yahoo.com'
 
 
+class YahooNews2018Page(TopRealWorldDesktopPage):
+  """Why: #1 news worldwide (Alexa global)"""
+  BASE_NAME = 'yahoo_news_2018'
+  URL = 'http://news.yahoo.com'
+
+
 class CNNNewsPage(TopRealWorldDesktopPage):
   """Why: #2 news worldwide"""
   BASE_NAME = 'cnn'
   URL = 'http://www.cnn.com'
 
 
+class CNNNews2018Page(TopRealWorldDesktopPage):
+  """Why: #2 news worldwide"""
+  BASE_NAME = 'cnn_2018'
+  URL = 'http://www.cnn.com'
+
+
 class AmazonPage(TopRealWorldDesktopPage):
   # Why: #1 world commerce website by visits; #3 commerce in the US by
   # time spent
@@ -484,31 +860,68 @@
   URL = 'http://www.amazon.com'
 
 
+class Amazon2018Page(TopRealWorldDesktopPage):
+  # Why: #1 world commerce website by visits; #3 commerce in the US by
+  # time spent
+  BASE_NAME = 'amazon_2018'
+  URL = 'http://www.amazon.com'
+
+
 class EbayPage(TopRealWorldDesktopPage):
   # Why: #1 commerce website by time spent by users in US
   BASE_NAME = 'ebay'
   URL = 'http://www.ebay.com'
 
 
+class Ebay2018Page(TopRealWorldDesktopPage):
+  # Why: #1 commerce website by time spent by users in US
+  BASE_NAME = 'ebay_2018'
+  URL = 'http://www.ebay.com'
+
+
 class BookingPage(TopRealWorldDesktopPage):
   # Why: #1 Alexa recreation
   BASE_NAME = 'booking.com'
   URL = 'http://booking.com'
 
 
+class Booking2018Page(TopRealWorldDesktopPage):
+  # Why: #1 Alexa recreation
+  BASE_NAME = 'booking.com_2018'
+  URL = 'http://booking.com'
+
+
 class YahooAnswersPage(TopRealWorldDesktopPage):
   # Why: #1 Alexa reference
   BASE_NAME = 'yahoo_answers'
   URL = 'http://answers.yahoo.com'
 
 
+class YahooAnswers2018Page(TopRealWorldDesktopPage):
+  # Why: #1 Alexa reference
+  BASE_NAME = 'yahoo_answers_2018'
+  URL = 'http://answers.yahoo.com'
+
+
 class YahooSportsPage(TopRealWorldDesktopPage):
   # Why: #1 Alexa sports
   BASE_NAME = 'yahoo_sports'
   URL = 'http://sports.yahoo.com/'
 
 
+class YahooSports2018Page(TopRealWorldDesktopPage):
+  # Why: #1 Alexa sports
+  BASE_NAME = 'yahoo_sports_2018'
+  URL = 'http://sports.yahoo.com/'
+
+
 class TechCrunchPage(TopRealWorldDesktopPage):
   # Why: top tech blog
   BASE_NAME = 'techcrunch'
   URL = 'http://techcrunch.com'
+
+
+class TechCrunch2018Page(TopRealWorldDesktopPage):
+  # Why: top tech blog
+  BASE_NAME = 'techcrunch_2018'
+  URL = 'http://techcrunch.com'
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index e72f359..e305ae3 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -26,16 +26,6 @@
 
 namespace {
 
-// Wait up to 5 seconds for the first frame to be produced. Having Android
-// display a placeholder for a longer period of time is preferable to drawing
-// nothing, and the first frame can take a while on low-end systems.
-static const int64_t kFirstFrameTimeoutSeconds = 5;
-
-// Wait up to 1 second for a frame of the correct size to be produced. Android
-// OS will only wait 4 seconds, so we limit this to 1 second to make sure we
-// have always produced a frame before the OS stops waiting.
-static const int64_t kResizeTimeoutSeconds = 1;
-
 scoped_refptr<cc::SurfaceLayer> CreateSurfaceLayer(
     const viz::SurfaceId& primary_surface_id,
     const viz::SurfaceId& fallback_surface_id,
@@ -225,8 +215,8 @@
   // a renderer frame is available instead.
   if (!enable_surface_synchronization_ &&
       compositor->IsDrawingFirstVisibleFrame() && !HasDelegatedContent()) {
-    compositor_attach_until_frame_lock_ = compositor->GetCompositorLock(
-        this, base::TimeDelta::FromSeconds(kFirstFrameTimeoutSeconds));
+    compositor_attach_until_frame_lock_ =
+        compositor->GetCompositorLock(this, FirstFrameTimeout());
   }
   compositor->AddChildFrameSink(frame_sink_id_);
   if (!enable_viz_)
@@ -267,12 +257,9 @@
   if (!enable_surface_synchronization_)
     return;
 
-  // Use the default deadline to synchronize web content with browser UI.
-  // TODO(fsamuel): We probably want to use the deadlines
-  // kFirstFrameTimeoutSeconds and kResizeTimeoutSeconds for equivalent
-  // cases with surface synchronization too.
-  EmbedSurface(new_pending_local_surface_id, new_pending_size_in_pixels,
-               cc::DeadlinePolicy::UseDefaultDeadline());
+  EmbedSurface(
+      new_pending_local_surface_id, new_pending_size_in_pixels,
+      cc::DeadlinePolicy::UseSpecifiedDeadline(FirstFrameTimeoutFrames()));
 }
 
 void DelegatedFrameHostAndroid::EmbedSurface(
@@ -284,6 +271,8 @@
 
   pending_local_surface_id_ = new_pending_local_surface_id;
   pending_surface_size_in_pixels_ = new_pending_size_in_pixels;
+  viz::SurfaceId current_primary_surface_id =
+      content_layer_->primary_surface_id();
 
   if (!frame_evictor_->visible()) {
     // If the tab is resized while hidden, reset the fallback so that the next
@@ -299,10 +288,28 @@
     // is visible, EmbedSurface will be called again. See WasShown.
     return;
   }
-
-  viz::SurfaceId primary_surface_id(frame_sink_id_, pending_local_surface_id_);
-  content_layer_->SetPrimarySurfaceId(primary_surface_id, deadline_policy);
-  content_layer_->SetBounds(new_pending_size_in_pixels);
+  if (!current_primary_surface_id.is_valid() ||
+      current_primary_surface_id.local_surface_id() !=
+          pending_local_surface_id_) {
+    if (base::android::BuildInfo::GetInstance()->sdk_int() <
+        base::android::SDK_VERSION_OREO) {
+      // On version of Android earlier than Oreo, we would like to produce new
+      // content as soon as possible or the OS will create an additional black
+      // gutter. We only reset the deadline on the first frame (no bounds yet
+      // specified) or on resize, and only if the deadline policy is not
+      // infinite.
+      if (deadline_policy.policy_type() !=
+              cc::DeadlinePolicy::kUseInfiniteDeadline &&
+          (content_layer_->bounds().IsEmpty() ||
+           content_layer_->bounds() != pending_surface_size_in_pixels_)) {
+        deadline_policy = cc::DeadlinePolicy::UseSpecifiedDeadline(0u);
+      }
+    }
+    viz::SurfaceId primary_surface_id(frame_sink_id_,
+                                      pending_local_surface_id_);
+    content_layer_->SetPrimarySurfaceId(primary_surface_id, deadline_policy);
+    content_layer_->SetBounds(new_pending_size_in_pixels);
+  }
 }
 
 void DelegatedFrameHostAndroid::PixelSizeWillChange(
@@ -322,8 +329,8 @@
   if (registered_parent_compositor_) {
     if (HasSavedFrame() && content_layer_->bounds() != expected_pixel_size_) {
       compositor_pending_resize_lock_ =
-          registered_parent_compositor_->GetCompositorLock(
-              this, base::TimeDelta::FromSeconds(kResizeTimeoutSeconds));
+          registered_parent_compositor_->GetCompositorLock(this,
+                                                           ResizeTimeout());
     }
   }
 }
diff --git a/ui/android/delegated_frame_host_android.h b/ui/android/delegated_frame_host_android.h
index 94371aa..2c6ba56b 100644
--- a/ui/android/delegated_frame_host_android.h
+++ b/ui/android/delegated_frame_host_android.h
@@ -62,6 +62,26 @@
 
   ~DelegatedFrameHostAndroid() override;
 
+  // Wait up to 5 seconds for the first frame to be produced. Having Android
+  // display a placeholder for a longer period of time is preferable to drawing
+  // nothing, and the first frame can take a while on low-end systems.
+  static constexpr base::TimeDelta FirstFrameTimeout() {
+    return base::TimeDelta::FromSeconds(5);
+  }
+  static constexpr int64_t FirstFrameTimeoutFrames() {
+    return FirstFrameTimeout() / viz::BeginFrameArgs::DefaultInterval();
+  }
+
+  // Wait up to 1 second for a frame of the correct size to be produced. Android
+  // OS will only wait 4 seconds, so we limit this to 1 second to make sure we
+  // have always produced a frame before the OS stops waiting.
+  static constexpr base::TimeDelta ResizeTimeout() {
+    return base::TimeDelta::FromSeconds(1);
+  }
+  static constexpr int64_t ResizeTimeoutFrames() {
+    return ResizeTimeout() / viz::BeginFrameArgs::DefaultInterval();
+  }
+
   void SubmitCompositorFrame(
       const viz::LocalSurfaceId& local_surface_id,
       viz::CompositorFrame frame,
diff --git a/ui/compositor/recyclable_compositor_mac.cc b/ui/compositor/recyclable_compositor_mac.cc
index c35c5e6..187aebad 100644
--- a/ui/compositor/recyclable_compositor_mac.cc
+++ b/ui/compositor/recyclable_compositor_mac.cc
@@ -16,6 +16,9 @@
 
 namespace {
 
+// The number of RecyclableCompositorMacs in existence.
+size_t g_recyclable_compositor_count = 0;
+
 // Returns a task runner for creating a ui::Compositor. This allows compositor
 // tasks to be funneled through ui::WindowResizeHelper's task runner to allow
 // resize operations to coordinate with frames provided by the GPU process.
@@ -43,6 +46,7 @@
                   GetCompositorTaskRunner(),
                   features::IsSurfaceSynchronizationEnabled(),
                   ui::IsPixelCanvasRecordingEnabled()) {
+  g_recyclable_compositor_count += 1;
   compositor_.SetAcceleratedWidget(
       accelerated_widget_mac_->accelerated_widget());
   Suspend();
@@ -51,6 +55,7 @@
 
 RecyclableCompositorMac::~RecyclableCompositorMac() {
   compositor_.RemoveObserver(this);
+  g_recyclable_compositor_count -= 1;
 }
 
 void RecyclableCompositorMac::Suspend() {
@@ -100,9 +105,9 @@
 std::unique_ptr<RecyclableCompositorMac>
 RecyclableCompositorMacFactory::CreateCompositor(
     ui::ContextFactory* context_factory,
-    ui::ContextFactoryPrivate* context_factory_private) {
-  active_compositor_count_ += 1;
-  if (!compositors_.empty()) {
+    ui::ContextFactoryPrivate* context_factory_private,
+    bool force_new_compositor) {
+  if (!compositors_.empty() && !force_new_compositor) {
     std::unique_ptr<RecyclableCompositorMac> result;
     result = std::move(compositors_.back());
     compositors_.pop_back();
@@ -114,16 +119,6 @@
 
 void RecyclableCompositorMacFactory::RecycleCompositor(
     std::unique_ptr<RecyclableCompositorMac> compositor) {
-  // When we get to zero compositors in use, destroy all spare compositors.
-  // This is done to appease tests that rely on compositors being destroyed
-  // immediately (if the compositor is recycled and continues to exist, its
-  // subsequent initialization will crash).
-  active_compositor_count_ -= 1;
-  if (!active_compositor_count_) {
-    compositors_.clear();
-    return;
-  }
-
   if (recycling_disabled_)
     return;
 
@@ -132,6 +127,15 @@
   // Make this RecyclableCompositorMac recyclable for future instances.
   compositors_.push_back(std::move(compositor));
 
+  // When we get to zero active compositors in use, destroy all spare
+  // compositors. This is done to appease tests that rely on compositors being
+  // destroyed immediately (if the compositor is recycled and continues to
+  // exist, its subsequent initialization will crash).
+  if (g_recyclable_compositor_count == compositors_.size()) {
+    compositors_.clear();
+    return;
+  }
+
   // Post a task to free up the spare ui::Compositors when needed. Post this
   // to the browser main thread so that we won't free any compositors while
   // in a nested loop waiting to put up a new frame.
diff --git a/ui/compositor/recyclable_compositor_mac.h b/ui/compositor/recyclable_compositor_mac.h
index 850fbb6..84dfbd9 100644
--- a/ui/compositor/recyclable_compositor_mac.h
+++ b/ui/compositor/recyclable_compositor_mac.h
@@ -82,7 +82,8 @@
   // Create a compositor, or recycle a preexisting one.
   std::unique_ptr<RecyclableCompositorMac> CreateCompositor(
       ui::ContextFactory* context_factory,
-      ui::ContextFactoryPrivate* context_factory_private);
+      ui::ContextFactoryPrivate* context_factory_private,
+      bool force_new_compositor = false);
 
   // Delete a compositor, or allow it to be recycled.
   void RecycleCompositor(std::unique_ptr<RecyclableCompositorMac> compositor);
@@ -92,13 +93,11 @@
 
  private:
   friend class base::NoDestructor<ui::RecyclableCompositorMacFactory>;
+  friend class RecyclableCompositorMac;
   RecyclableCompositorMacFactory();
   ~RecyclableCompositorMacFactory();
   void ReduceSpareCompositors();
 
-  // The number of RecyclableCompositors that have been vended out and have
-  // not yet been recycled.
-  size_t active_compositor_count_ = 0;
   bool recycling_disabled_ = false;
   std::list<std::unique_ptr<RecyclableCompositorMac>> compositors_;
   base::WeakPtrFactory<RecyclableCompositorMacFactory> weak_factory_;
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
index ad4f3e4d0..fb66fdf 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.cc
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.cc
@@ -7,11 +7,28 @@
 #include <xf86drm.h>
 
 #include "base/logging.h"
+#include "base/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
 
+// Private types defined in libdrm. Define them here so we can peek at the
+// commit and ensure the expected state has been set correctly.
+struct drmModeAtomicReqItem {
+  uint32_t object_id;
+  uint32_t property_id;
+  uint64_t value;
+};
+
+typedef drmModeAtomicReqItem* drmModeAtomicReqItemPtr;
+
+struct _drmModeAtomicReq {
+  uint32_t cursor;
+  uint32_t size_items;
+  drmModeAtomicReqItemPtr items;
+};
+
 namespace ui {
 
 namespace {
@@ -38,6 +55,13 @@
   return drm_properties;
 }
 
+template <class Type>
+Type* FindObjectById(uint32_t id, std::vector<Type>& properties) {
+  auto it = std::find_if(properties.begin(), properties.end(),
+                         [id](const Type& p) { return p.id == id; });
+  return it != properties.end() ? &(*it) : nullptr;
+}
+
 }  // namespace
 
 MockDrmDevice::CrtcProperties::CrtcProperties() = default;
@@ -150,21 +174,13 @@
     uint32_t object_id,
     uint32_t object_type) {
   if (object_type == DRM_MODE_OBJECT_PLANE) {
-    auto it = std::find_if(
-        plane_properties_.begin(), plane_properties_.end(),
-        [object_id](const PlaneProperties& p) { return p.id == object_id; });
-    if (it == plane_properties_.end())
-      return nullptr;
-
-    return CreatePropertyObject(it->properties);
+    PlaneProperties* properties = FindObjectById(object_id, plane_properties_);
+    if (properties)
+      return CreatePropertyObject(properties->properties);
   } else if (object_type == DRM_MODE_OBJECT_CRTC) {
-    auto it = std::find_if(
-        crtc_properties_.begin(), crtc_properties_.end(),
-        [object_id](const CrtcProperties& p) { return p.id == object_id; });
-    if (it == crtc_properties_.end())
-      return nullptr;
-
-    return CreatePropertyObject(it->properties);
+    CrtcProperties* properties = FindObjectById(object_id, crtc_properties_);
+    if (properties)
+      return CreatePropertyObject(properties->properties);
   }
 
   return nullptr;
@@ -245,15 +261,12 @@
 }
 
 ScopedDrmPlanePtr MockDrmDevice::GetPlane(uint32_t plane_id) {
-  auto it = std::find_if(plane_properties_.begin(), plane_properties_.end(),
-                         [plane_id](const PlaneProperties& plane) {
-                           return plane.id == plane_id;
-                         });
-  if (it == plane_properties_.end())
+  PlaneProperties* properties = FindObjectById(plane_id, plane_properties_);
+  if (!properties)
     return nullptr;
 
   ScopedDrmPlanePtr plane(DrmAllocator<drmModePlane>());
-  plane->possible_crtcs = it->crtc_mask;
+  plane->possible_crtcs = properties->crtc_mask;
   return plane;
 }
 
@@ -281,10 +294,14 @@
 
 ScopedDrmPropertyBlob MockDrmDevice::CreatePropertyBlob(void* blob,
                                                         size_t size) {
-  return ScopedDrmPropertyBlob(new DrmPropertyBlobMetadata(this, 0xffffffff));
+  uint32_t id = ++property_id_generator_;
+  allocated_property_blobs_.insert(id);
+  return ScopedDrmPropertyBlob(new DrmPropertyBlobMetadata(this, id));
 }
 
-void MockDrmDevice::DestroyPropertyBlob(uint32_t id) {}
+void MockDrmDevice::DestroyPropertyBlob(uint32_t id) {
+  EXPECT_TRUE(allocated_property_blobs_.erase(id));
+}
 
 bool MockDrmDevice::GetCapability(uint64_t capability, uint64_t* value) {
   return true;
@@ -374,10 +391,18 @@
 }
 
 bool MockDrmDevice::CommitProperties(
-    drmModeAtomicReq* properties,
+    drmModeAtomicReq* request,
     uint32_t flags,
     uint32_t crtc_count,
     scoped_refptr<PageFlipRequest> page_flip_request) {
+  for (uint32_t i = 0; i < request->cursor; ++i) {
+    EXPECT_TRUE(ValidatePropertyValue(request->items[i].property_id,
+                                      request->items[i].value));
+    EXPECT_TRUE(UpdateProperty(request->items[i].object_id,
+                               request->items[i].property_id,
+                               request->items[i].value));
+  }
+
   commit_count_++;
   if (page_flip_request) {
     callbacks_.push(page_flip_request->AddPageFlip());
@@ -407,4 +432,47 @@
   blob_property_map_[blob->id] = std::move(blob);
 }
 
+bool MockDrmDevice::UpdateProperty(
+    uint32_t id,
+    uint64_t value,
+    std::vector<DrmDevice::Property>* properties) {
+  DrmDevice::Property* property = FindObjectById(id, *properties);
+  if (!property)
+    return false;
+
+  property->value = value;
+  return true;
+}
+
+bool MockDrmDevice::UpdateProperty(uint32_t object_id,
+                                   uint32_t property_id,
+                                   uint64_t value) {
+  PlaneProperties* plane_properties =
+      FindObjectById(object_id, plane_properties_);
+  if (plane_properties)
+    return UpdateProperty(property_id, value, &plane_properties->properties);
+
+  CrtcProperties* crtc_properties = FindObjectById(object_id, crtc_properties_);
+  if (crtc_properties)
+    return UpdateProperty(property_id, value, &crtc_properties->properties);
+
+  return false;
+}
+
+bool MockDrmDevice::ValidatePropertyValue(uint32_t id, uint64_t value) {
+  auto it = property_names_.find(id);
+  if (it == property_names_.end())
+    return false;
+
+  if (value == 0)
+    return true;
+
+  std::vector<std::string> blob_properties = {"CTM", "DEGAMMA_LUT", "GAMMA_LUT",
+                                              "PLANE_CTM"};
+  if (base::ContainsValue(blob_properties, it->second))
+    return base::ContainsKey(allocated_property_blobs_, value);
+
+  return true;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/drm/gpu/mock_drm_device.h b/ui/ozone/platform/drm/gpu/mock_drm_device.h
index 1658cff..6da2226 100644
--- a/ui/ozone/platform/drm/gpu/mock_drm_device.h
+++ b/ui/ozone/platform/drm/gpu/mock_drm_device.h
@@ -10,6 +10,7 @@
 #include <stdint.h>
 
 #include <map>
+#include <set>
 #include <vector>
 
 #include "base/containers/queue.h"
@@ -161,7 +162,7 @@
   bool MapDumbBuffer(uint32_t handle, size_t size, void** pixels) override;
   bool UnmapDumbBuffer(void* pixels, size_t size) override;
   bool CloseBufferHandle(uint32_t handle) override;
-  bool CommitProperties(drmModeAtomicReq* properties,
+  bool CommitProperties(drmModeAtomicReq* request,
                         uint32_t flags,
                         uint32_t crtc_count,
                         scoped_refptr<PageFlipRequest> callback) override;
@@ -173,6 +174,14 @@
  private:
   ~MockDrmDevice() override;
 
+  bool UpdateProperty(uint32_t id,
+                      uint64_t value,
+                      std::vector<DrmDevice::Property>* properties);
+
+  bool UpdateProperty(uint32_t object_id, uint32_t property_id, uint64_t value);
+
+  bool ValidatePropertyValue(uint32_t id, uint64_t value);
+
   int get_crtc_call_count_;
   int set_crtc_call_count_;
   int restore_crtc_call_count_;
@@ -207,6 +216,12 @@
 
   std::map<uint32_t, std::string> property_names_;
 
+  // TODO(dnicoara): Generate all IDs internal to MockDrmDevice.
+  // For now generate something with a high enough ID to be unique in tests.
+  uint32_t property_id_generator_ = 0xff000000;
+
+  std::set<uint32_t> allocated_property_blobs_;
+
   DISALLOW_COPY_AND_ASSIGN(MockDrmDevice);
 };
 
diff --git a/ui/views/cocoa/bridged_content_view.h b/ui/views/cocoa/bridged_content_view.h
index 5bfbe297..a5a9b690 100644
--- a/ui/views/cocoa/bridged_content_view.h
+++ b/ui/views/cocoa/bridged_content_view.h
@@ -33,6 +33,11 @@
   // hierarchy rooted at |hostedView_|. Owned by the focused View.
   ui::TextInputClient* textInputClient_;
 
+  // The TextInputClient about to be set. Requests for a new -inputContext will
+  // use this, but while the input is changing, |self| still needs to service
+  // IME requests using the old |textInputClient_|.
+  ui::TextInputClient* pendingTextInputClient_;
+
   // A tracking area installed to enable mouseMoved events.
   ui::ScopedCrTrackingArea cursorTrackingArea_;
 
diff --git a/ui/views/cocoa/bridged_content_view.mm b/ui/views/cocoa/bridged_content_view.mm
index 9cf02c7..445d42c 100644
--- a/ui/views/cocoa/bridged_content_view.mm
+++ b/ui/views/cocoa/bridged_content_view.mm
@@ -80,6 +80,13 @@
   return client && client->GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
 }
 
+// Returns true if |event| may have triggered dismissal of an IME and would
+// otherwise be ignored by a ui::TextInputClient when inserted.
+bool IsImeTriggerEvent(NSEvent* event) {
+  ui::KeyboardCode key = ui::KeyboardCodeFromNSEvent(event);
+  return key == ui::VKEY_RETURN || key == ui::VKEY_TAB;
+}
+
 // Returns the boundary rectangle for composition characters in the
 // |requested_range|. Sets |actual_range| corresponding to the returned
 // rectangle. For cases, where there is no composition text or the
@@ -325,11 +332,33 @@
   [self removeTrackingArea:cursorTrackingArea_.get()];
 }
 
-- (void)setTextInputClient:(ui::TextInputClient*)textInputClient {
-  if (textInputClient_ == textInputClient)
+- (void)setTextInputClient:(ui::TextInputClient*)newTextInputClient {
+  if (pendingTextInputClient_ == newTextInputClient)
     return;
 
-  textInputClient_ = textInputClient;
+  // This method may cause the IME window to dismiss, which may cause it to
+  // insert text (e.g. to replace marked text with "real" text). That should
+  // happen in the old -inputContext (which AppKit stores a reference to).
+  // Unfortunately, the only way to invalidate the the old -inputContext is to
+  // invoke -[NSApp updateWindows], which also wants a reference to the _new_
+  // -inputContext. So put the new inputContext in |pendingTextInputClient_| and
+  // only use it for -inputContext.
+  ui::TextInputClient* oldInputClient = textInputClient_;
+
+  // Since dismissing an IME may insert text, a misbehaving IME or a
+  // ui::TextInputClient that acts on InsertChar() to change focus a second time
+  // may invoke -setTextInputClient: recursively; with [NSApp updateWindows]
+  // still on the stack. Calling [NSApp updateWindows] recursively may upset
+  // an IME. Since the rest of this method is only to decide whether to call
+  // updateWindows, and we're already calling it, just bail out.
+  if (textInputClient_ != pendingTextInputClient_) {
+    pendingTextInputClient_ = newTextInputClient;
+    return;
+  }
+
+  // Start by assuming no need to invoke -updateWindows.
+  textInputClient_ = newTextInputClient;
+  pendingTextInputClient_ = newTextInputClient;
 
   // If |self| was being used for the input context, and would now report a
   // different input context, manually invoke [NSApp updateWindows]. This is
@@ -353,8 +382,12 @@
   }
 
   if (current == [super inputContext]) {
+    DCHECK_NE(oldInputClient, textInputClient_);
+    textInputClient_ = oldInputClient;
     [NSApp updateWindows];
-    DCHECK_EQ(nil, [NSTextInputContext currentInputContext]);
+    // Note: |pendingTextInputClient_| (and therefore +[NSTextInputContext
+    // currentInputContext] may have changed if called recursively.
+    textInputClient_ = pendingTextInputClient_;
   }
 }
 
@@ -512,7 +545,7 @@
     text = [text string];
 
   bool isCharacterEvent = keyDownEvent_ && [text length] == 1;
-  // Pass the character event to the View hierarchy. Cases this handles (non-
+  // Pass "character" events to the View hierarchy. Cases this handles (non-
   // exhaustive)-
   //    - Space key press on controls. Unlike Tab and newline which have
   //      corresponding action messages, an insertText: message is generated for
@@ -524,14 +557,26 @@
   // key code to get the actual characters from the ui::KeyEvent. This for
   // example is necessary for menu mnemonic selection of non-latin text.
 
-  // Don't generate a key event when there is active composition text. These key
+  // Don't generate a key event when there is marked composition text. These key
   // down events should be consumed by the IME and not reach the Views layer.
   // For example, on pressing Return to commit composition text, if we passed a
   // synthetic key event to the View hierarchy, it will have the effect of
-  // performing the default action on the current dialog. We do not want this.
+  // performing the default action on the current dialog. We do not want this
+  // when there is marked text (Return should only confirm the IME).
 
-  // Also note that a single key down event can cause multiple
-  // insertText:replacementRange: action messages. Example, on pressing Alt+e,
+  // However, IME for phonetic languages such as Korean do not always _mark_
+  // text when a composition is active. For these, correct behaviour is to
+  // handle the final -keyDown: that caused the composition to be committed, but
+  // only _after_ the sequence of insertText: messages coming from IME have been
+  // sent to the TextInputClient. Detect this by comparing to -[NSEvent
+  // characters]. Note we do not use -charactersIgnoringModifiers: so that,
+  // e.g., ß (Alt+s) will match mnemonics with ß rather than s.
+  bool isFinalInsertForKeyEvent =
+      isCharacterEvent && [text isEqualToString:[keyDownEvent_ characters]];
+
+  // Also note that a single, non-IME key down event can also cause multiple
+  // insertText:replacementRange: action messages being generated from within
+  // -keyDown:'s call to -interpretKeyEvents:. One example, on pressing Alt+e,
   // the accent (´) character is composed via setMarkedText:. Now on pressing
   // the character 'r', two insertText:replacementRange: action messages are
   // generated with the text value of accent (´) and 'r' respectively. The key
@@ -541,7 +586,7 @@
 
   // Currently there seems to be no use case to pass non-character events routed
   // from insertText: handlers to the View hierarchy.
-  if (isCharacterEvent && ![self hasMarkedText]) {
+  if (isFinalInsertForKeyEvent && ![self hasMarkedText]) {
     ui::KeyEvent charEvent([text characterAtIndex:0],
                            ui::KeyboardCodeFromNSEvent(keyDownEvent_),
                            ui::EF_NONE);
@@ -561,18 +606,26 @@
     // the key modifier since |text| already accounts for the pressed key
     // modifiers.
 
-    // Also, note we don't use |keyDownEvent_| to generate the synthetic
-    // ui::KeyEvent since for composed text, [keyDownEvent_ characters] might
-    // not be the same as |text|. This is because |keyDownEvent_| will
-    // correspond to the event that caused the composition text to be confirmed,
-    // say, Return key press.
+    // Also, note we don't check isFinalInsertForKeyEvent, nor use
+    // |keyDownEvent_| to generate the synthetic ui::KeyEvent since:  For
+    //  composed text, [keyDownEvent_ characters] might not be the same as
+    // |text|. This is because |keyDownEvent_| will correspond to the event that
+    // caused the composition text to be confirmed, say, Return key press.
     if (isCharacterEvent) {
       textInputClient_->InsertChar(ui::KeyEvent([text characterAtIndex:0],
                                                 ui::VKEY_UNKNOWN, ui::EF_NONE));
+      // Leave character events that may have triggered IME confirmation for
+      // inline IME (e.g. Korean) as "unhandled". There will be no more
+      // -insertText: messages, but we are unable to handle these via
+      // -handleKeyEvent: earlier in this method since toolkit-views client code
+      // assumes it can ignore characters associated with, e.g., VKEY_TAB.
+      DCHECK(keyDownEvent_);  // Otherwise it is not a character event.
+      if ([self hasMarkedText] || !IsImeTriggerEvent(keyDownEvent_))
+        hasUnhandledKeyDownEvent_ = NO;
     } else {
       textInputClient_->InsertText(base::SysNSStringToUTF16(text));
+      hasUnhandledKeyDownEvent_ = NO;
     }
-    hasUnhandledKeyDownEvent_ = NO;
   }
 }
 
@@ -746,7 +799,7 @@
 - (NSTextInputContext*)inputContext {
   // If the textInputClient_ does not exist, return nil since this view does not
   // conform to NSTextInputClient protocol.
-  if (!textInputClient_)
+  if (!pendingTextInputClient_)
     return nil;
 
   // If a menu is active, and -[NSView interpretKeyEvents:] asks for the
@@ -759,7 +812,7 @@
   // (http://crbug.com/23219), we don't want to show IME candidate windows.
   // Returning nil prevents this view from getting messages defined as part of
   // the NSTextInputClient protocol.
-  switch (textInputClient_->GetTextInputType()) {
+  switch (pendingTextInputClient_->GetTextInputType()) {
     case ui::TEXT_INPUT_TYPE_NONE:
     case ui::TEXT_INPUT_TYPE_PASSWORD:
       return nil;
diff --git a/ui/views/cocoa/bridged_native_widget.mm b/ui/views/cocoa/bridged_native_widget.mm
index 2c8faeab..1c631ed3 100644
--- a/ui/views/cocoa/bridged_native_widget.mm
+++ b/ui/views/cocoa/bridged_native_widget.mm
@@ -1204,8 +1204,11 @@
 
   AddCompositorSuperview();
 
+  // TODO(ccameron): Re-enable compositor recycling here.
+  // https://crbug.com/863817
   compositor_ = ui::RecyclableCompositorMacFactory::Get()->CreateCompositor(
-      context_factory, context_factory_private);
+      context_factory, context_factory_private,
+      true /* force_new_compositor */);
   compositor_->widget()->SetNSView(this);
 }
 
@@ -1236,8 +1239,9 @@
     return;
   compositor_->widget()->ResetNSView();
   compositor_->compositor()->SetRootLayer(nullptr);
-  ui::RecyclableCompositorMacFactory::Get()->RecycleCompositor(
-      std::move(compositor_));
+  // TODO(ccameron): Re-enable compositor recycling here.
+  // https://crbug.com/863817
+  compositor_.reset();
 }
 
 void BridgedNativeWidget::AddCompositorSuperview() {
diff --git a/ui/views/cocoa/bridged_native_widget_unittest.mm b/ui/views/cocoa/bridged_native_widget_unittest.mm
index 282a17b..f25287d 100644
--- a/ui/views/cocoa/bridged_native_widget_unittest.mm
+++ b/ui/views/cocoa/bridged_native_widget_unittest.mm
@@ -10,6 +10,7 @@
 
 #import "base/mac/foundation_util.h"
 #import "base/mac/mac_util.h"
+#import "base/mac/scoped_objc_class_swizzler.h"
 #import "base/mac/sdk_forward_declarations.h"
 #include "base/macros.h"
 #include "base/strings/stringprintf.h"
@@ -28,6 +29,7 @@
 #import "ui/views/cocoa/native_widget_mac_nswindow.h"
 #import "ui/views/cocoa/views_nswindow_delegate.h"
 #include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
 #include "ui/views/controls/textfield/textfield_model.h"
 #include "ui/views/test/test_views_delegate.h"
 #include "ui/views/view.h"
@@ -197,8 +199,54 @@
          sel == @selector(moveLeftAndModifySelection:);
 }
 
+// Used by InterpretKeyEventsDonorForNSView to simulate IME behavior.
+using InterpretKeyEventsCallback = base::RepeatingCallback<void(id)>;
+InterpretKeyEventsCallback* g_fake_interpret_key_events = nullptr;
+
+// Used by UpdateWindowsDonorForNSApp to hook -[NSApp updateWindows].
+base::RepeatingClosure* g_update_windows_closure = nullptr;
+
+// Used to provide a return value for +[NSTextInputContext currentInputContext].
+NSTextInputContext* g_fake_current_input_context = nullptr;
+
 }  // namespace
 
+// Class to hook [NSView interpretKeyEvents:] to simulate it interacting with an
+// IME window.
+@interface InterpretKeyEventsDonorForNSView : NSView
+@end
+
+@implementation InterpretKeyEventsDonorForNSView
+
+- (void)interpretKeyEvents:(NSArray<NSEvent*>*)eventArray {
+  ASSERT_TRUE(g_fake_interpret_key_events);
+  g_fake_interpret_key_events->Run(self);
+}
+
+@end
+
+@interface UpdateWindowsDonorForNSApp : NSApplication
+@end
+
+@implementation UpdateWindowsDonorForNSApp
+
+- (void)updateWindows {
+  ASSERT_TRUE(g_update_windows_closure);
+  g_update_windows_closure->Run();
+}
+
+@end
+@interface CurrentInputContextDonorForNSTextInputContext : NSTextInputContext
+@end
+
+@implementation CurrentInputContextDonorForNSTextInputContext
+
++ (NSTextInputContext*)currentInputContext {
+  return g_fake_current_input_context;
+}
+
+@end
+
 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates
 // NSWindow's behavior when attempting to toggle fullscreen state again, when
 // the last attempt failed but Cocoa has not yet sent
@@ -326,22 +374,21 @@
   DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTestBase);
 };
 
-class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase {
+class BridgedNativeWidgetTest : public BridgedNativeWidgetTestBase,
+                                public TextfieldController {
  public:
+  using HandleKeyEventCallback =
+      base::RepeatingCallback<bool(Textfield*, const ui::KeyEvent& key_event)>;
+
   BridgedNativeWidgetTest();
   ~BridgedNativeWidgetTest() override;
 
   // Install a textfield with input type |text_input_type| in the view hierarchy
   // and make it the text input client. Also initializes |dummy_text_view_|.
-  void InstallTextField(const base::string16& text,
-                        ui::TextInputType text_input_type);
-
-  // Install a textfield with input type ui::TEXT_INPUT_TYPE_TEXT in the view
-  // hierarchy and make it the text input client. Also initializes
-  // |dummy_text_view_|.
-  void InstallTextField(const base::string16& text);
-
-  void InstallTextField(const std::string& text);
+  Textfield* InstallTextField(
+      const base::string16& text,
+      ui::TextInputType text_input_type = ui::TEXT_INPUT_TYPE_TEXT);
+  Textfield* InstallTextField(const std::string& text);
 
   // Returns the actual current text for |ns_view_|, or the selected substring.
   NSString* GetActualText();
@@ -372,10 +419,17 @@
   // Helper method to set the private |keyDownEvent_| field on |ns_view_|.
   void SetKeyDownEvent(NSEvent* event);
 
+  // Sets a callback to run on the next HandleKeyEvent().
+  void SetHandleKeyEventCallback(HandleKeyEventCallback callback);
+
   // testing::Test:
   void SetUp() override;
   void TearDown() override;
 
+  // TextfieldController:
+  bool HandleKeyEvent(Textfield* sender,
+                      const ui::KeyEvent& key_event) override;
+
  protected:
   // Test delete to beginning of line or paragraph based on |sel|. |sel| can be
   // either deleteToBeginningOfLine: or deleteToBeginningOfParagraph:.
@@ -402,6 +456,8 @@
   // An NSTextView which helps set the expectations for our tests.
   base::scoped_nsobject<NSTextView> dummy_text_view_;
 
+  HandleKeyEventCallback handle_key_event_callback_;
+
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
  private:
@@ -415,12 +471,13 @@
 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() {
 }
 
-void BridgedNativeWidgetTest::InstallTextField(
+Textfield* BridgedNativeWidgetTest::InstallTextField(
     const base::string16& text,
     ui::TextInputType text_input_type) {
   Textfield* textfield = new Textfield();
   textfield->SetText(text);
   textfield->SetTextInputType(text_input_type);
+  textfield->set_controller(this);
   view_->RemoveAllChildViews(true);
   view_->AddChildView(textfield);
   textfield->SetBoundsRect(init_params_.bounds);
@@ -437,14 +494,11 @@
   dummy_text_view_.reset(
       [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]);
   [dummy_text_view_ setString:SysUTF16ToNSString(text)];
+  return textfield;
 }
 
-void BridgedNativeWidgetTest::InstallTextField(const base::string16& text) {
-  InstallTextField(text, ui::TEXT_INPUT_TYPE_TEXT);
-}
-
-void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
-  InstallTextField(base::ASCIIToUTF16(text), ui::TEXT_INPUT_TYPE_TEXT);
+Textfield* BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
+  return InstallTextField(base::ASCIIToUTF16(text));
 }
 
 NSString* BridgedNativeWidgetTest::GetActualText() {
@@ -502,6 +556,11 @@
   [ns_view_ setValue:event forKey:@"keyDownEvent_"];
 }
 
+void BridgedNativeWidgetTest::SetHandleKeyEventCallback(
+    HandleKeyEventCallback callback) {
+  handle_key_event_callback_ = std::move(callback);
+}
+
 void BridgedNativeWidgetTest::SetUp() {
   BridgedNativeWidgetTestBase::SetUp();
 
@@ -534,6 +593,13 @@
   BridgedNativeWidgetTestBase::TearDown();
 }
 
+bool BridgedNativeWidgetTest::HandleKeyEvent(Textfield* sender,
+                                             const ui::KeyEvent& key_event) {
+  if (handle_key_event_callback_)
+    return handle_key_event_callback_.Run(sender, key_event);
+  return false;
+}
+
 void BridgedNativeWidgetTest::TestDeleteBeginning(SEL sel) {
   InstallTextField("foo bar baz");
   EXPECT_EQ_RANGE_3(NSMakeRange(11, 0), GetExpectedSelectionRange(),
@@ -1325,6 +1391,187 @@
   EXPECT_EQ_RANGE(query_range, actual_range);
 }
 
+// Test simulated codepaths for IMEs that do not always "mark" text. E.g.
+// phonetic languages such as Korean and Vietnamese.
+TEST_F(BridgedNativeWidgetTest, TextInput_SimulatePhoneticIme) {
+  Textfield* textfield = InstallTextField("");
+  EXPECT_TRUE([ns_view_ textInputClient]);
+
+  base::mac::ScopedObjCClassSwizzler interpret_key_events_swizzler(
+      [NSView class], [InterpretKeyEventsDonorForNSView class],
+      @selector(interpretKeyEvents:));
+
+  // Sequence of calls (and corresponding keyDown events) obtained via tracing
+  // with 2-Set Korean IME and pressing q, o, then Enter on the keyboard.
+  NSEvent* q_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode(
+      12, [@"ㅂ" characterAtIndex:0], NSKeyDown, 0);
+  InterpretKeyEventsCallback handle_q_in_ime = base::BindRepeating([](id view) {
+    [view insertText:@"ㅂ" replacementRange:NSMakeRange(NSNotFound, 0)];
+  });
+
+  NSEvent* o_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode(
+      31, [@"ㅐ" characterAtIndex:0], NSKeyDown, 0);
+  InterpretKeyEventsCallback handle_o_in_ime = base::BindRepeating([](id view) {
+    [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+  });
+
+  NSEvent* return_in_ime = cocoa_test_event_utils::SynthesizeKeyEvent(
+      widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0);
+  InterpretKeyEventsCallback handle_return_in_ime =
+      base::BindRepeating([](id view) {
+        // When confirming the composition, AppKit repeats itself.
+        [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+        [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+        // Note: there is no insertText:@"\r", which we would normally see when
+        // not in an IME context for VKEY_RETURN.
+      });
+
+  // Add a hook for the KeyEvent being received by the TextfieldController. E.g.
+  // this is where the Omnibox would start to search when Return is pressed.
+  bool saw_vkey_return = false;
+  SetHandleKeyEventCallback(base::BindRepeating(
+      [](bool* saw_return, Textfield* textfield, const ui::KeyEvent& event) {
+        if (event.key_code() == ui::VKEY_RETURN) {
+          EXPECT_FALSE(*saw_return);
+          *saw_return = true;
+          EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+        }
+        return false;
+      },
+      &saw_vkey_return));
+
+  EXPECT_EQ(base::UTF8ToUTF16(""), textfield->text());
+
+  g_fake_interpret_key_events = &handle_q_in_ime;
+  [ns_view_ keyDown:q_in_ime];
+  EXPECT_EQ(base::SysNSStringToUTF16(@"ㅂ"), textfield->text());
+  EXPECT_FALSE(saw_vkey_return);
+
+  g_fake_interpret_key_events = &handle_o_in_ime;
+  [ns_view_ keyDown:o_in_ime];
+  EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+  EXPECT_FALSE(saw_vkey_return);
+
+  // Note the "Enter" should not replace the replacement range, even though a
+  // replacement range was set.
+  g_fake_interpret_key_events = &handle_return_in_ime;
+  [ns_view_ keyDown:return_in_ime];
+  EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+
+  // VKEY_RETURN should be seen by via the unhandled key event handler (but not
+  // via -insertText:.
+  EXPECT_TRUE(saw_vkey_return);
+
+  g_fake_interpret_key_events = nullptr;
+}
+
+// Test a codepath that could hypothetically cause [NSApp updateWindows] to be
+// called recursively due to IME dismissal during teardown triggering a focus
+// change. Twice.
+TEST_F(BridgedNativeWidgetTest, TextInput_RecursiveUpdateWindows) {
+  Textfield* textfield = InstallTextField("");
+  EXPECT_TRUE([ns_view_ textInputClient]);
+
+  base::mac::ScopedObjCClassSwizzler interpret_key_events_swizzler(
+      [NSView class], [InterpretKeyEventsDonorForNSView class],
+      @selector(interpretKeyEvents:));
+  base::mac::ScopedObjCClassSwizzler update_windows_swizzler(
+      [NSApplication class], [UpdateWindowsDonorForNSApp class],
+      @selector(updateWindows));
+  base::mac::ScopedObjCClassSwizzler current_input_context_swizzler(
+      [NSTextInputContext class],
+      [CurrentInputContextDonorForNSTextInputContext class],
+      @selector(currentInputContext));
+
+  int vkey_return_count = 0;
+
+  // Everything happens with this one event.
+  NSEvent* return_with_fake_ime = cocoa_test_event_utils::SynthesizeKeyEvent(
+      widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0);
+
+  InterpretKeyEventsCallback generate_return_and_fake_ime = base::BindRepeating(
+      [](int* saw_return_count, id view) {
+        EXPECT_EQ(0, *saw_return_count);
+        // First generate the return to simulate an input context change.
+        [view insertText:@"\r" replacementRange:NSMakeRange(NSNotFound, 0)];
+
+        EXPECT_EQ(1, *saw_return_count);
+      },
+      &vkey_return_count);
+
+  bool saw_update_windows = false;
+  base::RepeatingClosure update_windows_closure = base::BindRepeating(
+      [](bool* saw_update_windows, BridgedContentView* view,
+         Textfield* textfield) {
+        // Ensure updateWindows is not invoked recursively.
+        EXPECT_FALSE(*saw_update_windows);
+        *saw_update_windows = true;
+
+        // Inside updateWindows, assume the IME got dismissed and wants to
+        // insert its last bit of text for the old input context.
+        [view insertText:@"배" replacementRange:NSMakeRange(0, 1)];
+
+        // This is triggered by the setTextInputClient:nullptr in
+        // SetHandleKeyEventCallback(), so -inputContext should also be nil.
+        EXPECT_FALSE([view inputContext]);
+
+        // Ensure we can't recursively call updateWindows. A TextInputClient
+        // reacting to InsertChar could theoretically do this, but toolkit-views
+        // DCHECKs if there is recursive event dispatch, so call
+        // setTextInputClient directly.
+        [view setTextInputClient:textfield];
+
+        // Finally simulate what -[NSApp updateWindows] should _actually_ do,
+        // which is to update the input context (from the first responder).
+        g_fake_current_input_context = [view inputContext];
+
+        // Now, the |textfield| set above should have been set again.
+        EXPECT_TRUE(g_fake_current_input_context);
+      },
+      &saw_update_windows, ns_view_, textfield);
+
+  SetHandleKeyEventCallback(base::BindRepeating(
+      [](int* saw_return_count, BridgedContentView* view, Textfield* textfield,
+         const ui::KeyEvent& event) {
+        if (event.key_code() == ui::VKEY_RETURN) {
+          *saw_return_count += 1;
+          // Simulate Textfield::OnBlur() by clearing the input method.
+          // Textfield needs to be in a Widget to do this normally.
+          [view setTextInputClient:nullptr];
+        }
+        return false;
+      },
+      &vkey_return_count, ns_view_));
+
+  // Starting text (just insert it).
+  [ns_view_ insertText:@"ㅂ" replacementRange:NSMakeRange(NSNotFound, 0)];
+
+  EXPECT_EQ(base::SysNSStringToUTF16(@"ㅂ"), textfield->text());
+
+  g_fake_interpret_key_events = &generate_return_and_fake_ime;
+  g_update_windows_closure = &update_windows_closure;
+  g_fake_current_input_context = [ns_view_ inputContext];
+  EXPECT_TRUE(g_fake_current_input_context);
+  [ns_view_ keyDown:return_with_fake_ime];
+
+  // We should see one VKEY_RETURNs and one updateWindows. In particular, note
+  // that there is not a second VKEY_RETURN seen generated by keyDown: thinking
+  // the event has been unhandled. This is because it was handled when the fake
+  // IME sent \r.
+  EXPECT_TRUE(saw_update_windows);
+  EXPECT_EQ(1, vkey_return_count);
+
+  // The text inserted during updateWindows should have been inserted, even
+  // though we were trying to change the input context.
+  EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text());
+
+  EXPECT_TRUE(g_fake_current_input_context);
+
+  g_fake_current_input_context = nullptr;
+  g_fake_interpret_key_events = nullptr;
+  g_update_windows_closure = nullptr;
+}
+
 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest;
 
 // Simulate the notifications that AppKit would send out if a fullscreen
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 19f7ed42..65ee52f 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -1599,18 +1599,26 @@
   if (LOWORD(w_param) != HIWORD(w_param))
     NOTIMPLEMENTED() << "Received non-square scaling factors";
 
+  int dpi;
+  float scaling_factor;
+  if (display::Display::HasForceDeviceScaleFactor()) {
+    scaling_factor = display::Display::GetForcedDeviceScaleFactor();
+    dpi = display::win::GetDPIFromScalingFactor(scaling_factor);
+  } else {
+    dpi = LOWORD(w_param);
+    scaling_factor = display::win::GetScalingFactorFromDPI(dpi_);
+  }
+
   // The first WM_DPICHANGED originates from EnableChildWindowDpiMessage during
   // initialization. We don't want to propagate this as the client is already
   // set at the current scale factor and may cause the window to display too
   // soon. See http://crbug.com/625076.
-  int dpi = LOWORD(w_param);
   if (dpi_ == dpi)
     return 0;
 
   dpi_ = dpi;
   SetBoundsInternal(gfx::Rect(*reinterpret_cast<RECT*>(l_param)), false);
-  delegate_->HandleWindowScaleFactorChanged(
-      display::win::GetScalingFactorFromDPI(dpi_));
+  delegate_->HandleWindowScaleFactorChanged(scaling_factor);
   return 0;
 }
 
diff --git a/ui/web_dialogs/web_dialog_ui.cc b/ui/web_dialogs/web_dialog_ui.cc
index 9b1d765..bb1b899 100644
--- a/ui/web_dialogs/web_dialog_ui.cc
+++ b/ui/web_dialogs/web_dialog_ui.cc
@@ -120,4 +120,15 @@
   HandleRenderFrameCreated(render_frame_host);
 }
 
+MojoWebDialogUI::MojoWebDialogUI(content::WebUI* web_ui)
+    : WebDialogUIBase(web_ui), MojoWebUIController(web_ui) {}
+
+MojoWebDialogUI::~MojoWebDialogUI() = default;
+
+void MojoWebDialogUI::RenderFrameCreated(
+    content::RenderFrameHost* render_frame_host) {
+  content::WebUIController::RenderFrameCreated(render_frame_host);
+  HandleRenderFrameCreated(render_frame_host);
+}
+
 }  // namespace ui
diff --git a/ui/web_dialogs/web_dialog_ui.h b/ui/web_dialogs/web_dialog_ui.h
index 62d83ee3..3df3093c 100644
--- a/ui/web_dialogs/web_dialog_ui.h
+++ b/ui/web_dialogs/web_dialog_ui.h
@@ -83,23 +83,17 @@
 
 // Displays file URL contents inside a modal web dialog while also enabling
 // Mojo calls to be made from within the dialog.
-template <typename Interface>
 class WEB_DIALOGS_EXPORT MojoWebDialogUI : public WebDialogUIBase,
                                            public MojoWebUIController {
  public:
   // When created, the delegate should already be set as user data on the
   // WebContents.
-  explicit MojoWebDialogUI(content::WebUI* web_ui)
-      : WebDialogUIBase(web_ui), MojoWebUIController(web_ui) {}
-  ~MojoWebDialogUI() override {}
+  explicit MojoWebDialogUI(content::WebUI* web_ui);
+  ~MojoWebDialogUI() override;
 
  private:
   // content::WebUIController:
-  void RenderFrameCreated(
-      content::RenderFrameHost* render_frame_host) override {
-    content::WebUIController::RenderFrameCreated(render_frame_host);
-    HandleRenderFrameCreated(render_frame_host);
-  }
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
 
   DISALLOW_COPY_AND_ASSIGN(MojoWebDialogUI);
 };
diff --git a/ui/webui/resources/cr_elements/icons.html b/ui/webui/resources/cr_elements/icons.html
index 186e3c61..4b842d6 100644
--- a/ui/webui/resources/cr_elements/icons.html
+++ b/ui/webui/resources/cr_elements/icons.html
@@ -65,6 +65,7 @@
       <g id="settings_icon"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"></path></g>
       <g id="star"><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"></path></g>
       <g id="supervisor-account" viewBox="0 0 48 48"><path d="M0 0h48v48H0z" fill="none"></path><path d="M33 24c2.76 0 4.98-2.24 4.98-5s-2.22-5-4.98-5c-2.76 0-5 2.24-5 5s2.24 5 5 5zm-15-2c3.31 0 5.98-2.69 5.98-6s-2.67-6-5.98-6c-3.31 0-6 2.69-6 6s2.69 6 6 6zm15 6c-3.67 0-11 1.84-11 5.5V38h22v-4.5c0-3.66-7.33-5.5-11-5.5zm-15-2c-4.67 0-14 2.34-14 7v5h14v-4.5c0-1.7.67-4.67 4.74-6.94C21 26.19 19.31 26 18 26z"></path></g>
+      <g id="sync"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z"></path></g>
       <g id="warning"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path></g>
     </defs>
   </svg>