diff --git a/DEPS b/DEPS
index f2811e5..59838de6 100644
--- a/DEPS
+++ b/DEPS
@@ -96,11 +96,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': '95e2b91d76d60ae69588ca700211146aabddbe5a',
+  'skia_revision': '8a95244f626c2f7d4b66c8ca18266c2b0640bebb',
   # 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': '0cb7b82ecaab1aeb72ec695c0aaaeda26d3513b4',
+  'v8_revision': '376d927a20a21470c53f62c8528ba71885ff0ec8',
   # 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.
@@ -108,7 +108,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': 'e88e454803a78a23d4578e5ac95a278dadfeea9f',
+  'angle_revision': '54aafe58f5bd530eea25461edc4690a057a3638f',
   # 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.
@@ -156,7 +156,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '3059fd7dba63773c249938b1b934b9c10143d684',
+  'catapult_revision': 'b9ff578f32e76574afd53543708a694d08b841de',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -968,7 +968,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c0541da63f571512c49758cbc0767117997a270',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e6256055e7db560da560829a3e249898f9685d1f', # commit position 21742
+    Var('webrtc_git') + '/src.git' + '@' + '823f9135f858c9fc7526e29a180818ad7f0d408c', # commit position 21742
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
index ac07ebb..d72ffdf8 100644
--- a/android_webview/browser/aw_autofill_client.cc
+++ b/android_webview/browser/aw_autofill_client.cc
@@ -218,6 +218,10 @@
   return true;
 }
 
+bool AwAutofillClient::AreServerCardsSupported() {
+  return true;
+}
+
 void AwAutofillClient::Dismissed(JNIEnv* env,
                                  const JavaParamRef<jobject>& obj) {
   anchor_view_.Reset();
diff --git a/android_webview/browser/aw_autofill_client.h b/android_webview/browser/aw_autofill_client.h
index f3d0b8e..ce8597b 100644
--- a/android_webview/browser/aw_autofill_client.h
+++ b/android_webview/browser/aw_autofill_client.h
@@ -103,6 +103,7 @@
   bool IsContextSecure() override;
   bool ShouldShowSigninPromo() override;
   bool IsAutofillSupported() override;
+  bool AreServerCardsSupported() override;
   void ExecuteCommand(int id) override;
 
   void Dismissed(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index d1da1f06..bb2e5be 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -209,6 +209,8 @@
     "events/event_rewriter_controller.h",
     "events/keyboard_driven_event_rewriter.cc",
     "events/keyboard_driven_event_rewriter.h",
+    "events/spoken_feedback_event_rewriter.cc",
+    "events/spoken_feedback_event_rewriter.h",
     "first_run/desktop_cleaner.cc",
     "first_run/desktop_cleaner.h",
     "first_run/first_run_helper.cc",
@@ -794,6 +796,8 @@
     "system/tray/system_tray_notifier.h",
     "system/tray/system_tray_view.cc",
     "system/tray/system_tray_view.h",
+    "system/tray/time_to_click_recorder.cc",
+    "system/tray/time_to_click_recorder.h",
     "system/tray/tray_background_view.cc",
     "system/tray/tray_background_view.h",
     "system/tray/tray_bubble_wrapper.cc",
@@ -1579,6 +1583,7 @@
     "drag_drop/drag_drop_tracker_unittest.cc",
     "drag_drop/drag_image_view_unittest.cc",
     "events/keyboard_driven_event_rewriter_unittest.cc",
+    "events/spoken_feedback_event_rewriter_unittest.cc",
     "extended_desktop_unittest.cc",
     "first_run/first_run_helper_unittest.cc",
     "focus_cycler_unittest.cc",
diff --git a/ash/events/OWNERS b/ash/events/OWNERS
new file mode 100644
index 0000000..fbd7277
--- /dev/null
+++ b/ash/events/OWNERS
@@ -0,0 +1 @@
+per-file spoken_feedback_event_rewriter*=file://ui/accessibility/OWNERS
diff --git a/ash/events/event_rewriter_controller.cc b/ash/events/event_rewriter_controller.cc
index e8546e96..91871fd6 100644
--- a/ash/events/event_rewriter_controller.cc
+++ b/ash/events/event_rewriter_controller.cc
@@ -9,10 +9,12 @@
 #include "ash/display/mirror_window_controller.h"
 #include "ash/display/window_tree_host_manager.h"
 #include "ash/events/keyboard_driven_event_rewriter.h"
+#include "ash/events/spoken_feedback_event_rewriter.h"
 #include "ash/shell.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/events/event_rewriter.h"
+#include "ui/events/event_sink.h"
 #include "ui/events/event_source.h"
 
 namespace ash {
@@ -25,6 +27,11 @@
       std::make_unique<KeyboardDrivenEventRewriter>();
   keyboard_driven_event_rewriter_ = keyboard_driven_event_rewriter.get();
   AddEventRewriter(std::move(keyboard_driven_event_rewriter));
+
+  std::unique_ptr<SpokenFeedbackEventRewriter> spoken_feedback_event_rewriter =
+      std::make_unique<SpokenFeedbackEventRewriter>();
+  spoken_feedback_event_rewriter_ = spoken_feedback_event_rewriter.get();
+  AddEventRewriter(std::move(spoken_feedback_event_rewriter));
 }
 
 EventRewriterController::~EventRewriterController() {
@@ -67,6 +74,17 @@
   keyboard_driven_event_rewriter_->set_arrow_to_tab_rewriting_enabled(enabled);
 }
 
+void EventRewriterController::SetSpokenFeedbackEventRewriterDelegate(
+    mojom::SpokenFeedbackEventRewriterDelegatePtr delegate) {
+  spoken_feedback_event_rewriter_->SetDelegate(std::move(delegate));
+}
+
+void EventRewriterController::OnUnhandledSpokenFeedbackEvent(
+    std::unique_ptr<ui::Event> event) {
+  spoken_feedback_event_rewriter_->OnUnhandledSpokenFeedbackEvent(
+      std::move(event));
+}
+
 void EventRewriterController::OnHostInitialized(aura::WindowTreeHost* host) {
   for (const auto& rewriter : rewriters_)
     host->GetEventSource()->AddEventRewriter(rewriter.get());
diff --git a/ash/events/event_rewriter_controller.h b/ash/events/event_rewriter_controller.h
index 05c6637..04ef56f8 100644
--- a/ash/events/event_rewriter_controller.h
+++ b/ash/events/event_rewriter_controller.h
@@ -22,6 +22,7 @@
 namespace ash {
 
 class KeyboardDrivenEventRewriter;
+class SpokenFeedbackEventRewriter;
 
 // Owns ui::EventRewriters and ensures that they are added to each root window
 // EventSource, current and future, in the order that they are added to this.
@@ -42,6 +43,10 @@
   // mojom::EventRewriterController:
   void SetKeyboardDrivenEventRewriterEnabled(bool enabled) override;
   void SetArrowToTabRewritingEnabled(bool enabled) override;
+  void SetSpokenFeedbackEventRewriterDelegate(
+      mojom::SpokenFeedbackEventRewriterDelegatePtr delegate) override;
+  void OnUnhandledSpokenFeedbackEvent(
+      std::unique_ptr<ui::Event> event) override;
 
   // aura::EnvObserver:
   void OnWindowInitialized(aura::Window* window) override {}
@@ -51,9 +56,12 @@
   // The |EventRewriter|s managed by this controller.
   std::vector<std::unique_ptr<ui::EventRewriter>> rewriters_;
 
-  // A weak pointer the KeyboardDrivenEventRewriter owned in |rewriters_|.
+  // A weak pointer to the KeyboardDrivenEventRewriter owned in |rewriters_|.
   KeyboardDrivenEventRewriter* keyboard_driven_event_rewriter_;
 
+  // A weak pointer to the SpokenFeedbackEventRewriter owned in |rewriters_|.
+  SpokenFeedbackEventRewriter* spoken_feedback_event_rewriter_;
+
   // Bindings for the EventRewriterController mojo interface.
   mojo::BindingSet<mojom::EventRewriterController> bindings_;
 
diff --git a/ash/events/spoken_feedback_event_rewriter.cc b/ash/events/spoken_feedback_event_rewriter.cc
new file mode 100644
index 0000000..96ce99e7
--- /dev/null
+++ b/ash/events/spoken_feedback_event_rewriter.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/events/spoken_feedback_event_rewriter.h"
+
+#include <string>
+#include <utility>
+
+#include "ash/accessibility/accessibility_controller.h"
+#include "ash/shell.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/event.h"
+#include "ui/events/event_sink.h"
+
+namespace ash {
+
+SpokenFeedbackEventRewriter::SpokenFeedbackEventRewriter() = default;
+
+SpokenFeedbackEventRewriter::~SpokenFeedbackEventRewriter() = default;
+
+void SpokenFeedbackEventRewriter::SetDelegate(
+    mojom::SpokenFeedbackEventRewriterDelegatePtr delegate) {
+  delegate_ = std::move(delegate);
+}
+
+void SpokenFeedbackEventRewriter::OnUnhandledSpokenFeedbackEvent(
+    std::unique_ptr<ui::Event> event) const {
+  DCHECK(event->IsKeyEvent()) << "Unexpected unhandled event type";
+  // For now, these events are sent directly to the primary display's EventSink.
+  // TODO: Pass the event to the original EventSource, not the primary one.
+  ui::EventSource* source =
+      Shell::GetPrimaryRootWindow()->GetHost()->GetEventSource();
+  if (SendEventToEventSource(source, event.get()).dispatcher_destroyed) {
+    VLOG(0) << "Undispatched key " << event->AsKeyEvent()->key_code()
+            << " due to destroyed dispatcher.";
+  }
+}
+
+ui::EventRewriteStatus SpokenFeedbackEventRewriter::RewriteEvent(
+    const ui::Event& event,
+    std::unique_ptr<ui::Event>* new_event) {
+  if (!delegate_.is_bound() || !event.IsKeyEvent())
+    return ui::EVENT_REWRITE_CONTINUE;
+
+  if (!Shell::Get()->accessibility_controller()->IsSpokenFeedbackEnabled())
+    return ui::EVENT_REWRITE_CONTINUE;
+
+  // TODO: Avoid passing events that will be reposted for system-wide dispatch.
+  delegate_->DispatchKeyEventToChromeVox(ui::Event::Clone(event));
+  return ui::EVENT_REWRITE_DISCARD;
+}
+
+ui::EventRewriteStatus SpokenFeedbackEventRewriter::NextDispatchEvent(
+    const ui::Event& last_event,
+    std::unique_ptr<ui::Event>* new_event) {
+  NOTREACHED();
+  return ui::EVENT_REWRITE_CONTINUE;
+}
+
+}  // namespace ash
diff --git a/ash/events/spoken_feedback_event_rewriter.h b/ash/events/spoken_feedback_event_rewriter.h
new file mode 100644
index 0000000..cd3568c
--- /dev/null
+++ b/ash/events/spoken_feedback_event_rewriter.h
@@ -0,0 +1,50 @@
+// 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 ASH_EVENTS_SPOKEN_FEEDBACK_EVENT_REWRITER_H_
+#define ASH_EVENTS_SPOKEN_FEEDBACK_EVENT_REWRITER_H_
+
+#include "ash/ash_export.h"
+#include "ash/public/interfaces/event_rewriter_controller.mojom.h"
+#include "base/macros.h"
+#include "ui/events/event_rewriter.h"
+
+namespace ash {
+
+// SpokenFeedbackEventRewriter sends key events to ChromeVox (via the delegate)
+// when spoken feedback is enabled. Continues dispatch of unhandled key events.
+// TODO(http://crbug.com/839541): Avoid reposting unhandled events.
+class ASH_EXPORT SpokenFeedbackEventRewriter : public ui::EventRewriter {
+ public:
+  SpokenFeedbackEventRewriter();
+  ~SpokenFeedbackEventRewriter() override;
+
+  // Set the delegate used to send key events to the ChromeVox extension.
+  void SetDelegate(mojom::SpokenFeedbackEventRewriterDelegatePtr delegate);
+  mojom::SpokenFeedbackEventRewriterDelegatePtr* get_delegate_for_testing() {
+    return &delegate_;
+  }
+
+  // Continue dispatch of events that were unhandled by the ChromeVox extension.
+  // NOTE: These events may be delivered out-of-order from non-ChromeVox events.
+  void OnUnhandledSpokenFeedbackEvent(std::unique_ptr<ui::Event> event) const;
+
+ private:
+  // ui::EventRewriter:
+  ui::EventRewriteStatus RewriteEvent(
+      const ui::Event& event,
+      std::unique_ptr<ui::Event>* new_event) override;
+  ui::EventRewriteStatus NextDispatchEvent(
+      const ui::Event& last_event,
+      std::unique_ptr<ui::Event>* new_event) override;
+
+  // The delegate used to send key events to the ChromeVox extension.
+  mojom::SpokenFeedbackEventRewriterDelegatePtr delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpokenFeedbackEventRewriter);
+};
+
+}  // namespace ash
+
+#endif  // ASH_EVENTS_SPOKEN_FEEDBACK_EVENT_REWRITER_H_
diff --git a/ash/events/spoken_feedback_event_rewriter_unittest.cc b/ash/events/spoken_feedback_event_rewriter_unittest.cc
new file mode 100644
index 0000000..ca9aad877
--- /dev/null
+++ b/ash/events/spoken_feedback_event_rewriter_unittest.cc
@@ -0,0 +1,214 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/events/spoken_feedback_event_rewriter.h"
+
+#include <memory>
+#include <vector>
+
+#include "ash/accessibility/accessibility_controller.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/event_rewriter.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/events/test/event_generator.h"
+
+namespace ash {
+namespace {
+
+// An event rewriter that simply records all events that it receives.
+class EventRecorder : public ui::EventRewriter {
+ public:
+  EventRecorder() = default;
+  ~EventRecorder() override = default;
+
+  // ui::EventRewriter:
+  ui::EventRewriteStatus RewriteEvent(
+      const ui::Event& event,
+      std::unique_ptr<ui::Event>* new_event) override {
+    recorded_event_count_++;
+    return ui::EVENT_REWRITE_CONTINUE;
+  }
+  ui::EventRewriteStatus NextDispatchEvent(
+      const ui::Event& last_event,
+      std::unique_ptr<ui::Event>* new_event) override {
+    NOTREACHED();
+    return ui::EVENT_REWRITE_CONTINUE;
+  }
+
+  // Count of events sent to the rewriter.
+  size_t recorded_event_count_ = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EventRecorder);
+};
+
+// A test implementation of the spoken feedback delegate interface.
+class TestDelegate : public mojom::SpokenFeedbackEventRewriterDelegate {
+ public:
+  TestDelegate() : binding_(this) {}
+  ~TestDelegate() override = default;
+
+  mojom::SpokenFeedbackEventRewriterDelegatePtr BindInterface() {
+    mojom::SpokenFeedbackEventRewriterDelegatePtr ptr;
+    binding_.Bind(MakeRequest(&ptr));
+    return ptr;
+  }
+
+  // Count of events sent to the delegate.
+  size_t recorded_event_count_ = 0;
+
+ private:
+  // SpokenFeedbackEventRewriterDelegate:
+  void DispatchKeyEventToChromeVox(std::unique_ptr<ui::Event> event) override {
+    recorded_event_count_++;
+  }
+
+  // The binding that backs the interface pointer held by the event rewriter.
+  mojo::Binding<ash::mojom::SpokenFeedbackEventRewriterDelegate> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
+};
+
+class SpokenFeedbackEventRewriterTest : public ash::AshTestBase {
+ public:
+  SpokenFeedbackEventRewriterTest() {
+    spoken_feedback_event_rewriter_.SetDelegate(delegate_.BindInterface());
+  }
+
+  void SetUp() override {
+    ash::AshTestBase::SetUp();
+    generator_ = &AshTestBase::GetEventGenerator();
+    CurrentContext()->GetHost()->GetEventSource()->AddEventRewriter(
+        &spoken_feedback_event_rewriter_);
+    CurrentContext()->GetHost()->GetEventSource()->AddEventRewriter(
+        &event_recorder_);
+  }
+
+  void TearDown() override {
+    CurrentContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
+        &event_recorder_);
+    CurrentContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
+        &spoken_feedback_event_rewriter_);
+    generator_ = nullptr;
+    ash::AshTestBase::TearDown();
+  }
+
+  // Flush any messages to the test delegate and return events it has recorded.
+  size_t GetDelegateRecordedEventCount() {
+    spoken_feedback_event_rewriter_.get_delegate_for_testing()
+        ->FlushForTesting();
+    return delegate_.recorded_event_count_;
+  }
+
+ protected:
+  // A test spoken feedback delegate; simulates ChromeVox.
+  TestDelegate delegate_;
+  // Generates ui::Events from simulated user input.
+  ui::test::EventGenerator* generator_ = nullptr;
+  // Records events delivered to the next event rewriter after spoken feedback.
+  EventRecorder event_recorder_;
+
+  SpokenFeedbackEventRewriter spoken_feedback_event_rewriter_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SpokenFeedbackEventRewriterTest);
+};
+
+// The delegate should not intercept events when spoken feedback is disabled.
+TEST_F(SpokenFeedbackEventRewriterTest, EventsNotConsumedWhenDisabled) {
+  AccessibilityController* controller =
+      Shell::Get()->accessibility_controller();
+  EXPECT_FALSE(controller->IsSpokenFeedbackEnabled());
+
+  generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(1U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(0U, GetDelegateRecordedEventCount());
+  generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(2U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(0U, GetDelegateRecordedEventCount());
+
+  generator_->ClickLeftButton();
+  EXPECT_EQ(4U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(0U, GetDelegateRecordedEventCount());
+
+  generator_->GestureTapAt(gfx::Point());
+  EXPECT_EQ(6U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(0U, GetDelegateRecordedEventCount());
+}
+
+// The delegate should intercept key events when spoken feedback is enabled.
+TEST_F(SpokenFeedbackEventRewriterTest, KeyEventsConsumedWhenEnabled) {
+  AccessibilityController* controller =
+      Shell::Get()->accessibility_controller();
+  controller->SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_NONE);
+  EXPECT_TRUE(controller->IsSpokenFeedbackEnabled());
+
+  generator_->PressKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(0U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(1U, GetDelegateRecordedEventCount());
+  generator_->ReleaseKey(ui::VKEY_A, ui::EF_NONE);
+  EXPECT_EQ(0U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(2U, GetDelegateRecordedEventCount());
+
+  generator_->ClickLeftButton();
+  EXPECT_EQ(2U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(2U, GetDelegateRecordedEventCount());
+
+  generator_->GestureTapAt(gfx::Point());
+  EXPECT_EQ(4U, event_recorder_.recorded_event_count_);
+  EXPECT_EQ(2U, GetDelegateRecordedEventCount());
+}
+
+// Asynchronously unhandled events should be sent to subsequent rewriters.
+TEST_F(SpokenFeedbackEventRewriterTest, UnhandledEventsSentToOtherRewriters) {
+  spoken_feedback_event_rewriter_.OnUnhandledSpokenFeedbackEvent(
+      std::make_unique<ui::KeyEvent>(ui::ET_KEY_PRESSED, ui::VKEY_A,
+                                     ui::EF_NONE));
+  EXPECT_EQ(1U, event_recorder_.recorded_event_count_);
+  spoken_feedback_event_rewriter_.OnUnhandledSpokenFeedbackEvent(
+      std::make_unique<ui::KeyEvent>(ui::ET_KEY_RELEASED, ui::VKEY_A,
+                                     ui::EF_NONE));
+  EXPECT_EQ(2U, event_recorder_.recorded_event_count_);
+}
+
+TEST_F(SpokenFeedbackEventRewriterTest, KeysNotEatenWithChromeVoxDisabled) {
+  AccessibilityController* controller =
+      Shell::Get()->accessibility_controller();
+  EXPECT_FALSE(controller->IsSpokenFeedbackEnabled());
+
+  // Send Search+Shift+Right.
+  generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
+  EXPECT_EQ(1U, event_recorder_.recorded_event_count_);
+  generator_->PressKey(ui::VKEY_SHIFT, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
+  EXPECT_EQ(2U, event_recorder_.recorded_event_count_);
+
+  // Mock successful commands lookup and dispatch; shouldn't matter either way.
+  generator_->PressKey(ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
+  EXPECT_EQ(3U, event_recorder_.recorded_event_count_);
+
+  // Released keys shouldn't get eaten.
+  generator_->ReleaseKey(ui::VKEY_RIGHT,
+                         ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
+  generator_->ReleaseKey(ui::VKEY_SHIFT, ui::EF_COMMAND_DOWN);
+  generator_->ReleaseKey(ui::VKEY_LWIN, 0);
+  EXPECT_EQ(6U, event_recorder_.recorded_event_count_);
+
+  // Try releasing more keys.
+  generator_->ReleaseKey(ui::VKEY_RIGHT, 0);
+  generator_->ReleaseKey(ui::VKEY_SHIFT, 0);
+  generator_->ReleaseKey(ui::VKEY_LWIN, 0);
+  EXPECT_EQ(9U, event_recorder_.recorded_event_count_);
+
+  EXPECT_EQ(0U, GetDelegateRecordedEventCount());
+}
+
+}  // namespace
+}  // namespace ash
diff --git a/ash/public/interfaces/event_rewriter_controller.mojom b/ash/public/interfaces/event_rewriter_controller.mojom
index 4917b6ee..b044de8 100644
--- a/ash/public/interfaces/event_rewriter_controller.mojom
+++ b/ash/public/interfaces/event_rewriter_controller.mojom
@@ -4,6 +4,14 @@
 
 module ash.mojom;
 
+import "ui/events/mojo/event.mojom";
+
+// Allows a client to implement spoken feedback features; used for ChromeVox.
+interface SpokenFeedbackEventRewriterDelegate {
+  // Used to send key events to the ChromeVox extension.
+  DispatchKeyEventToChromeVox(ui.mojom.Event event);
+};
+
 // Allows clients to toggle some event rewriting behavior.
 interface EventRewriterController {
   // Enables the KeyboardDrivenEventRewriter, which is disabled by default.
@@ -13,4 +21,12 @@
   // If true, Shift + Arrow keys are rewritten to Tab/Shift-Tab keys.
   // This only applies when the KeyboardDrivenEventRewriter is active.
   SetArrowToTabRewritingEnabled(bool enabled);
+
+  // Set the delegate used by the spoken feedback event rewriter.
+  SetSpokenFeedbackEventRewriterDelegate(
+      SpokenFeedbackEventRewriterDelegate delegate);
+
+  // Continue dispatch of key events that were unhandled by ChromeVox.
+  // TODO(crbug.com/839541): ChromeVox should not repost unhandled events.
+  OnUnhandledSpokenFeedbackEvent(ui.mojom.Event event);
 };
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index e7fba94..40084fe 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -189,6 +189,8 @@
 TrayBackgroundView* StatusAreaWidget::GetSystemTrayAnchor() const {
   if (overview_button_tray_->visible())
     return overview_button_tray_.get();
+  if (unified_system_tray_)
+    return unified_system_tray_.get();
   return system_tray_.get();
 }
 
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 0ff6ff7..ad1a282 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -620,13 +620,13 @@
   UserMetricsRecorder::RecordUserClickOnTray(
       LoginMetricsRecorder::TrayClickTarget::kSystemTray);
 
-  last_button_clicked_ = base::TimeTicks::Now();
-
   if (features::IsSystemTrayUnifiedEnabled()) {
     return shelf()->GetStatusAreaWidget()->unified_system_tray()->PerformAction(
         event);
   }
 
+  last_button_clicked_ = base::TimeTicks::Now();
+
   // If we're already showing a full system tray menu, either default or
   // detailed menu, hide it; otherwise, show it (and hide any popup that's
   // currently shown).
@@ -658,6 +658,14 @@
              : nullptr;
 }
 
+void SystemTray::SetVisible(bool visible) {
+  // TODO(tetsui): Port logic in SystemTrayItems that is unrelated to SystemTray
+  // UI, and stop instantiating SystemTray instead of hiding it when
+  // UnifiedSystemTray is enabled.
+  TrayBackgroundView::SetVisible(!features::IsSystemTrayUnifiedEnabled() &&
+                                 visible);
+}
+
 void SystemTray::BubbleViewDestroyed() {
   if (system_bubble_) {
     system_bubble_->bubble()->BubbleViewDestroyed();
@@ -747,16 +755,4 @@
   }
 }
 
-TimeToClickRecorder::TimeToClickRecorder(SystemTray* tray) : tray_(tray) {}
-
-void TimeToClickRecorder::OnEvent(ui::Event* event) {
-  // Ignore if the event is neither click nor tap.
-  if (event->type() != ui::ET_MOUSE_PRESSED &&
-      event->type() != ui::ET_GESTURE_TAP) {
-    return;
-  }
-
-  tray_->RecordTimeToClick();
-}
-
 }  // namespace ash
diff --git a/ash/system/tray/system_tray.h b/ash/system/tray/system_tray.h
index 8529a99..8d0db51 100644
--- a/ash/system/tray/system_tray.h
+++ b/ash/system/tray/system_tray.h
@@ -12,6 +12,7 @@
 #include "ash/ash_export.h"
 #include "ash/system/tray/system_tray_bubble.h"
 #include "ash/system/tray/system_tray_view.h"
+#include "ash/system/tray/time_to_click_recorder.h"
 #include "ash/system/tray/tray_background_view.h"
 #include "base/callback.h"
 #include "base/macros.h"
@@ -53,7 +54,8 @@
 // manages all the SystemTrayItem controllers, creates icon views that appear in
 // the tray, creates the bubble menu and fills the menu with items. It is also
 // the view that contains the icons in the tray.
-class ASH_EXPORT SystemTray : public TrayBackgroundView {
+class ASH_EXPORT SystemTray : public TrayBackgroundView,
+                              public TimeToClickRecorder::Delegate {
  public:
   explicit SystemTray(Shelf* shelf);
   ~SystemTray() override;
@@ -133,10 +135,6 @@
   // Returns TrayIME object if present or null otherwise.
   TrayIME* GetTrayIME() const;
 
-  // Record TimeToClick metrics and reset |last_button_clicked_|. Called by
-  // TimeToClickRecorder which is system tray view's PreTargetHandler.
-  void RecordTimeToClick();
-
   // Determines if it's ok to switch away from the currently active user. Screen
   // casting may block this (or at least throw up a confirmation dialog). Calls
   // |callback| with the result.
@@ -153,6 +151,7 @@
   void CloseBubble() override;
   void ShowBubble(bool show_by_click) override;
   views::TrayBubbleView* GetBubbleView() override;
+  void SetVisible(bool visible) override;
 
   // views::TrayBubbleView::Delegate:
   void BubbleViewDestroyed() override;
@@ -162,6 +161,9 @@
   bool ShouldEnableExtraKeyboardAccessibility() override;
   void HideBubble(const views::TrayBubbleView* bubble_view) override;
 
+  // TimeToClickRecorder::Delegate:
+  void RecordTimeToClick() override;
+
   ScreenTrayItem* GetScreenShareItem() { return screen_share_tray_item_; }
   ScreenTrayItem* GetScreenCaptureItem() { return screen_capture_tray_item_; }
 
@@ -256,22 +258,6 @@
   DISALLOW_COPY_AND_ASSIGN(SystemTray);
 };
 
-// An event handler that will be installed as system tray view PreTargetHandler
-// to record TimeToClick metrics.
-class TimeToClickRecorder : public ui::EventHandler {
- public:
-  TimeToClickRecorder(SystemTray* tray);
-  ~TimeToClickRecorder() override = default;
-
- private:
-  // ui::EventHandler:
-  void OnEvent(ui::Event* event) override;
-
-  SystemTray* const tray_;
-
-  DISALLOW_COPY_AND_ASSIGN(TimeToClickRecorder);
-};
-
 }  // namespace ash
 
 #endif  // ASH_SYSTEM_TRAY_SYSTEM_TRAY_H_
diff --git a/ash/system/tray/system_tray_view.cc b/ash/system/tray/system_tray_view.cc
index daf81229..b9b34c0 100644
--- a/ash/system/tray/system_tray_view.cc
+++ b/ash/system/tray/system_tray_view.cc
@@ -51,11 +51,10 @@
                                SystemTrayType system_tray_type,
                                const std::vector<ash::SystemTrayItem*>& items)
     : time_to_click_recorder_(
-          std::make_unique<TimeToClickRecorder>(system_tray)),
+          std::make_unique<TimeToClickRecorder>(system_tray, this)),
       items_(items),
       system_tray_type_(system_tray_type) {
   SetLayoutManager(std::make_unique<BottomAlignedBoxLayout>());
-  AddPreTargetHandler(time_to_click_recorder_.get());
 }
 
 SystemTrayView::~SystemTrayView() {
diff --git a/ash/system/tray/time_to_click_recorder.cc b/ash/system/tray/time_to_click_recorder.cc
new file mode 100644
index 0000000..b3509f87
--- /dev/null
+++ b/ash/system/tray/time_to_click_recorder.cc
@@ -0,0 +1,28 @@
+// 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 "ash/system/tray/time_to_click_recorder.h"
+
+#include "ui/events/event.h"
+#include "ui/views/view.h"
+
+namespace ash {
+
+TimeToClickRecorder::TimeToClickRecorder(Delegate* delegate,
+                                         views::View* target_view)
+    : delegate_(delegate) {
+  target_view->AddPreTargetHandler(this);
+}
+
+void TimeToClickRecorder::OnEvent(ui::Event* event) {
+  // Ignore if the event is neither click nor tap.
+  if (event->type() != ui::ET_MOUSE_PRESSED &&
+      event->type() != ui::ET_GESTURE_TAP) {
+    return;
+  }
+
+  delegate_->RecordTimeToClick();
+}
+
+}  // namespace ash
diff --git a/ash/system/tray/time_to_click_recorder.h b/ash/system/tray/time_to_click_recorder.h
new file mode 100644
index 0000000..4e26b9d
--- /dev/null
+++ b/ash/system/tray/time_to_click_recorder.h
@@ -0,0 +1,43 @@
+// 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 ASH_SYSTEM_TRAY_TIME_TO_CLICK_RECORDER_H_
+#define ASH_SYSTEM_TRAY_TIME_TO_CLICK_RECORDER_H_
+
+#include "ui/events/event_handler.h"
+
+namespace views {
+class View;
+}  // namespace views
+
+namespace ash {
+
+// An event handler that will be installed as PreTargetHandler of |target_view|
+// to record TimeToClick metrics.
+class TimeToClickRecorder : public ui::EventHandler {
+ public:
+  class Delegate {
+   public:
+    virtual ~Delegate() {}
+
+    // Record TimeToClick metrics. Called by TimeToClickRecorder which is a
+    // PreTargetHandler of |target_view|.
+    virtual void RecordTimeToClick() = 0;
+  };
+
+  TimeToClickRecorder(Delegate* delegate, views::View* target_view);
+  ~TimeToClickRecorder() override = default;
+
+ private:
+  // ui::EventHandler:
+  void OnEvent(ui::Event* event) override;
+
+  Delegate* const delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(TimeToClickRecorder);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_TRAY_TIME_TO_CLICK_RECORDER_H_
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 633dfa6..e4e70df 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -4,8 +4,12 @@
 
 #include "ash/system/unified/unified_system_tray.h"
 
+#include "ash/shell.h"
+#include "ash/system/date/date_view.h"
+#include "ash/system/model/system_tray_model.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/system/tray/system_tray.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/system/web_notification/ash_popup_alignment_delegate.h"
@@ -80,7 +84,7 @@
   if (owner_->IsBubbleShown())
     return false;
 
-  owner_->ShowBubbleInternal();
+  owner_->ShowBubbleInternal(show_by_click);
   return true;
 }
 
@@ -96,11 +100,11 @@
     : TrayBackgroundView(shelf),
       ui_delegate_(std::make_unique<UiDelegate>(this)),
       model_(std::make_unique<UnifiedSystemTrayModel>()) {
-  // On the first step, features in the status area button are still provided by
-  // TrayViews in SystemTray.
-  // TODO(tetsui): Remove SystemTray from StatusAreaWidget and provide these
-  // features from UnifiedSystemTray.
-  SetVisible(false);
+  tray_container()->AddChildView(
+      new tray::TimeView(tray::TimeView::ClockLayout::HORIZONTAL_CLOCK,
+                         Shell::Get()->system_tray_model()->clock()));
+  SetInkDropMode(InkDropMode::ON);
+  SetVisible(true);
 }
 
 UnifiedSystemTray::~UnifiedSystemTray() = default;
@@ -138,16 +142,14 @@
 
 void UnifiedSystemTray::ClickedOutsideBubble() {}
 
-void UnifiedSystemTray::ShowBubbleInternal() {
-  bubble_ = std::make_unique<UnifiedSystemTrayBubble>(this);
-  // TODO(tetsui): Call its own SetIsActive. See the comment in the ctor.
-  shelf()->GetStatusAreaWidget()->system_tray()->SetIsActive(true);
+void UnifiedSystemTray::ShowBubbleInternal(bool show_by_click) {
+  bubble_ = std::make_unique<UnifiedSystemTrayBubble>(this, show_by_click);
+  SetIsActive(true);
 }
 
 void UnifiedSystemTray::HideBubbleInternal() {
   bubble_.reset();
-  // TODO(tetsui): Call its own SetIsActive. See the comment in the ctor.
-  shelf()->GetStatusAreaWidget()->system_tray()->SetIsActive(false);
+  SetIsActive(false);
 }
 
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_tray.h b/ash/system/unified/unified_system_tray.h
index 0b7a958..09aeacc 100644
--- a/ash/system/unified/unified_system_tray.h
+++ b/ash/system/unified/unified_system_tray.h
@@ -51,7 +51,7 @@
   class UiDelegate;
 
   // Forwarded from UiDelegate.
-  void ShowBubbleInternal();
+  void ShowBubbleInternal(bool show_by_click);
   void HideBubbleInternal();
 
   std::unique_ptr<UiDelegate> ui_delegate_;
diff --git a/ash/system/unified/unified_system_tray_bubble.cc b/ash/system/unified/unified_system_tray_bubble.cc
index 5d67e56..17f6948 100644
--- a/ash/system/unified/unified_system_tray_bubble.cc
+++ b/ash/system/unified/unified_system_tray_bubble.cc
@@ -10,6 +10,7 @@
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
 #include "ash/system/unified/unified_system_tray_view.h"
+#include "base/metrics/histogram_macros.h"
 
 namespace ash {
 
@@ -19,11 +20,15 @@
 
 }  // namespace
 
-UnifiedSystemTrayBubble::UnifiedSystemTrayBubble(UnifiedSystemTray* tray)
+UnifiedSystemTrayBubble::UnifiedSystemTrayBubble(UnifiedSystemTray* tray,
+                                                 bool show_by_click)
     : controller_(std::make_unique<UnifiedSystemTrayController>(
           tray->model(),
           tray->shelf()->GetStatusAreaWidget()->system_tray())),
       tray_(tray) {
+  if (show_by_click)
+    time_shown_by_click_ = base::TimeTicks::Now();
+
   views::TrayBubbleView::InitParams init_params;
   init_params.anchor_alignment = views::TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM;
   init_params.min_width = kTrayMenuWidth;
@@ -40,6 +45,8 @@
                    kPaddingFromScreenTop -
                    bubble_view->GetBorderInsets().height();
   auto* unified_view = controller_->CreateView();
+  time_to_click_recorder_ =
+      std::make_unique<TimeToClickRecorder>(this, unified_view);
   unified_view->SetMaxHeight(max_height);
   bubble_view->SetMaxHeight(max_height);
   bubble_view->AddChildView(unified_view);
@@ -77,4 +84,15 @@
   tray_->CloseBubble();
 }
 
+void UnifiedSystemTrayBubble::RecordTimeToClick() {
+  // Ignore if the tray bubble is not opened by click.
+  if (!time_shown_by_click_)
+    return;
+
+  UMA_HISTOGRAM_TIMES("ChromeOS.SystemTray.TimeToClick",
+                      base::TimeTicks::Now() - time_shown_by_click_.value());
+
+  time_shown_by_click_.reset();
+}
+
 }  // namespace ash
diff --git a/ash/system/unified/unified_system_tray_bubble.h b/ash/system/unified/unified_system_tray_bubble.h
index 9e9a32d..82fff38 100644
--- a/ash/system/unified/unified_system_tray_bubble.h
+++ b/ash/system/unified/unified_system_tray_bubble.h
@@ -7,7 +7,10 @@
 
 #include <memory>
 
+#include "ash/system/tray/time_to_click_recorder.h"
 #include "base/macros.h"
+#include "base/optional.h"
+#include "base/time/time.h"
 #include "ui/views/widget/widget_observer.h"
 
 namespace views {
@@ -23,14 +26,18 @@
 // Shows the bubble on the constructor, and closes the bubble on the destructor.
 // It is possible that the bubble widget is closed on deactivation. In such
 // case, this class calls UnifiedSystemTray::CloseBubble() to delete itself.
-class UnifiedSystemTrayBubble : public views::WidgetObserver {
+class UnifiedSystemTrayBubble : public views::WidgetObserver,
+                                public TimeToClickRecorder::Delegate {
  public:
-  explicit UnifiedSystemTrayBubble(UnifiedSystemTray* tray);
+  explicit UnifiedSystemTrayBubble(UnifiedSystemTray* tray, bool show_by_click);
   ~UnifiedSystemTrayBubble() override;
 
   // views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
 
+  // TimeToClickRecorder::Delegate:
+  void RecordTimeToClick() override;
+
  private:
   // Controller of UnifiedSystemTrayView. As the view is owned by views
   // hierarchy, we have to own the controller here.
@@ -46,6 +53,13 @@
   // In order to do this, we observe OnWidgetDestroying().
   views::Widget* bubble_widget_ = nullptr;
 
+  // PreTargetHandler of |unified_view_| to record TimeToClick metrics. Owned.
+  std::unique_ptr<TimeToClickRecorder> time_to_click_recorder_;
+
+  // The time the bubble is created. If the bubble is not created by button
+  // click (|show_by_click| in ctor is false), it is not set.
+  base::Optional<base::TimeTicks> time_shown_by_click_;
+
   DISALLOW_COPY_AND_ASSIGN(UnifiedSystemTrayBubble);
 };
 
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index 7e3077c..78d8b61f 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -73,9 +73,6 @@
       std::make_unique<UnifiedBrightnessSliderController>(model_);
   unified_view_->AddSliderView(brightness_slider_controller_->CreateView());
 
-  time_to_click_recorder_ = std::make_unique<TimeToClickRecorder>(system_tray_);
-  unified_view_->AddPreTargetHandler(time_to_click_recorder_.get());
-
   return unified_view_;
 }
 
diff --git a/ash/system/unified/unified_system_tray_controller.h b/ash/system/unified/unified_system_tray_controller.h
index eb2e97b..52e11cf 100644
--- a/ash/system/unified/unified_system_tray_controller.h
+++ b/ash/system/unified/unified_system_tray_controller.h
@@ -22,7 +22,6 @@
 class FeaturePodControllerBase;
 class SystemTray;
 class SystemTrayItem;
-class TimeToClickRecorder;
 class UnifiedBrightnessSliderController;
 class UnifiedVolumeSliderController;
 class UnifiedSystemTrayModel;
@@ -130,9 +129,6 @@
   std::unique_ptr<UnifiedBrightnessSliderController>
       brightness_slider_controller_;
 
-  // PreTargetHandler of |unified_view_| to record TimeToClick metrics. Owned.
-  std::unique_ptr<TimeToClickRecorder> time_to_click_recorder_;
-
   // If the previous state is expanded or not. Only valid during dragging (from
   // BeginDrag to EndDrag).
   bool was_expanded_ = true;
diff --git a/base/strings/char_traits.h b/base/strings/char_traits.h
index 6cfbad5af..b193e216 100644
--- a/base/strings/char_traits.h
+++ b/base/strings/char_traits.h
@@ -7,6 +7,8 @@
 
 #include <stddef.h>
 
+#include "base/compiler_specific.h"
+
 namespace base {
 
 // constexpr version of http://en.cppreference.com/w/cpp/string/char_traits.
@@ -61,7 +63,7 @@
 constexpr int CharTraits<char>::compare(const char* s1,
                                         const char* s2,
                                         size_t n) noexcept {
-#if __has_feature(cxx_constexpr_string_builtins)
+#if HAS_FEATURE(cxx_constexpr_string_builtins)
   return __builtin_memcmp(s1, s2, n);
 #else
   for (; n; --n, ++s1, ++s2) {
diff --git a/build/android/pylib/symbols/elf_symbolizer.py b/build/android/pylib/symbols/elf_symbolizer.py
index 7f256ea..1f2f918 100644
--- a/build/android/pylib/symbols/elf_symbolizer.py
+++ b/build/android/pylib/symbols/elf_symbolizer.py
@@ -185,6 +185,11 @@
 
     a2l.EnqueueRequest(addr, callback_arg)
 
+  def WaitForIdle(self):
+    """Waits for all the outstanding requests to complete."""
+    for a2l in self._a2l_instances:
+      a2l.WaitForIdle()
+
   def Join(self):
     """Waits for all the outstanding requests to complete and terminates."""
     for a2l in self._a2l_instances:
diff --git a/build/android/pylib/symbols/elf_symbolizer_unittest.py b/build/android/pylib/symbols/elf_symbolizer_unittest.py
index 1d95b15..765b5989 100755
--- a/build/android/pylib/symbols/elf_symbolizer_unittest.py
+++ b/build/android/pylib/symbols/elf_symbolizer_unittest.py
@@ -117,6 +117,31 @@
 
     symbolizer.Join()
 
+  def testWaitForIdle(self):
+    symbolizer = elf_symbolizer.ELFSymbolizer(
+        elf_file_path='/path/doesnt/matter/mock_lib1.so',
+        addr2line_path=_MOCK_A2L_PATH,
+        callback=self._callback,
+        max_concurrent_jobs=1)
+
+    # Test symbols with valid name but incomplete path.
+    addr = _INCOMPLETE_MOCK_ADDR
+    exp_name = 'mock_sym_for_addr_%d' % addr
+    exp_source_path = None
+    exp_source_line = None
+    cb_arg = (addr, exp_name, exp_source_path, exp_source_line, False)
+    symbolizer.SymbolizeAsync(addr, cb_arg)
+    symbolizer.WaitForIdle()
+
+    # Test symbols with no name or sym info.
+    addr = _UNKNOWN_MOCK_ADDR
+    exp_name = None
+    exp_source_path = None
+    exp_source_line = None
+    cb_arg = (addr, exp_name, exp_source_path, exp_source_line, False)
+    symbolizer.SymbolizeAsync(addr, cb_arg)
+    symbolizer.Join()
+
   def _RunTest(self, max_concurrent_jobs, num_symbols):
     symbolizer = elf_symbolizer.ELFSymbolizer(
         elf_file_path='/path/doesnt/matter/mock_lib1.so',
diff --git a/chrome/VERSION b/chrome/VERSION
index bc1886a..87be405d 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=68
 MINOR=0
-BUILD=3423
+BUILD=3424
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
index 902f969..c51bb0e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java
@@ -98,10 +98,6 @@
             return true;
         }
 
-        if (navigationParams.suggestedFilename != null) {
-            return false;
-        }
-
         TabRedirectHandler tabRedirectHandler = null;
         if (navigationParams.isMainFrame) {
             tabRedirectHandler = mTab.getTabRedirectHandler();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
index 7ea0d29..3b189acc 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
@@ -522,14 +522,11 @@
         mActivityTestRule.getActivity();
 
         int initialTabCount = mActivityTestRule.getActivity().getCurrentTabModel().getCount();
-        ChromeTabUtils.newTabFromMenu(
-                InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity());
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-        View button = mActivityTestRule.getActivity().findViewById(R.id.tab_switcher_button);
-        OverviewModeBehaviorWatcher overviewModeWatcher = new OverviewModeBehaviorWatcher(
-                mActivityTestRule.getActivity().getLayoutManager(), true, false);
-        TouchCommon.singleClickView(button);
-        overviewModeWatcher.waitForBehavior();
+        ChromeTabUtils.fullyLoadUrlInNewTab(InstrumentationRegistry.getInstrumentation(),
+                mActivityTestRule.getActivity(), ContentUrlConstants.ABOUT_BLANK_URL, false);
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> { mActivityTestRule.getActivity().getLayoutManager().showOverview(false); });
+
         Assert.assertTrue("Expected: " + (initialTabCount + 1) + " tab Got: "
                         + mActivityTestRule.getActivity().getCurrentTabModel().getCount(),
                 (initialTabCount + 1)
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 cd458bc..ed84a968 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
@@ -2207,7 +2207,7 @@
                 "intent://test/#Intent;scheme=test;package=com.chrome.test;end", "",
                 false /* isPost */, true /* hasUserGesture */, PageTransition.LINK,
                 false /* isRedirect */, true /* isExternalProtocol */, true /* isMainFrame */,
-                null /* suggestedFilename */, false /* hasUserGestureCarryover */);
+                false /* hasUserGestureCarryover */);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
@@ -2232,12 +2232,12 @@
         final NavigationParams initialNavigationParams = new NavigationParams("http://test.com", "",
                 false /* isPost */, true /* hasUserGesture */, PageTransition.LINK,
                 false /* isRedirect */, false /* isExternalProtocol */, true /* isMainFrame */,
-                null /* suggestedFilename */, false /* hasUserGestureCarryover */);
+                false /* hasUserGestureCarryover */);
         final NavigationParams redirectedNavigationParams = new NavigationParams(
                 "intent://test/#Intent;scheme=test;package=com.chrome.test;end", "",
                 false /* isPost */, false /* hasUserGesture */, PageTransition.LINK,
                 true /* isRedirect */, true /* isExternalProtocol */, true /* isMainFrame */,
-                null /* suggestedFilename */, false /* hasUserGestureCarryover */);
+                false /* hasUserGestureCarryover */);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
@@ -2266,7 +2266,7 @@
                 "intent://test/#Intent;scheme=test;package=com.chrome.test;end", "",
                 false /* isPost */, false /* hasUserGesture */, PageTransition.LINK,
                 false /* isRedirect */, true /* isExternalProtocol */, true /* isMainFrame */,
-                null /* suggestedFilename */, false /* hasUserGestureCarryover */);
+                false /* hasUserGestureCarryover */);
         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 3ecb095..300d8d0 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-68.0.3422.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-68.0.3423.0_rc-r2.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index e282106..687b805 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -628,6 +628,9 @@
   <message name="IDS_FILE_BROWSER_SHARE_BUTTON_LABEL" desc="Menu item's label, showing dialog to share the selected Google Drive files. This message is also used as a tooltip label and a spoken feedback label of a button which also shows the dialog to share the files. The translation should be consistent with the sharing dialog's title in Google Drive Web UI.">
     Share with others
   </message>
+  <message name="IDS_FILE_BROWSER_MANAGE_IN_DRIVE_BUTTON_LABEL" desc="Menu item's label, showing dialog to open the selected Google Drive files in the Drive webpage for managing sharing permissions, etc.">
+    Manage in Drive
+  </message>
   <message name="IDS_FILE_BROWSER_TOGGLE_HIDDEN_FILES_COMMAND_LABEL" desc="Label for menu or button with checkmark that toggles visibility of hidden files.">
     Show hidden files
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 74a2e57..32378e0 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -18,7 +18,6 @@
 import("//components/spellcheck/spellcheck_build_features.gni")
 import("//device/vr/buildflags/buildflags.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//media/media_options.gni")
 import("//net/features.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//printing/buildflags/buildflags.gni")
@@ -453,6 +452,30 @@
     "engagement/top_sites/site_engagement_top_sites_provider.h",
     "experiments/memory_ablation_experiment.cc",
     "experiments/memory_ablation_experiment.h",
+    "media/webrtc/audio_debug_recordings_handler.cc",
+    "media/webrtc/audio_debug_recordings_handler.h",
+    "media/webrtc/webrtc_event_log_manager.cc",
+    "media/webrtc/webrtc_event_log_manager.h",
+    "media/webrtc/webrtc_event_log_manager_common.cc",
+    "media/webrtc/webrtc_event_log_manager_common.h",
+    "media/webrtc/webrtc_event_log_manager_local.cc",
+    "media/webrtc/webrtc_event_log_manager_local.h",
+    "media/webrtc/webrtc_event_log_manager_remote.cc",
+    "media/webrtc/webrtc_event_log_manager_remote.h",
+    "media/webrtc/webrtc_event_log_uploader.cc",
+    "media/webrtc/webrtc_event_log_uploader.h",
+    "media/webrtc/webrtc_log_uploader.cc",
+    "media/webrtc/webrtc_log_uploader.h",
+    "media/webrtc/webrtc_log_util.cc",
+    "media/webrtc/webrtc_log_util.h",
+    "media/webrtc/webrtc_logging_handler_host.cc",
+    "media/webrtc/webrtc_logging_handler_host.h",
+    "media/webrtc/webrtc_rtp_dump_handler.cc",
+    "media/webrtc/webrtc_rtp_dump_handler.h",
+    "media/webrtc/webrtc_rtp_dump_writer.cc",
+    "media/webrtc/webrtc_rtp_dump_writer.h",
+    "media/webrtc/webrtc_text_log_handler.cc",
+    "media/webrtc/webrtc_text_log_handler.h",
 
     # Oh hey, all the cool browser/extensions files are hanging out in
     # //chrome/browser/extensions/BUILD.gn
@@ -1735,8 +1758,11 @@
     "//components/web_resource",
     "//components/webdata/common",
     "//components/webdata_services",
+    "//components/webrtc_logging/browser",
+    "//components/webrtc_logging/common",
     "//content/app/resources",
     "//content/public/browser",
+    "//content/public/browser",
     "//content/public/common",
     "//content/public/common:buildflags",
     "//content/public/common:feature_h264_with_openh264_ffmpeg",
@@ -1769,6 +1795,7 @@
     "//ppapi/buildflags",
     "//printing/buildflags",
     "//rlz/buildflags",
+    "//services/audio/public/cpp",
     "//services/data_decoder/public/cpp",
     "//services/device/public/cpp:device_features",
     "//services/device/public/mojom",
@@ -1805,6 +1832,8 @@
     "//third_party/metrics_proto",
     "//third_party/re2",
     "//third_party/smhasher:cityhash",
+    "//third_party/webrtc_overrides",
+    "//third_party/webrtc_overrides:init_webrtc",
     "//third_party/widevine/cdm:headers",
     "//third_party/zlib",
     "//third_party/zlib:minizip",
@@ -4112,43 +4141,6 @@
     ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "media/webrtc/audio_debug_recordings_handler.cc",
-      "media/webrtc/audio_debug_recordings_handler.h",
-      "media/webrtc/webrtc_event_log_manager.cc",
-      "media/webrtc/webrtc_event_log_manager.h",
-      "media/webrtc/webrtc_event_log_manager_common.cc",
-      "media/webrtc/webrtc_event_log_manager_common.h",
-      "media/webrtc/webrtc_event_log_manager_local.cc",
-      "media/webrtc/webrtc_event_log_manager_local.h",
-      "media/webrtc/webrtc_event_log_manager_remote.cc",
-      "media/webrtc/webrtc_event_log_manager_remote.h",
-      "media/webrtc/webrtc_event_log_uploader.cc",
-      "media/webrtc/webrtc_event_log_uploader.h",
-      "media/webrtc/webrtc_log_uploader.cc",
-      "media/webrtc/webrtc_log_uploader.h",
-      "media/webrtc/webrtc_log_util.cc",
-      "media/webrtc/webrtc_log_util.h",
-      "media/webrtc/webrtc_logging_handler_host.cc",
-      "media/webrtc/webrtc_logging_handler_host.h",
-      "media/webrtc/webrtc_rtp_dump_handler.cc",
-      "media/webrtc/webrtc_rtp_dump_handler.h",
-      "media/webrtc/webrtc_rtp_dump_writer.cc",
-      "media/webrtc/webrtc_rtp_dump_writer.h",
-      "media/webrtc/webrtc_text_log_handler.cc",
-      "media/webrtc/webrtc_text_log_handler.h",
-    ]
-    deps += [
-      "//components/webrtc_logging/browser",
-      "//components/webrtc_logging/common",
-      "//content/public/browser",
-      "//services/audio/public/cpp",
-      "//third_party/webrtc_overrides",
-      "//third_party/webrtc_overrides:init_webrtc",
-    ]
-  }
-
   if (!is_chrome_branded && !is_android) {
     sources += [
       "search/local_files_ntp_source.cc",
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index 9555938..735a3db 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -474,7 +474,8 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& unused_obj) {
   return GetCreditCardGUIDs(env,
-                            personal_data_manager_->GetCreditCardsToSuggest());
+                            personal_data_manager_->GetCreditCardsToSuggest(
+                                /*include_server_cards=*/true));
 }
 
 ScopedJavaLocalRef<jobject> PersonalDataManagerAndroid::GetCreditCardByGUID(
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index af85ebe..62a28a6c 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -240,8 +240,8 @@
     "accessibility/magnification_manager.h",
     "accessibility/select_to_speak_event_handler.cc",
     "accessibility/select_to_speak_event_handler.h",
-    "accessibility/spoken_feedback_event_rewriter.cc",
-    "accessibility/spoken_feedback_event_rewriter.h",
+    "accessibility/spoken_feedback_event_rewriter_delegate.cc",
+    "accessibility/spoken_feedback_event_rewriter_delegate.h",
     "accessibility/switch_access_event_handler.cc",
     "accessibility/switch_access_event_handler.h",
     "app_mode/app_launch_utils.cc",
@@ -1834,7 +1834,6 @@
     "../policy/default_geolocation_policy_handler_unittest.cc",
     "../ui/browser_finder_chromeos_unittest.cc",
     "accessibility/select_to_speak_event_handler_unittest.cc",
-    "accessibility/spoken_feedback_event_rewriter_unittest.cc",
     "app_mode/startup_app_launcher_unittest.cc",
     "apps/intent_helper/apps_navigation_throttle_unittest.cc",
     "arc/accessibility/arc_accessibility_helper_bridge_unittest.cc",
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.cc
deleted file mode 100644
index 6ac01750..0000000
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.h"
-
-#include <string>
-#include <utility>
-
-#include "ash/shell.h"
-#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
-#include "chrome/browser/chromeos/accessibility/event_handler_common.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "content/public/browser/web_contents.h"
-#include "extensions/browser/event_router.h"
-#include "extensions/browser/extension_host.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/content_accelerators/accelerator_util.h"
-#include "ui/events/event.h"
-#include "ui/events/event_sink.h"
-
-SpokenFeedbackEventRewriterDelegate::SpokenFeedbackEventRewriterDelegate() {}
-
-bool SpokenFeedbackEventRewriterDelegate::IsSpokenFeedbackEnabled() const {
-  return chromeos::AccessibilityManager::Get()->IsSpokenFeedbackEnabled();
-}
-
-bool SpokenFeedbackEventRewriterDelegate::DispatchKeyEventToChromeVox(
-    const ui::KeyEvent& key_event,
-    bool capture) {
-  // Always capture the Search key.
-  capture |= key_event.IsCommandDown();
-
-  // Don't capture tab as it gets consumed by Blink so never comes back
-  // unhandled. In third_party/WebKit/Source/core/input/EventHandler.cpp, a
-  // default tab handler consumes tab even when no focusable nodes are found; it
-  // sets focus to Chrome and eats the event.
-  if (key_event.GetDomKey() == ui::DomKey::TAB)
-    capture = false;
-
-  extensions::ExtensionHost* host = chromeos::GetAccessibilityExtensionHost(
-      extension_misc::kChromeVoxExtensionId);
-  if (!host)
-    return false;
-
-  // Listen for any unhandled keyboard events from ChromeVox's background page
-  // when capturing keys to reinject.
-  if (capture)
-    host->host_contents()->SetDelegate(this);
-  else
-    host->host_contents()->SetDelegate(nullptr);
-
-  // Forward all key events to ChromeVox's background page.
-  chromeos::ForwardKeyToExtension(key_event, host);
-
-  return capture;
-}
-
-void SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent(
-    content::WebContents* source,
-    const content::NativeWebKeyboardEvent& event) {
-  ui::KeyEvent key_event(*static_cast<ui::KeyEvent*>(event.os_event));
-
-  ui::EventSink* sink =
-      ash::Shell::GetPrimaryRootWindow()->GetHost()->event_sink();
-
-  if (sink->OnEventFromSource(&key_event).dispatcher_destroyed) {
-    VLOG(0) << "Undispatched key " << key_event.key_code()
-            << " due to destroyed dispatcher.";
-  }
-}
-
-SpokenFeedbackEventRewriter::SpokenFeedbackEventRewriter() {
-  delegate_.reset(new SpokenFeedbackEventRewriterDelegate());
-}
-
-SpokenFeedbackEventRewriter::~SpokenFeedbackEventRewriter() {}
-
-void SpokenFeedbackEventRewriter::SetDelegateForTest(
-    std::unique_ptr<SpokenFeedbackEventRewriterDelegate> delegate) {
-  delegate_ = std::move(delegate);
-}
-
-ui::EventRewriteStatus SpokenFeedbackEventRewriter::RewriteEvent(
-    const ui::Event& event,
-    std::unique_ptr<ui::Event>* new_event) {
-  if (!delegate_->IsSpokenFeedbackEnabled())
-    return ui::EVENT_REWRITE_CONTINUE;
-
-  if ((event.type() != ui::ET_KEY_PRESSED &&
-       event.type() != ui::ET_KEY_RELEASED))
-    return ui::EVENT_REWRITE_CONTINUE;
-
-  std::string extension_id =
-      chromeos::AccessibilityManager::Get()->keyboard_listener_extension_id();
-  if (extension_id.empty())
-    return ui::EVENT_REWRITE_CONTINUE;
-
-  bool capture =
-      chromeos::AccessibilityManager::Get()->keyboard_listener_capture();
-  const ui::KeyEvent key_event = static_cast<const ui::KeyEvent&>(event);
-  if (delegate_->DispatchKeyEventToChromeVox(key_event, capture))
-    return ui::EVENT_REWRITE_DISCARD;
-  return ui::EVENT_REWRITE_CONTINUE;
-}
-
-ui::EventRewriteStatus SpokenFeedbackEventRewriter::NextDispatchEvent(
-    const ui::Event& last_event,
-    std::unique_ptr<ui::Event>* new_event) {
-  return ui::EVENT_REWRITE_CONTINUE;
-}
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.h b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.h
deleted file mode 100644
index 1b2a4e0..0000000
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPOKEN_FEEDBACK_EVENT_REWRITER_H_
-#define CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPOKEN_FEEDBACK_EVENT_REWRITER_H_
-
-#include <vector>
-
-#include "base/macros.h"
-#include "content/public/browser/web_contents_delegate.h"
-#include "ui/events/event_rewriter.h"
-
-namespace ui {
-class KeyEvent;
-}
-
-// Receives requests for spoken feedback enabled state and command dispatch.
-class SpokenFeedbackEventRewriterDelegate
-    : public content::WebContentsDelegate {
- public:
-  SpokenFeedbackEventRewriterDelegate();
-  ~SpokenFeedbackEventRewriterDelegate() override {}
-
-  // Returns true when ChromeVox is enabled.
-  virtual bool IsSpokenFeedbackEnabled() const;
-
-  // Returns true when |key_event| is dispatched to ChromeVox.
-  virtual bool DispatchKeyEventToChromeVox(const ui::KeyEvent& key_event,
-                                           bool capture);
-
-  // WebContentsDelegate:
-  void HandleKeyboardEvent(
-      content::WebContents* source,
-      const content::NativeWebKeyboardEvent& event) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SpokenFeedbackEventRewriterDelegate);
-};
-
-// SpokenFeedbackEventRewriter discards all keyboard events mapped by the spoken
-// feedback manifest commands block. It dispatches the associated command name
-// directly to spoken feedback. This only occurs whenever spoken feedback is
-// enabled.
-class SpokenFeedbackEventRewriter : public ui::EventRewriter {
- public:
-  SpokenFeedbackEventRewriter();
-  ~SpokenFeedbackEventRewriter() override;
-
-  void SetDelegateForTest(
-      std::unique_ptr<SpokenFeedbackEventRewriterDelegate> delegate);
-
- private:
-  // EventRewriter:
-  ui::EventRewriteStatus RewriteEvent(
-      const ui::Event& event,
-      std::unique_ptr<ui::Event>* new_event) override;
-  ui::EventRewriteStatus NextDispatchEvent(
-      const ui::Event& last_event,
-      std::unique_ptr<ui::Event>* new_event) override;
-
-  // Active delegate (used for testing).
-  std::unique_ptr<SpokenFeedbackEventRewriterDelegate> delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(SpokenFeedbackEventRewriter);
-};
-
-#endif  // CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPOKEN_FEEDBACK_EVENT_REWRITER_H_
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.cc
new file mode 100644
index 0000000..d1d46b6
--- /dev/null
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.cc
@@ -0,0 +1,98 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h"
+
+#include "ash/public/interfaces/constants.mojom.h"
+#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
+#include "chrome/browser/chromeos/accessibility/event_handler_common.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/service_manager_connection.h"
+#include "extensions/browser/extension_host.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/events/event.h"
+
+SpokenFeedbackEventRewriterDelegate::SpokenFeedbackEventRewriterDelegate()
+    : binding_(this) {
+  content::ServiceManagerConnection* connection =
+      content::ServiceManagerConnection::GetForProcess();
+  connection->GetConnector()->BindInterface(ash::mojom::kServiceName,
+                                            &event_rewriter_controller_ptr_);
+  // Set this object as the SpokenFeedbackEventRewriterDelegate.
+  ash::mojom::SpokenFeedbackEventRewriterDelegatePtr ptr;
+  binding_.Bind(mojo::MakeRequest(&ptr));
+  event_rewriter_controller_ptr_->SetSpokenFeedbackEventRewriterDelegate(
+      std::move(ptr));
+}
+
+SpokenFeedbackEventRewriterDelegate::~SpokenFeedbackEventRewriterDelegate() {}
+
+void SpokenFeedbackEventRewriterDelegate::DispatchKeyEventToChromeVox(
+    std::unique_ptr<ui::Event> event) {
+  if (!ShouldDispatchKeyEventToChromeVox(event.get())) {
+    OnUnhandledSpokenFeedbackEvent(std::move(event));
+    return;
+  }
+
+  bool capture =
+      chromeos::AccessibilityManager::Get()->keyboard_listener_capture();
+  const ui::KeyEvent* key_event = event->AsKeyEvent();
+
+  // Always capture the Search key.
+  capture |= key_event->IsCommandDown();
+
+  // Don't capture tab as it gets consumed by Blink so never comes back
+  // unhandled. In third_party/WebKit/Source/core/input/EventHandler.cpp, a
+  // default tab handler consumes tab even when no focusable nodes are found; it
+  // sets focus to Chrome and eats the event.
+  if (key_event->GetDomKey() == ui::DomKey::TAB)
+    capture = false;
+
+  extensions::ExtensionHost* host = chromeos::GetAccessibilityExtensionHost(
+      extension_misc::kChromeVoxExtensionId);
+  // Listen for any unhandled keyboard events from ChromeVox's background page
+  // when capturing keys to reinject.
+  host->host_contents()->SetDelegate(capture ? this : nullptr);
+
+  // Forward the event to ChromeVox's background page.
+  chromeos::ForwardKeyToExtension(*key_event, host);
+
+  if (!capture)
+    OnUnhandledSpokenFeedbackEvent(std::move(event));
+}
+
+bool SpokenFeedbackEventRewriterDelegate::ShouldDispatchKeyEventToChromeVox(
+    const ui::Event* event) const {
+  chromeos::AccessibilityManager* accessibility_manager =
+      chromeos::AccessibilityManager::Get();
+  if (!accessibility_manager->IsSpokenFeedbackEnabled() ||
+      accessibility_manager->keyboard_listener_extension_id().empty() ||
+      !chromeos::GetAccessibilityExtensionHost(
+          extension_misc::kChromeVoxExtensionId)) {
+    VLOG(1) << "Event sent to Spoken Feedback when disabled or unavailable";
+    return false;
+  }
+
+  if (!event || !event->IsKeyEvent()) {
+    NOTREACHED() << "Unexpected event sent to Spoken Feedback";
+    return false;
+  }
+
+  return true;
+}
+
+void SpokenFeedbackEventRewriterDelegate::OnUnhandledSpokenFeedbackEvent(
+    std::unique_ptr<ui::Event> event) const {
+  event_rewriter_controller_ptr_->OnUnhandledSpokenFeedbackEvent(
+      std::move(event));
+}
+
+void SpokenFeedbackEventRewriterDelegate::HandleKeyboardEvent(
+    content::WebContents* source,
+    const content::NativeWebKeyboardEvent& event) {
+  OnUnhandledSpokenFeedbackEvent(
+      ui::Event::Clone(*static_cast<ui::Event*>(event.os_event)));
+}
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h
new file mode 100644
index 0000000..cb3246f
--- /dev/null
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPOKEN_FEEDBACK_EVENT_REWRITER_DELEGATE_H_
+#define CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPOKEN_FEEDBACK_EVENT_REWRITER_DELEGATE_H_
+
+#include <memory>
+
+#include "ash/public/interfaces/event_rewriter_controller.mojom.h"
+#include "base/macros.h"
+#include "content/public/browser/web_contents_delegate.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+// Passes key events from Ash's EventRewriter to the ChromeVox extension code.
+// Reports ChromeVox's unhandled key events back to Ash for continued dispatch.
+// TODO(http://crbug.com/839541): Avoid reposting unhandled events.
+class SpokenFeedbackEventRewriterDelegate
+    : public ash::mojom::SpokenFeedbackEventRewriterDelegate,
+      public content::WebContentsDelegate {
+ public:
+  SpokenFeedbackEventRewriterDelegate();
+  ~SpokenFeedbackEventRewriterDelegate() override;
+
+  // ui::mojom::SpokenFeedbackEventRewriterDelegate:
+  void DispatchKeyEventToChromeVox(std::unique_ptr<ui::Event> event) override;
+
+ private:
+  // Returns whether the event should be dispatched to the ChromeVox extension.
+  bool ShouldDispatchKeyEventToChromeVox(const ui::Event* event) const;
+
+  // Reports unhandled key events to the EventRewriterController for dispatch.
+  void OnUnhandledSpokenFeedbackEvent(std::unique_ptr<ui::Event> event) const;
+
+  // WebContentsDelegate:
+  void HandleKeyboardEvent(
+      content::WebContents* source,
+      const content::NativeWebKeyboardEvent& event) override;
+
+  ash::mojom::EventRewriterControllerPtr event_rewriter_controller_ptr_;
+
+  mojo::Binding<ash::mojom::SpokenFeedbackEventRewriterDelegate> binding_;
+
+  DISALLOW_COPY_AND_ASSIGN(SpokenFeedbackEventRewriterDelegate);
+};
+
+#endif  // CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_SPOKEN_FEEDBACK_EVENT_REWRITER_DELEGATE_H_
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_unittest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_unittest.cc
deleted file mode 100644
index dab4e61..0000000
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_unittest.cc
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.h"
-
-#include <memory>
-#include <vector>
-
-#include "ash/shell.h"
-#include "ash/test/ash_test_base.h"
-#include "base/macros.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_tree_host.h"
-#include "ui/events/event.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/events/test/event_generator.h"
-
-// Records all key events.
-class EventCapturer : public ui::EventHandler {
- public:
-  EventCapturer() {}
-  ~EventCapturer() override {}
-
-  void Reset() { events_.clear(); }
-
-  void OnEvent(ui::Event* event) override {
-    if (event->IsKeyEvent())
-      events_.push_back(std::make_unique<ui::KeyEvent>(*event->AsKeyEvent()));
-  }
-
-  const std::vector<std::unique_ptr<ui::KeyEvent>>& captured_events() const {
-    return events_;
-  }
-
- private:
-  std::vector<std::unique_ptr<ui::KeyEvent>> events_;
-
-  DISALLOW_COPY_AND_ASSIGN(EventCapturer);
-};
-
-class TestDelegate : public SpokenFeedbackEventRewriterDelegate {
- public:
-  TestDelegate()
-      : is_spoken_feedback_enabled_(false), dispatch_result_(false) {}
-
-  ~TestDelegate() override {}
-
-  void set_is_spoken_feedback_enabled(bool enabled) {
-    is_spoken_feedback_enabled_ = enabled;
-  }
-
-  void set_dispatch_result(bool result) { dispatch_result_ = result; }
-
- private:
-  // SpokenFeedbackEventRewriterDelegate:
-  bool IsSpokenFeedbackEnabled() const override {
-    return is_spoken_feedback_enabled_;
-  }
-
-  bool DispatchKeyEventToChromeVox(const ui::KeyEvent& key_event,
-                                   bool capture) override {
-    return dispatch_result_;
-  }
-
-  bool is_spoken_feedback_enabled_;
-  bool dispatch_result_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
-};
-
-class SpokenFeedbackEventRewriterTest : public ash::AshTestBase {
- public:
-  SpokenFeedbackEventRewriterTest()
-      : generator_(nullptr),
-        spoken_feedback_event_rewriter_(new SpokenFeedbackEventRewriter()) {
-    delegate_ = new TestDelegate();
-    spoken_feedback_event_rewriter_->SetDelegateForTest(
-        std::unique_ptr<TestDelegate>(delegate_));
-  }
-
-  void SetUp() override {
-    ash::AshTestBase::SetUp();
-    generator_ = &AshTestBase::GetEventGenerator();
-    CurrentContext()->AddPreTargetHandler(&event_capturer_);
-    CurrentContext()->GetHost()->GetEventSource()->AddEventRewriter(
-        spoken_feedback_event_rewriter_.get());
-  }
-
-  void TearDown() override {
-    CurrentContext()->GetHost()->GetEventSource()->RemoveEventRewriter(
-        spoken_feedback_event_rewriter_.get());
-    CurrentContext()->RemovePreTargetHandler(&event_capturer_);
-    generator_ = nullptr;
-    ash::AshTestBase::TearDown();
-  }
-
- protected:
-  TestDelegate* delegate_;
-  ui::test::EventGenerator* generator_;
-  EventCapturer event_capturer_;
-
- private:
-  std::unique_ptr<SpokenFeedbackEventRewriter> spoken_feedback_event_rewriter_;
-
-  DISALLOW_COPY_AND_ASSIGN(SpokenFeedbackEventRewriterTest);
-};
-
-TEST_F(SpokenFeedbackEventRewriterTest, KeysNotEatenWithChromeVoxDisabled) {
-  // Send Search+Shift+Right.
-  generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
-  ASSERT_EQ(1U, event_capturer_.captured_events().size());
-  generator_->PressKey(ui::VKEY_SHIFT, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
-  ASSERT_EQ(2U, event_capturer_.captured_events().size());
-
-  // Mock successful commands lookup and dispatch; shouldn't matter either way.
-  delegate_->set_dispatch_result(true);
-  generator_->PressKey(ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
-  ASSERT_EQ(3U, event_capturer_.captured_events().size());
-
-  // Released keys shouldn't get eaten.
-  delegate_->set_dispatch_result(false);
-  generator_->ReleaseKey(ui::VKEY_RIGHT,
-                         ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
-  generator_->ReleaseKey(ui::VKEY_SHIFT, ui::EF_COMMAND_DOWN);
-  generator_->ReleaseKey(ui::VKEY_LWIN, 0);
-  ASSERT_EQ(6U, event_capturer_.captured_events().size());
-
-  // Try releasing more keys.
-  generator_->ReleaseKey(ui::VKEY_RIGHT, 0);
-  generator_->ReleaseKey(ui::VKEY_SHIFT, 0);
-  generator_->ReleaseKey(ui::VKEY_LWIN, 0);
-  ASSERT_EQ(9U, event_capturer_.captured_events().size());
-}
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index f62027c..3bacb389 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -37,7 +37,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
-#include "chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter.h"
+#include "chrome/browser/chromeos/accessibility/spoken_feedback_event_rewriter_delegate.h"
 #include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_launch_error.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
@@ -1028,12 +1028,14 @@
     event_rewriter_controller_ptr->SetKeyboardDrivenEventRewriterEnabled(true);
   }
 
+  // Construct a delegate to connect ChromeVox and SpokenFeedbackEventRewriter.
+  spoken_feedback_event_rewriter_delegate_ =
+      std::make_unique<SpokenFeedbackEventRewriterDelegate>();
+
   if (chromeos::GetAshConfig() != ash::Config::MASH) {
     // TODO(mash): Support EventRewriterController; see crbug.com/647781
     ash::EventRewriterController* event_rewriter_controller =
         ash::Shell::Get()->event_rewriter_controller();
-    event_rewriter_controller->AddEventRewriter(
-        std::unique_ptr<ui::EventRewriter>(new SpokenFeedbackEventRewriter()));
     event_rewriter_delegate_ = std::make_unique<EventRewriterDelegateImpl>();
     event_rewriter_controller->AddEventRewriter(
         std::make_unique<ui::EventRewriterChromeOS>(
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index 4bbb667..6860dec 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -16,6 +16,7 @@
 #include "chromeos/system/version_loader.h"
 
 class NotificationPlatformBridge;
+class SpokenFeedbackEventRewriterDelegate;
 
 namespace lock_screen_apps {
 class StateController;
@@ -107,6 +108,10 @@
 
   std::unique_ptr<EventRewriterDelegateImpl> event_rewriter_delegate_;
 
+  // Handles event dispatch to the spoken feedback extension (ChromeVox).
+  std::unique_ptr<SpokenFeedbackEventRewriterDelegate>
+      spoken_feedback_event_rewriter_delegate_;
+
   scoped_refptr<chromeos::ExternalMetrics> external_metrics_;
 
   std::unique_ptr<arc::ArcServiceLauncher> arc_service_launcher_;
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index a7b2aed4..1ca762b 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -156,6 +156,8 @@
   properties->available_when_metered.reset(
       new bool(file_specific_info.cache_state().is_present() ||
                file_specific_info.is_hosted_document()));
+  properties->alternate_url.reset(
+      new std::string(file_specific_info.alternate_url()));
 }
 
 // Creates entry definition list for (metadata) search result info list.
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
index 8a424bd2..018ea447 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc
@@ -699,6 +699,8 @@
   SET_STRING("TOGGLE_HIDDEN_FILES_COMMAND_LABEL",
              IDS_FILE_BROWSER_TOGGLE_HIDDEN_FILES_COMMAND_LABEL);
   SET_STRING("SHARE_BUTTON_LABEL", IDS_FILE_BROWSER_SHARE_BUTTON_LABEL);
+  SET_STRING("MANAGE_IN_DRIVE_BUTTON_LABEL",
+             IDS_FILE_BROWSER_MANAGE_IN_DRIVE_BUTTON_LABEL);
   SET_STRING("CHANGE_TO_LISTVIEW_BUTTON_LABEL",
              IDS_FILE_BROWSER_CHANGE_TO_LISTVIEW_BUTTON_LABEL);
   SET_STRING("CHANGE_TO_THUMBNAILVIEW_BUTTON_LABEL",
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index e4dc375..eb8694e 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -86,14 +86,8 @@
 #define WRAPPED_INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \
   INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator)
 
-// Fails on official build. http://crbug.com/429294
-#if defined(DISABLE_SLOW_FILESAPP_TESTS) || defined(OFFICIAL_BUILD)
-#define MAYBE_FileDisplay DISABLED_FileDisplay
-#else
-#define MAYBE_FileDisplay FileDisplay
-#endif
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    MAYBE_FileDisplay,
+    FileDisplay,
     FileManagerBrowserTest,
     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "fileDisplayDownloads"),
                       TestParameter(IN_GUEST_MODE, "fileDisplayDownloads"),
@@ -177,13 +171,8 @@
         TestParameter(NOT_IN_GUEST_MODE, "renameNewDirectoryDownloads"),
         TestParameter(NOT_IN_GUEST_MODE, "renameNewDirectoryDrive")));
 
-#if defined(DISABLE_SLOW_FILESAPP_TESTS)
-#define MAYBE_Delete DISABLED_Delete
-#else
-#define MAYBE_Delete Delete
-#endif
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    MAYBE_Delete,
+    Delete,
     FileManagerBrowserTest,
     ::testing::Values(
         TestParameter(NOT_IN_GUEST_MODE,
@@ -259,14 +248,8 @@
                       "createDirectoryFromDirectoryTreeWithoutChangingCurrentDi"
                       "rectory")));
 
-// Fails on official build. http://crbug.com/429294
-#if defined(DISABLE_SLOW_FILESAPP_TESTS) || defined(OFFICIAL_BUILD)
-#define MAYBE_DriveSpecific DISABLED_DriveSpecific
-#else
-#define MAYBE_DriveSpecific DriveSpecific
-#endif
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    MAYBE_DriveSpecific,
+    DriveSpecific,
     FileManagerBrowserTest,
     ::testing::Values(
         TestParameter(NOT_IN_GUEST_MODE, "openSidebarOffline"),
@@ -307,13 +290,8 @@
     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "shareFile"),
                       TestParameter(NOT_IN_GUEST_MODE, "shareDirectory")));
 
-#if defined(DISABLE_SLOW_FILESAPP_TESTS)
-#define MAYBE_RestoreGeometry DISABLED_RestoreGeometry
-#else
-#define MAYBE_RestoreGeometry RestoreGeometry
-#endif
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    MAYBE_RestoreGeometry,
+    RestoreGeometry,
     FileManagerBrowserTest,
     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE, "restoreGeometry"),
                       TestParameter(IN_GUEST_MODE, "restoreGeometry"),
@@ -345,13 +323,8 @@
         TestParameter(NOT_IN_GUEST_MODE, "executeDefaultTaskOnDownloads"),
         TestParameter(IN_GUEST_MODE, "executeDefaultTaskOnDownloads")));
 
-#if defined(DISABLE_SLOW_FILESAPP_TESTS)
-#define MAYBE_ExecuteDefaultTaskOnDrive DISABLED_ExecuteDefaultTaskOnDrive
-#else
-#define MAYBE_ExecuteDefaultTaskOnDrive ExecuteDefaultTaskOnDrive
-#endif
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    MAYBE_ExecuteDefaultTaskOnDrive,
+    ExecuteDefaultTaskOnDrive,
     FileManagerBrowserTest,
     ::testing::Values(TestParameter(NOT_IN_GUEST_MODE,
                                     "executeDefaultTaskOnDrive")));
@@ -369,13 +342,8 @@
         TestParameter(IN_GUEST_MODE, "defaultTaskDialogOnDownloads"),
         TestParameter(NOT_IN_GUEST_MODE, "defaultTaskDialogOnDrive")));
 
-#if defined(DISABLE_SLOW_FILESAPP_TESTS)
-#define MAYBE_GenericTask DISABLED_GenericTask
-#else
-#define MAYBE_GenericTask GenericTask
-#endif
 WRAPPED_INSTANTIATE_TEST_CASE_P(
-    MAYBE_GenericTask,
+    GenericTask,
     FileManagerBrowserTest,
     ::testing::Values(
         TestParameter(NOT_IN_GUEST_MODE, "genericTaskIsNotExecuted"),
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 311a1cf5..55d82bd 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -44,23 +44,56 @@
 namespace file_manager {
 namespace {
 
-enum EntryType {
-  FILE,
-  DIRECTORY,
+// During test, the test extensions can send a list of entries (directories
+// or files) to add to a target volume using an AddEntriesMessage command.
+
+enum TargetVolume { LOCAL_VOLUME, DRIVE_VOLUME, USB_VOLUME };
+
+struct AddEntriesMessage {
+  // The volume to add |entries| to.
+  TargetVolume volume;
+
+  // The |entries| to be added.
+  std::vector<std::unique_ptr<struct TestEntryInfo>> entries;
+
+  // Converts |value| to an AddEntriesMessage: true on success.
+  static bool ConvertJSONValue(const base::DictionaryValue& value,
+                               AddEntriesMessage* message) {
+    base::JSONValueConverter<AddEntriesMessage> converter;
+    return converter.Convert(value, message);
+  }
+
+  // Registers AddEntriesMessage member info to the |converter|.
+  static void RegisterJSONConverter(
+      base::JSONValueConverter<AddEntriesMessage>* converter) {
+    converter->RegisterCustomField("volume", &AddEntriesMessage::volume,
+                                   &MapStringToTargetVolume);
+    converter->RegisterRepeatedMessage<struct TestEntryInfo>(
+        "entries", &AddEntriesMessage::entries);
+  }
+
+  // Maps |value| to TargetVolume. Returns true on success.
+  static bool MapStringToTargetVolume(base::StringPiece value,
+                                      TargetVolume* volume) {
+    if (value == "drive")
+      *volume = DRIVE_VOLUME;
+    else if (value == "local")
+      *volume = LOCAL_VOLUME;
+    else if (value == "usb")
+      *volume = USB_VOLUME;
+    else
+      return false;
+    return true;
+  }
 };
 
-enum TargetVolume {
-  LOCAL_VOLUME,
-  DRIVE_VOLUME,
-  USB_VOLUME,
-};
+// The AddEntriesMessage contains a vector of TestEntryInfo: the elements of
+// the vector provide the file or directory entry details.
 
-enum SharedOption {
-  NONE,
-  SHARED,
-};
+enum EntryType { FILE, DIRECTORY };
 
-// Test data file or directory entry info.
+enum SharedOption { NONE, SHARED };
+
 struct TestEntryInfo {
   TestEntryInfo() : type(FILE), shared_option(NONE) {}
 
@@ -130,45 +163,6 @@
   }
 };
 
-// Message from JavaScript to add entries.
-struct AddEntriesMessage {
-  // Entries to be added.
-  std::vector<std::unique_ptr<TestEntryInfo>> entries;
-
-  // Target volume to add |entries| to.
-  TargetVolume volume;
-
-  // Converts |value| to an AddEntriesMessage: true on success.
-  static bool ConvertJSONValue(const base::DictionaryValue& value,
-                               AddEntriesMessage* message) {
-    base::JSONValueConverter<AddEntriesMessage> converter;
-    return converter.Convert(value, message);
-  }
-
-  // Registers AddEntriesMessage member info to the |converter|.
-  static void RegisterJSONConverter(
-      base::JSONValueConverter<AddEntriesMessage>* converter) {
-    converter->RegisterCustomField("volume", &AddEntriesMessage::volume,
-                                   &MapStringToTargetVolume);
-    converter->RegisterRepeatedMessage<TestEntryInfo>(
-        "entries", &AddEntriesMessage::entries);
-  }
-
-  // Maps |value| to TargetVolume. Returns true on success.
-  static bool MapStringToTargetVolume(base::StringPiece value,
-                                      TargetVolume* volume) {
-    if (value == "drive")
-      *volume = DRIVE_VOLUME;
-    else if (value == "local")
-      *volume = LOCAL_VOLUME;
-    else if (value == "usb")
-      *volume = USB_VOLUME;
-    else
-      return false;
-    return true;
-  }
-};
-
 // Listens for chrome.test messages: PASS, FAIL, and SendMessage.
 class FileManagerTestMessageListener : public content::NotificationObserver {
  public:
@@ -543,13 +537,13 @@
 
 void FileManagerBrowserTestBase::SetUpCommandLine(
     base::CommandLine* command_line) {
-  if (GetGuestMode() == IN_GUEST_MODE) {
+  if (IsGuestModeTest()) {
     command_line->AppendSwitch(chromeos::switches::kGuestSession);
     command_line->AppendSwitchNative(chromeos::switches::kLoginUser, "");
     command_line->AppendSwitch(switches::kIncognito);
   }
 
-  if (GetGuestMode() == IN_INCOGNITO) {
+  if (IsIncognitoModeTest()) {
     command_line->AppendSwitch(switches::kIncognito);
   }
 
@@ -561,7 +555,7 @@
 
   local_volume_.reset(new DownloadsTestVolume);
 
-  if (GetGuestMode() != IN_GUEST_MODE) {
+  if (!IsGuestModeTest()) {
     create_drive_integration_service_ =
         base::Bind(&FileManagerBrowserTestBase::CreateDriveIntegrationService,
                    base::Unretained(this));
@@ -577,12 +571,12 @@
 
   CHECK(local_volume_->Mount(profile()));
 
-  if (GetGuestMode() != IN_GUEST_MODE) {
+  if (!IsGuestModeTest()) {
     // Start the embedded test server to serve the mocked share dialog.
     CHECK(embedded_test_server()->Start());
     const GURL share_url_base(embedded_test_server()->GetURL(
         "/chromeos/file_manager/share_dialog_mock/index.html"));
-    drive_volume_ = drive_volumes_[profile()->GetOriginalProfile()];
+    drive_volume_ = std::move(drive_volumes_[profile()->GetOriginalProfile()]);
     drive_volume_->ConfigureShareUrlBase(share_url_base);
     test_util::WaitUntilDriveMountPointIsAdded(profile());
   }
@@ -671,29 +665,36 @@
                                            std::string* output) {
   base::ScopedAllowBlockingForTesting allow_blocking;
 
-  if (name == "getTestName") {
-    // Obtain the test case name.
-    *output = GetTestCaseName();
+  if (name == "isInGuestMode") {
+    // Obtain if the test runs in guest or incognito mode, or not.
+    if (IsGuestModeTest() || IsIncognitoModeTest()) {
+      LOG(INFO) << GetTestCaseName() << " isInGuestMode: true";
+      *output = "true";
+    } else {
+      ASSERT_EQ(NOT_IN_GUEST_MODE, GetGuestMode());
+      *output = "false";
+    }
+
     return;
   }
 
   if (name == "getRootPaths") {
     // Obtain the root paths.
-    const auto downloads = util::GetDownloadsMountPointName(profile());
+    const auto downloads_root = util::GetDownloadsMountPointName(profile());
     const auto drive = drive::util::GetDriveMountPointPath(profile());
 
     base::DictionaryValue dictionary;
     auto drive_root = drive.BaseName().AsUTF8Unsafe().append("/root");
     dictionary.SetString("drive", "/" + drive_root);
-    dictionary.SetString("downloads", "/" + downloads);
+    dictionary.SetString("downloads", "/" + downloads_root);
 
     base::JSONWriter::Write(dictionary, output);
     return;
   }
 
-  if (name == "isInGuestMode") {
-    // Obtain whether the test is in guest mode or not.
-    *output = GetGuestMode() != NOT_IN_GUEST_MODE ? "true" : "false";
+  if (name == "getTestName") {
+    // Obtain the test case name.
+    *output = GetTestCaseName();
     return;
   }
 
@@ -724,7 +725,7 @@
           local_volume_->CreateEntry(*message.entries[i]);
           break;
         case DRIVE_VOLUME:
-          if (drive_volume_.get())
+          if (drive_volume_)
             drive_volume_->CreateEntry(*message.entries[i]);
           break;
         case USB_VOLUME:
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
index 8cf21d4..3903668 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -6,9 +6,9 @@
 #define CHROME_BROWSER_CHROMEOS_FILE_MANAGER_FILE_MANAGER_BROWSERTEST_BASE_H_
 
 #include <map>
+#include <memory>
 #include <string>
 
-#include "base/memory/linked_ptr.h"
 #include "base/values.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/extensions/extension_apitest.h"
@@ -55,6 +55,12 @@
   virtual const char* GetTestExtensionManifestName() const = 0;
 
  private:
+  // Returns true if the test requires incognito mode.
+  bool IsIncognitoModeTest() const { return GetGuestMode() == IN_INCOGNITO; }
+
+  // Returns true if the test requires in guest mode.
+  bool IsGuestModeTest() const { return GetGuestMode() == IN_GUEST_MODE; }
+
   // Called during setup if needed, to create a drive integration service for
   // the given |profile|. Caller owns the return result.
   drive::DriveIntegrationService* CreateDriveIntegrationService(
@@ -77,8 +83,8 @@
                  std::string* output);
 
   std::unique_ptr<LocalTestVolume> local_volume_;
-  linked_ptr<DriveTestVolume> drive_volume_;
-  std::map<Profile*, linked_ptr<DriveTestVolume>> drive_volumes_;
+  std::unique_ptr<DriveTestVolume> drive_volume_;
+  std::map<Profile*, std::unique_ptr<DriveTestVolume>> drive_volumes_;
   std::unique_ptr<FakeTestVolume> usb_volume_;
   std::unique_ptr<FakeTestVolume> mtp_volume_;
 
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc
index 28d98a2..15279d7 100644
--- a/chrome/browser/download/download_ui_controller.cc
+++ b/chrome/browser/download/download_ui_controller.cc
@@ -143,8 +143,7 @@
   content::WebContents* web_contents =
       content::DownloadItemUtils::GetWebContents(item);
   if (web_contents && (item->IsSavePackageDownload() ||
-                       (!web_contents->GetURL().is_empty() &&
-                        web_contents->GetURL() != item->GetOriginalUrl() &&
+                       (web_contents->GetURL() != item->GetOriginalUrl() &&
                         web_contents->GetURL() != item->GetURL()))) {
     auto* security_state_tab_helper =
         SecurityStateTabHelper::FromWebContents(web_contents);
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index d8cfe551..750f58e 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -7,7 +7,6 @@
 import("//chrome/common/features.gni")
 import("//components/nacl/features.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//media/media_options.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//rlz/buildflags/buildflags.gni")
 
@@ -435,6 +434,7 @@
     "api/webrtc_audio_private/webrtc_audio_private_api.h",
     "api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.cc",
     "api/webrtc_desktop_capture_private/webrtc_desktop_capture_private_api.h",
+    "api/webrtc_logging_private/webrtc_logging_private_api.cc",
     "api/webrtc_logging_private/webrtc_logging_private_api.h",
     "api/webstore_private/webstore_private_api.cc",
     "api/webstore_private/webstore_private_api.h",
@@ -1139,13 +1139,6 @@
     deps += [ "//rlz:rlz_lib" ]
   }
 
-  if (enable_webrtc) {
-    sources += [ "api/webrtc_logging_private/webrtc_logging_private_api.cc" ]
-  } else {
-    sources +=
-        [ "api/webrtc_logging_private/webrtc_logging_private_api_stub.cc" ]
-  }
-
   if (toolkit_views) {
     deps += [ "//ui/views" ]
   }
diff --git a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
index 81874ce..7384bff 100644
--- a/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
+++ b/chrome/browser/extensions/api/streams_private/streams_private_apitest.cc
@@ -336,6 +336,9 @@
 // extension with a file browser handler that can handle the attachment's MIME
 // type.
 IN_PROC_BROWSER_TEST_F(StreamsPrivateApiTest, MAYBE_NavigateToAnAttachment) {
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;  // Streams not used with network service.
+
   InitializeDownloadSettings();
 
   ASSERT_TRUE(LoadTestExtension()) << message_;
@@ -381,6 +384,9 @@
 // StreamsResourceThrottle, even if there is an extension with a file
 // browser handler that can handle the download's MIME type.
 IN_PROC_BROWSER_TEST_F(StreamsPrivateApiTest, MAYBE_DirectDownload) {
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService))
+    return;  // Streams not used with network service.
+
   InitializeDownloadSettings();
 
   ASSERT_TRUE(LoadTestExtension()) << message_;
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index 44d1375..56f2645 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -40,6 +40,7 @@
 #include "content/public/common/resource_type.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/test/browser_test_utils.h"
+#include "content/public/test/download_test_observer.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/common/switches.h"
@@ -233,10 +234,17 @@
       << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, Download) {
+// https://crbug.com/660288
+IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, DISABLED_Download) {
   ASSERT_TRUE(StartEmbeddedTestServer());
-  ASSERT_TRUE(RunExtensionTest("webnavigation/download"))
-      << message_;
+  content::DownloadManager* download_manager =
+      content::BrowserContext::GetDownloadManager(browser()->profile());
+  content::DownloadTestObserverTerminal observer(
+      download_manager, 1,
+      content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
+  bool result = RunExtensionTest("webnavigation/download");
+  observer.WaitForFinished();
+  ASSERT_TRUE(result) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ServerRedirectSingleProcess) {
diff --git a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api_stub.cc b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api_stub.cc
deleted file mode 100644
index a47726a..0000000
--- a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api_stub.cc
+++ /dev/null
@@ -1,95 +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.
-//
-// Stub implementation used when WebRTC is not enabled.
-
-#include "chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h"
-
-namespace extensions {
-
-namespace {
-
-const char kErrorNotSupported[] = "Not supported";
-
-}  // namespace
-
-bool WebrtcLoggingPrivateSetMetaDataFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateStartFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateSetUploadOnRenderCloseFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateStopFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateStoreFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateUploadStoredFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateUploadFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateDiscardFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateStartRtpDumpFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateStopRtpDumpFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateStartAudioDebugRecordingsFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateStopAudioDebugRecordingsFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-bool WebrtcLoggingPrivateGetLogsDirectoryFunction::RunAsync() {
-  SetError(kErrorNotSupported);
-  SendResponse(false);
-  return false;
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc b/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
index fe55b30..142e791 100644
--- a/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
+++ b/chrome/browser/extensions/extension_gcm_app_handler_unittest.cc
@@ -212,6 +212,7 @@
         profile->GetPrefs(), profile->GetPath(), profile->GetRequestContext(),
         chrome::GetChannel(),
         gcm::GetProductCategoryForSubtypes(profile->GetPrefs()),
+        SigninManagerFactory::GetForProfile(profile),
         std::unique_ptr<ProfileIdentityProvider>(new ProfileIdentityProvider(
             SigninManagerFactory::GetForProfile(profile),
             ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
diff --git a/chrome/browser/extensions/extension_view_host.cc b/chrome/browser/extensions/extension_view_host.cc
index d4c7aa6..d4dc62958 100644
--- a/chrome/browser/extensions/extension_view_host.cc
+++ b/chrome/browser/extensions/extension_view_host.cc
@@ -161,21 +161,6 @@
       Browser* browser = view_->GetBrowser();
       return browser ? browser->OpenURL(params) : nullptr;
     }
-    case WindowOpenDisposition::CURRENT_TAB: {
-      // Only allow these from hosts that are bound to a browser (e.g. popups).
-      // Otherwise they are not driven by a user gesture.
-      Browser* browser = view_->GetBrowser();
-      if (!browser)
-        return nullptr;
-
-      // Only allow navigations that will surely result in a download.
-      if (!params.suggested_filename.has_value() ||
-          !(params.url.SchemeIsBlob() || params.url.SchemeIsFileSystem() ||
-            params.url.SchemeIs(url::kDataScheme))) {
-        return nullptr;
-      }
-      return browser->OpenURL(params);
-    }
     default:
       return nullptr;
   }
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.cc b/chrome/browser/gcm/gcm_profile_service_factory.cc
index ac7c59f..3834f7c 100644
--- a/chrome/browser/gcm/gcm_profile_service_factory.cc
+++ b/chrome/browser/gcm/gcm_profile_service_factory.cc
@@ -87,6 +87,7 @@
       profile->GetPrefs(), profile->GetPath(), profile->GetRequestContext(),
       chrome::GetChannel(),
       gcm::GetProductCategoryForSubtypes(profile->GetPrefs()),
+      SigninManagerFactory::GetForProfile(profile),
       std::unique_ptr<ProfileIdentityProvider>(new ProfileIdentityProvider(
           SigninManagerFactory::GetForProfile(profile),
           ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
diff --git a/chrome/browser/gcm/gcm_profile_service_unittest.cc b/chrome/browser/gcm/gcm_profile_service_unittest.cc
index fe93807..8a39cf87 100644
--- a/chrome/browser/gcm/gcm_profile_service_unittest.cc
+++ b/chrome/browser/gcm/gcm_profile_service_unittest.cc
@@ -54,6 +54,7 @@
       profile->GetPrefs(), profile->GetPath(), profile->GetRequestContext(),
       chrome::GetChannel(),
       gcm::GetProductCategoryForSubtypes(profile->GetPrefs()),
+      SigninManagerFactory::GetForProfile(profile),
       std::unique_ptr<ProfileIdentityProvider>(new ProfileIdentityProvider(
           SigninManagerFactory::GetForProfile(profile),
           ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
diff --git a/chrome/browser/profile_resetter/reset_report_uploader.cc b/chrome/browser/profile_resetter/reset_report_uploader.cc
index 4c7b445e..696cd4a6 100644
--- a/chrome/browser/profile_resetter/reset_report_uploader.cc
+++ b/chrome/browser/profile_resetter/reset_report_uploader.cc
@@ -13,8 +13,9 @@
 #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 "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/simple_url_loader.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
 
 namespace {
 const char kResetReportUrl[] =
@@ -31,10 +32,9 @@
 
 }  // namespace
 
-ResetReportUploader::ResetReportUploader(content::BrowserContext* context)
-    : url_request_context_getter_(
-          content::BrowserContext::GetDefaultStoragePartition(context)->
-              GetURLRequestContext()) {}
+ResetReportUploader::ResetReportUploader(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
+    : url_loader_factory_(std::move(url_loader_factory)) {}
 
 ResetReportUploader::~ResetReportUploader() {}
 
@@ -43,6 +43,11 @@
   std::string request_data;
   CHECK(report.SerializeToString(&request_data));
 
+  DispatchReportInternal(request_data);
+}
+
+void ResetReportUploader::DispatchReportInternal(
+    const std::string& request_data) {
   // Create traffic annotation tag.
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("profile_resetter_upload", R"(
@@ -72,19 +77,31 @@
             "send the data."
         })");
 
-  // Note fetcher will be deleted by OnURLFetchComplete.
-  net::URLFetcher* fetcher =
-      net::URLFetcher::Create(GetClientReportUrl(kResetReportUrl),
-                              net::URLFetcher::POST, this, traffic_annotation)
-          .release();
-  fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                        net::LOAD_DO_NOT_SAVE_COOKIES |
-                        net::LOAD_DISABLE_CACHE);
-  fetcher->SetRequestContext(url_request_context_getter_.get());
-  fetcher->SetUploadData("application/octet-stream", request_data);
-  fetcher->Start();
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = GetClientReportUrl(kResetReportUrl);
+  resource_request->load_flags = net::LOAD_DO_NOT_SEND_COOKIES |
+                                 net::LOAD_DO_NOT_SAVE_COOKIES |
+                                 net::LOAD_DISABLE_CACHE;
+  resource_request->method = "POST";
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
+      network::SimpleURLLoader::Create(std::move(resource_request),
+                                       traffic_annotation);
+  simple_url_loader->AttachStringForUpload(request_data,
+                                           "application/octet-stream");
+  auto it = simple_url_loaders_.insert(simple_url_loaders_.begin(),
+                                       std::move(simple_url_loader));
+  it->get()->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&ResetReportUploader::OnSimpleLoaderComplete,
+                     base::Unretained(this), std::move(it)));
 }
 
-void ResetReportUploader::OnURLFetchComplete(const net::URLFetcher* source) {
-  delete source;
+void ResetReportUploader::OnSimpleLoaderComplete(
+    SimpleURLLoaderList::iterator it,
+    std::unique_ptr<std::string> response_body) {
+  simple_url_loaders_.erase(it);
+}
+
+GURL ResetReportUploader::GetClientReportUrlForTesting() {
+  return GetClientReportUrl(kResetReportUrl);
 }
diff --git a/chrome/browser/profile_resetter/reset_report_uploader.h b/chrome/browser/profile_resetter/reset_report_uploader.h
index a710c101..7b05ef0 100644
--- a/chrome/browser/profile_resetter/reset_report_uploader.h
+++ b/chrome/browser/profile_resetter/reset_report_uploader.h
@@ -5,18 +5,16 @@
 #ifndef CHROME_BROWSER_PROFILE_RESETTER_RESET_REPORT_UPLOADER_H_
 #define CHROME_BROWSER_PROFILE_RESETTER_RESET_REPORT_UPLOADER_H_
 
+#include <list>
+
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "net/url_request/url_fetcher_delegate.h"
+#include "url/gurl.h"
 
-namespace content {
-class BrowserContext;
-}
-
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
 }
 
 namespace reset_report {
@@ -24,18 +22,27 @@
 }
 
 // Service whose job is up upload ChromeResetReports.
-class ResetReportUploader : public KeyedService,
-                            private net::URLFetcherDelegate {
+class ResetReportUploader : public KeyedService {
  public:
-  explicit ResetReportUploader(content::BrowserContext* context);
+  explicit ResetReportUploader(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~ResetReportUploader() override;
 
   void DispatchReport(const reset_report::ChromeResetReport& report);
 
- private:
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
+  // Visible for testing:
+  void DispatchReportInternal(const std::string& request_data);
+  static GURL GetClientReportUrlForTesting();
 
-  scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+ private:
+  using SimpleURLLoaderList =
+      std::list<std::unique_ptr<network::SimpleURLLoader>>;
+
+  void OnSimpleLoaderComplete(SimpleURLLoaderList::iterator it,
+                              std::unique_ptr<std::string> response_body);
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  SimpleURLLoaderList simple_url_loaders_;
 
   DISALLOW_COPY_AND_ASSIGN(ResetReportUploader);
 };
diff --git a/chrome/browser/profile_resetter/reset_report_uploader_factory.cc b/chrome/browser/profile_resetter/reset_report_uploader_factory.cc
index 7b567c4..d5572e08 100644
--- a/chrome/browser/profile_resetter/reset_report_uploader_factory.cc
+++ b/chrome/browser/profile_resetter/reset_report_uploader_factory.cc
@@ -7,6 +7,8 @@
 #include "base/memory/singleton.h"
 #include "chrome/browser/profile_resetter/reset_report_uploader.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
 
 // static
 ResetReportUploaderFactory* ResetReportUploaderFactory::GetInstance() {
@@ -29,5 +31,7 @@
 
 KeyedService* ResetReportUploaderFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return new ResetReportUploader(context);
+  return new ResetReportUploader(
+      content::BrowserContext::GetDefaultStoragePartition(context)
+          ->GetURLLoaderFactoryForBrowserProcess());
 }
diff --git a/chrome/browser/profile_resetter/reset_report_uploader_unittest.cc b/chrome/browser/profile_resetter/reset_report_uploader_unittest.cc
new file mode 100644
index 0000000..8e9d376
--- /dev/null
+++ b/chrome/browser/profile_resetter/reset_report_uploader_unittest.cc
@@ -0,0 +1,48 @@
+// 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/profile_resetter/reset_report_uploader.h"
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/common/weak_wrapper_shared_url_loader_factory.h"
+#include "content/public/test/test_utils.h"
+#include "net/http/http_status_code.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+class ResetReportUploaderTest : public testing::Test {
+ public:
+  ResetReportUploaderTest()
+      : test_shared_loader_factory_(
+            base::MakeRefCounted<content::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)) {}
+
+ protected:
+  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory() {
+    return test_shared_loader_factory_;
+  }
+  network::TestURLLoaderFactory* test_url_loader_factory() {
+    return &test_url_loader_factory_;
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
+};
+
+TEST_F(ResetReportUploaderTest, NoCrash) {
+  test_url_loader_factory()->AddResponse(
+      ResetReportUploader::GetClientReportUrlForTesting().spec(), "");
+  ResetReportUploader* uploader =
+      new ResetReportUploader(shared_url_loader_factory());
+  uploader->DispatchReportInternal("");
+}
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 1dd092b..1dbacd3 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -84,6 +84,12 @@
                  file="site_settings/all_sites.js"
                  type="chrome_html"
                  preprocess="true" />
+      <structure name="IDR_SETTINGS_SITE_ENTRY_HTML"
+                 file="site_settings/site_entry.html"
+                 type="chrome_html" />
+      <structure name="IDR_SETTINGS_SITE_ENTRY_JS"
+                 file="site_settings/site_entry.js"
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_CATEGORY_DEFAULT_SETTING_HTML"
                  file="site_settings/category_default_setting.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.html b/chrome/browser/resources/settings/site_settings/all_sites.html
index 49e8860b..d91cf76 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.html
+++ b/chrome/browser/resources/settings/site_settings/all_sites.html
@@ -1,10 +1,7 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
 <link rel="import" href="../settings_shared_css.html">
-<link rel="import" href="constants.html">
-<link rel="import" href="site_list.html">
-<link rel="import" href="site_settings_behavior.html">
+<link rel="import" href="site_entry.html">
 
 <dom-module id="all-sites">
   <template>
@@ -12,28 +9,19 @@
       :host {
         display: block;
       }
+
+      /* There is only one top-level heading for All Sites, so remove the
+       * additional leading padding used for lists. */
+      .list-frame.without-heading {
+        -webkit-padding-start: var(--settings-box-row-padding);
+      }
     </style>
     <div class="list-frame" hidden$="[[sites.length]]">
       <div class="list-item secondary">$i18n{noSitesAdded}</div>
     </div>
-    <div class="list-frame menu-content vertical-list" id="listContainer">
+    <div class="list-frame without-heading" id="listContainer">
       <template is="dom-repeat" items="[[sites]]">
-        <div class="list-item">
-          <div class="layout horizontal center flex" on-click="onOriginTap_"
-              actionable>
-            <div class="favicon-image"
-                style$="[[computeSiteIcon(item.origin)]]">
-            </div>
-            <div class="middle no-min-width text-elide"
-                id="displayName">
-              [[item.displayName]]
-            </div>
-            <paper-icon-button-light class="subpage-arrow">
-              <button aria-label$="[[item.displayName]]"
-                  aria-describedby="displayName"></button>
-            </paper-icon-button-light>
-          </div>
-        </div>
+        <site-entry site="{{item}}" label="[[item.displayName]]">
       </template>
     </div>
   </template>
diff --git a/chrome/browser/resources/settings/site_settings/all_sites.js b/chrome/browser/resources/settings/site_settings/all_sites.js
index 8edec70..81c9e55 100644
--- a/chrome/browser/resources/settings/site_settings/all_sites.js
+++ b/chrome/browser/resources/settings/site_settings/all_sites.js
@@ -62,17 +62,6 @@
     return Promise.all(promiseList);
   },
 
-  /**
-   * A handler for selecting a site (by clicking on the origin).
-   * @param {!{model: !{item: !SiteException}}} event
-   * @private
-   */
-  onOriginTap_: function(event) {
-    settings.navigateTo(
-        settings.routes.SITE_SETTINGS_SITE_DETAILS,
-        new URLSearchParams('site=' + event.model.item.origin));
-  },
-
   /** @private */
   populateList_: function() {
     this.getAllSitesList_().then(this.processExceptions_.bind(this));
diff --git a/chrome/browser/resources/settings/site_settings/site_entry.html b/chrome/browser/resources/settings/site_settings/site_entry.html
new file mode 100644
index 0000000..8e0e8d5
--- /dev/null
+++ b/chrome/browser/resources/settings/site_settings/site_entry.html
@@ -0,0 +1,24 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
+<link rel="import" href="../settings_shared_css.html">
+<link rel="import" href="site_settings_behavior.html">
+
+<dom-module id="site-entry">
+  <template>
+    <style include="settings-shared"></style>
+    <div class="settings-box list-item" on-click="onOriginTap_"
+        actionable>
+      <div class="favicon-image" style$="[[computeSiteIcon(site.origin)]]">
+      </div>
+      <div class="middle text-elide" id="displayName">
+        [[site.displayName]]
+      </div>
+      <paper-icon-button-light class="subpage-arrow">
+        <button aria-label$="[[site.displayName]]"
+            aria-describedby="displayName"></button>
+      </paper-icon-button-light>
+    </div>
+  </template>
+  <script src="site_entry.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/site_settings/site_entry.js b/chrome/browser/resources/settings/site_settings/site_entry.js
new file mode 100644
index 0000000..b424b21
--- /dev/null
+++ b/chrome/browser/resources/settings/site_settings/site_entry.js
@@ -0,0 +1,37 @@
+// 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.
+
+/**
+ * @fileoverview
+ * 'site-entry' is an element representing a single ETLD+1 site entity.
+ */
+
+Polymer({
+  is: 'site-entry',
+
+  behaviors: [SiteSettingsBehavior],
+
+  properties: {
+    /**
+     * TODO(https://crbug.com/835712): This should be an object representing a
+     * ETLD+1 site.
+     */
+    site: Object,
+  },
+
+  /** @override */
+  ready: function() {},
+
+  /**
+   * A handler for selecting a site (by clicking on the origin).
+   * @param {!{model: !{item: !SiteException}}} event
+   * @private
+   */
+  onOriginTap_: function(event) {
+    settings.navigateTo(
+        settings.routes.SITE_SETTINGS_SITE_DETAILS,
+        new URLSearchParams('site=' + this.site.origin));
+  },
+
+});
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service.cc b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
index bfe688c..ff084e0b 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service.cc
@@ -32,6 +32,8 @@
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/storage_partition.h"
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -328,7 +330,8 @@
       SigninManagerFactory::GetForProfile(profile_)
           ->GetAuthenticatedAccountId(),
       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_),
-      profile_->GetRequestContext()));
+      content::BrowserContext::GetDefaultStoragePartition(profile_)
+          ->GetURLLoaderFactoryForBrowserProcess()));
   family_fetcher_->StartGetFamilyMembers();
 }
 
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
index 7c6cdd9b..9939e91 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
@@ -16,7 +16,9 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "net/url_request/url_request_status.h"
+#include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
 const char kGetFamilyProfileApiPath[] = "families/mine?alt=json";
@@ -80,14 +82,13 @@
     Consumer* consumer,
     const std::string& account_id,
     OAuth2TokenService* token_service,
-    net::URLRequestContextGetter* request_context)
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : OAuth2TokenService::Consumer("family_info_fetcher"),
       consumer_(consumer),
       account_id_(account_id),
       token_service_(token_service),
-      request_context_(request_context),
-      access_token_expired_(false) {
-}
+      url_loader_factory_(std::move(url_loader_factory)),
+      access_token_expired_(false) {}
 
 FamilyInfoFetcher::~FamilyInfoFetcher() {
   // Ensures O2TS observation is cleared when FamilyInfoFetcher is destructed
@@ -167,7 +168,7 @@
   access_token_ = access_token;
 
   GURL url = kids_management_api::GetURL(request_path_);
-  const int id = 0;
+
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("family_info", R"(
         semantics {
@@ -195,21 +196,24 @@
             }
           }
         })");
-  url_fetcher_ = net::URLFetcher::Create(id, url, net::URLFetcher::GET, this,
-                                         traffic_annotation);
 
-  data_use_measurement::DataUseUserData::AttachToFetcher(
-      url_fetcher_.get(),
-      data_use_measurement::DataUseUserData::SUPERVISED_USER);
-  url_fetcher_->SetRequestContext(request_context_);
-  url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                             net::LOAD_DO_NOT_SAVE_COOKIES);
-  url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(
-      kNumFamilyInfoFetcherRetries);
-  url_fetcher_->AddExtraRequestHeader(base::StringPrintf(
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url;
+  resource_request->load_flags =
+      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
+  resource_request->headers.AddHeaderFromString(base::StringPrintf(
       supervised_users::kAuthorizationHeaderFormat, access_token.c_str()));
-
-  url_fetcher_->Start();
+  simple_url_loader_ = network::SimpleURLLoader::Create(
+      std::move(resource_request), traffic_annotation);
+  simple_url_loader_->SetRetryOptions(
+      kNumFamilyInfoFetcherRetries,
+      network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
+  // TODO re-add data use measurement once SimpleURLLoader supports it
+  // data_use_measurement::DataUseUserData::SUPERVISED_USER
+  simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&FamilyInfoFetcher::OnSimpleLoaderComplete,
+                     base::Unretained(this)));
 }
 
 void FamilyInfoFetcher::OnGetTokenFailure(
@@ -220,16 +224,25 @@
   consumer_->OnFailure(TOKEN_ERROR);
 }
 
-void FamilyInfoFetcher::OnURLFetchComplete(
-    const net::URLFetcher* source) {
-  const net::URLRequestStatus& status = source->GetStatus();
-  if (!status.is_success()) {
-    DLOG(WARNING) << "URLRequestStatus error " << status.error();
-    consumer_->OnFailure(NETWORK_ERROR);
-    return;
+void FamilyInfoFetcher::OnSimpleLoaderComplete(
+    std::unique_ptr<std::string> response_body) {
+  int response_code = -1;
+  if (simple_url_loader_->ResponseInfo() &&
+      simple_url_loader_->ResponseInfo()->headers) {
+    response_code =
+        simple_url_loader_->ResponseInfo()->headers->response_code();
   }
+  std::string body;
+  if (response_body)
+    body = std::move(*response_body);
+  OnSimpleLoaderCompleteInternal(simple_url_loader_->NetError(), response_code,
+                                 body);
+}
 
-  int response_code = source->GetResponseCode();
+void FamilyInfoFetcher::OnSimpleLoaderCompleteInternal(
+    int net_error,
+    int response_code,
+    const std::string& response_body) {
   if (response_code == net::HTTP_UNAUTHORIZED && !access_token_expired_) {
     DVLOG(1) << "Access token expired, retrying";
     access_token_expired_ = true;
@@ -246,8 +259,11 @@
     return;
   }
 
-  std::string response_body;
-  source->GetResponseAsString(&response_body);
+  if (net_error != net::OK) {
+    DLOG(WARNING) << "NetError " << net_error;
+    consumer_->OnFailure(NETWORK_ERROR);
+    return;
+  }
 
   if (request_path_ == kGetFamilyProfileApiPath) {
     FamilyProfileFetched(response_body);
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
index 2adb0b06..88275a2 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
@@ -11,9 +11,8 @@
 
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
 #include "google_apis/gaia/oauth2_token_service.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
 
 namespace base {
 class DictionaryValue;
@@ -21,16 +20,16 @@
 class Time;
 }
 
-namespace net {
-class URLRequestContextGetter;
-}
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}  // namespace network
 
 // Fetches information about the family of the signed-in user. It can get
 // information about the family itself (e.g. a name), as well as a list of
 // family members and their properties.
 class FamilyInfoFetcher : public OAuth2TokenService::Observer,
-                          public OAuth2TokenService::Consumer,
-                          public net::URLFetcherDelegate {
+                          public OAuth2TokenService::Consumer {
  public:
   enum ErrorCode {
     TOKEN_ERROR,    // Failed to get OAuth2 token.
@@ -81,10 +80,11 @@
 
   // Instantiates a fetcher, but doesn't start a fetch - use the StartGet*
   // methods below. |consumer| must outlive us.
-  FamilyInfoFetcher(Consumer* consumer,
-                    const std::string& account_id,
-                    OAuth2TokenService* token_service,
-                    net::URLRequestContextGetter* request_context);
+  FamilyInfoFetcher(
+      Consumer* consumer,
+      const std::string& account_id,
+      OAuth2TokenService* token_service,
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
   ~FamilyInfoFetcher() override;
 
   // Public so tests can use them.
@@ -96,6 +96,11 @@
   void StartGetFamilyProfile();
   void StartGetFamilyMembers();
 
+  // Public so tests can use it.
+  void OnSimpleLoaderCompleteInternal(int net_error,
+                                      int response_code,
+                                      const std::string& response_body);
+
  private:
   // OAuth2TokenService::Observer implementation:
   void OnRefreshTokenAvailable(const std::string& account_id) override;
@@ -108,8 +113,7 @@
   void OnGetTokenFailure(const OAuth2TokenService::Request* request,
                          const GoogleServiceAuthError& error) override;
 
-  // net::URLFetcherDelegate implementation.
-  void OnURLFetchComplete(const net::URLFetcher* source) override;
+  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
 
   static bool ParseMembers(const base::ListValue* list,
                            std::vector<FamilyMember>* members);
@@ -126,13 +130,13 @@
   Consumer* consumer_;
   const std::string account_id_;
   OAuth2TokenService* token_service_;
-  net::URLRequestContextGetter* request_context_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   std::string request_path_;
   std::unique_ptr<OAuth2TokenService::Request> access_token_request_;
   std::string access_token_;
   bool access_token_expired_;
-  std::unique_ptr<net::URLFetcher> url_fetcher_;
+  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
 
   DISALLOW_COPY_AND_ASSIGN(FamilyInfoFetcher);
 };
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc
index 6ef5fc0c6..1785e354 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc
@@ -16,15 +16,17 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "content/public/common/weak_wrapper_shared_url_loader_factory.h"
 #include "net/base/net_errors.h"
-#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/http/http_status_code.h"
 #include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/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"
 
 const char kAccountId[] = "user@gmail.com";
 const char kDifferentAccountId[] = "some_other_user@gmail.com";
-const int kFamilyInfoFetcherURLFetcherID = 0;
 
 bool operator==(const FamilyInfoFetcher::FamilyProfile& family1,
                 const FamilyInfoFetcher::FamilyProfile& family2) {
@@ -110,9 +112,12 @@
                               public FamilyInfoFetcher::Consumer {
  public:
   FamilyInfoFetcherTest()
-      : request_context_(new net::TestURLRequestContextGetter(
-            base::ThreadTaskRunnerHandle::Get())),
-        fetcher_(this, kAccountId, &token_service_, request_context_.get()) {}
+      : fetcher_(
+            this,
+            kAccountId,
+            &token_service_,
+            base::MakeRefCounted<content::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)) {}
 
   MOCK_METHOD1(OnGetFamilyProfileSuccess,
                void(const FamilyInfoFetcher::FamilyProfile& family));
@@ -137,20 +142,8 @@
         base::Time::Now() + base::TimeDelta::FromHours(1));
   }
 
-  net::TestURLFetcher* GetURLFetcher() {
-    net::TestURLFetcher* url_fetcher =
-        url_fetcher_factory_.GetFetcherByID(
-            kFamilyInfoFetcherURLFetcherID);
-    EXPECT_TRUE(url_fetcher);
-    return url_fetcher;
-  }
-
   void SendResponse(net::Error error, const std::string& response) {
-    net::TestURLFetcher* url_fetcher = GetURLFetcher();
-    url_fetcher->set_status(net::URLRequestStatus::FromError(error));
-    url_fetcher->set_response_code(net::HTTP_OK);
-    url_fetcher->SetResponseString(response);
-    url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
+    fetcher_.OnSimpleLoaderCompleteInternal(error, net::HTTP_OK, response);
   }
 
   void SendValidGetFamilyProfileResponse(
@@ -173,8 +166,7 @@
 
   base::MessageLoop message_loop_;
   FakeProfileOAuth2TokenService token_service_;
-  scoped_refptr<net::TestURLRequestContextGetter> request_context_;
-  net::TestURLFetcherFactory url_fetcher_factory_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
   FamilyInfoFetcher fetcher_;
 };
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 38caa0b..d174083 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -12,7 +12,6 @@
 import("//components/offline_pages/buildflags/features.gni")
 import("//components/signin/features.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//media/media_options.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//rlz/buildflags/buildflags.gni")
@@ -784,6 +783,8 @@
     "webui/log_web_ui_url.h",
     "webui/media/media_engagement_ui.cc",
     "webui/media/media_engagement_ui.h",
+    "webui/media/webrtc_logs_ui.cc",
+    "webui/media/webrtc_logs_ui.h",
     "webui/memory_internals_ui.cc",
     "webui/memory_internals_ui.h",
     "webui/metrics_handler.cc",
@@ -1015,6 +1016,7 @@
     "//components/version_ui",
     "//components/web_cache/browser",
     "//components/web_resource",
+    "//components/webrtc_logging/browser",
     "//content/app/resources",
     "//content/public/common",
     "//crypto",
@@ -3850,14 +3852,6 @@
     }
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "webui/media/webrtc_logs_ui.cc",
-      "webui/media/webrtc_logs_ui.h",
-    ]
-    deps += [ "//components/webrtc_logging/browser" ]
-  }
-
   if (safe_browsing_mode == 1) {
     deps += [
       "//chrome/browser/safe_browsing:chunk_proto",
diff --git a/chrome/browser/ui/android/external_protocol_dialog_android.cc b/chrome/browser/ui/android/external_protocol_dialog_android.cc
index c84db6c..000fcc6d 100644
--- a/chrome/browser/ui/android/external_protocol_dialog_android.cc
+++ b/chrome/browser/ui/android/external_protocol_dialog_android.cc
@@ -36,10 +36,9 @@
       has_user_gesture,  // has_user_gesture
       false,             // is_post, doesn't matter here.
       page_transition,
-      false,           // is_redirect, doesn't matter here.
-      true,            // is_external_protocol
-      false,           // is_main_frame
-      GURL(),          // base_url_for_data_url, not applicable.
-      base::nullopt);  // suggested_filename
+      false,    // is_redirect, doesn't matter here.
+      true,     // is_external_protocol
+      false,    // is_main_frame
+      GURL());  // base_url_for_data_url, not applicable.
   delegate->ShouldIgnoreNavigation(navigation_params);
 }
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index b5b3763..c46297a 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -290,6 +290,11 @@
   return GetPrefs()->GetBoolean(prefs::kAutofillEnabled);
 }
 
+bool ChromeAutofillClient::AreServerCardsSupported() {
+  // When in VR, server side cards are not supported.
+  return !vr::VrTabHelper::IsInVr(web_contents());
+}
+
 void ChromeAutofillClient::MainFrameWasResized(bool width_changed) {
 #if defined(OS_ANDROID)
   // Ignore virtual keyboard showing and hiding a strip of suggestions.
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 04f5124..6918d996 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -90,6 +90,7 @@
   bool IsContextSecure() override;
   bool ShouldShowSigninPromo() override;
   bool IsAutofillSupported() override;
+  bool AreServerCardsSupported() override;
   void ExecuteCommand(int id) override;
 
   // content::WebContentsObserver implementation.
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 3bcf193c..f235d9fc 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -322,7 +322,6 @@
       params->should_replace_current_entry;
   load_url_params.is_renderer_initiated = params->is_renderer_initiated;
   load_url_params.started_from_context_menu = params->started_from_context_menu;
-  load_url_params.suggested_filename = params->suggested_filename;
   load_url_params.has_user_gesture = params->user_gesture;
 
   // |frame_tree_node_id| is kNoFrameTreeNodeId for main frame navigations.
diff --git a/chrome/browser/ui/browser_navigator_params.cc b/chrome/browser/ui/browser_navigator_params.cc
index 0f5171b..50fe09ab 100644
--- a/chrome/browser/ui/browser_navigator_params.cc
+++ b/chrome/browser/ui/browser_navigator_params.cc
@@ -58,6 +58,5 @@
   this->uses_post = params.uses_post;
   this->post_data = params.post_data;
   this->started_from_context_menu = params.started_from_context_menu;
-  this->suggested_filename = params.suggested_filename;
   this->open_pwa_window_if_possible = params.open_app_window_if_possible;
 }
diff --git a/chrome/browser/ui/browser_navigator_params.h b/chrome/browser/ui/browser_navigator_params.h
index bfb2276..24b7099 100644
--- a/chrome/browser/ui/browser_navigator_params.h
+++ b/chrome/browser/ui/browser_navigator_params.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
-#include "base/optional.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "content/public/browser/global_request_id.h"
@@ -246,11 +245,6 @@
   // an about:blank or a data url navigation.
   scoped_refptr<content::SiteInstance> source_site_instance;
 
-  // If this event was triggered by an anchor element with a download
-  // attribute, |suggested_filename| will contain the (possibly empty) value of
-  // that attribute.
-  base::Optional<std::string> suggested_filename;
-
   // Indicates that the navigation should happen in an pwa window if
   // possible, i.e. if the is a PWA installed for the target URL.
   bool open_pwa_window_if_possible = false;
diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc
index a6bfb74..49f1f88 100644
--- a/chrome/browser/ui/search/local_ntp_browsertest.cc
+++ b/chrome/browser/ui/search/local_ntp_browsertest.cc
@@ -329,9 +329,8 @@
   ui_test_utils::NavigateToURL(browser(), download_url);
   download_observer.WaitForFinished();
 
-  // This should neither have changed the visible URL, nor the last committed
-  // one.
-  ASSERT_EQ(GURL(chrome::kChromeUINewTabURL), active_tab->GetVisibleURL());
+  // This should have changed the visible URL, but not the last committed one.
+  ASSERT_EQ(download_url, active_tab->GetVisibleURL());
   ASSERT_EQ(GURL(chrome::kChromeUINewTabURL),
             active_tab->GetLastCommittedURL());
 
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
index b978b700..cdeecc21 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h
@@ -41,7 +41,6 @@
 
 namespace syncer {
 class SyncService;
-class SyncServiceBase;
 }  // namespace syncer
 
 namespace ui {
@@ -198,8 +197,7 @@
   ScopedObserver<sessions::TabRestoreService, RecentTabsSubMenuModel>
       tab_restore_service_observer_;
 
-  ScopedObserver<syncer::SyncServiceBase, RecentTabsSubMenuModel>
-      sync_observer_;
+  ScopedObserver<syncer::SyncService, RecentTabsSubMenuModel> sync_observer_;
 #endif
 
   base::WeakPtrFactory<RecentTabsSubMenuModel> weak_ptr_factory_;
diff --git a/chrome/browser/ui/views/device_chooser_content_view.cc b/chrome/browser/ui/views/device_chooser_content_view.cc
index f5358a0..d555fa4 100644
--- a/chrome/browser/ui/views/device_chooser_content_view.cc
+++ b/chrome/browser/ui/views/device_chooser_content_view.cc
@@ -220,7 +220,7 @@
 
   if (chooser_controller_->IsConnected(row))
     return gfx::CreateVectorIcon(vector_icons::kBluetoothConnectedIcon,
-                                 gfx::kChromeIconGrey);
+                                 TableModel::kIconSize, gfx::kChromeIconGrey);
 
   int level = chooser_controller_->GetSignalStrengthLevel(row);
 
diff --git a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
index 1d2c74c9..dab1059f 100644
--- a/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_handler_web_flow_view_controller.cc
@@ -22,11 +22,13 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/image_button_factory.h"
+#include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/layout/fill_layout.h"
@@ -47,6 +49,7 @@
  public:
   ReadOnlyOriginView(const base::string16& page_title,
                      const GURL& origin,
+                     const gfx::ImageSkia* icon_image_skia,
                      SkColor background_color,
                      views::ButtonListener* site_settings_listener) {
     std::unique_ptr<views::View> title_origin_container =
@@ -86,6 +89,17 @@
     views::GridLayout* top_level_layout =
         SetLayoutManager(std::make_unique<views::GridLayout>(this));
     views::ColumnSet* top_level_columns = top_level_layout->AddColumnSet(0);
+    // Payment handler icon comes from Web Manifest, which are square.
+    constexpr int kPaymentHandlerIconSize = 32;
+    bool has_icon = icon_image_skia && icon_image_skia->width();
+    if (has_icon) {
+      // A column for the instrument icon.
+      top_level_columns->AddColumn(
+          views::GridLayout::LEADING, views::GridLayout::FILL, 0,
+          views::GridLayout::FIXED, kPaymentHandlerIconSize,
+          kPaymentHandlerIconSize);
+      top_level_columns->AddPaddingColumn(0, 8);
+    }
     top_level_columns->AddColumn(views::GridLayout::LEADING,
                                  views::GridLayout::FILL, 1,
                                  views::GridLayout::USE_PREF, 0, 0);
@@ -95,6 +109,14 @@
         views::GridLayout::FIXED, kSiteSettingsSize, kSiteSettingsSize);
 
     top_level_layout->StartRow(0, 0);
+    if (has_icon) {
+      std::unique_ptr<views::ImageView> instrument_icon_view =
+          CreateInstrumentIconView(/*icon_id=*/0, icon_image_skia,
+                                   /*label=*/page_title);
+      instrument_icon_view->SetImageSize(
+          gfx::Size(kPaymentHandlerIconSize, kPaymentHandlerIconSize));
+      top_level_layout->AddView(instrument_icon_view.release());
+    }
     top_level_layout->AddView(title_origin_container.release());
 
     views::ImageButton* site_settings_button =
@@ -175,8 +197,10 @@
   const GURL origin =
       web_contents() ? web_contents()->GetVisibleURL().GetOrigin() : GURL();
   std::unique_ptr<views::Background> background = GetHeaderBackground();
-  return std::make_unique<ReadOnlyOriginView>(GetSheetTitle(), origin,
-                                              background->get_color(), this);
+  return std::make_unique<ReadOnlyOriginView>(
+      GetSheetTitle(), origin,
+      state()->selected_instrument()->icon_image_skia(),
+      background->get_color(), this);
 }
 
 std::unique_ptr<views::Background>
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.cc b/chrome/browser/ui/views/payments/payment_request_views_util.cc
index 9ab17b84..d325dbd4 100644
--- a/chrome/browser/ui/views/payments/payment_request_views_util.cc
+++ b/chrome/browser/ui/views/payments/payment_request_views_util.cc
@@ -203,7 +203,7 @@
   columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0,
                      views::GridLayout::USE_PREF, 0, 0);
 
-  constexpr int kPaddingBetweenArrowAndTitle = 16;
+  constexpr int kPaddingBetweenArrowAndTitle = 8;
   if (show_back_arrow)
     columns->AddPaddingColumn(0, kPaddingBetweenArrowAndTitle);
 
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index b609302..0c7c6abe 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -479,9 +479,6 @@
       "pepper_permission_util.h",
     ]
   }
-  if (!enable_webrtc) {
-    sources -= [ "media/webrtc_logging_messages.h" ]
-  }
 
   if (safe_browsing_mode != 0) {
     public_deps += [ "//chrome/common/safe_browsing" ]
diff --git a/chrome/common/extensions/api/BUILD.gn b/chrome/common/extensions/api/BUILD.gn
index 288a413..8cdf32f 100644
--- a/chrome/common/extensions/api/BUILD.gn
+++ b/chrome/common/extensions/api/BUILD.gn
@@ -6,7 +6,6 @@
 import("//build/config/ui.gni")
 import("//chrome/common/features.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//media/media_options.gni")
 import("//tools/json_schema_compiler/json_features.gni")
 import("//tools/json_schema_compiler/json_schema_api.gni")
 
@@ -16,6 +15,10 @@
   "accessibility_features.json",
   "accessibility_private.json",
   "activity_log_private.json",
+  "cast_streaming_receiver_session.idl",
+  "cast_streaming_rtp_stream.idl",
+  "cast_streaming_session.idl",
+  "cast_streaming_udp_transport.idl",
   "autofill_private.idl",
   "automation.idl",
   "automation_internal.idl",
@@ -131,15 +134,6 @@
   schema_sources += [ "mdns.idl" ]
 }
 
-if (enable_webrtc) {
-  schema_sources += [
-    "cast_streaming_receiver_session.idl",
-    "cast_streaming_rtp_stream.idl",
-    "cast_streaming_session.idl",
-    "cast_streaming_udp_transport.idl",
-  ]
-}
-
 extensions_api_root_namespace = "extensions::api::%(namespace)s"
 
 extensions_api_uncompiled_sources = [
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 5027bbf..d7b7507 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -175,7 +175,8 @@
   sharedWithMe,
   shared,
   starred,
-  externalFileUrl
+  externalFileUrl,
+  alternateUrl
 };
 
 // Entry property visibility for setEntryTag();
@@ -301,8 +302,11 @@
   // True if the entry is starred by the user.
   boolean? starred;
 
-  // External file URL to open the file in browser.
+  // externalfile:// URL to open the file in browser.
   DOMString? externalFileUrl;
+
+  // https:// URL to open the file in the Drive website.
+  DOMString? alternateUrl;
 };
 
 // Information about total and remaining size on the mount point.
diff --git a/chrome/common/features.gni b/chrome/common/features.gni
index a949d72..a8d49da 100644
--- a/chrome/common/features.gni
+++ b/chrome/common/features.gni
@@ -80,8 +80,10 @@
   "enable_print_preview=$enable_print_preview",
   "enable_printing=$enable_basic_printing",
   "enable_service_discovery=$enable_service_discovery",
-  "enable_webrtc=$enable_webrtc",
   "enable_vr=$enable_vr",
+
+  # TODO(phoglund): Remove this.
+  "enable_webrtc=true",
   "mac_views_browser=$mac_views_browser",
   "safe_browsing_mode=$safe_browsing_mode",
   "optimize_webui=$optimize_webui",
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index 6fd923e..0b312fc 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -8,7 +8,6 @@
 import("//components/offline_pages/buildflags/features.gni")
 import("//components/spellcheck/spellcheck_build_features.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//media/media_options.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//tools/grit/grit_rule.gni")
 
@@ -60,6 +59,10 @@
     "media/chrome_key_systems.h",
     "media/chrome_key_systems_provider.cc",
     "media/chrome_key_systems_provider.h",
+    "media/chrome_webrtc_log_message_delegate.cc",
+    "media/chrome_webrtc_log_message_delegate.h",
+    "media/webrtc_logging_message_filter.cc",
+    "media/webrtc_logging_message_filter.h",
     "net/net_error_helper.cc",
     "net/net_error_helper.h",
     "net/net_error_helper_core.cc",
@@ -346,24 +349,8 @@
     public_deps = [
       "//ipc",
     ]
-    if (!enable_webrtc) {
-      sources -= [
-        "media/cast_receiver_session.cc",
-        "media/cast_receiver_session.h",
-        "media/cast_rtp_stream.cc",
-        "media/cast_rtp_stream.h",
-      ]
-    }
   }
-  if (enable_webrtc) {
-    sources += [
-      "media/chrome_webrtc_log_message_delegate.cc",
-      "media/chrome_webrtc_log_message_delegate.h",
-      "media/webrtc_logging_message_filter.cc",
-      "media/webrtc_logging_message_filter.h",
-    ]
-  }
-  if (enable_extensions && enable_webrtc) {
+  if (enable_extensions) {
     sources += [
       "extensions/cast_streaming_native_handler.cc",
       "extensions/cast_streaming_native_handler.h",
@@ -428,6 +415,8 @@
   sources = [
     "chrome_mock_render_thread.cc",
     "chrome_mock_render_thread.h",
+    "media/mock_webrtc_logging_message_filter.cc",
+    "media/mock_webrtc_logging_message_filter.h",
     "safe_browsing/mock_feature_extractor_clock.cc",
     "safe_browsing/mock_feature_extractor_clock.h",
     "safe_browsing/test_utils.cc",
@@ -443,13 +432,6 @@
     "//testing/gtest",
   ]
 
-  if (enable_webrtc) {
-    sources += [
-      "media/mock_webrtc_logging_message_filter.cc",
-      "media/mock_webrtc_logging_message_filter.h",
-    ]
-  }
-
   if (is_android) {
     sources -= [
       "safe_browsing/mock_feature_extractor_clock.cc",
diff --git a/chrome/renderer/autofill/form_autofill_browsertest.cc b/chrome/renderer/autofill/form_autofill_browsertest.cc
index ce598cc..9def392 100644
--- a/chrome/renderer/autofill/form_autofill_browsertest.cc
+++ b/chrome/renderer/autofill/form_autofill_browsertest.cc
@@ -2530,30 +2530,31 @@
 }
 
 TEST_F(FormAutofillTest, WebFormElementToFormData) {
-  LoadHTML("<FORM name='TestForm' action='http://cnn.com' method='post'>"
-           "  <LABEL for='firstname'>First name:</LABEL>"
-           "    <INPUT type='text' id='firstname' value='John'/>"
-           "  <LABEL for='lastname'>Last name:</LABEL>"
-           "    <INPUT type='text' id='lastname' value='Smith'/>"
-           "  <LABEL for='street-address'>Address:</LABEL>"
-           "    <TEXTAREA id='street-address'>"
-                 "123 Fantasy Ln.&#10;"
-                 "Apt. 42"
-                "</TEXTAREA>"
-           "  <LABEL for='state'>State:</LABEL>"
-           "    <SELECT id='state'/>"
-           "      <OPTION value='CA'>California</OPTION>"
-           "      <OPTION value='TX'>Texas</OPTION>"
-           "    </SELECT>"
-           "  <LABEL for='password'>Password:</LABEL>"
-           "    <INPUT type='password' id='password' value='secret'/>"
-           "  <LABEL for='month'>Card expiration:</LABEL>"
-           "    <INPUT type='month' id='month' value='2011-12'/>"
-           "    <INPUT type='submit' name='reply-send' value='Send'/>"
-           // The below inputs should be ignored
-           "  <LABEL for='notvisible'>Hidden:</LABEL>"
-           "    <INPUT type='hidden' id='notvisible' value='apple'/>"
-           "</FORM>");
+  LoadHTML(
+      "<FORM name='TestForm' action='http://cnn.com/submit/?a=1' method='post'>"
+      "  <LABEL for='firstname'>First name:</LABEL>"
+      "    <INPUT type='text' id='firstname' value='John'/>"
+      "  <LABEL for='lastname'>Last name:</LABEL>"
+      "    <INPUT type='text' id='lastname' value='Smith'/>"
+      "  <LABEL for='street-address'>Address:</LABEL>"
+      "    <TEXTAREA id='street-address'>"
+      "123 Fantasy Ln.&#10;"
+      "Apt. 42"
+      "</TEXTAREA>"
+      "  <LABEL for='state'>State:</LABEL>"
+      "    <SELECT id='state'/>"
+      "      <OPTION value='CA'>California</OPTION>"
+      "      <OPTION value='TX'>Texas</OPTION>"
+      "    </SELECT>"
+      "  <LABEL for='password'>Password:</LABEL>"
+      "    <INPUT type='password' id='password' value='secret'/>"
+      "  <LABEL for='month'>Card expiration:</LABEL>"
+      "    <INPUT type='month' id='month' value='2011-12'/>"
+      "    <INPUT type='submit' name='reply-send' value='Send'/>"
+      // The below inputs should be ignored
+      "  <LABEL for='notvisible'>Hidden:</LABEL>"
+      "    <INPUT type='hidden' id='notvisible' value='apple'/>"
+      "</FORM>");
 
   WebLocalFrame* frame = GetMainFrame();
   ASSERT_NE(nullptr, frame);
@@ -2571,7 +2572,7 @@
   EXPECT_EQ(ASCIIToUTF16("TestForm"), form.name);
   EXPECT_EQ(GetCanonicalOriginForDocument(frame->GetDocument()), form.origin);
   EXPECT_FALSE(form.origin.is_empty());
-  EXPECT_EQ(GURL("http://cnn.com"), form.action);
+  EXPECT_EQ(GURL("http://cnn.com/submit/"), form.action);
 
   const std::vector<FormFieldData>& fields = form.fields;
   ASSERT_EQ(6U, fields.size());
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 78bdbc1..deb01ac4 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -16,7 +16,6 @@
 import("//components/signin/features.gni")
 import("//components/spellcheck/spellcheck_build_features.gni")
 import("//extensions/buildflags/buildflags.gni")
-import("//media/media_options.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//remoting/remoting_enable.gni")
@@ -1742,6 +1741,7 @@
         "//components/exo:test_support",
         "//components/prefs",
         "//components/user_manager:test_support",
+        "//content/public/common:feature_h264_with_openh264_ffmpeg",
         "//services/audio/public/cpp:test_support",
         "//services/network/public/mojom",
         "//services/preferences/public/cpp",
@@ -1805,31 +1805,7 @@
     if (enable_captive_portal_detection) {
       sources += [ "../browser/captive_portal/captive_portal_browsertest.cc" ]
     }
-    if (enable_webrtc) {
-      deps += [ "//content/public/common:feature_h264_with_openh264_ffmpeg" ]
-    } else {
-      sources -= [
-        "../browser/media/webrtc/webrtc_apprtc_browsertest.cc",
-        "../browser/media/webrtc/webrtc_audio_quality_browsertest.cc",
-        "../browser/media/webrtc/webrtc_browsertest.cc",
-        "../browser/media/webrtc/webrtc_disable_encryption_flag_browsertest.cc",
-        "../browser/media/webrtc/webrtc_getmediadevices_browsertest.cc",
-        "../browser/media/webrtc/webrtc_internals_integration_browsertest.cc",
-        "../browser/media/webrtc/webrtc_internals_perf_browsertest.cc",
-        "../browser/media/webrtc/webrtc_rtp_browsertest.cc",
-        "../browser/media/webrtc/webrtc_simulcast_browsertest.cc",
-        "../browser/media/webrtc/webrtc_stats_perf_browsertest.cc",
-        "../browser/media/webrtc/webrtc_video_display_perf_browsertest.cc",
-        "../browser/media/webrtc/webrtc_video_quality_browsertest.cc",
-        "../browser/media/webrtc/webrtc_webcam_browsertest.cc",
-      ]
-      if (enable_extensions) {
-        sources -= [
-          "../browser/extensions/api/webrtc_audio_private/webrtc_audio_private_browsertest.cc",
-          "../browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc",
-        ]
-      }
-    }
+
     if (is_mac) {
       # Other platforms only need .pak files, and can build this target
       # standalone much faster.
@@ -2958,6 +2934,7 @@
       "../browser/platform_util_unittest.cc",
       "../browser/policy/policy_path_parser_unittest.cc",
       "../browser/profile_resetter/profile_resetter_unittest.cc",
+      "../browser/profile_resetter/reset_report_uploader_unittest.cc",
       "../browser/profile_resetter/triggered_profile_resetter_win_unittest.cc",
       "../browser/renderer_context_menu/render_view_context_menu_unittest.cc",
       "../browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_impl_win_unittest.cc",
@@ -3148,6 +3125,9 @@
       "../browser/media/router/providers/dial/dial_media_route_provider_unittest.cc",
       "../browser/media/router/providers/extension/extension_media_route_provider_proxy_unittest.cc",
       "../browser/media/router/providers/wired_display/wired_display_media_route_provider_unittest.cc",
+      "../browser/media/webrtc/webrtc_log_uploader_unittest.cc",
+      "../browser/media/webrtc/webrtc_rtp_dump_handler_unittest.cc",
+      "../browser/media/webrtc/webrtc_rtp_dump_writer_unittest.cc",
       "../browser/policy/local_sync_policy_handler_unittest.cc",
       "../browser/renderer_context_menu/render_view_context_menu_test_util.cc",
       "../browser/renderer_context_menu/render_view_context_menu_test_util.h",
@@ -3174,6 +3154,7 @@
       "../common/media_router/discovery/media_sink_service_base_unittest.cc",
       "../common/media_router/mojo/media_router_struct_traits_unittest.cc",
       "../common/media_router/providers/cast/cast_media_source_unittest.cc",
+      "../renderer/media/chrome_webrtc_log_message_delegate_unittest.cc",
     ]
     deps += [
       "//components/bubble:test_support",
@@ -3902,14 +3883,6 @@
       ]
     }
   }
-  if (enable_webrtc) {
-    sources += [
-      "../browser/media/webrtc/webrtc_log_uploader_unittest.cc",
-      "../browser/media/webrtc/webrtc_rtp_dump_handler_unittest.cc",
-      "../browser/media/webrtc/webrtc_rtp_dump_writer_unittest.cc",
-      "../renderer/media/chrome_webrtc_log_message_delegate_unittest.cc",
-    ]
-  }
   if (is_chromeos) {
     deps += [
       "//chrome/browser/chromeos:unit_tests",
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 0e2e92f..a525fef0 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -1702,28 +1702,18 @@
     self.assertTrue(frame_url.endswith('#two'))
 
 
-def log(message):
-  # Log a message with timestamp, in a format similar to ChromeDriver log
-  print '[%.3f] %s' % (time.time(), message)
-
 class ChromeDriverPageLoadTimeoutTest(ChromeDriverBaseTestWithWebServer):
 
   class _RequestHandler(object):
     def __init__(self):
       self.request_received_event = threading.Event()
       self.send_response_event = threading.Event()
-      print
-      log('Created _RequestHandler')
 
     def handle(self, request):
-      log('Request received')
       self.request_received_event.set()
-      log('After request_received_event.set()')
       # Don't hang infinitely, 10 seconds are enough.
       self.send_response_event.wait(10)
-      log('After send_response_event.wait(10)')
       self.send_response_event.clear()
-      log('After send_response_event.clear()')
       return {'Cache-Control': 'no-store'}, 'Hi!'
 
   def setUp(self):
@@ -1740,16 +1730,13 @@
     # on Mac. So we use longer timeout on Mac, 0.5 second on others.
     timeout = 3000 if util.GetPlatformName() == 'mac' else 500
     self._driver.SetTimeout('page load', timeout)
-    log('setUp complete')
 
   def tearDown(self):
     super(ChromeDriverPageLoadTimeoutTest, self).tearDown()
     self._http_server.SetCallbackForPath('/hang', None)
 
   def _LoadHangingUrl(self, host=None):
-    log('Entering _LoadHangingUrl')
     self._driver.Load(self._http_server.GetUrl(host) + '/hang')
-    log('Leaving _LoadHangingUrl')
 
   def _CheckPageLoadTimeout(self, action):
     self._handler.request_received_event.clear()
@@ -1779,30 +1766,21 @@
 
   def testHistoryNavigationWithPageLoadTimeout(self):
     # Allow the page to load for the first time.
-    log('Entering testHistoryNavigationWithPageLoadTimeout')
     self._handler.send_response_event.set()
-    log('After self._handler.send_response_event.set()')
     self._LoadHangingUrl()
     self.assertTrue(self._handler.request_received_event.wait(1))
 
-    log('Before GoBack')
     self._driver.GoBack()
-    log('Before GoForward')
     self._CheckPageLoadTimeout(self._driver.GoForward)
-    log('After GoForward')
     self.assertEquals(self._initial_url, self._driver.GetCurrentUrl())
 
   def testRefreshWithPageLoadTimeout(self):
     # Allow the page to load for the first time.
-    log('Entering testRefreshWithPageLoadTimeout')
     self._handler.send_response_event.set()
-    log('After self._handler.send_response_event.set()')
     self._LoadHangingUrl()
     self.assertTrue(self._handler.request_received_event.wait(1))
 
-    log('Before Refresh')
     self._CheckPageLoadTimeout(self._driver.Refresh)
-    log('After Refresh')
 
 
 class ChromeDriverAndroidTest(ChromeDriverBaseTest):
diff --git a/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js b/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js
index 126add5..6791a0e 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/download/test_download.js
@@ -3,7 +3,8 @@
 // found in the LICENSE file.
 
 onload = function() {
-  var getURL = chrome.extension.getURL;
+  var URL_START =
+      'http://127.0.0.1:PORT/extensions/api_test/webnavigation/download/a.html';
   var URL_LOAD_REDIRECT = "http://127.0.0.1:PORT/server-redirect";
   var URL_NOT_FOUND = "http://127.0.0.1:PORT/not-found";
   chrome.tabs.create({"url": "about:blank"}, function(tab) {
@@ -12,11 +13,12 @@
       var fixPort = function(url) {
         return url.replace(/PORT/g, config.testServer.port);
       };
+      URL_START = fixPort(URL_START);
       URL_LOAD_REDIRECT = fixPort(URL_LOAD_REDIRECT);
       URL_NOT_FOUND = fixPort(URL_NOT_FOUND);
       chrome.test.runTests([
         // Navigates to a page that redirects (on the server side) to a.html.
-        function serverRedirect() {
+        function download() {
           expect([
             { label: "a-onBeforeNavigate",
               event: "onBeforeNavigate",
@@ -25,7 +27,7 @@
                          processId: -1,
                          tabId: 0,
                          timeStamp: 0,
-                         url: getURL('a.html') }},
+                         url: URL_START }},
             { label: "a-onCommitted",
               event: "onCommitted",
               details: { frameId: 0,
@@ -34,41 +36,24 @@
                          timeStamp: 0,
                          transitionQualifiers: [],
                          transitionType: "link",
-                         url: getURL('a.html') }},
+                         url: URL_START }},
             { label: "a-onDOMContentLoaded",
               event: "onDOMContentLoaded",
               details: { frameId: 0,
                          processId: 0,
                          tabId: 0,
                          timeStamp: 0,
-                         url: getURL('a.html') }},
+                         url: URL_START }},
             { label: "a-onCompleted",
               event: "onCompleted",
               details: { frameId: 0,
                          processId: 0,
                          tabId: 0,
                          timeStamp: 0,
-                         url: getURL('a.html') }},
-            { label: "b-onBeforeNavigate",
-              event: "onBeforeNavigate",
-              details: { frameId: 0,
-                         parentFrameId: -1,
-                         processId: -1,
-                         tabId: 0,
-                         timeStamp: 0,
-                         url: URL_LOAD_REDIRECT }},
-            { label: "b-onErrorOccurred",
-              event: "onErrorOccurred",
-              details: { error: "net::ERR_INVALID_RESPONSE",
-                         frameId: 0,
-                         processId: -1,
-                         tabId: 0,
-                         timeStamp: 0,
-                         url: URL_NOT_FOUND }}],
-          [ navigationOrder("a-"),
-            [ "b-onBeforeNavigate", "b-onErrorOccurred"] ]);
+                         url: URL_START }}],
+          [ navigationOrder("a-") ]);
           chrome.tabs.update(
-              tabId, { url: getURL('a.html?' + config.testServer.port) });
+              tabId, { url: URL_START + "?" + config.testServer.port });
         },
       ]);
     });
diff --git a/chrome/test/data/webui/settings/all_sites_tests.js b/chrome/test/data/webui/settings/all_sites_tests.js
index 237a2b10..72e1d23 100644
--- a/chrome/test/data/webui/settings/all_sites_tests.js
+++ b/chrome/test/data/webui/settings/all_sites_tests.js
@@ -129,7 +129,8 @@
           testElement.async(resolver.resolve);
           return resolver.promise.then(function() {
             const item = testElement.$.listContainer.children[0];
-            const name = item.querySelector('#displayName');
+            assertEquals('SITE-ENTRY', item.tagName);
+            const name = item.root.querySelector('#displayName');
             assertTrue(!!name);
           });
         });
@@ -177,7 +178,7 @@
 
             // Validate that the sites are shown in UI and can be selected.
             const firstItem = testElement.$.listContainer.children[1];
-            const clickable = firstItem.querySelector('.middle');
+            const clickable = firstItem.root.querySelector('.middle');
             assertNotEquals(undefined, clickable);
             MockInteractions.tap(clickable);
             assertEquals(
@@ -226,7 +227,7 @@
             assertEquals(undefined, testElement.selectedOrigin);
             // Validate that the sites are shown in UI and can be selected.
             const firstItem = testElement.$.listContainer.children[0];
-            const clickable = firstItem.querySelector('.middle');
+            const clickable = firstItem.root.querySelector('.middle');
             assertNotEquals(undefined, clickable);
             MockInteractions.tap(clickable);
             if (testElement.sites.length == 1) {
diff --git a/chrome/test/media_router/media_router_integration_browsertest.cc b/chrome/test/media_router/media_router_integration_browsertest.cc
index 7b0bba42..2882717 100644
--- a/chrome/test/media_router/media_router_integration_browsertest.cc
+++ b/chrome/test/media_router/media_router_integration_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -839,8 +840,14 @@
   RunReconnectSessionTest();
 }
 
+// Flaky on Linux MSAN. https://crbug.com/840165
+#if defined(OS_LINUX) && defined(MEMORY_SANITIZER)
+#define MAYBE_Fail_ReconnectSession DISABLED_Fail_ReconnectSession
+#else
+#define MAYBE_Fail_ReconnectSession Fail_ReconnectSession
+#endif
 IN_PROC_BROWSER_TEST_F(MediaRouterIntegrationBrowserTest,
-                       Fail_ReconnectSession) {
+                       MAYBE_Fail_ReconnectSession) {
   WebContents* web_contents = StartSessionWithTestPageAndChooseSink();
   CheckSessionValidity(web_contents);
   std::string session_id(GetStartedConnectionId(web_contents));
diff --git a/chrome/tools/service_discovery_sniffer/BUILD.gn b/chrome/tools/service_discovery_sniffer/BUILD.gn
index 65f8279..5c3ba63 100644
--- a/chrome/tools/service_discovery_sniffer/BUILD.gn
+++ b/chrome/tools/service_discovery_sniffer/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
-import("//media/media_options.gni")
 
 executable("service_discovery_sniffer") {
   testonly = true
@@ -18,9 +17,6 @@
     "//build/config:exe_and_shlib_deps",
     "//chrome/browser",
     "//net",
+    "//third_party/webrtc_overrides:init_webrtc",
   ]
-
-  if (enable_webrtc) {
-    deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
-  }
 }
diff --git a/chromecast/graphics/cast_system_gesture_event_handler.cc b/chromecast/graphics/cast_system_gesture_event_handler.cc
index d6b27d1..2513a47 100644
--- a/chromecast/graphics/cast_system_gesture_event_handler.cc
+++ b/chromecast/graphics/cast_system_gesture_event_handler.cc
@@ -66,65 +66,79 @@
   return CastSideSwipeOrigin::NONE;
 }
 
-void CastSystemGestureEventHandler::OnEvent(ui::Event* event) {
-  if (current_swipe_ != CastSideSwipeOrigin::NONE &&
-      (event->IsTouchEvent() || event->IsGestureEvent())) {
-    // If we're in the process of handling a swipe, prevent the underlying touch
-    // or gesture event from being propagated out to other users.
-    event->StopPropagation();
-  }
-
-  // From here-on in, we're only interested in gestures.
-  if (!event->IsGestureEvent()) {
+void CastSystemGestureEventHandler::OnTouchEvent(ui::TouchEvent* event) {
+  if (swipe_gesture_handlers_.empty()) {
     return;
   }
-  ui::GestureEvent* gesture_event = event->AsGestureEvent();
-  gfx::Point gesture_location(gesture_event->location());
-  aura::Window* target = static_cast<aura::Window*>(event->target());
 
+  gfx::Point touch_location(event->location());
+  aura::Window* target = static_cast<aura::Window*>(event->target());
   // Convert the event's point to the point on the physical screen.
   // For cast on root window this is likely going to be identical, but put it
   // through the ScreenPositionClient just to be sure.
-  ::wm::ConvertPointToScreen(target, &gesture_location);
+  ::wm::ConvertPointToScreen(target, &touch_location);
   gfx::Rect screen_bounds = display::Screen::GetScreen()
-                                ->GetDisplayNearestPoint(gesture_location)
+                                ->GetDisplayNearestPoint(touch_location)
                                 .bounds();
   CastSideSwipeOrigin side_swipe_origin =
-      GetDragPosition(gesture_location, screen_bounds);
+      GetDragPosition(touch_location, screen_bounds);
 
-  // Detect the beginning of a system gesture swipe.
-  if (side_swipe_origin != CastSideSwipeOrigin::NONE &&
-      (event->type() == ui::ET_SCROLL_FLING_START ||
-       event->type() == ui::ET_GESTURE_BEGIN)) {
+  // A located event has occurred inside the margin. It might be the start of
+  // our gesture, or a touch that we need to squash.
+  if (current_swipe_ == CastSideSwipeOrigin::NONE &&
+      side_swipe_origin != CastSideSwipeOrigin::NONE) {
+    // Check to see if we have any potential consumers of events on this side.
+    // If not, we can continue on without consuming it.
+    bool have_swipe_consumer = false;
     for (auto* side_swipe_handler : swipe_gesture_handlers_) {
-      // Let the subscriber know about the swipe. If it is actually consumed by
-      // them, it will be marked as handled.
-      side_swipe_handler->OnSideSwipeBegin(side_swipe_origin, gesture_event);
-
-      // If handled, remember the origin and then stop the further propagation
-      // of the event.
-      if (event->handled()) {
-        // Record the swipe origin to properly fire OnSideSwipeEnd when the
-        // swipe gesture finishes.
-        current_swipe_ = side_swipe_origin;
-        event->StopPropagation();
-
+      if (side_swipe_handler->CanHandleSwipe(side_swipe_origin)) {
+        have_swipe_consumer = true;
         break;
       }
     }
+    if (!have_swipe_consumer) {
+      return;
+    }
+
+    // All touch or gesture events inside the margins have to be consumed, or we
+    // risk a state issue later when the touch ends (b/78461207)
+    event->StopPropagation();
+
+    // Detect the beginning of a system gesture swipe.
+    if (event->type() == ui::ET_TOUCH_PRESSED) {
+      current_swipe_ = side_swipe_origin;
+      for (auto* side_swipe_handler : swipe_gesture_handlers_) {
+        // Let the subscriber know about the gesture begin.
+        side_swipe_handler->HandleSideSwipeBegin(side_swipe_origin,
+                                                 touch_location);
+      }
+    }
+
     return;
   }
 
-  // Detect the end of a system gesture swipe.
-  if (current_swipe_ != CastSideSwipeOrigin::NONE &&
-      event->type() == ui::ET_GESTURE_END) {
+  if (current_swipe_ == CastSideSwipeOrigin::NONE) {
+    return;
+  }
+
+  // A swipe is in progress, or has completed, so stop propagation of underlying
+  // gesture/touch events.
+  event->StopPropagation();
+
+  // The system gesture has ended.
+  if (event->type() == ui::ET_TOUCH_RELEASED) {
     for (auto* side_swipe_handler : swipe_gesture_handlers_) {
-      side_swipe_handler->OnSideSwipeEnd(current_swipe_, gesture_event);
+      side_swipe_handler->HandleSideSwipeEnd(current_swipe_, touch_location);
     }
     current_swipe_ = CastSideSwipeOrigin::NONE;
 
-    // Prevent the gesture from being used for other gesture uses.
-    target->CleanupGestureState();
+    return;
+  }
+
+  // The system gesture is ongoing...
+  for (auto* side_swipe_handler : swipe_gesture_handlers_) {
+    // Let the subscriber know about the gesture begin.
+    side_swipe_handler->HandleSideSwipeContinue(current_swipe_, touch_location);
   }
 }
 
diff --git a/chromecast/graphics/cast_system_gesture_event_handler.h b/chromecast/graphics/cast_system_gesture_event_handler.h
index 9c04044..d968815 100644
--- a/chromecast/graphics/cast_system_gesture_event_handler.h
+++ b/chromecast/graphics/cast_system_gesture_event_handler.h
@@ -41,7 +41,7 @@
   CastSideSwipeOrigin GetDragPosition(const gfx::Point& point,
                                       const gfx::Rect& screen_bounds) const;
 
-  void OnEvent(ui::Event* event) override;
+  void OnTouchEvent(ui::TouchEvent* event) override;
 
  private:
   const int gesture_start_width_;
diff --git a/chromecast/graphics/cast_system_gesture_event_handler_test.cc b/chromecast/graphics/cast_system_gesture_event_handler_test.cc
index 8612974..d4d21f5a 100644
--- a/chromecast/graphics/cast_system_gesture_event_handler_test.cc
+++ b/chromecast/graphics/cast_system_gesture_event_handler_test.cc
@@ -21,6 +21,7 @@
 constexpr base::TimeDelta kTimeDelay = base::TimeDelta::FromMilliseconds(100);
 constexpr int kSwipeDistance = 50;
 constexpr int kNumSteps = 5;
+constexpr gfx::Point kZeroPoint{0, 0};
 
 }  // namespace
 
@@ -50,56 +51,61 @@
 class TestSideSwipeGestureHandler
     : public CastSideSwipeGestureHandlerInterface {
  public:
+  TestSideSwipeGestureHandler()
+      : begin_swipe_point_(kZeroPoint), end_swipe_point_(kZeroPoint) {}
+
   ~TestSideSwipeGestureHandler() override = default;
 
-  void OnSideSwipeBegin(CastSideSwipeOrigin swipe_origin,
-                        ui::GestureEvent* gesture_event) override {
+  bool CanHandleSwipe(CastSideSwipeOrigin swipe_origin) override {
+    return handle_swipe_;
+  }
+
+  void HandleSideSwipeBegin(CastSideSwipeOrigin swipe_origin,
+                            const gfx::Point& touch_location) override {
     if (handle_swipe_) {
       begin_swipe_origin_ = swipe_origin;
-      begin_gesture_event_ = gesture_event;
-
-      gesture_event->SetHandled();
+      begin_swipe_point_ = touch_location;
     }
   }
 
-  void OnSideSwipeEnd(CastSideSwipeOrigin swipe_origin,
-                      ui::GestureEvent* gesture_event) override {
+  void HandleSideSwipeEnd(CastSideSwipeOrigin swipe_origin,
+                          const gfx::Point& gesture_event) override {
     end_swipe_origin_ = swipe_origin;
-    end_gesture_event_ = gesture_event;
+    end_swipe_point_ = gesture_event;
   }
 
   void SetHandleSwipe(bool handle_swipe) { handle_swipe_ = handle_swipe; }
 
   CastSideSwipeOrigin begin_swipe_origin() const { return begin_swipe_origin_; }
-  ui::GestureEvent* begin_gesture_event() const { return begin_gesture_event_; }
+  gfx::Point begin_swipe_point() const { return begin_swipe_point_; }
 
   CastSideSwipeOrigin end_swipe_origin() const { return end_swipe_origin_; }
-  ui::GestureEvent* end_gesture_event() const { return end_gesture_event_; }
+  gfx::Point end_swipe_point() const { return end_swipe_point_; }
 
  private:
   bool handle_swipe_ = true;
 
   CastSideSwipeOrigin begin_swipe_origin_ = CastSideSwipeOrigin::NONE;
-  ui::GestureEvent* begin_gesture_event_ = nullptr;
+  gfx::Point begin_swipe_point_;
 
   CastSideSwipeOrigin end_swipe_origin_ = CastSideSwipeOrigin::NONE;
-  ui::GestureEvent* end_gesture_event_ = nullptr;
+  gfx::Point end_swipe_point_;
 };
 
 // Event sink to check for events that get through (or don't get through) after
 // the system gesture handler handles them.
 class TestEventHandler : public ui::EventHandler {
  public:
-  TestEventHandler() : EventHandler(), num_gesture_events_received_(0) {}
+  TestEventHandler() : EventHandler(), num_touch_events_received_(0) {}
 
-  void OnGestureEvent(ui::GestureEvent* event) override {
-    num_gesture_events_received_++;
+  void OnTouchEvent(ui::TouchEvent* event) override {
+    num_touch_events_received_++;
   }
 
-  int NumGestureEventsReceived() const { return num_gesture_events_received_; }
+  int NumTouchEventsReceived() const { return num_touch_events_received_; }
 
  private:
-  int num_gesture_events_received_;
+  int num_touch_events_received_;
 };
 
 class CastSystemGestureEventHandlerTest : public aura::test::AuraTestBase {
@@ -157,11 +163,11 @@
 TEST_F(CastSystemGestureEventHandlerTest, Initialization) {
   EXPECT_EQ(CastSideSwipeOrigin::NONE,
             test_gesture_handler().begin_swipe_origin());
-  EXPECT_EQ(nullptr, test_gesture_handler().begin_gesture_event());
+  EXPECT_EQ(kZeroPoint, test_gesture_handler().begin_swipe_point());
   EXPECT_EQ(CastSideSwipeOrigin::NONE,
             test_gesture_handler().end_swipe_origin());
-  EXPECT_EQ(nullptr, test_gesture_handler().end_gesture_event());
-  EXPECT_EQ(0, test_event_handler().NumGestureEventsReceived());
+  EXPECT_EQ(kZeroPoint, test_gesture_handler().end_swipe_point());
+  EXPECT_EQ(0, test_event_handler().NumTouchEventsReceived());
 }
 
 // A swipe in the middle of the screen should produce no system gesture.
@@ -176,11 +182,11 @@
 
   EXPECT_EQ(CastSideSwipeOrigin::NONE,
             test_gesture_handler().begin_swipe_origin());
-  EXPECT_EQ(nullptr, test_gesture_handler().begin_gesture_event());
+  EXPECT_EQ(kZeroPoint, test_gesture_handler().begin_swipe_point());
   EXPECT_EQ(CastSideSwipeOrigin::NONE,
             test_gesture_handler().end_swipe_origin());
-  EXPECT_EQ(nullptr, test_gesture_handler().end_gesture_event());
-  EXPECT_NE(0, test_event_handler().NumGestureEventsReceived());
+  EXPECT_EQ(kZeroPoint, test_gesture_handler().end_swipe_point());
+  EXPECT_NE(0, test_event_handler().NumTouchEventsReceived());
 }
 
 TEST_F(CastSystemGestureEventHandlerTest, SwipeFromLeft) {
@@ -193,11 +199,11 @@
 
   EXPECT_EQ(CastSideSwipeOrigin::LEFT,
             test_gesture_handler().begin_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().begin_gesture_event());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().begin_swipe_point());
   EXPECT_EQ(CastSideSwipeOrigin::LEFT,
             test_gesture_handler().end_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().end_gesture_event());
-  EXPECT_EQ(0, test_event_handler().NumGestureEventsReceived());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().end_swipe_point());
+  EXPECT_EQ(0, test_event_handler().NumTouchEventsReceived());
 }
 
 TEST_F(CastSystemGestureEventHandlerTest, SwipeFromRight) {
@@ -211,11 +217,11 @@
 
   EXPECT_EQ(CastSideSwipeOrigin::RIGHT,
             test_gesture_handler().begin_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().begin_gesture_event());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().begin_swipe_point());
   EXPECT_EQ(CastSideSwipeOrigin::RIGHT,
             test_gesture_handler().end_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().end_gesture_event());
-  EXPECT_EQ(0, test_event_handler().NumGestureEventsReceived());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().end_swipe_point());
+  EXPECT_EQ(0, test_event_handler().NumTouchEventsReceived());
 }
 
 TEST_F(CastSystemGestureEventHandlerTest, SwipeFromTop) {
@@ -228,11 +234,11 @@
 
   EXPECT_EQ(CastSideSwipeOrigin::TOP,
             test_gesture_handler().begin_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().begin_gesture_event());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().begin_swipe_point());
   EXPECT_EQ(CastSideSwipeOrigin::TOP,
             test_gesture_handler().end_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().end_gesture_event());
-  EXPECT_EQ(0, test_event_handler().NumGestureEventsReceived());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().end_swipe_point());
+  EXPECT_EQ(0, test_event_handler().NumTouchEventsReceived());
 }
 
 TEST_F(CastSystemGestureEventHandlerTest, SwipeFromBottom) {
@@ -246,11 +252,11 @@
 
   EXPECT_EQ(CastSideSwipeOrigin::BOTTOM,
             test_gesture_handler().begin_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().begin_gesture_event());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().begin_swipe_point());
   EXPECT_EQ(CastSideSwipeOrigin::BOTTOM,
             test_gesture_handler().end_swipe_origin());
-  EXPECT_NE(nullptr, test_gesture_handler().end_gesture_event());
-  EXPECT_EQ(0, test_event_handler().NumGestureEventsReceived());
+  EXPECT_NE(kZeroPoint, test_gesture_handler().end_swipe_point());
+  EXPECT_EQ(0, test_event_handler().NumTouchEventsReceived());
 }
 
 // Test that ignoring the gesture at its beginning will make it so the swipe
@@ -268,11 +274,11 @@
 
   EXPECT_EQ(CastSideSwipeOrigin::NONE,
             test_gesture_handler().begin_swipe_origin());
-  EXPECT_EQ(nullptr, test_gesture_handler().begin_gesture_event());
+  EXPECT_EQ(kZeroPoint, test_gesture_handler().begin_swipe_point());
   EXPECT_EQ(CastSideSwipeOrigin::NONE,
             test_gesture_handler().end_swipe_origin());
-  EXPECT_EQ(nullptr, test_gesture_handler().end_gesture_event());
-  EXPECT_NE(0, test_event_handler().NumGestureEventsReceived());
+  EXPECT_EQ(kZeroPoint, test_gesture_handler().end_swipe_point());
+  EXPECT_NE(0, test_event_handler().NumTouchEventsReceived());
 }
 
 }  // namespace test
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index ea66bbc5..0ac2dad 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-10651.0.0
\ No newline at end of file
+10654.0.0
\ No newline at end of file
diff --git a/chromeos/components/BUILD.gn b/chromeos/components/BUILD.gn
index 07becfd3..17af193 100644
--- a/chromeos/components/BUILD.gn
+++ b/chromeos/components/BUILD.gn
@@ -16,7 +16,9 @@
   deps = [
     "//base",
     "//base/test:test_support",
+    "//chromeos/components/drivefs:unit_tests",
     "//chromeos/components/proximity_auth:unit_tests",
     "//chromeos/components/tether:unit_tests",
+    "//mojo/edk",
   ]
 }
diff --git a/chromeos/components/drivefs/BUILD.gn b/chromeos/components/drivefs/BUILD.gn
new file mode 100644
index 0000000..6b24f78
--- /dev/null
+++ b/chromeos/components/drivefs/BUILD.gn
@@ -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.
+
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
+component("drivefs") {
+  sources = [
+    "drivefs_host.cc",
+    "drivefs_host.h",
+    "pending_connection_manager.cc",
+    "pending_connection_manager.h",
+  ]
+  deps = [
+    "//base",
+    "//chromeos",
+    "//chromeos/components/drivefs/mojom",
+    "//components/account_id",
+    "//mojo/edk",
+    "//mojo/public/cpp/bindings",
+  ]
+  defines = [ "IS_DRIVEFS_IMPL" ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "drivefs_host_unittest.cc",
+    "pending_connection_manager_unittest.cc",
+  ]
+
+  deps = [
+    ":drivefs",
+    "//base",
+    "//base/test:test_support",
+    "//chromeos:test_support",
+    "//chromeos/components/drivefs/mojom",
+    "//mojo/public/cpp/bindings",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromeos/components/drivefs/DEPS b/chromeos/components/drivefs/DEPS
new file mode 100644
index 0000000..348fd9f
--- /dev/null
+++ b/chromeos/components/drivefs/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+mojo/edk/embedder",
+  "+mojo/public",
+]
diff --git a/chromeos/components/drivefs/OWNERS b/chromeos/components/drivefs/OWNERS
new file mode 100644
index 0000000..0930c405
--- /dev/null
+++ b/chromeos/components/drivefs/OWNERS
@@ -0,0 +1,3 @@
+dats@chromium.org
+sammc@chromium.org
+slangley@chromium.org
diff --git a/chromeos/components/drivefs/drivefs_host.cc b/chromeos/components/drivefs/drivefs_host.cc
new file mode 100644
index 0000000..3a9fcfc
--- /dev/null
+++ b/chromeos/components/drivefs/drivefs_host.cc
@@ -0,0 +1,210 @@
+// 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 "chromeos/components/drivefs/drivefs_host.h"
+
+#include <utility>
+
+#include "base/strings/strcat.h"
+#include "base/unguessable_token.h"
+#include "chromeos/components/drivefs/pending_connection_manager.h"
+#include "mojo/edk/embedder/connection_params.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/embedder/transport_protocol.h"
+#include "mojo/public/cpp/bindings/binding.h"
+
+namespace drivefs {
+
+namespace {
+
+constexpr char kMountScheme[] = "drivefs://";
+constexpr char kDataPath[] = "GCache/v2";
+
+class MojoConnectionDelegateImpl : public DriveFsHost::MojoConnectionDelegate {
+ public:
+  MojoConnectionDelegateImpl() = default;
+
+  static std::unique_ptr<DriveFsHost::MojoConnectionDelegate> Create() {
+    return std::make_unique<MojoConnectionDelegateImpl>();
+  }
+
+  mojom::DriveFsBootstrapPtrInfo InitializeMojoConnection() override {
+    return mojom::DriveFsBootstrapPtrInfo(
+        invitation_.AttachMessagePipe("drivefs-bootstrap"),
+        mojom::DriveFsBootstrap::Version_);
+  }
+
+  void AcceptMojoConnection(base::ScopedFD handle) override {
+    invitation_.Send(base::kNullProcessHandle,
+                     {mojo::edk::TransportProtocol::kLegacy,
+                      mojo::edk::ScopedPlatformHandle(
+                          mojo::edk::PlatformHandle(handle.release()))});
+  }
+
+ private:
+  // The underlying mojo connection.
+  mojo::edk::OutgoingBrokerClientInvitation invitation_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoConnectionDelegateImpl);
+};
+
+base::RepeatingCallback<std::unique_ptr<DriveFsHost::MojoConnectionDelegate>()>
+GetMojoConnectionDelegateFactoryOrDefault(
+    base::RepeatingCallback<
+        std::unique_ptr<DriveFsHost::MojoConnectionDelegate>()> factory) {
+  return factory ? factory
+                 : base::BindRepeating(&MojoConnectionDelegateImpl::Create);
+}
+
+}  // namespace
+
+// A container of state tied to a particular mounting of DriveFS. None of this
+// should be shared between mounts.
+class DriveFsHost::MountState {
+ public:
+  explicit MountState(DriveFsHost* host)
+      : host_(host),
+        mojo_connection_delegate_(
+            host_->mojo_connection_delegate_factory_.Run()),
+        pending_token_(base::UnguessableToken::Create()),
+        binding_(host_) {
+    source_path_ = base::StrCat({kMountScheme, pending_token_.ToString(), "@",
+                                 host_->profile_path_.Append(kDataPath)
+                                     .Append(host_->account_id_.GetGaiaId())
+                                     .value()});
+
+    // TODO(sammc): Switch the mount type once a more appropriate mount type
+    // exists.
+    chromeos::disks::DiskMountManager::GetInstance()->MountPath(
+        source_path_, "", "", chromeos::MOUNT_TYPE_ARCHIVE,
+        chromeos::MOUNT_ACCESS_MODE_READ_WRITE);
+
+    auto bootstrap =
+        mojo::MakeProxy(mojo_connection_delegate_->InitializeMojoConnection());
+    mojom::DriveFsDelegatePtr delegate;
+    binding_.Bind(mojo::MakeRequest(&delegate));
+    bootstrap->Init({base::in_place, host_->account_id_.GetUserEmail()},
+                    mojo::MakeRequest(&drivefs_), std::move(delegate));
+
+    // If unconsumed, the registration is cleaned up when |this| is destructed.
+    PendingConnectionManager::Get().ExpectOpenIpcChannel(
+        pending_token_,
+        base::BindOnce(&DriveFsHost::MountState::AcceptMojoConnection,
+                       base::Unretained(this)));
+  }
+
+  ~MountState() {
+    if (pending_token_) {
+      PendingConnectionManager::Get().CancelExpectedOpenIpcChannel(
+          pending_token_);
+    }
+    if (!mount_path_.empty()) {
+      chromeos::disks::DiskMountManager::GetInstance()->UnmountPath(
+          mount_path_, chromeos::UNMOUNT_OPTIONS_NONE, {});
+    }
+  }
+
+  // Accepts the mojo connection over |handle|, delegating to
+  // |mojo_connection_delegate_|.
+  void AcceptMojoConnection(base::ScopedFD handle) {
+    DCHECK(pending_token_);
+    pending_token_ = {};
+    mojo_connection_delegate_->AcceptMojoConnection(std::move(handle));
+  }
+
+  // Returns false if there was an error for |source_path_| and thus if |this|
+  // should be deleted.
+  bool OnMountEvent(
+      chromeos::disks::DiskMountManager::MountEvent event,
+      chromeos::MountError error_code,
+      const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
+    if (mount_info.source_path != source_path_ ||
+        event != chromeos::disks::DiskMountManager::MOUNTING) {
+      return true;
+    }
+    if (error_code != chromeos::MOUNT_ERROR_NONE) {
+      return false;
+    }
+    mount_path_ = mount_info.mount_path;
+    return true;
+  }
+
+ private:
+  // Owns this.
+  DriveFsHost* const host_;
+
+  const std::unique_ptr<DriveFsHost::MojoConnectionDelegate>
+      mojo_connection_delegate_;
+
+  // The token passed to DriveFS as part of |source_path_| used to match it to
+  // this DriveFsHost instance.
+  base::UnguessableToken pending_token_;
+
+  // The path passed to cros-disks to mount.
+  std::string source_path_;
+
+  // The path where DriveFS is mounted.
+  std::string mount_path_;
+
+  // Mojo connections to the DriveFS process.
+  mojom::DriveFsPtr drivefs_;
+  mojo::Binding<mojom::DriveFsDelegate> binding_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MountState);
+};
+
+DriveFsHost::DriveFsHost(
+    const base::FilePath& profile_path,
+    const AccountId& account_id,
+    base::RepeatingCallback<
+        std::unique_ptr<DriveFsHost::MojoConnectionDelegate>()>
+        mojo_connection_delegate_factory)
+    : profile_path_(profile_path),
+      account_id_(account_id),
+      mojo_connection_delegate_factory_(
+          GetMojoConnectionDelegateFactoryOrDefault(
+              std::move(mojo_connection_delegate_factory))) {
+  chromeos::disks::DiskMountManager::GetInstance()->AddObserver(this);
+}
+
+DriveFsHost::~DriveFsHost() {
+  chromeos::disks::DiskMountManager::GetInstance()->RemoveObserver(this);
+}
+
+bool DriveFsHost::Mount() {
+  if (mount_state_ || account_id_.GetAccountType() != AccountType::GOOGLE) {
+    return false;
+  }
+  mount_state_ = std::make_unique<MountState>(this);
+  return true;
+}
+
+void DriveFsHost::Unmount() {
+  mount_state_.reset();
+}
+
+void DriveFsHost::GetAccessToken(const std::string& client_id,
+                                 const std::string& app_id,
+                                 const std::vector<std::string>& scopes,
+                                 GetAccessTokenCallback callback) {
+  // TODO(crbug.com/823956): Implement this.
+  std::move(callback).Run(mojom::AccessTokenStatus::kAuthError, "");
+}
+
+void DriveFsHost::OnMountEvent(
+    chromeos::disks::DiskMountManager::MountEvent event,
+    chromeos::MountError error_code,
+    const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
+  if (!mount_state_) {
+    return;
+  }
+  if (!mount_state_->OnMountEvent(event, error_code, mount_info)) {
+    Unmount();
+  }
+}
+
+}  // namespace drivefs
diff --git a/chromeos/components/drivefs/drivefs_host.h b/chromeos/components/drivefs/drivefs_host.h
new file mode 100644
index 0000000..2fe2450
--- /dev/null
+++ b/chromeos/components/drivefs/drivefs_host.h
@@ -0,0 +1,86 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_DRIVEFS_DRIVEFS_HOST_H_
+#define CHROMEOS_COMPONENTS_DRIVEFS_DRIVEFS_HOST_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "chromeos/components/drivefs/mojom/drivefs.mojom.h"
+#include "chromeos/disks/disk_mount_manager.h"
+#include "components/account_id/account_id.h"
+
+namespace drivefs {
+
+// A host for a DriveFS process. In addition to managing its lifetime via
+// mounting and unmounting, it also bridges between the DriveFS process and the
+// file manager.
+class COMPONENT_EXPORT(DRIVEFS) DriveFsHost
+    : public mojom::DriveFsDelegate,
+      public chromeos::disks::DiskMountManager::Observer {
+ public:
+  class MojoConnectionDelegate {
+   public:
+    virtual ~MojoConnectionDelegate() = default;
+
+    // Prepare the mojo connection to be used to communicate with the DriveFS
+    // process. Returns the mojo handle to use for bootstrapping.
+    virtual mojom::DriveFsBootstrapPtrInfo InitializeMojoConnection() = 0;
+
+    // Accepts the mojo connection over |handle|.
+    virtual void AcceptMojoConnection(base::ScopedFD handle) = 0;
+  };
+
+  DriveFsHost(const base::FilePath& profile_path,
+              const AccountId& account,
+              base::RepeatingCallback<std::unique_ptr<MojoConnectionDelegate>()>
+                  mojo_connection_delegate_factory = {});
+  ~DriveFsHost() override;
+
+  // Mount DriveFS.
+  bool Mount();
+
+  // Unmount DriveFS.
+  void Unmount();
+
+ private:
+  class MountState;
+
+  // mojom::DriveFsDelegate:
+  void GetAccessToken(const std::string& client_id,
+                      const std::string& app_id,
+                      const std::vector<std::string>& scopes,
+                      GetAccessTokenCallback callback) override;
+
+  // DiskMountManager::Observer:
+  void OnMountEvent(chromeos::disks::DiskMountManager::MountEvent event,
+                    chromeos::MountError error_code,
+                    const chromeos::disks::DiskMountManager::MountPointInfo&
+                        mount_info) override;
+
+  // The path to the user's profile.
+  const base::FilePath profile_path_;
+
+  // The user whose DriveFS instance |this| is host to.
+  const AccountId account_id_;
+
+  // A callback used to create mojo connection delegates.
+  const base::RepeatingCallback<std::unique_ptr<MojoConnectionDelegate>()>
+      mojo_connection_delegate_factory_;
+
+  // State specific to the current mount, or null if not mounted.
+  std::unique_ptr<MountState> mount_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriveFsHost);
+};
+
+}  // namespace drivefs
+
+#endif  // CHROMEOS_COMPONENTS_DRIVEFS_DRIVEFS_HOST_H_
diff --git a/chromeos/components/drivefs/drivefs_host_unittest.cc b/chromeos/components/drivefs/drivefs_host_unittest.cc
new file mode 100644
index 0000000..0f860f9
--- /dev/null
+++ b/chromeos/components/drivefs/drivefs_host_unittest.cc
@@ -0,0 +1,264 @@
+// 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 "chromeos/components/drivefs/drivefs_host.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string_split.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/components/drivefs/pending_connection_manager.h"
+#include "chromeos/disks/mock_disk_mount_manager.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace drivefs {
+namespace {
+
+using testing::_;
+
+class TestingMojoConnectionDelegate
+    : public DriveFsHost::MojoConnectionDelegate {
+ public:
+  TestingMojoConnectionDelegate(
+      mojom::DriveFsBootstrapPtrInfo pending_bootstrap)
+      : pending_bootstrap_(std::move(pending_bootstrap)) {}
+
+  mojom::DriveFsBootstrapPtrInfo InitializeMojoConnection() override {
+    return std::move(pending_bootstrap_);
+  }
+
+  void AcceptMojoConnection(base::ScopedFD handle) override {}
+
+ private:
+  mojom::DriveFsBootstrapPtrInfo pending_bootstrap_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestingMojoConnectionDelegate);
+};
+
+class DriveFsHostTest : public ::testing::Test,
+                        public mojom::DriveFsBootstrap,
+                        public mojom::DriveFs {
+ public:
+  DriveFsHostTest() : bootstrap_binding_(this), binding_(this) {}
+
+ protected:
+  void SetUp() override {
+    testing::Test::SetUp();
+    profile_path_ = base::FilePath(FILE_PATH_LITERAL("/path/to/profile"));
+    account_id_ = AccountId::FromUserEmailGaiaId("test@example.com", "ID");
+
+    disk_manager_ = new chromeos::disks::MockDiskMountManager;
+    // Takes ownership of |disk_manager_|.
+    chromeos::disks::DiskMountManager::InitializeForTesting(disk_manager_);
+
+    host_ = std::make_unique<DriveFsHost>(
+        profile_path_, account_id_,
+        base::BindRepeating(&DriveFsHostTest::CreateMojoConnectionDelegate,
+                            base::Unretained(this)));
+  }
+
+  void TearDown() override {
+    host_.reset();
+    disk_manager_ = nullptr;
+    chromeos::disks::DiskMountManager::Shutdown();
+  }
+
+  std::unique_ptr<DriveFsHost::MojoConnectionDelegate>
+  CreateMojoConnectionDelegate() {
+    DCHECK(pending_bootstrap_);
+    return std::make_unique<TestingMojoConnectionDelegate>(
+        std::move(pending_bootstrap_));
+  }
+
+  void DispatchMountEvent(
+      chromeos::disks::DiskMountManager::MountEvent event,
+      chromeos::MountError error_code,
+      const chromeos::disks::DiskMountManager::MountPointInfo& mount_info) {
+    static_cast<chromeos::disks::DiskMountManager::Observer&>(*host_)
+        .OnMountEvent(event, error_code, mount_info);
+  }
+
+  std::string StartMount() {
+    std::string source;
+    EXPECT_CALL(
+        *disk_manager_,
+        MountPath(
+            testing::AllOf(testing::StartsWith("drivefs://"),
+                           testing::EndsWith("@/path/to/profile/GCache/v2/ID")),
+            "", "", _, chromeos::MOUNT_ACCESS_MODE_READ_WRITE))
+        .WillOnce(testing::SaveArg<0>(&source));
+
+    mojom::DriveFsBootstrapPtrInfo bootstrap;
+    bootstrap_binding_.Bind(mojo::MakeRequest(&bootstrap));
+    pending_bootstrap_ = std::move(bootstrap);
+
+    EXPECT_TRUE(host_->Mount());
+    testing::Mock::VerifyAndClear(&disk_manager_);
+
+    return base::SplitStringPiece(
+               base::StringPiece(source).substr(strlen("drivefs://")), "@",
+               base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)[0]
+        .as_string();
+  }
+
+  void DispatchMountSuccessEvent(const std::string& token) {
+    DispatchMountEvent(
+        chromeos::disks::DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
+        {base::StrCat({"drivefs://", token, "@/path/to/profile/GCache/v2/ID"}),
+         "/media/drivefsroot/ID",
+         chromeos::MOUNT_TYPE_ARCHIVE,
+         {}});
+  }
+
+  void DoMount() {
+    auto token = StartMount();
+    DispatchMountSuccessEvent(token);
+
+    base::RunLoop run_loop;
+    bootstrap_binding_.set_connection_error_handler(run_loop.QuitClosure());
+    ASSERT_TRUE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
+    run_loop.Run();
+  }
+
+  void Init(mojom::DriveFsConfigurationPtr config,
+            mojom::DriveFsRequest drive_fs,
+            mojom::DriveFsDelegatePtr delegate) override {
+    EXPECT_EQ("test@example.com", config->user_email);
+    binding_.Bind(std::move(drive_fs));
+    delegate_ptr_ = std::move(delegate);
+  }
+
+  base::FilePath profile_path_;
+  base::test::ScopedTaskEnvironment task_environment_;
+  AccountId account_id_;
+  chromeos::disks::MockDiskMountManager* disk_manager_;
+  std::unique_ptr<DriveFsHost> host_;
+
+  mojo::Binding<mojom::DriveFsBootstrap> bootstrap_binding_;
+  mojo::Binding<mojom::DriveFs> binding_;
+  mojom::DriveFsDelegatePtr delegate_ptr_;
+
+  mojom::DriveFsBootstrapPtrInfo pending_bootstrap_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DriveFsHostTest);
+};
+
+TEST_F(DriveFsHostTest, Basic) {
+  ASSERT_NO_FATAL_FAILURE(DoMount());
+}
+
+TEST_F(DriveFsHostTest, UnmountAfterMountComplete) {
+  ASSERT_NO_FATAL_FAILURE(DoMount());
+
+  EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/ID",
+                                          chromeos::UNMOUNT_OPTIONS_NONE, _));
+  base::RunLoop run_loop;
+  binding_.set_connection_error_handler(run_loop.QuitClosure());
+  host_->Unmount();
+  run_loop.Run();
+}
+
+TEST_F(DriveFsHostTest, UnmountBeforeMountEvent) {
+  auto token = StartMount();
+  host_->Unmount();
+  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
+}
+
+TEST_F(DriveFsHostTest, UnmountBeforeMojoConnection) {
+  auto token = StartMount();
+  DispatchMountSuccessEvent(token);
+  EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/ID",
+                                          chromeos::UNMOUNT_OPTIONS_NONE, _));
+
+  host_->Unmount();
+  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
+}
+
+TEST_F(DriveFsHostTest, DestroyBeforeMountEvent) {
+  auto token = StartMount();
+  EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
+
+  host_.reset();
+  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
+}
+
+TEST_F(DriveFsHostTest, DestroyBeforeMojoConnection) {
+  auto token = StartMount();
+  DispatchMountSuccessEvent(token);
+  EXPECT_CALL(*disk_manager_, UnmountPath("/media/drivefsroot/ID",
+                                          chromeos::UNMOUNT_OPTIONS_NONE, _));
+
+  host_.reset();
+  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
+}
+
+TEST_F(DriveFsHostTest, ObserveOtherMount) {
+  auto token = StartMount();
+  EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
+
+  DispatchMountEvent(chromeos::disks::DiskMountManager::MOUNTING,
+                     chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED,
+                     {"some/other/mount/event",
+                      "/some/other/mount/point",
+                      chromeos::MOUNT_TYPE_DEVICE,
+                      {}});
+  DispatchMountEvent(
+      chromeos::disks::DiskMountManager::UNMOUNTING, chromeos::MOUNT_ERROR_NONE,
+      {base::StrCat({"drivefs://", token, "@/path/to/profile/GCache/v2/ID"}),
+       "/media/drivefsroot/ID",
+       chromeos::MOUNT_TYPE_ARCHIVE,
+       {}});
+  host_->Unmount();
+}
+
+TEST_F(DriveFsHostTest, MountError) {
+  auto token = StartMount();
+  EXPECT_CALL(*disk_manager_, UnmountPath(_, _, _)).Times(0);
+
+  DispatchMountEvent(
+      chromeos::disks::DiskMountManager::MOUNTING,
+      chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED,
+      {base::StrCat({"drivefs://", token, "@/path/to/profile/GCache/v2/ID"}),
+       "/media/drivefsroot/ID",
+       chromeos::MOUNT_TYPE_ARCHIVE,
+       {}});
+  EXPECT_FALSE(PendingConnectionManager::Get().OpenIpcChannel(token, {}));
+}
+
+TEST_F(DriveFsHostTest, MountWhileAlreadyMounted) {
+  DoMount();
+  EXPECT_FALSE(host_->Mount());
+}
+
+TEST_F(DriveFsHostTest, NonGaiaAccount) {
+  EXPECT_CALL(*disk_manager_, MountPath(_, _, _, _, _)).Times(0);
+  AccountId non_gaia_account = AccountId::FromUserEmail("test2@example.com");
+  host_ = std::make_unique<DriveFsHost>(profile_path_, non_gaia_account);
+  EXPECT_FALSE(host_->Mount());
+}
+
+TEST_F(DriveFsHostTest, GetAccessToken) {
+  ASSERT_NO_FATAL_FAILURE(DoMount());
+
+  base::RunLoop run_loop;
+  auto quit_closure = run_loop.QuitClosure();
+  delegate_ptr_->GetAccessToken(
+      "client ID", "app ID", {"scope1", "scope2"},
+      base::BindLambdaForTesting(
+          [&](mojom::AccessTokenStatus status, const std::string& token) {
+            EXPECT_EQ(mojom::AccessTokenStatus::kAuthError, status);
+            EXPECT_TRUE(token.empty());
+            std::move(quit_closure).Run();
+          }));
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace drivefs
diff --git a/chromeos/components/drivefs/mojom/BUILD.gn b/chromeos/components/drivefs/mojom/BUILD.gn
new file mode 100644
index 0000000..2ebf047
--- /dev/null
+++ b/chromeos/components/drivefs/mojom/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom_component("mojom") {
+  sources = [
+    "drivefs.mojom",
+  ]
+
+  public_deps = [
+    "//mojo/public/mojom/base",
+  ]
+
+  output_prefix = "drivefs_mojom"
+  macro_prefix = "DRIVEFS_MOJOM"
+}
diff --git a/chromeos/components/drivefs/mojom/OWNERS b/chromeos/components/drivefs/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/chromeos/components/drivefs/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromeos/components/drivefs/mojom/drivefs.mojom b/chromeos/components/drivefs/mojom/drivefs.mojom
new file mode 100644
index 0000000..4f2190a6
--- /dev/null
+++ b/chromeos/components/drivefs/mojom/drivefs.mojom
@@ -0,0 +1,44 @@
+// 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 drivefs.mojom;
+
+// This file tracks platform/drivefs/mojom/drivefs.mojom. Changes should be made
+// there first and then replicated here.
+
+// Implemented by DriveFS, used from Chrome.
+interface DriveFsBootstrap {
+  // Initialize a DriveFS instance with its configuration and mojo connections
+  // to the browser.
+  Init(DriveFsConfiguration config, DriveFs& drive_fs,
+       DriveFsDelegate delegate);
+};
+
+// Implemented by DriveFS, used from Chrome.
+interface DriveFs {
+};
+
+// Implemented by Chrome, used from DriveFS.
+interface DriveFsDelegate {
+  // Get an access token for |client_id| and |app_id| with access to |scopes|.
+  // |access_token| is only valid if |status| is kSuccess.
+  GetAccessToken(string client_id, string app_id, array<string> scopes) => (
+      AccessTokenStatus status, string access_token);
+};
+
+struct DriveFsConfiguration {
+  string user_email;
+};
+
+enum AccessTokenStatus {
+  // Getting an access token succeeded.
+  kSuccess,
+
+  // Getting an access token failed due to a transient error (e.g. network
+  // access is unavailable).
+  kTransientError,
+
+  // Getting an access token failed due to an auth error.
+  kAuthError,
+};
diff --git a/chromeos/components/drivefs/pending_connection_manager.cc b/chromeos/components/drivefs/pending_connection_manager.cc
new file mode 100644
index 0000000..75a9ae9
--- /dev/null
+++ b/chromeos/components/drivefs/pending_connection_manager.cc
@@ -0,0 +1,47 @@
+// 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 "chromeos/components/drivefs/pending_connection_manager.h"
+
+#include <utility>
+
+#include "base/logging.h"
+
+namespace drivefs {
+
+// static
+PendingConnectionManager& PendingConnectionManager::Get() {
+  static base::NoDestructor<PendingConnectionManager> connection_manager;
+  return *connection_manager;
+}
+
+bool PendingConnectionManager::OpenIpcChannel(const std::string& identity,
+                                              base::ScopedFD ipc_channel) {
+  auto it = open_ipc_channel_callbacks_.find(identity);
+  if (it == open_ipc_channel_callbacks_.end()) {
+    return false;
+  }
+  OpenIpcChannelCallback callback = std::move(it->second);
+  open_ipc_channel_callbacks_.erase(it);
+  std::move(callback).Run(std::move(ipc_channel));
+  return true;
+}
+
+void PendingConnectionManager::ExpectOpenIpcChannel(
+    base::UnguessableToken token,
+    OpenIpcChannelCallback handler) {
+  DCHECK(token);
+  open_ipc_channel_callbacks_.emplace(token.ToString(), std::move(handler));
+}
+
+void PendingConnectionManager::CancelExpectedOpenIpcChannel(
+    base::UnguessableToken token) {
+  bool erased = open_ipc_channel_callbacks_.erase(token.ToString());
+  DCHECK_EQ(1u, erased);
+}
+
+PendingConnectionManager::PendingConnectionManager() = default;
+PendingConnectionManager::~PendingConnectionManager() = default;
+
+}  // namespace drivefs
diff --git a/chromeos/components/drivefs/pending_connection_manager.h b/chromeos/components/drivefs/pending_connection_manager.h
new file mode 100644
index 0000000..b223829
--- /dev/null
+++ b/chromeos/components/drivefs/pending_connection_manager.h
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_DRIVEFS_PENDING_CONNECTION_MANAGER_H_
+#define CHROMEOS_COMPONENTS_DRIVEFS_PENDING_CONNECTION_MANAGER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/component_export.h"
+#include "base/containers/flat_map.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "base/unguessable_token.h"
+#include "chromeos/chromeos_export.h"
+
+namespace drivefs {
+
+class PendingConnectionManagerTest;
+
+class COMPONENT_EXPORT(DRIVEFS) PendingConnectionManager {
+ public:
+  using OpenIpcChannelCallback = base::OnceCallback<void(base::ScopedFD)>;
+
+  static PendingConnectionManager& Get();
+
+  bool OpenIpcChannel(const std::string& identity, base::ScopedFD ipc_channel);
+
+  void ExpectOpenIpcChannel(base::UnguessableToken token,
+                            OpenIpcChannelCallback handler);
+  void CancelExpectedOpenIpcChannel(base::UnguessableToken token);
+
+ private:
+  friend class base::NoDestructor<PendingConnectionManager>;
+  friend class PendingConnectionManagerTest;
+
+  PendingConnectionManager();
+  ~PendingConnectionManager();
+
+  base::flat_map<std::string, OpenIpcChannelCallback>
+      open_ipc_channel_callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(PendingConnectionManager);
+};
+
+}  // namespace drivefs
+
+#endif  // CHROMEOS_COMPONENTS_DRIVEFS_PENDING_CONNECTION_MANAGER_H_
diff --git a/chromeos/components/drivefs/pending_connection_manager_unittest.cc b/chromeos/components/drivefs/pending_connection_manager_unittest.cc
new file mode 100644
index 0000000..9fd1defb
--- /dev/null
+++ b/chromeos/components/drivefs/pending_connection_manager_unittest.cc
@@ -0,0 +1,44 @@
+// 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 "chromeos/components/drivefs/pending_connection_manager.h"
+
+#include "base/test/bind_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace drivefs {
+
+class PendingConnectionManagerTest : public testing::Test {
+ protected:
+  PendingConnectionManager connection_manager_;
+};
+
+namespace {
+
+TEST_F(PendingConnectionManagerTest, Basic) {
+  auto token = base::UnguessableToken::Create();
+  int callback_calls = 0;
+  connection_manager_.ExpectOpenIpcChannel(
+      token,
+      base::BindLambdaForTesting([&](base::ScopedFD fd) { callback_calls++; }));
+  EXPECT_TRUE(connection_manager_.OpenIpcChannel(token.ToString(), {}));
+  EXPECT_FALSE(connection_manager_.OpenIpcChannel(token.ToString(), {}));
+  EXPECT_EQ(1, callback_calls);
+}
+
+TEST_F(PendingConnectionManagerTest, Cancel) {
+  auto token = base::UnguessableToken::Create();
+  connection_manager_.ExpectOpenIpcChannel(
+      token, base::BindLambdaForTesting(
+                 [&](base::ScopedFD fd) { FAIL() << "Unexpected call"; }));
+  connection_manager_.CancelExpectedOpenIpcChannel(token);
+  EXPECT_FALSE(connection_manager_.OpenIpcChannel(token.ToString(), {}));
+}
+
+TEST_F(PendingConnectionManagerTest, UnexpectedConnection) {
+  EXPECT_FALSE(connection_manager_.OpenIpcChannel("invalid", {}));
+}
+
+}  // namespace
+}  // namespace drivefs
diff --git a/chromeos/components/run_all_unittests.cc b/chromeos/components/run_all_unittests.cc
index a223cf77..660ab97 100644
--- a/chromeos/components/run_all_unittests.cc
+++ b/chromeos/components/run_all_unittests.cc
@@ -5,8 +5,12 @@
 #include "base/bind.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
+#include "mojo/edk/embedder/embedder.h"
 
 int main(int argc, char** argv) {
+  // Some unit tests make Mojo calls.
+  mojo::edk::Init();
+
   base::TestSuite test_suite(argc, argv);
   return base::LaunchUnitTests(
       argc, argv,
diff --git a/chromeos/disks/disk_mount_manager.h b/chromeos/disks/disk_mount_manager.h
index f45eb463..f8be5ed 100644
--- a/chromeos/disks/disk_mount_manager.h
+++ b/chromeos/disks/disk_mount_manager.h
@@ -250,32 +250,29 @@
   typedef base::Callback<void(bool success)> EnsureMountInfoRefreshedCallback;
 
   // Implement this interface to be notified about disk/mount related events.
-  // TODO(agawronska): Make observer methods non-pure virtual, because
-  // subclasses only use small subset of them.
   class Observer {
    public:
     virtual ~Observer() {}
 
     // Called when auto-mountable disk mount status is changed.
-    virtual void OnAutoMountableDiskEvent(DiskEvent event,
-                                          const Disk& disk) = 0;
+    virtual void OnAutoMountableDiskEvent(DiskEvent event, const Disk& disk) {}
     // Called when fixed storage disk status is changed.
-    virtual void OnBootDeviceDiskEvent(DiskEvent event, const Disk& disk) = 0;
+    virtual void OnBootDeviceDiskEvent(DiskEvent event, const Disk& disk) {}
     // Called when device status is changed.
     virtual void OnDeviceEvent(DeviceEvent event,
-                               const std::string& device_path) = 0;
+                               const std::string& device_path) {}
     // Called after a mount point has been mounted or unmounted.
     virtual void OnMountEvent(MountEvent event,
                               MountError error_code,
-                              const MountPointInfo& mount_info) = 0;
+                              const MountPointInfo& mount_info) {}
     // Called on format process events.
     virtual void OnFormatEvent(FormatEvent event,
                                FormatError error_code,
-                               const std::string& device_path) = 0;
+                               const std::string& device_path) {}
     // Called on rename process events.
     virtual void OnRenameEvent(RenameEvent event,
                                RenameError error_code,
-                               const std::string& device_path) = 0;
+                               const std::string& device_path) {}
   };
 
   virtual ~DiskMountManager() {}
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 23bf42e..a58e5743 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -337,7 +337,7 @@
     deps += [ "//components/safe_browsing/android:unit_tests_mobile" ]
   }
 
-  if (enable_webrtc && !is_ios) {
+  if (!is_ios) {
     deps += [
       "//components/webrtc_logging/browser:unit_tests",
       "//components/webrtc_logging/common:unit_tests",
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 8f5157ab..b5cab99 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1594,7 +1594,7 @@
 
   form->name = GetFormIdentifier(form_element);
   form->origin = GetCanonicalOriginForDocument(frame->GetDocument());
-  form->action = frame->GetDocument().CompleteURL(form_element.Action());
+  form->action = GetCanonicalActionForForm(form_element);
   if (frame->Top()) {
     form->main_frame_origin = frame->Top()->GetSecurityOrigin();
   } else {
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 85fef365..77f5dd83 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -194,6 +194,10 @@
   // features of Autofill are disabled, including Autocomplete.
   virtual bool IsAutofillSupported() = 0;
 
+  // Whether server side cards are supported by the client. If false, only
+  // local cards will be shown.
+  virtual bool AreServerCardsSupported() = 0;
+
   // Handles simple actions for the autofill popups.
   virtual void ExecuteCommand(int id) = 0;
 };
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index f50eccd1..529efe2 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -1590,9 +1590,11 @@
   // data.
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
-          type, SanitizeCreditCardFieldValue(field.value));
+          type, SanitizeCreditCardFieldValue(field.value),
+          client_->AreServerCardsSupported());
   const std::vector<CreditCard*> cards_to_suggest =
-      personal_data_->GetCreditCardsToSuggest();
+      personal_data_->GetCreditCardsToSuggest(
+          client_->AreServerCardsSupported());
   for (const CreditCard* credit_card : cards_to_suggest) {
     if (!credit_card->bank_name().empty()) {
       credit_card_form_event_logger_->SetBankNameAvailable();
@@ -1687,7 +1689,8 @@
   // class' FillCreditCardForm().
   if (autofill_assistant_.CanShowCreditCardAssist(form_structures_)) {
     const std::vector<CreditCard*> cards =
-        personal_data_->GetCreditCardsToSuggest();
+        personal_data_->GetCreditCardsToSuggest(
+            client_->AreServerCardsSupported());
     // Expired cards are last in the sorted order, so if the first one is
     // expired, they all are.
     if (!cards.empty() && !cards.front()->IsExpired(AutofillClock::Now()))
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index f6f95ddd..23c69b4 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -1095,10 +1095,10 @@
 
 // TODO(crbug.com/613187): Investigate if it would be more efficient to dedupe
 // with a vector instead of a list.
-const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest()
-    const {
+const std::vector<CreditCard*> PersonalDataManager::GetCreditCardsToSuggest(
+    bool include_server_cards) const {
   std::vector<CreditCard*> credit_cards;
-  if (ShouldSuggestServerCards()) {
+  if (include_server_cards && ShouldSuggestServerCards()) {
     credit_cards = GetCreditCards();
   } else {
     credit_cards = GetLocalCreditCards();
@@ -1150,10 +1150,12 @@
 
 std::vector<Suggestion> PersonalDataManager::GetCreditCardSuggestions(
     const AutofillType& type,
-    const base::string16& field_contents) {
+    const base::string16& field_contents,
+    bool include_server_cards) {
   if (IsInAutofillSuggestionsDisabledExperiment())
     return std::vector<Suggestion>();
-  std::vector<CreditCard*> cards = GetCreditCardsToSuggest();
+  std::vector<CreditCard*> cards =
+      GetCreditCardsToSuggest(include_server_cards);
   // If enabled, suppress disused address profiles when triggered from an empty
   // field.
   if (field_contents.empty() &&
diff --git a/components/autofill/core/browser/personal_data_manager.h b/components/autofill/core/browser/personal_data_manager.h
index 038c3a3..6eb722b0 100644
--- a/components/autofill/core/browser/personal_data_manager.h
+++ b/components/autofill/core/browser/personal_data_manager.h
@@ -231,8 +231,10 @@
 
   // Returns the credit cards to suggest to the user. Those have been deduped
   // and ordered by frecency with the expired cards put at the end of the
-  // vector.
-  const std::vector<CreditCard*> GetCreditCardsToSuggest() const;
+  // vector. If |include_server_cards| is false, server side cards should not
+  // be included.
+  const std::vector<CreditCard*> GetCreditCardsToSuggest(
+      bool include_server_cards) const;
 
   // Remove credit cards that are expired at |comparison_time| and not used
   // since |min_last_used| from |cards|. The relative ordering of |cards| is
@@ -244,10 +246,12 @@
 
   // Gets credit cards that can suggest data for |type|. See
   // GetProfileSuggestions for argument descriptions. The variant in each
-  // GUID pair should be ignored.
+  // GUID pair should be ignored. If |include_server_cards| is false, server
+  // side cards should not be included.
   std::vector<Suggestion> GetCreditCardSuggestions(
       const AutofillType& type,
-      const base::string16& field_contents);
+      const base::string16& field_contents,
+      bool include_server_cards);
 
   // Tries to delete disused credit cards once per major version if the
   // feature is enabled.
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index acce1f30..1f3b0ff 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -2043,7 +2043,8 @@
 
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(AutofillType(CREDIT_CARD_NUMBER),
-                                               base::ASCIIToUTF16("12345678"));
+                                               base::ASCIIToUTF16("12345678"),
+                                               /*include_server_cards=*/true);
 
   // There should be no suggestions.
   ASSERT_EQ(0U, suggestions.size());
@@ -2058,7 +2059,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME_FULL),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(3U, suggestions.size());
 
   // Ordered as expected.
@@ -2106,7 +2108,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME_FULL),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(5U, suggestions.size());
 
   // All cards should be ordered as expected.
@@ -2155,7 +2158,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME_FULL),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(0U, suggestions.size());
 }
 
@@ -2203,7 +2207,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME_FULL),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(0U, suggestions.size());
 }
 
@@ -2248,7 +2253,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME_FULL),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /* include_server_cards= */ true);
   ASSERT_EQ(3U, suggestions.size());
 
   // The never used non expired card should be suggested first.
@@ -2316,7 +2322,8 @@
 
     std::vector<Suggestion> suggestions =
         personal_data_->GetCreditCardSuggestions(
-            AutofillType(CREDIT_CARD_NAME_FULL), base::string16());
+            AutofillType(CREDIT_CARD_NAME_FULL), base::string16(),
+            /*include_server_cards=*/true);
     EXPECT_EQ(4U, suggestions.size());
     EXPECT_EQ(base::ASCIIToUTF16("Bonnie Parker"), suggestions[0].value);
     EXPECT_EQ(base::ASCIIToUTF16("Clyde Barrow"), suggestions[1].value);
@@ -2332,7 +2339,8 @@
   {
     std::vector<Suggestion> suggestions =
         personal_data_->GetCreditCardSuggestions(
-            AutofillType(CREDIT_CARD_NAME_FULL), base::string16());
+            AutofillType(CREDIT_CARD_NAME_FULL), base::string16(),
+            /*include_server_cards=*/true);
     EXPECT_EQ(2U, suggestions.size());
     EXPECT_EQ(base::ASCIIToUTF16("Bonnie Parker"), suggestions[0].value);
     EXPECT_EQ(base::ASCIIToUTF16("Clyde Barrow"), suggestions[1].value);
@@ -2342,7 +2350,8 @@
   {
     std::vector<Suggestion> suggestions =
         personal_data_->GetCreditCardSuggestions(
-            AutofillType(CREDIT_CARD_NAME_FULL), base::ASCIIToUTF16("B"));
+            AutofillType(CREDIT_CARD_NAME_FULL), base::ASCIIToUTF16("B"),
+            /*include_server_cards=*/true);
 
     ASSERT_EQ(1U, suggestions.size());
     EXPECT_EQ(base::ASCIIToUTF16("Bonnie Parker"), suggestions[0].value);
@@ -2352,7 +2361,8 @@
   {
     std::vector<Suggestion> suggestions =
         personal_data_->GetCreditCardSuggestions(
-            AutofillType(CREDIT_CARD_NAME_FULL), base::ASCIIToUTF16("Cl"));
+            AutofillType(CREDIT_CARD_NAME_FULL), base::ASCIIToUTF16("Cl"),
+            /*include_server_cards=*/true);
 
     ASSERT_EQ(1U, suggestions.size());
     EXPECT_EQ(base::ASCIIToUTF16("Clyde Barrow"), suggestions[0].value);
@@ -2362,7 +2372,8 @@
   {
     std::vector<Suggestion> suggestions =
         personal_data_->GetCreditCardSuggestions(
-            AutofillType(CREDIT_CARD_NAME_FULL), base::ASCIIToUTF16("Jo"));
+            AutofillType(CREDIT_CARD_NAME_FULL), base::ASCIIToUTF16("Jo"),
+            /*include_server_cards=*/true);
 
     ASSERT_EQ(1U, suggestions.size());
     EXPECT_EQ(base::ASCIIToUTF16("John Dillinger"), suggestions[0].value);
@@ -2374,7 +2385,8 @@
   {
     std::vector<Suggestion> suggestions =
         personal_data_->GetCreditCardSuggestions(
-            AutofillType(CREDIT_CARD_NUMBER), base::ASCIIToUTF16("4234"));
+            AutofillType(CREDIT_CARD_NUMBER), base::ASCIIToUTF16("4234"),
+            /*include_server_cards=*/true);
 
     ASSERT_EQ(2U, suggestions.size());
     EXPECT_EQ(
@@ -2420,7 +2432,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NUMBER),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(1U, suggestions.size());
   EXPECT_EQ(
       base::UTF8ToUTF16(std::string("Amex") + kUTF8MidlineEllipsis + "0005"),
@@ -2478,7 +2491,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME_FULL),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(4U, suggestions.size());
   EXPECT_EQ(base::ASCIIToUTF16("John Dillinger"), suggestions[0].value);
   EXPECT_EQ(base::ASCIIToUTF16("Clyde Barrow"), suggestions[1].value);
@@ -2486,7 +2500,8 @@
   EXPECT_EQ(base::ASCIIToUTF16("Bonnie Parker"), suggestions[3].value);
 
   suggestions = personal_data_->GetCreditCardSuggestions(
-      AutofillType(CREDIT_CARD_NUMBER), /* field_contents= */ base::string16());
+      AutofillType(CREDIT_CARD_NUMBER), /* field_contents= */ base::string16(),
+      /*include_server_cards=*/true);
   ASSERT_EQ(4U, suggestions.size());
   EXPECT_EQ(
       base::UTF8ToUTF16(std::string("Visa") + kUTF8MidlineEllipsis + "3456"),
@@ -2527,7 +2542,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NAME_FULL),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(3U, suggestions.size());
 
   // Add a second dupe local card to make sure a full server card can be a dupe
@@ -2541,7 +2557,7 @@
 
   suggestions = personal_data_->GetCreditCardSuggestions(
       AutofillType(CREDIT_CARD_NAME_FULL),
-      /* field_contents= */ base::string16());
+      /* field_contents= */ base::string16(), /*include_server_cards=*/true);
   ASSERT_EQ(3U, suggestions.size());
 }
 
@@ -2597,7 +2613,8 @@
   std::vector<Suggestion> suggestions =
       personal_data_->GetCreditCardSuggestions(
           AutofillType(CREDIT_CARD_NUMBER),
-          /* field_contents= */ base::string16());
+          /* field_contents= */ base::string16(),
+          /*include_server_cards=*/true);
   ASSERT_EQ(3U, suggestions.size());
 
   // Local cards will show network.
@@ -3759,7 +3776,7 @@
   std::vector<AutofillProfile*> profiles =
       personal_data_->GetProfilesToSuggest();
   std::vector<CreditCard*> credit_cards =
-      personal_data_->GetCreditCardsToSuggest();
+      personal_data_->GetCreditCardsToSuggest(/*include_server_cards=*/true);
 
   // |profile1| should have been merged into |profile2| which should then have
   // been merged into |profile3|. |profile4| should have been merged into
@@ -5735,7 +5752,7 @@
   // Check that no server cards are available for suggestion, but that the other
   // calls to get the credit cards are unaffected.
   EXPECT_EQ(3U, personal_data_->GetCreditCards().size());
-  EXPECT_EQ(1U, personal_data_->GetCreditCardsToSuggest().size());
+  EXPECT_EQ(1U, personal_data_->GetCreditCardsToSuggest(true).size());
   EXPECT_EQ(1U, personal_data_->GetLocalCreditCards().size());
   EXPECT_EQ(2U, personal_data_->GetServerCreditCards().size());
 
@@ -5744,7 +5761,21 @@
 
   // Check that all cards are available.
   EXPECT_EQ(3U, personal_data_->GetCreditCards().size());
-  EXPECT_EQ(3U, personal_data_->GetCreditCardsToSuggest().size());
+  EXPECT_EQ(3U, personal_data_->GetCreditCardsToSuggest(true).size());
+  EXPECT_EQ(1U, personal_data_->GetLocalCreditCards().size());
+  EXPECT_EQ(2U, personal_data_->GetServerCreditCards().size());
+}
+
+TEST_F(PersonalDataManagerTest, ExcludeServerSideCards) {
+  SetUpThreeCardTypes();
+
+  // include_server_cards is set to false, therefore no server cards should be
+  // available for suggestion, but that the other calls to get the credit cards
+  // are unaffected.
+  EXPECT_EQ(3U, personal_data_->GetCreditCards().size());
+  EXPECT_EQ(1U, personal_data_
+                    ->GetCreditCardsToSuggest(/*include_server_cards=*/false)
+                    .size());
   EXPECT_EQ(1U, personal_data_->GetLocalCreditCards().size());
   EXPECT_EQ(2U, personal_data_->GetServerCreditCards().size());
 }
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index 9cc6166..7bff6dfa 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -132,4 +132,8 @@
   return true;
 }
 
+bool TestAutofillClient::AreServerCardsSupported() {
+  return true;
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 4ad768b..6475c1c 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -73,6 +73,7 @@
   bool IsContextSecure() override;
   bool ShouldShowSigninPromo() override;
   bool IsAutofillSupported() override;
+  bool AreServerCardsSupported() override;
   void ExecuteCommand(int id) override;
 
   void SetPrefs(std::unique_ptr<PrefService> prefs) {
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index e9ef1f0..f7bf4c7 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -23,6 +23,7 @@
 #include "components/invalidation/public/invalidation_service.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/reading_list/features/reading_list_buildflags.h"
+#include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_metrics.h"
@@ -46,8 +47,10 @@
 #include "components/sync/driver/user_selectable_sync_type.h"
 #include "components/sync/engine/configure_reason.h"
 #include "components/sync/engine/cycle/type_debug_info_observer.h"
+#include "components/sync/engine/engine_components_factory_impl.h"
 #include "components/sync/engine/net/http_bridge_network_resources.h"
 #include "components/sync/engine/net/network_resources.h"
+#include "components/sync/engine/polling_constants.h"
 #include "components/sync/engine/sync_encryption_handler.h"
 #include "components/sync/engine/sync_string_conversions.h"
 #include "components/sync/js/js_event_details.h"
@@ -66,6 +69,7 @@
 #include "components/version_info/version_info_values.h"
 #include "google_apis/gaia/gaia_constants.h"
 #include "net/url_request/url_request_context_getter.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
 
 using sync_sessions::SessionsSyncManager;
@@ -75,6 +79,8 @@
 using syncer::DataTypeManager;
 using syncer::DataTypeStatusTable;
 using syncer::DeviceInfoSyncBridge;
+using syncer::EngineComponentsFactory;
+using syncer::EngineComponentsFactoryImpl;
 using syncer::JsBackend;
 using syncer::JsController;
 using syncer::JsEventHandler;
@@ -83,8 +89,11 @@
 using syncer::ModelTypeSet;
 using syncer::ModelTypeStore;
 using syncer::ProtocolEventObserver;
-using syncer::SyncEngine;
+using syncer::SyncBackendRegistrar;
+using syncer::SyncClient;
 using syncer::SyncCredentials;
+using syncer::SyncEngine;
+using syncer::SyncManagerFactory;
 using syncer::SyncProtocolError;
 using syncer::WeakHandle;
 
@@ -92,11 +101,11 @@
 
 namespace {
 
-const char kSyncOAuthConsumerName[] = "sync";
+constexpr char kSyncOAuthConsumerName[] = "sync";
 
-const char kSyncUnrecoverableErrorHistogram[] = "Sync.UnrecoverableErrors";
+constexpr char kSyncUnrecoverableErrorHistogram[] = "Sync.UnrecoverableErrors";
 
-const net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
+constexpr net::BackoffEntry::Policy kRequestAccessTokenBackoffPolicy = {
     // Number of initial errors (in sequence) to ignore before applying
     // exponential back-off rules.
     0,
@@ -124,6 +133,43 @@
     false,
 };
 
+constexpr base::FilePath::CharType kSyncDataFolderName[] =
+    FILE_PATH_LITERAL("Sync Data");
+
+constexpr base::FilePath::CharType kLevelDBFolderName[] =
+    FILE_PATH_LITERAL("LevelDB");
+
+base::FilePath FormatSyncDataPath(const base::FilePath& base_directory) {
+  return base_directory.Append(base::FilePath(kSyncDataFolderName));
+}
+
+base::FilePath FormatSharedModelTypeStorePath(
+    const base::FilePath& base_directory) {
+  return FormatSyncDataPath(base_directory)
+      .Append(base::FilePath(kLevelDBFolderName));
+}
+
+EngineComponentsFactory::Switches EngineSwitchesFromCommandLine() {
+  EngineComponentsFactory::Switches factory_switches = {
+      EngineComponentsFactory::ENCRYPTION_KEYSTORE,
+      EngineComponentsFactory::BACKOFF_NORMAL};
+
+  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
+  if (cl->HasSwitch(switches::kSyncShortInitialRetryOverride)) {
+    factory_switches.backoff_override =
+        EngineComponentsFactory::BACKOFF_SHORT_INITIAL_RETRY_OVERRIDE;
+  }
+  if (cl->HasSwitch(switches::kSyncEnableGetUpdateAvoidance)) {
+    factory_switches.pre_commit_updates_policy =
+        EngineComponentsFactory::FORCE_ENABLE_PRE_COMMIT_UPDATE_AVOIDANCE;
+  }
+  if (cl->HasSwitch(switches::kSyncShortNudgeDelayForTest)) {
+    factory_switches.nudge_delay =
+        EngineComponentsFactory::NudgeDelay::SHORT_NUDGE_DELAY;
+  }
+  return factory_switches;
+}
+
 }  // namespace
 
 ProfileSyncService::InitParams::InitParams() = default;
@@ -131,11 +177,12 @@
 ProfileSyncService::InitParams::~InitParams() = default;
 
 ProfileSyncService::ProfileSyncService(InitParams init_params)
-    : SyncServiceBase(std::move(init_params.sync_client),
-                      std::move(init_params.signin_wrapper),
-                      init_params.channel,
-                      init_params.base_directory,
-                      init_params.debug_identifier),
+    : sync_client_(std::move(init_params.sync_client)),
+      signin_(std::move(init_params.signin_wrapper)),
+      channel_(init_params.channel),
+      base_directory_(init_params.base_directory),
+      debug_identifier_(init_params.debug_identifier),
+      sync_prefs_(sync_client_->GetPrefService()),
       signin_scoped_device_id_callback_(
           init_params.signin_scoped_device_id_callback),
       last_auth_error_(GoogleServiceAuthError::AuthErrorNone()),
@@ -151,7 +198,6 @@
       is_auth_in_progress_(false),
       unrecoverable_error_reason_(ERROR_REASON_UNSET),
       expect_sync_configuration_aborted_(false),
-      configure_status_(DataTypeManager::UNKNOWN),
       oauth2_token_service_(init_params.oauth2_token_service),
       request_access_token_backoff_(&kRequestAccessTokenBackoffPolicy),
       gaia_cookie_manager_service_(init_params.gaia_cookie_manager_service),
@@ -161,9 +207,12 @@
       passphrase_prompt_triggered_by_version_(false),
       sync_enabled_weak_factory_(this),
       weak_factory_(this) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(signin_scoped_device_id_callback_);
   DCHECK(sync_client_);
+
+  ResetCryptoState();
+
   std::string last_version = sync_prefs_.GetLastRunVersion();
   std::string current_version = PRODUCT_VERSION;
   sync_prefs_.SetLastRunVersion(current_version);
@@ -183,7 +232,7 @@
 }
 
 ProfileSyncService::~ProfileSyncService() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (gaia_cookie_manager_service_)
     gaia_cookie_manager_service_->RemoveObserver(this);
   sync_prefs_.RemoveSyncPrefObserver(this);
@@ -192,13 +241,13 @@
 }
 
 bool ProfileSyncService::CanSyncStart() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return (IsSyncAllowed() && IsSyncRequested() &&
           (IsLocalSyncEnabled() || IsSignedIn()));
 }
 
 void ProfileSyncService::Initialize() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   sync_client_->Initialize();
 
   // We don't pass StartupController an Unretained reference to future-proof
@@ -342,14 +391,14 @@
 }
 
 void ProfileSyncService::RegisterAuthNotifications() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   oauth2_token_service_->AddObserver(this);
   if (signin_)
     signin_->GetIdentityManager()->AddObserver(this);
 }
 
 void ProfileSyncService::UnregisterAuthNotifications() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (signin_)
     signin_->GetIdentityManager()->RemoveObserver(this);
   if (oauth2_token_service_)
@@ -358,7 +407,7 @@
 
 void ProfileSyncService::RegisterDataTypeController(
     std::unique_ptr<DataTypeController> data_type_controller) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U);
   data_type_controllers_[data_type_controller->type()] =
       std::move(data_type_controller);
@@ -366,7 +415,7 @@
 
 bool ProfileSyncService::IsDataTypeControllerRunning(
     syncer::ModelType type) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DataTypeController::TypeMap::const_iterator iter =
       data_type_controllers_.find(type);
   if (iter == data_type_controllers_.end()) {
@@ -376,7 +425,7 @@
 }
 
 sync_sessions::OpenTabsUIDelegate* ProfileSyncService::GetOpenTabsUIDelegate() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Although the backing data actually is of type |SESSIONS|, the desire to use
   // open tabs functionality is tracked by the state of the |PROXY_TABS| type.
   if (!IsDataTypeControllerRunning(syncer::PROXY_TABS)) {
@@ -388,24 +437,24 @@
 }
 
 sync_sessions::FaviconCache* ProfileSyncService::GetFaviconCache() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sessions_sync_manager_->GetFaviconCache();
 }
 
 syncer::DeviceInfoTracker* ProfileSyncService::GetDeviceInfoTracker() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return device_info_sync_bridge_.get();
 }
 
 syncer::LocalDeviceInfoProvider*
 ProfileSyncService::GetLocalDeviceInfoProvider() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return local_device_.get();
 }
 
 void ProfileSyncService::GetDataTypeControllerStates(
     DataTypeController::StateMap* state_map) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   for (const auto& item : data_type_controllers_) {
     const syncer::ModelType type = item.first;
     const std::unique_ptr<DataTypeController>& controller = item.second;
@@ -414,7 +463,7 @@
 }
 
 void ProfileSyncService::OnSessionRestoreComplete() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DataTypeController::TypeMap::const_iterator iter =
       data_type_controllers_.find(syncer::SESSIONS);
   if (iter == data_type_controllers_.end()) {
@@ -437,9 +486,10 @@
   if (IsLocalSyncEnabled())
     return credentials;
 
-  credentials.account_id = signin_->GetAccountIdToUse();
+  const AccountInfo account_info = GetAuthenticatedAccountInfo();
+  credentials.account_id = account_info.account_id;
   DCHECK(!credentials.account_id.empty());
-  credentials.email = signin_->GetEffectiveUsername();
+  credentials.email = account_info.email;
   credentials.sync_token = access_token_;
 
   if (credentials.sync_token.empty())
@@ -468,6 +518,15 @@
   return syncer::MakeWeakHandle(sync_enabled_weak_factory_.GetWeakPtr());
 }
 
+void ProfileSyncService::ResetCryptoState() {
+  crypto_ = std::make_unique<syncer::SyncServiceCrypto>(
+      base::BindRepeating(&ProfileSyncService::NotifyObservers,
+                          base::Unretained(this)),
+      base::BindRepeating(&ProfileSyncService::GetPreferredDataTypes,
+                          base::Unretained(this)),
+      &sync_prefs_);
+}
+
 bool ProfileSyncService::IsEncryptedDatatypeEnabled() const {
   if (encryption_pending())
     return true;
@@ -478,7 +537,7 @@
 }
 
 void ProfileSyncService::OnProtocolEvent(const syncer::ProtocolEvent& event) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   for (auto& observer : protocol_event_observers_)
     observer.OnProtocolEvent(event);
 }
@@ -486,7 +545,7 @@
 void ProfileSyncService::OnDirectoryTypeCommitCounterUpdated(
     syncer::ModelType type,
     const syncer::CommitCounters& counters) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   for (auto& observer : type_debug_info_observers_)
     observer.OnCommitCountersUpdated(type, counters);
 }
@@ -494,7 +553,7 @@
 void ProfileSyncService::OnDirectoryTypeUpdateCounterUpdated(
     syncer::ModelType type,
     const syncer::UpdateCounters& counters) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   for (auto& observer : type_debug_info_observers_)
     observer.OnUpdateCountersUpdated(type, counters);
 }
@@ -502,13 +561,13 @@
 void ProfileSyncService::OnDatatypeStatusCounterUpdated(
     syncer::ModelType type,
     const syncer::StatusCounters& counters) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   for (auto& observer : type_debug_info_observers_)
     observer.OnStatusCountersUpdated(type, counters);
 }
 
 void ProfileSyncService::OnDataTypeRequestsSyncStartup(syncer::ModelType type) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(syncer::UserTypes().Has(type));
 
   if (!GetPreferredDataTypes().Has(type)) {
@@ -557,9 +616,70 @@
   ReportPreviousSessionMemoryWarningCount();
 }
 
+void ProfileSyncService::InitializeEngine() {
+  DCHECK(engine_);
+
+  if (!sync_thread_) {
+    sync_thread_ = std::make_unique<base::Thread>("Chrome_SyncThread");
+    base::Thread::Options options;
+    options.timer_slack = base::TIMER_SLACK_MAXIMUM;
+    bool success = sync_thread_->StartWithOptions(options);
+    DCHECK(success);
+  }
+
+  SyncEngine::InitParams params;
+  params.sync_task_runner = sync_thread_->task_runner();
+  params.host = this;
+  params.registrar = std::make_unique<SyncBackendRegistrar>(
+      debug_identifier_,
+      base::BindRepeating(&SyncClient::CreateModelWorkerForGroup,
+                          base::Unretained(sync_client_.get())));
+  params.encryption_observer_proxy = crypto_->GetEncryptionObserverProxy();
+  params.extensions_activity = sync_client_->GetExtensionsActivity();
+  params.event_handler = GetJsEventHandler();
+  params.service_url = sync_service_url();
+  params.sync_user_agent = GetLocalDeviceInfoProvider()->GetSyncUserAgent();
+  params.http_factory_getter = MakeHttpPostProviderFactoryGetter();
+  params.credentials = GetCredentials();
+  invalidation::InvalidationService* invalidator =
+      sync_client_->GetInvalidationService();
+  params.invalidator_client_id =
+      invalidator ? invalidator->GetInvalidatorClientId() : "",
+  params.sync_manager_factory = std::make_unique<SyncManagerFactory>();
+  // The first time we start up the engine we want to ensure we have a clean
+  // directory, so delete any old one that might be there.
+  params.delete_sync_data_folder = !IsFirstSetupComplete();
+  params.enable_local_sync_backend = sync_prefs_.IsLocalSyncEnabled();
+  params.local_sync_backend_folder = sync_client_->GetLocalSyncBackendFolder();
+  params.restored_key_for_bootstrapping =
+      sync_prefs_.GetEncryptionBootstrapToken();
+  params.restored_keystore_key_for_bootstrapping =
+      sync_prefs_.GetKeystoreEncryptionBootstrapToken();
+  params.engine_components_factory =
+      std::make_unique<EngineComponentsFactoryImpl>(
+          EngineSwitchesFromCommandLine());
+  params.unrecoverable_error_handler = GetUnrecoverableErrorHandler();
+  params.report_unrecoverable_error_function =
+      base::BindRepeating(syncer::ReportUnrecoverableError, channel_);
+  params.saved_nigori_state = crypto_->TakeSavedNigoriState();
+  sync_prefs_.GetInvalidationVersions(&params.invalidation_versions);
+  params.short_poll_interval = sync_prefs_.GetShortPollInterval();
+  if (params.short_poll_interval.is_zero()) {
+    params.short_poll_interval =
+        base::TimeDelta::FromSeconds(syncer::kDefaultShortPollIntervalSeconds);
+  }
+  params.long_poll_interval = sync_prefs_.GetLongPollInterval();
+  if (params.long_poll_interval.is_zero()) {
+    params.long_poll_interval =
+        base::TimeDelta::FromSeconds(syncer::kDefaultLongPollIntervalSeconds);
+  }
+
+  engine_->Initialize(std::move(params));
+}
+
 void ProfileSyncService::AccessTokenFetched(const GoogleServiceAuthError& error,
                                             const std::string& access_token) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(ongoing_access_token_fetch_);
 
   std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> fetcher_deleter(
@@ -615,14 +735,14 @@
 
 void ProfileSyncService::OnRefreshTokenAvailable(
     const std::string& account_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (account_id == signin_->GetAccountIdToUse())
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (account_id == GetAuthenticatedAccountInfo().account_id)
     OnRefreshTokensLoaded();
 }
 
 void ProfileSyncService::OnRefreshTokenRevoked(const std::string& account_id) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (account_id == signin_->GetAccountIdToUse()) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (account_id == GetAuthenticatedAccountInfo().account_id) {
     access_token_.clear();
     UpdateAuthErrorState(
         GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED));
@@ -630,10 +750,10 @@
 }
 
 void ProfileSyncService::OnRefreshTokensLoaded() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  GoogleServiceAuthError token_error =
-      oauth2_token_service_->GetAuthError(signin_->GetAccountIdToUse());
+  GoogleServiceAuthError token_error = oauth2_token_service_->GetAuthError(
+      GetAuthenticatedAccountInfo().account_id);
   if (token_error == GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
                          GoogleServiceAuthError::InvalidGaiaCredentialsReason::
                              CREDENTIALS_REJECTED_BY_CLIENT)) {
@@ -668,7 +788,7 @@
 }
 
 void ProfileSyncService::Shutdown() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   UnregisterAuthNotifications();
 
   ShutdownImpl(syncer::BROWSER_SHUTDOWN);
@@ -778,12 +898,12 @@
 }
 
 bool ProfileSyncService::IsFirstSetupComplete() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sync_prefs_.IsFirstSetupComplete();
 }
 
 void ProfileSyncService::SetFirstSetupComplete() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   sync_prefs_.SetFirstSetupComplete();
   if (IsEngineInitialized()) {
     ReconfigureDatatypeManager();
@@ -791,7 +911,7 @@
 }
 
 bool ProfileSyncService::IsSyncConfirmationNeeded() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return (!IsLocalSyncEnabled() && IsSignedIn()) && !IsFirstSetupInProgress() &&
          !IsFirstSetupComplete() && IsSyncRequested();
 }
@@ -800,6 +920,12 @@
   sync_prefs_.SetLastSyncedTime(base::Time::Now());
 }
 
+void ProfileSyncService::NotifyObservers() {
+  for (auto& observer : observers_) {
+    observer.OnStateChanged(this);
+  }
+}
+
 void ProfileSyncService::NotifySyncCycleCompleted() {
   for (auto& observer : observers_)
     observer.OnSyncCycleCompleted(this);
@@ -833,7 +959,7 @@
 // to do as little work as possible, to avoid further corruption or crashes.
 void ProfileSyncService::OnUnrecoverableError(const base::Location& from_here,
                                               const std::string& message) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Unrecoverable errors that arrive via the syncer::UnrecoverableErrorHandler
   // interface are assumed to originate within the syncer.
   unrecoverable_error_reason_ = ERROR_REASON_SYNCER;
@@ -862,7 +988,7 @@
 }
 
 void ProfileSyncService::ReenableDatatype(syncer::ModelType type) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!engine_initialized_ || !data_type_manager_)
     return;
   data_type_manager_->ReenableType(type);
@@ -894,7 +1020,7 @@
         debug_info_listener,
     const std::string& cache_guid,
     bool success) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   UpdateEngineInitUMA(success);
 
   if (!success) {
@@ -918,7 +1044,6 @@
   engine_initialized_ = true;
 
   sync_js_controller_.AttachJsBackend(js_backend);
-  debug_info_listener_ = debug_info_listener;
 
   // Initialize local device info.
   local_device_->Initialize(cache_guid,
@@ -941,7 +1066,7 @@
 
   data_type_manager_.reset(
       sync_client_->GetSyncApiComponentFactory()->CreateDataTypeManager(
-          initial_types, debug_info_listener_, &data_type_controllers_, this,
+          initial_types, debug_info_listener, &data_type_controllers_, this,
           engine_.get(), this));
 
   crypto_->SetSyncEngine(engine_.get());
@@ -974,7 +1099,7 @@
 
 void ProfileSyncService::OnSyncCycleCompleted(
     const syncer::SyncCycleSnapshot& snapshot) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   last_snapshot_ = snapshot;
 
@@ -1001,7 +1126,7 @@
 
 void ProfileSyncService::OnExperimentsChanged(
     const syncer::Experiments& experiments) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (current_experiments_.Matches(experiments))
     return;
 
@@ -1046,7 +1171,7 @@
 
 void ProfileSyncService::OnConnectionStatusChange(
     syncer::ConnectionStatus status) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   token_status_.connection_status_update_time = base::Time::Now();
   token_status_.connection_status = status;
   if (status == syncer::CONNECTION_AUTH_ERROR) {
@@ -1107,7 +1232,7 @@
 }
 
 void ProfileSyncService::OnMigrationNeededForTypes(syncer::ModelTypeSet types) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(engine_initialized_);
   DCHECK(data_type_manager_);
 
@@ -1117,7 +1242,7 @@
 }
 
 void ProfileSyncService::OnActionableError(const SyncProtocolError& error) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   last_actionable_error_ = error;
   DCHECK_NE(last_actionable_error_.action, syncer::UNKNOWN_ACTION);
   switch (error.action) {
@@ -1174,7 +1299,7 @@
 }
 
 void ProfileSyncService::ClearAndRestartSyncForPassphraseEncryption() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   engine_->ClearServerData(
       base::BindRepeating(&ProfileSyncService::OnClearServerDataDone,
                           sync_enabled_weak_factory_.GetWeakPtr()));
@@ -1198,7 +1323,7 @@
 }
 
 void ProfileSyncService::ClearServerDataForTest(const base::Closure& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // Sync has a restriction that the engine must be in configuration mode
   // in order to run clear server data.
   engine_->StartConfiguration();
@@ -1207,12 +1332,11 @@
 
 void ProfileSyncService::OnConfigureDone(
     const DataTypeManager::ConfigureResult& result) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  configure_status_ = result.status;
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   data_type_status_table_ = result.data_type_status_table;
 
   if (!sync_configure_start_time_.is_null()) {
-    if (configure_status_ == DataTypeManager::OK) {
+    if (result.status == DataTypeManager::OK) {
       base::Time sync_configure_stop_time = base::Time::Now();
       base::TimeDelta delta =
           sync_configure_stop_time - sync_configure_start_time_;
@@ -1229,7 +1353,7 @@
   for (auto& observer : observers_)
     observer.OnSyncConfigurationCompleted(this);
 
-  DVLOG(1) << "PSS OnConfigureDone called with status: " << configure_status_;
+  DVLOG(1) << "PSS OnConfigureDone called with status: " << result.status;
   // The possible status values:
   //    ABORT - Configuration was aborted. This is not an error, if
   //            initiated by user.
@@ -1237,7 +1361,7 @@
   //    Everything else is an UnrecoverableError. So treat it as such.
 
   // First handle the abort case.
-  if (configure_status_ == DataTypeManager::ABORTED &&
+  if (result.status == DataTypeManager::ABORTED &&
       expect_sync_configuration_aborted_) {
     DVLOG(0) << "ProfileSyncService::Observe Sync Configure aborted";
     expect_sync_configuration_aborted_ = false;
@@ -1245,7 +1369,7 @@
   }
 
   // Handle unrecoverable error.
-  if (configure_status_ != DataTypeManager::OK) {
+  if (result.status != DataTypeManager::OK) {
     if (result.was_catch_up_configure) {
       // Record catchup configuration failure.
       UMA_HISTOGRAM_ENUMERATION("Sync.ClearServerDataEvents",
@@ -1258,7 +1382,7 @@
     DCHECK(error.IsSet());
     std::string message =
         "Sync configuration failed with status " +
-        DataTypeManager::ConfigureStatusToString(configure_status_) +
+        DataTypeManager::ConfigureStatusToString(result.status) +
         " caused by " +
         syncer::ModelTypeSetToString(
             data_type_status_table_.GetUnrecoverableErrorTypes()) +
@@ -1269,7 +1393,7 @@
     return;
   }
 
-  DCHECK_EQ(DataTypeManager::OK, configure_status_);
+  DCHECK_EQ(DataTypeManager::OK, result.status);
 
   // We should never get in a state where we have no encrypted datatypes
   // enabled, and yet we still think we require a passphrase for decryption.
@@ -1301,7 +1425,7 @@
 }
 
 void ProfileSyncService::OnConfigureStart() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   sync_configure_start_time_ = base::Time::Now();
   engine_->StartConfiguration();
   NotifyObservers();
@@ -1309,7 +1433,7 @@
 
 ProfileSyncService::SyncStatusSummary
 ProfileSyncService::QuerySyncStatusSummary() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (HasUnrecoverableError())
     return UNRECOVERABLE_ERROR;
   if (!engine_)
@@ -1326,15 +1450,9 @@
 }
 
 std::string ProfileSyncService::QuerySyncStatusSummaryString() {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  SyncStatusSummary status = QuerySyncStatusSummary();
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  std::string config_status_str =
-      configure_status_ != DataTypeManager::UNKNOWN
-          ? DataTypeManager::ConfigureStatusToString(configure_status_)
-          : "";
-
-  switch (status) {
+  switch (QuerySyncStatusSummary()) {
     case UNRECOVERABLE_ERROR:
       return "Unrecoverable error detected";
     case NOT_ENABLED:
@@ -1351,17 +1469,17 @@
 }
 
 std::string ProfileSyncService::GetEngineInitializationStateString() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return startup_controller_->GetEngineInitializationStateString();
 }
 
 bool ProfileSyncService::IsSetupInProgress() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return startup_controller_->IsSetupInProgress();
 }
 
 bool ProfileSyncService::QueryDetailedSyncStatus(SyncEngine::Status* result) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (engine_ && engine_initialized_) {
     *result = engine_->GetDetailedStatus();
     return true;
@@ -1373,7 +1491,7 @@
 }
 
 const GoogleServiceAuthError& ProfileSyncService::GetAuthError() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return last_auth_error_;
 }
 
@@ -1382,13 +1500,13 @@
 }
 
 bool ProfileSyncService::IsFirstSetupInProgress() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return !IsFirstSetupComplete() && startup_controller_->IsSetupInProgress();
 }
 
 std::unique_ptr<syncer::SyncSetupInProgressHandle>
 ProfileSyncService::GetSetupInProgressHandle() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (++outstanding_setup_in_progress_handles_ == 1) {
     DCHECK(!startup_controller_->IsSetupInProgress());
@@ -1403,31 +1521,31 @@
 }
 
 bool ProfileSyncService::IsSyncAllowed() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return IsSyncAllowedByFlag() && !IsManaged() && IsSyncAllowedByPlatform();
 }
 
 bool ProfileSyncService::IsSyncActive() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return engine_initialized_ && data_type_manager_ &&
          data_type_manager_->state() != DataTypeManager::STOPPED;
 }
 
 bool ProfileSyncService::IsLocalSyncEnabled() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sync_prefs_.IsLocalSyncEnabled();
 }
 
 // TODO(tschumann): This is only called for tests. Add ForTesting name suffix.
 void ProfileSyncService::TriggerRefresh(const syncer::ModelTypeSet& types) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (engine_initialized_)
     engine_->TriggerRefresh(types);
 }
 
 bool ProfileSyncService::IsSignedIn() const {
-  // Sync is logged in if there is a non-empty effective account id.
-  return !signin_->GetAccountIdToUse().empty();
+  // Sync is logged in if there is a non-empty account id.
+  return !GetAuthenticatedAccountInfo().account_id.empty();
 }
 
 bool ProfileSyncService::CanEngineStart() const {
@@ -1435,38 +1553,38 @@
     return true;
   return CanSyncStart() && oauth2_token_service_ &&
          oauth2_token_service_->RefreshTokenIsAvailable(
-             signin_->GetAccountIdToUse());
+             GetAuthenticatedAccountInfo().account_id);
 }
 
 bool ProfileSyncService::IsEngineInitialized() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return engine_initialized_;
 }
 
 bool ProfileSyncService::ConfigurationDone() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return data_type_manager_ &&
          data_type_manager_->state() == DataTypeManager::CONFIGURED;
 }
 
 bool ProfileSyncService::waiting_for_auth() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return is_auth_in_progress_;
 }
 
 bool ProfileSyncService::HasUnrecoverableError() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return unrecoverable_error_reason_ != ERROR_REASON_UNSET;
 }
 
 bool ProfileSyncService::IsPassphraseRequired() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return crypto_->passphrase_required_reason() !=
          syncer::REASON_PASSPHRASE_NOT_REQUIRED;
 }
 
 bool ProfileSyncService::IsPassphraseRequiredForDecryption() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // If there is an encrypted datatype enabled and we don't have the proper
   // passphrase, we must prompt the user for a passphrase. The only way for the
   // user to avoid entering their passphrase is to disable the encrypted types.
@@ -1534,7 +1652,7 @@
 void ProfileSyncService::OnUserChoseDatatypes(
     bool sync_everything,
     syncer::ModelTypeSet chosen_types) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(syncer::UserSelectableTypes().HasAll(chosen_types));
 
   if (!engine_ && !HasUnrecoverableError()) {
@@ -1551,7 +1669,7 @@
 }
 
 void ProfileSyncService::OnUserChangedSyncEverythingOnly(bool sync_everything) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!engine_ && !HasUnrecoverableError()) {
     NOTREACHED();
     return;
@@ -1566,7 +1684,7 @@
 
 void ProfileSyncService::ChangePreferredDataTypes(
     syncer::ModelTypeSet preferred_types) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DVLOG(1) << "ChangePreferredDataTypes invoked";
   const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
   // Will only enable those types that are registered and preferred.
@@ -1577,19 +1695,35 @@
 }
 
 syncer::ModelTypeSet ProfileSyncService::GetActiveDataTypes() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!data_type_manager_)
     return syncer::ModelTypeSet();
   return data_type_manager_->GetActiveDataTypes();
 }
 
 syncer::SyncClient* ProfileSyncService::GetSyncClient() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sync_client_.get();
 }
 
+void ProfileSyncService::AddObserver(syncer::SyncServiceObserver* observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  observers_.AddObserver(observer);
+}
+
+void ProfileSyncService::RemoveObserver(syncer::SyncServiceObserver* observer) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  observers_.RemoveObserver(observer);
+}
+
+bool ProfileSyncService::HasObserver(
+    const syncer::SyncServiceObserver* observer) const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return observers_.HasObserver(observer);
+}
+
 syncer::ModelTypeSet ProfileSyncService::GetPreferredDataTypes() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   const syncer::ModelTypeSet registered_types = GetRegisteredDataTypes();
   const syncer::ModelTypeSet preferred_types =
       Union(sync_prefs_.GetPreferredDataTypes(registered_types),
@@ -1600,7 +1734,7 @@
 }
 
 syncer::ModelTypeSet ProfileSyncService::GetForcedDataTypes() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // TODO(treib,zea): When SyncPrefs also implements SyncTypePreferenceProvider,
   // we'll need another way to distinguish user-choosable types from
   // programmatically-enabled types.
@@ -1608,7 +1742,7 @@
 }
 
 syncer::ModelTypeSet ProfileSyncService::GetRegisteredDataTypes() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   syncer::ModelTypeSet registered_types;
   // The data_type_controllers_ are determined by command-line flags;
   // that's effectively what controls the values returned here.
@@ -1621,12 +1755,12 @@
 }
 
 bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return crypto_->IsUsingSecondaryPassphrase();
 }
 
 std::string ProfileSyncService::GetCustomPassphraseKey() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   syncer::SystemEncryptor encryptor;
   syncer::Cryptographer cryptographer(&encryptor);
   cryptographer.Bootstrap(sync_prefs_.GetEncryptionBootstrapToken());
@@ -1634,24 +1768,24 @@
 }
 
 syncer::PassphraseType ProfileSyncService::GetPassphraseType() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return crypto_->GetPassphraseType();
 }
 
 base::Time ProfileSyncService::GetExplicitPassphraseTime() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return crypto_->GetExplicitPassphraseTime();
 }
 
 bool ProfileSyncService::IsCryptographerReady(
     const syncer::BaseTransaction* trans) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return engine_ && engine_->IsCryptographerReady(trans);
 }
 
 void ProfileSyncService::SetPlatformSyncAllowedProvider(
     const PlatformSyncAllowedProvider& platform_sync_allowed_provider) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   platform_sync_allowed_provider_ = platform_sync_allowed_provider;
 }
 
@@ -1708,7 +1842,7 @@
 }
 
 syncer::UserShare* ProfileSyncService::GetUserShare() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (engine_ && engine_initialized_) {
     return engine_->GetUserShare();
   }
@@ -1717,35 +1851,25 @@
 }
 
 syncer::SyncCycleSnapshot ProfileSyncService::GetLastCycleSnapshot() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return last_snapshot_;
 }
 
 void ProfileSyncService::HasUnsyncedItemsForTest(
     base::OnceCallback<void(bool)> cb) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(HasSyncingEngine());
   DCHECK(engine_initialized_);
   engine_->HasUnsyncedItemsForTest(std::move(cb));
 }
 
 BackendMigrator* ProfileSyncService::GetBackendMigratorForTest() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return migrator_.get();
 }
 
-void ProfileSyncService::GetModelSafeRoutingInfo(
-    syncer::ModelSafeRoutingInfo* out) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  if (engine_ && engine_initialized_) {
-    engine_->GetModelSafeRoutingInfo(out);
-  } else {
-    NOTREACHED();
-  }
-}
-
 std::unique_ptr<base::Value> ProfileSyncService::GetTypeStatusMap() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   auto result = std::make_unique<base::ListValue>();
 
   if (!engine_ || !engine_initialized_) {
@@ -1838,10 +1962,9 @@
 
   // Invalidate previous token, otherwise token service will return the same
   // token again.
-  const std::string& account_id = signin_->GetAccountIdToUse();
   if (!access_token_.empty()) {
-    oauth2_token_service_->InvalidateAccessToken(account_id, oauth2_scopes,
-                                                 access_token_);
+    signin_->GetIdentityManager()->RemoveAccessTokenFromCache(
+        GetAuthenticatedAccountInfo(), oauth2_scopes, access_token_);
   }
 
   access_token_.clear();
@@ -1850,9 +1973,8 @@
   token_status_.token_receive_time = base::Time();
   token_status_.next_token_request_time = base::Time();
   ongoing_access_token_fetch_ =
-      std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
-          kSyncOAuthConsumerName, signin_->GetSigninManager(),
-          oauth2_token_service_, oauth2_scopes,
+      signin_->GetIdentityManager()->CreateAccessTokenFetcherForPrimaryAccount(
+          kSyncOAuthConsumerName, oauth2_scopes,
           base::BindOnce(&ProfileSyncService::AccessTokenFetched,
                          base::Unretained(this)),
           identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
@@ -1860,13 +1982,13 @@
 
 void ProfileSyncService::SetEncryptionPassphrase(const std::string& passphrase,
                                                  PassphraseType type) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   crypto_->SetEncryptionPassphrase(passphrase, type == EXPLICIT);
 }
 
 bool ProfileSyncService::SetDecryptionPassphrase(
     const std::string& passphrase) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (IsPassphraseRequired()) {
     DVLOG(1) << "Setting passphrase for decryption.";
     bool result = crypto_->SetDecryptionPassphrase(passphrase);
@@ -1879,22 +2001,22 @@
 }
 
 bool ProfileSyncService::IsEncryptEverythingAllowed() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return crypto_->IsEncryptEverythingAllowed();
 }
 
 void ProfileSyncService::SetEncryptEverythingAllowed(bool allowed) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   crypto_->SetEncryptEverythingAllowed(allowed);
 }
 
 void ProfileSyncService::EnableEncryptEverything() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   crypto_->EnableEncryptEverything();
 }
 
 bool ProfileSyncService::encryption_pending() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // We may be called during the setup process before we're
   // initialized (via IsEncryptedDatatypeEnabled and
   // IsPassphraseRequiredForDecryption).
@@ -1902,17 +2024,17 @@
 }
 
 bool ProfileSyncService::IsEncryptEverythingEnabled() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return crypto_->IsEncryptEverythingEnabled();
 }
 
 syncer::ModelTypeSet ProfileSyncService::GetEncryptedDataTypes() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return crypto_->GetEncryptedDataTypes();
 }
 
 void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (is_sync_managed) {
     StopImpl(CLEAR_DATA);
   } else {
@@ -1923,7 +2045,7 @@
 
 void ProfileSyncService::OnPrimaryAccountSet(
     const AccountInfo& primary_account_info) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!IsEngineInitialized() ||
       GetAuthError().state() != GoogleServiceAuthError::NONE) {
     // Track the fact that we're still waiting for auth to complete.
@@ -1938,7 +2060,7 @@
 
 void ProfileSyncService::OnPrimaryAccountCleared(
     const AccountInfo& previous_primary_account_info) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   sync_disabled_by_admin_ = false;
   UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::SIGN_OUT,
                             syncer::STOP_SOURCE_LIMIT);
@@ -1955,7 +2077,7 @@
 void ProfileSyncService::OnGaiaAccountsInCookieUpdatedWithCallback(
     const std::vector<gaia::ListedAccount>& accounts,
     const base::Closure& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!IsEngineInitialized())
     return;
 
@@ -1969,7 +2091,7 @@
 
 bool ProfileSyncService::HasCookieJarMismatch(
     const std::vector<gaia::ListedAccount>& cookie_jar_accounts) {
-  std::string account_id = signin_->GetAccountIdToUse();
+  std::string account_id = GetAuthenticatedAccountInfo().account_id;
   // Iterate through list of accounts, looking for current sync account.
   for (const auto& account : cookie_jar_accounts) {
     if (account.id == account_id)
@@ -1980,7 +2102,7 @@
 
 void ProfileSyncService::AddProtocolEventObserver(
     ProtocolEventObserver* observer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   protocol_event_observers_.AddObserver(observer);
   if (HasSyncingEngine()) {
     engine_->RequestBufferedProtocolEventsAndEnableForwarding();
@@ -1989,7 +2111,7 @@
 
 void ProfileSyncService::RemoveProtocolEventObserver(
     ProtocolEventObserver* observer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   protocol_event_observers_.RemoveObserver(observer);
   if (HasSyncingEngine() && !protocol_event_observers_.might_have_observers()) {
     engine_->DisableProtocolEventForwarding();
@@ -1998,7 +2120,7 @@
 
 void ProfileSyncService::AddTypeDebugInfoObserver(
     syncer::TypeDebugInfoObserver* type_debug_info_observer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   type_debug_info_observers_.AddObserver(type_debug_info_observer);
   if (type_debug_info_observers_.might_have_observers() &&
       engine_initialized_) {
@@ -2008,7 +2130,7 @@
 
 void ProfileSyncService::RemoveTypeDebugInfoObserver(
     syncer::TypeDebugInfoObserver* type_debug_info_observer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   type_debug_info_observers_.RemoveObserver(type_debug_info_observer);
   if (!type_debug_info_observers_.might_have_observers() &&
       engine_initialized_) {
@@ -2018,7 +2140,7 @@
 
 void ProfileSyncService::AddPreferenceProvider(
     syncer::SyncTypePreferenceProvider* provider) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(!HasPreferenceProvider(provider))
       << "Providers may only be added once!";
   preference_providers_.insert(provider);
@@ -2026,7 +2148,7 @@
 
 void ProfileSyncService::RemovePreferenceProvider(
     syncer::SyncTypePreferenceProvider* provider) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(HasPreferenceProvider(provider))
       << "Only providers that have been added before can be removed!";
   preference_providers_.erase(provider);
@@ -2034,7 +2156,7 @@
 
 bool ProfileSyncService::HasPreferenceProvider(
     syncer::SyncTypePreferenceProvider* provider) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return preference_providers_.count(provider) > 0;
 }
 
@@ -2082,7 +2204,7 @@
 void GetAllNodesRequestHelper::OnReceivedNodesForType(
     const syncer::ModelType type,
     std::unique_ptr<base::ListValue> node_list) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Add these results to our list.
   std::unique_ptr<base::DictionaryValue> type_dict(new base::DictionaryValue());
@@ -2103,7 +2225,7 @@
 
 void ProfileSyncService::GetAllNodes(
     const base::Callback<void(std::unique_ptr<base::ListValue>)>& callback) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   ModelTypeSet all_types = GetActiveDataTypes();
   all_types.PutAll(syncer::ControlTypes());
   scoped_refptr<GetAllNodesRequestHelper> helper =
@@ -2133,12 +2255,18 @@
   }
 }
 
+AccountInfo ProfileSyncService::GetAuthenticatedAccountInfo() const {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  return signin_ ? signin_->GetIdentityManager()->GetPrimaryAccountInfo()
+                 : AccountInfo();
+}
+
 syncer::GlobalIdMapper* ProfileSyncService::GetGlobalIdMapper() const {
   return sessions_sync_manager_->GetGlobalIdMapper();
 }
 
 base::WeakPtr<syncer::JsController> ProfileSyncService::GetJsController() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sync_js_controller_.AsWeakPtr();
 }
 
@@ -2154,31 +2282,31 @@
 }
 
 bool ProfileSyncService::IsSyncAllowedByPlatform() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return platform_sync_allowed_provider_.is_null() ||
          platform_sync_allowed_provider_.Run();
 }
 
 bool ProfileSyncService::IsManaged() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sync_prefs_.IsManaged() || sync_disabled_by_admin_;
 }
 
 void ProfileSyncService::RequestStop(SyncStopDataFate data_fate) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   sync_prefs_.SetSyncRequested(false);
   StopImpl(data_fate);
 }
 
 bool ProfileSyncService::IsSyncRequested() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // When local sync is on sync should be considered requsted or otherwise it
   // will not resume after the policy or the flag has been removed.
   return sync_prefs_.IsSyncRequested() || sync_prefs_.IsLocalSyncEnabled();
 }
 
 void ProfileSyncService::RequestStart() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!IsSyncAllowed()) {
     // Sync cannot be requested if it's not allowed.
     return;
@@ -2192,7 +2320,7 @@
 }
 
 void ProfileSyncService::ReconfigureDatatypeManager() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // If we haven't initialized yet, don't configure the DTM as it could cause
   // association to start before a Directory has even been created.
   if (engine_initialized_) {
@@ -2221,7 +2349,7 @@
 }
 
 const DataTypeStatusTable& ProfileSyncService::data_type_status_table() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return data_type_status_table_;
 }
 
@@ -2236,37 +2364,37 @@
 }
 
 bool ProfileSyncService::IsRetryingAccessTokenFetchForTest() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return request_access_token_retry_timer_.IsRunning();
 }
 
 std::string ProfileSyncService::GetAccessTokenForTest() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return access_token_;
 }
 
 syncer::SyncableService* ProfileSyncService::GetSessionsSyncableService() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!sessions_sync_manager_)
     return nullptr;
   return sessions_sync_manager_->GetSyncableService();
 }
 
 syncer::ModelTypeSyncBridge* ProfileSyncService::GetSessionSyncBridge() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (!sessions_sync_manager_)
     return nullptr;
   return sessions_sync_manager_->GetModelTypeSyncBridge();
 }
 
 syncer::ModelTypeSyncBridge* ProfileSyncService::GetDeviceInfoSyncBridge() {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return device_info_sync_bridge_.get();
 }
 
 syncer::SyncService::SyncTokenStatus ProfileSyncService::GetSyncTokenStatus()
     const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   SyncTokenStatus status = token_status_;
   if (!request_access_token_retry_timer_.IsRunning())
     status.next_token_request_time = base::Time();
@@ -2275,7 +2403,7 @@
 
 void ProfileSyncService::OverrideNetworkResourcesForTest(
     std::unique_ptr<syncer::NetworkResources> network_resources) {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   network_resources_ = std::move(network_resources);
 }
 
@@ -2293,7 +2421,7 @@
 }
 
 void ProfileSyncService::FlushDirectory() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   // engine_initialized_ implies engine_ isn't null and the manager exists.
   // If sync is not initialized yet, we fail silently.
   if (engine_initialized_)
@@ -2301,12 +2429,12 @@
 }
 
 base::FilePath ProfileSyncService::GetDirectoryPathForTest() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return FormatSyncDataPath(base_directory_);
 }
 
 base::MessageLoop* ProfileSyncService::GetSyncLoopForTest() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sync_thread_ ? sync_thread_->message_loop() : nullptr;
 }
 
@@ -2369,17 +2497,17 @@
 }
 
 const GURL& ProfileSyncService::sync_service_url() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return sync_service_url_;
 }
 
 std::string ProfileSyncService::unrecoverable_error_message() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return unrecoverable_error_message_;
 }
 
 base::Location ProfileSyncService::unrecoverable_error_location() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   return unrecoverable_error_location_;
 }
 
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 6d08362..9e836d574 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -16,6 +16,8 @@
 #include "base/memory/memory_pressure_listener.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
@@ -29,7 +31,9 @@
 #include "components/sync/driver/data_type_manager_observer.h"
 #include "components/sync/driver/data_type_status_table.h"
 #include "components/sync/driver/startup_controller.h"
-#include "components/sync/driver/sync_service_base.h"
+#include "components/sync/driver/sync_client.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_service_crypto.h"
 #include "components/sync/driver/sync_stopped_reporter.h"
 #include "components/sync/engine/events/protocol_event_observer.h"
 #include "components/sync/engine/model_safe_worker.h"
@@ -69,8 +73,9 @@
 class DeviceInfoSyncBridge;
 class DeviceInfoTracker;
 class LocalDeviceInfoProvider;
+class ModelTypeSyncBridge;
 class NetworkResources;
-class SyncClient;
+class SyncableService;
 class SyncErrorController;
 class SyncTypePreferenceProvider;
 class TypeDebugInfoObserver;
@@ -163,7 +168,8 @@
 //   Once first setup has completed and there are no outstanding
 //   setup-in-progress handles, CanConfigureDataTypes() will return true and
 //   datatype configuration can begin.
-class ProfileSyncService : public syncer::SyncServiceBase,
+class ProfileSyncService : public syncer::SyncService,
+                           public syncer::SyncEngineHost,
                            public syncer::SyncPrefObserver,
                            public syncer::DataTypeManagerObserver,
                            public syncer::UnrecoverableErrorHandler,
@@ -260,6 +266,9 @@
   void RequestStart() override;
   syncer::ModelTypeSet GetActiveDataTypes() const override;
   syncer::SyncClient* GetSyncClient() const override;
+  void AddObserver(syncer::SyncServiceObserver* observer) override;
+  void RemoveObserver(syncer::SyncServiceObserver* observer) override;
+  bool HasObserver(const syncer::SyncServiceObserver* observer) const override;
   syncer::ModelTypeSet GetPreferredDataTypes() const override;
   void OnUserChoseDatatypes(bool sync_everything,
                             syncer::ModelTypeSet chosen_types) override;
@@ -310,6 +319,7 @@
   base::WeakPtr<syncer::JsController> GetJsController() override;
   void GetAllNodes(const base::Callback<void(std::unique_ptr<base::ListValue>)>&
                        callback) override;
+  AccountInfo GetAuthenticatedAccountInfo() const override;
   syncer::GlobalIdMapper* GetGlobalIdMapper() const override;
 
   // Changes only the KeepEverythingSynced value.
@@ -466,9 +476,6 @@
   // Used by tests to inspect the OAuth2 access tokens used by PSS.
   std::string GetAccessTokenForTest() const;
 
-  // TODO(sync): This is only used in tests.  Can we remove it?
-  void GetModelSafeRoutingInfo(syncer::ModelSafeRoutingInfo* out) const;
-
   // SyncPrefObserver implementation.
   void OnSyncManagedPrefChange(bool is_sync_managed) override;
 
@@ -503,10 +510,6 @@
   // TODO(sync): This is only used in tests.  Can we remove it?
   const syncer::DataTypeStatusTable& data_type_status_table() const;
 
-  syncer::DataTypeManager::ConfigureStatus configure_status() {
-    return configure_status_;
-  }
-
   // If true, the ProfileSyncService has detected that a new GAIA signin has
   // succeeded, and is waiting for initialization to complete. This is used by
   // the UI to differentiate between a new auth error (encountered as part of
@@ -563,16 +566,17 @@
   // to start accounts with a clean slate when performing end to end testing.
   void ClearServerDataForTest(const base::Closure& callback);
 
- protected:
-  // SyncServiceBase implementation.
-  syncer::SyncCredentials GetCredentials() override;
-  syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler() override;
-  syncer::SyncEngine::HttpPostProviderFactoryGetter
-  MakeHttpPostProviderFactoryGetter() override;
-  syncer::WeakHandle<syncer::UnrecoverableErrorHandler>
-  GetUnrecoverableErrorHandler() override;
-
  private:
+  syncer::SyncCredentials GetCredentials();
+  virtual syncer::WeakHandle<syncer::JsEventHandler> GetJsEventHandler();
+  syncer::SyncEngine::HttpPostProviderFactoryGetter
+  MakeHttpPostProviderFactoryGetter();
+  syncer::WeakHandle<syncer::UnrecoverableErrorHandler>
+  GetUnrecoverableErrorHandler();
+
+  // Destroys the |crypto_| object and creates a new one with fresh state.
+  void ResetCryptoState();
+
   enum UnrecoverableErrorReason {
     ERROR_REASON_UNSET,
     ERROR_REASON_SYNCER,
@@ -644,6 +648,9 @@
   // Sets the last synced time to the current time.
   void UpdateLastSyncedTime();
 
+  // Notify all observers that a change has occurred.
+  void NotifyObservers();
+
   void NotifySyncCycleCompleted();
   void NotifyForeignSessionUpdated();
   void NotifyShutdown();
@@ -655,6 +662,9 @@
   // Starts up the engine sync components.
   virtual void StartUpSlowEngineComponents();
 
+  // Kicks off asynchronous initialization of the SyncEngine.
+  void InitializeEngine();
+
   // Collects preferred sync data types from |preference_providers_|.
   syncer::ModelTypeSet GetDataTypesFromPreferenceProviders() const;
 
@@ -725,6 +735,45 @@
   // Called when a SetupInProgressHandle issued by this instance is destroyed.
   virtual void OnSetupInProgressHandleDestroyed();
 
+  // This profile's SyncClient, which abstracts away non-Sync dependencies and
+  // the Sync API component factory.
+  const std::unique_ptr<syncer::SyncClient> sync_client_;
+
+  // Encapsulates user signin - used to set/get the user's authenticated
+  // email address.
+  const std::unique_ptr<SigninManagerWrapper> signin_;
+
+  // The product channel of the embedder.
+  const version_info::Channel channel_;
+
+  // The path to the base directory under which sync should store its
+  // information.
+  const base::FilePath base_directory_;
+
+  // An identifier representing this instance for debugging purposes.
+  const std::string debug_identifier_;
+
+  // The class that handles getting, setting, and persisting sync preferences.
+  syncer::SyncPrefs sync_prefs_;
+
+  // A utility object containing logic and state relating to encryption. It is
+  // never null.
+  std::unique_ptr<syncer::SyncServiceCrypto> crypto_;
+
+  // The thread where all the sync operations happen. This thread is kept alive
+  // until browser shutdown and reused if sync is turned off and on again. It is
+  // joined during the shutdown process, but there is an abort mechanism in
+  // place to prevent slow HTTP requests from blocking browser shutdown.
+  std::unique_ptr<base::Thread> sync_thread_;
+
+  // Our asynchronous engine to communicate with sync components living on
+  // other threads.
+  std::unique_ptr<syncer::SyncEngine> engine_;
+
+  // Used to ensure that certain operations are performed on the thread that
+  // this object was created on.
+  THREAD_CHECKER(thread_checker_);
+
   SigninScopedDeviceIdCallback signin_scoped_device_id_callback_;
 
   // This is a cache of the last authentication response we received from the
@@ -781,6 +830,7 @@
   // Manages the start and stop of the data types.
   std::unique_ptr<syncer::DataTypeManager> data_type_manager_;
 
+  base::ObserverList<syncer::SyncServiceObserver> observers_;
   base::ObserverList<syncer::ProtocolEventObserver> protocol_event_observers_;
   base::ObserverList<syncer::TypeDebugInfoObserver> type_debug_info_observers_;
 
@@ -806,15 +856,9 @@
   // or must delay loading for some reason).
   syncer::DataTypeStatusTable data_type_status_table_;
 
-  syncer::DataTypeManager::ConfigureStatus configure_status_;
-
   // The set of currently enabled sync experiments.
   syncer::Experiments current_experiments_;
 
-  // Sync's internal debug info listener. Used to record datatype configuration
-  // and association information.
-  syncer::WeakHandle<syncer::DataTypeDebugInfoListener> debug_info_listener_;
-
   // ProfileSyncService uses this service to get access tokens.
   ProfileOAuth2TokenService* const oauth2_token_service_;
 
@@ -879,8 +923,6 @@
   DISALLOW_COPY_AND_ASSIGN(ProfileSyncService);
 };
 
-bool ShouldShowActionOnUI(const syncer::SyncProtocolError& error);
-
 }  // namespace browser_sync
 
 #endif  // COMPONENTS_BROWSER_SYNC_PROFILE_SYNC_SERVICE_H_
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc
index 22eacc3..3fe3948 100644
--- a/components/browser_sync/profile_sync_service_unittest.cc
+++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -20,6 +20,7 @@
 #include "components/sync/base/pref_names.h"
 #include "components/sync/device_info/local_device_info_provider.h"
 #include "components/sync/driver/fake_data_type_controller.h"
+#include "components/sync/driver/signin_manager_wrapper.h"
 #include "components/sync/driver/sync_api_component_factory_mock.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service_observer.h"
diff --git a/components/cronet/tools/combine_maven_modules.py b/components/cronet/tools/combine_maven_modules.py
new file mode 100755
index 0000000..30ba3260
--- /dev/null
+++ b/components/cronet/tools/combine_maven_modules.py
@@ -0,0 +1,200 @@
+#!/usr/bin/python
+# 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.
+
+"""combine_maven_modules.py - Combine Cronet maven modules together."""
+
+import fileinput
+import optparse
+import os
+import shutil
+import sys
+import tempfile
+import zipfile
+
+REPOSITORY_ROOT = os.path.abspath(os.path.join(
+    os.path.dirname(__file__), '..', '..', '..'))
+ANDROID_MANIFEST = os.path.join(REPOSITORY_ROOT, 'build', 'android',
+                                'AndroidManifest.xml')
+VERSION_SCRIPT = os.path.join(REPOSITORY_ROOT, 'build', 'util', 'version.py')
+KEEP_RESOURCE = os.path.join(REPOSITORY_ROOT, 'components', 'cronet', 'android',
+                            'api', 'res', 'raw', 'keep_cronet_api.xml')
+
+def zipdir(path, zip_file):
+  ziph = zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED)
+  for root, _, files in os.walk(path):
+    for file_to_zip in files:
+      filename = os.path.join(root, file_to_zip)
+      ziph.write(filename, filename[len(path):])
+  ziph.close()
+
+
+class ModuleBuilder(object):
+
+  def __init__(self, work_dir, version, suffix):
+    """ModuleBuilder builds Maven modules.
+
+    Args:
+      work_dir: Working directory to build Maven modules.
+      version: Chromium version (e.g. 66.0.3359.126).
+      suffix: Maven module version suffix (e.g. -alpha).
+    """
+    self._build_dir = os.path.join(work_dir, 'Release', 'cronet')
+    self._version_file = os.path.join(work_dir, 'Release', 'VERSION')
+    self._modules_dir = os.path.join(work_dir, 'org', 'chromium', 'net')
+
+    # Convert from Chromium's four number version (e.g. 66.0.3359.126) to a
+    # three number version more compatible with Maven version comparing.
+    # Remove the second number from Chromium's which is always 0.
+    version = version.split('.')
+    del version[1]
+    version = '.'.join(version)
+
+    self._version_without_suffix = version
+    self._version = '%s%s' % (version, suffix)
+    self._suffix = suffix
+
+  def make_module(self, module_name, aar_jar, include_javadocs=False,
+                  include_keep_resource=False, aar_proguard_config=None,
+                  aar_native_lib=None):
+    """Make a Maven module.
+
+    Args:
+      module_name: Maven module name (e.g. cronet-api).
+      aar_jar: Name of jar to include in aar.
+      include_javadocs: Boolean indicating if javadocs should be put in aar.
+      include_keep_resource: Boolean indicating if keep_cronet_api.xml
+                             resource should be included in aar.
+      aar_proguard_config: Proguard config file to include in aar.
+      aar_native_lib: Native library name to include in aar.
+    """
+    aar_jar = os.path.join(self._build_dir, aar_jar)
+
+    module_dir = os.path.join(self._modules_dir, module_name, self._version)
+    os.makedirs(module_dir)
+    module_prefix = '%s-%s' % (module_name, self._version)
+
+    aar_dir = tempfile.mkdtemp()
+    shutil.copyfile(aar_jar, os.path.join(aar_dir, 'classes.jar'))
+    open(os.path.join(aar_dir, 'public.txt'), 'a').close()
+    shutil.copy(ANDROID_MANIFEST, aar_dir)
+    manifest = fileinput.FileInput(os.path.join(aar_dir, 'AndroidManifest.xml'),
+                                   inplace=True)
+    for line in manifest:
+      print line.replace('org.dummy', 'org.chromium.net'),
+
+    if aar_proguard_config:
+      aar_proguard_config = os.path.join(self._build_dir, aar_proguard_config)
+      shutil.copyfile(aar_proguard_config, os.path.join(aar_dir,
+                                                        'proguard.txt'))
+    if aar_native_lib:
+      for arch in ['arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64']:
+        lib_dir = os.path.join(aar_dir, 'jni', arch)
+        os.makedirs(lib_dir)
+        shutil.copyfile(os.path.join(self._build_dir, 'libs', arch,
+                                     aar_native_lib),
+                        os.path.join(lib_dir, aar_native_lib))
+    with open(os.path.join(aar_dir, 'R.txt'), 'a') as r_file:
+      if include_keep_resource:
+        r_file.write('int raw keep_cronet_api 0x7f020000\n')
+        res_dir = os.path.join(aar_dir, 'res', 'raw')
+        os.makedirs(res_dir)
+        shutil.copy(KEEP_RESOURCE, res_dir)
+
+    zipdir(aar_dir, os.path.join(module_dir, '%s.aar' % module_prefix))
+    shutil.rmtree(aar_dir)
+
+    shutil.copyfile(aar_jar.replace('.jar', '-src.jar'),
+                    os.path.join(module_dir, '%s-sources.jar' % module_prefix))
+
+    pom_template = os.path.join(REPOSITORY_ROOT, 'components', 'cronet',
+                                'android', 'maven',
+                                '%s.pom.template' % module_name)
+    pom_file = os.path.join(module_dir, '%s.pom' % module_prefix)
+    if os.system('%s -f %s -i %s -o %s' % (VERSION_SCRIPT, self._version_file,
+                                           pom_template, pom_file)):
+      sys.stderr.write('version.py failed.')
+      exit(1)
+    if self._suffix != '':
+      pom_file = fileinput.FileInput(pom_file, inplace=True)
+      for line in pom_file:
+        print line.replace('%s</version>' % self._version_without_suffix,
+                           '%s</version>' % self._version),
+
+    if include_javadocs:
+      javadoc_dir = os.path.join(self._build_dir, 'javadoc')
+
+      # Create an index.html file at the root as this is the accepted format.
+      # Do this by copying reference/index.html and adjusting the path.
+      with open(os.path.join(javadoc_dir, 'reference', 'index.html'), 'r') as \
+          old_index, open(os.path.join(javadoc_dir, 'index.html'), 'w') as \
+          new_index:
+        for line in old_index:
+          new_index.write(line.replace('classes.html',
+                                       'reference/classes.html'))
+
+      zipdir(javadoc_dir, os.path.join(module_dir,
+                                       '%s-javadoc.jar' % module_prefix))
+
+def main():
+  parser = optparse.OptionParser()
+  parser.add_option('--version',
+                    help='Version of Cronet to download (e.g. 66.0.3359.126).')
+  parser.add_option('--suffix',
+                    help='The suffix to add. Must be alpha or beta.')
+  options, _ = parser.parse_args()
+
+  if not options.version:
+    parser.error('Version not provided.')
+
+  suffix = options.suffix
+  if suffix:
+    if suffix != 'alpha' and suffix != 'beta':
+      parser.error('Suffix must be alpha or beta')
+    suffix = '-%s' % suffix
+  else:
+    suffix = ''
+
+  work_dir = tempfile.mkdtemp()
+
+  if os.system(
+            'cd %s && gsutil -m cp -R gs://chromium-cronet/android/%s/Release .'
+               % (work_dir, options.version)):
+    sys.stderr.write('Google cloud storage download failed.')
+    exit(1)
+
+  module_builder = ModuleBuilder(work_dir, options.version, suffix)
+
+  module_builder.make_module(
+    module_name="cronet-api",
+    aar_jar="cronet_api.jar",
+    include_javadocs=True,
+    include_keep_resource=True,
+  )
+
+  module_builder.make_module(
+    module_name="cronet-common",
+    aar_jar="cronet_impl_common_java.jar",
+    aar_proguard_config="cronet_impl_common_proguard.cfg",
+  )
+
+  module_builder.make_module(
+    module_name="cronet-embedded",
+    aar_jar="cronet_impl_native_java.jar",
+    aar_proguard_config="cronet_impl_native_proguard.cfg",
+    aar_native_lib="libcronet.%s.so" % options.version
+  )
+
+  module_builder.make_module(
+    module_name="cronet-fallback",
+    aar_jar="cronet_impl_platform_java.jar",
+    aar_proguard_config="cronet_impl_platform_proguard.cfg"
+  )
+
+  shutil.rmtree(os.path.join(work_dir, 'Release'))
+
+  print 'Maven modules in: %s' % work_dir
+
+if __name__ == '__main__':
+  main()
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc
index 30d6684..7ad88f9 100644
--- a/components/download/internal/common/in_progress_download_manager.cc
+++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -66,7 +66,6 @@
     const GURL& tab_url,
     const GURL& tab_referrer_url,
     std::vector<GURL> url_chain,
-    const base::Optional<std::string>& suggested_filename,
     scoped_refptr<network::ResourceResponse> response,
     net::CertStatus cert_status,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
@@ -77,8 +76,8 @@
       ResourceDownloader::InterceptNavigationResponse(
           download_manager, std::move(resource_request), render_process_id,
           render_frame_id, site_url, tab_url, tab_referrer_url,
-          std::move(url_chain), suggested_filename, std::move(response),
-          std::move(cert_status), std::move(url_loader_client_endpoints),
+          std::move(url_chain), std::move(response), std::move(cert_status),
+          std::move(url_loader_client_endpoints),
           std::move(url_loader_factory_getter), main_task_runner)
           .release(),
       base::OnTaskRunnerDeleter(base::ThreadTaskRunnerHandle::Get()));
@@ -219,7 +218,6 @@
     const GURL& tab_url,
     const GURL& tab_referrer_url,
     std::vector<GURL> url_chain,
-    const base::Optional<std::string>& suggested_filename,
     scoped_refptr<network::ResourceResponse> response,
     net::CertStatus cert_status,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
@@ -229,7 +227,7 @@
       base::BindOnce(&CreateDownloadHandlerForNavigation,
                      weak_factory_.GetWeakPtr(), std::move(resource_request),
                      render_process_id, render_frame_id, site_url, tab_url,
-                     tab_referrer_url, std::move(url_chain), suggested_filename,
+                     tab_referrer_url, std::move(url_chain),
                      std::move(response), std::move(cert_status),
                      std::move(url_loader_client_endpoints),
                      std::move(url_loader_factory_getter),
diff --git a/components/download/internal/common/resource_downloader.cc b/components/download/internal/common/resource_downloader.cc
index 6d687263..acd5147b 100644
--- a/components/download/internal/common/resource_downloader.cc
+++ b/components/download/internal/common/resource_downloader.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "base/strings/utf_string_conversions.h"
 #include "components/download/public/common/download_url_loader_factory_getter.h"
 #include "components/download/public/common/stream_handle_input_stream.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -90,7 +89,6 @@
     const GURL& tab_url,
     const GURL& tab_referrer_url,
     std::vector<GURL> url_chain,
-    const base::Optional<std::string>& suggested_filename,
     const scoped_refptr<network::ResourceResponse>& response,
     net::CertStatus cert_status,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
@@ -102,7 +100,7 @@
       site_url, tab_url, tab_referrer_url, download::DownloadItem::kInvalidId,
       task_runner, std::move(url_loader_factory_getter));
   downloader->InterceptResponse(std::move(response), std::move(url_chain),
-                                suggested_filename, cert_status,
+                                cert_status,
                                 std::move(url_loader_client_endpoints));
   return downloader;
 }
@@ -173,18 +171,14 @@
 void ResourceDownloader::InterceptResponse(
     const scoped_refptr<network::ResourceResponse>& response,
     std::vector<GURL> url_chain,
-    const base::Optional<std::string>& suggested_filename,
     net::CertStatus cert_status,
     network::mojom::URLLoaderClientEndpointsPtr endpoints) {
   // Set the URLLoader.
   url_loader_.Bind(std::move(endpoints->url_loader));
 
   // Create the new URLLoaderClient that will intercept the navigation.
-  auto save_info = std::make_unique<DownloadSaveInfo>();
-  if (suggested_filename.has_value())
-    save_info->suggested_name = base::UTF8ToUTF16(suggested_filename.value());
   url_loader_client_ = std::make_unique<DownloadResponseHandler>(
-      resource_request_.get(), this, std::move(save_info),
+      resource_request_.get(), this, std::make_unique<DownloadSaveInfo>(),
       false, /* is_parallel_request */
       false, /* is_transient */
       false, /* fetch_error_body */
diff --git a/components/download/internal/common/resource_downloader.h b/components/download/internal/common/resource_downloader.h
index 171748d..ce6eef6 100644
--- a/components/download/internal/common/resource_downloader.h
+++ b/components/download/internal/common/resource_downloader.h
@@ -47,7 +47,6 @@
       const GURL& tab_url,
       const GURL& tab_referrer_url,
       std::vector<GURL> url_chain,
-      const base::Optional<std::string>& suggested_filename,
       const scoped_refptr<network::ResourceResponse>& response,
       net::CertStatus cert_status,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
@@ -85,7 +84,6 @@
   void InterceptResponse(
       const scoped_refptr<network::ResourceResponse>& response,
       std::vector<GURL> url_chain,
-      const base::Optional<std::string>& suggested_filename,
       net::CertStatus cert_status,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints);
 
diff --git a/components/download/public/common/in_progress_download_manager.h b/components/download/public/common/in_progress_download_manager.h
index 3659cd2..9dba8c6 100644
--- a/components/download/public/common/in_progress_download_manager.h
+++ b/components/download/public/common/in_progress_download_manager.h
@@ -92,7 +92,6 @@
       const GURL& tab_url,
       const GURL& tab_referrer_url,
       std::vector<GURL> url_chain,
-      const base::Optional<std::string>& suggested_filename,
       scoped_refptr<network::ResourceResponse> response,
       net::CertStatus cert_status,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
diff --git a/components/gcm_driver/gcm_profile_service.cc b/components/gcm_driver/gcm_profile_service.cc
index a27e9dae..b44d2083 100644
--- a/components/gcm_driver/gcm_profile_service.cc
+++ b/components/gcm_driver/gcm_profile_service.cc
@@ -28,7 +28,6 @@
 #include "components/gcm_driver/gcm_client_factory.h"
 #include "components/gcm_driver/gcm_desktop_utils.h"
 #include "components/gcm_driver/gcm_driver_desktop.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "google_apis/gaia/identity_provider.h"
 #include "net/url_request/url_request_context_getter.h"
 #endif
@@ -38,21 +37,25 @@
 #if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
 // Identity observer only has actual work to do when the user is actually signed
 // in. It ensures that account tracker is taking
-class GCMProfileService::IdentityObserver : public IdentityProvider::Observer {
+class GCMProfileService::IdentityObserver : public SigninManagerBase::Observer {
  public:
-  IdentityObserver(ProfileIdentityProvider* identity_provider,
+  IdentityObserver(SigninManagerBase* signin_manager,
+                   ProfileIdentityProvider* identity_provider,
                    net::URLRequestContextGetter* request_context,
                    GCMDriver* driver);
   ~IdentityObserver() override;
 
-  // IdentityProvider::Observer:
-  void OnActiveAccountLogin() override;
-  void OnActiveAccountLogout() override;
+  // SigninManagerBase::Observer:
+  void GoogleSigninSucceeded(const std::string& account_id,
+                             const std::string& username) override;
+  void GoogleSignedOut(const std::string& account_id,
+                       const std::string& username) override;
 
  private:
   void StartAccountTracker(net::URLRequestContextGetter* request_context);
 
   GCMDriver* driver_;
+  SigninManagerBase* signin_manager_;
   IdentityProvider* identity_provider_;
   std::unique_ptr<GCMAccountTracker> gcm_account_tracker_;
 
@@ -66,27 +69,31 @@
 };
 
 GCMProfileService::IdentityObserver::IdentityObserver(
+    SigninManagerBase* signin_manager,
     ProfileIdentityProvider* identity_provider,
     net::URLRequestContextGetter* request_context,
     GCMDriver* driver)
     : driver_(driver),
+      signin_manager_(signin_manager),
       identity_provider_(identity_provider),
       weak_ptr_factory_(this) {
-  identity_provider_->AddObserver(this);
+  signin_manager_->AddObserver(this);
 
-  OnActiveAccountLogin();
+  GoogleSigninSucceeded(signin_manager_->GetAuthenticatedAccountId(),
+                        signin_manager_->GetAuthenticatedAccountInfo().email);
   StartAccountTracker(request_context);
 }
 
 GCMProfileService::IdentityObserver::~IdentityObserver() {
   if (gcm_account_tracker_)
     gcm_account_tracker_->Shutdown();
-  identity_provider_->RemoveObserver(this);
+  signin_manager_->RemoveObserver(this);
 }
 
-void GCMProfileService::IdentityObserver::OnActiveAccountLogin() {
+void GCMProfileService::IdentityObserver::GoogleSigninSucceeded(
+    const std::string& account_id,
+    const std::string& username) {
   // This might be called multiple times when the password changes.
-  const std::string account_id = identity_provider_->GetActiveAccountId();
   if (account_id == account_id_)
     return;
   account_id_ = account_id;
@@ -95,7 +102,9 @@
   driver_->OnSignedIn();
 }
 
-void GCMProfileService::IdentityObserver::OnActiveAccountLogout() {
+void GCMProfileService::IdentityObserver::GoogleSignedOut(
+    const std::string& account_id,
+    const std::string& username) {
   account_id_.clear();
 
   // Still need to notify GCMDriver for UMA purpose.
@@ -141,12 +150,14 @@
     net::URLRequestContextGetter* request_context,
     version_info::Channel channel,
     const std::string& product_category_for_subtypes,
+    SigninManagerBase* signin_manager,
     std::unique_ptr<ProfileIdentityProvider> identity_provider,
     std::unique_ptr<GCMClientFactory> gcm_client_factory,
     const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
     const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
     scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
     : profile_identity_provider_(std::move(identity_provider)),
+      signin_manager_(signin_manager),
       request_context_(request_context) {
   driver_ = CreateGCMDriverDesktop(
       std::move(gcm_client_factory), prefs,
@@ -154,8 +165,9 @@
       product_category_for_subtypes, ui_task_runner, io_task_runner,
       blocking_task_runner);
 
-  identity_observer_.reset(new IdentityObserver(
-      profile_identity_provider_.get(), request_context_, driver_.get()));
+  identity_observer_.reset(
+      new IdentityObserver(signin_manager_, profile_identity_provider_.get(),
+                           request_context_, driver_.get()));
 }
 #endif  // BUILDFLAG(USE_GCM_FROM_PLATFORM)
 
@@ -179,7 +191,8 @@
 #if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
   if (identity_observer_) {
     identity_observer_ = std::make_unique<IdentityObserver>(
-        profile_identity_provider_.get(), request_context_, driver_.get());
+        signin_manager_, profile_identity_provider_.get(), request_context_,
+        driver.get());
   }
 #endif  // !BUILDFLAG(USE_GCM_FROM_PLATFORM)
 }
diff --git a/components/gcm_driver/gcm_profile_service.h b/components/gcm_driver/gcm_profile_service.h
index 5e1bbe3..13275d6 100644
--- a/components/gcm_driver/gcm_profile_service.h
+++ b/components/gcm_driver/gcm_profile_service.h
@@ -17,6 +17,7 @@
 #include "components/gcm_driver/gcm_buildflags.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/profile_identity_provider.h"
+#include "components/signin/core/browser/signin_manager.h"
 #include "components/version_info/version_info.h"
 
 class PrefService;
@@ -48,6 +49,7 @@
       net::URLRequestContextGetter* request_context,
       version_info::Channel channel,
       const std::string& product_category_for_subtypes,
+      SigninManagerBase* signin_manager,
       std::unique_ptr<ProfileIdentityProvider> identity_provider,
       std::unique_ptr<GCMClientFactory> gcm_client_factory,
       const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner,
@@ -76,6 +78,7 @@
   std::unique_ptr<GCMDriver> driver_;
 
 #if !BUILDFLAG(USE_GCM_FROM_PLATFORM)
+  SigninManagerBase* signin_manager_;
   net::URLRequestContextGetter* request_context_ = nullptr;
 
   // Used for both account tracker and GCM.UserSignedIn UMA.
diff --git a/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java b/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java
index 457c2a84..d6e14f9a 100644
--- a/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java
+++ b/components/navigation_interception/android/java/src/org/chromium/components/navigation_interception/NavigationParams.java
@@ -34,12 +34,6 @@
     public final boolean isExternalProtocol;
 
     /**
-     * If this navigation was triggered by an anchor with a download
-     * attribute, this is the (possibly empty) value of that attribute.
-     * */
-    public final String suggestedFilename;
-
-    /**
      * True if the navigation was originated from a navigation which had been
      * initiated by the user.
      */
@@ -50,7 +44,7 @@
 
     public NavigationParams(String url, String referrer, boolean isPost, boolean hasUserGesture,
             int pageTransitionType, boolean isRedirect, boolean isExternalProtocol,
-            boolean isMainFrame, String suggestedFilename, boolean hasUserGestureCarryover) {
+            boolean isMainFrame, boolean hasUserGestureCarryover) {
         this.url = url;
         this.referrer = TextUtils.isEmpty(referrer) ? null : referrer;
         this.isPost = isPost;
@@ -59,17 +53,14 @@
         this.isRedirect = isRedirect;
         this.isExternalProtocol = isExternalProtocol;
         this.isMainFrame = isMainFrame;
-        this.suggestedFilename = suggestedFilename;
         this.hasUserGestureCarryover = hasUserGestureCarryover;
     }
 
     @CalledByNative
     public static NavigationParams create(String url, String referrer, boolean isPost,
             boolean hasUserGesture, int pageTransitionType, boolean isRedirect,
-            boolean isExternalProtocol, boolean isMainFrame, String suggestedFilename,
-            boolean hasUserGestureCarryover) {
+            boolean isExternalProtocol, boolean isMainFrame, boolean hasUserGestureCarryover) {
         return new NavigationParams(url, referrer, isPost, hasUserGesture, pageTransitionType,
-                isRedirect, isExternalProtocol, isMainFrame, suggestedFilename,
-                hasUserGestureCarryover);
+                isRedirect, isExternalProtocol, isMainFrame, hasUserGestureCarryover);
     }
 }
diff --git a/components/navigation_interception/intercept_navigation_throttle.cc b/components/navigation_interception/intercept_navigation_throttle.cc
index b126c38..81acec4 100644
--- a/components/navigation_interception/intercept_navigation_throttle.cc
+++ b/components/navigation_interception/intercept_navigation_throttle.cc
@@ -52,8 +52,7 @@
       navigation_handle()->HasUserGesture(), navigation_handle()->IsPost(),
       navigation_handle()->GetPageTransition(), is_redirect,
       navigation_handle()->IsExternalProtocol(), true,
-      navigation_handle()->GetBaseURLForDataURL(),
-      navigation_handle()->GetSuggestedFilename());
+      navigation_handle()->GetBaseURLForDataURL());
   bool should_ignore_navigation = should_ignore_callback_.Run(
       navigation_handle()->GetWebContents(), navigation_params);
   return should_ignore_navigation
diff --git a/components/navigation_interception/navigation_params.cc b/components/navigation_interception/navigation_params.cc
index 2a60e7c..02d8755 100644
--- a/components/navigation_interception/navigation_params.cc
+++ b/components/navigation_interception/navigation_params.cc
@@ -6,17 +6,15 @@
 
 namespace navigation_interception {
 
-NavigationParams::NavigationParams(
-    const GURL& url,
-    const content::Referrer& referrer,
-    bool has_user_gesture,
-    bool is_post,
-    ui::PageTransition transition_type,
-    bool is_redirect,
-    bool is_external_protocol,
-    bool is_main_frame,
-    const GURL& base_url_for_data_url,
-    const base::Optional<std::string>& suggested_filename)
+NavigationParams::NavigationParams(const GURL& url,
+                                   const content::Referrer& referrer,
+                                   bool has_user_gesture,
+                                   bool is_post,
+                                   ui::PageTransition transition_type,
+                                   bool is_redirect,
+                                   bool is_external_protocol,
+                                   bool is_main_frame,
+                                   const GURL& base_url_for_data_url)
     : url_(url),
       referrer_(referrer),
       has_user_gesture_(has_user_gesture),
@@ -25,8 +23,7 @@
       is_redirect_(is_redirect),
       is_external_protocol_(is_external_protocol),
       is_main_frame_(is_main_frame),
-      base_url_for_data_url_(base_url_for_data_url),
-      suggested_filename_(suggested_filename) {}
+      base_url_for_data_url_(base_url_for_data_url) {}
 
 NavigationParams::~NavigationParams() = default;
 
diff --git a/components/navigation_interception/navigation_params.h b/components/navigation_interception/navigation_params.h
index 1630235..d14e88f 100644
--- a/components/navigation_interception/navigation_params.h
+++ b/components/navigation_interception/navigation_params.h
@@ -5,7 +5,6 @@
 #ifndef COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_
 #define COMPONENTS_NAVIGATION_INTERCEPTION_NAVIGATION_PARAMS_H_
 
-#include "base/optional.h"
 #include "content/public/common/referrer.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -22,8 +21,7 @@
                    bool is_redirect,
                    bool is_external_protocol,
                    bool is_main_frame,
-                   const GURL& base_url_for_data_url,
-                   const base::Optional<std::string>& suggested_filename);
+                   const GURL& base_url_for_data_url);
   ~NavigationParams();
   NavigationParams(const NavigationParams&);
   NavigationParams& operator=(const NavigationParams&) = delete;
@@ -38,9 +36,6 @@
   bool is_external_protocol() const { return is_external_protocol_; }
   bool is_main_frame() const { return is_main_frame_; }
   const GURL& base_url_for_data_url() const { return base_url_for_data_url_; }
-  const base::Optional<std::string>& suggested_filename() const {
-    return suggested_filename_;
-  }
 
  private:
 
@@ -53,7 +48,6 @@
   bool is_external_protocol_;
   bool is_main_frame_;
   GURL base_url_for_data_url_;
-  base::Optional<std::string> suggested_filename_;
 };
 
 }  // namespace navigation_interception
diff --git a/components/navigation_interception/navigation_params_android.cc b/components/navigation_interception/navigation_params_android.cc
index cc449e49..a5d6dcf 100644
--- a/components/navigation_interception/navigation_params_android.cc
+++ b/components/navigation_interception/navigation_params_android.cc
@@ -25,17 +25,11 @@
   ScopedJavaLocalRef<jstring> jstring_referrer =
       ConvertUTF8ToJavaString(env, params.referrer().url.spec());
 
-  ScopedJavaLocalRef<jstring> jstring_suggested_filename = nullptr;
-  if (params.suggested_filename().has_value()) {
-    jstring_suggested_filename =
-        ConvertUTF8ToJavaString(env, params.suggested_filename().value());
-  }
-
   return Java_NavigationParams_create(
       env, jstring_url, jstring_referrer, params.is_post(),
       params.has_user_gesture(), params.transition_type(), params.is_redirect(),
       params.is_external_protocol(), params.is_main_frame(),
-      jstring_suggested_filename, has_user_gesture_carryover);
+      has_user_gesture_carryover);
 }
 
 }  // namespace navigation_interception
diff --git a/components/password_manager/core/browser/password_manager.h b/components/password_manager/core/browser/password_manager.h
index 777554cf..ee030a8 100644
--- a/components/password_manager/core/browser/password_manager.h
+++ b/components/password_manager/core/browser/password_manager.h
@@ -56,32 +56,6 @@
   explicit PasswordManager(PasswordManagerClient* client);
   ~PasswordManager() override;
 
-  // Called by a PasswordFormManager when it decides a form can be autofilled
-  // on the page.
-  void Autofill(
-      password_manager::PasswordManagerDriver* driver,
-      const autofill::PasswordForm& form_for_autofill,
-      const std::map<base::string16, const autofill::PasswordForm*>&
-          best_matches,
-      const std::vector<const autofill::PasswordForm*>& federated_matches,
-      const autofill::PasswordForm& preferred_match,
-      bool wait_for_username) const;
-
-  // Called by a PasswordFormManager when a page initially loads and it decides
-  // that a form can be autofilled on the page, but a menu of account options
-  // should be shown instead. Similar to Autofill() above, but does not fill; it
-  // only shows a selection of accounts.
-  //
-  // Currently used by the fill-on-account-select experiment only. See
-  // https://crbug.com/568713.
-  void ShowInitialPasswordAccountSuggestions(
-      password_manager::PasswordManagerDriver* driver,
-      const autofill::PasswordForm& form_for_autofill,
-      const std::map<base::string16, const autofill::PasswordForm*>&
-          best_matches,
-      const autofill::PasswordForm& preferred_match,
-      bool wait_for_username) const;
-
   // Called by a PasswordFormManager when it decides a HTTP auth dialog can be
   // autofilled.
   void AutofillHttpAuth(
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index f38dab9..6377d67 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -414,7 +414,8 @@
   // Create the list of available instruments. A copy of each card will be made
   // by their respective AutofillPaymentInstrument.
   const std::vector<autofill::CreditCard*>& cards =
-      personal_data_manager_->GetCreditCardsToSuggest();
+      personal_data_manager_->GetCreditCardsToSuggest(
+          /*include_server_cards=*/true);
   for (autofill::CreditCard* card : cards)
     AddAutofillPaymentInstrument(/*selected=*/false, *card);
 }
diff --git a/components/safe_browsing/features.cc b/components/safe_browsing/features.cc
index c645946..61977125 100644
--- a/components/safe_browsing/features.cc
+++ b/components/safe_browsing/features.cc
@@ -48,6 +48,10 @@
 const base::Feature kThreatDomDetailsTagAndAttributeFeature{
     "ThreatDomDetailsTagAttributes", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kSuspiciousSiteTriggerQuotaFeature{
+    "SafeBrowsingSuspiciousSiteTriggerQuota",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kTriggerThrottlerDailyQuotaFeature{
     "SafeBrowsingTriggerThrottlerDailyQuota",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/safe_browsing/features.h b/components/safe_browsing/features.h
index e0fc05a..2d08f8c 100644
--- a/components/safe_browsing/features.h
+++ b/components/safe_browsing/features.h
@@ -34,10 +34,17 @@
 // be lower case.
 extern const base::Feature kThreatDomDetailsTagAndAttributeFeature;
 
+// Controls the daily quota for the suspicious site trigger.
+extern const base::Feature kSuspiciousSiteTriggerQuotaFeature;
+
 // Controls the daily quota for data collection triggers. It's a single param
 // containing a comma-separated list of pairs. The format of the param is
 // "T1,Q1,T2,Q2,...Tn,Qn", where Tx is a TriggerType and Qx is how many reports
 // that trigger is allowed to send per day.
+// TODO(crbug.com/744869): This param should be deprecated after ad sampler
+// launch in favour of having a unique quota feature and param per trigger.
+// Having a single shared feature makes it impossible to run multiple trigger
+// trials simultaneously.
 extern const base::Feature kTriggerThrottlerDailyQuotaFeature;
 
 // Controls whether to dispatch the SafetyNet check on a worker thread. Android
diff --git a/components/safe_browsing/triggers/trigger_throttler.cc b/components/safe_browsing/triggers/trigger_throttler.cc
index 335f91d30..ed95d3d 100644
--- a/components/safe_browsing/triggers/trigger_throttler.cc
+++ b/components/safe_browsing/triggers/trigger_throttler.cc
@@ -13,8 +13,9 @@
 #include "components/safe_browsing/features.h"
 
 namespace safe_browsing {
-const char kTriggerTypeAndQuotaParam[] = "trigger_type_and_quota_csv";
 const size_t kAdSamplerTriggerDefaultQuota = 10;
+const char kSuspiciousSiteTriggerQuotaParam[] = "suspicious_site_trigger_quota";
+const char kTriggerTypeAndQuotaParam[] = "trigger_type_and_quota_csv";
 
 namespace {
 const size_t kUnlimitedTriggerQuota = std::numeric_limits<size_t>::max();
@@ -35,13 +36,20 @@
 void ParseTriggerTypeAndQuotaParam(
     std::vector<TriggerTypeAndQuotaItem>* trigger_type_and_quota_list) {
   DCHECK(trigger_type_and_quota_list);
+  trigger_type_and_quota_list->clear();
+
+  // First, handle the trigger-specific features.
+  trigger_type_and_quota_list->push_back(std::make_pair(
+      TriggerType::SUSPICIOUS_SITE, base::GetFieldTrialParamByFeatureAsInt(
+                                        kSuspiciousSiteTriggerQuotaFeature,
+                                        kSuspiciousSiteTriggerQuotaParam, 0)));
+
   // If the feature is disabled we just use the default list. Otherwise the list
   // from the Finch param will be the one used.
   if (!base::FeatureList::IsEnabled(kTriggerThrottlerDailyQuotaFeature)) {
     return;
   }
 
-  trigger_type_and_quota_list->clear();
   const std::string& trigger_and_quota_csv_param =
       base::GetFieldTrialParamValueByFeature(kTriggerThrottlerDailyQuotaFeature,
                                              kTriggerTypeAndQuotaParam);
diff --git a/components/safe_browsing/triggers/trigger_throttler.h b/components/safe_browsing/triggers/trigger_throttler.h
index 6de4d53..0d1fedd9 100644
--- a/components/safe_browsing/triggers/trigger_throttler.h
+++ b/components/safe_browsing/triggers/trigger_throttler.h
@@ -14,14 +14,21 @@
 #include "base/time/clock.h"
 
 namespace safe_browsing {
+// Default quota for ad sampler trigger.
+extern const size_t kAdSamplerTriggerDefaultQuota;
+
+// Param name of the finch param containing the quota for the suspicious site
+// trigger.
+extern const char kSuspiciousSiteTriggerQuotaParam[];
 
 // Param name of the finch param containing the comma-separated list of trigger
 // types and daily quotas.
+// TODO(crbug.com/744869): This param should be deprecated after ad sampler
+// launch in favour of having a unique quota feature and param per trigger.
+// Having a single shared feature makes it impossible to run multiple trigger
+// trials simultaneously.
 extern const char kTriggerTypeAndQuotaParam[];
 
-// Default quota for ad sampler trigger.
-extern const size_t kAdSamplerTriggerDefaultQuota;
-
 enum class TriggerType {
   SECURITY_INTERSTITIAL = 1,
   AD_SAMPLE = 2,
diff --git a/components/safe_browsing/triggers/trigger_throttler_unittest.cc b/components/safe_browsing/triggers/trigger_throttler_unittest.cc
index e0288d76..386d1836 100644
--- a/components/safe_browsing/triggers/trigger_throttler_unittest.cc
+++ b/components/safe_browsing/triggers/trigger_throttler_unittest.cc
@@ -144,20 +144,20 @@
       const TriggerType trigger_type,
       const std::string& group_name,
       int quota) {
-    base::FieldTrial* trial = base::FieldTrialList::CreateFieldTrial(
-        safe_browsing::kTriggerThrottlerDailyQuotaFeature.name, group_name);
+    std::string feature_name = "";
+    std::string param_name = "";
+    GetFeatureAndParamForTrigger(trigger_type, &feature_name, &param_name);
+
+    base::FieldTrial* trial =
+        base::FieldTrialList::CreateFieldTrial(feature_name, group_name);
     std::map<std::string, std::string> feature_params;
-    feature_params[std::string(safe_browsing::kTriggerTypeAndQuotaParam)] =
-        base::StringPrintf("%d,%d", trigger_type, quota);
-    base::AssociateFieldTrialParams(
-        safe_browsing::kTriggerThrottlerDailyQuotaFeature.name, group_name,
-        feature_params);
+    feature_params[param_name] =
+        GetQuotaParamValueForTrigger(trigger_type, quota);
+    base::AssociateFieldTrialParams(feature_name, group_name, feature_params);
     std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
-    feature_list->InitializeFromCommandLine(
-        safe_browsing::kTriggerThrottlerDailyQuotaFeature.name, std::string());
+    feature_list->InitializeFromCommandLine(feature_name, std::string());
     feature_list->AssociateReportingFieldTrial(
-        safe_browsing::kTriggerThrottlerDailyQuotaFeature.name,
-        base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
+        feature_name, base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
     return feature_list;
   }
 
@@ -165,6 +165,35 @@
                                  const TriggerType trigger_type) {
     return throttler.GetDailyQuotaForTrigger(trigger_type);
   }
+
+ private:
+  void GetFeatureAndParamForTrigger(const TriggerType trigger_type,
+                                    std::string* out_feature,
+                                    std::string* out_param) {
+    switch (trigger_type) {
+      case TriggerType::AD_SAMPLE:
+        *out_feature = safe_browsing::kTriggerThrottlerDailyQuotaFeature.name;
+        *out_param = safe_browsing::kTriggerTypeAndQuotaParam;
+        break;
+
+      case TriggerType::SUSPICIOUS_SITE:
+        *out_feature = safe_browsing::kSuspiciousSiteTriggerQuotaFeature.name;
+        *out_param = safe_browsing::kSuspiciousSiteTriggerQuotaParam;
+        break;
+
+      default:
+        NOTREACHED() << "Unhandled trigger type: "
+                     << static_cast<int>(trigger_type);
+    }
+  }
+
+  std::string GetQuotaParamValueForTrigger(const TriggerType trigger_type,
+                                           int quota) {
+    if (trigger_type == TriggerType::AD_SAMPLE)
+      return base::StringPrintf("%d,%d", trigger_type, quota);
+    else
+      return base::StringPrintf("%d", quota);
+  }
 };
 
 TEST_F(TriggerThrottlerTestFinch, ConfigureQuotaViaFinch) {
diff --git a/components/spellcheck/renderer/spellcheck.cc b/components/spellcheck/renderer/spellcheck.cc
index 097917cb..c7028f62 100644
--- a/components/spellcheck/renderer/spellcheck.cc
+++ b/components/spellcheck/renderer/spellcheck.cc
@@ -65,29 +65,12 @@
   return true;
 }
 
-class DocumentMarkersRemover : public content::RenderFrameVisitor {
- public:
-  explicit DocumentMarkersRemover(const std::set<std::string>& words);
-  ~DocumentMarkersRemover() override {}
-  bool Visit(content::RenderFrame* render_frame) override;
-
- private:
-  WebVector<WebString> words_;
-  DISALLOW_COPY_AND_ASSIGN(DocumentMarkersRemover);
-};
-
-DocumentMarkersRemover::DocumentMarkersRemover(
-    const std::set<std::string>& words)
-    : words_(words.size()) {
-  std::transform(words.begin(), words.end(), words_.begin(),
+WebVector<WebString> ConvertToWebStringFromUtf8(
+    const std::set<std::string>& words) {
+  WebVector<WebString> result(words.size());
+  std::transform(words.begin(), words.end(), result.begin(),
                  [](const std::string& w) { return WebString::FromUTF8(w); });
-}
-
-bool DocumentMarkersRemover::Visit(content::RenderFrame* render_frame) {
-  // TODO(xiaochengh): Both nullptr checks seem unnecessary.
-  if (render_frame && render_frame->GetWebFrame())
-    render_frame->GetWebFrame()->RemoveSpellingMarkersUnderWords(words_);
-  return true;
+  return result;
 }
 
 bool IsApostrophe(base::char16 c) {
@@ -245,14 +228,9 @@
     const std::vector<std::string>& words_added,
     const std::vector<std::string>& words_removed) {
   const std::set<std::string> added(words_added.begin(), words_added.end());
-
+  NotifyDictionaryObservers(ConvertToWebStringFromUtf8(added));
   custom_dictionary_.OnCustomDictionaryChanged(
       added, std::set<std::string>(words_removed.begin(), words_removed.end()));
-  if (added.empty())
-    return;
-
-  DocumentMarkersRemover markersRemover(added);
-  content::RenderFrame::ForEach(&markersRemover);
 }
 
 // TODO(groby): Make sure we always have a spelling engine, even before
@@ -533,3 +511,20 @@
 #endif
   return spellcheck_enabled_;
 }
+
+void SpellCheck::AddDictionaryUpdateObserver(
+    DictionaryUpdateObserver* observer) {
+  return dictionary_update_observers_.AddObserver(observer);
+}
+
+void SpellCheck::RemoveDictionaryUpdateObserver(
+    DictionaryUpdateObserver* observer) {
+  return dictionary_update_observers_.RemoveObserver(observer);
+}
+
+void SpellCheck::NotifyDictionaryObservers(
+    const WebVector<WebString>& words_added) {
+  for (auto& observer : dictionary_update_observers_) {
+    observer.OnDictionaryUpdated(words_added);
+  }
+}
diff --git a/components/spellcheck/renderer/spellcheck.h b/components/spellcheck/renderer/spellcheck.h
index f5617c68..58debac 100644
--- a/components/spellcheck/renderer/spellcheck.h
+++ b/components/spellcheck/renderer/spellcheck.h
@@ -14,6 +14,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "base/strings/string16.h"
 #include "components/spellcheck/common/spellcheck.mojom.h"
 #include "components/spellcheck/renderer/custom_dictionary_engine.h"
@@ -28,12 +29,22 @@
 class WebTextCheckingCompletion;
 struct WebTextCheckingResult;
 template <typename T> class WebVector;
+class WebString;
 }
 
 namespace service_manager {
 class LocalInterfaceProvider;
 }
 
+class DictionaryUpdateObserver {
+ public:
+  virtual ~DictionaryUpdateObserver() = default;
+  // |words_added| is newly added words to dictionary as correct words.
+  // OnDictionaryUpdated should be called even if |words_added| empty.
+  virtual void OnDictionaryUpdated(
+      const blink::WebVector<blink::WebString>& words_added) = 0;
+};
+
 // TODO(morrita): Needs reorg with SpellCheckProvider.
 // See http://crbug.com/73699.
 // Shared spellchecking logic/data for a RenderProcess. All RenderViews use
@@ -109,6 +120,11 @@
 
   bool IsSpellcheckEnabled();
 
+  // Add observer on dictionary update event.
+  void AddDictionaryUpdateObserver(DictionaryUpdateObserver* observer);
+  // Remove observer on dictionary update event.
+  void RemoveDictionaryUpdateObserver(DictionaryUpdateObserver* observer);
+
  private:
    friend class SpellCheckTest;
    FRIEND_TEST_ALL_PREFIXES(SpellCheckTest, GetAutoCorrectionWord_EN_US);
@@ -135,6 +151,10 @@
        const std::vector<std::string>& words_added,
        const std::vector<std::string>& words_removed) override;
 
+   // Performs dictionary update notification.
+   void NotifyDictionaryObservers(
+       const blink::WebVector<blink::WebString>& words_added);
+
 #if !BUILDFLAG(USE_BROWSER_SPELLCHECKER)
   // Posts delayed spellcheck task and clear it if any.
   // Takes ownership of |request|.
@@ -166,6 +186,9 @@
   // Remember state for spellchecking.
   bool spellcheck_enabled_;
 
+  // Observers of update dictionary events.
+  base::ObserverList<DictionaryUpdateObserver> dictionary_update_observers_;
+
   base::WeakPtrFactory<SpellCheck> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SpellCheck);
diff --git a/components/spellcheck/renderer/spellcheck_provider.cc b/components/spellcheck/renderer/spellcheck_provider.cc
index 8bf21f6..1e255d2 100644
--- a/components/spellcheck/renderer/spellcheck_provider.cc
+++ b/components/spellcheck/renderer/spellcheck_provider.cc
@@ -38,6 +38,44 @@
                   int(SpellCheckResult::GRAMMAR),
               "mismatching enums");
 
+class SpellCheckProvider::DictionaryUpdateObserverImpl
+    : public DictionaryUpdateObserver {
+ public:
+  explicit DictionaryUpdateObserverImpl(SpellCheckProvider* owner);
+  ~DictionaryUpdateObserverImpl() override;
+
+  // DictionaryUpdateObserver:
+  void OnDictionaryUpdated(const WebVector<WebString>& words_added) override;
+
+ private:
+  SpellCheckProvider* owner_;
+};
+
+SpellCheckProvider::DictionaryUpdateObserverImpl::DictionaryUpdateObserverImpl(
+    SpellCheckProvider* owner)
+    : owner_(owner) {
+  owner_->spellcheck_->AddDictionaryUpdateObserver(this);
+}
+
+SpellCheckProvider::DictionaryUpdateObserverImpl::
+    ~DictionaryUpdateObserverImpl() {
+  owner_->spellcheck_->RemoveDictionaryUpdateObserver(this);
+}
+
+void SpellCheckProvider::DictionaryUpdateObserverImpl::OnDictionaryUpdated(
+    const WebVector<WebString>& words_added) {
+  // Clear only cache. Current pending requests should continue as they are.
+  owner_->last_request_.clear();
+  owner_->last_results_.Assign(
+      blink::WebVector<blink::WebTextCheckingResult>());
+
+  // owner_->render_frame() is nullptr in unit tests.
+  if (auto* render_frame = owner_->render_frame()) {
+    DCHECK(render_frame->GetWebFrame());
+    render_frame->GetWebFrame()->RemoveSpellingMarkersUnderWords(words_added);
+  }
+}
+
 SpellCheckProvider::SpellCheckProvider(
     content::RenderFrame* render_frame,
     SpellCheck* spellcheck,
@@ -51,11 +89,18 @@
   DCHECK(embedder_provider);
   if (render_frame)  // NULL in unit tests.
     render_frame->GetWebFrame()->SetTextCheckClient(this);
+
+  dictionary_update_observer_ =
+      std::make_unique<DictionaryUpdateObserverImpl>(this);
 }
 
 SpellCheckProvider::~SpellCheckProvider() {
 }
 
+void SpellCheckProvider::ResetDictionaryUpdateObserverForTesting() {
+  dictionary_update_observer_.reset();
+}
+
 spellcheck::mojom::SpellCheckHost& SpellCheckProvider::GetSpellCheckHost() {
   if (spell_check_host_)
     return *spell_check_host_;
diff --git a/components/spellcheck/renderer/spellcheck_provider.h b/components/spellcheck/renderer/spellcheck_provider.h
index 79b55aa..d142707 100644
--- a/components/spellcheck/renderer/spellcheck_provider.h
+++ b/components/spellcheck/renderer/spellcheck_provider.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_H_
 #define COMPONENTS_SPELLCHECK_RENDERER_SPELLCHECK_PROVIDER_H_
 
+#include <memory>
 #include <vector>
 
 #include "base/containers/id_map.h"
@@ -63,12 +64,16 @@
 
  private:
   friend class TestingSpellCheckProvider;
+  class DictionaryUpdateObserverImpl;
 
   // Sets the SpellCheckHost (for unit tests).
   void SetSpellCheckHostForTesting(spellcheck::mojom::SpellCheckHostPtr host) {
     spell_check_host_ = std::move(host);
   }
 
+  // Reset dictionary_update_observer_ in TestingSpellCheckProvider dtor.
+  void ResetDictionaryUpdateObserverForTesting();
+
   // Returns the SpellCheckHost.
   spellcheck::mojom::SpellCheckHost& GetSpellCheckHost();
 
@@ -129,6 +134,9 @@
   // Interface to the SpellCheckHost.
   spellcheck::mojom::SpellCheckHostPtr spell_check_host_;
 
+  // Dictionary updated observer.
+  std::unique_ptr<DictionaryUpdateObserverImpl> dictionary_update_observer_;
+
   base::WeakPtrFactory<SpellCheckProvider> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SpellCheckProvider);
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.cc b/components/spellcheck/renderer/spellcheck_provider_test.cc
index 9c1b6a33..94fb1eb 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_test.cc
@@ -44,6 +44,8 @@
 
 TestingSpellCheckProvider::~TestingSpellCheckProvider() {
   binding_.Close();
+  // dictionary_update_observer_ must be released before deleting spellcheck_.
+  ResetDictionaryUpdateObserverForTesting();
   delete spellcheck_;
 }
 
diff --git a/components/spellcheck/renderer/spellcheck_provider_test.h b/components/spellcheck/renderer/spellcheck_provider_test.h
index 5e0cfbf0..9e8534e 100644
--- a/components/spellcheck/renderer/spellcheck_provider_test.h
+++ b/components/spellcheck/renderer/spellcheck_provider_test.h
@@ -70,6 +70,9 @@
   std::vector<RequestTextCheckParams> text_check_requests_;
 #endif
 
+  // Returns |spellcheck|.
+  SpellCheck* spellcheck() { return spellcheck_; }
+
  private:
   // spellcheck::mojom::SpellCheckHost:
   void RequestDictionary() override;
diff --git a/components/spellcheck/renderer/spellcheck_provider_unittest.cc b/components/spellcheck/renderer/spellcheck_provider_unittest.cc
index 555e718..64d9f3c 100644
--- a/components/spellcheck/renderer/spellcheck_provider_unittest.cc
+++ b/components/spellcheck/renderer/spellcheck_provider_unittest.cc
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/strings/utf_string_conversions.h"
 #include "components/spellcheck/renderer/spellcheck_provider_test.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/spellcheck/renderer/spellcheck.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
@@ -12,7 +14,17 @@
 
 namespace {
 
-class SpellCheckProviderCacheTest : public SpellCheckProviderTest {};
+class SpellCheckProviderCacheTest : public SpellCheckProviderTest {
+ protected:
+  void UpdateCustomDictionary() {
+    SpellCheck* spellcheck = provider_.spellcheck();
+    EXPECT_NE(spellcheck, nullptr);
+    // Skip adding friend class - use public CustomDictionaryChanged from
+    // |spellcheck::mojom::SpellChecker|
+    static_cast<spellcheck::mojom::SpellChecker*>(spellcheck)
+        ->CustomDictionaryChanged({}, {});
+  }
+};
 
 TEST_F(SpellCheckProviderCacheTest, SubstringWithoutMisspellings) {
   FakeTextCheckingCompletion completion;
@@ -49,4 +61,17 @@
   EXPECT_EQ(completion.completion_count_, 0U);
 }
 
+TEST_F(SpellCheckProviderCacheTest, ResetCacheOnCustomDictionaryUpdate) {
+  FakeTextCheckingCompletion completion;
+
+  blink::WebVector<blink::WebTextCheckingResult> last_results;
+  provider_.SetLastResults(base::ASCIIToUTF16("This is a test"), last_results);
+
+  UpdateCustomDictionary();
+
+  EXPECT_FALSE(provider_.SatisfyRequestFromCache(
+      base::ASCIIToUTF16("This is a"), &completion));
+  EXPECT_EQ(completion.completion_count_, 0U);
+}
+
 }  // namespace
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index f83e796..280f6a10 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -141,8 +141,6 @@
     "driver/sync_error_controller.h",
     "driver/sync_service.cc",
     "driver/sync_service.h",
-    "driver/sync_service_base.cc",
-    "driver/sync_service_base.h",
     "driver/sync_service_crypto.cc",
     "driver/sync_service_crypto.h",
     "driver/sync_service_observer.cc",
diff --git a/components/sync/driver/signin_manager_wrapper.cc b/components/sync/driver/signin_manager_wrapper.cc
index 7862741..2dddca8 100644
--- a/components/sync/driver/signin_manager_wrapper.cc
+++ b/components/sync/driver/signin_manager_wrapper.cc
@@ -20,11 +20,3 @@
 SigninManagerBase* SigninManagerWrapper::GetSigninManager() {
   return signin_manager_;
 }
-
-std::string SigninManagerWrapper::GetEffectiveUsername() const {
-  return identity_manager_->GetPrimaryAccountInfo().email;
-}
-
-std::string SigninManagerWrapper::GetAccountIdToUse() const {
-  return identity_manager_->GetPrimaryAccountInfo().account_id;
-}
diff --git a/components/sync/driver/signin_manager_wrapper.h b/components/sync/driver/signin_manager_wrapper.h
index 8058d04..c4f750b 100644
--- a/components/sync/driver/signin_manager_wrapper.h
+++ b/components/sync/driver/signin_manager_wrapper.h
@@ -5,8 +5,6 @@
 #ifndef COMPONENTS_SYNC_DRIVER_SIGNIN_MANAGER_WRAPPER_H_
 #define COMPONENTS_SYNC_DRIVER_SIGNIN_MANAGER_WRAPPER_H_
 
-#include <string>
-
 #include "base/macros.h"
 
 class SigninManagerBase;
@@ -23,12 +21,6 @@
                                 SigninManagerBase* signin_manager);
   ~SigninManagerWrapper();
 
-  // Get the email address to use for this account.
-  std::string GetEffectiveUsername() const;
-
-  // Get the unique ID used to represent this account.
-  std::string GetAccountIdToUse() const;
-
   // Return the original IdentityManager object that was passed in.
   identity::IdentityManager* GetIdentityManager();
 
diff --git a/components/sync/driver/sync_service_base.cc b/components/sync/driver/sync_service_base.cc
deleted file mode 100644
index f31158d3..0000000
--- a/components/sync/driver/sync_service_base.cc
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/driver/sync_service_base.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/path_service.h"
-#include "base/syslog_logging.h"
-#include "components/invalidation/public/invalidation_service.h"
-#include "components/signin/core/browser/account_info.h"
-#include "components/sync/base/report_unrecoverable_error.h"
-#include "components/sync/device_info/local_device_info_provider.h"
-#include "components/sync/driver/sync_driver_switches.h"
-#include "components/sync/engine/engine_components_factory_impl.h"
-#include "components/sync/engine/polling_constants.h"
-#include "services/identity/public/cpp/identity_manager.h"
-
-namespace syncer {
-
-namespace {
-
-const base::FilePath::CharType kSyncDataFolderName[] =
-    FILE_PATH_LITERAL("Sync Data");
-
-const base::FilePath::CharType kLevelDBFolderName[] =
-    FILE_PATH_LITERAL("LevelDB");
-
-EngineComponentsFactory::Switches EngineSwitchesFromCommandLine() {
-  EngineComponentsFactory::Switches factory_switches = {
-      EngineComponentsFactory::ENCRYPTION_KEYSTORE,
-      EngineComponentsFactory::BACKOFF_NORMAL};
-
-  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
-  if (cl->HasSwitch(switches::kSyncShortInitialRetryOverride)) {
-    factory_switches.backoff_override =
-        EngineComponentsFactory::BACKOFF_SHORT_INITIAL_RETRY_OVERRIDE;
-  }
-  if (cl->HasSwitch(switches::kSyncEnableGetUpdateAvoidance)) {
-    factory_switches.pre_commit_updates_policy =
-        EngineComponentsFactory::FORCE_ENABLE_PRE_COMMIT_UPDATE_AVOIDANCE;
-  }
-  if (cl->HasSwitch(switches::kSyncShortNudgeDelayForTest)) {
-    factory_switches.nudge_delay =
-        EngineComponentsFactory::NudgeDelay::SHORT_NUDGE_DELAY;
-  }
-  return factory_switches;
-}
-
-}  // namespace
-
-SyncServiceBase::SyncServiceBase(std::unique_ptr<SyncClient> sync_client,
-                                 std::unique_ptr<SigninManagerWrapper> signin,
-                                 const version_info::Channel& channel,
-                                 const base::FilePath& base_directory,
-                                 const std::string& debug_identifier)
-    : sync_client_(std::move(sync_client)),
-      signin_(std::move(signin)),
-      channel_(channel),
-      base_directory_(base_directory),
-      debug_identifier_(debug_identifier),
-      sync_prefs_(sync_client_->GetPrefService()) {
-  ResetCryptoState();
-}
-
-SyncServiceBase::~SyncServiceBase() = default;
-
-void SyncServiceBase::AddObserver(SyncServiceObserver* observer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  observers_.AddObserver(observer);
-}
-
-void SyncServiceBase::RemoveObserver(SyncServiceObserver* observer) {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  observers_.RemoveObserver(observer);
-}
-
-bool SyncServiceBase::HasObserver(const SyncServiceObserver* observer) const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return observers_.HasObserver(observer);
-}
-
-AccountInfo SyncServiceBase::GetAuthenticatedAccountInfo() const {
-  DCHECK(thread_checker_.CalledOnValidThread());
-  return signin_ ? signin_->GetIdentityManager()->GetPrimaryAccountInfo()
-                 : AccountInfo();
-}
-
-// static
-base::FilePath SyncServiceBase::FormatSyncDataPath(
-    const base::FilePath& base_directory) {
-  return base_directory.Append(base::FilePath(kSyncDataFolderName));
-}
-
-// static
-base::FilePath SyncServiceBase::FormatSharedModelTypeStorePath(
-    const base::FilePath& base_directory) {
-  return FormatSyncDataPath(base_directory)
-      .Append(base::FilePath(kLevelDBFolderName));
-}
-
-void SyncServiceBase::NotifyObservers() {
-  for (auto& observer : observers_) {
-    observer.OnStateChanged(this);
-  }
-}
-
-void SyncServiceBase::InitializeEngine() {
-  DCHECK(engine_);
-
-  if (!sync_thread_) {
-    sync_thread_ = std::make_unique<base::Thread>("Chrome_SyncThread");
-    base::Thread::Options options;
-    options.timer_slack = base::TIMER_SLACK_MAXIMUM;
-    bool success = sync_thread_->StartWithOptions(options);
-    DCHECK(success);
-  }
-
-  SyncEngine::InitParams params;
-  params.sync_task_runner = sync_thread_->task_runner();
-  params.host = this;
-  params.registrar = std::make_unique<SyncBackendRegistrar>(
-      debug_identifier_, base::Bind(&SyncClient::CreateModelWorkerForGroup,
-                                    base::Unretained(sync_client_.get())));
-  params.encryption_observer_proxy = crypto_->GetEncryptionObserverProxy();
-  params.extensions_activity = sync_client_->GetExtensionsActivity();
-  params.event_handler = GetJsEventHandler();
-  params.service_url = sync_service_url();
-  params.sync_user_agent = GetLocalDeviceInfoProvider()->GetSyncUserAgent();
-  params.http_factory_getter = MakeHttpPostProviderFactoryGetter();
-  params.credentials = GetCredentials();
-  invalidation::InvalidationService* invalidator =
-      sync_client_->GetInvalidationService();
-  params.invalidator_client_id =
-      invalidator ? invalidator->GetInvalidatorClientId() : "",
-  params.sync_manager_factory = std::make_unique<SyncManagerFactory>();
-  // The first time we start up the engine we want to ensure we have a clean
-  // directory, so delete any old one that might be there.
-  params.delete_sync_data_folder = !IsFirstSetupComplete();
-  params.enable_local_sync_backend = sync_prefs_.IsLocalSyncEnabled();
-  params.local_sync_backend_folder = sync_client_->GetLocalSyncBackendFolder();
-  params.restored_key_for_bootstrapping =
-      sync_prefs_.GetEncryptionBootstrapToken();
-  params.restored_keystore_key_for_bootstrapping =
-      sync_prefs_.GetKeystoreEncryptionBootstrapToken();
-  params.engine_components_factory =
-      std::make_unique<EngineComponentsFactoryImpl>(
-          EngineSwitchesFromCommandLine());
-  params.unrecoverable_error_handler = GetUnrecoverableErrorHandler();
-  params.report_unrecoverable_error_function =
-      base::Bind(ReportUnrecoverableError, channel_);
-  params.saved_nigori_state = crypto_->TakeSavedNigoriState();
-  sync_prefs_.GetInvalidationVersions(&params.invalidation_versions);
-  params.short_poll_interval = sync_prefs_.GetShortPollInterval();
-  if (params.short_poll_interval.is_zero()) {
-    params.short_poll_interval =
-        base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds);
-  }
-  params.long_poll_interval = sync_prefs_.GetLongPollInterval();
-  if (params.long_poll_interval.is_zero()) {
-    params.long_poll_interval =
-        base::TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds);
-  }
-
-  engine_->Initialize(std::move(params));
-}
-
-void SyncServiceBase::ResetCryptoState() {
-  crypto_ = std::make_unique<SyncServiceCrypto>(
-      base::BindRepeating(&SyncServiceBase::NotifyObservers,
-                          base::Unretained(this)),
-      base::BindRepeating(&SyncService::GetPreferredDataTypes,
-                          base::Unretained(this)),
-      &sync_prefs_);
-}
-
-}  // namespace syncer
diff --git a/components/sync/driver/sync_service_base.h b/components/sync/driver/sync_service_base.h
deleted file mode 100644
index 1f97abf4..0000000
--- a/components/sync/driver/sync_service_base.h
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_SYNC_DRIVER_SYNC_SERVICE_BASE_H_
-#define COMPONENTS_SYNC_DRIVER_SYNC_SERVICE_BASE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/observer_list.h"
-#include "base/threading/thread.h"
-#include "base/threading/thread_checker.h"
-#include "components/sync/base/sync_prefs.h"
-#include "components/sync/base/unrecoverable_error_handler.h"
-#include "components/sync/base/weak_handle.h"
-#include "components/sync/driver/signin_manager_wrapper.h"
-#include "components/sync/driver/sync_client.h"
-#include "components/sync/driver/sync_service.h"
-#include "components/sync/driver/sync_service_crypto.h"
-#include "components/sync/engine/sync_engine.h"
-#include "components/sync/engine/sync_engine_host.h"
-#include "components/sync/engine/sync_manager.h"
-#include "components/sync/js/sync_js_controller.h"
-#include "components/version_info/version_info.h"
-
-namespace syncer {
-
-// This is a base class for implementations of SyncService that contains some
-// common functionality and member variables. Anything that can live inside the
-// sync component should eventually live here instead of a concrete
-// implementation. This is set up as a base class so things can be transferred
-// piece by piece as easily as possible.
-class SyncServiceBase : public SyncService, public SyncEngineHost {
- public:
-  SyncServiceBase(std::unique_ptr<SyncClient> sync_client,
-                  std::unique_ptr<SigninManagerWrapper> signin,
-                  const version_info::Channel& channel,
-                  const base::FilePath& base_directory,
-                  const std::string& debug_identifier);
-  ~SyncServiceBase() override;
-
-  // SyncService partial implementation.
-  void AddObserver(SyncServiceObserver* observer) override;
-  void RemoveObserver(SyncServiceObserver* observer) override;
-  bool HasObserver(const SyncServiceObserver* observer) const override;
-  AccountInfo GetAuthenticatedAccountInfo() const override;
-
-  // Given base path (path to profile) formats path to "Sync Data" folder where
-  // sync engine stores directory database.
-  static base::FilePath FormatSyncDataPath(
-      const base::FilePath& base_directory);
-
-  // Given base path (path to profile) formats path to a folder containing
-  // ModelTypeStore's leveldb database.
-  static base::FilePath FormatSharedModelTypeStorePath(
-      const base::FilePath& base_directory);
-
- protected:
-  // Notify all observers that a change has occurred.
-  void NotifyObservers();
-
-  // Kicks off asynchronous initialization of the SyncEngine.
-  void InitializeEngine();
-
-  // Destroys the |crypto_| object and creates a new one with fresh state.
-  void ResetCryptoState();
-
-  // Returns SyncCredentials from the OAuth2TokenService.
-  virtual SyncCredentials GetCredentials() = 0;
-
-  // Returns a weak handle to the JsEventHandler.
-  virtual WeakHandle<JsEventHandler> GetJsEventHandler() = 0;
-
-  // Returns a callback that makes an HttpPostProviderFactory.
-  virtual SyncEngine::HttpPostProviderFactoryGetter
-  MakeHttpPostProviderFactoryGetter() = 0;
-
-  // Returns a weak handle to an UnrecoverableErrorHandler (may be |this|).
-  virtual WeakHandle<UnrecoverableErrorHandler>
-  GetUnrecoverableErrorHandler() = 0;
-
-  // This profile's SyncClient, which abstracts away non-Sync dependencies and
-  // the Sync API component factory.
-  const std::unique_ptr<SyncClient> sync_client_;
-
-  // Encapsulates user signin - used to set/get the user's authenticated
-  // email address.
-  const std::unique_ptr<SigninManagerWrapper> signin_;
-
-  // The product channel of the embedder.
-  const version_info::Channel channel_;
-
-  // The path to the base directory under which sync should store its
-  // information.
-  const base::FilePath base_directory_;
-
-  // An identifier representing this instance for debugging purposes.
-  const std::string debug_identifier_;
-
-  // The class that handles getting, setting, and persisting sync
-  // preferences.
-  SyncPrefs sync_prefs_;
-
-  // A utility object containing logic and state relating to encryption. It is
-  // never null.
-  std::unique_ptr<SyncServiceCrypto> crypto_;
-
-  // The thread where all the sync operations happen. This thread is kept alive
-  // until browser shutdown and reused if sync is turned off and on again. It is
-  // joined during the shutdown process, but there is an abort mechanism in
-  // place to prevent slow HTTP requests from blocking browser shutdown.
-  std::unique_ptr<base::Thread> sync_thread_;
-
-  // Our asynchronous engine to communicate with sync components living on
-  // other threads.
-  std::unique_ptr<SyncEngine> engine_;
-
-  // The list of observers of the SyncService state.
-  base::ObserverList<SyncServiceObserver> observers_;
-
-  // Used to ensure that certain operations are performed on the thread that
-  // this object was created on.
-  base::ThreadChecker thread_checker_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SyncServiceBase);
-};
-
-}  // namespace syncer
-
-#endif  // COMPONENTS_SYNC_DRIVER_SYNC_SERVICE_BASE_H_
diff --git a/components/sync_preferences/BUILD.gn b/components/sync_preferences/BUILD.gn
index a3ae9d25..49ca951 100644
--- a/components/sync_preferences/BUILD.gn
+++ b/components/sync_preferences/BUILD.gn
@@ -63,6 +63,7 @@
     ":test_support",
     "//components/pref_registry",
     "//components/prefs",
+    "//components/prefs:test_support",
     "//components/sync",
     "//components/sync:test_support_model",
     "//testing/gtest",
diff --git a/components/sync_preferences/pref_model_associator.cc b/components/sync_preferences/pref_model_associator.cc
index 401d614..2aeff8db 100644
--- a/components/sync_preferences/pref_model_associator.cc
+++ b/components/sync_preferences/pref_model_associator.cc
@@ -375,7 +375,7 @@
     // Windows client, the Windows client does not support
     // kConfirmToQuitEnabled. Ignore updates from these preferences.
     std::string pref_name = pref_specifics.name();
-    if (!IsPrefRegistered(pref_name.c_str()))
+    if (!IsPrefRegistered(pref_name))
       continue;
 
     if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
@@ -406,6 +406,7 @@
   return syncer::SyncError();
 }
 
+// static
 base::Value* PrefModelAssociator::ReadPreferenceSpecifics(
     const sync_pb::PreferenceSpecifics& preference) {
   base::JSONReader reader;
@@ -425,10 +426,10 @@
 
 void PrefModelAssociator::AddSyncedPrefObserver(const std::string& name,
                                                 SyncedPrefObserver* observer) {
-  std::unique_ptr<SyncedPrefObserverList>& observers =
+  std::unique_ptr<base::ObserverList<SyncedPrefObserver>>& observers =
       synced_pref_observers_[name];
   if (!observers)
-    observers = std::make_unique<SyncedPrefObserverList>();
+    observers = std::make_unique<base::ObserverList<SyncedPrefObserver>>();
 
   observers->AddObserver(observer);
 }
@@ -439,18 +440,7 @@
   auto observer_iter = synced_pref_observers_.find(name);
   if (observer_iter == synced_pref_observers_.end())
     return;
-  SyncedPrefObserverList* observers = observer_iter->second.get();
-  observers->RemoveObserver(observer);
-}
-
-void PrefModelAssociator::SetPrefModelAssociatorClientForTesting(
-    const PrefModelAssociatorClient* client) {
-  DCHECK(!client_);
-  client_ = client;
-}
-
-std::set<std::string> PrefModelAssociator::registered_preferences() const {
-  return registered_preferences_;
+  observer_iter->second->RemoveObserver(observer);
 }
 
 void PrefModelAssociator::RegisterPref(const char* name) {
@@ -458,7 +448,7 @@
   registered_preferences_.insert(name);
 }
 
-bool PrefModelAssociator::IsPrefRegistered(const char* name) {
+bool PrefModelAssociator::IsPrefRegistered(const std::string& name) const {
   return registered_preferences_.count(name) > 0;
 }
 
@@ -475,7 +465,7 @@
   if (!preference)
     return;
 
-  if (!IsPrefRegistered(name.c_str()))
+  if (!IsPrefRegistered(name))
     return;  // We are not syncing this preference.
 
   syncer::SyncChangeList changes;
@@ -522,8 +512,7 @@
   auto observer_iter = synced_pref_observers_.find(path);
   if (observer_iter == synced_pref_observers_.end())
     return;
-  SyncedPrefObserverList* observers = observer_iter->second.get();
-  for (auto& observer : *observers)
+  for (auto& observer : *observer_iter->second)
     observer.OnSyncedPrefChanged(path, from_sync);
 }
 
diff --git a/components/sync_preferences/pref_model_associator.h b/components/sync_preferences/pref_model_associator.h
index 8832be19..d47a36b4 100644
--- a/components/sync_preferences/pref_model_associator.h
+++ b/components/sync_preferences/pref_model_associator.h
@@ -61,10 +61,6 @@
       std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) override;
   void StopSyncing(syncer::ModelType type) override;
 
-  // Returns the list of preference names that are registered as syncable, and
-  // hence should be monitored for changes.
-  std::set<std::string> registered_preferences() const;
-
   // Register a preference with the specified name for syncing. We do not care
   // about the type at registration time, but when changes arrive from the
   // syncer, we check if they can be applied and if not drop them.
@@ -72,9 +68,6 @@
   // begins).
   virtual void RegisterPref(const char* name);
 
-  // Returns true if the specified preference is registered for syncing.
-  virtual bool IsPrefRegistered(const char* name);
-
   // Process a local preference change. This can trigger new SyncChanges being
   // sent to the syncer.
   virtual void ProcessPrefChange(const std::string& name);
@@ -95,15 +88,13 @@
   bool CreatePrefSyncData(const std::string& name,
                           const base::Value& value,
                           syncer::SyncData* sync_data) const;
-
-  // Extract preference value from sync specifics.
-  base::Value* ReadPreferenceSpecifics(
-      const sync_pb::PreferenceSpecifics& specifics);
-
   // Returns true if the pref under the given name is pulled down from sync.
   // Note this does not refer to SYNCABLE_PREF.
   bool IsPrefSynced(const std::string& name) const;
 
+  // Returns true if the specified preference is registered for syncing.
+  bool IsPrefRegistered(const std::string& name) const;
+
   // Adds a SyncedPrefObserver to watch for changes to a specific pref.
   void AddSyncedPrefObserver(const std::string& name,
                              SyncedPrefObserver* observer);
@@ -115,19 +106,13 @@
   // Returns the PrefModelAssociatorClient for this object.
   const PrefModelAssociatorClient* client() const { return client_; }
 
-  // Set the PrefModelAssociatorClient to use for that object during tests.
-  void SetPrefModelAssociatorClientForTesting(
-      const PrefModelAssociatorClient* client);
-
   // Register callback method which will get called at the end of
   // PrefModelAssociator::MergeDataAndStartSyncing().
   void RegisterMergeDataFinishedCallback(const base::Closure& callback);
 
- protected:
+ private:
   friend class PrefServiceSyncableTest;
 
-  typedef std::map<std::string, syncer::SyncData> SyncDataMap;
-
   // Create an association for a given preference. If |sync_pref| is valid,
   // signifying that sync has data for this preference, we reconcile their data
   // with ours and append a new UPDATE SyncChange to |sync_changes|. If
@@ -143,9 +128,14 @@
   static std::unique_ptr<base::Value> MergeListValues(
       const base::Value& from_value,
       const base::Value& to_value);
+
   static base::Value MergeDictionaryValues(const base::Value& from_value,
                                            const base::Value& to_value);
 
+  // Extract preference value from sync specifics.
+  static base::Value* ReadPreferenceSpecifics(
+      const sync_pb::PreferenceSpecifics& specifics);
+
   // Do we have an active association between the preferences and sync models?
   // Set when start syncing, reset in StopSyncing. While this is not set, we
   // ignore any local preference changes (when we start syncing we will look
@@ -184,17 +174,14 @@
   // PRIORITY_PREFERENCES.
   syncer::ModelType type_;
 
- private:
+  void NotifySyncedPrefObservers(const std::string& path, bool from_sync) const;
+
   // Map prefs to lists of observers. Observers will receive notification when
   // a pref changes, including the detail of whether or not the change came
   // from sync.
-  using SyncedPrefObserverList = base::ObserverList<SyncedPrefObserver>;
-  using SyncedPrefObserverMap =
-      base::hash_map<std::string, std::unique_ptr<SyncedPrefObserverList>>;
-
-  void NotifySyncedPrefObservers(const std::string& path, bool from_sync) const;
-
-  SyncedPrefObserverMap synced_pref_observers_;
+  base::hash_map<std::string,
+                 std::unique_ptr<base::ObserverList<SyncedPrefObserver>>>
+      synced_pref_observers_;
   const PrefModelAssociatorClient* client_;  // Weak.
 
   std::vector<base::Closure> callback_list_;
diff --git a/components/sync_preferences/pref_service_syncable.cc b/components/sync_preferences/pref_service_syncable.cc
index 96e192db..478d278a 100644
--- a/components/sync_preferences/pref_service_syncable.cc
+++ b/components/sync_preferences/pref_service_syncable.cc
@@ -174,15 +174,6 @@
   pref_sync_associator_.RegisterMergeDataFinishedCallback(callback);
 }
 
-// Set the PrefModelAssociatorClient to use for that object during tests.
-void PrefServiceSyncable::SetPrefModelAssociatorClientForTesting(
-    const PrefModelAssociatorClient* pref_model_associator_client) {
-  pref_sync_associator_.SetPrefModelAssociatorClientForTesting(
-      pref_model_associator_client);
-  priority_pref_sync_associator_.SetPrefModelAssociatorClientForTesting(
-      pref_model_associator_client);
-}
-
 void PrefServiceSyncable::AddRegisteredSyncablePreference(
     const std::string& path,
     uint32_t flags) {
diff --git a/components/sync_preferences/pref_service_syncable.h b/components/sync_preferences/pref_service_syncable.h
index f425965..656b10e8 100644
--- a/components/sync_preferences/pref_service_syncable.h
+++ b/components/sync_preferences/pref_service_syncable.h
@@ -40,7 +40,7 @@
       std::unique_ptr<PrefValueStore> pref_value_store,
       scoped_refptr<PersistentPrefStore> user_prefs,
       scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry,
-      const PrefModelAssociatorClient* pref_model_associato_client,
+      const PrefModelAssociatorClient* pref_model_associator_client,
       base::RepeatingCallback<void(PersistentPrefStore::PrefReadError)>
           read_error_callback,
       bool async);
@@ -91,11 +91,6 @@
   void RemoveSyncedPrefObserver(const std::string& name,
                                 SyncedPrefObserver* observer);
 
- protected:
-  // Set the PrefModelAssociatorClient to use for that object during tests.
-  void SetPrefModelAssociatorClientForTesting(
-      const PrefModelAssociatorClient* pref_model_associator_client);
-
  private:
   friend class PrefModelAssociator;
 
diff --git a/components/sync_preferences/pref_service_syncable_unittest.cc b/components/sync_preferences/pref_service_syncable_unittest.cc
index bc0d19b..1129b29 100644
--- a/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -15,7 +15,9 @@
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_notifier_impl.h"
 #include "components/prefs/scoped_user_pref_update.h"
+#include "components/prefs/testing_pref_store.h"
 #include "components/sync/model/sync_change.h"
 #include "components/sync/model/sync_data.h"
 #include "components/sync/model/sync_error_factory_mock.h"
@@ -29,6 +31,7 @@
 
 using syncer::SyncChange;
 using syncer::SyncData;
+using testing::NotNull;
 
 namespace sync_preferences {
 
@@ -39,6 +42,7 @@
 const char kExampleUrl2[] = "http://example.com/2";
 const char kStringPrefName[] = "string_pref_name";
 const char kListPrefName[] = "list_pref_name";
+const char kDictPrefName[] = "dict_pref_name";
 const char kUnsyncedPreferenceName[] = "nonsense_pref_name";
 const char kUnsyncedPreferenceDefaultValue[] = "default";
 const char kDefaultCharsetPrefName[] = "default_charset";
@@ -49,25 +53,6 @@
   (*num)++;
 }
 
-class TestPrefModelAssociatorClient : public PrefModelAssociatorClient {
- public:
-  TestPrefModelAssociatorClient() {}
-  ~TestPrefModelAssociatorClient() override {}
-
-  // PrefModelAssociatorClient implementation.
-  bool IsMergeableListPreference(const std::string& pref_name) const override {
-    return pref_name == kListPrefName;
-  }
-
-  bool IsMergeableDictionaryPreference(
-      const std::string& pref_name) const override {
-    return false;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(TestPrefModelAssociatorClient);
-};
-
 class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
  public:
   explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
@@ -100,11 +85,9 @@
  public:
   PrefServiceSyncableTest()
       : pref_sync_service_(nullptr),
-        test_processor_(nullptr),
         next_pref_remote_sync_node_id_(0) {}
 
   void SetUp() override {
-    prefs_.SetPrefModelAssociatorClientForTesting(&client_);
     prefs_.registry()->RegisterStringPref(kUnsyncedPreferenceName,
                                           kUnsyncedPreferenceDefaultValue);
     prefs_.registry()->RegisterStringPref(
@@ -116,7 +99,7 @@
         kDefaultCharsetPrefName, kDefaultCharsetValue,
         user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 
-    pref_sync_service_ = reinterpret_cast<PrefModelAssociator*>(
+    pref_sync_service_ = static_cast<PrefModelAssociator*>(
         prefs_.GetSyncableService(syncer::PREFERENCES));
     ASSERT_TRUE(pref_sync_service_);
   }
@@ -154,9 +137,9 @@
 
   void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data,
                                   syncer::SyncChangeList* output) {
-    test_processor_ = new TestSyncProcessorStub(output);
     syncer::SyncMergeResult r = pref_sync_service_->MergeDataAndStartSyncing(
-        syncer::PREFERENCES, initial_data, base::WrapUnique(test_processor_),
+        syncer::PREFERENCES, initial_data,
+        std::make_unique<TestSyncProcessorStub>(output),
         std::make_unique<syncer::SyncErrorFactoryMock>());
     EXPECT_FALSE(r.error().IsSet());
   }
@@ -184,22 +167,20 @@
   }
 
   bool IsSynced(const std::string& pref_name) {
-    return pref_sync_service_->registered_preferences().count(pref_name) > 0;
+    return pref_sync_service_->IsPrefSynced(pref_name);
   }
 
-  bool HasSyncData(const std::string& pref_name) {
-    return pref_sync_service_->IsPrefSynced(pref_name);
+  bool IsRegistered(const std::string& pref_name) {
+    return pref_sync_service_->IsPrefRegistered(pref_name.c_str());
   }
 
   PrefService* GetPrefs() { return &prefs_; }
   TestingPrefServiceSyncable* GetTestingPrefService() { return &prefs_; }
 
  protected:
-  TestPrefModelAssociatorClient client_;
   TestingPrefServiceSyncable prefs_;
 
   PrefModelAssociator* pref_sync_service_;
-  TestSyncProcessorStub* test_processor_;
 
   int next_pref_remote_sync_node_id_;
 };
@@ -228,7 +209,7 @@
   syncer::SyncChangeList out;
   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
 
-  EXPECT_TRUE(IsSynced(kStringPrefName));
+  EXPECT_TRUE(IsRegistered(kStringPrefName));
   EXPECT_TRUE(pref->IsDefaultValue());
   EXPECT_FALSE(FindValue(kStringPrefName, out).get());
 }
@@ -258,7 +239,6 @@
     ListPrefUpdate update(GetPrefs(), kListPrefName);
     base::ListValue* url_list = update.Get();
     url_list->AppendString(kExampleUrl0);
-    url_list->AppendString(kExampleUrl1);
   }
 
   syncer::SyncDataList in;
@@ -266,7 +246,6 @@
   AddToRemoteDataList(kStringPrefName, base::Value(kExampleUrl1), &in);
   base::ListValue urls_to_restore;
   urls_to_restore.AppendString(kExampleUrl1);
-  urls_to_restore.AppendString(kExampleUrl2);
   AddToRemoteDataList(kListPrefName, urls_to_restore, &in);
   AddToRemoteDataList(kDefaultCharsetPrefName,
                       base::Value(kNonDefaultCharsetValue), &in);
@@ -277,15 +256,259 @@
 
   EXPECT_EQ(kExampleUrl1, prefs_.GetString(kStringPrefName));
 
+  // No associator client is registered, so lists and dictionaries should not
+  // get merged (remote write wins).
+  auto expected_urls = std::make_unique<base::ListValue>();
+  expected_urls->AppendString(kExampleUrl1);
+  EXPECT_FALSE(FindValue(kListPrefName, out));
+  EXPECT_TRUE(GetPreferenceValue(kListPrefName).Equals(expected_urls.get()));
+  EXPECT_EQ(kNonDefaultCharsetValue, prefs_.GetString(kDefaultCharsetPrefName));
+}
+
+class TestPrefModelAssociatorClient : public PrefModelAssociatorClient {
+ public:
+  TestPrefModelAssociatorClient() {}
+  ~TestPrefModelAssociatorClient() override {}
+
+  // PrefModelAssociatorClient implementation.
+  bool IsMergeableListPreference(const std::string& pref_name) const override {
+    return pref_name == kListPrefName;
+  }
+
+  bool IsMergeableDictionaryPreference(
+      const std::string& pref_name) const override {
+    return true;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestPrefModelAssociatorClient);
+};
+
+class PrefServiceSyncableMergeTest : public testing::Test {
+ public:
+  PrefServiceSyncableMergeTest()
+      : pref_registry_(
+            base::MakeRefCounted<user_prefs::PrefRegistrySyncable>()),
+        pref_notifier_(new PrefNotifierImpl),
+        managed_prefs_(base::MakeRefCounted<TestingPrefStore>()),
+        user_prefs_(base::MakeRefCounted<TestingPrefStore>()),
+        prefs_(
+            std::unique_ptr<PrefNotifierImpl>(pref_notifier_),
+            std::make_unique<PrefValueStore>(managed_prefs_.get(),
+                                             new TestingPrefStore,
+                                             new TestingPrefStore,
+                                             new TestingPrefStore,
+                                             user_prefs_.get(),
+                                             new TestingPrefStore,
+                                             pref_registry_->defaults().get(),
+                                             pref_notifier_),
+            user_prefs_,
+            pref_registry_,
+            &client_,
+            base::BindRepeating(&PrefServiceSyncableMergeTest::HandleReadError),
+            /*async=*/false),
+        pref_sync_service_(nullptr),
+        next_pref_remote_sync_node_id_(0) {}
+
+  void SetUp() override {
+    pref_registry_->RegisterStringPref(kUnsyncedPreferenceName,
+                                       kUnsyncedPreferenceDefaultValue);
+    pref_registry_->RegisterStringPref(
+        kStringPrefName, std::string(),
+        user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+    pref_registry_->RegisterListPref(
+        kListPrefName, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+    pref_registry_->RegisterDictionaryPref(
+        kDictPrefName, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+    pref_registry_->RegisterStringPref(
+        kDefaultCharsetPrefName, kDefaultCharsetValue,
+        user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+
+    // Downcast to PrefModelAssociator so that tests can access its' specific
+    // behavior. This is a smell. The roles between PrefServiceSyncable and
+    // PrefModelAssociator are not clearly separated (and this test should only
+    // test against the SyncableService interface).
+    pref_sync_service_ =  // static_cast<PrefModelAssociator*>(
+        prefs_.GetSyncableService(syncer::PREFERENCES);  //);
+    ASSERT_THAT(pref_sync_service_, NotNull());
+  }
+
+  /// Empty stub for prefs_ error handling.
+  static void HandleReadError(PersistentPrefStore::PrefReadError error) {}
+
+  syncer::SyncChange MakeRemoteChange(int64_t id,
+                                      const std::string& name,
+                                      const base::Value& value,
+                                      SyncChange::SyncChangeType type) {
+    std::string serialized;
+    JSONStringValueSerializer json(&serialized);
+    CHECK(json.Serialize(value));
+    sync_pb::EntitySpecifics entity;
+    sync_pb::PreferenceSpecifics* pref_one = entity.mutable_preference();
+    pref_one->set_name(name);
+    pref_one->set_value(serialized);
+    return syncer::SyncChange(
+        FROM_HERE, type,
+        syncer::SyncData::CreateRemoteData(id, entity, base::Time()));
+  }
+
+  void AddToRemoteDataList(const std::string& name,
+                           const base::Value& value,
+                           syncer::SyncDataList* out) {
+    std::string serialized;
+    JSONStringValueSerializer json(&serialized);
+    ASSERT_TRUE(json.Serialize(value));
+    sync_pb::EntitySpecifics one;
+    sync_pb::PreferenceSpecifics* pref_one = one.mutable_preference();
+    pref_one->set_name(name);
+    pref_one->set_value(serialized);
+    out->push_back(SyncData::CreateRemoteData(++next_pref_remote_sync_node_id_,
+                                              one, base::Time()));
+  }
+
+  void InitWithSyncDataTakeOutput(const syncer::SyncDataList& initial_data,
+                                  syncer::SyncChangeList* output) {
+    syncer::SyncMergeResult r = pref_sync_service_->MergeDataAndStartSyncing(
+        syncer::PREFERENCES, initial_data,
+        std::make_unique<TestSyncProcessorStub>(output),
+        std::make_unique<syncer::SyncErrorFactoryMock>());
+    EXPECT_FALSE(r.error().IsSet());
+  }
+
+  const base::Value& GetPreferenceValue(const std::string& name) {
+    const PrefService::Preference* preference =
+        prefs_.FindPreference(name.c_str());
+    return *preference->GetValue();
+  }
+
+  std::unique_ptr<base::Value> FindValue(const std::string& name,
+                                         const syncer::SyncChangeList& list) {
+    syncer::SyncChangeList::const_iterator it = list.begin();
+    for (; it != list.end(); ++it) {
+      if (syncer::SyncDataLocal(it->sync_data()).GetTag() == name) {
+        return base::JSONReader::Read(
+            it->sync_data().GetSpecifics().preference().value());
+      }
+    }
+    return nullptr;
+  }
+
+ protected:
+  scoped_refptr<user_prefs::PrefRegistrySyncable> pref_registry_;
+  // Owned by prefs_;
+  PrefNotifierImpl* pref_notifier_;
+  scoped_refptr<TestingPrefStore> managed_prefs_;
+  scoped_refptr<TestingPrefStore> user_prefs_;
+  TestPrefModelAssociatorClient client_;
+  PrefServiceSyncable prefs_;
+  syncer::SyncableService* pref_sync_service_;
+  int next_pref_remote_sync_node_id_;
+};
+
+TEST_F(PrefServiceSyncableMergeTest, ShouldMergeSelectedListValues) {
+  {
+    ListPrefUpdate update(&prefs_, kListPrefName);
+    base::ListValue* url_list = update.Get();
+    url_list->AppendString(kExampleUrl0);
+    url_list->AppendString(kExampleUrl1);
+  }
+
+  base::ListValue urls_to_restore;
+  urls_to_restore.AppendString(kExampleUrl1);
+  urls_to_restore.AppendString(kExampleUrl2);
+  syncer::SyncDataList in;
+  AddToRemoteDataList(kListPrefName, urls_to_restore, &in);
+
+  syncer::SyncChangeList out;
+  InitWithSyncDataTakeOutput(in, &out);
+
   std::unique_ptr<base::ListValue> expected_urls(new base::ListValue);
   expected_urls->AppendString(kExampleUrl1);
   expected_urls->AppendString(kExampleUrl2);
   expected_urls->AppendString(kExampleUrl0);
   std::unique_ptr<base::Value> value(FindValue(kListPrefName, out));
   ASSERT_TRUE(value.get());
-  EXPECT_TRUE(value->Equals(expected_urls.get()));
+  EXPECT_TRUE(value->Equals(expected_urls.get())) << *value;
   EXPECT_TRUE(GetPreferenceValue(kListPrefName).Equals(expected_urls.get()));
-  EXPECT_EQ(kNonDefaultCharsetValue, prefs_.GetString(kDefaultCharsetPrefName));
+}
+
+// List preferences have special handling at association time due to our ability
+// to merge the local and sync value. Make sure the merge logic doesn't merge
+// managed preferences.
+TEST_F(PrefServiceSyncableMergeTest, ManagedListPreferences) {
+  // Make the list of urls to restore on startup managed.
+  base::ListValue managed_value;
+  managed_value.AppendString(kExampleUrl0);
+  managed_value.AppendString(kExampleUrl1);
+  managed_prefs_->SetValue(kListPrefName, managed_value.CreateDeepCopy(),
+                           WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+  // Set a cloud version.
+  syncer::SyncDataList in;
+  base::ListValue urls_to_restore;
+  urls_to_restore.AppendString(kExampleUrl1);
+  urls_to_restore.AppendString(kExampleUrl2);
+  AddToRemoteDataList(kListPrefName, urls_to_restore, &in);
+
+  // Start sync and verify the synced value didn't get merged.
+  {
+    syncer::SyncChangeList out;
+    InitWithSyncDataTakeOutput(in, &out);
+    EXPECT_FALSE(FindValue(kListPrefName, out).get());
+  }
+
+  // Changing the user's urls to restore on startup pref should not sync
+  // anything.
+  {
+    syncer::SyncChangeList out;
+    base::ListValue user_value;
+    user_value.AppendString("http://chromium.org");
+    prefs_.Set(kListPrefName, user_value);
+    EXPECT_FALSE(FindValue(kListPrefName, out).get());
+  }
+
+  // An incoming sync transaction should change the user value, not the managed
+  // value.
+  base::ListValue sync_value;
+  sync_value.AppendString("http://crbug.com");
+  syncer::SyncChangeList list;
+  list.push_back(MakeRemoteChange(1, kListPrefName, sync_value,
+                                  SyncChange::ACTION_UPDATE));
+  pref_sync_service_->ProcessSyncChanges(FROM_HERE, list);
+
+  const base::Value* managed_prefs_result;
+  ASSERT_TRUE(managed_prefs_->GetValue(kListPrefName, &managed_prefs_result));
+  EXPECT_TRUE(managed_value.Equals(managed_prefs_result));
+  // Get should return the managed value, too.
+  EXPECT_TRUE(managed_value.Equals(prefs_.Get(kListPrefName)));
+  // Verify the user pref value has the change.
+  EXPECT_TRUE(sync_value.Equals(prefs_.GetUserPrefValue(kListPrefName)));
+}
+
+TEST_F(PrefServiceSyncableMergeTest, ShouldMergeSelectedDictionaryValues) {
+  {
+    DictionaryPrefUpdate update(&prefs_, kDictPrefName);
+    base::DictionaryValue* dict_value = update.Get();
+    dict_value->Set("my_key1", std::make_unique<base::Value>("my_value1"));
+    dict_value->Set("my_key3", std::make_unique<base::Value>("my_value3"));
+  }
+
+  base::DictionaryValue remote_update;
+  remote_update.Set("my_key2", std::make_unique<base::Value>("my_value2"));
+  syncer::SyncDataList in;
+  AddToRemoteDataList(kDictPrefName, remote_update, &in);
+
+  syncer::SyncChangeList out;
+  InitWithSyncDataTakeOutput(in, &out);
+
+  base::DictionaryValue expected_dict;
+  expected_dict.Set("my_key1", std::make_unique<base::Value>("my_value1"));
+  expected_dict.Set("my_key2", std::make_unique<base::Value>("my_value2"));
+  expected_dict.Set("my_key3", std::make_unique<base::Value>("my_value3"));
+  std::unique_ptr<base::Value> value(FindValue(kDictPrefName, out));
+  ASSERT_TRUE(value.get());
+  EXPECT_TRUE(value->Equals(&expected_dict));
+  EXPECT_TRUE(GetPreferenceValue(kDictPrefName).Equals(&expected_dict));
 }
 
 TEST_F(PrefServiceSyncableTest, FailModelAssociation) {
@@ -353,8 +576,7 @@
 
   const base::Value& actual = GetPreferenceValue(kStringPrefName);
   EXPECT_TRUE(expected.Equals(&actual));
-  EXPECT_EQ(
-      1U, pref_sync_service_->registered_preferences().count(kStringPrefName));
+  EXPECT_TRUE(pref_sync_service_->IsPrefSynced(kStringPrefName));
 }
 
 TEST_F(PrefServiceSyncableTest, UpdatedSyncNodeUnknownPreference) {
@@ -394,49 +616,6 @@
   EXPECT_TRUE(sync_value.Equals(prefs_.GetUserPref(kStringPrefName)));
 }
 
-// List preferences have special handling at association time due to our ability
-// to merge the local and sync value. Make sure the merge logic doesn't merge
-// managed preferences.
-TEST_F(PrefServiceSyncableTest, ManagedListPreferences) {
-  // Make the list of urls to restore on startup managed.
-  base::ListValue managed_value;
-  managed_value.AppendString(kExampleUrl0);
-  managed_value.AppendString(kExampleUrl1);
-  prefs_.SetManagedPref(kListPrefName, managed_value.CreateDeepCopy());
-
-  // Set a cloud version.
-  syncer::SyncDataList in;
-  syncer::SyncChangeList out;
-  base::ListValue urls_to_restore;
-  urls_to_restore.AppendString(kExampleUrl1);
-  urls_to_restore.AppendString(kExampleUrl2);
-  AddToRemoteDataList(kListPrefName, urls_to_restore, &in);
-
-  // Start sync and verify the synced value didn't get merged.
-  InitWithSyncDataTakeOutput(in, &out);
-  EXPECT_FALSE(FindValue(kListPrefName, out).get());
-  out.clear();
-
-  // Changing the user's urls to restore on startup pref should not sync
-  // anything.
-  base::ListValue user_value;
-  user_value.AppendString("http://chromium.org");
-  prefs_.SetUserPref(kListPrefName, user_value.CreateDeepCopy());
-  EXPECT_FALSE(FindValue(kListPrefName, out).get());
-
-  // An incoming sync transaction should change the user value, not the managed
-  // value.
-  base::ListValue sync_value;
-  sync_value.AppendString("http://crbug.com");
-  syncer::SyncChangeList list;
-  list.push_back(MakeRemoteChange(1, kListPrefName, sync_value,
-                                  SyncChange::ACTION_UPDATE));
-  pref_sync_service_->ProcessSyncChanges(FROM_HERE, list);
-
-  EXPECT_TRUE(managed_value.Equals(prefs_.GetManagedPref(kListPrefName)));
-  EXPECT_TRUE(sync_value.Equals(prefs_.GetUserPref(kListPrefName)));
-}
-
 TEST_F(PrefServiceSyncableTest, DynamicManagedPreferences) {
   syncer::SyncChangeList out;
   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
@@ -500,7 +679,7 @@
   syncer::SyncChangeList out;
   InitWithSyncDataTakeOutput(syncer::SyncDataList(), &out);
 
-  EXPECT_TRUE(IsSynced(kStringPrefName));
+  EXPECT_TRUE(IsRegistered(kStringPrefName));
   EXPECT_TRUE(pref->IsDefaultValue());
   EXPECT_FALSE(FindValue(kStringPrefName, out).get());
   out.clear();
diff --git a/components/sync_preferences/testing_pref_service_syncable.cc b/components/sync_preferences/testing_pref_service_syncable.cc
index 92fa102..e42f5020 100644
--- a/components/sync_preferences/testing_pref_service_syncable.cc
+++ b/components/sync_preferences/testing_pref_service_syncable.cc
@@ -4,6 +4,8 @@
 
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_notifier_impl.h"
@@ -31,7 +33,7 @@
                                            pref_notifier),
           user_prefs,
           pref_registry,
-          nullptr,  // pref_model_associator_client
+          /*pref_model_associator_client=*/nullptr,
           base::Bind(&TestingPrefServiceBase<
                      PrefServiceSyncable,
                      user_prefs::PrefRegistrySyncable>::HandleReadError),
diff --git a/components/sync_preferences/testing_pref_service_syncable.h b/components/sync_preferences/testing_pref_service_syncable.h
index cf83573f..ee59cc1 100644
--- a/components/sync_preferences/testing_pref_service_syncable.h
+++ b/components/sync_preferences/testing_pref_service_syncable.h
@@ -16,6 +16,18 @@
 namespace sync_preferences {
 
 // Test version of PrefServiceSyncable.
+// This class hierarchy has a flaw: TestingPrefServiceBase is inheriting from
+// the first template parameter (PrefServiceSyncable in this case). This means,
+// all of the supported parameter types must support the same constructor
+// signatures -- which they don't. Hence, it's not possible to properly inject
+//  a PrefModelAssociatorClient.
+// TODO(tschumann) The whole purpose of TestingPrefServiceBase is questionable
+// and I'd be in favor of removing it completely:
+//  -- it hides the dependency injetion of the different stores
+//  -- just to later offer ways to manipulate speficic stores.
+//  -- if tests just dependency injects the individual stores directly, they
+//     already have full control and won't need that indirection at all.
+// See PrefServiceSyncableMergeTest as an example of a cleaner way.
 class TestingPrefServiceSyncable
     : public TestingPrefServiceBase<PrefServiceSyncable,
                                     user_prefs::PrefRegistrySyncable> {
@@ -34,8 +46,6 @@
   // a PrefRegistry via its constructor (or via e.g. PrefServiceFactory).
   user_prefs::PrefRegistrySyncable* registry();
 
-  using PrefServiceSyncable::SetPrefModelAssociatorClientForTesting;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceSyncable);
 };
diff --git a/components/translate/core/browser/translate_infobar_delegate.h b/components/translate/core/browser/translate_infobar_delegate.h
index 7d19064..a30bf284 100644
--- a/components/translate/core/browser/translate_infobar_delegate.h
+++ b/components/translate/core/browser/translate_infobar_delegate.h
@@ -205,6 +205,12 @@
   // Set a observer.
   void SetObserver(Observer* observer);
 
+  // InfoBarDelegate:
+  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
+  int GetIconId() const override;
+  void InfoBarDismissed() override;
+  TranslateInfoBarDelegate* AsTranslateInfoBarDelegate() override;
+
  protected:
   TranslateInfoBarDelegate(
       const base::WeakPtr<TranslateManager>& translate_manager,
@@ -219,12 +225,6 @@
   friend class TranslationInfoBarTest;
   typedef std::pair<std::string, base::string16> LanguageNamePair;
 
-  // InfoBarDelegate:
-  infobars::InfoBarDelegate::InfoBarIdentifier GetIdentifier() const override;
-  int GetIconId() const override;
-  void InfoBarDismissed() override;
-  TranslateInfoBarDelegate* AsTranslateInfoBarDelegate() override;
-
   bool is_off_the_record_;
   translate::TranslateStep step_;
 
diff --git a/components/webrtc_logging/browser/BUILD.gn b/components/webrtc_logging/browser/BUILD.gn
index b1f22936..ddc7597 100644
--- a/components/webrtc_logging/browser/BUILD.gn
+++ b/components/webrtc_logging/browser/BUILD.gn
@@ -2,10 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//media/media_options.gni")
-
-assert(enable_webrtc)
-
 source_set("browser") {
   sources = [
     "log_cleanup.cc",
diff --git a/components/webrtc_logging/common/BUILD.gn b/components/webrtc_logging/common/BUILD.gn
index 9df6f3b6..34ff3156 100644
--- a/components/webrtc_logging/common/BUILD.gn
+++ b/components/webrtc_logging/common/BUILD.gn
@@ -2,10 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//media/media_options.gni")
-
-assert(enable_webrtc)
-
 source_set("common") {
   sources = [
     "partial_circular_buffer.cc",
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 75952c7..dbc59a9 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -97,6 +97,7 @@
     "//gpu/ipc/common:gpu_preferences_util",
     "//gpu/ipc/host",
     "//gpu/vulkan:buildflags",
+    "//jingle:jingle_glue",
     "//media",
     "//media:media_buildflags",
     "//media/capture",
@@ -170,6 +171,9 @@
     "//third_party/icu",
     "//third_party/libyuv",
     "//third_party/re2",
+    "//third_party/webrtc/media:rtc_media_base",
+    "//third_party/webrtc/modules/desktop_capture:primitives",
+    "//third_party/webrtc/rtc_base:rtc_base",
     "//third_party/zlib",
     "//third_party/zlib/google:compression_utils",
     "//third_party/zlib/google:zip",
@@ -1054,6 +1058,8 @@
     "media/android/media_web_contents_observer_android.h",
     "media/audible_metrics.cc",
     "media/audible_metrics.h",
+    "media/audio_input_stream_broker.cc",
+    "media/audio_input_stream_broker.h",
     "media/audio_output_stream_broker.cc",
     "media/audio_output_stream_broker.h",
     "media/audio_stream_broker.cc",
@@ -1356,6 +1362,8 @@
     "renderer_host/media/media_stream_track_metrics_host.h",
     "renderer_host/media/media_stream_ui_proxy.cc",
     "renderer_host/media/media_stream_ui_proxy.h",
+    "renderer_host/media/peer_connection_tracker_host.cc",
+    "renderer_host/media/peer_connection_tracker_host.h",
     "renderer_host/media/render_frame_audio_input_stream_factory.cc",
     "renderer_host/media/render_frame_audio_input_stream_factory.h",
     "renderer_host/media/render_frame_audio_output_stream_factory.cc",
@@ -1372,11 +1380,11 @@
     "renderer_host/media/video_capture_controller.cc",
     "renderer_host/media/video_capture_controller.h",
     "renderer_host/media/video_capture_controller_event_handler.h",
-    "renderer_host/media/video_capture_dependencies.cc",
-    "renderer_host/media/video_capture_dependencies.h",
     "renderer_host/media/video_capture_device_launch_observer.h",
     "renderer_host/media/video_capture_factory_delegate.cc",
     "renderer_host/media/video_capture_factory_delegate.h",
+    "renderer_host/media/video_capture_gpu_jpeg_decoder.cc",
+    "renderer_host/media/video_capture_gpu_jpeg_decoder.h",
     "renderer_host/media/video_capture_host.cc",
     "renderer_host/media/video_capture_host.h",
     "renderer_host/media/video_capture_manager.cc",
@@ -1390,6 +1398,18 @@
     "renderer_host/overscroll_controller.cc",
     "renderer_host/overscroll_controller.h",
     "renderer_host/overscroll_controller_delegate.h",
+    "renderer_host/p2p/socket_dispatcher_host.cc",
+    "renderer_host/p2p/socket_dispatcher_host.h",
+    "renderer_host/p2p/socket_host.cc",
+    "renderer_host/p2p/socket_host.h",
+    "renderer_host/p2p/socket_host_tcp.cc",
+    "renderer_host/p2p/socket_host_tcp.h",
+    "renderer_host/p2p/socket_host_tcp_server.cc",
+    "renderer_host/p2p/socket_host_tcp_server.h",
+    "renderer_host/p2p/socket_host_throttler.cc",
+    "renderer_host/p2p/socket_host_throttler.h",
+    "renderer_host/p2p/socket_host_udp.cc",
+    "renderer_host/p2p/socket_host_udp.h",
     "renderer_host/render_frame_metadata_provider_impl.cc",
     "renderer_host/render_frame_metadata_provider_impl.h",
     "renderer_host/render_message_filter.cc",
@@ -1699,6 +1719,13 @@
     "web_package/web_package_prefetch_handler.h",
     "web_package/web_package_request_handler.cc",
     "web_package/web_package_request_handler.h",
+    "webrtc/webrtc_internals.cc",
+    "webrtc/webrtc_internals.h",
+    "webrtc/webrtc_internals_message_handler.cc",
+    "webrtc/webrtc_internals_message_handler.h",
+    "webrtc/webrtc_internals_ui.cc",
+    "webrtc/webrtc_internals_ui.h",
+    "webrtc/webrtc_internals_ui_observer.h",
     "websockets/websocket_handshake_request_info_impl.cc",
     "websockets/websocket_handshake_request_info_impl.h",
     "websockets/websocket_manager.cc",
@@ -1739,6 +1766,8 @@
   # ChromeOS also defines linux but their memory-monitors conflict.
   if (is_chromeos) {
     sources += [
+      "media/keyboard_mic_registration.cc",
+      "media/keyboard_mic_registration.h",
       "memory/memory_monitor_chromeos.cc",
       "memory/memory_monitor_chromeos.h",
       "tracing/cros_tracing_agent.cc",
@@ -1797,39 +1826,6 @@
     ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "renderer_host/media/peer_connection_tracker_host.cc",
-      "renderer_host/media/peer_connection_tracker_host.h",
-      "renderer_host/p2p/socket_dispatcher_host.cc",
-      "renderer_host/p2p/socket_dispatcher_host.h",
-      "renderer_host/p2p/socket_host.cc",
-      "renderer_host/p2p/socket_host.h",
-      "renderer_host/p2p/socket_host_tcp.cc",
-      "renderer_host/p2p/socket_host_tcp.h",
-      "renderer_host/p2p/socket_host_tcp_server.cc",
-      "renderer_host/p2p/socket_host_tcp_server.h",
-      "renderer_host/p2p/socket_host_throttler.cc",
-      "renderer_host/p2p/socket_host_throttler.h",
-      "renderer_host/p2p/socket_host_udp.cc",
-      "renderer_host/p2p/socket_host_udp.h",
-      "webrtc/webrtc_internals.cc",
-      "webrtc/webrtc_internals.h",
-      "webrtc/webrtc_internals_message_handler.cc",
-      "webrtc/webrtc_internals_message_handler.h",
-      "webrtc/webrtc_internals_ui.cc",
-      "webrtc/webrtc_internals_ui.h",
-      "webrtc/webrtc_internals_ui_observer.h",
-    ]
-
-    deps += [
-      "//jingle:jingle_glue",
-      "//third_party/webrtc/media:rtc_media_base",
-      "//third_party/webrtc/modules/desktop_capture:primitives",
-      "//third_party/webrtc/rtc_base:rtc_base",
-    ]
-  }
-
   # Desktop/Window/WebContents screen capture implementations, conditionally
   # built depending on the available implementations for each platform.
   if (is_linux || is_mac || is_win) {
@@ -1837,6 +1833,8 @@
     sources += [
       "media/capture/cursor_renderer.cc",
       "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",
@@ -1844,6 +1842,8 @@
       "media/capture/web_contents_video_capture_device.cc",
       "media/capture/web_contents_video_capture_device.h",
     ]
+    public_deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
+    deps += [ "//third_party/webrtc/modules/desktop_capture" ]
     if (use_aura) {
       sources += [
         "media/capture/aura_window_capture_machine.cc",
@@ -1864,14 +1864,6 @@
         "//sandbox/mac:seatbelt_extension",
       ]
     }
-    if (enable_webrtc) {
-      sources += [
-        "media/capture/desktop_capture_device.cc",
-        "media/capture/desktop_capture_device.h",
-      ]
-      deps += [ "//third_party/webrtc/modules/desktop_capture" ]
-      public_deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
-    }
   }
 
   if (is_win) {
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 9c7c35f..b25ed2a 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -19,6 +19,10 @@
 #include "services/viz/public/interfaces/compositing/compositing_mode_watcher.mojom.h"
 #include "ui/base/ui_features.h"
 
+#if defined(OS_CHROMEOS)
+#include "content/browser/media/keyboard_mic_registration.h"
+#endif
+
 #if defined(USE_AURA)
 namespace aura {
 class Env;
@@ -166,6 +170,13 @@
   media::UserInputMonitor* user_input_monitor() const {
     return user_input_monitor_.get();
   }
+
+#if defined(OS_CHROMEOS)
+  KeyboardMicRegistration* keyboard_mic_registration() {
+    return &keyboard_mic_registration_;
+  }
+#endif
+
   discardable_memory::DiscardableSharedMemoryManager*
   discardable_shared_memory_manager() const {
     return discardable_shared_memory_manager_.get();
@@ -344,6 +355,10 @@
   scoped_refptr<base::DeferredSequencedTaskRunner> audio_service_runner_;
   std::unique_ptr<media::AudioSystem> audio_system_;
 
+#if defined(OS_CHROMEOS)
+  KeyboardMicRegistration keyboard_mic_registration_;
+#endif
+
   std::unique_ptr<midi::MidiService> midi_service_;
 
   // Must be deleted on the IO thread.
diff --git a/content/browser/browser_side_navigation_browsertest.cc b/content/browser/browser_side_navigation_browsertest.cc
index f5b07b2..1931019 100644
--- a/content/browser/browser_side_navigation_browsertest.cc
+++ b/content/browser/browser_side_navigation_browsertest.cc
@@ -482,7 +482,7 @@
       base::TimeTicks::Now() /* navigation_start */, "GET",
       nullptr /* post_data */, base::Optional<SourceLocation>(),
       CSPDisposition::CHECK, false /* started_from_context_menu */,
-      false /* has_user_gesture */, base::nullopt /* suggested_filename */);
+      false /* has_user_gesture */);
   mojom::BeginNavigationParamsPtr begin_params =
       mojom::BeginNavigationParams::New(
           std::string() /* headers */, net::LOAD_NORMAL,
diff --git a/content/browser/devtools/devtools_url_interceptor_request_job.cc b/content/browser/devtools/devtools_url_interceptor_request_job.cc
index 622f681..b966e334 100644
--- a/content/browser/devtools/devtools_url_interceptor_request_job.cc
+++ b/content/browser/devtools/devtools_url_interceptor_request_job.cc
@@ -155,8 +155,7 @@
       resource_request_info->ShouldReportRawHeaders(),
       resource_request_info->IsAsync(),
       resource_request_info->GetPreviewsState(), resource_request_info->body(),
-      resource_request_info->initiated_in_secure_context(),
-      resource_request_info->suggested_filename());
+      resource_request_info->initiated_in_secure_context());
   extra_data->AssociateWithRequest(request_.get());
 
   if (request_details.post_data)
@@ -549,12 +548,9 @@
   // should not have this problem, as it's on top of MIME sniffer.
   std::string mime_type;
   subrequest->GetMimeType(&mime_type);
-  bool is_cross_origin = navigation_loader_util::IsCrossOriginRequest(
-      orig_request->url(), orig_request->initiator());
   return req_info->allow_download() &&
          navigation_loader_util::IsDownload(
-             orig_request->url(), subrequest->response_headers(), mime_type,
-             req_info->suggested_filename().has_value(), is_cross_origin);
+             orig_request->url(), subrequest->response_headers(), mime_type);
 }
 
 }  // namespace
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.cc b/content/browser/devtools/devtools_url_loader_interceptor.cc
index 0178aa6..751c113 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.cc
+++ b/content/browser/devtools/devtools_url_loader_interceptor.cc
@@ -172,7 +172,6 @@
                   const base::UnguessableToken& frame_token,
                   int32_t process_id,
                   std::unique_ptr<CreateLoaderParameters> create_loader_params,
-                  bool has_suggested_download_filename,
                   network::mojom::URLLoaderRequest loader_request,
                   network::mojom::URLLoaderClientPtr client,
                   network::mojom::URLLoaderFactoryPtr target_factory);
@@ -264,7 +263,6 @@
   InterceptionStage stage_;
 
   std::unique_ptr<CreateLoaderParameters> create_loader_params_;
-  const bool has_suggested_download_filename_;
 
   mojo::Binding<network::mojom::URLLoaderClient> client_binding_;
   mojo::Binding<network::mojom::URLLoader> loader_binding_;
@@ -311,7 +309,6 @@
   void CreateJob(const base::UnguessableToken& frame_token,
                  int32_t process_id,
                  std::unique_ptr<CreateLoaderParameters> create_params,
-                 bool has_suggested_download_filename,
                  network::mojom::URLLoaderRequest loader_request,
                  network::mojom::URLLoaderClientPtr client,
                  network::mojom::URLLoaderFactoryPtr target_factory) {
@@ -320,10 +317,10 @@
     static int last_id = 0;
 
     std::string id = base::StringPrintf("interception-job-%d", ++last_id);
-    InterceptionJob* job = new InterceptionJob(
-        this, id, frame_token, process_id, std::move(create_params),
-        has_suggested_download_filename, std::move(loader_request),
-        std::move(client), std::move(target_factory));
+    InterceptionJob* job =
+        new InterceptionJob(this, id, frame_token, process_id,
+                            std::move(create_params), std::move(loader_request),
+                            std::move(client), std::move(target_factory));
     jobs_.emplace(std::move(id), job);
   }
 
@@ -403,7 +400,6 @@
   DevToolsURLLoaderFactoryProxy(
       const base::UnguessableToken& frame_token,
       int32_t process_id,
-      bool has_suggested_download_filename,
       network::mojom::URLLoaderFactoryRequest loader_request,
       network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
       base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor);
@@ -428,7 +424,6 @@
 
   const base::UnguessableToken frame_token_;
   const int32_t process_id_;
-  const bool has_suggested_download_filename_;
 
   network::mojom::URLLoaderFactoryPtr target_factory_;
   base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor_;
@@ -440,13 +435,11 @@
 DevToolsURLLoaderFactoryProxy::DevToolsURLLoaderFactoryProxy(
     const base::UnguessableToken& frame_token,
     int32_t process_id,
-    bool has_suggested_download_filename,
     network::mojom::URLLoaderFactoryRequest loader_request,
     network::mojom::URLLoaderFactoryPtrInfo target_factory_info,
     base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor)
     : frame_token_(frame_token),
       process_id_(process_id),
-      has_suggested_download_filename_(has_suggested_download_filename),
       interceptor_(std::move(interceptor)) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
   BrowserThread::PostTask(
@@ -480,8 +473,8 @@
   network::mojom::URLLoaderFactoryPtr factory_clone;
   target_factory_->Clone(MakeRequest(&factory_clone));
   interceptor->CreateJob(frame_token_, process_id_, std::move(creation_params),
-                         has_suggested_download_filename_, std::move(loader),
-                         std::move(client), std::move(factory_clone));
+                         std::move(loader), std::move(client),
+                         std::move(factory_clone));
 }
 
 void DevToolsURLLoaderFactoryProxy::StartOnIO(
@@ -600,7 +593,6 @@
 bool DevToolsURLLoaderInterceptor::CreateProxyForInterception(
     const base::UnguessableToken frame_token,
     int process_id,
-    bool has_suggested_download_filename,
     network::mojom::URLLoaderFactoryRequest* request) const {
   if (!enabled_)
     return false;
@@ -609,9 +601,9 @@
   network::mojom::URLLoaderFactoryPtrInfo target_ptr_info;
   *request = MakeRequest(&target_ptr_info);
 
-  new DevToolsURLLoaderFactoryProxy(
-      frame_token, process_id, has_suggested_download_filename,
-      std::move(original_request), std::move(target_ptr_info), weak_impl_);
+  new DevToolsURLLoaderFactoryProxy(frame_token, process_id,
+                                    std::move(original_request),
+                                    std::move(target_ptr_info), weak_impl_);
   return true;
 }
 
@@ -621,7 +613,6 @@
     const base::UnguessableToken& frame_token,
     int process_id,
     std::unique_ptr<CreateLoaderParameters> create_loader_params,
-    bool has_suggested_download_filename,
     network::mojom::URLLoaderRequest loader_request,
     network::mojom::URLLoaderClientPtr client,
     network::mojom::URLLoaderFactoryPtr target_factory)
@@ -636,7 +627,6 @@
       report_upload_(!!create_loader_params->request.request_body),
       interceptor_(interceptor),
       create_loader_params_(std::move(create_loader_params)),
-      has_suggested_download_filename_(has_suggested_download_filename),
       client_binding_(this),
       loader_binding_(this),
       client_(std::move(client)),
@@ -1140,13 +1130,10 @@
 
   auto request_info = BuildRequestInfo(&head);
   const network::ResourceRequest& request = create_loader_params_->request;
-  bool is_cross_origin = navigation_loader_util::IsCrossOriginRequest(
-      request.url, request.request_initiator);
   request_info->is_download =
       request_info->is_navigation && request.allow_download &&
-      navigation_loader_util::IsDownload(
-          request.url, head.headers.get(), head.mime_type,
-          has_suggested_download_filename_, is_cross_origin);
+      navigation_loader_util::IsDownload(request.url, head.headers.get(),
+                                         head.mime_type);
   NotifyClient(std::move(request_info));
 }
 
diff --git a/content/browser/devtools/devtools_url_loader_interceptor.h b/content/browser/devtools/devtools_url_loader_interceptor.h
index 8c123c9..36f3774 100644
--- a/content/browser/devtools/devtools_url_loader_interceptor.h
+++ b/content/browser/devtools/devtools_url_loader_interceptor.h
@@ -53,7 +53,6 @@
   bool CreateProxyForInterception(
       const base::UnguessableToken frame_token,
       int process_id,  // 0 for navigation
-      bool has_suggested_download_filename,
       network::mojom::URLLoaderFactoryRequest* request) const;
 
  private:
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 7d66f47..567b26c7 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -1857,12 +1857,10 @@
 bool NetworkHandler::MaybeCreateProxyForInterception(
     const base::UnguessableToken& frame_token,
     int process_id,
-    bool has_suggested_download_filename,
     network::mojom::URLLoaderFactoryRequest* target_factory_request) {
   return url_loader_interceptor_ &&
          url_loader_interceptor_->CreateProxyForInterception(
-             frame_token, process_id, has_suggested_download_filename,
-             target_factory_request);
+             frame_token, process_id, target_factory_request);
 }
 
 void NetworkHandler::ApplyOverrides(net::HttpRequestHeaders* headers,
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index 5a0787eb..2669e08 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -135,7 +135,6 @@
   bool MaybeCreateProxyForInterception(
       const base::UnguessableToken& frame_token,
       int process_id,
-      bool has_suggested_download_filename,
       network::mojom::URLLoaderFactoryRequest* target_factory_request);
 
   void ApplyOverrides(net::HttpRequestHeaders* headers,
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index e0daf093..572843f 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -296,7 +296,6 @@
 bool RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
     RenderFrameHostImpl* rfh,
     bool is_navigation,
-    bool has_suggested_download_filename,
     network::mojom::URLLoaderFactoryRequest* target_factory_request) {
   FrameTreeNode* frame_tree_node = rfh->frame_tree_node();
   base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token();
@@ -305,11 +304,9 @@
   if (!agent_host)
     return false;
   int process_id = is_navigation ? 0 : rfh->GetProcess()->GetID();
-  DCHECK(!has_suggested_download_filename || is_navigation);
   for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) {
-    if (network->MaybeCreateProxyForInterception(
-            frame_token, process_id, has_suggested_download_filename,
-            target_factory_request)) {
+    if (network->MaybeCreateProxyForInterception(frame_token, process_id,
+                                                 target_factory_request)) {
       return true;
     }
   }
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index 2fa68895..afd6191 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -73,7 +73,6 @@
   static bool WillCreateURLLoaderFactory(
       RenderFrameHostImpl* rfh,
       bool is_navigation,
-      bool has_suggested_download_filename,
       network::mojom::URLLoaderFactoryRequest* loader_factory_request);
 
   static void OnNavigationRequestWillBeSent(
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 7264de65..9b7f2de 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -2932,7 +2932,7 @@
 }
 
 // A cross-origin request that fails before it gets a response from the server
-// should result in the old page staying current.
+// should result in an network error page.
 IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadAttributeNetworkError) {
   SetupErrorInjectionDownloads();
   GURL url = TestDownloadHttpResponse::GetNextURLForDownload();
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index bcbfb5e..7d2fb68 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -251,8 +251,7 @@
 
 scoped_refptr<download::DownloadURLLoaderFactoryGetter>
 CreateDownloadURLLoaderFactoryGetter(StoragePartitionImpl* storage_partition,
-                                     RenderFrameHost* rfh,
-                                     bool has_suggested_filename) {
+                                     RenderFrameHost* rfh) {
   network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info;
   network::mojom::URLLoaderFactoryRequest proxy_factory_request;
   if (rfh) {
@@ -261,7 +260,7 @@
         MakeRequest(&devtools_factory_ptr_info);
     if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
             static_cast<RenderFrameHostImpl*>(rfh), true,
-            has_suggested_filename, &devtools_factory_request)) {
+            &devtools_factory_request)) {
       proxy_factory_ptr_info = std::move(devtools_factory_ptr_info);
       proxy_factory_request = std::move(devtools_factory_request);
     }
@@ -716,7 +715,6 @@
 void DownloadManagerImpl::InterceptNavigation(
     std::unique_ptr<network::ResourceRequest> resource_request,
     std::vector<GURL> url_chain,
-    const base::Optional<std::string>& suggested_filename,
     scoped_refptr<network::ResourceResponse> response,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
     net::CertStatus cert_status,
@@ -738,7 +736,7 @@
       on_download_checks_done = base::BindOnce(
           &DownloadManagerImpl::InterceptNavigationOnChecksComplete,
           weak_factory_.GetWeakPtr(), web_contents_getter,
-          std::move(resource_request), std::move(url_chain), suggested_filename,
+          std::move(resource_request), std::move(url_chain),
           std::move(response), cert_status,
           std::move(url_loader_client_endpoints));
 
@@ -979,7 +977,6 @@
     ResourceRequestInfo::WebContentsGetter web_contents_getter,
     std::unique_ptr<network::ResourceRequest> resource_request,
     std::vector<GURL> url_chain,
-    const base::Optional<std::string>& suggested_filename,
     scoped_refptr<network::ResourceResponse> response,
     net::CertStatus cert_status,
     network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
@@ -1011,11 +1008,10 @@
       GetStoragePartition(browser_context_, render_process_id, render_frame_id);
   in_progress_manager_->InterceptDownloadFromNavigation(
       std::move(resource_request), render_process_id, render_frame_id, site_url,
-      tab_url, tab_referrer_url, std::move(url_chain), suggested_filename,
-      std::move(response), std::move(cert_status),
-      std::move(url_loader_client_endpoints),
-      CreateDownloadURLLoaderFactoryGetter(storage_partition, render_frame_host,
-                                           suggested_filename.has_value()));
+      tab_url, tab_referrer_url, std::move(url_chain), std::move(response),
+      std::move(cert_status), std::move(url_loader_client_endpoints),
+      CreateDownloadURLLoaderFactoryGetter(storage_partition,
+                                           render_frame_host));
 }
 
 void DownloadManagerImpl::BeginDownloadInternal(
@@ -1060,8 +1056,8 @@
           base::MakeRefCounted<BlobDownloadURLLoaderFactoryGetter>(
               params->url(), std::move(blob_data_handle));
     } else {
-      url_loader_factory_getter = CreateDownloadURLLoaderFactoryGetter(
-          storage_partition, rfh, !params->suggested_name().empty());
+      url_loader_factory_getter =
+          CreateDownloadURLLoaderFactoryGetter(storage_partition, rfh);
     }
 
     in_progress_manager_->BeginDownload(
diff --git a/content/browser/download/download_manager_impl.h b/content/browser/download/download_manager_impl.h
index f79ea71..b62b8ae7 100644
--- a/content/browser/download/download_manager_impl.h
+++ b/content/browser/download/download_manager_impl.h
@@ -167,7 +167,6 @@
   void InterceptNavigation(
       std::unique_ptr<network::ResourceRequest> resource_request,
       std::vector<GURL> url_chain,
-      const base::Optional<std::string>& suggested_filename,
       scoped_refptr<network::ResourceResponse> response,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
       net::CertStatus cert_status,
@@ -254,7 +253,6 @@
       ResourceRequestInfo::WebContentsGetter web_contents_getter,
       std::unique_ptr<network::ResourceRequest> resource_request,
       std::vector<GURL> url_chain,
-      const base::Optional<std::string>& suggested_filename,
       scoped_refptr<network::ResourceResponse> response,
       net::CertStatus cert_status,
       network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
diff --git a/content/browser/download/download_request_core.cc b/content/browser/download/download_request_core.cc
index b7eb780..25068ec 100644
--- a/content/browser/download/download_request_core.cc
+++ b/content/browser/download/download_request_core.cc
@@ -203,12 +203,6 @@
     is_partial_request_ = save_info_->offset > 0;
   } else {
     save_info_.reset(new download::DownloadSaveInfo);
-    ResourceRequestInfoImpl* request_info =
-        ResourceRequestInfoImpl::ForRequest(request_);
-    if (request_info && request_info->suggested_filename().has_value()) {
-      save_info_->suggested_name =
-          base::UTF8ToUTF16(*request_info->suggested_filename());
-    }
   }
 }
 
diff --git a/content/browser/frame_host/blocked_scheme_navigation_throttle.cc b/content/browser/frame_host/blocked_scheme_navigation_throttle.cc
index c23c2931..48613b4 100644
--- a/content/browser/frame_host/blocked_scheme_navigation_throttle.cc
+++ b/content/browser/frame_host/blocked_scheme_navigation_throttle.cc
@@ -36,12 +36,6 @@
   if (handle->IsDownload())
     return PROCEED;
 
-  // We treat <a download href="data:.."> and <a download href="filesystem:..">
-  // as a navigation, but it will always result in a download, not a top-level
-  // navigation, so not blocking it here.
-  if (handle->GetSuggestedFilename().has_value())
-    return PROCEED;
-
   RenderFrameHost* top_frame =
       handle->frame_tree_node()->frame_tree()->root()->current_frame_host();
   top_frame->AddMessageToConsole(
diff --git a/content/browser/frame_host/form_submission_throttle_browsertest.cc b/content/browser/frame_host/form_submission_throttle_browsertest.cc
index 92ef88f54..96083c7 100644
--- a/content/browser/frame_host/form_submission_throttle_browsertest.cc
+++ b/content/browser/frame_host/form_submission_throttle_browsertest.cc
@@ -73,7 +73,6 @@
         false,                   // started_from_context_menu
         CSPDisposition::CHECK,   // should_check_main_world_csp
         true,                    // is_form_submission
-        base::nullopt,           // suggested_filename
         nullptr);                // navigation_ui_data
 
     // Test the expectations with a FormSubmissionThrottle.
@@ -111,7 +110,6 @@
       false,                         // started_from_context_menu
       CSPDisposition::DO_NOT_CHECK,  // should_check_main_world_csp
       true,                          // is_form_submission
-      base::nullopt,                 // suggested_filename
       nullptr);                      // navigation_ui_data
 
   // Test that the navigation is allowed because "should_by_pass_main_world_csp"
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc
index fc4bf669..ed68927 100644
--- a/content/browser/frame_host/frame_tree_node.cc
+++ b/content/browser/frame_host/frame_tree_node.cc
@@ -600,8 +600,7 @@
         expected_pending_nav_entry_id =
             navigation_request_->navigation_handle()->pending_nav_entry_id();
       }
-      navigator_->DiscardPendingEntryIfNeeded(expected_pending_nav_entry_id,
-                                              false /* is_download */);
+      navigator_->DiscardPendingEntryIfNeeded(expected_pending_nav_entry_id);
     }
     ResetNavigationRequest(false, true);
   }
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 004b046..0968e48 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -809,7 +809,6 @@
     entry->set_source_site_instance(
         static_cast<SiteInstanceImpl*>(params.source_site_instance.get()));
     entry->SetRedirectChain(params.redirect_chain);
-    entry->set_suggested_filename(params.suggested_filename);
   }
 
   // Set the FTN ID (only used in non-site-per-process, for tests).
@@ -1922,8 +1921,7 @@
     bool should_replace_current_entry,
     const std::string& method,
     scoped_refptr<network::ResourceRequestBody> post_body,
-    const std::string& extra_headers,
-    const base::Optional<std::string>& suggested_filename) {
+    const std::string& extra_headers) {
   FrameTreeNode* node = render_frame_host->frame_tree_node();
   // Create a NavigationEntry for the transfer, without making it the pending
   // entry. Subframe transfers should have a clone of the last committed entry
@@ -1963,7 +1961,6 @@
         static_cast<SiteInstanceImpl*>(source_site_instance));
     entry->root_node()->frame_entry->set_method(method);
   }
-  entry->set_suggested_filename(suggested_filename);
 
   // Don't allow an entry replacement if there is no entry to replace.
   // http://crbug.com/457149
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index 0897fb7e..7fc6d9d 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -113,8 +113,7 @@
       bool should_replace_current_entry,
       const std::string& method,
       scoped_refptr<network::ResourceRequestBody> post_body,
-      const std::string& extra_headers,
-      const base::Optional<std::string>& suggested_filename);
+      const std::string& extra_headers);
 
   void ClearAllScreenshots() override;
 
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index a23fbb7..fa309525 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -8097,39 +8097,4 @@
   EXPECT_TRUE(capturer.is_same_document());
 }
 
-IN_PROC_BROWSER_TEST_F(ContentBrowserTest, HideDownloadFromUnmodifiedNewTab) {
-  GURL url("data:application/octet-stream,");
-
-  const NavigationControllerImpl& controller =
-      static_cast<const NavigationControllerImpl&>(
-          shell()->web_contents()->GetController());
-
-  {
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    base::ScopedTempDir downloads_directory;
-    ASSERT_TRUE(downloads_directory.CreateUniqueTempDir());
-    DownloadManager* download_manager = BrowserContext::GetDownloadManager(
-        shell()->web_contents()->GetBrowserContext());
-    ShellDownloadManagerDelegate* download_delegate =
-        static_cast<ShellDownloadManagerDelegate*>(
-            download_manager->GetDelegate());
-    download_delegate->SetDownloadBehaviorForTesting(
-        downloads_directory.GetPath());
-
-    DownloadTestObserverTerminal observer(
-        download_manager, 1, DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
-
-    OpenURLParams params(url, Referrer(), WindowOpenDisposition::CURRENT_TAB,
-                         ui::PAGE_TRANSITION_LINK, true);
-    params.suggested_filename = std::string("foo");
-
-    shell()->web_contents()->OpenURL(params);
-    WaitForLoadStop(shell()->web_contents());
-    observer.WaitForFinished();
-  }
-
-  EXPECT_FALSE(controller.GetPendingEntry());
-  EXPECT_FALSE(controller.GetVisibleEntry());
-}
-
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_entry_impl.cc b/content/browser/frame_host/navigation_entry_impl.cc
index 039906c..e1012eab 100644
--- a/content/browser/frame_host/navigation_entry_impl.cc
+++ b/content/browser/frame_host/navigation_entry_impl.cc
@@ -654,7 +654,6 @@
   // ResetForCommit: reload_type_
   copy->extra_data_ = extra_data_;
   copy->replaced_entry_data_ = replaced_entry_data_;
-  // ResetForCommit: suggested_filename_
 
   return copy;
 }
@@ -693,7 +692,7 @@
       navigation_start, method, post_body ? post_body : post_data_,
       base::Optional<SourceLocation>(),
       CSPDisposition::CHECK /* should_check_main_world_csp */,
-      has_started_from_context_menu(), has_user_gesture(), suggested_filename_);
+      has_started_from_context_menu(), has_user_gesture());
 }
 
 RequestNavigationParams NavigationEntryImpl::ConstructRequestNavigationParams(
@@ -771,7 +770,6 @@
   // loaded again in the future.
   set_intent_received_timestamp(base::TimeTicks());
 #endif
-  suggested_filename_.reset();
 }
 
 NavigationEntryImpl::TreeNode* NavigationEntryImpl::GetTreeNode(
diff --git a/content/browser/frame_host/navigation_entry_impl.h b/content/browser/frame_host/navigation_entry_impl.h
index 5d711d1..0e1adbf 100644
--- a/content/browser/frame_host/navigation_entry_impl.h
+++ b/content/browser/frame_host/navigation_entry_impl.h
@@ -430,14 +430,6 @@
     replaced_entry_data_ = data;
   }
 
-  const base::Optional<std::string> suggested_filename() const {
-    return suggested_filename_;
-  }
-  void set_suggested_filename(
-      const base::Optional<std::string> suggested_filename) {
-    suggested_filename_ = suggested_filename;
-  }
-
  private:
   // WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
   // Session/Tab restore save portions of this class so that it can be recreated
@@ -581,11 +573,6 @@
   // why the field is listed here.
   base::Optional<ReplacedNavigationEntryData> replaced_entry_data_;
 
-  // If this event was triggered by an anchor element with a download
-  // attribute, |suggested_filename_| will contain the (possibly empty) value of
-  // that attribute. Reset at commit and not persisted.
-  base::Optional<std::string> suggested_filename_;
-
   DISALLOW_COPY_AND_ASSIGN(NavigationEntryImpl);
 };
 
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index b0cd9e4b..df35ab90 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -118,7 +118,6 @@
     bool started_from_context_menu,
     CSPDisposition should_check_main_world_csp,
     bool is_form_submission,
-    const base::Optional<std::string>& suggested_filename,
     std::unique_ptr<NavigationUIData> navigation_ui_data,
     const std::string& method,
     net::HttpRequestHeaders request_headers,
@@ -133,10 +132,10 @@
       url, redirect_chain, frame_tree_node, is_renderer_initiated,
       is_same_document, navigation_start, pending_nav_entry_id,
       started_from_context_menu, should_check_main_world_csp,
-      is_form_submission, suggested_filename, std::move(navigation_ui_data),
-      method, std::move(request_headers), resource_request_body,
-      sanitized_referrer, has_user_gesture, transition, is_external_protocol,
-      request_context_type, mixed_content_context_type));
+      is_form_submission, std::move(navigation_ui_data), method,
+      std::move(request_headers), resource_request_body, sanitized_referrer,
+      has_user_gesture, transition, is_external_protocol, request_context_type,
+      mixed_content_context_type));
 }
 
 NavigationHandleImpl::NavigationHandleImpl(
@@ -150,7 +149,6 @@
     bool started_from_context_menu,
     CSPDisposition should_check_main_world_csp,
     bool is_form_submission,
-    const base::Optional<std::string>& suggested_filename,
     std::unique_ptr<NavigationUIData> navigation_ui_data,
     const std::string& method,
     net::HttpRequestHeaders request_headers,
@@ -192,7 +190,6 @@
       navigation_type_(NAVIGATION_TYPE_UNKNOWN),
       should_check_main_world_csp_(should_check_main_world_csp),
       expected_render_process_host_id_(ChildProcessHost::kInvalidUniqueID),
-      suggested_filename_(suggested_filename),
       is_transferring_(false),
       is_form_submission_(is_form_submission),
       should_replace_current_entry_(false),
@@ -602,11 +599,6 @@
   return is_form_submission_;
 }
 
-const base::Optional<std::string>&
-NavigationHandleImpl::GetSuggestedFilename() {
-  return suggested_filename_;
-}
-
 void NavigationHandleImpl::InitServiceWorkerHandle(
     ServiceWorkerContextWrapper* service_worker_context) {
   service_worker_handle_.reset(
@@ -794,16 +786,8 @@
   // If the navigation is done processing the response, then it's ready to
   // commit. Inform observers that the navigation is now ready to commit, unless
   // it is not set to commit (204/205s/downloads).
-  if (result.action() == NavigationThrottle::PROCEED && render_frame_host_) {
-    CHECK(!suggested_filename_.has_value() ||
-          !(url_.SchemeIsBlob() || url_.SchemeIsFileSystem() ||
-            url_.SchemeIs(url::kAboutScheme) ||
-            url_.SchemeIs(url::kDataScheme)))
-        << "Blob, filesystem, data, and about URLs with a suggested filename "
-           "should always result in a download, so we should never process a "
-           "navigation response here.";
+  if (result.action() == NavigationThrottle::PROCEED && render_frame_host_)
     ReadyToCommitNavigation(render_frame_host_, false);
-  }
 
   TRACE_EVENT_ASYNC_STEP_INTO1("navigation", "NavigationHandle", this,
                                "ProcessResponse", "result", result.action());
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index e2a7a8b..3e7d25f4 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -66,7 +66,6 @@
       bool started_from_context_menu,
       CSPDisposition should_check_main_world_csp,
       bool is_form_submission,
-      const base::Optional<std::string>& suggested_filename,
       std::unique_ptr<NavigationUIData> navigation_ui_data,
       const std::string& method = std::string(),
       net::HttpRequestHeaders request_headers = net::HttpRequestHeaders(),
@@ -159,7 +158,6 @@
   const GlobalRequestID& GetGlobalRequestID() override;
   bool IsDownload() override;
   bool IsFormSubmission() override;
-  const base::Optional<std::string>& GetSuggestedFilename() override;
 
   // Resume and CancelDeferredNavigation must only be called by the
   // NavigationThrottle that is currently deferring the navigation.
@@ -381,7 +379,6 @@
       bool started_from_context_menu,
       CSPDisposition should_check_main_world_csp,
       bool is_form_submission,
-      const base::Optional<std::string>& suggested_filename,
       std::unique_ptr<NavigationUIData> navigation_ui_data,
       const std::string& method,
       net::HttpRequestHeaders request_headers,
@@ -557,11 +554,6 @@
   // in it.
   int expected_render_process_host_id_;
 
-  // If this navigation was triggered by an anchor element with a download
-  // attribute, the |suggested_filename_| contains the attribute's (possibly
-  // empty) value.
-  base::Optional<std::string> suggested_filename_;
-
   // Whether the navigation is in the middle of a transfer. Set to false when
   // the DidStartProvisionalLoad is received from the new renderer.
   bool is_transferring_;
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index 6d06027..e63fa54 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -258,7 +258,6 @@
         false,                  // started_from_context_menu
         CSPDisposition::CHECK,  // should_check_main_world_csp
         false,                  // is_form_submission
-        base::nullopt,          // suggested_filename
         nullptr,                // navigation_ui_data
         "GET", net::HttpRequestHeaders(),
         nullptr,  // resource_request_body
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index b108f913..1cf85fe 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -607,9 +607,8 @@
           common_params_.navigation_start, nav_entry_id_,
           common_params_.started_from_context_menu,
           common_params_.should_check_main_world_csp,
-          begin_params_->is_form_submission, common_params_.suggested_filename,
-          std::move(navigation_ui_data_), common_params_.method,
-          std::move(headers), common_params_.post_data,
+          begin_params_->is_form_submission, std::move(navigation_ui_data_),
+          common_params_.method, std::move(headers), common_params_.post_data,
           Referrer::SanitizeForRequest(common_params_.url,
                                        common_params_.referrer),
           common_params_.has_user_gesture, common_params_.transition,
@@ -1028,15 +1027,11 @@
   if (navigation_handle_.get())
     navigation_handle_->set_net_error_code(static_cast<net::Error>(net_error));
 
-  int expected_pending_entry_id = nav_entry_id_;
-  bool is_download = false;
-  if (navigation_handle_.get()) {
-    expected_pending_entry_id = navigation_handle_->pending_nav_entry_id();
-    is_download = navigation_handle_->IsDownload();
-  }
-
+  int expected_pending_entry_id =
+      navigation_handle_.get() ? navigation_handle_->pending_nav_entry_id()
+                               : nav_entry_id_;
   frame_tree_node_->navigator()->DiscardPendingEntryIfNeeded(
-      expected_pending_entry_id, is_download);
+      expected_pending_entry_id);
 
   // If the request was canceled by the user do not show an error page.
   if (net_error == net::ERR_ABORTED) {
@@ -1350,9 +1345,8 @@
           BrowserContext::GetDownloadManager(browser_context));
       download_manager->InterceptNavigation(
           std::move(resource_request), navigation_handle_->GetRedirectChain(),
-          common_params_.suggested_filename, response_,
-          std::move(url_loader_client_endpoints_), ssl_info_.cert_status,
-          frame_tree_node_->frame_tree_node_id());
+          response_, std::move(url_loader_client_endpoints_),
+          ssl_info_.cert_status, frame_tree_node_->frame_tree_node_id());
 
       OnRequestFailed(false, net::ERR_ABORTED, base::nullopt);
       return;
diff --git a/content/browser/frame_host/navigator.h b/content/browser/frame_host/navigator.h
index 960b0190..128bb52d 100644
--- a/content/browser/frame_host/navigator.h
+++ b/content/browser/frame_host/navigator.h
@@ -141,8 +141,7 @@
       WindowOpenDisposition disposition,
       bool should_replace_current_entry,
       bool user_gesture,
-      blink::WebTriggeringEventInfo triggering_event_info,
-      const base::Optional<std::string>& suggested_filename) {}
+      blink::WebTriggeringEventInfo triggering_event_info) {}
 
   // Called when a document requests a navigation in another document through a
   // RenderFrameProxy. If |method| is "POST", then |post_body| needs to specify
@@ -156,8 +155,7 @@
       bool should_replace_current_entry,
       const std::string& method,
       scoped_refptr<network::ResourceRequestBody> post_body,
-      const std::string& extra_headers,
-      const base::Optional<std::string>& suggested_filename) {}
+      const std::string& extra_headers) {}
 
   // Called after receiving a BeforeUnloadACK IPC from the renderer. If
   // |frame_tree_node| has a NavigationRequest waiting for the renderer
@@ -211,8 +209,7 @@
   // With sufficiently bad interleaving of IPCs, this may no longer be the
   // pending NavigationEntry, in which case the pending NavigationEntry will not
   // be discarded.
-  virtual void DiscardPendingEntryIfNeeded(int expected_pending_entry_id,
-                                           bool is_download) {}
+  virtual void DiscardPendingEntryIfNeeded(int expected_pending_entry_id) {}
 
  protected:
   friend class base::RefCounted<Navigator>;
diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc
index 799fbb8..1d1e2c43 100644
--- a/content/browser/frame_host/navigator_impl.cc
+++ b/content/browser/frame_host/navigator_impl.cc
@@ -237,13 +237,11 @@
   }
 
   // Discard the pending navigation entry if needed.
-  int expected_pending_entry_id = 0;
-  if (render_frame_host->GetNavigationHandle()) {
-    expected_pending_entry_id =
-        render_frame_host->GetNavigationHandle()->pending_nav_entry_id();
-    DCHECK(!render_frame_host->GetNavigationHandle()->IsDownload());
-  }
-  DiscardPendingEntryIfNeeded(expected_pending_entry_id, false);
+  int expected_pending_entry_id =
+      render_frame_host->GetNavigationHandle()
+          ? render_frame_host->GetNavigationHandle()->pending_nav_entry_id()
+          : 0;
+  DiscardPendingEntryIfNeeded(expected_pending_entry_id);
 }
 
 void NavigatorImpl::DidFailLoadWithError(
@@ -556,8 +554,7 @@
     WindowOpenDisposition disposition,
     bool should_replace_current_entry,
     bool user_gesture,
-    blink::WebTriggeringEventInfo triggering_event_info,
-    const base::Optional<std::string>& suggested_filename) {
+    blink::WebTriggeringEventInfo triggering_event_info) {
   // Note: This can be called for subframes (even when OOPIFs are not possible)
   // if the disposition calls for a different window.
 
@@ -606,7 +603,6 @@
   params.should_replace_current_entry = should_replace_current_entry;
   params.user_gesture = user_gesture;
   params.triggering_event_info = triggering_event_info;
-  params.suggested_filename = suggested_filename;
 
   // RequestOpenURL is used only for local frames, so we can get here only if
   // the navigation is initiated by a frame in the same SiteInstance as this
@@ -645,8 +641,7 @@
     bool should_replace_current_entry,
     const std::string& method,
     scoped_refptr<network::ResourceRequestBody> post_body,
-    const std::string& extra_headers,
-    const base::Optional<std::string>& suggested_filename) {
+    const std::string& extra_headers) {
   // |method != "POST"| should imply absence of |post_body|.
   if (method != "POST" && post_body) {
     NOTREACHED();
@@ -694,7 +689,7 @@
   controller_->NavigateFromFrameProxy(
       render_frame_host, url, is_renderer_initiated, source_site_instance,
       referrer_to_use, page_transition, should_replace_current_entry, method,
-      post_body, extra_headers, suggested_filename);
+      post_body, extra_headers);
 }
 
 void NavigatorImpl::OnBeforeUnloadACK(FrameTreeNode* frame_tree_node,
@@ -884,15 +879,14 @@
   }
 }
 
-void NavigatorImpl::DiscardPendingEntryIfNeeded(int expected_pending_entry_id,
-                                                bool is_download) {
+void NavigatorImpl::DiscardPendingEntryIfNeeded(int expected_pending_entry_id) {
   // Racy conditions can cause a fail message to arrive after its corresponding
   // pending entry has been replaced by another navigation. If
   // |DiscardPendingEntry| is called in this case, then the completely valid
   // entry for the new navigation would be discarded. See crbug.com/513742. To
   // catch this case, the current pending entry is compared against the current
   // navigation handle's entry id, which should correspond to the failed load.
-  NavigationEntryImpl* pending_entry = controller_->GetPendingEntry();
+  NavigationEntry* pending_entry = controller_->GetPendingEntry();
   bool pending_matches_fail_msg =
       pending_entry &&
       expected_pending_entry_id == pending_entry->GetUniqueID();
@@ -913,13 +907,9 @@
   // allow the view to clear the pending entry and typed URL if the user
   // requests (e.g., hitting Escape with focus in the address bar).
   //
-  // Note that the pending entry does not need to be preserved for downloads,
-  // since the user is unlikely to try again.
-  //
   // Note: don't touch the transient entry, since an interstitial may exist.
-  bool should_preserve_entry = (controller_->IsUnmodifiedBlankTab() ||
-                                delegate_->ShouldPreserveAbortedURLs()) &&
-                               !is_download;
+  bool should_preserve_entry = controller_->IsUnmodifiedBlankTab() ||
+                               delegate_->ShouldPreserveAbortedURLs();
   if (pending_entry != controller_->GetVisibleEntry() ||
       !should_preserve_entry) {
     controller_->DiscardPendingEntry(true);
diff --git a/content/browser/frame_host/navigator_impl.h b/content/browser/frame_host/navigator_impl.h
index 99aeea9e..741e7a0e 100644
--- a/content/browser/frame_host/navigator_impl.h
+++ b/content/browser/frame_host/navigator_impl.h
@@ -86,8 +86,7 @@
       WindowOpenDisposition disposition,
       bool should_replace_current_entry,
       bool user_gesture,
-      blink::WebTriggeringEventInfo triggering_event_info,
-      const base::Optional<std::string>& suggested_filename) override;
+      blink::WebTriggeringEventInfo triggering_event_info) override;
   void NavigateFromFrameProxy(
       RenderFrameHostImpl* render_frame_host,
       const GURL& url,
@@ -97,8 +96,7 @@
       bool should_replace_current_entry,
       const std::string& method,
       scoped_refptr<network::ResourceRequestBody> post_body,
-      const std::string& extra_headers,
-      const base::Optional<std::string>& suggested_filename) override;
+      const std::string& extra_headers) override;
   void OnBeforeUnloadACK(FrameTreeNode* frame_tree_node,
                          bool proceed,
                          const base::TimeTicks& proceed_time) override;
@@ -117,8 +115,7 @@
       const base::TimeTicks& renderer_before_unload_end_time) override;
   void CancelNavigation(FrameTreeNode* frame_tree_node,
                         bool inform_renderer) override;
-  void DiscardPendingEntryIfNeeded(int expected_pending_entry_id,
-                                   bool is_download) override;
+  void DiscardPendingEntryIfNeeded(int expected_pending_entry_id) override;
 
  private:
   // Holds data used to track browser side navigation metrics.
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 3430e16..20bf6abe 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1490,7 +1490,7 @@
       this, validated_url, params.uses_post, params.resource_request_body,
       params.extra_headers, params.referrer, params.disposition,
       params.should_replace_current_entry, params.user_gesture,
-      params.triggering_event_info, params.suggested_filename);
+      params.triggering_event_info);
 }
 
 void RenderFrameHostImpl::CancelInitialHistoryLoad() {
@@ -3529,8 +3529,7 @@
       GURL(), GURL(), PREVIEWS_OFF, base::TimeTicks::Now(), "GET", nullptr,
       base::Optional<SourceLocation>(),
       CSPDisposition::CHECK /* should_check_main_world_csp */,
-      false /* started_from_context_menu */, false /* has_user_gesture */,
-      base::nullopt /* suggested_filename */);
+      false /* started_from_context_menu */, false /* has_user_gesture */);
   CommitNavigation(nullptr, network::mojom::URLLoaderClientEndpointsPtr(),
                    common_params, RequestNavigationParams(), false,
                    base::nullopt, base::nullopt /* subresource_overrides */,
@@ -3795,7 +3794,7 @@
           this, false /* is_navigation */, &factory_request);
       // Keep DevTools proxy lasy, i.e. closest to the network.
       RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-          this, false, false /* have_suggested_filename */, &factory_request);
+          this, false, &factory_request);
       factory.second->Clone(std::move(factory_request));
       subresource_loader_factories->factories_info().emplace(
           factory.first, std::move(factory_proxy_info));
@@ -4367,7 +4366,7 @@
       this, false /* is_navigation */, &default_factory_request);
   // Keep DevTools proxy lasy, i.e. closest to the network.
   RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-      this, false, false, &default_factory_request);
+      this, false, &default_factory_request);
   StoragePartitionImpl* storage_partition = static_cast<StoragePartitionImpl*>(
       BrowserContext::GetStoragePartition(context, GetSiteInstance()));
   if (g_create_network_factory_callback_for_test.Get().is_null()) {
@@ -4732,7 +4731,6 @@
       false,                  // started_from_context_menu
       CSPDisposition::CHECK,  // should_check_main_world_csp
       false,                  // is_form_submission
-      base::nullopt,          // suggested_filename
       nullptr);               // navigation_ui_data
 }
 
@@ -4794,7 +4792,6 @@
       false,                  // started_from_context_menu
       CSPDisposition::CHECK,  // should_check_main_world_csp
       false,                  // is_form_submission
-      base::nullopt,          // suggested_filename
       nullptr);               // navigation_ui_data
 }
 
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 0347880f..73b66a5 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -514,12 +514,9 @@
       // already updated its state properly, and doesn't need to be notified.
       if (speculative_render_frame_host_->GetNavigationHandle() &&
           request.from_begin_navigation()) {
-        DCHECK(!speculative_render_frame_host_->GetNavigationHandle()
-                    ->IsDownload());
         frame_tree_node_->navigator()->DiscardPendingEntryIfNeeded(
             speculative_render_frame_host_->GetNavigationHandle()
-                ->pending_nav_entry_id(),
-            false /* is_download */);
+                ->pending_nav_entry_id());
       }
       DiscardUnusedFrame(UnsetSpeculativeRenderFrameHost());
     }
@@ -556,12 +553,9 @@
       if (speculative_render_frame_host_ &&
           speculative_render_frame_host_->GetNavigationHandle() &&
           request.from_begin_navigation()) {
-        DCHECK(!speculative_render_frame_host_->GetNavigationHandle()
-                    ->IsDownload());
         frame_tree_node_->navigator()->DiscardPendingEntryIfNeeded(
             speculative_render_frame_host_->GetNavigationHandle()
-                ->pending_nav_entry_id(),
-            false /* is_download */);
+                ->pending_nav_entry_id());
       }
 
       // If a previous speculative RenderFrameHost didn't exist or if its
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index 6dcca03..fecf653 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -306,7 +306,7 @@
       current_rfh, validated_url, site_instance_.get(), params.referrer,
       ui::PAGE_TRANSITION_LINK, params.should_replace_current_entry,
       params.uses_post ? "POST" : "GET", params.resource_request_body,
-      params.extra_headers, params.suggested_filename);
+      params.extra_headers);
 }
 
 void RenderFrameProxyHost::OnCheckCompleted() {
diff --git a/content/browser/gpu/gpu_client_impl.cc b/content/browser/gpu/gpu_client_impl.cc
index 2444086..a591794 100644
--- a/content/browser/gpu/gpu_client_impl.cc
+++ b/content/browser/gpu/gpu_client_impl.cc
@@ -25,30 +25,30 @@
   return gpu_client;
 }
 
-GpuClientImpl::GpuClientImpl(int client_id)
-    : client_id_(client_id), weak_factory_(this) {
-  gpu_bindings_.set_connection_error_handler(
+GpuClientImpl::GpuClientImpl(int render_process_id)
+    : render_process_id_(render_process_id), weak_factory_(this) {
+  bindings_.set_connection_error_handler(
       base::Bind(&GpuClientImpl::OnError, base::Unretained(this),
                  ErrorReason::kConnectionLost));
 }
 
 GpuClientImpl::~GpuClientImpl() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  gpu_bindings_.CloseAllBindings();
+  bindings_.CloseAllBindings();
   OnError(ErrorReason::kInDestructor);
 }
 
 void GpuClientImpl::Add(ui::mojom::GpuRequest request) {
-  gpu_bindings_.AddBinding(this, std::move(request));
+  bindings_.AddBinding(this, std::move(request));
 }
 
 void GpuClientImpl::OnError(ErrorReason reason) {
   ClearCallback();
-  if (gpu_bindings_.empty()) {
+  if (bindings_.empty()) {
     BrowserGpuMemoryBufferManager* gpu_memory_buffer_manager =
         BrowserGpuMemoryBufferManager::current();
     if (gpu_memory_buffer_manager)
-      gpu_memory_buffer_manager->ProcessRemoved(client_id_);
+      gpu_memory_buffer_manager->ProcessRemoved(render_process_id_);
   }
   if (reason == ErrorReason::kConnectionLost && connection_error_handler_)
     std::move(connection_error_handler_).Run(this);
@@ -85,8 +85,8 @@
   }
   if (callback) {
     // A request is waiting.
-    std::move(callback).Run(client_id_, std::move(channel_handle), gpu_info,
-                            gpu_feature_info);
+    std::move(callback).Run(render_process_id_, std::move(channel_handle),
+                            gpu_info, gpu_feature_info);
     return;
   }
   if (status == GpuProcessHost::EstablishChannelStatus::SUCCESS) {
@@ -108,7 +108,7 @@
   if (!callback_)
     return;
   EstablishGpuChannelCallback callback = std::move(callback_);
-  std::move(callback).Run(client_id_, mojo::ScopedMessagePipeHandle(),
+  std::move(callback).Run(render_process_id_, mojo::ScopedMessagePipeHandle(),
                           gpu::GPUInfo(), gpu::GpuFeatureInfo());
   DCHECK(!callback_);
 }
@@ -123,8 +123,8 @@
     //   2) if callback is empty, it's PreEstablishGpyChannel() being called
     //      more than once, no need to do anything.
     if (callback) {
-      std::move(callback).Run(client_id_, std::move(channel_handle_), gpu_info_,
-                              gpu_feature_info_);
+      std::move(callback).Run(render_process_id_, std::move(channel_handle_),
+                              gpu_info_, gpu_feature_info_);
       DCHECK(!channel_handle_.is_valid());
     }
     return;
@@ -132,8 +132,9 @@
   GpuProcessHost* host = GpuProcessHost::Get();
   if (!host) {
     if (callback) {
-      std::move(callback).Run(client_id_, mojo::ScopedMessagePipeHandle(),
-                              gpu::GPUInfo(), gpu::GpuFeatureInfo());
+      std::move(callback).Run(render_process_id_,
+                              mojo::ScopedMessagePipeHandle(), gpu::GPUInfo(),
+                              gpu::GpuFeatureInfo());
     }
     return;
   }
@@ -145,8 +146,9 @@
   bool allow_view_command_buffers = false;
   bool allow_real_time_streams = false;
   host->EstablishGpuChannel(
-      client_id_,
-      ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(client_id_),
+      render_process_id_,
+      ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
+          render_process_id_),
       preempts, allow_view_command_buffers, allow_real_time_streams,
       base::Bind(&GpuClientImpl::OnEstablishGpuChannel,
                  weak_factory_.GetWeakPtr()));
@@ -173,7 +175,7 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    ui::mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback) {
+    ui::mojom::Gpu::CreateGpuMemoryBufferCallback callback) {
   DCHECK(BrowserGpuMemoryBufferManager::current());
 
   base::CheckedNumeric<int> bytes = size.width();
@@ -185,7 +187,7 @@
 
   BrowserGpuMemoryBufferManager::current()
       ->AllocateGpuMemoryBufferForChildProcess(
-          id, size, format, usage, client_id_,
+          id, size, format, usage, render_process_id_,
           base::BindOnce(&GpuClientImpl::OnCreateGpuMemoryBuffer,
                          weak_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -195,12 +197,7 @@
   DCHECK(BrowserGpuMemoryBufferManager::current());
 
   BrowserGpuMemoryBufferManager::current()->ChildProcessDeletedGpuMemoryBuffer(
-      id, client_id_, sync_token);
-}
-
-void GpuClientImpl::CreateGpuMemoryBufferFactory(
-    ui::mojom::GpuMemoryBufferFactoryRequest request) {
-  gpu_memory_buffer_factory_bindings_.AddBinding(this, std::move(request));
+      id, render_process_id_, sync_token);
 }
 
 }  // namespace content
diff --git a/content/browser/gpu/gpu_client_impl.h b/content/browser/gpu/gpu_client_impl.h
index 4d518c8..03e29bf4 100644
--- a/content/browser/gpu/gpu_client_impl.h
+++ b/content/browser/gpu/gpu_client_impl.h
@@ -13,9 +13,7 @@
 
 namespace content {
 
-class GpuClientImpl : public ui::mojom::GpuMemoryBufferFactory,
-                      public ui::mojom::Gpu,
-                      public GpuClient {
+class GpuClientImpl : public ui::mojom::Gpu, public GpuClient {
  public:
   explicit GpuClientImpl(int render_process_id);
   ~GpuClientImpl() override;
@@ -27,27 +25,6 @@
   void SetConnectionErrorHandler(
       ConnectionErrorHandlerClosure connection_error_handler);
 
-  // ui::mojom::GpuMemoryBufferFactory overrides:
-  void CreateGpuMemoryBuffer(
-      gfx::GpuMemoryBufferId id,
-      const gfx::Size& size,
-      gfx::BufferFormat format,
-      gfx::BufferUsage usage,
-      ui::mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback)
-      override;
-  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
-                              const gpu::SyncToken& sync_token) override;
-
-  // ui::mojom::Gpu overrides:
-  void CreateGpuMemoryBufferFactory(
-      ui::mojom::GpuMemoryBufferFactoryRequest request) override;
-  void EstablishGpuChannel(EstablishGpuChannelCallback callback) override;
-  void CreateJpegDecodeAccelerator(
-      media::mojom::JpegDecodeAcceleratorRequest jda_request) override;
-  void CreateVideoEncodeAcceleratorProvider(
-      media::mojom::VideoEncodeAcceleratorProviderRequest vea_provider_request)
-      override;
-
  private:
   enum class ErrorReason {
     // OnError() is being called from the destructor.
@@ -64,10 +41,24 @@
                                const gfx::GpuMemoryBufferHandle& handle);
   void ClearCallback();
 
-  const int client_id_;
-  mojo::BindingSet<ui::mojom::GpuMemoryBufferFactory>
-      gpu_memory_buffer_factory_bindings_;
-  mojo::BindingSet<ui::mojom::Gpu> gpu_bindings_;
+  // ui::mojom::Gpu overrides:
+  void EstablishGpuChannel(EstablishGpuChannelCallback callback) override;
+  void CreateJpegDecodeAccelerator(
+      media::mojom::JpegDecodeAcceleratorRequest jda_request) override;
+  void CreateVideoEncodeAcceleratorProvider(
+      media::mojom::VideoEncodeAcceleratorProviderRequest vea_provider_request)
+      override;
+  void CreateGpuMemoryBuffer(
+      gfx::GpuMemoryBufferId id,
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      gfx::BufferUsage usage,
+      ui::mojom::Gpu::CreateGpuMemoryBufferCallback callback) override;
+  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                              const gpu::SyncToken& sync_token) override;
+
+  const int render_process_id_;
+  mojo::BindingSet<ui::mojom::Gpu> bindings_;
   bool gpu_channel_requested_ = false;
   EstablishGpuChannelCallback callback_;
   mojo::ScopedMessagePipeHandle channel_handle_;
diff --git a/content/browser/loader/mime_sniffing_resource_handler.cc b/content/browser/loader/mime_sniffing_resource_handler.cc
index 577f62b5..305f48e6 100644
--- a/content/browser/loader/mime_sniffing_resource_handler.cc
+++ b/content/browser/loader/mime_sniffing_resource_handler.cc
@@ -557,23 +557,11 @@
 
   must_download_is_set_ = true;
 
-  bool is_cross_origin =
-      (request()->initiator().has_value() &&
-       !request()->url_chain().back().SchemeIsBlob() &&
-       !request()->url_chain().back().SchemeIsFileSystem() &&
-       !request()->url_chain().back().SchemeIs(url::kAboutScheme) &&
-       !request()->url_chain().back().SchemeIs(url::kDataScheme) &&
-       request()->initiator()->GetURL() !=
-           request()->url_chain().back().GetOrigin());
-
   std::string disposition;
   request()->GetResponseHeaderByName("content-disposition", &disposition);
   if (!disposition.empty() &&
       net::HttpContentDisposition(disposition, std::string()).is_attachment()) {
     must_download_ = true;
-  } else if (GetRequestInfo()->suggested_filename().has_value() &&
-             !is_cross_origin) {
-    must_download_ = true;
   } else if (GetContentClient()->browser()->ShouldForceDownloadResource(
                  request()->url(), response_->head.mime_type)) {
     must_download_ = true;
@@ -592,11 +580,6 @@
     must_download_ = false;
   }
 
-  if (GetRequestInfo()->suggested_filename().has_value() && !must_download_) {
-    download::RecordDownloadCount(
-        download::CROSS_ORIGIN_DOWNLOAD_WITHOUT_CONTENT_DISPOSITION);
-  }
-
   return must_download_;
 }
 
diff --git a/content/browser/loader/navigation_loader_util.cc b/content/browser/loader/navigation_loader_util.cc
index 6a49ee5..96a7879 100644
--- a/content/browser/loader/navigation_loader_util.cc
+++ b/content/browser/loader/navigation_loader_util.cc
@@ -17,9 +17,7 @@
 
 bool MustDownload(const GURL& url,
                   net::HttpResponseHeaders* headers,
-                  const std::string& mime_type,
-                  bool have_suggested_filename,
-                  bool is_cross_origin) {
+                  const std::string& mime_type) {
   if (headers) {
     std::string disposition;
     if (headers->GetNormalizedHeader("content-disposition", &disposition) &&
@@ -28,8 +26,6 @@
             .is_attachment()) {
       return true;
     }
-    if (have_suggested_filename && !is_cross_origin)
-      return true;
     if (GetContentClient()->browser()->ShouldForceDownloadResource(url,
                                                                    mime_type))
       return true;
@@ -48,13 +44,9 @@
 
 bool IsDownload(const GURL& url,
                 net::HttpResponseHeaders* headers,
-                const std::string& mime_type,
-                bool have_suggested_filename,
-                bool is_cross_origin) {
-  if (MustDownload(url, headers, mime_type, have_suggested_filename,
-                   is_cross_origin)) {
+                const std::string& mime_type) {
+  if (MustDownload(url, headers, mime_type))
     return true;
-  }
 
   if (blink::IsSupportedMimeType(mime_type))
     return false;
@@ -62,14 +54,5 @@
   return !headers || headers->response_code() / 100 == 2;
 }
 
-bool IsCrossOriginRequest(const GURL& request_url,
-                          const base::Optional<url::Origin>& initiator) {
-  return initiator.has_value() && !request_url.SchemeIsBlob() &&
-         !request_url.SchemeIsFileSystem() &&
-         !request_url.SchemeIs(url::kAboutScheme) &&
-         !request_url.SchemeIs(url::kDataScheme) &&
-         initiator->GetURL() != request_url.GetOrigin();
-}
-
 }  // namespace navigation_loader_util
 }  // namespace content
diff --git a/content/browser/loader/navigation_loader_util.h b/content/browser/loader/navigation_loader_util.h
index 69ae9d5..d574155 100644
--- a/content/browser/loader/navigation_loader_util.h
+++ b/content/browser/loader/navigation_loader_util.h
@@ -13,9 +13,6 @@
 namespace net {
 class HttpResponseHeaders;
 }
-namespace url {
-class Origin;
-}
 
 namespace content {
 namespace navigation_loader_util {
@@ -23,22 +20,15 @@
 // Returns true if the given response must be downloaded because of the headers.
 bool MustDownload(const GURL& url,
                   net::HttpResponseHeaders* headers,
-                  const std::string& mime_type,
-                  bool have_suggested_filename,
-                  bool is_cross_origin);
+                  const std::string& mime_type);
 
 // Determines whether given response would result in a download.
 // Note this doesn't handle the case when a plugin exists for the |mime_type|.
 bool IsDownload(const GURL& url,
                 net::HttpResponseHeaders* headers,
-                const std::string& mime_type,
-                bool have_suggested_filename,
-                bool is_cross_origin);
-
-bool IsCrossOriginRequest(const GURL& request_url,
-                          const base::Optional<url::Origin>& initiator);
+                const std::string& mime_type);
 
 }  // namespace navigation_loader_util
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_LOADER_NAVIGATION_LOADER_UTIL_H_
\ No newline at end of file
+#endif  // CONTENT_BROWSER_LOADER_NAVIGATION_LOADER_UTIL_H_
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc
index fcac66e..2832ad4 100644
--- a/content/browser/loader/navigation_url_loader_network_service.cc
+++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -149,14 +149,6 @@
         "combination of both) limits the scope of these requests."
       )");
 
-bool HasContentDispositionAttachment(const net::HttpResponseHeaders& headers) {
-  std::string disposition;
-  return headers.GetNormalizedHeader("content-disposition", &disposition) &&
-         !disposition.empty() &&
-         net::HttpContentDisposition(disposition, std::string())
-             .is_attachment();
-}
-
 std::unique_ptr<network::ResourceRequest> CreateResourceRequest(
     NavigationRequestInfo* request_info,
     int frame_tree_node_id,
@@ -289,8 +281,6 @@
       ResourceContext* resource_context,
       scoped_refptr<URLLoaderFactoryGetter> default_url_loader_factory_getter,
       const GURL& url,
-      base::Optional<url::Origin> initiator_origin,
-      base::Optional<std::string> suggested_filename,
       network::mojom::URLLoaderFactoryRequest proxied_factory_request,
       network::mojom::URLLoaderFactoryPtrInfo proxied_factory_info,
       std::set<std::string> known_schemes,
@@ -300,8 +290,6 @@
         resource_context_(resource_context),
         default_url_loader_factory_getter_(default_url_loader_factory_getter),
         url_(url),
-        initiator_origin_(initiator_origin),
-        suggested_filename_(suggested_filename),
         owner_(owner),
         response_loader_binding_(this),
         proxied_factory_request_(std::move(proxied_factory_request)),
@@ -811,17 +799,8 @@
     bool is_stream;
     std::unique_ptr<NavigationData> cloned_navigation_data;
     if (IsLoaderInterceptionEnabled()) {
-      DCHECK(url_chain_.empty() || url_ == url_chain_.back());
-      bool is_cross_origin =
-          navigation_loader_util::IsCrossOriginRequest(url_, initiator_origin_);
       bool must_download = navigation_loader_util::MustDownload(
-          url_, head.headers.get(), head.mime_type,
-          suggested_filename_.has_value(), is_cross_origin);
-      if (must_download && suggested_filename_.has_value() && is_cross_origin &&
-          (!head.headers || !HasContentDispositionAttachment(*head.headers))) {
-        download::RecordDownloadCount(
-            download::CROSS_ORIGIN_DOWNLOAD_WITHOUT_CONTENT_DISPOSITION);
-      }
+          url_, head.headers.get(), head.mime_type);
       bool known_mime_type = blink::IsSupportedMimeType(head.mime_type);
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -1095,13 +1074,6 @@
   // Current URL that is being navigated, updated after redirection.
   GURL url_;
 
-  base::Optional<url::Origin> initiator_origin_;
-
-  // If this request was triggered by an anchor tag with a download attribute,
-  // the |suggested_filename_| will be the (possibly empty) value of said
-  // attribute.
-  base::Optional<std::string> suggested_filename_;
-
   // Currently used by the AppCache loader to pass its factory to the
   // renderer which enables it to handle subresources.
   base::Optional<SubresourceLoaderParams> subresource_loader_params_;
@@ -1207,8 +1179,6 @@
         std::move(new_request), resource_context,
         /* default_url_factory_getter = */ nullptr,
         request_info->common_params.url,
-        request_info->begin_params->initiator_origin,
-        request_info->common_params.suggested_filename,
         /* proxied_url_loader_factory_request */ nullptr,
         /* proxied_url_loader_factory_info */ nullptr, std::set<std::string>(),
         weak_factory_.GetWeakPtr());
@@ -1259,9 +1229,7 @@
         frame_tree_node->current_frame_host(), true /* is_navigation */,
         &factory_request);
     if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory(
-            frame_tree_node->current_frame_host(), true,
-            request_info->common_params.suggested_filename.has_value(),
-            &factory_request)) {
+            frame_tree_node->current_frame_host(), true, &factory_request)) {
       use_proxy = true;
     }
     if (use_proxy) {
@@ -1285,8 +1253,6 @@
   request_controller_ = std::make_unique<URLLoaderRequestController>(
       std::move(initial_interceptors), std::move(new_request), resource_context,
       partition->url_loader_factory_getter(), request_info->common_params.url,
-      request_info->begin_params->initiator_origin,
-      request_info->common_params.suggested_filename,
       std::move(proxied_factory_request), std::move(proxied_factory_info),
       std::move(known_schemes), weak_factory_.GetWeakPtr());
   BrowserThread::PostTask(
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 2baf6b5..c2f29f6 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -1050,8 +1050,7 @@
           request_data.referrer_policy),
       request_data.is_prerendering, resource_context, report_raw_headers,
       !is_sync_load, request_data.previews_state, request_data.request_body,
-      request_data.initiated_in_secure_context,
-      base::nullopt);  // suggested_filename
+      request_data.initiated_in_secure_context);
   extra_info->SetBlobHandles(std::move(blob_handles));
 
   // Request takes ownership.
@@ -1308,8 +1307,7 @@
       true,            // is_async
       previews_state,  // previews_state
       nullptr,         // body
-      false,           // initiated_in_secure_context
-      base::nullopt);  // suggested_filename
+      false);          // initiated_in_secure_context
 }
 
 void ResourceDispatcherHostImpl::OnRenderViewHostCreated(
@@ -1690,8 +1688,7 @@
       // subresource requests, so it doesn't matter what value it gets here.
       // If in the future this changes this should be updated to somehow get a
       // meaningful value.
-      false,                                   // initiated_in_secure_context
-      info.common_params.suggested_filename);  // suggested_filename
+      false);  // initiated_in_secure_context
   extra_info->SetBlobHandles(std::move(blob_handles));
   extra_info->set_navigation_ui_data(std::move(navigation_ui_data));
 
diff --git a/content/browser/loader/resource_request_info_impl.cc b/content/browser/loader/resource_request_info_impl.cc
index 36ffa04..4714491 100644
--- a/content/browser/loader/resource_request_info_impl.cc
+++ b/content/browser/loader/resource_request_info_impl.cc
@@ -84,8 +84,7 @@
       is_async,                            // is_async
       previews_state,                      // previews_state
       nullptr,                             // body
-      false,                               // initiated_in_secure_context
-      base::nullopt);                      // suggested_filename
+      false);                              // initiated_in_secure_context
   info->AssociateWithRequest(request);
   info->set_navigation_ui_data(std::move(navigation_ui_data));
 }
@@ -152,8 +151,7 @@
     bool is_async,
     PreviewsState previews_state,
     const scoped_refptr<network::ResourceRequestBody> body,
-    bool initiated_in_secure_context,
-    const base::Optional<std::string>& suggested_filename)
+    bool initiated_in_secure_context)
     : detachable_handler_(nullptr),
       requester_info_(std::move(requester_info)),
       route_id_(route_id),
@@ -183,7 +181,6 @@
       previews_state_(previews_state),
       body_(body),
       initiated_in_secure_context_(initiated_in_secure_context),
-      suggested_filename_(suggested_filename),
       blocked_cross_site_document_(false),
       first_auth_attempt_(true) {}
 
diff --git a/content/browser/loader/resource_request_info_impl.h b/content/browser/loader/resource_request_info_impl.h
index 93f3018..e9b9745 100644
--- a/content/browser/loader/resource_request_info_impl.h
+++ b/content/browser/loader/resource_request_info_impl.h
@@ -69,8 +69,7 @@
       bool is_async,
       PreviewsState previews_state,
       const scoped_refptr<network::ResourceRequestBody> body,
-      bool initiated_in_secure_context,
-      const base::Optional<std::string>& suggested_filename);
+      bool initiated_in_secure_context);
   ~ResourceRequestInfoImpl() override;
 
   // ResourceRequestInfo implementation:
@@ -181,10 +180,6 @@
 
   void SetBlobHandles(BlobHandles blob_handles);
 
-  const base::Optional<std::string>& suggested_filename() const {
-    return suggested_filename_;
-  }
-
   bool blocked_cross_site_document() const {
     return blocked_cross_site_document_;
   }
@@ -239,7 +234,6 @@
   scoped_refptr<network::ResourceRequestBody> body_;
   bool initiated_in_secure_context_;
   std::unique_ptr<NavigationUIData> navigation_ui_data_;
-  base::Optional<std::string> suggested_filename_;
   bool blocked_cross_site_document_;
   bool first_auth_attempt_;
 
diff --git a/content/browser/media/audio_input_stream_broker.cc b/content/browser/media/audio_input_stream_broker.cc
new file mode 100644
index 0000000..d4bbd8c
--- /dev/null
+++ b/content/browser/media/audio_input_stream_broker.cc
@@ -0,0 +1,161 @@
+// 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 "content/browser/media/audio_input_stream_broker.h"
+
+#include <utility>
+
+#include "base/command_line.h"
+#include "content/browser/browser_main_loop.h"
+#include "content/browser/media/media_internals.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/media_observer.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_client.h"
+#include "media/audio/audio_logging.h"
+#include "media/base/media_switches.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+#if defined(OS_CHROMEOS)
+#include "content/browser/media/keyboard_mic_registration.h"
+#endif
+
+namespace content {
+
+AudioInputStreamBroker::AudioInputStreamBroker(
+    int render_process_id,
+    int render_frame_id,
+    const std::string& device_id,
+    const media::AudioParameters& params,
+    uint32_t shared_memory_count,
+    bool enable_agc,
+    AudioStreamBroker::DeleterCallback deleter,
+    mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client)
+    : AudioStreamBroker(render_process_id, render_frame_id),
+      device_id_(device_id),
+      params_(params),
+      shared_memory_count_(shared_memory_count),
+      enable_agc_(enable_agc),
+      deleter_(std::move(deleter)),
+      renderer_factory_client_(std::move(renderer_factory_client)),
+      observer_binding_(this),
+      weak_ptr_factory_(this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(renderer_factory_client_);
+  DCHECK(deleter_);
+
+  // Unretained is safe because |this| owns |renderer_factory_client_|.
+  renderer_factory_client_.set_connection_error_handler(
+      base::BindOnce(&AudioInputStreamBroker::Cleanup, base::Unretained(this)));
+
+  // Notify RenderProcessHost about input stream so the renderer is not
+  // background.
+  auto* process_host = RenderProcessHost::FromID(render_process_id);
+  if (process_host)
+    process_host->OnMediaStreamAdded();
+
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kUseFakeDeviceForMediaStream)) {
+    params_.set_format(media::AudioParameters::AUDIO_FAKE);
+  }
+
+#if defined(OS_CHROMEOS)
+  if (params_.channel_layout() ==
+      media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
+    BrowserMainLoop* browser_main_loop = BrowserMainLoop::GetInstance();
+
+    // May be null in unit tests.
+    if (browser_main_loop)
+      browser_main_loop->keyboard_mic_registration()->Register();
+  }
+#endif
+}
+
+AudioInputStreamBroker::~AudioInputStreamBroker() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+#if defined(OS_CHROMEOS)
+  if (params_.channel_layout() ==
+      media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
+    BrowserMainLoop* browser_main_loop = BrowserMainLoop::GetInstance();
+
+    // May be null in unit tests.
+    if (browser_main_loop)
+      browser_main_loop->keyboard_mic_registration()->Deregister();
+  }
+#endif
+
+  auto* process_host = RenderProcessHost::FromID(render_process_id());
+  if (process_host)
+    process_host->OnMediaStreamRemoved();
+
+  // TODO(https://crbug.com/829317) update tab recording indicator.
+}
+
+void AudioInputStreamBroker::CreateStream(
+    audio::mojom::StreamFactory* factory) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(!observer_binding_.is_bound());
+  DCHECK(!client_request_);
+
+  media::mojom::AudioInputStreamClientPtr client;
+  client_request_ = mojo::MakeRequest(&client);
+
+  media::mojom::AudioInputStreamPtr stream;
+  media::mojom::AudioInputStreamRequest stream_request =
+      mojo::MakeRequest(&stream);
+
+  media::mojom::AudioInputStreamObserverPtr observer_ptr;
+  observer_binding_.Bind(mojo::MakeRequest(&observer_ptr));
+
+  // Unretained is safe because |this| owns |observer_binding_|.
+  observer_binding_.set_connection_error_handler(
+      base::BindOnce(&AudioInputStreamBroker::Cleanup, base::Unretained(this)));
+
+  // Note that the component id for AudioLog is used to differentiate between
+  // several users of the same audio log. Since this audio log is for a single
+  // stream, the component id used doesn't matter.
+  // TODO(https://crbug.com/836226) pass valid user input monitor handle when
+  // switching to audio service input streams.
+  constexpr int log_component_id = 0;
+  factory->CreateInputStream(
+      std::move(stream_request), std::move(client), std::move(observer_ptr),
+      MediaInternals::GetInstance()->CreateMojoAudioLog(
+          media::AudioLogFactory::AudioComponent::AUDIO_INPUT_CONTROLLER,
+          log_component_id, render_process_id(), render_frame_id()),
+      device_id_, params_, shared_memory_count_, enable_agc_,
+      mojo::ScopedSharedBufferHandle(),
+      base::BindOnce(&AudioInputStreamBroker::StreamCreated,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(stream)));
+}
+
+void AudioInputStreamBroker::DidStartRecording() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  // TODO(https://crbug.com/829317) update tab recording indicator.
+}
+
+void AudioInputStreamBroker::StreamCreated(
+    media::mojom::AudioInputStreamPtr stream,
+    media::mojom::AudioDataPipePtr data_pipe,
+    bool initially_muted) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (!data_pipe) {
+    Cleanup();
+    return;
+  }
+
+  DCHECK(renderer_factory_client_);
+  renderer_factory_client_->StreamCreated(
+      std::move(stream), std::move(client_request_), std::move(data_pipe),
+      initially_muted);
+}
+
+void AudioInputStreamBroker::Cleanup() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  std::move(deleter_).Run(this);
+}
+
+}  // namespace content
diff --git a/content/browser/media/audio_input_stream_broker.h b/content/browser/media/audio_input_stream_broker.h
new file mode 100644
index 0000000..4697479
--- /dev/null
+++ b/content/browser/media/audio_input_stream_broker.h
@@ -0,0 +1,70 @@
+// 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 CONTENT_BROWSER_MEDIA_AUDIO_INPUT_STREAM_BROKER_H_
+#define CONTENT_BROWSER_MEDIA_AUDIO_INPUT_STREAM_BROKER_H_
+
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "content/browser/media/audio_stream_broker.h"
+#include "content/common/content_export.h"
+#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
+#include "media/base/audio_parameters.h"
+#include "media/mojo/interfaces/audio_input_stream.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/audio/public/mojom/stream_factory.mojom.h"
+
+namespace content {
+
+// AudioInputStreamBroker is used to broker a connection between a client
+// (typically renderer) and the audio service. It is operated on the UI thread.
+class CONTENT_EXPORT AudioInputStreamBroker final
+    : public AudioStreamBroker,
+      public media::mojom::AudioInputStreamObserver {
+ public:
+  AudioInputStreamBroker(
+      int render_process_id,
+      int render_frame_id,
+      const std::string& device_id,
+      const media::AudioParameters& params,
+      uint32_t shared_memory_count,
+      bool enable_agc,
+      AudioStreamBroker::DeleterCallback deleter,
+      mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client);
+
+  ~AudioInputStreamBroker() final;
+
+  // Creates the stream.
+  void CreateStream(audio::mojom::StreamFactory* factory) final;
+
+  // media::AudioInputStreamObserver implementation.
+  void DidStartRecording() final;
+
+ private:
+  void StreamCreated(media::mojom::AudioInputStreamPtr stream,
+                     media::mojom::AudioDataPipePtr data_pipe,
+                     bool initially_muted);
+  void Cleanup();
+
+  const std::string device_id_;
+  media::AudioParameters params_;
+  const uint32_t shared_memory_count_;
+  const bool enable_agc_;
+
+  DeleterCallback deleter_;
+
+  mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client_;
+  mojo::Binding<AudioInputStreamObserver> observer_binding_;
+  media::mojom::AudioInputStreamClientRequest client_request_;
+
+  base::WeakPtrFactory<AudioInputStreamBroker> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioInputStreamBroker);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_MEDIA_AUDIO_INPUT_STREAM_BROKER_H_
diff --git a/content/browser/media/audio_input_stream_broker_unittest.cc b/content/browser/media/audio_input_stream_broker_unittest.cc
new file mode 100644
index 0000000..1d078d3
--- /dev/null
+++ b/content/browser/media/audio_input_stream_broker_unittest.cc
@@ -0,0 +1,296 @@
+// 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 "content/browser/media/audio_input_stream_broker.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/sync_socket.h"
+#include "base/test/mock_callback.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "media/mojo/interfaces/audio_input_stream.mojom.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Test;
+using ::testing::Mock;
+using ::testing::StrictMock;
+using ::testing::InSequence;
+
+namespace content {
+
+namespace {
+
+const int kRenderProcessId = 123;
+const int kRenderFrameId = 234;
+const uint32_t kShMemCount = 10;
+const bool kEnableAgc = false;
+const char kDeviceId[] = "testdeviceid";
+const bool kInitiallyMuted = false;
+
+media::AudioParameters TestParams() {
+  return media::AudioParameters::UnavailableDeviceParams();
+}
+
+using MockDeleterCallback = StrictMock<
+    base::MockCallback<base::OnceCallback<void(AudioStreamBroker*)>>>;
+
+class MockRendererAudioInputStreamFactoryClient
+    : public mojom::RendererAudioInputStreamFactoryClient {
+ public:
+  MockRendererAudioInputStreamFactoryClient() : binding_(this) {}
+  ~MockRendererAudioInputStreamFactoryClient() override {}
+
+  mojom::RendererAudioInputStreamFactoryClientPtr MakePtr() {
+    mojom::RendererAudioInputStreamFactoryClientPtr ret;
+    binding_.Bind(mojo::MakeRequest(&ret));
+    return ret;
+  }
+
+  MOCK_METHOD0(OnStreamCreated, void());
+
+  void StreamCreated(media::mojom::AudioInputStreamPtr input_stream,
+                     media::mojom::AudioInputStreamClientRequest client_request,
+                     media::mojom::AudioDataPipePtr data_pipe,
+                     bool initially_muted) override {
+    input_stream_ = std::move(input_stream);
+    client_request_ = std::move(client_request);
+    OnStreamCreated();
+  }
+
+  void CloseBinding() { binding_.Close(); }
+
+ private:
+  mojo::Binding<mojom::RendererAudioInputStreamFactoryClient> binding_;
+  media::mojom::AudioInputStreamPtr input_stream_;
+  media::mojom::AudioInputStreamClientRequest client_request_;
+};
+
+class MockStreamFactory : public audio::mojom::StreamFactory {
+ public:
+  MockStreamFactory() : binding_(this) {}
+  ~MockStreamFactory() final {}
+
+  audio::mojom::StreamFactoryPtr MakePtr() {
+    audio::mojom::StreamFactoryPtr ret;
+    binding_.Bind(mojo::MakeRequest(&ret));
+    return ret;
+  }
+
+  void CloseBinding() { binding_.Close(); }
+
+  // State of an expected stream creation. |device_id| and |params| are set
+  // ahead of time and verified during request. The other fields are filled in
+  // when the request is received.
+  struct StreamRequestData {
+    StreamRequestData(const std::string& device_id,
+                      const media::AudioParameters& params)
+        : device_id(device_id), params(params) {}
+
+    bool requested = false;
+    media::mojom::AudioInputStreamRequest stream_request;
+    media::mojom::AudioInputStreamClientPtr client;
+    media::mojom::AudioInputStreamObserverPtr observer;
+    media::mojom::AudioLogPtr log;
+    const std::string device_id;
+    const media::AudioParameters params;
+    uint32_t shared_memory_count;
+    bool enable_agc;
+    mojo::ScopedSharedBufferHandle key_press_count_buffer;
+    CreateInputStreamCallback created_callback;
+  };
+
+  void ExpectStreamCreation(StreamRequestData* ex) {
+    stream_request_data_ = ex;
+  }
+
+ private:
+  void CreateInputStream(media::mojom::AudioInputStreamRequest stream_request,
+                         media::mojom::AudioInputStreamClientPtr client,
+                         media::mojom::AudioInputStreamObserverPtr observer,
+                         media::mojom::AudioLogPtr log,
+                         const std::string& device_id,
+                         const media::AudioParameters& params,
+                         uint32_t shared_memory_count,
+                         bool enable_agc,
+                         mojo::ScopedSharedBufferHandle key_press_count_buffer,
+                         CreateInputStreamCallback created_callback) final {
+    // No way to cleanly exit the test here in case of failure, so use CHECK.
+    CHECK(stream_request_data_);
+    EXPECT_EQ(stream_request_data_->device_id, device_id);
+    EXPECT_TRUE(stream_request_data_->params.Equals(params));
+    stream_request_data_->requested = true;
+    stream_request_data_->stream_request = std::move(stream_request);
+    stream_request_data_->client = std::move(client);
+    stream_request_data_->observer = std::move(observer);
+    stream_request_data_->log = std::move(log);
+    stream_request_data_->shared_memory_count = shared_memory_count;
+    stream_request_data_->enable_agc = enable_agc;
+    stream_request_data_->key_press_count_buffer =
+        std::move(key_press_count_buffer);
+    stream_request_data_->created_callback = std::move(created_callback);
+  }
+
+  void CreateOutputStream(
+      media::mojom::AudioOutputStreamRequest stream_request,
+      media::mojom::AudioOutputStreamObserverAssociatedPtrInfo observer_info,
+      media::mojom::AudioLogPtr log,
+      const std::string& output_device_id,
+      const media::AudioParameters& params,
+      const base::UnguessableToken& group_id,
+      CreateOutputStreamCallback created_callback) final {
+    ADD_FAILURE() << "Unexpected output stream creation in input test.";
+  }
+
+  void BindMuter(audio::mojom::LocalMuterAssociatedRequest request,
+                 const base::UnguessableToken& group_id) final {
+    ADD_FAILURE() << "Unexpected muting in input stream test";
+  }
+
+  mojo::Binding<audio::mojom::StreamFactory> binding_;
+  StreamRequestData* stream_request_data_;
+};
+
+struct TestEnvironment {
+  TestEnvironment()
+      : broker(std::make_unique<AudioInputStreamBroker>(
+            kRenderProcessId,
+            kRenderFrameId,
+            kDeviceId,
+            TestParams(),
+            kShMemCount,
+            kEnableAgc,
+            deleter.Get(),
+            renderer_factory_client.MakePtr())) {}
+
+  void RunUntilIdle() { thread_bundle.RunUntilIdle(); }
+
+  TestBrowserThreadBundle thread_bundle;
+  MockDeleterCallback deleter;
+  StrictMock<MockRendererAudioInputStreamFactoryClient> renderer_factory_client;
+  std::unique_ptr<AudioInputStreamBroker> broker;
+  MockStreamFactory stream_factory;
+  audio::mojom::StreamFactoryPtr factory_ptr = stream_factory.MakePtr();
+};
+
+}  // namespace
+
+TEST(AudioInputStreamBrokerTest, StoresProcessAndFrameId) {
+  TestBrowserThreadBundle thread_bundle;
+  MockDeleterCallback deleter;
+  StrictMock<MockRendererAudioInputStreamFactoryClient> renderer_factory_client;
+
+  AudioInputStreamBroker broker(
+      kRenderProcessId, kRenderFrameId, kDeviceId, TestParams(), kShMemCount,
+      kEnableAgc, deleter.Get(), renderer_factory_client.MakePtr());
+
+  EXPECT_EQ(kRenderProcessId, broker.render_process_id());
+  EXPECT_EQ(kRenderFrameId, broker.render_frame_id());
+}
+
+TEST(AudioInputStreamBrokerTest, StreamCreationSuccess_Propagates) {
+  TestEnvironment env;
+  MockStreamFactory::StreamRequestData stream_request_data(kDeviceId,
+                                                           TestParams());
+  env.stream_factory.ExpectStreamCreation(&stream_request_data);
+
+  env.broker->CreateStream(env.factory_ptr.get());
+  env.RunUntilIdle();
+
+  EXPECT_TRUE(stream_request_data.requested);
+
+  // Set up test IPC primitives.
+  const size_t shmem_size = 456;
+  base::SyncSocket socket1, socket2;
+  base::SyncSocket::CreatePair(&socket1, &socket2);
+  std::move(stream_request_data.created_callback)
+      .Run({base::in_place, mojo::SharedBufferHandle::Create(shmem_size),
+            mojo::WrapPlatformFile(socket1.Release())},
+           kInitiallyMuted);
+
+  EXPECT_CALL(env.renderer_factory_client, OnStreamCreated());
+
+  env.RunUntilIdle();
+
+  Mock::VerifyAndClear(&env.renderer_factory_client);
+
+  env.broker.reset();
+}
+
+TEST(AudioInputStreamBrokerTest, StreamCreationFailure_CallsDeleter) {
+  TestEnvironment env;
+  MockStreamFactory::StreamRequestData stream_request_data(kDeviceId,
+                                                           TestParams());
+  env.stream_factory.ExpectStreamCreation(&stream_request_data);
+
+  env.broker->CreateStream(env.factory_ptr.get());
+  env.RunUntilIdle();
+
+  EXPECT_TRUE(stream_request_data.requested);
+  EXPECT_CALL(env.deleter, Run(env.broker.release()))
+      .WillOnce(testing::DeleteArg<0>());
+
+  std::move(stream_request_data.created_callback).Run(nullptr, kInitiallyMuted);
+
+  env.RunUntilIdle();
+}
+
+TEST(AudioInputStreamBrokerTest, RendererFactoryClientDisconnect_CallsDeleter) {
+  InSequence seq;
+  TestEnvironment env;
+  MockStreamFactory::StreamRequestData stream_request_data(kDeviceId,
+                                                           TestParams());
+  env.stream_factory.ExpectStreamCreation(&stream_request_data);
+
+  env.broker->CreateStream(env.factory_ptr.get());
+  env.RunUntilIdle();
+  EXPECT_TRUE(stream_request_data.requested);
+
+  EXPECT_CALL(env.deleter, Run(env.broker.release()))
+      .WillOnce(testing::DeleteArg<0>());
+  env.renderer_factory_client.CloseBinding();
+  env.RunUntilIdle();
+  Mock::VerifyAndClear(&env.deleter);
+
+  env.stream_factory.CloseBinding();
+  env.RunUntilIdle();
+}
+
+TEST(AudioInputStreamBrokerTest, ObserverDisconnect_CallsDeleter) {
+  InSequence seq;
+  TestEnvironment env;
+  MockStreamFactory::StreamRequestData stream_request_data(kDeviceId,
+                                                           TestParams());
+  env.stream_factory.ExpectStreamCreation(&stream_request_data);
+
+  env.broker->CreateStream(env.factory_ptr.get());
+  env.RunUntilIdle();
+  EXPECT_TRUE(stream_request_data.requested);
+
+  EXPECT_CALL(env.deleter, Run(env.broker.release()))
+      .WillOnce(testing::DeleteArg<0>());
+  stream_request_data.observer.reset();
+  env.RunUntilIdle();
+  Mock::VerifyAndClear(&env.deleter);
+
+  env.stream_factory.CloseBinding();
+  env.RunUntilIdle();
+}
+
+TEST(AudioInputStreamBrokerTest,
+     FactoryDisconnectDuringConstruction_CallsDeleter) {
+  TestEnvironment env;
+
+  env.broker->CreateStream(env.factory_ptr.get());
+  env.stream_factory.CloseBinding();
+
+  EXPECT_CALL(env.deleter, Run(env.broker.release()))
+      .WillOnce(testing::DeleteArg<0>());
+
+  env.RunUntilIdle();
+}
+
+}  // namespace content
diff --git a/content/browser/media/audio_output_stream_broker.cc b/content/browser/media/audio_output_stream_broker.cc
index c32c664..72dab68 100644
--- a/content/browser/media/audio_output_stream_broker.cc
+++ b/content/browser/media/audio_output_stream_broker.cc
@@ -113,6 +113,7 @@
 }
 
 void AudioOutputStreamBroker::Cleanup() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   std::move(deleter_).Run(this);
 }
 
diff --git a/content/browser/media/audio_output_stream_broker_unittest.cc b/content/browser/media/audio_output_stream_broker_unittest.cc
index e4d0f26..4b94dca 100644
--- a/content/browser/media/audio_output_stream_broker_unittest.cc
+++ b/content/browser/media/audio_output_stream_broker_unittest.cc
@@ -169,8 +169,7 @@
             TestParams(),
             group,
             deleter.Get(),
-            provider_client.MakePtr())),
-        factory_ptr(stream_factory.MakePtr()) {}
+            provider_client.MakePtr())) {}
 
   void RunUntilIdle() { env.RunUntilIdle(); }
 
diff --git a/content/browser/media/audio_stream_broker.cc b/content/browser/media/audio_stream_broker.cc
index 82dc1908..abe535a 100644
--- a/content/browser/media/audio_stream_broker.cc
+++ b/content/browser/media/audio_stream_broker.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "content/browser/media/audio_input_stream_broker.h"
 #include "content/browser/media/audio_output_stream_broker.h"
 
 namespace content {
@@ -17,6 +18,22 @@
   AudioStreamBrokerFactoryImpl() = default;
   ~AudioStreamBrokerFactoryImpl() final = default;
 
+  std::unique_ptr<AudioStreamBroker> CreateAudioInputStreamBroker(
+      int render_process_id,
+      int render_frame_id,
+      const std::string& device_id,
+      const media::AudioParameters& params,
+      uint32_t shared_memory_count,
+      bool enable_agc,
+      AudioStreamBroker::DeleterCallback deleter,
+      mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client)
+      final {
+    return std::make_unique<AudioInputStreamBroker>(
+        render_process_id, render_frame_id, device_id, params,
+        shared_memory_count, enable_agc, std::move(deleter),
+        std::move(renderer_factory_client));
+  }
+
   std::unique_ptr<AudioStreamBroker> CreateAudioOutputStreamBroker(
       int render_process_id,
       int render_frame_id,
diff --git a/content/browser/media/audio_stream_broker.h b/content/browser/media/audio_stream_broker.h
index 6636907..29e3079 100644
--- a/content/browser/media/audio_stream_broker.h
+++ b/content/browser/media/audio_stream_broker.h
@@ -11,6 +11,8 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "content/common/content_export.h"
+#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
+#include "media/mojo/interfaces/audio_input_stream.mojom.h"
 #include "media/mojo/interfaces/audio_output_stream.mojom.h"
 
 namespace audio {
@@ -60,6 +62,17 @@
   AudioStreamBrokerFactory();
   virtual ~AudioStreamBrokerFactory();
 
+  virtual std::unique_ptr<AudioStreamBroker> CreateAudioInputStreamBroker(
+      int render_process_id,
+      int render_frame_id,
+      const std::string& device_id,
+      const media::AudioParameters& params,
+      uint32_t shared_memory_count,
+      bool enable_agc,
+      AudioStreamBroker::DeleterCallback deleter,
+      mojom::RendererAudioInputStreamFactoryClientPtr
+          renderer_factory_client) = 0;
+
   virtual std::unique_ptr<AudioStreamBroker> CreateAudioOutputStreamBroker(
       int render_process_id,
       int render_frame_id,
diff --git a/content/browser/media/forwarding_audio_stream_factory.cc b/content/browser/media/forwarding_audio_stream_factory.cc
index 64fbc59..e94f4b3 100644
--- a/content/browser/media/forwarding_audio_stream_factory.cc
+++ b/content/browser/media/forwarding_audio_stream_factory.cc
@@ -23,7 +23,8 @@
     std::unique_ptr<AudioStreamBrokerFactory> broker_factory)
     : WebContentsObserver(web_contents),
       connector_(std::move(connector)),
-      broker_factory_(std::move(broker_factory)) {
+      broker_factory_(std::move(broker_factory)),
+      group_id_(base::UnguessableToken::Create()) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(web_contents);
   DCHECK(broker_factory_);
@@ -33,6 +34,28 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 }
 
+void ForwardingAudioStreamFactory::CreateInputStream(
+    RenderFrameHost* frame,
+    const std::string& device_id,
+    const media::AudioParameters& params,
+    uint32_t shared_memory_count,
+    bool enable_agc,
+    mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  const int process_id = frame->GetProcess()->GetID();
+  const int frame_id = frame->GetRoutingID();
+  inputs_
+      .insert(broker_factory_->CreateAudioInputStreamBroker(
+          process_id, frame_id, device_id, params, shared_memory_count,
+          enable_agc,
+          base::BindOnce(&ForwardingAudioStreamFactory::RemoveInput,
+                         base::Unretained(this)),
+          std::move(renderer_factory_client)))
+      .first->get()
+      ->CreateStream(GetFactory());
+}
+
 void ForwardingAudioStreamFactory::CreateOutputStream(
     RenderFrameHost* frame,
     const std::string& device_id,
@@ -70,6 +93,7 @@
 
 void ForwardingAudioStreamFactory::WebContentsDestroyed() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(inputs_.empty());
   DCHECK(outputs_.empty());
 }
 
@@ -85,11 +109,20 @@
            broker->render_frame_id() == frame_id;
   };
 
+  base::EraseIf(inputs_, match_rfh);
   base::EraseIf(outputs_, match_rfh);
 
   ResetRemoteFactoryPtrIfIdle();
 }
 
+void ForwardingAudioStreamFactory::RemoveInput(AudioStreamBroker* broker) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  size_t removed = inputs_.erase(broker);
+  DCHECK_EQ(1u, removed);
+
+  ResetRemoteFactoryPtrIfIdle();
+}
+
 void ForwardingAudioStreamFactory::RemoveOutput(AudioStreamBroker* broker) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   size_t removed = outputs_.erase(broker);
@@ -114,13 +147,14 @@
 
 void ForwardingAudioStreamFactory::ResetRemoteFactoryPtrIfIdle() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (outputs_.empty())
+  if (inputs_.empty() && outputs_.empty())
     remote_factory_.reset();
 }
 
 void ForwardingAudioStreamFactory::ResetRemoteFactoryPtr() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   remote_factory_.reset();
+  inputs_.clear();
   outputs_.clear();
 }
 
diff --git a/content/browser/media/forwarding_audio_stream_factory.h b/content/browser/media/forwarding_audio_stream_factory.h
index 6aef571..fce7320d 100644
--- a/content/browser/media/forwarding_audio_stream_factory.h
+++ b/content/browser/media/forwarding_audio_stream_factory.h
@@ -11,8 +11,10 @@
 #include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/macros.h"
+#include "base/unguessable_token.h"
 #include "content/browser/media/audio_stream_broker.h"
 #include "content/common/content_export.h"
+#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "services/audio/public/mojom/stream_factory.mojom.h"
 
@@ -41,9 +43,19 @@
 
   ~ForwardingAudioStreamFactory() final;
 
-  // TODO(https://crbug.com/803102): Add input streams, loopback, and muting.
+  const base::UnguessableToken& group_id() { return group_id_; }
+
+  // TODO(https://crbug.com/803102): Add loopback and muting streams.
   // TODO(https://crbug.com/787806): Automatically restore streams on audio
   // service restart.
+  void CreateInputStream(
+      RenderFrameHost* frame,
+      const std::string& device_id,
+      const media::AudioParameters& params,
+      uint32_t shared_memory_count,
+      bool enable_agc,
+      mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client);
+
   void CreateOutputStream(
       RenderFrameHost* frame,
       const std::string& device_id,
@@ -62,6 +74,7 @@
 
   void CleanupStreamsBelongingTo(RenderFrameHost* render_frame_host);
 
+  void RemoveInput(AudioStreamBroker* handle);
   void RemoveOutput(AudioStreamBroker* handle);
 
   audio::mojom::StreamFactory* GetFactory();
@@ -71,6 +84,11 @@
   const std::unique_ptr<service_manager::Connector> connector_;
   const std::unique_ptr<AudioStreamBrokerFactory> broker_factory_;
 
+  // Unique id indentifying all streams belonging to the WebContents owning
+  // |this|.
+  // TODO(https://crbug.com/824019): Use this for loopback.
+  const base::UnguessableToken group_id_;
+
   // Lazily acquired. Reset on connection error and when we no longer have any
   // streams. Note: we don't want muting to force the connection to be open,
   // since we want to clean up the service when not in use. If we have active
@@ -84,6 +102,7 @@
   // remove it.
   int stream_id_counter_ = 0;
 
+  StreamBrokerSet inputs_;
   StreamBrokerSet outputs_;
 
   DISALLOW_COPY_AND_ASSIGN(ForwardingAudioStreamFactory);
diff --git a/content/browser/media/forwarding_audio_stream_factory_unittest.cc b/content/browser/media/forwarding_audio_stream_factory_unittest.cc
index 9ce1c4c..2d47c7d 100644
--- a/content/browser/media/forwarding_audio_stream_factory_unittest.cc
+++ b/content/browser/media/forwarding_audio_stream_factory_unittest.cc
@@ -11,8 +11,8 @@
 #include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/test/mock_callback.h"
-#include "base/test/scoped_task_environment.h"
 #include "base/unguessable_token.h"
+#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/test/test_renderer_host.h"
 #include "media/base/audio_parameters.h"
@@ -33,6 +33,8 @@
 
 namespace content {
 
+namespace {
+
 class MockBroker : public AudioStreamBroker {
  public:
   explicit MockBroker(RenderFrameHost* rfh)
@@ -46,8 +48,6 @@
   // Can be used to verify that |this| has been destructed.
   base::WeakPtr<MockBroker> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
 
-  int stream_id;
-  base::UnguessableToken group_id;
   DeleterCallback deleter;
 
  private:
@@ -58,14 +58,40 @@
  public:
   MockBrokerFactory() {}
   ~MockBrokerFactory() final {
+    EXPECT_TRUE(prepared_input_stream_brokers_.empty())
+        << "Input broker creation was expected but didn't happen";
     EXPECT_TRUE(prepared_output_stream_brokers_.empty())
-        << "Broker creation was expected but didn't happen";
+        << "Output broker creation was expected but didn't happen";
+  }
+
+  void ExpectInputStreamBrokerCreation(std::unique_ptr<MockBroker> broker) {
+    prepared_input_stream_brokers_.push(std::move(broker));
   }
 
   void ExpectOutputStreamBrokerCreation(std::unique_ptr<MockBroker> broker) {
     prepared_output_stream_brokers_.push(std::move(broker));
   }
 
+  std::unique_ptr<AudioStreamBroker> CreateAudioInputStreamBroker(
+      int render_process_id,
+      int render_frame_id,
+      const std::string& device_id,
+      const media::AudioParameters& params,
+      uint32_t shared_memory_count,
+      bool enable_agc,
+      AudioStreamBroker::DeleterCallback deleter,
+      mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client)
+      final {
+    std::unique_ptr<MockBroker> prepared_broker =
+        std::move(prepared_input_stream_brokers_.front());
+    prepared_input_stream_brokers_.pop();
+    CHECK_NE(nullptr, prepared_broker.get());
+    EXPECT_EQ(render_process_id, prepared_broker->render_process_id());
+    EXPECT_EQ(render_frame_id, prepared_broker->render_frame_id());
+    prepared_broker->deleter = std::move(deleter);
+    return std::move(prepared_broker);
+  }
+
   std::unique_ptr<AudioStreamBroker> CreateAudioOutputStreamBroker(
       int render_process_id,
       int render_frame_id,
@@ -81,13 +107,12 @@
     CHECK_NE(nullptr, prepared_broker.get());
     EXPECT_EQ(render_process_id, prepared_broker->render_process_id());
     EXPECT_EQ(render_frame_id, prepared_broker->render_frame_id());
-    prepared_broker->stream_id = stream_id;
-    prepared_broker->group_id = group_id;
     prepared_broker->deleter = std::move(deleter);
     return std::move(prepared_broker);
   }
 
  private:
+  base::queue<std::unique_ptr<MockBroker>> prepared_input_stream_brokers_;
   base::queue<std::unique_ptr<MockBroker>> prepared_output_stream_brokers_;
 };
 
@@ -111,13 +136,21 @@
     RenderViewHostTestHarness::SetUp();
     RenderFrameHostTester::For(main_rfh())->InitializeRenderFrameIfNeeded();
     other_rfh_ =
-        RenderFrameHostTester::For(main_rfh())->AppendChild("sderfdgijho");
+        RenderFrameHostTester::For(main_rfh())->AppendChild("other_rfh");
   }
 
   void SetPendingFactoryRequest(mojo::ScopedMessagePipeHandle factory_request) {
     pending_factory_request_ = std::move(factory_request);
   }
 
+  base::WeakPtr<MockBroker> ExpectInputBrokerConstruction(
+      RenderFrameHost* rfh) {
+    auto broker = std::make_unique<StrictMock<MockBroker>>(rfh);
+    auto weak_broker = broker->GetWeakPtr();
+    broker_factory_->ExpectInputStreamBrokerCreation(std::move(broker));
+    return weak_broker;
+  }
+
   base::WeakPtr<MockBroker> ExpectOutputBrokerConstruction(
       RenderFrameHost* rfh) {
     auto broker = std::make_unique<StrictMock<MockBroker>>(rfh);
@@ -128,8 +161,11 @@
 
   RenderFrameHost* other_rfh() { return other_rfh_; }
 
-  static const char kDeviceId[];
+  static const char kInputDeviceId[];
+  static const char kOutputDeviceId[];
   static const media::AudioParameters kParams;
+  static const uint32_t kSharedMemoryCount;
+  static const bool kEnableAgc;
   service_manager::mojom::ConnectorRequest connector_request_;
   std::unique_ptr<service_manager::Connector> connector_;
   mojo::ScopedMessagePipeHandle pending_factory_request_;
@@ -137,9 +173,29 @@
   std::unique_ptr<MockBrokerFactory> broker_factory_;
 };
 
-const char ForwardingAudioStreamFactoryTest::kDeviceId[] = "test device id";
+const char ForwardingAudioStreamFactoryTest::kInputDeviceId[] =
+    "test input device id";
+const char ForwardingAudioStreamFactoryTest::kOutputDeviceId[] =
+    "test output device id";
 const media::AudioParameters ForwardingAudioStreamFactoryTest::kParams =
     media::AudioParameters::UnavailableDeviceParams();
+const uint32_t ForwardingAudioStreamFactoryTest::kSharedMemoryCount = 10;
+const bool ForwardingAudioStreamFactoryTest::kEnableAgc = false;
+
+}  // namespace
+
+TEST_F(ForwardingAudioStreamFactoryTest, CreateInputStream_CreatesInputStream) {
+  mojom::RendererAudioInputStreamFactoryClientPtr client;
+  base::WeakPtr<MockBroker> broker = ExpectInputBrokerConstruction(main_rfh());
+
+  ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
+                                       std::move(broker_factory_));
+
+  EXPECT_CALL(*broker, CreateStream(NotNull()));
+  mojo::MakeRequest(&client);
+  factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
+                            kSharedMemoryCount, kEnableAgc, std::move(client));
+}
 
 TEST_F(ForwardingAudioStreamFactoryTest,
        CreateOutputStream_CreatesOutputStream) {
@@ -151,15 +207,17 @@
 
   EXPECT_CALL(*broker, CreateStream(NotNull()));
   mojo::MakeRequest(&client);
-  factory.CreateOutputStream(main_rfh(), kDeviceId, kParams, std::move(client));
+  factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
+                             std::move(client));
 }
 
-TEST_F(ForwardingAudioStreamFactoryTest, DeleterCalled_DestroysOutputStream) {
-  media::mojom::AudioOutputStreamProviderClientPtr client;
+TEST_F(ForwardingAudioStreamFactoryTest,
+       InputBrokerDeleterCalled_DestroysInputStream) {
+  mojom::RendererAudioInputStreamFactoryClientPtr client;
   base::WeakPtr<MockBroker> main_rfh_broker =
-      ExpectOutputBrokerConstruction(main_rfh());
+      ExpectInputBrokerConstruction(main_rfh());
   base::WeakPtr<MockBroker> other_rfh_broker =
-      ExpectOutputBrokerConstruction(other_rfh());
+      ExpectInputBrokerConstruction(other_rfh());
 
   ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
                                        std::move(broker_factory_));
@@ -167,26 +225,28 @@
   {
     EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(main_rfh(), kDeviceId, kParams,
-                               std::move(client));
+    factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
+                              kSharedMemoryCount, kEnableAgc,
+                              std::move(client));
     testing::Mock::VerifyAndClear(&*main_rfh_broker);
   }
   {
     EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(other_rfh(), kDeviceId, kParams,
-                               std::move(client));
+    factory.CreateInputStream(other_rfh(), kInputDeviceId, kParams,
+                              kSharedMemoryCount, kEnableAgc,
+                              std::move(client));
     testing::Mock::VerifyAndClear(&*other_rfh_broker);
   }
 
   std::move(other_rfh_broker->deleter).Run(&*other_rfh_broker);
   EXPECT_FALSE(other_rfh_broker)
-      << "Broker should be destructed when deleter is called.";
+      << "Input broker should be destructed when deleter is called.";
   EXPECT_TRUE(main_rfh_broker);
 }
 
 TEST_F(ForwardingAudioStreamFactoryTest,
-       DestroyFrame_DestroysRelatedOutputStream) {
+       OutputBrokerDeleterCalled_DestroysOutputStream) {
   media::mojom::AudioOutputStreamProviderClientPtr client;
   base::WeakPtr<MockBroker> main_rfh_broker =
       ExpectOutputBrokerConstruction(main_rfh());
@@ -199,93 +259,210 @@
   {
     EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(main_rfh(), kDeviceId, kParams,
+    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
                                std::move(client));
     testing::Mock::VerifyAndClear(&*main_rfh_broker);
   }
   {
     EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
     mojo::MakeRequest(&client);
-    factory.CreateOutputStream(other_rfh(), kDeviceId, kParams,
+    factory.CreateOutputStream(other_rfh(), kOutputDeviceId, kParams,
                                std::move(client));
     testing::Mock::VerifyAndClear(&*other_rfh_broker);
   }
 
   factory.FrameDeleted(other_rfh());
   EXPECT_FALSE(other_rfh_broker)
-      << "Broker should be destructed when owning frame is destructed.";
+      << "Output broker should be destructed when deleter is called.";
   EXPECT_TRUE(main_rfh_broker);
 }
 
-TEST_F(ForwardingAudioStreamFactoryTest,
-       DestroyWebContents_DestroysOutputStream) {
-  media::mojom::AudioOutputStreamProviderClientPtr client;
-  base::WeakPtr<MockBroker> broker = ExpectOutputBrokerConstruction(main_rfh());
+TEST_F(ForwardingAudioStreamFactoryTest, DestroyFrame_DestroysRelatedStreams) {
+  mojom::RendererAudioInputStreamFactoryClientPtr input_client;
+  base::WeakPtr<MockBroker> main_rfh_input_broker =
+      ExpectInputBrokerConstruction(main_rfh());
+  base::WeakPtr<MockBroker> other_rfh_input_broker =
+      ExpectInputBrokerConstruction(other_rfh());
 
-  ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
-                                       std::move(broker_factory_));
-
-  EXPECT_CALL(*broker, CreateStream(NotNull()));
-  mojo::MakeRequest(&client);
-  factory.CreateOutputStream(main_rfh(), kDeviceId, kParams, std::move(client));
-
-  DeleteContents();
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(broker)
-      << "Broker should be destructed when owning WebContents is destructed.";
-}
-
-TEST_F(ForwardingAudioStreamFactoryTest, DestroyRemoteFactory_CleansUpStreams) {
-  media::mojom::AudioOutputStreamProviderClientPtr client;
-  base::WeakPtr<MockBroker> broker = ExpectOutputBrokerConstruction(main_rfh());
-
-  ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
-                                       std::move(broker_factory_));
-
-  EXPECT_CALL(*broker, CreateStream(NotNull()));
-  mojo::MakeRequest(&client);
-  factory.CreateOutputStream(main_rfh(), kDeviceId, kParams, std::move(client));
-
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(broker);
-  pending_factory_request_.reset();  // Triggers connection error.
-  base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(broker)
-      << "Broker should be destructed when owning WebContents is destructed.";
-}
-
-TEST_F(ForwardingAudioStreamFactoryTest, LastStreamDeleted_ClearsFactoryPtr) {
-  media::mojom::AudioOutputStreamProviderClientPtr client;
-  base::WeakPtr<MockBroker> main_rfh_broker =
+  media::mojom::AudioOutputStreamProviderClientPtr output_client;
+  base::WeakPtr<MockBroker> main_rfh_output_broker =
       ExpectOutputBrokerConstruction(main_rfh());
-  base::WeakPtr<MockBroker> other_rfh_broker =
+  base::WeakPtr<MockBroker> other_rfh_output_broker =
       ExpectOutputBrokerConstruction(other_rfh());
 
   ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
                                        std::move(broker_factory_));
 
   {
-    EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
-    mojo::MakeRequest(&client);
-    factory.CreateOutputStream(main_rfh(), kDeviceId, kParams,
-                               std::move(client));
-    testing::Mock::VerifyAndClear(&*main_rfh_broker);
+    EXPECT_CALL(*main_rfh_input_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&input_client);
+    factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
+                              kSharedMemoryCount, kEnableAgc,
+                              std::move(input_client));
+    testing::Mock::VerifyAndClear(&*main_rfh_input_broker);
   }
   {
-    EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
-    mojo::MakeRequest(&client);
-    factory.CreateOutputStream(other_rfh(), kDeviceId, kParams,
-                               std::move(client));
-    testing::Mock::VerifyAndClear(&*other_rfh_broker);
+    EXPECT_CALL(*other_rfh_input_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&input_client);
+    factory.CreateInputStream(other_rfh(), kInputDeviceId, kParams,
+                              kSharedMemoryCount, kEnableAgc,
+                              std::move(input_client));
+    testing::Mock::VerifyAndClear(&*other_rfh_input_broker);
+  }
+
+  {
+    EXPECT_CALL(*main_rfh_output_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&output_client);
+    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
+                               std::move(output_client));
+    testing::Mock::VerifyAndClear(&*main_rfh_output_broker);
+  }
+  {
+    EXPECT_CALL(*other_rfh_output_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&output_client);
+    factory.CreateOutputStream(other_rfh(), kOutputDeviceId, kParams,
+                               std::move(output_client));
+    testing::Mock::VerifyAndClear(&*other_rfh_output_broker);
+  }
+
+  factory.FrameDeleted(other_rfh());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(other_rfh_input_broker)
+      << "Input broker should be destructed when owning frame is destructed.";
+  EXPECT_TRUE(main_rfh_input_broker);
+  EXPECT_FALSE(other_rfh_output_broker)
+      << "Output broker should be destructed when owning frame is destructed.";
+  EXPECT_TRUE(main_rfh_output_broker);
+}
+
+TEST_F(ForwardingAudioStreamFactoryTest, DestroyWebContents_DestroysStreams) {
+  mojom::RendererAudioInputStreamFactoryClientPtr input_client;
+  base::WeakPtr<MockBroker> input_broker =
+      ExpectInputBrokerConstruction(main_rfh());
+
+  media::mojom::AudioOutputStreamProviderClientPtr output_client;
+  base::WeakPtr<MockBroker> output_broker =
+      ExpectOutputBrokerConstruction(main_rfh());
+
+  ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
+                                       std::move(broker_factory_));
+
+  EXPECT_CALL(*input_broker, CreateStream(NotNull()));
+  mojo::MakeRequest(&input_client);
+  factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
+                            kSharedMemoryCount, kEnableAgc,
+                            std::move(input_client));
+
+  EXPECT_CALL(*output_broker, CreateStream(NotNull()));
+  mojo::MakeRequest(&output_client);
+  factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
+                             std::move(output_client));
+
+  DeleteContents();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(input_broker) << "Input broker should be destructed when owning "
+                                "WebContents is destructed.";
+  EXPECT_FALSE(output_broker)
+      << "Output broker should be destructed when owning "
+         "WebContents is destructed.";
+}
+
+TEST_F(ForwardingAudioStreamFactoryTest, DestroyRemoteFactory_CleansUpStreams) {
+  mojom::RendererAudioInputStreamFactoryClientPtr input_client;
+  base::WeakPtr<MockBroker> input_broker =
+      ExpectInputBrokerConstruction(main_rfh());
+  media::mojom::AudioOutputStreamProviderClientPtr output_client;
+  base::WeakPtr<MockBroker> output_broker =
+      ExpectOutputBrokerConstruction(main_rfh());
+
+  ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
+                                       std::move(broker_factory_));
+
+  EXPECT_CALL(*input_broker, CreateStream(NotNull()));
+  mojo::MakeRequest(&input_client);
+  factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
+                            kSharedMemoryCount, kEnableAgc,
+                            std::move(input_client));
+
+  EXPECT_CALL(*output_broker, CreateStream(NotNull()));
+  mojo::MakeRequest(&output_client);
+  factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
+                             std::move(output_client));
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(input_broker);
+  EXPECT_TRUE(output_broker);
+  pending_factory_request_.reset();  // Triggers connection error.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(input_broker) << "Input broker should be destructed when owning "
+                                "WebContents is destructed.";
+  EXPECT_FALSE(output_broker) << "Output broker should be destructed when "
+                                 "owning WebContents is destructed.";
+}
+
+TEST_F(ForwardingAudioStreamFactoryTest, LastStreamDeleted_ClearsFactoryPtr) {
+  mojom::RendererAudioInputStreamFactoryClientPtr input_client;
+  base::WeakPtr<MockBroker> main_rfh_input_broker =
+      ExpectInputBrokerConstruction(main_rfh());
+  base::WeakPtr<MockBroker> other_rfh_input_broker =
+      ExpectInputBrokerConstruction(other_rfh());
+
+  media::mojom::AudioOutputStreamProviderClientPtr output_client;
+  base::WeakPtr<MockBroker> main_rfh_output_broker =
+      ExpectOutputBrokerConstruction(main_rfh());
+  base::WeakPtr<MockBroker> other_rfh_output_broker =
+      ExpectOutputBrokerConstruction(other_rfh());
+
+  ForwardingAudioStreamFactory factory(web_contents(), std::move(connector_),
+                                       std::move(broker_factory_));
+
+  {
+    EXPECT_CALL(*main_rfh_input_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&input_client);
+    factory.CreateInputStream(main_rfh(), kInputDeviceId, kParams,
+                              kSharedMemoryCount, kEnableAgc,
+                              std::move(input_client));
+    testing::Mock::VerifyAndClear(&*main_rfh_input_broker);
+  }
+  {
+    EXPECT_CALL(*other_rfh_input_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&input_client);
+    factory.CreateInputStream(other_rfh(), kInputDeviceId, kParams,
+                              kSharedMemoryCount, kEnableAgc,
+                              std::move(input_client));
+    testing::Mock::VerifyAndClear(&*other_rfh_input_broker);
+  }
+
+  {
+    EXPECT_CALL(*main_rfh_output_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&output_client);
+    factory.CreateOutputStream(main_rfh(), kOutputDeviceId, kParams,
+                               std::move(output_client));
+    testing::Mock::VerifyAndClear(&*main_rfh_output_broker);
+  }
+  {
+    EXPECT_CALL(*other_rfh_output_broker, CreateStream(NotNull()));
+    mojo::MakeRequest(&output_client);
+    factory.CreateOutputStream(other_rfh(), kOutputDeviceId, kParams,
+                               std::move(output_client));
+    testing::Mock::VerifyAndClear(&*other_rfh_output_broker);
   }
 
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(pending_factory_request_->QuerySignalsState().peer_closed());
-  std::move(other_rfh_broker->deleter).Run(&*other_rfh_broker);
+  std::move(other_rfh_input_broker->deleter).Run(&*other_rfh_input_broker);
+  base::RunLoop().RunUntilIdle();
+  // Connection should still be open, since there are still streams left.
+  EXPECT_FALSE(pending_factory_request_->QuerySignalsState().peer_closed());
+  std::move(main_rfh_input_broker->deleter).Run(&*main_rfh_input_broker);
+  base::RunLoop().RunUntilIdle();
+  // Connection should still be open, since there are still streams left.
+  EXPECT_FALSE(pending_factory_request_->QuerySignalsState().peer_closed());
+  std::move(other_rfh_output_broker->deleter).Run(&*other_rfh_output_broker);
   base::RunLoop().RunUntilIdle();
   // Connection should still be open, since there's still a stream left.
   EXPECT_FALSE(pending_factory_request_->QuerySignalsState().peer_closed());
-  std::move(main_rfh_broker->deleter).Run(&*main_rfh_broker);
+  std::move(main_rfh_output_broker->deleter).Run(&*main_rfh_output_broker);
   base::RunLoop().RunUntilIdle();
   // Now there are no streams left, connection should be broken.
   EXPECT_TRUE(pending_factory_request_->QuerySignalsState().peer_closed());
diff --git a/content/browser/media/keyboard_mic_registration.cc b/content/browser/media/keyboard_mic_registration.cc
new file mode 100644
index 0000000..785f6d3
--- /dev/null
+++ b/content/browser/media/keyboard_mic_registration.cc
@@ -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.
+
+#include "content/browser/media/keyboard_mic_registration.h"
+
+#include "chromeos/audio/cras_audio_handler.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace content {
+
+KeyboardMicRegistration::KeyboardMicRegistration() = default;
+
+KeyboardMicRegistration::~KeyboardMicRegistration() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK_EQ(0, register_count_);
+}
+
+void KeyboardMicRegistration::Register() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (++register_count_ == 1)
+    chromeos::CrasAudioHandler::Get()->SetKeyboardMicActive(true);
+}
+
+void KeyboardMicRegistration::Deregister() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (--register_count_ == 0)
+    chromeos::CrasAudioHandler::Get()->SetKeyboardMicActive(false);
+}
+
+}  // namespace content
diff --git a/content/browser/media/keyboard_mic_registration.h b/content/browser/media/keyboard_mic_registration.h
new file mode 100644
index 0000000..267d0de
--- /dev/null
+++ b/content/browser/media/keyboard_mic_registration.h
@@ -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.
+
+#ifndef CONTENT_BROWSER_MEDIA_KEYBOARD_MIC_REGISTRATION_H_
+#define CONTENT_BROWSER_MEDIA_KEYBOARD_MIC_REGISTRATION_H_
+
+#include "base/macros.h"
+
+namespace content {
+
+// Chrome OS keyboard mic stream registration. Used on UI thread only and owned
+// by BrowserMainLoop; instance must be obtained through
+// BrowserMainLoop::keyboard_mic_registration().
+class KeyboardMicRegistration {
+ public:
+  KeyboardMicRegistration();
+  ~KeyboardMicRegistration();
+
+  void Register();
+  void Deregister();
+
+ private:
+  int register_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(KeyboardMicRegistration);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_MEDIA_KEYBOARD_MIC_REGISTRATION_H_
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index eca40978..9655197 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -458,7 +458,14 @@
 // Make sure the factory info returned from
 // |StoragePartition::GetURLLoaderFactoryForBrowserProcessIOThread()| can be
 // used after crashes.
-IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest, BrowserIOFactoryInfo) {
+// Flaky on Windows. https://crbug.com/840127
+#if defined(OS_WIN)
+#define MAYBE_BrowserIOFactoryInfo DISABLED_BrowserIOFactoryInfo
+#else
+#define MAYBE_BrowserIOFactoryInfo BrowserIOFactoryInfo
+#endif
+IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest,
+                       MAYBE_BrowserIOFactoryInfo) {
   auto* partition =
       BrowserContext::GetDefaultStoragePartition(browser_context());
   auto shared_url_loader_factory_info =
diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_mac.h b/content/browser/renderer_host/input/synthetic_gesture_target_mac.h
index 4ecf50f..1074c50 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_target_mac.h
+++ b/content/browser/renderer_host/input/synthetic_gesture_target_mac.h
@@ -20,6 +20,9 @@
 
   // SyntheticGestureTarget:
   void DispatchInputEventToPlatform(const blink::WebInputEvent& event) override;
+  void DispatchWebTouchEventToPlatform(
+      const blink::WebTouchEvent& event,
+      const ui::LatencyInfo& latency_info) override;
 
  private:
   RenderWidgetHostViewCocoa* cocoa_view_;
diff --git a/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm b/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm
index 71b3c59..3ea4f8c 100644
--- a/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm
+++ b/content/browser/renderer_host/input/synthetic_gesture_target_mac.mm
@@ -131,4 +131,10 @@
   SyntheticGestureTargetBase::DispatchInputEventToPlatform(event);
 }
 
+void SyntheticGestureTargetMac::DispatchWebTouchEventToPlatform(
+    const blink::WebTouchEvent& web_touch,
+    const ui::LatencyInfo& latency_info) {
+  render_widget_host()->GetView()->InjectTouchEvent(web_touch, latency_info);
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
index 898bf03..934e7ba 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -9,14 +9,13 @@
 #include "build/build_config.h"
 #include "content/browser/renderer_host/media/in_process_launched_video_capture_device.h"
 #include "content/browser/renderer_host/media/video_capture_controller.h"
-#include "content/browser/renderer_host/media/video_capture_dependencies.h"
+#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/media_stream_request.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/capture/video/video_capture_buffer_pool_impl.h"
 #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
 #include "media/capture/video/video_capture_device_client.h"
-#include "media/capture/video/video_capture_jpeg_decoder_impl.h"
 #include "media/capture/video/video_frame_receiver.h"
 #include "media/capture/video/video_frame_receiver_on_task_runner.h"
 
@@ -41,11 +40,7 @@
 std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
     media::VideoCaptureJpegDecoder::DecodeDoneCB decode_done_cb,
     base::Callback<void(const std::string&)> send_log_message_cb) {
-  return std::make_unique<media::VideoCaptureJpegDecoderImpl>(
-      base::BindRepeating(
-          &content::VideoCaptureDependencies::CreateJpegDecodeAccelerator),
-      content::BrowserThread::GetTaskRunnerForThread(
-          content::BrowserThread::IO),
+  return std::make_unique<content::VideoCaptureGpuJpegDecoder>(
       std::move(decode_done_cb), std::move(send_log_message_cb));
 }
 
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index cfc8f6b..366ad60 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -35,7 +35,6 @@
 #include "content/browser/renderer_host/media/media_devices_manager.h"
 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
 #include "content/browser/renderer_host/media/service_video_capture_provider.h"
-#include "content/browser/renderer_host/media/video_capture_dependencies.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
 #include "content/browser/renderer_host/media/video_capture_provider_switcher.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
@@ -96,6 +95,42 @@
   return label;
 }
 
+void CreateJpegDecodeAcceleratorOnIOThread(
+    media::mojom::JpegDecodeAcceleratorRequest request) {
+  auto* host =
+      GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, false);
+  if (host) {
+    host->gpu_service()->CreateJpegDecodeAccelerator(std::move(request));
+  } else {
+    LOG(ERROR) << "No GpuProcessHost";
+  }
+}
+
+void CreateJpegDecodeAccelerator(
+    media::mojom::JpegDecodeAcceleratorRequest request) {
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                          base::BindOnce(&CreateJpegDecodeAcceleratorOnIOThread,
+                                         std::move(request)));
+}
+
+void CreateJpegEncodeAcceleratorOnIOThread(
+    media::mojom::JpegEncodeAcceleratorRequest request) {
+  auto* host =
+      GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, false);
+  if (host) {
+    host->gpu_service()->CreateJpegEncodeAccelerator(std::move(request));
+  } else {
+    LOG(ERROR) << "No GpuProcessHost";
+  }
+}
+
+void CreateJpegEncodeAccelerator(
+    media::mojom::JpegEncodeAcceleratorRequest request) {
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                          base::BindOnce(&CreateJpegEncodeAcceleratorOnIOThread,
+                                         std::move(request)));
+}
+
 void ParseStreamType(const StreamControls& controls,
                      MediaStreamType* audio_type,
                      MediaStreamType* video_type) {
@@ -473,10 +508,8 @@
               media::VideoCaptureDeviceFactory::CreateFactory(
                   BrowserThread::GetTaskRunnerForThread(BrowserThread::UI),
                   BrowserGpuMemoryBufferManager::current(),
-                  base::BindRepeating(
-                      &VideoCaptureDependencies::CreateJpegDecodeAccelerator),
-                  base::BindRepeating(
-                      &VideoCaptureDependencies::CreateJpegEncodeAccelerator))),
+                  base::BindRepeating(&CreateJpegDecodeAccelerator),
+                  base::BindRepeating(&CreateJpegEncodeAccelerator))),
           std::move(device_task_runner),
           base::BindRepeating(&SendVideoCaptureLogMessage));
     }
diff --git a/content/browser/renderer_host/media/service_video_capture_provider.cc b/content/browser/renderer_host/media/service_video_capture_provider.cc
index dd765959..1211057d 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider.cc
@@ -4,15 +4,11 @@
 
 #include "content/browser/renderer_host/media/service_video_capture_provider.h"
 
-#include "content/browser/gpu/gpu_client_impl.h"
 #include "content/browser/renderer_host/media/service_video_capture_device_launcher.h"
-#include "content/browser/renderer_host/media/video_capture_dependencies.h"
 #include "content/browser/renderer_host/media/video_capture_factory_delegate.h"
-#include "content/common/child_process_host_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/service_manager_connection.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/video_capture/public/mojom/constants.mojom.h"
 #include "services/video_capture/public/uma/video_capture_service_event.h"
@@ -23,7 +19,7 @@
     : public content::ServiceVideoCaptureProvider::ServiceConnector {
  public:
   ServiceConnectorImpl() {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     // In unit test environments, there may not be any connector.
     auto* connection = content::ServiceManagerConnection::GetForProcess();
     if (!connection)
@@ -48,66 +44,34 @@
   std::unique_ptr<service_manager::Connector> connector_;
 };
 
-class DelegateToBrowserGpuServiceAcceleratorFactory
-    : public video_capture::mojom::AcceleratorFactory {
- public:
-  void CreateJpegDecodeAccelerator(
-      media::mojom::JpegDecodeAcceleratorRequest jda_request) override {
-    content::VideoCaptureDependencies::CreateJpegDecodeAccelerator(
-        std::move(jda_request));
-  }
-  void CreateJpegEncodeAccelerator(
-      media::mojom::JpegEncodeAcceleratorRequest jea_request) override {
-    content::VideoCaptureDependencies::CreateJpegEncodeAccelerator(
-        std::move(jea_request));
-  }
-};
-
-std::unique_ptr<ui::mojom::GpuMemoryBufferFactory> CreateGpuClient() {
-  const auto gpu_client_id =
-      content::ChildProcessHostImpl::GenerateChildProcessUniqueId();
-  return std::make_unique<content::GpuClientImpl>(gpu_client_id);
-}
-
-std::unique_ptr<video_capture::mojom::AcceleratorFactory>
-CreateAcceleratorFactory() {
-  return std::make_unique<DelegateToBrowserGpuServiceAcceleratorFactory>();
-}
-
 }  // anonymous namespace
 
 namespace content {
 
 ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
     base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
-    : ServiceVideoCaptureProvider(
-          std::make_unique<ServiceConnectorImpl>(),
-          base::BindRepeating(&CreateGpuClient),
-          base::BindRepeating(&CreateAcceleratorFactory),
-          std::move(emit_log_message_cb)) {}
+    : ServiceVideoCaptureProvider(std::make_unique<ServiceConnectorImpl>(),
+                                  std::move(emit_log_message_cb)) {}
 
 ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
     std::unique_ptr<ServiceConnector> service_connector,
-    CreateMemoryBufferFactoryCallback create_memory_buffer_factory_cb,
-    CreateAcceleratorFactoryCallback create_accelerator_factory_cb,
     base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
     : service_connector_(std::move(service_connector)),
-      create_memory_buffer_factory_cb_(
-          std::move(create_memory_buffer_factory_cb)),
-      create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)),
       emit_log_message_cb_(std::move(emit_log_message_cb)),
       usage_count_(0),
       launcher_has_connected_to_device_factory_(false),
-      weak_ptr_factory_(this) {}
+      weak_ptr_factory_(this) {
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
 
 ServiceVideoCaptureProvider::~ServiceVideoCaptureProvider() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   UninitializeInternal(ReasonForUninitialize::kShutdown);
 }
 
 void ServiceVideoCaptureProvider::GetDeviceInfosAsync(
     GetDeviceInfosCallback result_callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   emit_log_message_cb_.Run("ServiceVideoCaptureProvider::GetDeviceInfosAsync");
   IncreaseUsageCount();
   LazyConnectToService();
@@ -122,7 +86,7 @@
 
 std::unique_ptr<VideoCaptureDeviceLauncher>
 ServiceVideoCaptureProvider::CreateDeviceLauncher() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return std::make_unique<ServiceVideoCaptureDeviceLauncher>(
       base::BindRepeating(&ServiceVideoCaptureProvider::ConnectToDeviceFactory,
                           weak_ptr_factory_.GetWeakPtr()));
@@ -130,7 +94,7 @@
 
 void ServiceVideoCaptureProvider::ConnectToDeviceFactory(
     std::unique_ptr<VideoCaptureFactoryDelegate>* out_factory) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   IncreaseUsageCount();
   LazyConnectToService();
   launcher_has_connected_to_device_factory_ = true;
@@ -141,7 +105,6 @@
 }
 
 void ServiceVideoCaptureProvider::LazyConnectToService() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
   if (device_factory_provider_.is_bound())
     return;
 
@@ -160,15 +123,7 @@
   launcher_has_connected_to_device_factory_ = false;
   time_of_last_connect_ = base::TimeTicks::Now();
 
-  video_capture::mojom::AcceleratorFactoryPtr accelerator_factory;
-  ui::mojom::GpuMemoryBufferFactoryPtr memory_buffer_factory;
-  mojo::MakeStrongBinding(create_accelerator_factory_cb_.Run(),
-                          mojo::MakeRequest(&accelerator_factory));
-  mojo::MakeStrongBinding(create_memory_buffer_factory_cb_.Run(),
-                          mojo::MakeRequest(&memory_buffer_factory));
   service_connector_->BindFactoryProvider(&device_factory_provider_);
-  device_factory_provider_->InjectGpuDependencies(
-      std::move(memory_buffer_factory), std::move(accelerator_factory));
   device_factory_provider_->ConnectToDeviceFactory(
       mojo::MakeRequest(&device_factory_));
   // Unretained |this| is safe, because |this| owns |device_factory_|.
@@ -180,13 +135,13 @@
 void ServiceVideoCaptureProvider::OnDeviceInfosReceived(
     GetDeviceInfosCallback result_callback,
     const std::vector<media::VideoCaptureDeviceInfo>& infos) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   base::ResetAndReturn(&result_callback).Run(infos);
   DecreaseUsageCount();
 }
 
 void ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   emit_log_message_cb_.Run(
       "ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory");
   // This may indicate that the video capture service has crashed. Uninitialize
@@ -196,12 +151,12 @@
 }
 
 void ServiceVideoCaptureProvider::IncreaseUsageCount() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   usage_count_++;
 }
 
 void ServiceVideoCaptureProvider::DecreaseUsageCount() {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   usage_count_--;
   DCHECK_GE(usage_count_, 0);
   if (usage_count_ == 0)
@@ -210,7 +165,7 @@
 
 void ServiceVideoCaptureProvider::UninitializeInternal(
     ReasonForUninitialize reason) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!device_factory_.is_bound()) {
     return;
   }
diff --git a/content/browser/renderer_host/media/service_video_capture_provider.h b/content/browser/renderer_host/media/service_video_capture_provider.h
index f2e67f4..2ee46aae 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider.h
+++ b/content/browser/renderer_host/media/service_video_capture_provider.h
@@ -27,23 +27,14 @@
         video_capture::mojom::DeviceFactoryProviderPtr* provider) = 0;
   };
 
-  using CreateMemoryBufferFactoryCallback = base::RepeatingCallback<
-      std::unique_ptr<ui::mojom::GpuMemoryBufferFactory>()>;
-  using CreateAcceleratorFactoryCallback = base::RepeatingCallback<
-      std::unique_ptr<video_capture::mojom::AcceleratorFactory>()>;
-
-  // This constructor creates a default ServiceConnector which
+  // The parameterless constructor creates a default ServiceConnector which
   // uses the ServiceManager associated with the current process to connect
-  // to the video capture service. It uses a default factory for instances of
-  // ui::mojom::Gpu which produces instances of class content::GpuClient.
+  // to the video capture service.
   explicit ServiceVideoCaptureProvider(
       base::RepeatingCallback<void(const std::string&)> emit_log_message_cb);
-  // Lets clients provide a custom ServiceConnector and factory method for
-  // creating instances of ui::mojom::Gpu.
+  // Lets clients provide a custom ServiceConnector.
   ServiceVideoCaptureProvider(
       std::unique_ptr<ServiceConnector> service_connector,
-      CreateMemoryBufferFactoryCallback create_memory_buffer_factory_cb,
-      CreateAcceleratorFactoryCallback create_accelerator_factory_cb,
       base::RepeatingCallback<void(const std::string&)> emit_log_message_cb);
   ~ServiceVideoCaptureProvider() override;
 
@@ -65,8 +56,6 @@
   void UninitializeInternal(ReasonForUninitialize reason);
 
   std::unique_ptr<ServiceConnector> service_connector_;
-  CreateMemoryBufferFactoryCallback create_memory_buffer_factory_cb_;
-  CreateAcceleratorFactoryCallback create_accelerator_factory_cb_;
   base::RepeatingCallback<void(const std::string&)> emit_log_message_cb_;
   // We must hold on to |device_factory_provider_| because it holds the
   // service-side binding for |device_factory_|.
@@ -74,6 +63,7 @@
   video_capture::mojom::DeviceFactoryPtr device_factory_;
   // Used for automatically uninitializing when no longer in use.
   int usage_count_;
+  SEQUENCE_CHECKER(sequence_checker_);
 
   bool launcher_has_connected_to_device_factory_;
   base::TimeTicks time_of_last_connect_;
diff --git a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
index 39bfe73..6a683e5 100644
--- a/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
+++ b/content/browser/renderer_host/media/service_video_capture_provider_unittest.cc
@@ -6,9 +6,9 @@
 
 #include "base/run_loop.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
 #include "content/public/browser/video_capture_device_launcher.h"
-#include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "services/video_capture/public/mojom/device_factory.mojom.h"
 #include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
@@ -44,17 +44,6 @@
     DoConnectToDeviceFactory(request);
   }
 
-  void InjectGpuDependencies(
-      ui::mojom::GpuMemoryBufferFactoryPtr memory_buffer_factory,
-      video_capture::mojom::AcceleratorFactoryPtr accelerator_factory)
-      override {
-    DoInjectGpuDependencies(memory_buffer_factory, accelerator_factory);
-  }
-
-  MOCK_METHOD2(
-      DoInjectGpuDependencies,
-      void(ui::mojom::GpuMemoryBufferFactoryPtr& memory_buffer_factory,
-           video_capture::mojom::AcceleratorFactoryPtr& accelerator_factory));
   MOCK_METHOD1(SetShutdownDelayInSeconds, void(float seconds));
   MOCK_METHOD1(DoConnectToDeviceFactory,
                void(video_capture::mojom::DeviceFactoryRequest& request));
@@ -121,13 +110,7 @@
     auto mock_service_connector = std::make_unique<MockServiceConnector>();
     mock_service_connector_ = mock_service_connector.get();
     provider_ = std::make_unique<ServiceVideoCaptureProvider>(
-        std::move(mock_service_connector), base::BindRepeating([]() {
-          return std::unique_ptr<ui::mojom::GpuMemoryBufferFactory>();
-        }),
-        base::BindRepeating([]() {
-          return std::unique_ptr<video_capture::mojom::AcceleratorFactory>();
-        }),
-        kIgnoreLogMessageCB);
+        std::move(mock_service_connector), kIgnoreLogMessageCB);
 
     ON_CALL(*mock_service_connector_, BindFactoryProvider(_))
         .WillByDefault(
@@ -150,7 +133,7 @@
 
   void TearDown() override {}
 
-  content::TestBrowserThreadBundle test_browser_thread_bundle_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   MockServiceConnector* mock_service_connector_;
   MockDeviceFactoryProvider mock_device_factory_provider_;
   mojo::Binding<video_capture::mojom::DeviceFactoryProvider>
diff --git a/content/browser/renderer_host/media/video_capture_browsertest.cc b/content/browser/renderer_host/media/video_capture_browsertest.cc
index 128d9064..59b89dc8 100644
--- a/content/browser/renderer_host/media/video_capture_browsertest.cc
+++ b/content/browser/renderer_host/media/video_capture_browsertest.cc
@@ -115,8 +115,6 @@
     params_ = TestParams(GetParam());
     if (params_.use_mojo_service) {
       scoped_feature_list_.InitAndEnableFeature(features::kMojoVideoCapture);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(features::kMojoVideoCapture);
     }
   }
 
@@ -233,6 +231,11 @@
   if (params_.use_mojo_service)
     return;
 #endif
+  // Mojo video capture currently does not support accelerated jpeg decoding.
+  // TODO(chfremer): Remove this as soon as https://crbug.com/720604 is
+  // resolved.
+  if (params_.use_mojo_service && params_.exercise_accelerated_jpeg_decoding)
+    return;
 
   SetUpRequiringBrowserMainLoopOnMainThread();
   base::RunLoop run_loop;
@@ -250,8 +253,16 @@
   run_loop.Run();
 }
 
+// Flaky on MSAN. https://crbug.com/840294
+#if defined(MEMORY_SANITIZER)
+#define MAYBE_ReceiveFramesFromFakeCaptureDevice \
+  DISABLED_ReceiveFramesFromFakeCaptureDevice
+#else
+#define MAYBE_ReceiveFramesFromFakeCaptureDevice \
+  ReceiveFramesFromFakeCaptureDevice
+#endif
 IN_PROC_BROWSER_TEST_P(VideoCaptureBrowserTest,
-                       ReceiveFramesFromFakeCaptureDevice) {
+                       MAYBE_ReceiveFramesFromFakeCaptureDevice) {
 #if defined(OS_ANDROID)
   // TODO(chfremer): This test case is flaky on Android. Find out cause of
   // flakiness and then re-enable. See https://crbug.com/709039.
@@ -263,11 +274,21 @@
   if (params_.use_mojo_service)
     return;
 #endif
+  // Mojo video capture currently does not support accelerated jpeg decoding.
+  // TODO(chfremer): Remove this as soon as https://crbug.com/720604 is
+  // resolved.
+  if (params_.use_mojo_service && params_.exercise_accelerated_jpeg_decoding)
+    return;
   // Only fake device with index 2 delivers MJPEG.
   if (params_.exercise_accelerated_jpeg_decoding &&
-      params_.device_index_to_use != 2) {
+      params_.device_index_to_use != 2)
     return;
-  }
+  // There is an intermittent use-after-free in GpuChannelHost::Send() during
+  // Browser shutdown, which causes MSan tests to fail.
+  // TODO(chfremer): Remove this as soon as https://crbug.com/725271 is
+  // resolved.
+  if (params_.exercise_accelerated_jpeg_decoding)
+    return;
 
   SetUpRequiringBrowserMainLoopOnMainThread();
 
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 75e032b..cb0e911 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -22,6 +22,7 @@
 #include "content/browser/renderer_host/media/media_stream_provider.h"
 #include "content/browser/renderer_host/media/mock_video_capture_provider.h"
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
+#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
 #include "content/browser/renderer_host/media/video_capture_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -30,7 +31,6 @@
 #include "media/capture/video/video_capture_buffer_pool_impl.h"
 #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
 #include "media/capture/video/video_capture_device_client.h"
-#include "media/capture/video/video_capture_jpeg_decoder_impl.h"
 #include "media/capture/video/video_frame_receiver_on_task_runner.h"
 #include "media/capture/video_capture_types.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -44,6 +44,12 @@
 
 namespace content {
 
+std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
+    media::VideoCaptureJpegDecoder::DecodeDoneCB decode_done_cb) {
+  return std::make_unique<content::VideoCaptureGpuJpegDecoder>(
+      std::move(decode_done_cb), base::DoNothing());
+}
+
 class MockVideoCaptureControllerEventHandler
     : public VideoCaptureControllerEventHandler {
  public:
@@ -157,7 +163,10 @@
         std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
             controller_->GetWeakPtrForIOThread(),
             BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)),
-        buffer_pool_, media::VideoCaptureJpegDecoderFactoryCB()));
+        buffer_pool_,
+        base::Bind(&CreateGpuJpegDecoder,
+                   base::Bind(&media::VideoFrameReceiver::OnFrameReadyInBuffer,
+                              controller_->GetWeakPtrForIOThread()))));
   }
 
   void SendStubFrameToDeviceClient(const media::VideoCaptureFormat format) {
diff --git a/content/browser/renderer_host/media/video_capture_dependencies.cc b/content/browser/renderer_host/media/video_capture_dependencies.cc
deleted file mode 100644
index 26567c65..0000000
--- a/content/browser/renderer_host/media/video_capture_dependencies.cc
+++ /dev/null
@@ -1,53 +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 "content/browser/renderer_host/media/video_capture_dependencies.h"
-
-#include "content/browser/gpu/gpu_process_host.h"
-#include "content/public/browser/browser_thread.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-
-namespace content {
-
-// static
-void VideoCaptureDependencies::CreateJpegDecodeAccelerator(
-    media::mojom::JpegDecodeAcceleratorRequest accelerator) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::BindOnce(&VideoCaptureDependencies::CreateJpegDecodeAccelerator,
-                       std::move(accelerator)));
-    return;
-  }
-
-  auto* host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
-                                   true /*force_create*/);
-  if (host) {
-    host->gpu_service()->CreateJpegDecodeAccelerator(std::move(accelerator));
-  } else {
-    LOG(ERROR) << "No GpuProcessHost";
-  }
-}
-
-// static
-void VideoCaptureDependencies::CreateJpegEncodeAccelerator(
-    media::mojom::JpegEncodeAcceleratorRequest accelerator) {
-  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
-    BrowserThread::PostTask(
-        BrowserThread::IO, FROM_HERE,
-        base::BindOnce(&VideoCaptureDependencies::CreateJpegEncodeAccelerator,
-                       std::move(accelerator)));
-    return;
-  }
-
-  auto* host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,
-                                   true /*force_create*/);
-  if (host) {
-    host->gpu_service()->CreateJpegEncodeAccelerator(std::move(accelerator));
-  } else {
-    LOG(ERROR) << "No GpuProcessHost";
-  }
-}
-
-}  // namespace content
diff --git a/content/browser/renderer_host/media/video_capture_dependencies.h b/content/browser/renderer_host/media/video_capture_dependencies.h
deleted file mode 100644
index 5804eef..0000000
--- a/content/browser/renderer_host/media/video_capture_dependencies.h
+++ /dev/null
@@ -1,25 +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 CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_DEPENDENCIES_H_
-#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_DEPENDENCIES_H_
-
-#include "content/common/content_export.h"
-#include "services/video_capture/public/mojom/device_factory.mojom.h"
-#include "services/viz/privileged/interfaces/gl/gpu_service.mojom.h"
-
-namespace content {
-
-// Browser-provided GPU dependencies for video capture.
-class CONTENT_EXPORT VideoCaptureDependencies {
- public:
-  static void CreateJpegDecodeAccelerator(
-      media::mojom::JpegDecodeAcceleratorRequest accelerator);
-  static void CreateJpegEncodeAccelerator(
-      media::mojom::JpegEncodeAcceleratorRequest accelerator);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_DEPENDENCIES_H_
diff --git a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
new file mode 100644
index 0000000..f9769023
--- /dev/null
+++ b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc
@@ -0,0 +1,336 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
+#include "content/browser/browser_main_loop.h"
+#include "content/browser/gpu/gpu_process_host.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/content_switches.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/media_switches.h"
+#include "media/base/video_frame.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "services/ui/public/cpp/gpu/gpu.h"
+
+namespace content {
+
+VideoCaptureGpuJpegDecoder::VideoCaptureGpuJpegDecoder(
+    DecodeDoneCB decode_done_cb,
+    base::Callback<void(const std::string&)> send_log_message_cb)
+    : decode_done_cb_(std::move(decode_done_cb)),
+      send_log_message_cb_(std::move(send_log_message_cb)),
+      has_received_decoded_frame_(false),
+      next_bitstream_buffer_id_(0),
+      in_buffer_id_(media::JpegDecodeAccelerator::kInvalidBitstreamBufferId),
+      decoder_status_(INIT_PENDING),
+      weak_ptr_factory_(this) {}
+
+VideoCaptureGpuJpegDecoder::~VideoCaptureGpuJpegDecoder() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // |this| was set as |decoder_|'s client. |decoder_| has to be deleted before
+  // this destructor returns to ensure that it doesn't call back into its
+  // client. Hence, we wait here while we delete |decoder_| on the IO thread.
+  if (decoder_) {
+    base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                              base::WaitableEvent::InitialState::NOT_SIGNALED);
+    // base::Unretained is safe because |this| will be valid until |event|
+    // is signaled.
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::BindOnce(&VideoCaptureGpuJpegDecoder::DestroyDecoderOnIOThread,
+                       base::Unretained(this), &event));
+    event.Wait();
+  }
+}
+
+void VideoCaptureGpuJpegDecoder::Initialize() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::AutoLock lock(lock_);
+  bool is_platform_supported =
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kUseFakeJpegDecodeAccelerator);
+#if defined(OS_CHROMEOS)
+  // Non-ChromeOS platforms do not support HW JPEG decode now. Do not establish
+  // gpu channel to avoid introducing overhead.
+  is_platform_supported = true;
+#endif
+
+  if (!is_platform_supported ||
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableAcceleratedMjpegDecode)) {
+    decoder_status_ = FAILED;
+    RecordInitDecodeUMA_Locked();
+    return;
+  }
+
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                          base::BindOnce(&RequestGPUInfoOnIOThread,
+                                         base::ThreadTaskRunnerHandle::Get(),
+                                         weak_ptr_factory_.GetWeakPtr()));
+}
+
+VideoCaptureGpuJpegDecoder::STATUS VideoCaptureGpuJpegDecoder::GetStatus()
+    const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::AutoLock lock(lock_);
+  return decoder_status_;
+}
+
+void VideoCaptureGpuJpegDecoder::DecodeCapturedData(
+    const uint8_t* data,
+    size_t in_buffer_size,
+    const media::VideoCaptureFormat& frame_format,
+    base::TimeTicks reference_time,
+    base::TimeDelta timestamp,
+    media::VideoCaptureDevice::Client::Buffer out_buffer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(decoder_);
+
+  TRACE_EVENT_ASYNC_BEGIN0("jpeg", "VideoCaptureGpuJpegDecoder decoding",
+                           next_bitstream_buffer_id_);
+  TRACE_EVENT0("jpeg", "VideoCaptureGpuJpegDecoder::DecodeCapturedData");
+
+  // TODO(kcwu): enqueue decode requests in case decoding is not fast enough
+  // (say, if decoding time is longer than 16ms for 60fps 4k image)
+  {
+    base::AutoLock lock(lock_);
+    if (IsDecoding_Locked()) {
+      DVLOG(1) << "Drop captured frame. Previous jpeg frame is still decoding";
+      return;
+    }
+  }
+
+  // Enlarge input buffer if necessary.
+  if (!in_shared_memory_.get() ||
+      in_buffer_size > in_shared_memory_->mapped_size()) {
+    // Reserve 2x space to avoid frequent reallocations for initial frames.
+    const size_t reserved_size = 2 * in_buffer_size;
+    in_shared_memory_.reset(new base::SharedMemory);
+    if (!in_shared_memory_->CreateAndMapAnonymous(reserved_size)) {
+      base::AutoLock lock(lock_);
+      decoder_status_ = FAILED;
+      LOG(WARNING) << "CreateAndMapAnonymous failed, size=" << reserved_size;
+      return;
+    }
+  }
+  memcpy(in_shared_memory_->memory(), data, in_buffer_size);
+
+  // No need to lock for |in_buffer_id_| since IsDecoding_Locked() is false.
+  in_buffer_id_ = next_bitstream_buffer_id_;
+  media::BitstreamBuffer in_buffer(in_buffer_id_, in_shared_memory_->handle(),
+                                   in_buffer_size);
+  // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
+  next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
+
+  // The API of |decoder_| requires us to wrap the |out_buffer| in a VideoFrame.
+  const gfx::Size dimensions = frame_format.frame_size;
+  std::unique_ptr<media::VideoCaptureBufferHandle> out_buffer_access =
+      out_buffer.handle_provider->GetHandleForInProcessAccess();
+  base::SharedMemoryHandle out_handle =
+      out_buffer.handle_provider->GetNonOwnedSharedMemoryHandleForLegacyIPC();
+  scoped_refptr<media::VideoFrame> out_frame =
+      media::VideoFrame::WrapExternalSharedMemory(
+          media::PIXEL_FORMAT_I420,          // format
+          dimensions,                        // coded_size
+          gfx::Rect(dimensions),             // visible_rect
+          dimensions,                        // natural_size
+          out_buffer_access->data(),         // data
+          out_buffer_access->mapped_size(),  // data_size
+          out_handle,                        // handle
+          0,                                 // shared_memory_offset
+          timestamp);                        // timestamp
+  if (!out_frame) {
+    base::AutoLock lock(lock_);
+    decoder_status_ = FAILED;
+    LOG(ERROR) << "DecodeCapturedData: WrapExternalSharedMemory failed";
+    return;
+  }
+  // Hold onto the buffer access handle for the lifetime of the VideoFrame, to
+  // ensure the data pointers remain valid.
+  out_frame->AddDestructionObserver(base::BindOnce(
+      [](std::unique_ptr<media::VideoCaptureBufferHandle> handle) {},
+      std::move(out_buffer_access)));
+  out_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
+                                   frame_format.frame_rate);
+
+  out_frame->metadata()->SetTimeTicks(media::VideoFrameMetadata::REFERENCE_TIME,
+                                      reference_time);
+
+  media::mojom::VideoFrameInfoPtr out_frame_info =
+      media::mojom::VideoFrameInfo::New();
+  out_frame_info->timestamp = timestamp;
+  out_frame_info->pixel_format = media::PIXEL_FORMAT_I420;
+  out_frame_info->coded_size = dimensions;
+  out_frame_info->visible_rect = gfx::Rect(dimensions);
+  out_frame_info->metadata = out_frame->metadata()->GetInternalValues().Clone();
+
+  {
+    base::AutoLock lock(lock_);
+    decode_done_closure_ =
+        base::Bind(decode_done_cb_, out_buffer.id, out_buffer.frame_feedback_id,
+                   base::Passed(&out_buffer.access_permission),
+                   base::Passed(&out_frame_info));
+  }
+
+  // base::Unretained is safe because |decoder_| is deleted on the IO thread.
+  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+                          base::BindOnce(&media::JpegDecodeAccelerator::Decode,
+                                         base::Unretained(decoder_.get()),
+                                         in_buffer, std::move(out_frame)));
+}
+
+void VideoCaptureGpuJpegDecoder::VideoFrameReady(int32_t bitstream_buffer_id) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  TRACE_EVENT0("jpeg", "VideoCaptureGpuJpegDecoder::VideoFrameReady");
+  if (!has_received_decoded_frame_) {
+    send_log_message_cb_.Run("Received decoded frame from Gpu Jpeg decoder");
+    has_received_decoded_frame_ = true;
+  }
+  base::AutoLock lock(lock_);
+
+  if (!IsDecoding_Locked()) {
+    LOG(ERROR) << "Got decode response while not decoding";
+    return;
+  }
+
+  if (bitstream_buffer_id != in_buffer_id_) {
+    LOG(ERROR) << "Unexpected bitstream_buffer_id " << bitstream_buffer_id
+               << ", expected " << in_buffer_id_;
+    return;
+  }
+  in_buffer_id_ = media::JpegDecodeAccelerator::kInvalidBitstreamBufferId;
+
+  decode_done_closure_.Run();
+  decode_done_closure_.Reset();
+
+  TRACE_EVENT_ASYNC_END0("jpeg", "VideoCaptureGpuJpegDecoder decoding",
+                         bitstream_buffer_id);
+}
+
+void VideoCaptureGpuJpegDecoder::NotifyError(
+    int32_t bitstream_buffer_id,
+    media::JpegDecodeAccelerator::Error error) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  LOG(ERROR) << "Decode error, bitstream_buffer_id=" << bitstream_buffer_id
+             << ", error=" << error;
+  send_log_message_cb_.Run("Gpu Jpeg decoder failed");
+  base::AutoLock lock(lock_);
+  decode_done_closure_.Reset();
+  decoder_status_ = FAILED;
+}
+
+// static
+void VideoCaptureGpuJpegDecoder::RequestGPUInfoOnIOThread(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    base::WeakPtr<VideoCaptureGpuJpegDecoder> weak_this) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  GpuProcessHost* host =
+      GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, false);
+  if (host) {
+    host->RequestGPUInfo(
+        base::Bind(&VideoCaptureGpuJpegDecoder::DidReceiveGPUInfoOnIOThread,
+                   task_runner, weak_this));
+  } else {
+    DidReceiveGPUInfoOnIOThread(std::move(task_runner), std::move(weak_this),
+                                gpu::GPUInfo());
+  }
+}
+
+// static
+void VideoCaptureGpuJpegDecoder::DidReceiveGPUInfoOnIOThread(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    base::WeakPtr<VideoCaptureGpuJpegDecoder> weak_this,
+    const gpu::GPUInfo& gpu_info) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  media::mojom::JpegDecodeAcceleratorPtr remote_decoder;
+
+  if (gpu_info.jpeg_decode_accelerator_supported) {
+    GpuProcessHost* host =
+        GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, false);
+    if (host) {
+      host->gpu_service()->CreateJpegDecodeAccelerator(
+          mojo::MakeRequest(&remote_decoder));
+    }
+  }
+
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(&VideoCaptureGpuJpegDecoder::FinishInitialization,
+                     weak_this, remote_decoder.PassInterface()));
+}
+
+void VideoCaptureGpuJpegDecoder::FinishInitialization(
+    media::mojom::JpegDecodeAcceleratorPtrInfo unbound_remote_decoder) {
+  TRACE_EVENT0("gpu", "VideoCaptureGpuJpegDecoder::FinishInitialization");
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (unbound_remote_decoder.is_valid()) {
+    base::AutoLock lock(lock_);
+    decoder_ = std::make_unique<media::MojoJpegDecodeAccelerator>(
+        BrowserThread::GetTaskRunnerForThread(BrowserThread::IO),
+        std::move(unbound_remote_decoder));
+
+    // base::Unretained is safe because |decoder_| is deleted on the IO thread.
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::BindOnce(&media::JpegDecodeAccelerator::InitializeAsync,
+                       base::Unretained(decoder_.get()), this,
+                       media::BindToCurrentLoop(base::Bind(
+                           &VideoCaptureGpuJpegDecoder::OnInitializationDone,
+                           weak_ptr_factory_.GetWeakPtr()))));
+  } else {
+    OnInitializationDone(false);
+  }
+}
+
+void VideoCaptureGpuJpegDecoder::OnInitializationDone(bool success) {
+  TRACE_EVENT0("gpu", "VideoCaptureGpuJpegDecoder::OnInitializationDone");
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  base::AutoLock lock(lock_);
+  if (!success) {
+    BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, decoder_.release());
+    DLOG(ERROR) << "Failed to initialize JPEG decoder";
+  }
+
+  decoder_status_ = success ? INIT_PASSED : FAILED;
+  RecordInitDecodeUMA_Locked();
+}
+
+bool VideoCaptureGpuJpegDecoder::IsDecoding_Locked() const {
+  lock_.AssertAcquired();
+  return !decode_done_closure_.is_null();
+}
+
+void VideoCaptureGpuJpegDecoder::RecordInitDecodeUMA_Locked() {
+  UMA_HISTOGRAM_BOOLEAN("Media.VideoCaptureGpuJpegDecoder.InitDecodeSuccess",
+                        decoder_status_ == INIT_PASSED);
+}
+
+void VideoCaptureGpuJpegDecoder::DestroyDecoderOnIOThread(
+    base::WaitableEvent* event) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  decoder_.reset();
+  event->Signal();
+}
+
+}  // namespace content
diff --git a/media/capture/video/video_capture_jpeg_decoder_impl.h b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h
similarity index 60%
rename from media/capture/video/video_capture_jpeg_decoder_impl.h
rename to content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h
index b3010fb..bf089ba 100644
--- a/media/capture/video/video_capture_jpeg_decoder_impl.h
+++ b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.h
@@ -1,9 +1,9 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
+// 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 MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_JPEG_DECODER_IMPL_H_
-#define MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_JPEG_DECODER_IMPL_H_
+#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_GPU_JPEG_DECODER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_GPU_JPEG_DECODER_H_
 
 #include <stddef.h>
 #include <stdint.h>
@@ -15,9 +15,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "content/common/content_export.h"
 #include "gpu/config/gpu_info.h"
-#include "media/capture/capture_export.h"
-#include "media/capture/video/video_capture_device_factory.h"
 #include "media/capture/video/video_capture_jpeg_decoder.h"
 #include "media/mojo/clients/mojo_jpeg_decode_accelerator.h"
 
@@ -25,28 +24,26 @@
 class WaitableEvent;
 }
 
-namespace media {
+namespace content {
 
-// Implementation of media::VideoCaptureJpegDecoder that delegates to a
-// media::mojom::JpegDecodeAccelerator. When a frame is received in
-// DecodeCapturedData(), it is copied to |in_shared_memory| for IPC transport
-// to |decoder_|. When the decoder is finished with the frame, |decode_done_cb_|
-// is invoked. Until |decode_done_cb_| is invoked, subsequent calls to
-// DecodeCapturedData() are ignored.
-// The given |decoder_task_runner| must allow blocking on |lock_|.
-class CAPTURE_EXPORT VideoCaptureJpegDecoderImpl
+// Adapter to GpuJpegDecodeAccelerator for VideoCaptureDevice::Client. It takes
+// care of GpuJpegDecodeAccelerator creation, shared memory, and threading
+// issues.
+//
+// All public methods except JpegDecodeAccelerator::Client ones should be called
+// on the same thread. JpegDecodeAccelerator::Client methods should be called on
+// the IO thread.
+class CONTENT_EXPORT VideoCaptureGpuJpegDecoder
     : public media::VideoCaptureJpegDecoder,
       public media::JpegDecodeAccelerator::Client {
  public:
   // |decode_done_cb| is called on the IO thread when decode succeeds. This can
   // be on any thread. |decode_done_cb| is never called after
   // VideoCaptureGpuJpegDecoder is destroyed.
-  VideoCaptureJpegDecoderImpl(
-      MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory,
-      scoped_refptr<base::SingleThreadTaskRunner> decoder_task_runner,
+  VideoCaptureGpuJpegDecoder(
       DecodeDoneCB decode_done_cb,
-      base::RepeatingCallback<void(const std::string&)> send_log_message_cb);
-  ~VideoCaptureJpegDecoderImpl() override;
+      base::Callback<void(const std::string&)> send_log_message_cb);
+  ~VideoCaptureGpuJpegDecoder() override;
 
   // Implementation of VideoCaptureJpegDecoder:
   void Initialize() override;
@@ -66,7 +63,17 @@
                    media::JpegDecodeAccelerator::Error error) override;
 
  private:
-  void FinishInitialization();
+  static void RequestGPUInfoOnIOThread(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      base::WeakPtr<VideoCaptureGpuJpegDecoder> weak_this);
+
+  static void DidReceiveGPUInfoOnIOThread(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+      base::WeakPtr<VideoCaptureGpuJpegDecoder> weak_this,
+      const gpu::GPUInfo& gpu_info);
+
+  void FinishInitialization(
+      media::mojom::JpegDecodeAcceleratorPtrInfo unbound_remote_decoder);
   void OnInitializationDone(bool success);
 
   // Returns true if the decoding of last frame is not finished yet.
@@ -77,23 +84,20 @@
 
   void DestroyDecoderOnIOThread(base::WaitableEvent* event);
 
-  MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_;
-  scoped_refptr<base::SingleThreadTaskRunner> decoder_task_runner_;
-
   // The underlying JPEG decode accelerator.
   std::unique_ptr<media::JpegDecodeAccelerator> decoder_;
 
   // The callback to run when decode succeeds.
   const DecodeDoneCB decode_done_cb_;
 
-  const base::RepeatingCallback<void(const std::string&)> send_log_message_cb_;
+  const base::Callback<void(const std::string&)> send_log_message_cb_;
   bool has_received_decoded_frame_;
 
   // Guards |decode_done_closure_| and |decoder_status_|.
   mutable base::Lock lock_;
 
   // The closure of |decode_done_cb_| with bound parameters.
-  base::OnceClosure decode_done_closure_;
+  base::Closure decode_done_closure_;
 
   // Next id for input BitstreamBuffer.
   int32_t next_bitstream_buffer_id_;
@@ -109,11 +113,11 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  base::WeakPtrFactory<VideoCaptureJpegDecoderImpl> weak_ptr_factory_;
+  base::WeakPtrFactory<VideoCaptureGpuJpegDecoder> weak_ptr_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(VideoCaptureJpegDecoderImpl);
+  DISALLOW_COPY_AND_ASSIGN(VideoCaptureGpuJpegDecoder);
 };
 
-}  // namespace media
+}  // namespace content
 
-#endif  // MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_JPEG_DECODER_IMPL_H_
+#endif  // CONTENT_BROWSER_RENDERER_HOST_MEDIA_VIDEO_CAPTURE_GPU_JPEG_DECODER_H_
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
index c0aec90..025702c 100644
--- a/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
+++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.cc
@@ -10,8 +10,10 @@
 namespace content {
 
 RenderFrameMetadataProviderImpl::RenderFrameMetadataProviderImpl(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     FrameTokenMessageQueue* frame_token_message_queue)
-    : frame_token_message_queue_(frame_token_message_queue),
+    : task_runner_(task_runner),
+      frame_token_message_queue_(frame_token_message_queue),
       render_frame_metadata_observer_client_binding_(this),
       weak_factory_(this) {}
 
@@ -30,8 +32,8 @@
     mojom::RenderFrameMetadataObserverPtr observer) {
   render_frame_metadata_observer_ptr_ = std::move(observer);
   render_frame_metadata_observer_client_binding_.Close();
-  render_frame_metadata_observer_client_binding_.Bind(
-      std::move(client_request));
+  render_frame_metadata_observer_client_binding_.Bind(std::move(client_request),
+                                                      task_runner_);
 }
 
 void RenderFrameMetadataProviderImpl::ReportAllFrameSubmissionsForTesting(
diff --git a/content/browser/renderer_host/render_frame_metadata_provider_impl.h b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
index fb101fb..ff93d90 100644
--- a/content/browser/renderer_host/render_frame_metadata_provider_impl.h
+++ b/content/browser/renderer_host/render_frame_metadata_provider_impl.h
@@ -27,7 +27,8 @@
     : public RenderFrameMetadataProvider,
       public mojom::RenderFrameMetadataObserverClient {
  public:
-  explicit RenderFrameMetadataProviderImpl(
+  RenderFrameMetadataProviderImpl(
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
       FrameTokenMessageQueue* frame_token_message_queue);
   ~RenderFrameMetadataProviderImpl() override;
 
@@ -69,6 +70,8 @@
 
   base::Optional<viz::LocalSurfaceId> last_local_surface_id_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
   // Not owned.
   FrameTokenMessageQueue* const frame_token_message_queue_;
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index ddda2189..8a5c3e1 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1976,8 +1976,8 @@
   if (gpu_client_) {
     // |gpu_client_| outlives the registry, because its destruction is posted to
     // IO thread from the destructor of |this|.
-    registry->AddInterface(base::BindRepeating(
-        &GpuClientImpl::Add, base::Unretained(gpu_client_.get())));
+    registry->AddInterface(
+        base::Bind(&GpuClientImpl::Add, base::Unretained(gpu_client_.get())));
   }
 
   registry->AddInterface(
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 35e004dc..2e65c33 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -369,7 +369,13 @@
       compositor_frame_sink_binding_(this),
       frame_token_message_queue_(
           std::make_unique<FrameTokenMessageQueue>(this)),
-      render_frame_metadata_provider_(frame_token_message_queue_.get()),
+      render_frame_metadata_provider_(
+#if defined(OS_MACOSX)
+          ui::WindowResizeHelperMac::Get()->task_runner(),
+#else
+          base::ThreadTaskRunnerHandle::Get(),
+#endif
+          frame_token_message_queue_.get()),
       frame_sink_id_(base::checked_cast<uint32_t>(process_->GetID()),
                      base::checked_cast<uint32_t>(routing_id_)),
       weak_factory_(this) {
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index aeef5d5..d4d8e7350 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -1218,11 +1218,21 @@
   base::debug::SetCrashKeyString(
       target_ptr_key,
       base::StringPrintf("%p", touchscreen_gesture_target_.target));
+  static auto* root_ptr_key = base::debug::AllocateCrashKeyString(
+      "touchscreen-gesture-root-ptr", base::debug::CrashKeySize::Size64);
+  base::debug::SetCrashKeyString(root_ptr_key,
+                                 base::StringPrintf("%p", root_view));
   static auto* target_ptr_in_map_key = base::debug::AllocateCrashKeyString(
       "touchscreen-gesture-target-in-map", base::debug::CrashKeySize::Size32);
   base::debug::SetCrashKeyString(
       target_ptr_in_map_key,
       touchscreen_gesture_target_in_map_ ? "true" : "false");
+  static auto* map_size_key = base::debug::AllocateCrashKeyString(
+      "touchscreen-gesture-map-size", base::debug::CrashKeySize::Size32);
+  base::debug::SetCrashKeyString(
+      map_size_key,
+      base::StringPrintf("%u", static_cast<int>(owner_map_.size())));
+
   touchscreen_gesture_target_.target->ProcessGestureEvent(event, latency);
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h
index cd7fba6d..288401cd 100644
--- a/content/browser/renderer_host/render_widget_host_view_base.h
+++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -327,6 +327,9 @@
       gfx::PointF* transformed_point,
       bool* out_query_renderer);
 
+  virtual void InjectTouchEvent(const blink::WebTouchEvent& event,
+                                const ui::LatencyInfo& latency) {}
+
   virtual void PreProcessMouseEvent(const blink::WebMouseEvent& event) {}
   virtual void PreProcessTouchEvent(const blink::WebTouchEvent& event) {}
 
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h
index ac758e5..3671fef 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.h
+++ b/content/browser/renderer_host/render_widget_host_view_mac.h
@@ -25,6 +25,7 @@
 #include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
 #include "ui/accelerated_widget_mac/display_link_mac.h"
 #include "ui/base/cocoa/remote_layer_api.h"
+#include "ui/events/gesture_detection/filtered_gesture_provider.h"
 
 namespace content {
 class CursorManager;
@@ -67,6 +68,7 @@
       public RenderWidgetHostNSViewClient,
       public BrowserCompositorMacClient,
       public TextInputManager::Observer,
+      public ui::GestureProviderClient,
       public ui::AcceleratedWidgetMacNSView,
       public IPC::Sender {
  public:
@@ -120,6 +122,7 @@
   void UpdateCursor(const WebCursor& cursor) override;
   void DisplayCursor(const WebCursor& cursor) override;
   CursorManager* GetCursorManager() override;
+  void OnDidNavigateMainFrameToNewPage() override;
   void SetIsLoading(bool is_loading) override;
   void RenderProcessGone(base::TerminationStatus status,
                          int error_code) override;
@@ -166,6 +169,8 @@
   bool IsKeyboardLocked() override;
   void GestureEventAck(const blink::WebGestureEvent& event,
                        InputEventAckState ack_result) override;
+  void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch,
+                              InputEventAckState ack_result) override;
 
   void DidOverscroll(const ui::DidOverscrollParams& params) override;
 
@@ -184,6 +189,11 @@
   // consumer, such as PDF or maps, wants to intercept them and implement a
   // custom behavior.
   void SendGesturePinchEvent(blink::WebGestureEvent* event);
+
+  // Inject synthetic touch events.
+  void InjectTouchEvent(const blink::WebTouchEvent& event,
+                        const ui::LatencyInfo& latency_info) override;
+
   bool TransformPointToLocalCoordSpace(const gfx::PointF& point,
                                        const viz::SurfaceId& original_surface,
                                        gfx::PointF* transformed_point) override;
@@ -209,15 +219,15 @@
   void OnTextSelectionChanged(TextInputManager* text_input_manager,
                               RenderWidgetHostViewBase* updated_view) override;
 
+  // ui::GestureProviderClient implementation.
+  void OnGestureEvent(const ui::GestureEventData& gesture) override;
+
   // RenderFrameMetadataProvider::Observer
   void OnRenderFrameMetadataChanged() override;
 
   // IPC::Sender implementation.
   bool Send(IPC::Message* message) override;
 
-  // Forwards the mouse event to the renderer.
-  void ForwardMouseEvent(const blink::WebMouseEvent& event);
-
   void SetTextInputActive(bool active);
 
   // Returns true and stores first rectangle for character range if the
@@ -515,6 +525,10 @@
   // Used to track active password input sessions.
   std::unique_ptr<ui::ScopedPasswordInputEnabler> password_input_enabler_;
 
+  // Provides gesture synthesis given a stream of touch events and touch event
+  // acks. This is for generating gesture events from injected touch events.
+  ui::FilteredGestureProvider gesture_provider_;
+
   // Used to ensure that a consistent RenderWidgetHost is targeted throughout
   // the duration of a keyboard event.
   bool in_keyboard_event_ = false;
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index cac0ea2c..9936425d 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -19,6 +19,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "content/browser/accessibility/browser_accessibility_manager_mac.h"
 #include "content/browser/renderer_host/cursor_manager.h"
+#include "content/browser/renderer_host/input/motion_event_web.h"
 #import "content/browser/renderer_host/input/synthetic_gesture_target_mac.h"
 #include "content/browser/renderer_host/input/web_input_event_builders_mac.h"
 #include "content/browser/renderer_host/render_view_host_delegate.h"
@@ -28,6 +29,7 @@
 #import "content/browser/renderer_host/render_widget_host_ns_view_bridge.h"
 #import "content/browser/renderer_host/render_widget_host_view_cocoa.h"
 #import "content/browser/renderer_host/text_input_client_mac.h"
+#import "content/browser/renderer_host/ui_events_helper.h"
 #include "content/common/text_input_state.h"
 #include "content/common/view_messages.h"
 #include "content/public/browser/browser_context.h"
@@ -45,6 +47,7 @@
 #include "ui/base/cocoa/text_services_context_menu.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/mac/coordinate_conversion.h"
@@ -53,6 +56,7 @@
 using blink::WebInputEvent;
 using blink::WebMouseEvent;
 using blink::WebGestureEvent;
+using blink::WebTouchEvent;
 
 namespace content {
 
@@ -132,6 +136,9 @@
       is_loading_(false),
       allow_pause_for_resize_or_repaint_(true),
       is_guest_view_hack_(is_guest_view_hack),
+      gesture_provider_(ui::GetGestureProviderConfig(
+                            ui::GestureProviderConfigType::CURRENT_PLATFORM),
+                        this),
       weak_factory_(this) {
   // The NSView is on the other side of |ns_view_bridge_|.
   ns_view_bridge_ = RenderWidgetHostNSViewBridge::Create(this);
@@ -415,6 +422,10 @@
   return cursor_manager_.get();
 }
 
+void RenderWidgetHostViewMac::OnDidNavigateMainFrameToNewPage() {
+  gesture_provider_.ResetDetection();
+}
+
 void RenderWidgetHostViewMac::SetIsLoading(bool is_loading) {
   is_loading_ = is_loading;
   // If we ever decide to show the waiting cursor while the page is loading
@@ -521,6 +532,22 @@
                                     selection->range());
 }
 
+void RenderWidgetHostViewMac::OnGestureEvent(
+    const ui::GestureEventData& gesture) {
+  blink::WebGestureEvent web_gesture =
+      ui::CreateWebGestureEventFromGestureEventData(gesture);
+
+  ui::LatencyInfo latency_info(ui::SourceEventType::TOUCH);
+
+  if (ShouldRouteEvent(web_gesture)) {
+    blink::WebGestureEvent gesture_event(web_gesture);
+    host()->delegate()->GetInputEventRouter()->RouteGestureEvent(
+        this, &gesture_event, latency_info);
+  } else {
+    host()->ForwardGestureEventWithLatencyInfo(web_gesture, latency_info);
+  }
+}
+
 void RenderWidgetHostViewMac::OnRenderFrameMetadataChanged() {
   last_frame_root_background_color_ = host()
                                           ->render_frame_metadata_provider()
@@ -1015,6 +1042,23 @@
   mouse_wheel_phase_handler_.GestureEventAck(event, ack_result);
 }
 
+void RenderWidgetHostViewMac::ProcessAckedTouchEvent(
+    const TouchEventWithLatencyInfo& touch,
+    InputEventAckState ack_result) {
+  const bool event_consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
+  gesture_provider_.OnTouchEventAck(
+      touch.event.unique_touch_event_id, event_consumed,
+      InputEventAckStateIsSetNonBlocking(ack_result));
+  if (touch.event.touch_start_or_first_touch_move && event_consumed &&
+      host()->delegate() && host()->delegate()->GetInputEventRouter()) {
+    host()
+        ->delegate()
+        ->GetInputEventRouter()
+        ->OnHandledTouchStartOrFirstTouchMove(
+            touch.event.unique_touch_event_id);
+  }
+}
+
 void RenderWidgetHostViewMac::DidOverscroll(
     const ui::DidOverscrollParams& params) {
   [cocoa_view() processedOverscroll:params];
@@ -1041,9 +1085,8 @@
   // See also RenderWidgetHostViewAura::ShouldRouteEvent.
   // TODO(wjmaclean): Update this function if RenderWidgetHostViewMac implements
   // OnTouchEvent(), to match what we are doing in RenderWidgetHostViewAura.
-  DCHECK(WebInputEvent::IsMouseEventType(event.GetType()) ||
-         event.GetType() == WebInputEvent::kMouseWheel ||
-         WebInputEvent::IsPinchGestureEventType(event.GetType()));
+  // The only touch events and touch gesture events expected here are
+  // injected synthetic events.
   return host()->delegate() && host()->delegate()->GetInputEventRouter();
 }
 
@@ -1059,6 +1102,23 @@
   host()->ForwardGestureEvent(*event);
 }
 
+void RenderWidgetHostViewMac::InjectTouchEvent(
+    const WebTouchEvent& event,
+    const ui::LatencyInfo& latency_info) {
+  ui::FilteredGestureProvider::TouchHandlingResult result =
+      gesture_provider_.OnTouchEvent(MotionEventWeb(event));
+  if (!result.succeeded)
+    return;
+
+  if (ShouldRouteEvent(event)) {
+    WebTouchEvent touch_event(event);
+    host()->delegate()->GetInputEventRouter()->RouteTouchEvent(
+        this, &touch_event, latency_info);
+  } else {
+    host()->ForwardTouchEventWithLatencyInfo(event, latency_info);
+  }
+}
+
 bool RenderWidgetHostViewMac::TransformPointToLocalCoordSpace(
     const gfx::PointF& point,
     const viz::SurfaceId& original_surface,
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 3bf2e9e..9f13f95e 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -103,8 +103,7 @@
   wc->GetFrameTree()->root()->navigator()->RequestOpenURL(
       wc->GetFrameTree()->root()->current_frame_host(), extension_url, false,
       nullptr, std::string(), Referrer(), WindowOpenDisposition::CURRENT_TAB,
-      false, true, blink::WebTriggeringEventInfo::kFromTrustedEvent,
-      base::nullopt);
+      false, true, blink::WebTriggeringEventInfo::kFromTrustedEvent);
 
   // Since the navigation above requires a cross-process swap, there will be a
   // speculative/pending RenderFrameHost. Ensure it exists and is in a different
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 6cced7e1..39aeeb3 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -417,13 +417,16 @@
       network::mojom::URLLoaderFactoryPtrInfo non_network_loader_factory_info) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
-    // We allocated a process but will not use it. Tell the process manager to
-    // release the process, by making a WorkerProcessHandle and immediately
-    // destroying it.
-    if (status == SERVICE_WORKER_OK && !instance_->context_) {
-      WorkerProcessHandle(process_manager, instance_->embedded_worker_id(),
-                          process_info->process_id);
-      status = SERVICE_WORKER_ERROR_ABORT;
+    std::unique_ptr<WorkerProcessHandle> process_handle;
+    if (status == SERVICE_WORKER_OK) {
+      // If we allocated a process, WorkerProcessHandle has to be created before
+      // returning to ensure the process is eventually released.
+      process_handle = std::make_unique<WorkerProcessHandle>(
+          process_manager, instance_->embedded_worker_id(),
+          process_info->process_id);
+
+      if (!instance_->context_)
+        status = SERVICE_WORKER_ERROR_ABORT;
     }
 
     if (status != SERVICE_WORKER_OK) {
@@ -452,11 +455,7 @@
 
     // Notify the instance that a process is allocated.
     state_ = ProcessAllocationState::ALLOCATED;
-    instance_->OnProcessAllocated(
-        std::make_unique<WorkerProcessHandle>(process_manager,
-                                              instance_->embedded_worker_id(),
-                                              process_info->process_id),
-        start_situation);
+    instance_->OnProcessAllocated(std::move(process_handle), start_situation);
 
     // Notify the instance that it is registered to the DevTools manager.
     instance_->OnRegisteredToDevToolsManager(std::move(devtools_proxy),
@@ -464,14 +463,9 @@
 
     network::mojom::URLLoaderFactoryPtr non_network_loader_factory(
         std::move(non_network_loader_factory_info));
-    status = instance_->SendStartWorker(std::move(params),
-                                        std::move(non_network_loader_factory));
-    if (status != SERVICE_WORKER_OK) {
-      StatusCallback callback = std::move(start_callback_);
-      start_callback_.Reset();
-      instance_->OnStartFailed(std::move(callback), status);
-      // |this| may be destroyed.
-    }
+    instance_->SendStartWorker(std::move(params),
+                               std::move(non_network_loader_factory));
+
     TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ServiceWorker",
                                       "INITIALIZING_ON_RENDERER", this);
     // |this|'s work is done here, but |instance_| still uses its state until
@@ -642,23 +636,10 @@
     observer.OnRegisteredToDevToolsManager();
 }
 
-ServiceWorkerStatusCode EmbeddedWorkerInstance::SendStartWorker(
+void EmbeddedWorkerInstance::SendStartWorker(
     mojom::EmbeddedWorkerStartParamsPtr params,
     network::mojom::URLLoaderFactoryPtr non_network_loader_factory) {
   DCHECK(context_);
-
-  if (!context_->GetDispatcherHost(process_id())) {
-    // Check if there's a dispatcher host, which is a good sign the process is
-    // still alive. It's possible that previously the process crashed, and the
-    // Mojo connection error via |client_| detected it and this instance was
-    // detached, but on restart ServiceWorkerProcessManager assigned us the
-    // process again before RenderProcessHostImpl itself or
-    // ServiceWorkerProcessManager knew it crashed, and by the time we get here
-    // RenderProcessHostImpl::EnableSendQueue may have been called in
-    // anticipation of the RPHI being reused for another renderer process, so
-    // Mojo doesn't consider it an error. See https://crbug.com/732729.
-    return SERVICE_WORKER_ERROR_IPC_FAILED;
-  }
   DCHECK(params->dispatcher_request.is_pending());
   DCHECK(params->controller_request.is_pending());
   DCHECK(params->service_worker_host.is_valid());
@@ -677,12 +658,7 @@
           .Run(process_id(), std::move(non_network_loader_factory));
   client_->StartWorker(std::move(params));
   registry_->BindWorkerToProcess(process_id(), embedded_worker_id());
-  OnStartWorkerMessageSent(is_script_streaming);
-  return SERVICE_WORKER_OK;
-}
 
-void EmbeddedWorkerInstance::OnStartWorkerMessageSent(
-    bool is_script_streaming) {
   if (!step_time_.is_null()) {
     base::TimeDelta duration = UpdateStepTime();
     if (inflight_start_task_->is_installed()) {
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index bc0d96b..d0556eb 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -244,13 +244,10 @@
   // |non_network_loader_factory| is non-null when the service worker script URL
   // has a non-http(s) scheme. In that case, it is used to load the script since
   // the usual network factory can't be used.
-  ServiceWorkerStatusCode SendStartWorker(
+  void SendStartWorker(
       mojom::EmbeddedWorkerStartParamsPtr params,
       network::mojom::URLLoaderFactoryPtr non_network_loader_factory);
 
-  // Called back from StartTask after a start worker message is sent.
-  void OnStartWorkerMessageSent(bool is_script_streaming);
-
   // Implements mojom::EmbeddedWorkerInstanceHost.
   // These functions all run on the IO thread.
   void RequestTermination() override;
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index 9ee02ba..ed9ac4d 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -234,13 +234,6 @@
     worker->status_ = status;
   }
 
-  ServiceWorkerStatusCode SimulateSendStartWorker(
-      EmbeddedWorkerInstance* worker,
-      mojom::EmbeddedWorkerStartParamsPtr params) {
-    return worker->SendStartWorker(std::move(params),
-                                   nullptr /* non_network_loader_factory */);
-  }
-
   blink::mojom::ServiceWorkerInstalledScriptsInfoPtr
   GetInstalledScriptsInfoPtr() {
     installed_scripts_managers_.emplace_back();
@@ -976,21 +969,4 @@
   EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, worker->status());
 }
 
-// Test that SendStartWorker checks if dispatcher host exists.
-TEST_F(EmbeddedWorkerInstanceTest, NoDispatcherHost) {
-  const GURL scope("http://example.com/");
-  const GURL url("http://example.com/worker.js");
-
-  RegistrationAndVersionPair pair = PrepareRegistrationAndVersion(scope, url);
-  std::unique_ptr<EmbeddedWorkerInstance> worker =
-      embedded_worker_registry()->CreateWorker(pair.second.get());
-  SetWorkerStatus(worker.get(), EmbeddedWorkerStatus::STARTING);
-  auto params = mojom::EmbeddedWorkerStartParams::New();
-  ServiceWorkerStatusCode result =
-      SimulateSendStartWorker(worker.get(), std::move(params));
-  EXPECT_EQ(SERVICE_WORKER_ERROR_IPC_FAILED, result);
-  // Set to STOPPED because EWInstance's destructor DCHECKs status.
-  SetWorkerStatus(worker.get(), EmbeddedWorkerStatus::STOPPED);
-}
-
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index afa17e4..c2da069 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -269,7 +269,7 @@
 }
 
 ServiceWorkerContextCore::ServiceWorkerContextCore(
-    const base::FilePath& path,
+    const base::FilePath& user_data_directory,
     scoped_refptr<base::SequencedTaskRunner> database_task_runner,
     storage::QuotaManagerProxy* quota_manager_proxy,
     storage::SpecialStoragePolicy* special_storage_policy,
@@ -290,8 +290,8 @@
   // These get a WeakPtr from |weak_factory_|, so must be set after
   // |weak_factory_| is initialized.
   storage_ = ServiceWorkerStorage::Create(
-      path, AsWeakPtr(), std::move(database_task_runner), quota_manager_proxy,
-      special_storage_policy);
+      user_data_directory, AsWeakPtr(), std::move(database_task_runner),
+      quota_manager_proxy, special_storage_policy);
   embedded_worker_registry_ = EmbeddedWorkerRegistry::Create(AsWeakPtr());
   job_coordinator_ = std::make_unique<ServiceWorkerJobCoordinator>(AsWeakPtr());
 }
@@ -768,6 +768,14 @@
   return it->second.count;
 }
 
+void ServiceWorkerContextCore::NotifyRegistrationStored(int64_t registration_id,
+                                                        const GURL& pattern) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+  observer_list_->Notify(
+      FROM_HERE, &ServiceWorkerContextCoreObserver::OnRegistrationStored,
+      registration_id, pattern);
+}
+
 void ServiceWorkerContextCore::OnStorageWiped() {
   observer_list_->Notify(FROM_HERE,
                          &ServiceWorkerContextCoreObserver::OnStorageWiped);
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index 71f4b53..8268537 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -287,6 +287,9 @@
   // version. The count resets to zero when the worker successfully starts.
   int GetVersionFailureCount(int64_t version_id);
 
+  // Called by ServiceWorkerStorage when StoreRegistration() succeeds.
+  void NotifyRegistrationStored(int64_t registration_id, const GURL& pattern);
+
   URLLoaderFactoryGetter* loader_factory_getter() {
     return loader_factory_getter_.get();
   }
diff --git a/content/browser/service_worker/service_worker_context_core_observer.h b/content/browser/service_worker/service_worker_context_core_observer.h
index 4b00077c..1a5a4d2 100644
--- a/content/browser/service_worker/service_worker_context_core_observer.h
+++ b/content/browser/service_worker/service_worker_context_core_observer.h
@@ -91,11 +91,18 @@
                                    const std::string& uuid) {}
   // Called when the ServiceWorkerContainer.register() promise is resolved.
   //
-  // This is called before the service worker registration is persisted to disk.
-  // The caller cannot assume that the ServiceWorkerContextCore will find the
-  // registration at this point.
+  // This is called before the service worker registration is persisted to
+  // storage. The implementation cannot assume that the ServiceWorkerContextCore
+  // will find the registration at this point.
   virtual void OnRegistrationCompleted(int64_t registration_id,
                                        const GURL& pattern) {}
+  // Called after a service worker registration is persisted to storage.
+  //
+  // This happens after OnRegistrationCompleted(). The implementation can assume
+  // that ServiceWorkerContextCore will find the registration, and can safely
+  // add user data to the registration.
+  virtual void OnRegistrationStored(int64_t registration_id,
+                                    const GURL& pattern) {}
   virtual void OnRegistrationDeleted(int64_t registration_id,
                                      const GURL& pattern) {}
 
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index f01c68f..c496e2b 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -112,6 +112,7 @@
 
 enum NotificationType {
   REGISTRATION_COMPLETED,
+  REGISTRATION_STORED,
   REGISTRATION_DELETED,
   STORAGE_RECOVERED,
 };
@@ -146,6 +147,14 @@
     log.registration_id = registration_id;
     notifications_.push_back(log);
   }
+  void OnRegistrationStored(int64_t registration_id,
+                            const GURL& pattern) override {
+    NotificationLog log;
+    log.type = REGISTRATION_STORED;
+    log.pattern = pattern;
+    log.registration_id = registration_id;
+    notifications_.push_back(log);
+  }
   void OnRegistrationDeleted(int64_t registration_id,
                              const GURL& pattern) override {
     NotificationLog log;
@@ -217,8 +226,8 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(called);
 
-  ASSERT_EQ(2UL, helper_->dispatched_events()->size());
-  ASSERT_EQ(1UL, client->events().size());
+  ASSERT_EQ(2u, helper_->dispatched_events()->size());
+  ASSERT_EQ(1u, client->events().size());
   EXPECT_EQ(RecordableEmbeddedWorkerInstanceClient::Message::StartWorker,
             client->events()[0]);
   EXPECT_EQ(EmbeddedWorkerTestHelper::Event::Install,
@@ -228,10 +237,13 @@
 
   EXPECT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, registration_id);
 
-  ASSERT_EQ(1u, notifications_.size());
+  ASSERT_EQ(2u, notifications_.size());
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type);
   EXPECT_EQ(pattern, notifications_[0].pattern);
   EXPECT_EQ(registration_id, notifications_[0].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type);
+  EXPECT_EQ(pattern, notifications_[1].pattern);
+  EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
   context()->storage()->FindRegistrationForId(
       registration_id, pattern.GetOrigin(),
@@ -266,8 +278,8 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(called);
 
-  ASSERT_EQ(1UL, helper_->dispatched_events()->size());
-  ASSERT_EQ(2UL, client->events().size());
+  ASSERT_EQ(1u, helper_->dispatched_events()->size());
+  ASSERT_EQ(2u, client->events().size());
   EXPECT_EQ(RecordableEmbeddedWorkerInstanceClient::Message::StartWorker,
             client->events()[0]);
   EXPECT_EQ(EmbeddedWorkerTestHelper::Event::Install,
@@ -314,8 +326,8 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(called);
 
-  ASSERT_EQ(2UL, helper_->dispatched_events()->size());
-  ASSERT_EQ(1UL, client->events().size());
+  ASSERT_EQ(2u, helper_->dispatched_events()->size());
+  ASSERT_EQ(1u, client->events().size());
   EXPECT_EQ(RecordableEmbeddedWorkerInstanceClient::Message::StartWorker,
             client->events()[0]);
   EXPECT_EQ(EmbeddedWorkerTestHelper::Event::Install,
@@ -325,10 +337,13 @@
 
   EXPECT_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, registration_id);
 
-  ASSERT_EQ(1u, notifications_.size());
+  ASSERT_EQ(2u, notifications_.size());
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type);
   EXPECT_EQ(pattern, notifications_[0].pattern);
   EXPECT_EQ(registration_id, notifications_[0].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type);
+  EXPECT_EQ(pattern, notifications_[1].pattern);
+  EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
   context()->storage()->FindRegistrationForId(
       registration_id, pattern.GetOrigin(),
@@ -368,13 +383,16 @@
                      false /* expect_waiting */, false /* expect_active */));
   base::RunLoop().RunUntilIdle();
 
-  ASSERT_EQ(2u, notifications_.size());
+  ASSERT_EQ(3u, notifications_.size());
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type);
   EXPECT_EQ(pattern, notifications_[0].pattern);
   EXPECT_EQ(registration_id, notifications_[0].registration_id);
-  EXPECT_EQ(REGISTRATION_DELETED, notifications_[1].type);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type);
   EXPECT_EQ(pattern, notifications_[1].pattern);
   EXPECT_EQ(registration_id, notifications_[1].registration_id);
+  EXPECT_EQ(REGISTRATION_DELETED, notifications_[2].type);
+  EXPECT_EQ(pattern, notifications_[2].pattern);
+  EXPECT_EQ(registration_id, notifications_[2].registration_id);
 }
 
 // Make sure registrations are cleaned up when they are unregistered in bulk.
@@ -473,25 +491,37 @@
 
   base::RunLoop().RunUntilIdle();
 
-  ASSERT_EQ(6u, notifications_.size());
+  ASSERT_EQ(10u, notifications_.size());
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type);
   EXPECT_EQ(registration_id1, notifications_[0].registration_id);
   EXPECT_EQ(origin1_p1, notifications_[0].pattern);
-  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[1].type);
-  EXPECT_EQ(origin1_p2, notifications_[1].pattern);
-  EXPECT_EQ(registration_id2, notifications_[1].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type);
+  EXPECT_EQ(registration_id1, notifications_[1].registration_id);
+  EXPECT_EQ(origin1_p1, notifications_[1].pattern);
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[2].type);
-  EXPECT_EQ(origin2_p1, notifications_[2].pattern);
-  EXPECT_EQ(registration_id3, notifications_[2].registration_id);
-  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[3].type);
-  EXPECT_EQ(origin3_p1, notifications_[3].pattern);
-  EXPECT_EQ(registration_id4, notifications_[3].registration_id);
-  EXPECT_EQ(REGISTRATION_DELETED, notifications_[4].type);
-  EXPECT_EQ(origin1_p1, notifications_[4].pattern);
-  EXPECT_EQ(registration_id1, notifications_[4].registration_id);
-  EXPECT_EQ(REGISTRATION_DELETED, notifications_[5].type);
-  EXPECT_EQ(origin1_p2, notifications_[5].pattern);
-  EXPECT_EQ(registration_id2, notifications_[5].registration_id);
+  EXPECT_EQ(origin1_p2, notifications_[2].pattern);
+  EXPECT_EQ(registration_id2, notifications_[2].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[3].type);
+  EXPECT_EQ(origin1_p2, notifications_[3].pattern);
+  EXPECT_EQ(registration_id2, notifications_[3].registration_id);
+  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[4].type);
+  EXPECT_EQ(origin2_p1, notifications_[4].pattern);
+  EXPECT_EQ(registration_id3, notifications_[4].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[5].type);
+  EXPECT_EQ(origin2_p1, notifications_[5].pattern);
+  EXPECT_EQ(registration_id3, notifications_[5].registration_id);
+  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[6].type);
+  EXPECT_EQ(origin3_p1, notifications_[6].pattern);
+  EXPECT_EQ(registration_id4, notifications_[6].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[7].type);
+  EXPECT_EQ(origin3_p1, notifications_[7].pattern);
+  EXPECT_EQ(registration_id4, notifications_[7].registration_id);
+  EXPECT_EQ(REGISTRATION_DELETED, notifications_[8].type);
+  EXPECT_EQ(origin1_p1, notifications_[8].pattern);
+  EXPECT_EQ(registration_id1, notifications_[8].registration_id);
+  EXPECT_EQ(REGISTRATION_DELETED, notifications_[9].type);
+  EXPECT_EQ(origin1_p2, notifications_[9].pattern);
+  EXPECT_EQ(registration_id2, notifications_[9].registration_id);
 }
 
 // Make sure registering a new script shares an existing registration.
@@ -528,13 +558,19 @@
             new_registration_id);
   EXPECT_EQ(old_registration_id, new_registration_id);
 
-  ASSERT_EQ(2u, notifications_.size());
+  ASSERT_EQ(4u, notifications_.size());
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type);
   EXPECT_EQ(pattern, notifications_[0].pattern);
   EXPECT_EQ(old_registration_id, notifications_[0].registration_id);
-  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[1].type);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type);
   EXPECT_EQ(pattern, notifications_[1].pattern);
-  EXPECT_EQ(new_registration_id, notifications_[1].registration_id);
+  EXPECT_EQ(old_registration_id, notifications_[1].registration_id);
+  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[2].type);
+  EXPECT_EQ(pattern, notifications_[2].pattern);
+  EXPECT_EQ(new_registration_id, notifications_[2].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[3].type);
+  EXPECT_EQ(pattern, notifications_[3].pattern);
+  EXPECT_EQ(new_registration_id, notifications_[3].registration_id);
 }
 
 // Make sure that when registering a duplicate pattern+script_url
@@ -570,13 +606,16 @@
   ASSERT_TRUE(called);
   EXPECT_EQ(old_registration_id, new_registration_id);
 
-  ASSERT_EQ(2u, notifications_.size());
+  ASSERT_EQ(3u, notifications_.size());
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type);
   EXPECT_EQ(pattern, notifications_[0].pattern);
   EXPECT_EQ(old_registration_id, notifications_[0].registration_id);
-  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[1].type);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type);
   EXPECT_EQ(pattern, notifications_[1].pattern);
   EXPECT_EQ(old_registration_id, notifications_[1].registration_id);
+  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[2].type);
+  EXPECT_EQ(pattern, notifications_[2].pattern);
+  EXPECT_EQ(old_registration_id, notifications_[2].registration_id);
 }
 
 TEST_F(ServiceWorkerContextTest, ProviderHostIterator) {
@@ -753,14 +792,20 @@
   // taken by the running registration, so the following method calls return 3.
   EXPECT_EQ(3, context()->GetNewServiceWorkerHandleId());
 
-  ASSERT_EQ(3u, notifications_.size());
+  ASSERT_EQ(5u, notifications_.size());
   EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[0].type);
   EXPECT_EQ(pattern, notifications_[0].pattern);
   EXPECT_EQ(registration_id, notifications_[0].registration_id);
-  EXPECT_EQ(STORAGE_RECOVERED, notifications_[1].type);
-  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[2].type);
-  EXPECT_EQ(pattern, notifications_[2].pattern);
-  EXPECT_EQ(registration_id, notifications_[2].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[1].type);
+  EXPECT_EQ(pattern, notifications_[1].pattern);
+  EXPECT_EQ(registration_id, notifications_[1].registration_id);
+  EXPECT_EQ(STORAGE_RECOVERED, notifications_[2].type);
+  EXPECT_EQ(REGISTRATION_COMPLETED, notifications_[3].type);
+  EXPECT_EQ(pattern, notifications_[3].pattern);
+  EXPECT_EQ(registration_id, notifications_[3].registration_id);
+  EXPECT_EQ(REGISTRATION_STORED, notifications_[4].type);
+  EXPECT_EQ(pattern, notifications_[4].pattern);
+  EXPECT_EQ(registration_id, notifications_[4].registration_id);
 }
 
 INSTANTIATE_TEST_CASE_P(ServiceWorkerContextRecoveryTest,
diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc
index 8233ff3..81676aa 100644
--- a/content/browser/service_worker/service_worker_storage.cc
+++ b/content/browser/service_worker/service_worker_storage.cc
@@ -120,24 +120,25 @@
 
 // static
 std::unique_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
-    const base::FilePath& path,
+    const base::FilePath& user_data_directory,
     const base::WeakPtr<ServiceWorkerContextCore>& context,
     scoped_refptr<base::SequencedTaskRunner> database_task_runner,
     storage::QuotaManagerProxy* quota_manager_proxy,
     storage::SpecialStoragePolicy* special_storage_policy) {
-  return base::WrapUnique(
-      new ServiceWorkerStorage(path, context, std::move(database_task_runner),
-                               quota_manager_proxy, special_storage_policy));
+  return base::WrapUnique(new ServiceWorkerStorage(
+      user_data_directory, context, std::move(database_task_runner),
+      quota_manager_proxy, special_storage_policy));
 }
 
 // static
 std::unique_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
     const base::WeakPtr<ServiceWorkerContextCore>& context,
     ServiceWorkerStorage* old_storage) {
-  return base::WrapUnique(new ServiceWorkerStorage(
-      old_storage->path_, context, old_storage->database_task_runner_,
-      old_storage->quota_manager_proxy_.get(),
-      old_storage->special_storage_policy_.get()));
+  return base::WrapUnique(
+      new ServiceWorkerStorage(old_storage->user_data_directory_, context,
+                               old_storage->database_task_runner_,
+                               old_storage->quota_manager_proxy_.get(),
+                               old_storage->special_storage_policy_.get()));
 }
 
 void ServiceWorkerStorage::FindRegistrationForDocument(
@@ -1051,7 +1052,7 @@
 }
 
 ServiceWorkerStorage::ServiceWorkerStorage(
-    const base::FilePath& path,
+    const base::FilePath& user_data_directory,
     base::WeakPtr<ServiceWorkerContextCore> context,
     scoped_refptr<base::SequencedTaskRunner> database_task_runner,
     storage::QuotaManagerProxy* quota_manager_proxy,
@@ -1061,7 +1062,7 @@
       next_resource_id_(kInvalidServiceWorkerResourceId),
       state_(UNINITIALIZED),
       expecting_done_with_disk_on_disable_(false),
-      path_(path),
+      user_data_directory_(user_data_directory),
       context_(context),
       database_task_runner_(std::move(database_task_runner)),
       quota_manager_proxy_(quota_manager_proxy),
@@ -1074,16 +1075,18 @@
 }
 
 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
-  if (path_.empty())
+  if (user_data_directory_.empty())
     return base::FilePath();
-  return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
+  return user_data_directory_
+      .Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
       .Append(kDatabaseName);
 }
 
 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
-  if (path_.empty())
+  if (user_data_directory_.empty())
     return base::FilePath();
-  return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
+  return user_data_directory_
+      .Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
       .Append(kDiskCacheName);
 }
 
@@ -1388,6 +1391,8 @@
             deleted_version.resources_total_size_bytes);
   }
 
+  context_->NotifyRegistrationStored(new_version.registration_id,
+                                     new_version.scope);
   std::move(callback).Run(SERVICE_WORKER_OK);
 
   if (!context_->GetLiveVersion(deleted_version.version_id))
diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h
index 3daafe9..88a1c29 100644
--- a/content/browser/service_worker/service_worker_storage.h
+++ b/content/browser/service_worker/service_worker_storage.h
@@ -97,7 +97,7 @@
   ~ServiceWorkerStorage() override;
 
   static std::unique_ptr<ServiceWorkerStorage> Create(
-      const base::FilePath& path,
+      const base::FilePath& user_data_directory,
       const base::WeakPtr<ServiceWorkerContextCore>& context,
       scoped_refptr<base::SequencedTaskRunner> database_task_runner,
       storage::QuotaManagerProxy* quota_manager_proxy,
@@ -356,7 +356,7 @@
                               ServiceWorkerDatabase::Status status)>;
 
   ServiceWorkerStorage(
-      const base::FilePath& path,
+      const base::FilePath& user_data_directory,
       base::WeakPtr<ServiceWorkerContextCore> context,
       scoped_refptr<base::SequencedTaskRunner> database_task_runner,
       storage::QuotaManagerProxy* quota_manager_proxy,
@@ -583,7 +583,7 @@
   // ... so it's easier to keep track of the case when it will happen.
   bool expecting_done_with_disk_on_disable_;
 
-  base::FilePath path_;
+  base::FilePath user_data_directory_;
 
   // The context should be valid while the storage is alive.
   base::WeakPtr<ServiceWorkerContextCore> context_;
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index eed2f40e..426695a4 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -11556,9 +11556,10 @@
 
 // Tests that when a large OOPIF has been scaled, the compositor raster area
 // sent from the embedder is correct.
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_MACOSX)
 // Temporarily disabled on Android because this doesn't account for browser
 // control height or page scale factor.
+// Flaky on Mac. https://crbug.com/840314
 #define MAYBE_ScaledIframeRasterSize DISABLED_ScaledframeRasterSize
 #else
 #define MAYBE_ScaledIframeRasterSize ScaledIframeRasterSize
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index e664d56..a4ffea58 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -276,9 +276,7 @@
       switches::kOverrideUseSoftwareGLForTests,
       switches::kOverrideEnabledCdmInterfaceVersion,
       switches::kProxyServer,
-      switches::kDisableAcceleratedMjpegDecode,
       switches::kUseFakeDeviceForMediaStream,
-      switches::kUseFakeJpegDecodeAccelerator,
       switches::kUseFileForFakeVideoCapture,
       switches::kUseMockCertVerifierForTesting,
       switches::kUtilityStartupDialog,
diff --git a/content/browser/webrtc/webrtc_add_stream_no_deadlock_browsertest.cc b/content/browser/webrtc/webrtc_add_stream_no_deadlock_browsertest.cc
new file mode 100644
index 0000000..d3cd2eb9
--- /dev/null
+++ b/content/browser/webrtc/webrtc_add_stream_no_deadlock_browsertest.cc
@@ -0,0 +1,50 @@
+// 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/command_line.h"
+#include "build/build_config.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/webrtc/webrtc_content_browsertest_base.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+
+namespace content {
+
+#if defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)
+// Renderer crashes under Android ASAN: https://crbug.com/408496.
+#define MAYBE_WebRtcAddStreamNoDeadlockBrowserTest \
+  DISABLED_WebRtcAddStreamNoDeadlockBrowserTest
+#else
+#define MAYBE_WebRtcAddStreamNoDeadlockBrowserTest \
+  WebRtcAddStreamNoDeadlockBrowserTest
+#endif
+
+// This test sets up a peer connection in a specific way which previously
+// resulted in a deadlock. The test succeeds if the JavaScript completes
+// without errors. If the deadlock is re-introduced the test will time out.
+// This is a regression test for https://crbug.com/736725.
+class MAYBE_WebRtcAddStreamNoDeadlockBrowserTest
+    : public WebRtcContentBrowserTestBase {
+ public:
+  MAYBE_WebRtcAddStreamNoDeadlockBrowserTest() {}
+  ~MAYBE_WebRtcAddStreamNoDeadlockBrowserTest() override {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    WebRtcContentBrowserTestBase::SetUpCommandLine(command_line);
+    // Automatically grant device permission.
+    AppendUseFakeUIForMediaStreamFlag();
+  }
+
+ protected:
+  void MakeTypicalPeerConnectionCall(const std::string& javascript) {
+    MakeTypicalCall(javascript, "/media/peerconnection-add-stream.html");
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(MAYBE_WebRtcAddStreamNoDeadlockBrowserTest,
+                       AddStreamDoesNotCauseDeadlock) {
+  MakeTypicalPeerConnectionCall("runTest();");
+}
+}  // namespace content
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 78a1061..350988d 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -7,7 +7,6 @@
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
 import("//ipc/features.gni")
-import("//media/media_options.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//sandbox/features.gni")
@@ -231,6 +230,8 @@
     "notifications/notification_struct_traits.cc",
     "notifications/notification_struct_traits.h",
     "origin_util.cc",
+    "p2p_messages.h",
+    "p2p_socket_type.h",
     "page_message_enums.h",
     "page_messages.h",
     "page_state_serialization.cc",
@@ -376,6 +377,8 @@
     "//third_party/angle:angle_gpu_info_util",
     "//third_party/boringssl",
     "//third_party/icu",
+    "//third_party/webrtc/rtc_base:rtc_base",
+    "//third_party/webrtc_overrides",
     "//ui/base",
     "//ui/base/ime",
     "//ui/display",
@@ -433,17 +436,6 @@
     deps += [ "//ppapi/proxy:ipc_sources" ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "p2p_messages.h",
-      "p2p_socket_type.h",
-    ]
-    deps += [
-      "//third_party/webrtc/rtc_base:rtc_base",
-      "//third_party/webrtc_overrides",
-    ]
-  }
-
   if (use_ozone) {
     deps += [ "//ui/ozone" ]
   } else {
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 5b1b037..f61f24dd 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -478,7 +478,6 @@
   IPC_STRUCT_TRAITS_MEMBER(should_check_main_world_csp)
   IPC_STRUCT_TRAITS_MEMBER(has_user_gesture)
   IPC_STRUCT_TRAITS_MEMBER(started_from_context_menu)
-  IPC_STRUCT_TRAITS_MEMBER(suggested_filename)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(content::NavigationTiming)
@@ -555,7 +554,6 @@
   IPC_STRUCT_MEMBER(bool, user_gesture)
   IPC_STRUCT_MEMBER(bool, is_history_navigation_in_new_child)
   IPC_STRUCT_MEMBER(blink::WebTriggeringEventInfo, triggering_event_info)
-  IPC_STRUCT_MEMBER(base::Optional<std::string>, suggested_filename)
 IPC_STRUCT_END()
 
 IPC_STRUCT_BEGIN(FrameHostMsg_DownloadUrl_Params)
diff --git a/content/common/input/synthetic_web_input_event_builders.cc b/content/common/input/synthetic_web_input_event_builders.cc
index d8fd877..5cf6591 100644
--- a/content/common/input/synthetic_web_input_event_builders.cc
+++ b/content/common/input/synthetic_web_input_event_builders.cc
@@ -35,6 +35,7 @@
   DCHECK(WebInputEvent::IsMouseEventType(type));
   WebMouseEvent result(type, modifiers, ui::EventTimeForNow());
   result.SetPositionInWidget(window_x, window_y);
+  result.SetPositionInScreen(window_x, window_y);
   result.SetModifiers(modifiers);
   result.pointer_type = pointer_type;
   result.id = ui::MouseEvent::kMousePointerId;
@@ -198,6 +199,7 @@
   point.rotation_angle = 1.f;
   point.force = 1.f;
   point.tilt_x = point.tilt_y = 0;
+  point.pointer_type = blink::WebPointerProperties::PointerType::kTouch;
   ++touches_length;
   WebTouchEventTraits::ResetType(WebInputEvent::kTouchStart, TimeStamp(), this);
   return point.id;
diff --git a/content/common/navigation_params.cc b/content/common/navigation_params.cc
index 6d18f9fe..362669b0 100644
--- a/content/common/navigation_params.cc
+++ b/content/common/navigation_params.cc
@@ -44,8 +44,7 @@
     base::Optional<SourceLocation> source_location,
     CSPDisposition should_check_main_world_csp,
     bool started_from_context_menu,
-    bool has_user_gesture,
-    const base::Optional<std::string>& suggested_filename)
+    bool has_user_gesture)
     : url(url),
       referrer(referrer),
       transition(transition),
@@ -63,8 +62,7 @@
       source_location(source_location),
       should_check_main_world_csp(should_check_main_world_csp),
       started_from_context_menu(started_from_context_menu),
-      has_user_gesture(has_user_gesture),
-      suggested_filename(suggested_filename) {
+      has_user_gesture(has_user_gesture) {
   // |method != "POST"| should imply absence of |post_data|.
   if (method != "POST" && post_data) {
     NOTREACHED();
diff --git a/content/common/navigation_params.h b/content/common/navigation_params.h
index 7d5a93f..e761d92 100644
--- a/content/common/navigation_params.h
+++ b/content/common/navigation_params.h
@@ -76,8 +76,7 @@
       base::Optional<SourceLocation> source_location,
       CSPDisposition should_check_main_world_csp,
       bool started_from_context_menu,
-      bool has_user_gesture,
-      const base::Optional<std::string>& suggested_filename);
+      bool has_user_gesture);
   CommonNavigationParams(const CommonNavigationParams& other);
   ~CommonNavigationParams();
 
@@ -161,11 +160,6 @@
 
   // True if the request was user initiated.
   bool has_user_gesture = false;
-
-  // If the navigation started in response to a HTML anchor element with a
-  // download attribute, this is the (possible empty) value of the download
-  // attribute.
-  base::Optional<std::string> suggested_filename;
 };
 
 // Provided by the browser -----------------------------------------------------
diff --git a/content/common/resource_timing_info.h b/content/common/resource_timing_info.h
index 78b9a61..cb327f7f 100644
--- a/content/common/resource_timing_info.h
+++ b/content/common/resource_timing_info.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/optional.h"
+#include "base/time/time.h"
 
 namespace content {
 
@@ -36,22 +37,22 @@
   ResourceLoadTiming(const ResourceLoadTiming&);
   ~ResourceLoadTiming();
 
-  double request_time = 0.0;
-  double proxy_start = 0.0;
-  double proxy_end = 0.0;
-  double dns_start = 0.0;
-  double dns_end = 0.0;
-  double connect_start = 0.0;
-  double connect_end = 0.0;
-  double worker_start = 0.0;
-  double worker_ready = 0.0;
-  double send_start = 0.0;
-  double send_end = 0.0;
-  double receive_headers_end = 0.0;
-  double ssl_start = 0.0;
-  double ssl_end = 0.0;
-  double push_start = 0.0;
-  double push_end = 0.0;
+  base::TimeTicks request_time;
+  base::TimeTicks proxy_start;
+  base::TimeTicks proxy_end;
+  base::TimeTicks dns_start;
+  base::TimeTicks dns_end;
+  base::TimeTicks connect_start;
+  base::TimeTicks connect_end;
+  base::TimeTicks worker_start;
+  base::TimeTicks worker_ready;
+  base::TimeTicks send_start;
+  base::TimeTicks send_end;
+  base::TimeTicks receive_headers_end;
+  base::TimeTicks ssl_start;
+  base::TimeTicks ssl_end;
+  base::TimeTicks push_start;
+  base::TimeTicks push_end;
 };
 
 // TODO(dcheng): Migrate this struct over to Mojo so it doesn't need to be
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index c9e95a6..fe062d6 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
-import("//media/media_options.gni")
 
 # See //content/BUILD.gn for how this works.
 group("browser") {
@@ -93,6 +92,8 @@
     "content_browser_client.h",
     "context_factory.h",
     "cookie_store_factory.h",
+    "desktop_capture.cc",
+    "desktop_capture.h",
     "desktop_media_id.cc",
     "desktop_media_id.h",
     "devtools_agent_host.h",
@@ -300,6 +301,8 @@
     "web_ui_url_loader_factory.h",
     "webrtc_event_logger.cc",
     "webrtc_event_logger.h",
+    "webrtc_log.cc",
+    "webrtc_log.h",
     "websocket_handshake_request_info.h",
     "webvr_service_provider.cc",
     "webvr_service_provider.h",
@@ -323,6 +326,7 @@
     "//services/service_manager/public/cpp",
     "//services/tracing/public/cpp",
     "//services/ui/public/interfaces",
+    "//third_party/webrtc/modules/desktop_capture",
 
     # We expose skia headers in the public API.
     "//skia",
@@ -375,14 +379,4 @@
       "zoom_level_delegate.h",
     ]
   }
-
-  if (enable_webrtc) {
-    sources += [
-      "desktop_capture.cc",
-      "desktop_capture.h",
-      "webrtc_log.cc",
-      "webrtc_log.h",
-    ]
-    public_deps += [ "//third_party/webrtc/modules/desktop_capture" ]
-  }
 }
diff --git a/content/public/browser/navigation_controller.h b/content/public/browser/navigation_controller.h
index 3bd12f4..9bba376 100644
--- a/content/public/browser/navigation_controller.h
+++ b/content/public/browser/navigation_controller.h
@@ -199,11 +199,6 @@
     // Indicates whether or not this navigation was initiated via context menu.
     bool started_from_context_menu;
 
-    // If this event was triggered by an anchor element with a download
-    // attribute, |suggested_filename| will contain the (possibly empty) value
-    // of that attribute.
-    base::Optional<std::string> suggested_filename;
-
     // This value should only be set for main frame navigations. Subframe
     // navigations will always get their NavigationUIData from
     // ContentBrowserClient::GetNavigationUIData.
diff --git a/content/public/browser/navigation_handle.cc b/content/public/browser/navigation_handle.cc
index a109ce3..f0770fdd 100644
--- a/content/public/browser/navigation_handle.cc
+++ b/content/public/browser/navigation_handle.cc
@@ -50,7 +50,6 @@
           false,                  // started_from_context_menu
           CSPDisposition::CHECK,  // should_check_main_world_csp
           is_form_submission,     // is_form_submission
-          base::nullopt,          // suggested_filename
           nullptr,                // navigation_ui_data
           method, net::HttpRequestHeaders(), resource_request_body, Referrer(),
           false,  // has_user_gesture
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index 28e3bf68..fbcb339 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -262,10 +262,6 @@
   // Returns true if this navigation was initiated by a form submission.
   virtual bool IsFormSubmission() = 0;
 
-  // If this navigation was triggered by an anchor with a download attribute,
-  // this returns the (possibly empty) value of that attribute.
-  virtual const base::Optional<std::string>& GetSuggestedFilename() = 0;
-
   // Testing methods ----------------------------------------------------------
   //
   // The following methods should be used exclusively for writing unit tests.
diff --git a/content/public/browser/page_navigator.h b/content/public/browser/page_navigator.h
index a7572a8..1f16e0f 100644
--- a/content/public/browser/page_navigator.h
+++ b/content/public/browser/page_navigator.h
@@ -108,11 +108,6 @@
   // Indicates whether this navigation was started via context menu.
   bool started_from_context_menu;
 
-  // If this event was triggered by an anchor element with a download
-  // attribute, |suggested_filename| will contain the (possibly empty) value of
-  // that attribute.
-  base::Optional<std::string> suggested_filename;
-
   // Indicates that the navigation should happen in an app window if
   // possible, i.e. if an app for the URL is installed.
   bool open_app_window_if_possible;
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index ebb4d5e..aa2ada2 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -8,7 +8,6 @@
 import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
 import("//content/public/common/zygote_features.gni")
-import("//media/media_options.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//third_party/webrtc/webrtc.gni")
@@ -242,6 +241,8 @@
     "webplugininfo.cc",
     "webplugininfo.h",
     "webplugininfo_param_traits.h",
+    "webrtc_ip_handling_policy.cc",
+    "webrtc_ip_handling_policy.h",
     "zygote_fork_delegate_linux.h",
   ]
 
@@ -319,13 +320,6 @@
     ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "webrtc_ip_handling_policy.cc",
-      "webrtc_ip_handling_policy.h",
-    ]
-  }
-
   if (use_zygote_handle) {
     sources += [ "zygote_handle.h" ]
   }
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index f4f8ab9..0ec4696 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -79,6 +79,11 @@
 // Disable gpu-accelerated 2d canvas.
 const char kDisableAccelerated2dCanvas[]    = "disable-accelerated-2d-canvas";
 
+// Disable hardware acceleration of mjpeg decode for captured frame, where
+// available.
+const char kDisableAcceleratedMjpegDecode[] =
+    "disable-accelerated-mjpeg-decode";
+
 // Disables hardware acceleration of video decode, where available.
 const char kDisableAcceleratedVideoDecode[] =
     "disable-accelerated-video-decode";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 8a960021..9a64c0d 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -33,6 +33,7 @@
 CONTENT_EXPORT extern const char kDisable3DAPIs[];
 CONTENT_EXPORT extern const char kDisableAccelerated2dCanvas[];
 CONTENT_EXPORT extern const char kDisableAcceleratedJpegDecoding[];
+CONTENT_EXPORT extern const char kDisableAcceleratedMjpegDecode[];
 CONTENT_EXPORT extern const char kDisableAcceleratedVideoDecode[];
 CONTENT_EXPORT extern const char kDisableAcceleratedVideoEncode[];
 CONTENT_EXPORT extern const char kDisableAudioSupportForDesktopShare[];
diff --git a/content/public/renderer/BUILD.gn b/content/public/renderer/BUILD.gn
index 18e34b8..ea773ce 100644
--- a/content/public/renderer/BUILD.gn
+++ b/content/public/renderer/BUILD.gn
@@ -42,6 +42,16 @@
     "document_state.h",
     "fixed_received_data.cc",
     "fixed_received_data.h",
+    "media_stream_audio_renderer.h",
+    "media_stream_audio_sink.cc",
+    "media_stream_audio_sink.h",
+    "media_stream_renderer_factory.h",
+    "media_stream_sink.h",
+    "media_stream_utils.cc",
+    "media_stream_utils.h",
+    "media_stream_video_renderer.h",
+    "media_stream_video_sink.cc",
+    "media_stream_video_sink.h",
     "navigation_state.cc",
     "navigation_state.h",
     "pepper_plugin_instance.h",
@@ -72,6 +82,7 @@
     "v8_value_converter.h",
     "video_encode_accelerator.cc",
     "video_encode_accelerator.h",
+    "webrtc_log_message_delegate.h",
     "websocket_handshake_throttle_provider.h",
     "window_features_converter.cc",
     "window_features_converter.h",
@@ -86,7 +97,9 @@
 
   deps = [
     "//content/public/child:child_sources",
+    "//content/public/common:buildflags",
     "//content/public/common:common_sources",
+    "//content/public/common:feature_h264_with_openh264_ffmpeg",
     "//content/renderer",
     "//gin",
     "//media/capture",
@@ -96,6 +109,7 @@
     "//ppapi/c",
     "//skia",
     "//third_party/blink/public:blink_headers",
+    "//third_party/webrtc_overrides",
     "//third_party/widevine/cdm:headers",
     "//ui/base",
     "//ui/base/ime",
@@ -114,27 +128,6 @@
     deps += [ "//sandbox" ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "media_stream_audio_renderer.h",
-      "media_stream_audio_sink.cc",
-      "media_stream_audio_sink.h",
-      "media_stream_renderer_factory.h",
-      "media_stream_sink.h",
-      "media_stream_utils.cc",
-      "media_stream_utils.h",
-      "media_stream_video_renderer.h",
-      "media_stream_video_sink.cc",
-      "media_stream_video_sink.h",
-      "webrtc_log_message_delegate.h",
-    ]
-    deps += [
-      "//content/public/common:buildflags",
-      "//content/public/common:feature_h264_with_openh264_ffmpeg",
-      "//third_party/webrtc_overrides",
-    ]
-  }
-
   if (enable_plugins) {
     sources += [ "plugin_instance_throttler.h" ]
   }
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index c75faa8..39a356fb 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -546,8 +546,7 @@
       PREVIEWS_UNSPECIFIED, base::TimeTicks::Now(), "GET", nullptr,
       base::Optional<SourceLocation>(),
       CSPDisposition::CHECK /* should_check_main_world_csp */,
-      false /* started_from_context_menu */, false /* has_user_gesture */,
-      base::nullopt /* suggested_filename */);
+      false /* started_from_context_menu */, false /* has_user_gesture */);
   RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
   TestRenderFrame* frame =
       static_cast<TestRenderFrame*>(impl->GetMainRenderFrame());
@@ -689,8 +688,7 @@
       GURL(), PREVIEWS_UNSPECIFIED, base::TimeTicks::Now(), "GET", nullptr,
       base::Optional<SourceLocation>(),
       CSPDisposition::CHECK /* should_check_main_world_csp */,
-      false /* started_from_context_menu */, false /* has_user_gesture */,
-      base::nullopt /* suggested_filename */);
+      false /* started_from_context_menu */, false /* has_user_gesture */);
   RequestNavigationParams request_params;
   request_params.page_state = state;
   request_params.nav_entry_id = pending_offset + 1;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index 261a8928..fee5fc9c 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -160,6 +160,8 @@
     "history_serialization.h",
     "idle_user_detector.cc",
     "idle_user_detector.h",
+    "image_capture/image_capture_frame_grabber.cc",
+    "image_capture/image_capture_frame_grabber.h",
     "image_downloader/image_downloader_base.cc",
     "image_downloader/image_downloader_base.h",
     "image_downloader/image_downloader_impl.cc",
@@ -303,12 +305,180 @@
     "media/renderer_webaudiodevice_impl.h",
     "media/renderer_webmediaplayer_delegate.cc",
     "media/renderer_webmediaplayer_delegate.h",
+    "media/stream/aec_dump_message_filter.cc",
+    "media/stream/aec_dump_message_filter.h",
+    "media/stream/apply_constraints_processor.cc",
+    "media/stream/apply_constraints_processor.h",
+    "media/stream/external_media_stream_audio_source.cc",
+    "media/stream/external_media_stream_audio_source.h",
+    "media/stream/local_media_stream_audio_source.cc",
+    "media/stream/local_media_stream_audio_source.h",
+    "media/stream/media_stream_audio_deliverer.h",
+    "media/stream/media_stream_audio_level_calculator.cc",
+    "media/stream/media_stream_audio_level_calculator.h",
+    "media/stream/media_stream_audio_processor.cc",
+    "media/stream/media_stream_audio_processor.h",
+    "media/stream/media_stream_audio_processor_options.cc",
+    "media/stream/media_stream_audio_processor_options.h",
+    "media/stream/media_stream_audio_source.cc",
+    "media/stream/media_stream_audio_source.h",
+    "media/stream/media_stream_audio_track.cc",
+    "media/stream/media_stream_audio_track.h",
+    "media/stream/media_stream_center.cc",
+    "media/stream/media_stream_center.h",
+    "media/stream/media_stream_constraints_util.cc",
+    "media/stream/media_stream_constraints_util.h",
+    "media/stream/media_stream_constraints_util_audio.cc",
+    "media/stream/media_stream_constraints_util_audio.h",
+    "media/stream/media_stream_constraints_util_sets.cc",
+    "media/stream/media_stream_constraints_util_sets.h",
+    "media/stream/media_stream_constraints_util_video_content.cc",
+    "media/stream/media_stream_constraints_util_video_content.h",
+    "media/stream/media_stream_constraints_util_video_device.cc",
+    "media/stream/media_stream_constraints_util_video_device.h",
+    "media/stream/media_stream_device_observer.cc",
+    "media/stream/media_stream_device_observer.h",
+    "media/stream/media_stream_dispatcher_eventhandler.h",
+    "media/stream/media_stream_registry_interface.h",
+    "media/stream/media_stream_renderer_factory_impl.cc",
+    "media/stream/media_stream_renderer_factory_impl.h",
+    "media/stream/media_stream_source.cc",
+    "media/stream/media_stream_source.h",
+    "media/stream/media_stream_track.cc",
+    "media/stream/media_stream_track.h",
+    "media/stream/media_stream_video_capturer_source.cc",
+    "media/stream/media_stream_video_capturer_source.h",
+    "media/stream/media_stream_video_renderer_sink.cc",
+    "media/stream/media_stream_video_renderer_sink.h",
+    "media/stream/media_stream_video_source.cc",
+    "media/stream/media_stream_video_source.h",
+    "media/stream/media_stream_video_track.cc",
+    "media/stream/media_stream_video_track.h",
+    "media/stream/processed_local_audio_source.cc",
+    "media/stream/processed_local_audio_source.h",
+    "media/stream/remote_media_stream_track_adapter.cc",
+    "media/stream/remote_media_stream_track_adapter.h",
+    "media/stream/secure_display_link_tracker.h",
+    "media/stream/track_audio_renderer.cc",
+    "media/stream/track_audio_renderer.h",
+    "media/stream/user_media_client_impl.cc",
+    "media/stream/user_media_client_impl.h",
+    "media/stream/user_media_processor.cc",
+    "media/stream/user_media_processor.h",
+    "media/stream/video_track_adapter.cc",
+    "media/stream/video_track_adapter.h",
+    "media/stream/webaudio_media_stream_source.cc",
+    "media/stream/webaudio_media_stream_source.h",
+    "media/stream/webmediaplayer_ms.cc",
+    "media/stream/webmediaplayer_ms.h",
+    "media/stream/webmediaplayer_ms_compositor.cc",
+    "media/stream/webmediaplayer_ms_compositor.h",
     "media/video_capture_impl.cc",
     "media/video_capture_impl.h",
     "media/video_capture_impl_manager.cc",
     "media/video_capture_impl_manager.h",
     "media/web_media_element_source_utils.cc",
     "media/web_media_element_source_utils.h",
+    "media/webrtc/audio_codec_factory.cc",
+    "media/webrtc/audio_codec_factory.h",
+    "media/webrtc/media_stream_remote_video_source.cc",
+    "media/webrtc/media_stream_remote_video_source.h",
+    "media/webrtc/media_stream_track_metrics.cc",
+    "media/webrtc/media_stream_track_metrics.h",
+    "media/webrtc/media_stream_video_webrtc_sink.cc",
+    "media/webrtc/media_stream_video_webrtc_sink.h",
+    "media/webrtc/peer_connection_dependency_factory.cc",
+    "media/webrtc/peer_connection_dependency_factory.h",
+    "media/webrtc/peer_connection_remote_audio_source.cc",
+    "media/webrtc/peer_connection_remote_audio_source.h",
+    "media/webrtc/peer_connection_tracker.cc",
+    "media/webrtc/peer_connection_tracker.h",
+    "media/webrtc/rtc_certificate.cc",
+    "media/webrtc/rtc_certificate.h",
+    "media/webrtc/rtc_certificate_generator.cc",
+    "media/webrtc/rtc_certificate_generator.h",
+    "media/webrtc/rtc_data_channel_handler.cc",
+    "media/webrtc/rtc_data_channel_handler.h",
+    "media/webrtc/rtc_dtmf_sender_handler.cc",
+    "media/webrtc/rtc_dtmf_sender_handler.h",
+    "media/webrtc/rtc_event_log_output_sink.h",
+    "media/webrtc/rtc_event_log_output_sink_proxy.cc",
+    "media/webrtc/rtc_event_log_output_sink_proxy.h",
+    "media/webrtc/rtc_peer_connection_handler.cc",
+    "media/webrtc/rtc_peer_connection_handler.h",
+    "media/webrtc/rtc_rtp_contributing_source.cc",
+    "media/webrtc/rtc_rtp_contributing_source.h",
+    "media/webrtc/rtc_rtp_parameters.cc",
+    "media/webrtc/rtc_rtp_parameters.h",
+    "media/webrtc/rtc_rtp_receiver.cc",
+    "media/webrtc/rtc_rtp_receiver.h",
+    "media/webrtc/rtc_rtp_sender.cc",
+    "media/webrtc/rtc_rtp_sender.h",
+    "media/webrtc/rtc_stats.cc",
+    "media/webrtc/rtc_stats.h",
+    "media/webrtc/rtc_video_decoder.cc",
+    "media/webrtc/rtc_video_decoder.h",
+    "media/webrtc/rtc_video_decoder_factory.cc",
+    "media/webrtc/rtc_video_decoder_factory.h",
+    "media/webrtc/rtc_video_encoder.cc",
+    "media/webrtc/rtc_video_encoder.h",
+    "media/webrtc/rtc_video_encoder_factory.cc",
+    "media/webrtc/rtc_video_encoder_factory.h",
+    "media/webrtc/stun_field_trial.cc",
+    "media/webrtc/stun_field_trial.h",
+    "media/webrtc/track_observer.cc",
+    "media/webrtc/track_observer.h",
+    "media/webrtc/two_keys_adapter_map.h",
+    "media/webrtc/webrtc_audio_device_impl.cc",
+    "media/webrtc/webrtc_audio_device_impl.h",
+    "media/webrtc/webrtc_audio_device_not_impl.cc",
+    "media/webrtc/webrtc_audio_device_not_impl.h",
+    "media/webrtc/webrtc_audio_renderer.cc",
+    "media/webrtc/webrtc_audio_renderer.h",
+    "media/webrtc/webrtc_audio_sink.cc",
+    "media/webrtc/webrtc_audio_sink.h",
+    "media/webrtc/webrtc_media_stream_adapter.cc",
+    "media/webrtc/webrtc_media_stream_adapter.h",
+    "media/webrtc/webrtc_media_stream_adapter_map.cc",
+    "media/webrtc/webrtc_media_stream_adapter_map.h",
+    "media/webrtc/webrtc_media_stream_track_adapter.cc",
+    "media/webrtc/webrtc_media_stream_track_adapter.h",
+    "media/webrtc/webrtc_media_stream_track_adapter_map.cc",
+    "media/webrtc/webrtc_media_stream_track_adapter_map.h",
+    "media/webrtc/webrtc_set_remote_description_observer.cc",
+    "media/webrtc/webrtc_set_remote_description_observer.h",
+    "media/webrtc/webrtc_uma_histograms.cc",
+    "media/webrtc/webrtc_uma_histograms.h",
+    "media/webrtc/webrtc_video_capturer_adapter.cc",
+    "media/webrtc/webrtc_video_capturer_adapter.h",
+    "media/webrtc/webrtc_video_frame_adapter.cc",
+    "media/webrtc/webrtc_video_frame_adapter.h",
+    "media/webrtc_local_audio_source_provider.cc",
+    "media/webrtc_local_audio_source_provider.h",
+    "media/webrtc_logging.cc",
+    "media/webrtc_logging.h",
+    "media_capture_from_element/canvas_capture_handler.cc",
+    "media_capture_from_element/canvas_capture_handler.h",
+    "media_capture_from_element/html_audio_element_capturer_source.cc",
+    "media_capture_from_element/html_audio_element_capturer_source.h",
+    "media_capture_from_element/html_video_element_capturer_source.cc",
+    "media_capture_from_element/html_video_element_capturer_source.h",
+    "media_recorder/audio_track_encoder.cc",
+    "media_recorder/audio_track_encoder.h",
+    "media_recorder/audio_track_opus_encoder.cc",
+    "media_recorder/audio_track_opus_encoder.h",
+    "media_recorder/audio_track_pcm_encoder.cc",
+    "media_recorder/audio_track_pcm_encoder.h",
+    "media_recorder/audio_track_recorder.cc",
+    "media_recorder/audio_track_recorder.h",
+    "media_recorder/media_recorder_handler.cc",
+    "media_recorder/media_recorder_handler.h",
+    "media_recorder/vea_encoder.cc",
+    "media_recorder/vea_encoder.h",
+    "media_recorder/video_track_recorder.cc",
+    "media_recorder/video_track_recorder.h",
+    "media_recorder/vpx_encoder.cc",
+    "media_recorder/vpx_encoder.h",
     "menu_item_builder.cc",
     "menu_item_builder.h",
     "message_delivery_policy.h",
@@ -328,7 +498,28 @@
     "notifications/notification_dispatcher.h",
     "notifications/notification_manager.cc",
     "notifications/notification_manager.h",
+    "p2p/empty_network_manager.cc",
+    "p2p/empty_network_manager.h",
+    "p2p/filtering_network_manager.cc",
+    "p2p/filtering_network_manager.h",
+    "p2p/host_address_request.cc",
+    "p2p/host_address_request.h",
+    "p2p/ipc_network_manager.cc",
+    "p2p/ipc_network_manager.h",
+    "p2p/ipc_socket_factory.cc",
+    "p2p/ipc_socket_factory.h",
     "p2p/network_list_manager.h",
+    "p2p/network_list_observer.h",
+    "p2p/network_manager_uma.cc",
+    "p2p/network_manager_uma.h",
+    "p2p/port_allocator.cc",
+    "p2p/port_allocator.h",
+    "p2p/socket_client.h",
+    "p2p/socket_client_delegate.h",
+    "p2p/socket_client_impl.cc",
+    "p2p/socket_client_impl.h",
+    "p2p/socket_dispatcher.cc",
+    "p2p/socket_dispatcher.h",
     "pepper/fullscreen_container.h",
     "peripheral_content_heuristic.cc",
     "peripheral_content_heuristic.h",
@@ -510,6 +701,7 @@
     "//content/public/common:buildflags",
     "//content/public/common:feature_h264_with_openh264_ffmpeg",
     "//content/public/common:service_names",
+    "//crypto",
     "//crypto:platform",
     "//device/base/synchronization",
     "//device/gamepad/public/cpp:shared_with_blink",
@@ -519,6 +711,7 @@
     "//gpu",
     "//gpu/command_buffer/client:gles2_interface",
     "//gpu/command_buffer/client:raster_interface",
+    "//jingle:jingle_glue",
     "//media",
     "//media:media_buildflags",
     "//media/blink",
@@ -554,7 +747,49 @@
     "//third_party/blink/public/common",
     "//third_party/boringssl",
     "//third_party/icu",
+    "//third_party/libvpx",
     "//third_party/libyuv",
+    "//third_party/opus",
+    "//third_party/webrtc/api:libjingle_logging_api",
+    "//third_party/webrtc/api:libjingle_peerconnection_api",
+    "//third_party/webrtc/api:optional",
+    "//third_party/webrtc/api:rtc_stats_api",
+    "//third_party/webrtc/api:video_frame_api",
+    "//third_party/webrtc/api:video_frame_api_i420",
+    "//third_party/webrtc/api/audio:aec3_factory",
+    "//third_party/webrtc/api/audio_codecs:audio_codecs_api",
+    "//third_party/webrtc/api/audio_codecs/L16:audio_decoder_L16",
+    "//third_party/webrtc/api/audio_codecs/L16:audio_encoder_L16",
+    "//third_party/webrtc/api/audio_codecs/g711:audio_decoder_g711",
+    "//third_party/webrtc/api/audio_codecs/g711:audio_encoder_g711",
+    "//third_party/webrtc/api/audio_codecs/g722:audio_decoder_g722",
+    "//third_party/webrtc/api/audio_codecs/g722:audio_encoder_g722",
+    "//third_party/webrtc/api/audio_codecs/isac:audio_decoder_isac",
+    "//third_party/webrtc/api/audio_codecs/isac:audio_encoder_isac",
+    "//third_party/webrtc/api/audio_codecs/opus:audio_decoder_opus",
+    "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_opus",
+    "//third_party/webrtc/api/video_codecs:video_codecs_api",
+    "//third_party/webrtc/common_video:common_video",
+    "//third_party/webrtc/media:rtc_internal_video_codecs",
+    "//third_party/webrtc/media:rtc_media",
+    "//third_party/webrtc/media:rtc_media_base",
+    "//third_party/webrtc/modules/audio_device",
+    "//third_party/webrtc/modules/audio_processing",
+    "//third_party/webrtc/modules/audio_processing:audio_processing_statistics",
+    "//third_party/webrtc/modules/audio_processing/aec_dump",
+    "//third_party/webrtc/modules/video_coding:video_codec_interface",
+    "//third_party/webrtc/modules/video_coding:webrtc_h264",
+    "//third_party/webrtc/p2p:libstunprober",
+    "//third_party/webrtc/p2p:rtc_p2p",
+    "//third_party/webrtc/pc:libjingle_peerconnection",
+    "//third_party/webrtc/pc:peerconnection",
+    "//third_party/webrtc/pc:rtc_pc",
+    "//third_party/webrtc/pc:rtc_pc_base",
+    "//third_party/webrtc/rtc_base:rtc_base",
+    "//third_party/webrtc/rtc_base:rtc_task_queue",
+    "//third_party/webrtc/stats",
+    "//third_party/webrtc/system_wrappers",
+    "//third_party/webrtc_overrides:init_webrtc",
     "//third_party/widevine/cdm:headers",
     "//ui/accessibility",
     "//ui/base",
@@ -625,279 +860,31 @@
     deps += [ "//media/remoting" ]
   }
 
-  if (enable_webrtc) {
-    # WebRTC plugin-related stuff goes in a different section below.
+  if (enable_plugins) {
     sources += [
-      "image_capture/image_capture_frame_grabber.cc",
-      "image_capture/image_capture_frame_grabber.h",
-      "media/stream/aec_dump_message_filter.cc",
-      "media/stream/aec_dump_message_filter.h",
-      "media/stream/apply_constraints_processor.cc",
-      "media/stream/apply_constraints_processor.h",
-      "media/stream/external_media_stream_audio_source.cc",
-      "media/stream/external_media_stream_audio_source.h",
-      "media/stream/local_media_stream_audio_source.cc",
-      "media/stream/local_media_stream_audio_source.h",
-      "media/stream/media_stream_audio_deliverer.h",
-      "media/stream/media_stream_audio_level_calculator.cc",
-      "media/stream/media_stream_audio_level_calculator.h",
-      "media/stream/media_stream_audio_processor.cc",
-      "media/stream/media_stream_audio_processor.h",
-      "media/stream/media_stream_audio_processor_options.cc",
-      "media/stream/media_stream_audio_processor_options.h",
-      "media/stream/media_stream_audio_source.cc",
-      "media/stream/media_stream_audio_source.h",
-      "media/stream/media_stream_audio_track.cc",
-      "media/stream/media_stream_audio_track.h",
-      "media/stream/media_stream_center.cc",
-      "media/stream/media_stream_center.h",
-      "media/stream/media_stream_constraints_util.cc",
-      "media/stream/media_stream_constraints_util.h",
-      "media/stream/media_stream_constraints_util_audio.cc",
-      "media/stream/media_stream_constraints_util_audio.h",
-      "media/stream/media_stream_constraints_util_sets.cc",
-      "media/stream/media_stream_constraints_util_sets.h",
-      "media/stream/media_stream_constraints_util_video_content.cc",
-      "media/stream/media_stream_constraints_util_video_content.h",
-      "media/stream/media_stream_constraints_util_video_device.cc",
-      "media/stream/media_stream_constraints_util_video_device.h",
-      "media/stream/media_stream_device_observer.cc",
-      "media/stream/media_stream_device_observer.h",
-      "media/stream/media_stream_dispatcher_eventhandler.h",
-      "media/stream/media_stream_registry_interface.h",
-      "media/stream/media_stream_renderer_factory_impl.cc",
-      "media/stream/media_stream_renderer_factory_impl.h",
-      "media/stream/media_stream_source.cc",
-      "media/stream/media_stream_source.h",
-      "media/stream/media_stream_track.cc",
-      "media/stream/media_stream_track.h",
-      "media/stream/media_stream_video_capturer_source.cc",
-      "media/stream/media_stream_video_capturer_source.h",
-      "media/stream/media_stream_video_renderer_sink.cc",
-      "media/stream/media_stream_video_renderer_sink.h",
-      "media/stream/media_stream_video_source.cc",
-      "media/stream/media_stream_video_source.h",
-      "media/stream/media_stream_video_track.cc",
-      "media/stream/media_stream_video_track.h",
-      "media/stream/processed_local_audio_source.cc",
-      "media/stream/processed_local_audio_source.h",
-      "media/stream/remote_media_stream_track_adapter.cc",
-      "media/stream/remote_media_stream_track_adapter.h",
-      "media/stream/secure_display_link_tracker.h",
-      "media/stream/track_audio_renderer.cc",
-      "media/stream/track_audio_renderer.h",
-      "media/stream/user_media_client_impl.cc",
-      "media/stream/user_media_client_impl.h",
-      "media/stream/user_media_processor.cc",
-      "media/stream/user_media_processor.h",
-      "media/stream/video_track_adapter.cc",
-      "media/stream/video_track_adapter.h",
-      "media/stream/webaudio_media_stream_source.cc",
-      "media/stream/webaudio_media_stream_source.h",
-      "media/stream/webmediaplayer_ms.cc",
-      "media/stream/webmediaplayer_ms.h",
-      "media/stream/webmediaplayer_ms_compositor.cc",
-      "media/stream/webmediaplayer_ms_compositor.h",
-      "media/webrtc/audio_codec_factory.cc",
-      "media/webrtc/audio_codec_factory.h",
-      "media/webrtc/media_stream_remote_video_source.cc",
-      "media/webrtc/media_stream_remote_video_source.h",
-      "media/webrtc/media_stream_track_metrics.cc",
-      "media/webrtc/media_stream_track_metrics.h",
-      "media/webrtc/media_stream_video_webrtc_sink.cc",
-      "media/webrtc/media_stream_video_webrtc_sink.h",
-      "media/webrtc/peer_connection_dependency_factory.cc",
-      "media/webrtc/peer_connection_dependency_factory.h",
-      "media/webrtc/peer_connection_remote_audio_source.cc",
-      "media/webrtc/peer_connection_remote_audio_source.h",
-      "media/webrtc/peer_connection_tracker.cc",
-      "media/webrtc/peer_connection_tracker.h",
-      "media/webrtc/rtc_certificate.cc",
-      "media/webrtc/rtc_certificate.h",
-      "media/webrtc/rtc_certificate_generator.cc",
-      "media/webrtc/rtc_certificate_generator.h",
-      "media/webrtc/rtc_data_channel_handler.cc",
-      "media/webrtc/rtc_data_channel_handler.h",
-      "media/webrtc/rtc_dtmf_sender_handler.cc",
-      "media/webrtc/rtc_dtmf_sender_handler.h",
-      "media/webrtc/rtc_event_log_output_sink.h",
-      "media/webrtc/rtc_event_log_output_sink_proxy.cc",
-      "media/webrtc/rtc_event_log_output_sink_proxy.h",
-      "media/webrtc/rtc_peer_connection_handler.cc",
-      "media/webrtc/rtc_peer_connection_handler.h",
-      "media/webrtc/rtc_rtp_contributing_source.cc",
-      "media/webrtc/rtc_rtp_contributing_source.h",
-      "media/webrtc/rtc_rtp_parameters.cc",
-      "media/webrtc/rtc_rtp_parameters.h",
-      "media/webrtc/rtc_rtp_receiver.cc",
-      "media/webrtc/rtc_rtp_receiver.h",
-      "media/webrtc/rtc_rtp_sender.cc",
-      "media/webrtc/rtc_rtp_sender.h",
-      "media/webrtc/rtc_stats.cc",
-      "media/webrtc/rtc_stats.h",
-      "media/webrtc/rtc_video_decoder.cc",
-      "media/webrtc/rtc_video_decoder.h",
-      "media/webrtc/rtc_video_decoder_factory.cc",
-      "media/webrtc/rtc_video_decoder_factory.h",
-      "media/webrtc/rtc_video_encoder.cc",
-      "media/webrtc/rtc_video_encoder.h",
-      "media/webrtc/rtc_video_encoder_factory.cc",
-      "media/webrtc/rtc_video_encoder_factory.h",
-      "media/webrtc/stun_field_trial.cc",
-      "media/webrtc/stun_field_trial.h",
-      "media/webrtc/track_observer.cc",
-      "media/webrtc/track_observer.h",
-      "media/webrtc/two_keys_adapter_map.h",
-      "media/webrtc/webrtc_audio_device_impl.cc",
-      "media/webrtc/webrtc_audio_device_impl.h",
-      "media/webrtc/webrtc_audio_device_not_impl.cc",
-      "media/webrtc/webrtc_audio_device_not_impl.h",
-      "media/webrtc/webrtc_audio_renderer.cc",
-      "media/webrtc/webrtc_audio_renderer.h",
-      "media/webrtc/webrtc_audio_sink.cc",
-      "media/webrtc/webrtc_audio_sink.h",
-      "media/webrtc/webrtc_media_stream_adapter.cc",
-      "media/webrtc/webrtc_media_stream_adapter.h",
-      "media/webrtc/webrtc_media_stream_adapter_map.cc",
-      "media/webrtc/webrtc_media_stream_adapter_map.h",
-      "media/webrtc/webrtc_media_stream_track_adapter.cc",
-      "media/webrtc/webrtc_media_stream_track_adapter.h",
-      "media/webrtc/webrtc_media_stream_track_adapter_map.cc",
-      "media/webrtc/webrtc_media_stream_track_adapter_map.h",
-      "media/webrtc/webrtc_set_remote_description_observer.cc",
-      "media/webrtc/webrtc_set_remote_description_observer.h",
-      "media/webrtc/webrtc_uma_histograms.cc",
-      "media/webrtc/webrtc_uma_histograms.h",
-      "media/webrtc/webrtc_video_capturer_adapter.cc",
-      "media/webrtc/webrtc_video_capturer_adapter.h",
-      "media/webrtc/webrtc_video_frame_adapter.cc",
-      "media/webrtc/webrtc_video_frame_adapter.h",
-      "media/webrtc_local_audio_source_provider.cc",
-      "media/webrtc_local_audio_source_provider.h",
-      "media/webrtc_logging.cc",
-      "media/webrtc_logging.h",
-      "media_capture_from_element/canvas_capture_handler.cc",
-      "media_capture_from_element/canvas_capture_handler.h",
-      "media_capture_from_element/html_audio_element_capturer_source.cc",
-      "media_capture_from_element/html_audio_element_capturer_source.h",
-      "media_capture_from_element/html_video_element_capturer_source.cc",
-      "media_capture_from_element/html_video_element_capturer_source.h",
-      "media_recorder/audio_track_encoder.cc",
-      "media_recorder/audio_track_encoder.h",
-      "media_recorder/audio_track_opus_encoder.cc",
-      "media_recorder/audio_track_opus_encoder.h",
-      "media_recorder/audio_track_pcm_encoder.cc",
-      "media_recorder/audio_track_pcm_encoder.h",
-      "media_recorder/audio_track_recorder.cc",
-      "media_recorder/audio_track_recorder.h",
-      "media_recorder/media_recorder_handler.cc",
-      "media_recorder/media_recorder_handler.h",
-      "media_recorder/vea_encoder.cc",
-      "media_recorder/vea_encoder.h",
-      "media_recorder/video_track_recorder.cc",
-      "media_recorder/video_track_recorder.h",
-      "media_recorder/vpx_encoder.cc",
-      "media_recorder/vpx_encoder.h",
-      "p2p/empty_network_manager.cc",
-      "p2p/empty_network_manager.h",
-      "p2p/filtering_network_manager.cc",
-      "p2p/filtering_network_manager.h",
-      "p2p/host_address_request.cc",
-      "p2p/host_address_request.h",
-      "p2p/ipc_network_manager.cc",
-      "p2p/ipc_network_manager.h",
-      "p2p/ipc_socket_factory.cc",
-      "p2p/ipc_socket_factory.h",
-      "p2p/network_list_observer.h",
-      "p2p/network_manager_uma.cc",
-      "p2p/network_manager_uma.h",
-      "p2p/port_allocator.cc",
-      "p2p/port_allocator.h",
-      "p2p/socket_client.h",
-      "p2p/socket_client_delegate.h",
-      "p2p/socket_client_impl.cc",
-      "p2p/socket_client_impl.h",
-      "p2p/socket_dispatcher.cc",
-      "p2p/socket_dispatcher.h",
+      "media/pepper/pepper_to_video_track_adapter.cc",
+      "media/pepper/pepper_to_video_track_adapter.h",
+      "media/pepper/video_track_to_pepper_adapter.cc",
+      "media/pepper/video_track_to_pepper_adapter.h",
+      "pepper/pepper_media_stream_audio_track_host.cc",
+      "pepper/pepper_media_stream_audio_track_host.h",
+      "pepper/pepper_media_stream_track_host_base.cc",
+      "pepper/pepper_media_stream_track_host_base.h",
+      "pepper/pepper_media_stream_video_track_host.cc",
+      "pepper/pepper_media_stream_video_track_host.h",
+      "pepper/pepper_video_destination_host.cc",
+      "pepper/pepper_video_destination_host.h",
+      "pepper/pepper_video_source_host.cc",
+      "pepper/pepper_video_source_host.h",
     ]
+  }
 
-    if (enable_plugins) {
-      sources += [
-        "media/pepper/pepper_to_video_track_adapter.cc",
-        "media/pepper/pepper_to_video_track_adapter.h",
-        "media/pepper/video_track_to_pepper_adapter.cc",
-        "media/pepper/video_track_to_pepper_adapter.h",
-        "pepper/pepper_media_stream_audio_track_host.cc",
-        "pepper/pepper_media_stream_audio_track_host.h",
-        "pepper/pepper_media_stream_track_host_base.cc",
-        "pepper/pepper_media_stream_track_host_base.h",
-        "pepper/pepper_media_stream_video_track_host.cc",
-        "pepper/pepper_media_stream_video_track_host.h",
-        "pepper/pepper_video_destination_host.cc",
-        "pepper/pepper_video_destination_host.h",
-        "pepper/pepper_video_source_host.cc",
-        "pepper/pepper_video_source_host.h",
-      ]
-    }
-
-    deps += [
-      "//crypto",
-      "//jingle:jingle_glue",
-      "//third_party/libvpx",
-      "//third_party/opus",
-      "//third_party/webrtc/api:libjingle_logging_api",
-      "//third_party/webrtc/api:libjingle_peerconnection_api",
-      "//third_party/webrtc/api:optional",
-      "//third_party/webrtc/api:rtc_stats_api",
-      "//third_party/webrtc/api:video_frame_api",
-      "//third_party/webrtc/api:video_frame_api_i420",
-      "//third_party/webrtc/api/audio:aec3_factory",
-      "//third_party/webrtc/api/audio_codecs:audio_codecs_api",
-      "//third_party/webrtc/api/audio_codecs/L16:audio_decoder_L16",
-      "//third_party/webrtc/api/audio_codecs/L16:audio_encoder_L16",
-      "//third_party/webrtc/api/audio_codecs/g711:audio_decoder_g711",
-      "//third_party/webrtc/api/audio_codecs/g711:audio_encoder_g711",
-      "//third_party/webrtc/api/audio_codecs/g722:audio_decoder_g722",
-      "//third_party/webrtc/api/audio_codecs/g722:audio_encoder_g722",
-      "//third_party/webrtc/api/audio_codecs/isac:audio_decoder_isac",
-      "//third_party/webrtc/api/audio_codecs/isac:audio_encoder_isac",
-      "//third_party/webrtc/api/audio_codecs/opus:audio_decoder_opus",
-      "//third_party/webrtc/api/audio_codecs/opus:audio_encoder_opus",
-      "//third_party/webrtc/api/video_codecs:video_codecs_api",
-      "//third_party/webrtc/common_video:common_video",
-      "//third_party/webrtc/media:rtc_internal_video_codecs",
-      "//third_party/webrtc/media:rtc_media",
-      "//third_party/webrtc/media:rtc_media_base",
-      "//third_party/webrtc/modules/audio_device",
-      "//third_party/webrtc/modules/audio_processing",
-      "//third_party/webrtc/modules/audio_processing:audio_processing_statistics",
-      "//third_party/webrtc/modules/audio_processing/aec_dump",
-      "//third_party/webrtc/modules/video_coding:video_codec_interface",
-      "//third_party/webrtc/modules/video_coding:webrtc_h264",
-      "//third_party/webrtc/p2p:libstunprober",
-      "//third_party/webrtc/p2p:rtc_p2p",
-      "//third_party/webrtc/pc:libjingle_peerconnection",
-      "//third_party/webrtc/pc:peerconnection",
-      "//third_party/webrtc/pc:rtc_pc",
-      "//third_party/webrtc/pc:rtc_pc_base",
-      "//third_party/webrtc/rtc_base:rtc_base",
-      "//third_party/webrtc/rtc_base:rtc_task_queue",
-      "//third_party/webrtc/stats",
-      "//third_party/webrtc/system_wrappers",
-      "//third_party/webrtc_overrides:init_webrtc",
-    ]
-    if (rtc_use_h264) {
-      sources += [
-        "media_recorder/h264_encoder.cc",
-        "media_recorder/h264_encoder.h",
-      ]
-      deps += [ "//third_party/openh264:encoder" ]
-    }
-  } else {
+  if (rtc_use_h264) {
     sources += [
-      "media/webrtc_logging.h",
-      "media/webrtc_logging_noop.cc",
+      "media_recorder/h264_encoder.cc",
+      "media_recorder/h264_encoder.h",
     ]
+    deps += [ "//third_party/openh264:encoder" ]
   }
 
   if (enable_plugins) {
diff --git a/content/renderer/gpu/actions_parser.cc b/content/renderer/gpu/actions_parser.cc
index 5cb285a..fe0b34a 100644
--- a/content/renderer/gpu/actions_parser.cc
+++ b/content/renderer/gpu/actions_parser.cc
@@ -117,14 +117,6 @@
 
   if (source_type_.empty()) {
     source_type_ = source_type;
-
-#if defined(OS_MACOSX)
-    if (source_type == "touch") {
-      error_message_ =
-          base::StringPrintf("Mac OS does not support touch events");
-      return false;
-    }
-#endif  // defined(OS_MACOSX)
   }
 
   if (source_type_ != source_type) {
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index c7d09c3..622625df 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -142,32 +142,21 @@
                            WebURLLoadTiming* url_timing) {
   DCHECK(!load_timing.request_start.is_null());
 
-  const TimeTicks kNullTicks;
   url_timing->Initialize();
-  url_timing->SetRequestTime(
-      (load_timing.request_start - kNullTicks).InSecondsF());
-  url_timing->SetProxyStart(
-      (load_timing.proxy_resolve_start - kNullTicks).InSecondsF());
-  url_timing->SetProxyEnd(
-      (load_timing.proxy_resolve_end - kNullTicks).InSecondsF());
-  url_timing->SetDNSStart(
-      (load_timing.connect_timing.dns_start - kNullTicks).InSecondsF());
-  url_timing->SetDNSEnd(
-      (load_timing.connect_timing.dns_end - kNullTicks).InSecondsF());
-  url_timing->SetConnectStart(
-      (load_timing.connect_timing.connect_start - kNullTicks).InSecondsF());
-  url_timing->SetConnectEnd(
-      (load_timing.connect_timing.connect_end - kNullTicks).InSecondsF());
-  url_timing->SetSSLStart(
-      (load_timing.connect_timing.ssl_start - kNullTicks).InSecondsF());
-  url_timing->SetSSLEnd(
-      (load_timing.connect_timing.ssl_end - kNullTicks).InSecondsF());
-  url_timing->SetSendStart((load_timing.send_start - kNullTicks).InSecondsF());
-  url_timing->SetSendEnd((load_timing.send_end - kNullTicks).InSecondsF());
-  url_timing->SetReceiveHeadersEnd(
-      (load_timing.receive_headers_end - kNullTicks).InSecondsF());
-  url_timing->SetPushStart((load_timing.push_start - kNullTicks).InSecondsF());
-  url_timing->SetPushEnd((load_timing.push_end - kNullTicks).InSecondsF());
+  url_timing->SetRequestTime(load_timing.request_start);
+  url_timing->SetProxyStart(load_timing.proxy_resolve_start);
+  url_timing->SetProxyEnd(load_timing.proxy_resolve_end);
+  url_timing->SetDNSStart(load_timing.connect_timing.dns_start);
+  url_timing->SetDNSEnd(load_timing.connect_timing.dns_end);
+  url_timing->SetConnectStart(load_timing.connect_timing.connect_start);
+  url_timing->SetConnectEnd(load_timing.connect_timing.connect_end);
+  url_timing->SetSSLStart(load_timing.connect_timing.ssl_start);
+  url_timing->SetSSLEnd(load_timing.connect_timing.ssl_end);
+  url_timing->SetSendStart(load_timing.send_start);
+  url_timing->SetSendEnd(load_timing.send_end);
+  url_timing->SetReceiveHeadersEnd(load_timing.receive_headers_end);
+  url_timing->SetPushStart(load_timing.push_start);
+  url_timing->SetPushEnd(load_timing.push_end);
 }
 
 net::RequestPriority ConvertWebKitPriorityToNetPriority(
@@ -1237,11 +1226,8 @@
   if (!info.load_timing.receive_headers_end.is_null()) {
     WebURLLoadTiming timing;
     PopulateURLLoadTiming(info.load_timing, &timing);
-    const TimeTicks kNullTicks;
-    timing.SetWorkerStart(
-        (info.service_worker_start_time - kNullTicks).InSecondsF());
-    timing.SetWorkerReady(
-        (info.service_worker_ready_time - kNullTicks).InSecondsF());
+    timing.SetWorkerStart(info.service_worker_start_time);
+    timing.SetWorkerReady(info.service_worker_ready_time);
     response->SetLoadTiming(timing);
   }
 
diff --git a/content/renderer/media/stream/media_stream_constraints_util.cc b/content/renderer/media/stream/media_stream_constraints_util.cc
index bcc96ef2..52a4824e 100644
--- a/content/renderer/media/stream/media_stream_constraints_util.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util.cc
@@ -9,6 +9,7 @@
 
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "content/public/common/media_stream_request.h"
 #include "content/renderer/media/stream/media_stream_constraints_util_sets.h"
 #include "content/renderer/media/stream/media_stream_constraints_util_video_device.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -327,11 +328,15 @@
     const blink::WebString& device_id,
     const media::VideoCaptureFormats& formats,
     media::VideoFacingMode facing_mode,
-    bool is_device_capture) {
+    bool is_device_capture,
+    const base::Optional<std::string>& group_id) {
   blink::WebMediaStreamSource::Capabilities capabilities;
-  capabilities.device_id = device_id;
-  if (is_device_capture)
+  capabilities.device_id = std::move(device_id);
+  if (is_device_capture) {
     capabilities.facing_mode = ToWebFacingMode(facing_mode);
+    if (group_id)
+      capabilities.group_id = blink::WebString::FromUTF8(*group_id);
+  }
   if (!formats.empty()) {
     int max_width = 1;
     int max_height = 1;
diff --git a/content/renderer/media/stream/media_stream_constraints_util.h b/content/renderer/media/stream/media_stream_constraints_util.h
index fcd85ec2..0e40d07 100644
--- a/content/renderer/media/stream/media_stream_constraints_util.h
+++ b/content/renderer/media/stream/media_stream_constraints_util.h
@@ -373,10 +373,12 @@
 // This method computes capabilities for a video source based on the given
 // |formats|. |facing_mode| is valid only in case of video device capture.
 blink::WebMediaStreamSource::Capabilities CONTENT_EXPORT
-ComputeCapabilitiesForVideoSource(const blink::WebString& device_id,
-                                  const media::VideoCaptureFormats& formats,
-                                  media::VideoFacingMode facing_mode,
-                                  bool is_device_capture);
+ComputeCapabilitiesForVideoSource(
+    const blink::WebString& device_id,
+    const media::VideoCaptureFormats& formats,
+    media::VideoFacingMode facing_mode,
+    bool is_device_capture,
+    const base::Optional<std::string>& group_id = base::nullopt);
 
 }  // namespace content
 
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index cc173a1..c4642c9 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -812,7 +812,8 @@
     source.SetCapabilities(ComputeCapabilitiesForVideoSource(
         blink::WebString::FromUTF8(device.id),
         *current_request_info_->GetNativeVideoFormats(device.id),
-        device.video_facing, current_request_info_->is_video_device_capture()));
+        device.video_facing, current_request_info_->is_video_device_capture(),
+        device.group_id));
     local_sources_.push_back(source);
   }
   return source;
diff --git a/content/renderer/media/webrtc_logging_noop.cc b/content/renderer/media/webrtc_logging_noop.cc
deleted file mode 100644
index 00bfdef..0000000
--- a/content/renderer/media/webrtc_logging_noop.cc
+++ /dev/null
@@ -1,11 +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/renderer/media/webrtc_logging.h"
-
-namespace content {
-
-void WebRtcLogMessage(const std::string& message) {}
-
-}  // namespace content
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index a0be375f..3e30a5e3 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -554,11 +554,7 @@
       base::TimeTicks::Now(), info.url_request.HttpMethod().Latin1(),
       GetRequestBodyForWebURLRequest(info.url_request), source_location,
       should_check_main_world_csp, false /* started_from_context_menu */,
-      info.url_request.HasUserGesture(),
-      info.url_request.GetSuggestedFilename().has_value()
-          ? base::Optional<std::string>(
-                info.url_request.GetSuggestedFilename()->Utf8())
-          : base::nullopt);
+      info.url_request.HasUserGesture());
 }
 
 WebFrameLoadType NavigationTypeToLoadType(
@@ -5967,10 +5963,7 @@
       info.url_request.CheckForBrowserSideNavigation() &&
       // No need to dispatch beforeunload if the frame has not committed a
       // navigation and contains an empty initial document.
-      (has_accessed_initial_document_ || !current_history_item_.IsNull()) &&
-      // Don't dispatch beforeunload if the navigation might end up as a
-      // download.
-      !info.url_request.GetSuggestedFilename().has_value();
+      (has_accessed_initial_document_ || !current_history_item_.IsNull());
 
   if (should_dispatch_before_unload) {
     // Execute the BeforeUnload event. If asked not to proceed or the frame is
@@ -6406,11 +6399,6 @@
                                   : content::Referrer();
   params.disposition = RenderViewImpl::NavigationPolicyToDisposition(policy);
   params.triggering_event_info = info.triggering_event_info;
-  params.suggested_filename =
-      info.url_request.GetSuggestedFilename().has_value()
-          ? base::Optional<std::string>(
-                info.url_request.GetSuggestedFilename()->Utf8())
-          : base::nullopt;
 
   if (IsBrowserInitiated(pending_navigation_params_.get())) {
     // This is necessary to preserve the should_replace_current_entry value on
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index 6ef8d1fd..a355de34 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -770,10 +770,6 @@
   params.should_replace_current_entry = should_replace_current_entry;
   params.user_gesture = request.HasUserGesture();
   params.triggering_event_info = blink::WebTriggeringEventInfo::kUnknown;
-  params.suggested_filename =
-      request.GetSuggestedFilename().has_value()
-          ? base::Optional<std::string>(request.GetSuggestedFilename()->Utf8())
-          : base::nullopt;
 
   Send(new FrameHostMsg_OpenURL(routing_id_, params));
 }
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index f6ae808..424f38f 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -966,7 +966,14 @@
       static_cast<blink::WebEffectiveConnectionType>(
           prefs.low_priority_iframes_threshold));
 
-  settings->SetPictureInPictureEnabled(prefs.picture_in_picture_enabled);
+  // TODO(crbug.com/806249): Remove this once Picture-in-Picture is compatible
+  // without GPU compositing.
+  RenderThreadImpl* render_thread = RenderThreadImpl::current();
+  if (render_thread && render_thread->IsGpuCompositingDisabled()) {
+    settings->SetPictureInPictureEnabled(false);
+  } else {
+    settings->SetPictureInPictureEnabled(prefs.picture_in_picture_enabled);
+  }
 
 #if defined(OS_MACOSX)
   settings->SetDoubleTapToZoomEnabled(true);
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index e2831bb..61a248f6 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -402,7 +402,6 @@
   load_url_params.is_renderer_initiated = params.is_renderer_initiated;
   load_url_params.should_replace_current_entry =
       params.should_replace_current_entry;
-  load_url_params.suggested_filename = params.suggested_filename;
 
   if (params.uses_post) {
     load_url_params.load_type = NavigationController::LOAD_TYPE_HTTP_POST;
diff --git a/content/shell/test_runner/web_frame_test_client.cc b/content/shell/test_runner/web_frame_test_client.cc
index 28bf873..f5c58ed 100644
--- a/content/shell/test_runner/web_frame_test_client.cc
+++ b/content/shell/test_runner/web_frame_test_client.cc
@@ -386,15 +386,6 @@
 void WebFrameTestClient::DidStartProvisionalLoad(
     blink::WebDocumentLoader* document_loader,
     blink::WebURLRequest& request) {
-  if (request.GetSuggestedFilename().has_value() &&
-      test_runner()->shouldWaitUntilExternalURLLoad()) {
-    delegate_->PrintMessage(
-        std::string("Downloading URL with suggested filename \"") +
-        request.GetSuggestedFilename()->Utf8() + "\"\n");
-    delegate_->PostTask(base::BindRepeating(&WebTestDelegate::TestFinished,
-                                            base::Unretained(delegate_)));
-  }
-
   // PlzNavigate
   // A provisional load notification is received when a frame navigation is
   // sent to the browser. We don't want to log it again during commit.
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 7f4738f..4a33896c 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -174,6 +174,18 @@
     "../public/test/web_contents_binding_set_test_binder.h",
     "../public/test/web_contents_tester.cc",
     "../public/test/web_contents_tester.h",
+    "../renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc",
+    "../renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h",
+    "../renderer/media/webrtc/mock_data_channel_impl.cc",
+    "../renderer/media/webrtc/mock_data_channel_impl.h",
+    "../renderer/media/webrtc/mock_peer_connection_dependency_factory.cc",
+    "../renderer/media/webrtc/mock_peer_connection_dependency_factory.h",
+    "../renderer/media/webrtc/mock_peer_connection_impl.cc",
+    "../renderer/media/webrtc/mock_peer_connection_impl.h",
+    "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.cc",
+    "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h",
+    "../renderer/media/webrtc/test/webrtc_stats_report_obtainer.cc",
+    "../renderer/media/webrtc/test/webrtc_stats_report_obtainer.h",
     "accessibility_browser_test_utils.cc",
     "accessibility_browser_test_utils.h",
     "appcache_test_helper.cc",
@@ -339,6 +351,14 @@
     "//storage/common",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/webrtc/api:libjingle_peerconnection_api",
+    "//third_party/webrtc/api:rtc_stats_api",
+    "//third_party/webrtc/media:rtc_media_base",
+    "//third_party/webrtc/modules/video_capture",
+    "//third_party/webrtc/pc:libjingle_peerconnection",
+    "//third_party/webrtc/rtc_base:rtc_base_approved",
+    "//third_party/webrtc/stats:rtc_stats",
+    "//third_party/webrtc_overrides:init_webrtc",
     "//tools/v8_context_snapshot:v8_context_snapshot",
     "//ui/accessibility:ax_enums_mojo",
     "//ui/base",
@@ -389,34 +409,6 @@
     ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "../renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc",
-      "../renderer/media/stream/mock_mojo_media_stream_dispatcher_host.h",
-      "../renderer/media/webrtc/mock_data_channel_impl.cc",
-      "../renderer/media/webrtc/mock_data_channel_impl.h",
-      "../renderer/media/webrtc/mock_peer_connection_dependency_factory.cc",
-      "../renderer/media/webrtc/mock_peer_connection_dependency_factory.h",
-      "../renderer/media/webrtc/mock_peer_connection_impl.cc",
-      "../renderer/media/webrtc/mock_peer_connection_impl.h",
-      "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.cc",
-      "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h",
-      "../renderer/media/webrtc/test/webrtc_stats_report_obtainer.cc",
-      "../renderer/media/webrtc/test/webrtc_stats_report_obtainer.h",
-    ]
-
-    deps += [
-      "//third_party/webrtc/api:libjingle_peerconnection_api",
-      "//third_party/webrtc/api:rtc_stats_api",
-      "//third_party/webrtc/media:rtc_media_base",
-      "//third_party/webrtc/modules/video_capture",
-      "//third_party/webrtc/pc:libjingle_peerconnection",
-      "//third_party/webrtc/rtc_base:rtc_base_approved",
-      "//third_party/webrtc/stats:rtc_stats",
-      "//third_party/webrtc_overrides:init_webrtc",
-    ]
-  }
-
   if (use_glib) {
     configs += [ "//build/config/linux:glib" ]
   }
@@ -838,6 +830,28 @@
     "../browser/web_contents_binding_set_browsertest.cc",
     "../browser/web_package/web_package_request_handler_browsertest.cc",
     "../browser/webkit_browsertest.cc",
+    "../browser/webrtc/webrtc_add_stream_no_deadlock_browsertest.cc",
+    "../browser/webrtc/webrtc_audio_browsertest.cc",
+    "../browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc",
+    "../browser/webrtc/webrtc_browsertest.cc",
+    "../browser/webrtc/webrtc_capture_from_element_browsertest.cc",
+    "../browser/webrtc/webrtc_constraints_browsertest.cc",
+    "../browser/webrtc/webrtc_content_browsertest_base.cc",
+    "../browser/webrtc/webrtc_content_browsertest_base.h",
+    "../browser/webrtc/webrtc_data_browsertest.cc",
+    "../browser/webrtc/webrtc_datachannel_browsertest.cc",
+    "../browser/webrtc/webrtc_depth_capture_browsertest.cc",
+    "../browser/webrtc/webrtc_getusermedia_browsertest.cc",
+    "../browser/webrtc/webrtc_image_capture_browsertest.cc",
+    "../browser/webrtc/webrtc_internals_browsertest.cc",
+    "../browser/webrtc/webrtc_ip_permissions_browsertest.cc",
+    "../browser/webrtc/webrtc_media_recorder_browsertest.cc",
+    "../browser/webrtc/webrtc_stress_pause_browsertest.cc",
+    "../browser/webrtc/webrtc_stress_resolution_switch_browsertest.cc",
+    "../browser/webrtc/webrtc_stress_source_switch_browsertest.cc",
+    "../browser/webrtc/webrtc_video_capture_browsertest.cc",
+    "../browser/webrtc/webrtc_webcam_browsertest.cc",
+    "../browser/webrtc/webrtc_webcam_browsertest.h",
     "../browser/webui/web_ui_mojo_browsertest.cc",
     "../renderer/accessibility/render_accessibility_impl_browsertest.cc",
     "../renderer/blink_platform_audio_hardware_browsertest.cc",
@@ -891,6 +905,7 @@
     "//content/public/browser",
     "//content/public/child",
     "//content/public/common",
+    "//content/public/common:buildflags",
     "//content/public/gpu",
     "//content/public/renderer",
     "//content/renderer:for_content_tests",
@@ -1094,36 +1109,6 @@
         [ "../browser/compositor/image_transport_factory_browsertest.cc" ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "../browser/webrtc/webrtc_audio_browsertest.cc",
-      "../browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc",
-      "../browser/webrtc/webrtc_browsertest.cc",
-      "../browser/webrtc/webrtc_capture_from_element_browsertest.cc",
-      "../browser/webrtc/webrtc_constraints_browsertest.cc",
-      "../browser/webrtc/webrtc_content_browsertest_base.cc",
-      "../browser/webrtc/webrtc_content_browsertest_base.h",
-      "../browser/webrtc/webrtc_data_browsertest.cc",
-      "../browser/webrtc/webrtc_datachannel_browsertest.cc",
-      "../browser/webrtc/webrtc_depth_capture_browsertest.cc",
-      "../browser/webrtc/webrtc_getusermedia_browsertest.cc",
-      "../browser/webrtc/webrtc_image_capture_browsertest.cc",
-      "../browser/webrtc/webrtc_internals_browsertest.cc",
-      "../browser/webrtc/webrtc_ip_permissions_browsertest.cc",
-      "../browser/webrtc/webrtc_media_recorder_browsertest.cc",
-      "../browser/webrtc/webrtc_stress_pause_browsertest.cc",
-      "../browser/webrtc/webrtc_stress_resolution_switch_browsertest.cc",
-      "../browser/webrtc/webrtc_stress_source_switch_browsertest.cc",
-      "../browser/webrtc/webrtc_video_capture_browsertest.cc",
-      "../browser/webrtc/webrtc_webcam_browsertest.cc",
-      "../browser/webrtc/webrtc_webcam_browsertest.h",
-    ]
-    deps += [
-      "//content/public/common:buildflags",
-      "//testing/perf",
-    ]
-  }
-
   if (enable_plugins) {
     sources += [
       "../browser/plugin_service_impl_browsertest.cc",
@@ -1369,6 +1354,7 @@
     "../browser/manifest/manifest_icon_downloader_unittest.cc",
     "../browser/manifest/manifest_icon_selector_unittest.cc",
     "../browser/media/audible_metrics_unittest.cc",
+    "../browser/media/audio_input_stream_broker_unittest.cc",
     "../browser/media/audio_output_stream_broker_unittest.cc",
     "../browser/media/audio_stream_monitor_unittest.cc",
     "../browser/media/capture/audio_mirroring_manager_unittest.cc",
@@ -1458,6 +1444,11 @@
     "../browser/renderer_host/media/video_capture_manager_unittest.cc",
     "../browser/renderer_host/media/video_capture_unittest.cc",
     "../browser/renderer_host/overscroll_controller_unittest.cc",
+    "../browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc",
+    "../browser/renderer_host/p2p/socket_host_tcp_unittest.cc",
+    "../browser/renderer_host/p2p/socket_host_test_utils.cc",
+    "../browser/renderer_host/p2p/socket_host_test_utils.h",
+    "../browser/renderer_host/p2p/socket_host_udp_unittest.cc",
     "../browser/renderer_host/render_process_host_unittest.cc",
     "../browser/renderer_host/render_view_host_unittest.cc",
     "../browser/renderer_host/render_widget_host_input_event_router_unittest.cc",
@@ -1528,6 +1519,8 @@
     "../browser/web_package/signed_exchange_header_parser_unittest.cc",
     "../browser/web_package/signed_exchange_header_unittest.cc",
     "../browser/web_package/signed_exchange_signature_verifier_unittest.cc",
+    "../browser/webrtc/webrtc_internals_message_handler_unittest.cc",
+    "../browser/webrtc/webrtc_internals_unittest.cc",
     "../browser/websockets/websocket_manager_unittest.cc",
     "../browser/webui/url_data_manager_backend_unittest.cc",
     "../browser/webui/web_ui_data_source_unittest.cc",
@@ -1621,9 +1614,65 @@
     "../renderer/media/mojo_audio_output_ipc_unittest.cc",
     "../renderer/media/render_media_log_unittest.cc",
     "../renderer/media/renderer_webaudiodevice_impl_unittest.cc",
+    "../renderer/media/stream/media_stream_audio_processor_unittest.cc",
+    "../renderer/media/stream/media_stream_audio_unittest.cc",
+    "../renderer/media/stream/media_stream_constraints_util_audio_unittest.cc",
+    "../renderer/media/stream/media_stream_constraints_util_sets_unittest.cc",
+    "../renderer/media/stream/media_stream_constraints_util_unittest.cc",
+    "../renderer/media/stream/media_stream_constraints_util_video_content_unittest.cc",
+    "../renderer/media/stream/media_stream_constraints_util_video_device_unittest.cc",
+    "../renderer/media/stream/media_stream_device_observer_unittest.cc",
+    "../renderer/media/stream/media_stream_video_capturer_source_unittest.cc",
+    "../renderer/media/stream/media_stream_video_renderer_sink_unittest.cc",
+    "../renderer/media/stream/media_stream_video_source_unittest.cc",
+    "../renderer/media/stream/media_stream_video_track_unittest.cc",
+    "../renderer/media/stream/mock_constraint_factory.cc",
+    "../renderer/media/stream/mock_constraint_factory.h",
+    "../renderer/media/stream/mock_media_stream_registry.cc",
+    "../renderer/media/stream/mock_media_stream_registry.h",
+    "../renderer/media/stream/mock_media_stream_video_sink.cc",
+    "../renderer/media/stream/mock_media_stream_video_sink.h",
+    "../renderer/media/stream/mock_media_stream_video_source.cc",
+    "../renderer/media/stream/mock_media_stream_video_source.h",
+    "../renderer/media/stream/processed_local_audio_source_unittest.cc",
+    "../renderer/media/stream/user_media_client_impl_unittest.cc",
+    "../renderer/media/stream/video_track_adapter_unittest.cc",
+    "../renderer/media/stream/webmediaplayer_ms_unittest.cc",
     "../renderer/media/video_capture_impl_manager_unittest.cc",
     "../renderer/media/video_capture_impl_unittest.cc",
+    "../renderer/media/webrtc/media_stream_remote_video_source_unittest.cc",
+    "../renderer/media/webrtc/media_stream_track_metrics_unittest.cc",
+    "../renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc",
+    "../renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc",
+    "../renderer/media/webrtc/peer_connection_tracker_unittest.cc",
+    "../renderer/media/webrtc/rtc_data_channel_handler_unittest.cc",
+    "../renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc",
+    "../renderer/media/webrtc/rtc_rtp_parameters_unittest.cc",
+    "../renderer/media/webrtc/rtc_rtp_receiver_unittest.cc",
+    "../renderer/media/webrtc/rtc_rtp_sender_unittest.cc",
+    "../renderer/media/webrtc/rtc_stats_unittest.cc",
+    "../renderer/media/webrtc/rtc_video_decoder_unittest.cc",
+    "../renderer/media/webrtc/rtc_video_encoder_unittest.cc",
+    "../renderer/media/webrtc/stun_field_trial_unittest.cc",
+    "../renderer/media/webrtc/two_keys_adapter_map_unittest.cc",
+    "../renderer/media/webrtc/webrtc_audio_renderer_unittest.cc",
+    "../renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc",
+    "../renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc",
+    "../renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc",
+    "../renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc",
+    "../renderer/media/webrtc/webrtc_set_remote_description_observer_unittest.cc",
+    "../renderer/media/webrtc/webrtc_uma_histograms_unittest.cc",
+    "../renderer/media/webrtc/webrtc_video_capturer_adapter_unittest.cc",
+    "../renderer/media/webrtc_local_audio_source_provider_unittest.cc",
+    "../renderer/media_capture_from_element/canvas_capture_handler_unittest.cc",
+    "../renderer/media_capture_from_element/html_audio_element_capturer_source_unittest.cc",
+    "../renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc",
+    "../renderer/media_recorder/audio_track_recorder_unittest.cc",
+    "../renderer/media_recorder/media_recorder_handler_unittest.cc",
+    "../renderer/media_recorder/video_track_recorder_unittest.cc",
     "../renderer/notifications/notification_data_conversions_unittest.cc",
+    "../renderer/p2p/filtering_network_manager_unittest.cc",
+    "../renderer/p2p/ipc_network_manager_unittest.cc",
     "../renderer/peripheral_content_heuristic_unittest.cc",
     "../renderer/render_thread_impl_unittest.cc",
     "../renderer/render_widget_unittest.cc",
@@ -1738,6 +1787,7 @@
     "//services/device/public/cpp/generic_sensor",
     "//services/device/public/cpp/test:test_support",
     "//services/device/public/mojom",
+    "//services/device/public/mojom",
     "//services/file:lib",
     "//services/file/public/mojom",
     "//services/metrics/public/cpp:ukm_builders",
@@ -1760,8 +1810,24 @@
     "//third_party/blink/public:blink",
     "//third_party/icu",
     "//third_party/leveldatabase",
+    "//third_party/libyuv",
     "//third_party/metrics_proto",
+    "//third_party/opus",
     "//third_party/re2",
+    "//third_party/webrtc/api:libjingle_peerconnection_api",
+    "//third_party/webrtc/api:rtc_stats_api",
+    "//third_party/webrtc/api:video_frame_api",
+    "//third_party/webrtc/api:video_frame_api_i420",
+    "//third_party/webrtc/api/video_codecs:video_codecs_api",
+    "//third_party/webrtc/media:rtc_media",
+    "//third_party/webrtc/modules/desktop_capture:primitives",
+    "//third_party/webrtc/modules/video_capture",
+    "//third_party/webrtc/modules/video_coding:video_codec_interface",
+    "//third_party/webrtc/pc:libjingle_peerconnection",
+    "//third_party/webrtc/rtc_base:rtc_base",
+    "//third_party/webrtc/stats:rtc_stats_test_utils",
+    "//third_party/webrtc_overrides",
+    "//third_party/webrtc_overrides:init_webrtc",
     "//third_party/widevine/cdm:headers",
     "//ui/accessibility",
     "//ui/base:test_support",
@@ -1779,6 +1845,7 @@
     "//ui/gl",
     "//ui/gl:test_support",
     "//ui/latency:test_support",
+    "//ui/shell_dialogs:shell_dialogs",
   ]
 
   data_deps = [
@@ -1796,8 +1863,6 @@
   }
 
   if (enable_plugins) {
-    # Put WebRTC-related plugins sources in the "enable_webrtc &&
-    # enable_plugins" section below.
     sources += [
       "../browser/plugin_service_impl_unittest.cc",
       "../browser/renderer_host/pepper/browser_ppapi_host_test.cc",
@@ -1806,6 +1871,8 @@
       "../browser/renderer_host/pepper/pepper_gamepad_host_unittest.cc",
       "../browser/renderer_host/pepper/pepper_printing_host_unittest.cc",
       "../browser/renderer_host/pepper/quota_reservation_unittest.cc",
+      "../renderer/media/pepper/pepper_to_video_track_adapter_unittest.cc",
+      "../renderer/media/pepper/video_track_to_pepper_adapter_unittest.cc",
       "../renderer/pepper/event_conversion_unittest.cc",
       "../renderer/pepper/host_var_tracker_unittest.cc",
       "../renderer/pepper/mock_resource.h",
@@ -1827,121 +1894,22 @@
     ]
   }
 
-  if (enable_webrtc) {
-    # Put WebRTC-related plugins sources in the "enable_webrtc &&
-    # enable_plugins" section below.
-    sources += [
-      "../browser/renderer_host/p2p/socket_host_tcp_server_unittest.cc",
-      "../browser/renderer_host/p2p/socket_host_tcp_unittest.cc",
-      "../browser/renderer_host/p2p/socket_host_test_utils.cc",
-      "../browser/renderer_host/p2p/socket_host_test_utils.h",
-      "../browser/renderer_host/p2p/socket_host_udp_unittest.cc",
-      "../browser/webrtc/webrtc_internals_message_handler_unittest.cc",
-      "../browser/webrtc/webrtc_internals_unittest.cc",
-      "../renderer/media/stream/media_stream_audio_processor_unittest.cc",
-      "../renderer/media/stream/media_stream_audio_unittest.cc",
-      "../renderer/media/stream/media_stream_constraints_util_audio_unittest.cc",
-      "../renderer/media/stream/media_stream_constraints_util_sets_unittest.cc",
-      "../renderer/media/stream/media_stream_constraints_util_unittest.cc",
-      "../renderer/media/stream/media_stream_constraints_util_video_content_unittest.cc",
-      "../renderer/media/stream/media_stream_constraints_util_video_device_unittest.cc",
-      "../renderer/media/stream/media_stream_device_observer_unittest.cc",
-      "../renderer/media/stream/media_stream_video_capturer_source_unittest.cc",
-      "../renderer/media/stream/media_stream_video_renderer_sink_unittest.cc",
-      "../renderer/media/stream/media_stream_video_source_unittest.cc",
-      "../renderer/media/stream/media_stream_video_track_unittest.cc",
-      "../renderer/media/stream/mock_constraint_factory.cc",
-      "../renderer/media/stream/mock_constraint_factory.h",
-      "../renderer/media/stream/mock_media_stream_registry.cc",
-      "../renderer/media/stream/mock_media_stream_registry.h",
-      "../renderer/media/stream/mock_media_stream_video_sink.cc",
-      "../renderer/media/stream/mock_media_stream_video_sink.h",
-      "../renderer/media/stream/mock_media_stream_video_source.cc",
-      "../renderer/media/stream/mock_media_stream_video_source.h",
-      "../renderer/media/stream/processed_local_audio_source_unittest.cc",
-      "../renderer/media/stream/user_media_client_impl_unittest.cc",
-      "../renderer/media/stream/video_track_adapter_unittest.cc",
-      "../renderer/media/stream/webmediaplayer_ms_unittest.cc",
-      "../renderer/media/webrtc/media_stream_remote_video_source_unittest.cc",
-      "../renderer/media/webrtc/media_stream_track_metrics_unittest.cc",
-      "../renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc",
-      "../renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc",
-      "../renderer/media/webrtc/peer_connection_tracker_unittest.cc",
-      "../renderer/media/webrtc/rtc_data_channel_handler_unittest.cc",
-      "../renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc",
-      "../renderer/media/webrtc/rtc_rtp_parameters_unittest.cc",
-      "../renderer/media/webrtc/rtc_rtp_receiver_unittest.cc",
-      "../renderer/media/webrtc/rtc_rtp_sender_unittest.cc",
-      "../renderer/media/webrtc/rtc_stats_unittest.cc",
-      "../renderer/media/webrtc/rtc_video_decoder_unittest.cc",
-      "../renderer/media/webrtc/rtc_video_encoder_unittest.cc",
-      "../renderer/media/webrtc/stun_field_trial_unittest.cc",
-      "../renderer/media/webrtc/two_keys_adapter_map_unittest.cc",
-      "../renderer/media/webrtc/webrtc_audio_renderer_unittest.cc",
-      "../renderer/media/webrtc/webrtc_media_stream_adapter_map_unittest.cc",
-      "../renderer/media/webrtc/webrtc_media_stream_adapter_unittest.cc",
-      "../renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc",
-      "../renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc",
-      "../renderer/media/webrtc/webrtc_set_remote_description_observer_unittest.cc",
-      "../renderer/media/webrtc/webrtc_uma_histograms_unittest.cc",
-      "../renderer/media/webrtc/webrtc_video_capturer_adapter_unittest.cc",
-      "../renderer/media/webrtc_local_audio_source_provider_unittest.cc",
-      "../renderer/media_capture_from_element/canvas_capture_handler_unittest.cc",
-      "../renderer/media_capture_from_element/html_audio_element_capturer_source_unittest.cc",
-      "../renderer/media_capture_from_element/html_video_element_capturer_source_unittest.cc",
+  if (is_chromecast) {
+    sources -= [
       "../renderer/media_recorder/audio_track_recorder_unittest.cc",
       "../renderer/media_recorder/media_recorder_handler_unittest.cc",
       "../renderer/media_recorder/video_track_recorder_unittest.cc",
-      "../renderer/p2p/filtering_network_manager_unittest.cc",
-      "../renderer/p2p/ipc_network_manager_unittest.cc",
-    ]
-    deps += [
-      "//services/device/public/mojom",
-      "//third_party/libyuv",
-      "//third_party/opus",
-      "//third_party/webrtc/api:libjingle_peerconnection_api",
-      "//third_party/webrtc/api:rtc_stats_api",
-      "//third_party/webrtc/api:video_frame_api",
-      "//third_party/webrtc/api:video_frame_api_i420",
-      "//third_party/webrtc/api/video_codecs:video_codecs_api",
-      "//third_party/webrtc/media:rtc_media",
-      "//third_party/webrtc/modules/desktop_capture:primitives",
-      "//third_party/webrtc/modules/video_capture",
-      "//third_party/webrtc/modules/video_coding:video_codec_interface",
-      "//third_party/webrtc/pc:libjingle_peerconnection",
-      "//third_party/webrtc/rtc_base:rtc_base",
-      "//third_party/webrtc/stats:rtc_stats_test_utils",
-      "//third_party/webrtc_overrides",
-      "//third_party/webrtc_overrides:init_webrtc",
-      "//ui/shell_dialogs:shell_dialogs",
-    ]
-
-    if (is_linux || is_mac || is_win) {
-      sources +=
-          [ "../browser/media/capture/desktop_capture_device_unittest.cc" ]
-      deps += [ "//third_party/webrtc/modules/desktop_capture" ]
-    }
-
-    if (is_chromecast) {
-      sources -= [
-        "../renderer/media_recorder/audio_track_recorder_unittest.cc",
-        "../renderer/media_recorder/media_recorder_handler_unittest.cc",
-        "../renderer/media_recorder/video_track_recorder_unittest.cc",
-      ]
-    }
-  }
-
-  if (enable_webrtc && enable_plugins) {
-    sources += [
-      "../renderer/media/pepper/pepper_to_video_track_adapter_unittest.cc",
-      "../renderer/media/pepper/video_track_to_pepper_adapter_unittest.cc",
     ]
   }
 
   # Screen capture unit tests.
   if (is_linux || is_mac || is_win) {
-    deps += [ "//third_party/libyuv" ]
+    deps += [
+      "//third_party/libyuv",
+      "//third_party/webrtc/modules/desktop_capture",
+    ]
     sources += [
+      "../browser/media/capture/desktop_capture_device_unittest.cc",
       "../browser/media/capture/frame_sink_video_capture_device_unittest.cc",
     ]
     if (use_aura) {
diff --git a/content/test/data/media/peerconnection-add-stream.html b/content/test/data/media/peerconnection-add-stream.html
new file mode 100644
index 0000000..1eefa16f
--- /dev/null
+++ b/content/test/data/media/peerconnection-add-stream.html
@@ -0,0 +1,33 @@
+<html>
+<body id="body">
+    <h1>PeerConnection add stream test</h1>
+</body>
+<script type="text/javascript" src="webrtc_test_utilities.js"></script>
+<script type="text/javascript">
+
+function runTest() {
+  let pc1 = new RTCPeerConnection();
+  var stream;
+  navigator.mediaDevices.getUserMedia({audio: true, video: true})
+      .then(theStream => {
+    stream = theStream;
+    pc1.addStream(stream);
+    return pc1.createOffer({offerToReceiveAudio: true, offerToReceiveVideo: true});
+  }).then(offer => {
+    pc1.setLocalDescription(offer);
+    return offer;
+  }).then(offer => {
+    const pc2 = new RTCPeerConnection();
+    pc2.setRemoteDescription(pc1.localDescription);
+    pc2.addStream(stream);
+    pc2.createAnswer();
+  }).then(() => {
+    reportTestSuccess();
+  }).catch(e => {
+    console.error(`Unexpected error: ${e}`);
+    failTest(`Unexpected error: ${e}`);
+  });
+}
+
+</script>
+</html>
diff --git a/extensions/browser/extension_navigation_throttle.cc b/extensions/browser/extension_navigation_throttle.cc
index 9ff926c..880a1b8b 100644
--- a/extensions/browser/extension_navigation_throttle.cc
+++ b/extensions/browser/extension_navigation_throttle.cc
@@ -85,13 +85,6 @@
           navigation_handle()->GetStartingSiteInstance()->GetSiteURL());
 
   if (!url_has_extension_scheme && !current_frame_is_extension_process) {
-    // Relax this restriction for navigations that will result in downloads.
-    // See https://crbug.com/714373.
-    if (target_origin.scheme() == kExtensionScheme &&
-        navigation_handle()->GetSuggestedFilename().has_value()) {
-      return content::NavigationThrottle::PROCEED;
-    }
-
     // Relax this restriction for apps that use <webview>.  See
     // https://crbug.com/652077.
     bool has_webview_permission =
diff --git a/headless/test/headless_render_browsertest.cc b/headless/test/headless_render_browsertest.cc
index 9716436..56429bdc 100644
--- a/headless/test/headless_render_browsertest.cc
+++ b/headless/test/headless_render_browsertest.cc
@@ -510,7 +510,13 @@
                     "http://www.example.com/FAIL"));
   }
 };
-HEADLESS_RENDER_BROWSERTEST(ServerRedirectToFailure);
+// Flaky on Linux. https://crbug.com/839747
+#if defined(OS_LINUX)
+#define MAYBE_HEADLESS_RENDER_BROWSERTEST DISABLED_HEADLESS_RENDER_BROWSERTEST
+#else
+#define MAYBE_HEADLESS_RENDER_BROWSERTEST HEADLESS_RENDER_BROWSERTEST
+#endif
+MAYBE_HEADLESS_RENDER_BROWSERTEST(ServerRedirectToFailure);
 
 class ServerRedirectRelativeChain : public HeadlessRenderTest {
  private:
@@ -1001,7 +1007,8 @@
                 ElementsAre("http://www.example.com/"));
   }
 };
-HEADLESS_RENDER_BROWSERTEST(RedirectInvalidUrl);
+// Flaky on Linux. https://crbug.com/839747
+MAYBE_HEADLESS_RENDER_BROWSERTEST(RedirectInvalidUrl);
 
 class RedirectKeepsFragment : public HeadlessRenderTest {
  private:
@@ -1164,11 +1171,6 @@
 };
 
 // Flaky on Linux. https://crbug.com/839747
-#if defined(OS_LINUX)
-#define MAYBE_HEADLESS_RENDER_BROWSERTEST DISABLED_HEADLESS_RENDER_BROWSERTEST
-#else
-#define MAYBE_HEADLESS_RENDER_BROWSERTEST HEADLESS_RENDER_BROWSERTEST
-#endif
 MAYBE_HEADLESS_RENDER_BROWSERTEST(CookieSetFromJs_NoCookies);
 
 class CookieUpdatedFromJs : public HeadlessRenderTest {
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index 361b287..8da4ae53 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -523,7 +523,8 @@
 void PaymentRequest::PopulatePaymentMethodCache(
     std::vector<std::unique_ptr<IOSPaymentInstrument>> native_app_instruments) {
   const std::vector<autofill::CreditCard*>& credit_cards_to_suggest =
-      personal_data_manager_->GetCreditCardsToSuggest();
+      personal_data_manager_->GetCreditCardsToSuggest(
+          /*include_server_cards=*/true);
 
   // Return early if the user has no stored credit cards or installed payment
   // apps.
diff --git a/ios/chrome/browser/services/gcm/ios_chrome_gcm_profile_service_factory.cc b/ios/chrome/browser/services/gcm/ios_chrome_gcm_profile_service_factory.cc
index 6895a989..6a9939f 100644
--- a/ios/chrome/browser/services/gcm/ios_chrome_gcm_profile_service_factory.cc
+++ b/ios/chrome/browser/services/gcm/ios_chrome_gcm_profile_service_factory.cc
@@ -67,6 +67,7 @@
       browser_state->GetPrefs(), browser_state->GetStatePath(),
       browser_state->GetRequestContext(), ::GetChannel(),
       GetProductCategoryForSubtypes(),
+      ios::SigninManagerFactory::GetForBrowserState(browser_state),
       base::WrapUnique(new ProfileIdentityProvider(
           ios::SigninManagerFactory::GetForBrowserState(browser_state),
           OAuth2TokenServiceFactory::GetForBrowserState(browser_state),
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
index cbab300..7307341 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
@@ -86,6 +86,7 @@
   bool IsContextSecure() override;
   bool ShouldShowSigninPromo() override;
   bool IsAutofillSupported() override;
+  bool AreServerCardsSupported() override;
   void ExecuteCommand(int id) override;
 
  private:
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index 72cc98c..45159d7a6 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -227,4 +227,8 @@
   return true;
 }
 
+bool ChromeAutofillClientIOS::AreServerCardsSupported() {
+  return true;
+}
+
 }  // namespace autofill
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index 383001c..3afa7a2 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -4115,6 +4115,16 @@
   return _isOffTheRecord;
 }
 
+- (BOOL)isFindInPageAvailable {
+  Tab* tab = [_model currentTab];
+  if (!tab) {
+    return NO;
+  }
+
+  auto* helper = FindTabHelper::FromWebState(tab.webState);
+  return (helper && helper->CurrentPageSupportsFindInPage());
+}
+
 - (NSUInteger)tabsCount {
   return [_model count];
 }
diff --git a/ios/chrome/browser/ui/key_commands_provider.h b/ios/chrome/browser/ui/key_commands_provider.h
index 22972d7..3cac5459 100644
--- a/ios/chrome/browser/ui/key_commands_provider.h
+++ b/ios/chrome/browser/ui/key_commands_provider.h
@@ -18,6 +18,10 @@
 // Whether the current profile is off-the-record.
 - (BOOL)isOffTheRecord;
 
+// Whether the Find in Page is available on current page. For example it's not
+// supported on NTP and other native content pages.
+- (BOOL)isFindInPageAvailable;
+
 // Returns the current number of tabs.
 - (NSUInteger)tabsCount;
 
diff --git a/ios/chrome/browser/ui/key_commands_provider.mm b/ios/chrome/browser/ui/key_commands_provider.mm
index 97d09d2..44170e6 100644
--- a/ios/chrome/browser/ui/key_commands_provider.mm
+++ b/ios/chrome/browser/ui/key_commands_provider.mm
@@ -114,6 +114,33 @@
   // List the commands that only appear when there is at least a tab. When they
   // appear, they are in the HUD since they have titles.
   if (hasTabs) {
+    if ([consumer isFindInPageAvailable]) {
+      [keyCommands addObjectsFromArray:@[
+
+        [UIKeyCommand
+            cr_keyCommandWithInput:@"f"
+                     modifierFlags:UIKeyModifierCommand
+                             title:l10n_util::GetNSStringWithFixup(
+                                       IDS_IOS_TOOLS_MENU_FIND_IN_PAGE)
+                            action:^{
+                              [weakDispatcher showFindInPage];
+                            }],
+        [UIKeyCommand cr_keyCommandWithInput:@"g"
+                               modifierFlags:UIKeyModifierCommand
+                                       title:nil
+                                      action:^{
+                                        [weakDispatcher findNextStringInPage];
+                                      }],
+        [UIKeyCommand
+            cr_keyCommandWithInput:@"g"
+                     modifierFlags:UIKeyModifierCommand | UIKeyModifierShift
+                             title:nil
+                            action:^{
+                              [weakDispatcher findPreviousStringInPage];
+                            }]
+      ]];
+    }
+
     [keyCommands addObjectsFromArray:@[
       [UIKeyCommand cr_keyCommandWithInput:@"l"
                              modifierFlags:UIKeyModifierCommand
@@ -137,26 +164,6 @@
                           action:^{
                             [weakDispatcher bookmarkPage];
                           }],
-      [UIKeyCommand cr_keyCommandWithInput:@"f"
-                             modifierFlags:UIKeyModifierCommand
-                                     title:l10n_util::GetNSStringWithFixup(
-                                               IDS_IOS_TOOLS_MENU_FIND_IN_PAGE)
-                                    action:^{
-                                      [weakDispatcher showFindInPage];
-                                    }],
-      [UIKeyCommand cr_keyCommandWithInput:@"g"
-                             modifierFlags:UIKeyModifierCommand
-                                     title:nil
-                                    action:^{
-                                      [weakDispatcher findNextStringInPage];
-                                    }],
-      [UIKeyCommand
-          cr_keyCommandWithInput:@"g"
-                   modifierFlags:UIKeyModifierCommand | UIKeyModifierShift
-                           title:nil
-                          action:^{
-                            [weakDispatcher findPreviousStringInPage];
-                          }],
       [UIKeyCommand cr_keyCommandWithInput:@"r"
                              modifierFlags:UIKeyModifierCommand
                                      title:l10n_util::GetNSStringWithFixup(
diff --git a/ios/chrome/browser/ui/key_commands_provider_unittest.mm b/ios/chrome/browser/ui/key_commands_provider_unittest.mm
index 4a6e58b..091b493 100644
--- a/ios/chrome/browser/ui/key_commands_provider_unittest.mm
+++ b/ios/chrome/browser/ui/key_commands_provider_unittest.mm
@@ -65,6 +65,7 @@
 
   // Tabs.
   [[[mockConsumer expect] andReturnUnsignedInteger:1] tabsCount];
+  [[[mockConsumer expect] andReturnBool:YES] isFindInPageAvailable];
   NSUInteger numberOfKeyCommandsWithTabs =
       [[provider keyCommandsForConsumer:mockConsumer
                      baseViewController:nil
@@ -82,6 +83,7 @@
 
   // Not editing text.
   [[[mockConsumer expect] andReturnUnsignedInteger:1] tabsCount];
+  [[[mockConsumer expect] andReturnBool:YES] isFindInPageAvailable];
   NSUInteger numberOfKeyCommandsWhenNotEditingText =
       [[provider keyCommandsForConsumer:mockConsumer
                      baseViewController:nil
@@ -90,6 +92,7 @@
 
   // Editing text.
   [[[mockConsumer expect] andReturnUnsignedInteger:1] tabsCount];
+  [[[mockConsumer expect] andReturnBool:YES] isFindInPageAvailable];
   NSUInteger numberOfKeyCommandsWhenEditingText =
       [[provider keyCommandsForConsumer:mockConsumer
                      baseViewController:nil
@@ -100,4 +103,31 @@
             numberOfKeyCommandsWhenNotEditingText);
 }
 
+TEST_F(KeyCommandsProviderTest, MoreKeyboardCommandsWhenFindInPageAvailable) {
+  KeyCommandsProvider* provider = [[KeyCommandsProvider alloc] init];
+  id mockConsumer =
+      [OCMockObject mockForProtocol:@protocol(KeyCommandsPlumbing)];
+  id<ApplicationCommands, BrowserCommands, OmniboxFocuser> dispatcher = nil;
+
+  // No Find in Page.
+  [[[mockConsumer expect] andReturnUnsignedInteger:1] tabsCount];
+  [[[mockConsumer expect] andReturnBool:NO] isFindInPageAvailable];
+  NSUInteger numberOfKeyCommandsWithoutFIP =
+      [[provider keyCommandsForConsumer:mockConsumer
+                     baseViewController:nil
+                             dispatcher:dispatcher
+                            editingText:NO] count];
+
+  // Tabs.
+  [[[mockConsumer expect] andReturnUnsignedInteger:1] tabsCount];
+  [[[mockConsumer expect] andReturnBool:YES] isFindInPageAvailable];
+  NSUInteger numberOfKeyCommandsWithFIP =
+      [[provider keyCommandsForConsumer:mockConsumer
+                     baseViewController:nil
+                             dispatcher:dispatcher
+                            editingText:NO] count];
+
+  EXPECT_GT(numberOfKeyCommandsWithFIP, numberOfKeyCommandsWithoutFIP);
+}
+
 }  // namespace
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
index b8d772e..9b1573b 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
@@ -216,6 +216,11 @@
   NSMutableAttributedString* attributing_display_string_;
 
   OmniboxPopupProvider* popup_provider_;  // weak
+
+  // A flag that is set whenever any input or copy/paste event happened in the
+  // omnibox while it was focused. Used to count event "user focuses the omnibox
+  // to view the complete URL and immediately defocuses it".
+  BOOL omnibox_interacted_while_focused_;
 };
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_VIEW_IOS_H_
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
index f0cee02..e1043e3 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
@@ -397,6 +397,9 @@
 }
 
 void OmniboxViewIOS::OnDidBeginEditing() {
+  // Reset the changed flag.
+  omnibox_interacted_while_focused_ = NO;
+
   // If Open from Clipboard offers a suggestion, the popup may be opened when
   // |OnSetFocus| is called on the model. The state of the popup is saved early
   // to ignore that case.
@@ -449,6 +452,11 @@
   // Blow away any in-progress edits.
   RevertAll();
   DCHECK(![field_ hasAutocompleteText]);
+
+  if (!omnibox_interacted_while_focused_) {
+    RecordAction(
+        UserMetricsAction("Mobile_FocusedDefocusedOmnibox_WithNoAction"));
+  }
 }
 
 bool OmniboxViewIOS::OnWillChange(NSRange range, NSString* new_text) {
@@ -536,6 +544,8 @@
 }
 
 void OmniboxViewIOS::OnDidChange(bool processing_user_event) {
+  omnibox_interacted_while_focused_ = YES;
+
   // Sanitize pasted text.
   if (model()->is_pasting()) {
     base::string16 pastedText = base::SysNSStringToUTF16([field_ text]);
@@ -611,6 +621,7 @@
 }
 
 bool OmniboxViewIOS::OnCopy() {
+  omnibox_interacted_while_focused_ = YES;
   UIPasteboard* board = [UIPasteboard generalPasteboard];
   NSString* selectedText = nil;
   NSInteger start_location = 0;
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
index 35cecb199..0851e46 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
@@ -78,6 +78,7 @@
   bool IsContextSecure() override;
   bool ShouldShowSigninPromo() override;
   bool IsAutofillSupported() override;
+  bool AreServerCardsSupported() override;
   void ExecuteCommand(int id) override;
 
  private:
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index 9e1bc36..6b96091 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -152,4 +152,8 @@
   return true;
 }
 
+bool WebViewAutofillClientIOS::AreServerCardsSupported() {
+  return true;
+}
+
 }  // namespace autofill
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index 9d399aec..bdf1e4b 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -87,6 +87,8 @@
   // Handles presentation of JavaScript dialogs.
   std::unique_ptr<ios_web_view::WebViewJavaScriptDialogPresenter>
       _javaScriptDialogPresenter;
+  std::map<std::string, web::WebState::ScriptCommandCallback>
+      _scriptCommandCallbacks;
 }
 
 // Redefine these properties as readwrite to define setters, which send KVO
@@ -429,13 +431,15 @@
         return [handler webView:weakSelf handleScriptCommand:command];
       });
 
-  _webState->AddScriptCommandCallback(callback,
-                                      base::SysNSStringToUTF8(commandPrefix));
+  std::string stdCommandPrefix = base::SysNSStringToUTF8(commandPrefix);
+  _webState->AddScriptCommandCallback(callback, stdCommandPrefix);
+  _scriptCommandCallbacks[stdCommandPrefix] = callback;
 }
 
 - (void)removeScriptCommandHandlerForCommandPrefix:(NSString*)commandPrefix {
-  _webState->RemoveScriptCommandCallback(
-      base::SysNSStringToUTF8(commandPrefix));
+  std::string stdCommandPrefix = base::SysNSStringToUTF8(commandPrefix);
+  _webState->RemoveScriptCommandCallback(stdCommandPrefix);
+  _scriptCommandCallbacks.erase(stdCommandPrefix);
 }
 
 #pragma mark - Translation
@@ -518,6 +522,9 @@
     if (_webStateObserver) {
       _webState->RemoveObserver(_webStateObserver.get());
     }
+    for (const auto& pair : _scriptCommandCallbacks) {
+      _webState->RemoveScriptCommandCallback(pair.first);
+    }
 
     // The web view provided by the old |_webState| has been added as a subview.
     // It must be removed and replaced with a new |_webState|'s web view, which
@@ -551,6 +558,10 @@
       std::make_unique<ios_web_view::WebViewJavaScriptDialogPresenter>(self,
                                                                        nullptr);
 
+  for (const auto& pair : _scriptCommandCallbacks) {
+    _webState->AddScriptCommandCallback(pair.second, pair.first);
+  }
+
   _scrollView.proxy = _webState.get()->GetWebViewProxy().scrollViewProxy;
 
   if (_translationController) {
diff --git a/ios/web_view/test/web_view_restorable_state_inttest.mm b/ios/web_view/test/web_view_restorable_state_inttest.mm
index 946141c..c4a8400 100644
--- a/ios/web_view/test/web_view_restorable_state_inttest.mm
+++ b/ios/web_view/test/web_view_restorable_state_inttest.mm
@@ -14,24 +14,6 @@
 
 namespace ios_web_view {
 
-namespace {
-
-// Creates a new web view and restores its state from |source_web_view|.
-CWVWebView* CreateWebViewWithState(CWVWebView* source_web_view) {
-  NSMutableData* data = [[NSMutableData alloc] init];
-  NSKeyedArchiver* archiver =
-      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
-  [source_web_view encodeRestorableStateWithCoder:archiver];
-  [archiver finishEncoding];
-  NSKeyedUnarchiver* unarchiver =
-      [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
-  CWVWebView* result = test::CreateWebView();
-  [result decodeRestorableStateWithCoder:unarchiver];
-  return result;
-}
-
-}  // namespace
-
 // Tests encodeRestorableStateWithCoder: and decodeRestorableStateWithCoder:
 // methods.
 typedef ios_web_view::WebViewIntTest WebViewRestorableStateTest;
@@ -51,7 +33,8 @@
   ASSERT_FALSE([web_view_ canGoForward]);
 
   // Create second web view and restore its state from the first web view.
-  CWVWebView* restored_web_view = CreateWebViewWithState(web_view_);
+  CWVWebView* restored_web_view = test::CreateWebView();
+  test::CopyWebViewState(web_view_, restored_web_view);
 
   // Verify that the state has been restored correctly.
   EXPECT_NSEQ(@"about:blank",
diff --git a/ios/web_view/test/web_view_script_command_inttest.mm b/ios/web_view/test/web_view_script_command_inttest.mm
index 442f056..ac9fee3 100644
--- a/ios/web_view/test/web_view_script_command_inttest.mm
+++ b/ios/web_view/test/web_view_script_command_inttest.mm
@@ -78,4 +78,43 @@
   [web_view_ removeScriptCommandHandlerForCommandPrefix:@"test"];
 }
 
+// Tests that added script commands are still valid after state restoration.
+// Tests the same thing as TestScriptCommand() after state restoration.
+TEST_F(WebViewScriptCommandTest, TestScriptCommandAfterStateRestoration) {
+  CWVFakeScriptCommandHandler* handler =
+      [[CWVFakeScriptCommandHandler alloc] init];
+  [web_view_ addScriptCommandHandler:handler commandPrefix:@"test"];
+
+  CWVWebView* source_web_view = test::CreateWebView();
+  test::CopyWebViewState(source_web_view, web_view_);
+
+  // Uses GetUrlForPageWithHtmlBody() instead of simply using about:blank
+  // because it looks __gCrWeb may not be available on about:blank.
+  // TODO(crbug.com/836114): Analyze why.
+  NSURL* url = net::NSURLWithGURL(GetUrlForPageWithHtmlBody(""));
+  ASSERT_TRUE(test::LoadUrl(web_view_, url));
+  ASSERT_TRUE(test::WaitForWebViewLoadCompletionOrTimeout(web_view_));
+
+  NSString* script =
+      @"__gCrWeb.message.invokeOnHost("
+      @"{'command': 'test.command1', 'key1': 'value1', 'key2': 42});";
+  NSError* script_error = nil;
+  test::EvaluateJavaScript(web_view_, script, &script_error);
+  ASSERT_NSEQ(nil, script_error);
+
+  EXPECT_TRUE(testing::WaitUntilConditionOrTimeout(
+      testing::kWaitForJSCompletionTimeout, ^{
+        return handler.lastReceivedCommand != nil;
+      }));
+
+  EXPECT_NSEQ(@"test.command1",
+              handler.lastReceivedCommand.content[@"command"]);
+  EXPECT_NSEQ(@"value1", handler.lastReceivedCommand.content[@"key1"]);
+  EXPECT_NSEQ(@42, handler.lastReceivedCommand.content[@"key2"]);
+  EXPECT_NSEQ(url, handler.lastReceivedCommand.mainDocumentURL);
+  EXPECT_FALSE(handler.lastReceivedCommand.userInteracting);
+
+  [web_view_ removeScriptCommandHandlerForCommandPrefix:@"test"];
+}
+
 }  // namespace ios_web_view
diff --git a/ios/web_view/test/web_view_test_util.h b/ios/web_view/test/web_view_test_util.h
index 3be1f61..c56989a 100644
--- a/ios/web_view/test/web_view_test_util.h
+++ b/ios/web_view/test/web_view_test_util.h
@@ -42,6 +42,11 @@
 bool WaitForWebViewLoadCompletionOrTimeout(CWVWebView* web_view)
     WARN_UNUSED_RESULT;
 
+// Copies the state of |source_web_view| to |destination_web_view| using state
+// restoration.
+void CopyWebViewState(CWVWebView* source_web_view,
+                      CWVWebView* destination_web_view);
+
 }  // namespace test
 }  // namespace ios_web_view
 
diff --git a/ios/web_view/test/web_view_test_util.mm b/ios/web_view/test/web_view_test_util.mm
index 4d67901..fedd60a 100644
--- a/ios/web_view/test/web_view_test_util.mm
+++ b/ios/web_view/test/web_view_test_util.mm
@@ -81,5 +81,17 @@
   });
 }
 
+void CopyWebViewState(CWVWebView* source_web_view,
+                      CWVWebView* destination_web_view) {
+  NSMutableData* data = [[NSMutableData alloc] init];
+  NSKeyedArchiver* archiver =
+      [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
+  [source_web_view encodeRestorableStateWithCoder:archiver];
+  [archiver finishEncoding];
+  NSKeyedUnarchiver* unarchiver =
+      [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
+  [destination_web_view decodeRestorableStateWithCoder:unarchiver];
+}
+
 }  // namespace test
 }  // namespace ios_web_view
diff --git a/jingle/BUILD.gn b/jingle/BUILD.gn
index a6785cf..7d481e3 100644
--- a/jingle/BUILD.gn
+++ b/jingle/BUILD.gn
@@ -3,212 +3,183 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
-import("//media/media_options.gni")
 import("//testing/test.gni")
 
-if (enable_webrtc || !is_android) {
-  static_library("jingle_glue") {
-    sources = [
+static_library("jingle_glue") {
+  sources = [
+    "glue/chrome_async_socket.cc",
+    "glue/chrome_async_socket.h",
+    "glue/fake_ssl_client_socket.cc",
+    "glue/fake_ssl_client_socket.h",
+    "glue/resolving_client_socket_factory.h",
+    "glue/task_pump.cc",
+    "glue/task_pump.h",
+    "glue/thread_wrapper.cc",
+    "glue/thread_wrapper.h",
+    "glue/utils.cc",
+    "glue/utils.h",
+    "glue/xmpp_client_socket_factory.cc",
+    "glue/xmpp_client_socket_factory.h",
+  ]
+  public_deps = [
+    "//third_party/webrtc_overrides",
+  ]
+  deps = [
+    "//base",
+    "//base/third_party/dynamic_annotations",
+    "//net",
+    "//services/network:network_service",
+  ]
+
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+  if (is_nacl) {
+    sources -= [
       "glue/chrome_async_socket.cc",
-      "glue/chrome_async_socket.h",
-      "glue/fake_ssl_client_socket.cc",
-      "glue/fake_ssl_client_socket.h",
-      "glue/resolving_client_socket_factory.h",
-      "glue/task_pump.cc",
-      "glue/task_pump.h",
-      "glue/thread_wrapper.cc",
-      "glue/thread_wrapper.h",
-      "glue/utils.cc",
-      "glue/utils.h",
       "glue/xmpp_client_socket_factory.cc",
-      "glue/xmpp_client_socket_factory.h",
     ]
-    public_deps = [
-      "//third_party/webrtc_overrides",
-    ]
-    deps = [
-      "//base",
-      "//base/third_party/dynamic_annotations",
-      "//net",
-      "//services/network:network_service",
-    ]
-
-    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
-    if (is_nacl) {
-      sources -= [
-        "glue/chrome_async_socket.cc",
-        "glue/xmpp_client_socket_factory.cc",
-      ]
-      deps -= [ "//services/network:network_service" ]
-    }
+    deps -= [ "//services/network:network_service" ]
   }
+}
 
-  # A library for sending and receiving peer-issued notifications.
-  static_library("notifier") {
-    sources = [
-      "notifier/base/const_communicator.h",
-      "notifier/base/gaia_constants.cc",
-      "notifier/base/gaia_constants.h",
-      "notifier/base/gaia_token_pre_xmpp_auth.cc",
-      "notifier/base/gaia_token_pre_xmpp_auth.h",
-      "notifier/base/notification_method.cc",
-      "notifier/base/notification_method.h",
-      "notifier/base/notifier_options.cc",
-      "notifier/base/notifier_options.h",
-      "notifier/base/notifier_options_util.cc",
-      "notifier/base/notifier_options_util.h",
-      "notifier/base/server_information.cc",
-      "notifier/base/server_information.h",
-      "notifier/base/weak_xmpp_client.cc",
-      "notifier/base/weak_xmpp_client.h",
-      "notifier/base/xmpp_connection.cc",
-      "notifier/base/xmpp_connection.h",
-      "notifier/communicator/connection_settings.cc",
-      "notifier/communicator/connection_settings.h",
-      "notifier/communicator/login.cc",
-      "notifier/communicator/login.h",
-      "notifier/communicator/login_settings.cc",
-      "notifier/communicator/login_settings.h",
-      "notifier/communicator/single_login_attempt.cc",
-      "notifier/communicator/single_login_attempt.h",
-      "notifier/listener/non_blocking_push_client.cc",
-      "notifier/listener/non_blocking_push_client.h",
-      "notifier/listener/notification_constants.cc",
-      "notifier/listener/notification_constants.h",
-      "notifier/listener/notification_defines.cc",
-      "notifier/listener/notification_defines.h",
-      "notifier/listener/push_client.cc",
-      "notifier/listener/push_client.h",
-      "notifier/listener/push_client_observer.cc",
-      "notifier/listener/push_client_observer.h",
-      "notifier/listener/push_notifications_listen_task.cc",
-      "notifier/listener/push_notifications_listen_task.h",
-      "notifier/listener/push_notifications_send_update_task.cc",
-      "notifier/listener/push_notifications_send_update_task.h",
-      "notifier/listener/push_notifications_subscribe_task.cc",
-      "notifier/listener/push_notifications_subscribe_task.h",
-      "notifier/listener/send_ping_task.cc",
-      "notifier/listener/send_ping_task.h",
-      "notifier/listener/xml_element_util.cc",
-      "notifier/listener/xml_element_util.h",
-      "notifier/listener/xmpp_push_client.cc",
-      "notifier/listener/xmpp_push_client.h",
-    ]
-    defines = [ "_CRT_SECURE_NO_WARNINGS" ]
+# A library for sending and receiving peer-issued notifications.
+static_library("notifier") {
+  sources = [
+    "notifier/base/const_communicator.h",
+    "notifier/base/gaia_constants.cc",
+    "notifier/base/gaia_constants.h",
+    "notifier/base/gaia_token_pre_xmpp_auth.cc",
+    "notifier/base/gaia_token_pre_xmpp_auth.h",
+    "notifier/base/notification_method.cc",
+    "notifier/base/notification_method.h",
+    "notifier/base/notifier_options.cc",
+    "notifier/base/notifier_options.h",
+    "notifier/base/notifier_options_util.cc",
+    "notifier/base/notifier_options_util.h",
+    "notifier/base/server_information.cc",
+    "notifier/base/server_information.h",
+    "notifier/base/weak_xmpp_client.cc",
+    "notifier/base/weak_xmpp_client.h",
+    "notifier/base/xmpp_connection.cc",
+    "notifier/base/xmpp_connection.h",
+    "notifier/communicator/connection_settings.cc",
+    "notifier/communicator/connection_settings.h",
+    "notifier/communicator/login.cc",
+    "notifier/communicator/login.h",
+    "notifier/communicator/login_settings.cc",
+    "notifier/communicator/login_settings.h",
+    "notifier/communicator/single_login_attempt.cc",
+    "notifier/communicator/single_login_attempt.h",
+    "notifier/listener/non_blocking_push_client.cc",
+    "notifier/listener/non_blocking_push_client.h",
+    "notifier/listener/notification_constants.cc",
+    "notifier/listener/notification_constants.h",
+    "notifier/listener/notification_defines.cc",
+    "notifier/listener/notification_defines.h",
+    "notifier/listener/push_client.cc",
+    "notifier/listener/push_client.h",
+    "notifier/listener/push_client_observer.cc",
+    "notifier/listener/push_client_observer.h",
+    "notifier/listener/push_notifications_listen_task.cc",
+    "notifier/listener/push_notifications_listen_task.h",
+    "notifier/listener/push_notifications_send_update_task.cc",
+    "notifier/listener/push_notifications_send_update_task.h",
+    "notifier/listener/push_notifications_subscribe_task.cc",
+    "notifier/listener/push_notifications_subscribe_task.h",
+    "notifier/listener/send_ping_task.cc",
+    "notifier/listener/send_ping_task.h",
+    "notifier/listener/xml_element_util.cc",
+    "notifier/listener/xml_element_util.h",
+    "notifier/listener/xmpp_push_client.cc",
+    "notifier/listener/xmpp_push_client.h",
+  ]
+  defines = [ "_CRT_SECURE_NO_WARNINGS" ]
 
-    public_deps = [
-      "//third_party/libjingle_xmpp",
-      "//third_party/webrtc_overrides",
-    ]
-    deps = [
-      ":jingle_glue",
-      "//base",
-      "//net",
-      "//third_party/expat",
-      "//url",
-    ]
-  }
+  public_deps = [
+    "//third_party/libjingle_xmpp",
+    "//third_party/webrtc_overrides",
+  ]
+  deps = [
+    ":jingle_glue",
+    "//base",
+    "//net",
+    "//third_party/expat",
+    "//url",
+  ]
+}
 
-  static_library("notifier_test_util") {
-    testonly = true
-    sources = [
-      "notifier/base/fake_base_task.cc",
-      "notifier/base/fake_base_task.h",
-      "notifier/listener/fake_push_client.cc",
-      "notifier/listener/fake_push_client.h",
-      "notifier/listener/fake_push_client_observer.cc",
-      "notifier/listener/fake_push_client_observer.h",
-    ]
-    public_deps = [
-      ":jingle_glue",
-      ":notifier",
-    ]
-    deps = [
-      "//base",
-      "//testing/gmock",
-    ]
-  }
+static_library("notifier_test_util") {
+  testonly = true
+  sources = [
+    "notifier/base/fake_base_task.cc",
+    "notifier/base/fake_base_task.h",
+    "notifier/listener/fake_push_client.cc",
+    "notifier/listener/fake_push_client.h",
+    "notifier/listener/fake_push_client_observer.cc",
+    "notifier/listener/fake_push_client_observer.h",
+  ]
+  public_deps = [
+    ":jingle_glue",
+    ":notifier",
+  ]
+  deps = [
+    "//base",
+    "//testing/gmock",
+  ]
+}
 
-  test("jingle_unittests") {
-    sources = [
+test("jingle_unittests") {
+  sources = [
+    "glue/chrome_async_socket_unittest.cc",
+    "glue/fake_ssl_client_socket_unittest.cc",
+    "glue/jingle_glue_mock_objects.cc",
+    "glue/jingle_glue_mock_objects.h",
+    "glue/logging_unittest.cc",
+    "glue/mock_task.cc",
+    "glue/mock_task.h",
+    "glue/task_pump_unittest.cc",
+    "glue/thread_wrapper_unittest.cc",
+    "notifier/base/weak_xmpp_client_unittest.cc",
+    "notifier/base/xmpp_connection_unittest.cc",
+    "notifier/communicator/connection_settings_unittest.cc",
+    "notifier/communicator/login_settings_unittest.cc",
+    "notifier/communicator/single_login_attempt_unittest.cc",
+    "notifier/listener/non_blocking_push_client_unittest.cc",
+    "notifier/listener/notification_defines_unittest.cc",
+    "notifier/listener/push_client_unittest.cc",
+    "notifier/listener/push_notifications_send_update_task_unittest.cc",
+    "notifier/listener/push_notifications_subscribe_task_unittest.cc",
+    "notifier/listener/send_ping_task_unittest.cc",
+    "notifier/listener/xml_element_util_unittest.cc",
+    "notifier/listener/xmpp_push_client_unittest.cc",
+  ]
+
+  if (is_android || is_ios) {
+    sources -= [
+      # TODO(jrg):
+      # EXPECT_DEBUG_DEATH() uses features not enabled.
+      # Should we -std=c++0x or -std=gnu++0x?
       "glue/chrome_async_socket_unittest.cc",
-      "glue/fake_ssl_client_socket_unittest.cc",
-      "glue/jingle_glue_mock_objects.cc",
-      "glue/jingle_glue_mock_objects.h",
-      "glue/logging_unittest.cc",
-      "glue/mock_task.cc",
-      "glue/mock_task.h",
-      "glue/task_pump_unittest.cc",
-      "glue/thread_wrapper_unittest.cc",
-      "notifier/base/weak_xmpp_client_unittest.cc",
       "notifier/base/xmpp_connection_unittest.cc",
-      "notifier/communicator/connection_settings_unittest.cc",
-      "notifier/communicator/login_settings_unittest.cc",
-      "notifier/communicator/single_login_attempt_unittest.cc",
-      "notifier/listener/non_blocking_push_client_unittest.cc",
-      "notifier/listener/notification_defines_unittest.cc",
-      "notifier/listener/push_client_unittest.cc",
-      "notifier/listener/push_notifications_send_update_task_unittest.cc",
-      "notifier/listener/push_notifications_subscribe_task_unittest.cc",
-      "notifier/listener/send_ping_task_unittest.cc",
-      "notifier/listener/xml_element_util_unittest.cc",
-      "notifier/listener/xmpp_push_client_unittest.cc",
-    ]
-
-    if (is_android || is_ios) {
-      sources -= [
-        # TODO(jrg):
-        # EXPECT_DEBUG_DEATH() uses features not enabled.
-        # Should we -std=c++0x or -std=gnu++0x?
-        "glue/chrome_async_socket_unittest.cc",
-        "notifier/base/xmpp_connection_unittest.cc",
-      ]
-    }
-
-    configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
-
-    public_deps = [
-      "//third_party/libjingle_xmpp",
-      "//third_party/webrtc_overrides",
-    ]
-    deps = [
-      ":jingle_glue",
-      ":notifier",
-      ":notifier_test_util",
-      "//base",
-      "//base/test:run_all_unittests",
-      "//base/test:test_support",
-      "//net",
-      "//net:test_support",
-      "//testing/gmock",
-      "//testing/gtest",
-    ]
-  }
-} else {
-  # !enable_webrtc and is_android
-  # Stub targets as Android doesn't use libjingle when webrtc is disabled.
-  source_set("jingle_glue") {
-  }
-
-  source_set("jingle_glue_test_util") {
-  }
-
-  static_library("notifier") {
-    sources = [
-      "notifier/base/gaia_constants.cc",
-      "notifier/base/gaia_constants.h",
-      "notifier/base/notification_method.cc",
-      "notifier/base/notification_method.h",
-      "notifier/base/notifier_options.cc",
-      "notifier/base/notifier_options.h",
-    ]
-    deps = [
-      "//base",
-      "//net",
     ]
   }
 
-  source_set("notifier_test_util") {
-  }
+  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+  public_deps = [
+    "//third_party/libjingle_xmpp",
+    "//third_party/webrtc_overrides",
+  ]
+  deps = [
+    ":jingle_glue",
+    ":notifier",
+    ":notifier_test_util",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//base/test:test_support",
+    "//net",
+    "//net:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
 }
diff --git a/media/BUILD.gn b/media/BUILD.gn
index a1467fa..8960c523 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -33,7 +33,9 @@
     "ENABLE_CDM_STORAGE_ID=$enable_cdm_storage_id",
     "ENABLE_MEDIA_REMOTING=$enable_media_remoting",
     "ENABLE_MEDIA_REMOTING_RPC=$enable_media_remoting_rpc",
-    "ENABLE_WEBRTC=$enable_webrtc",
+
+    # TODO(phoglund): Remove this define from the code.
+    "ENABLE_WEBRTC=true",
     "USE_PROPRIETARY_CODECS=$proprietary_codecs",
   ]
 }
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index 35e67c9..b5b13580 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -92,6 +92,8 @@
     "audio_input_device.h",
     "audio_input_ipc.cc",
     "audio_input_ipc.h",
+    "audio_input_stream_data_interceptor.cc",
+    "audio_input_stream_data_interceptor.h",
     "audio_input_sync_writer.cc",
     "audio_input_sync_writer.h",
     "audio_io.h",
@@ -311,13 +313,6 @@
     libs += [ "media_client" ]
   }
 
-  if (enable_webrtc) {
-    sources += [
-      "audio_input_stream_data_interceptor.cc",
-      "audio_input_stream_data_interceptor.h",
-    ]
-  }
-
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
 }
 
@@ -385,6 +380,7 @@
     "audio_debug_recording_session_impl_unittest.cc",
     "audio_input_controller_unittest.cc",
     "audio_input_device_unittest.cc",
+    "audio_input_stream_data_interceptor_unittest.cc",
     "audio_input_sync_writer_unittest.cc",
     "audio_input_unittest.cc",
     "audio_manager_unittest.cc",
@@ -416,10 +412,6 @@
     "//media:media_config",
   ]
 
-  if (enable_webrtc) {
-    sources += [ "audio_input_stream_data_interceptor_unittest.cc" ]
-  }
-
   if (is_android) {
     sources += [ "android/audio_android_unittest.cc" ]
     deps += [ "//ui/gl" ]
diff --git a/media/audio/mac/coreaudio_dispatch_override.cc b/media/audio/mac/coreaudio_dispatch_override.cc
index 30b4e2c..2ac812b 100644
--- a/media/audio/mac/coreaudio_dispatch_override.cc
+++ b/media/audio/mac/coreaudio_dispatch_override.cc
@@ -16,7 +16,7 @@
 namespace {
 struct dyld_interpose_tuple {
   template <typename T>
-  dyld_interpose_tuple(const T* replacement, const T* replacee)
+  dyld_interpose_tuple(T* replacement, T* replacee)
       : replacement(reinterpret_cast<const void*>(replacement)),
         replacee(reinterpret_cast<const void*>(replacee)) {}
   const void* replacement;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index c7aa2bf1..2c6b11d 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -113,11 +113,6 @@
 // accelerator hardware to be present.
 const char kUseFakeJpegDecodeAccelerator[] = "use-fake-jpeg-decode-accelerator";
 
-// Disable hardware acceleration of mjpeg decode for captured frame, where
-// available.
-const char kDisableAcceleratedMjpegDecode[] =
-    "disable-accelerated-mjpeg-decode";
-
 // Enables support for inband text tracks in media content.
 const char kEnableInbandTextTracks[] = "enable-inband-text-tracks";
 
@@ -416,19 +411,4 @@
     "PreloadMediaEngagementData", base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
-bool IsVideoCaptureAcceleratedJpegDecodingEnabled() {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableAcceleratedMjpegDecode)) {
-    return false;
-  }
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseFakeJpegDecodeAccelerator)) {
-    return true;
-  }
-#if defined(OS_CHROMEOS)
-  return true;
-#endif
-  return false;
-}
-
 }  // namespace media
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index c9077f9e..12515dbb 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -65,7 +65,6 @@
 MEDIA_EXPORT extern const char kUseFileForFakeVideoCapture[];
 MEDIA_EXPORT extern const char kUseFileForFakeAudioCapture[];
 MEDIA_EXPORT extern const char kUseFakeJpegDecodeAccelerator[];
-MEDIA_EXPORT extern const char kDisableAcceleratedMjpegDecode[];
 
 MEDIA_EXPORT extern const char kEnableInbandTextTracks[];
 
@@ -161,8 +160,6 @@
 // audio focus duck flash should be enabled.
 MEDIA_EXPORT bool IsAudioFocusDuckFlashEnabled();
 
-MEDIA_EXPORT bool IsVideoCaptureAcceleratedJpegDecodingEnabled();
-
 }  // namespace media
 
 #endif  // MEDIA_BASE_MEDIA_SWITCHES_H_
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 0239813..acdf83e 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -130,8 +130,6 @@
     "video/video_capture_device_client.cc",
     "video/video_capture_device_client.h",
     "video/video_capture_jpeg_decoder.h",
-    "video/video_capture_jpeg_decoder_impl.cc",
-    "video/video_capture_jpeg_decoder_impl.h",
     "video/video_capture_system.h",
     "video/video_capture_system_impl.cc",
     "video/video_capture_system_impl.h",
@@ -175,7 +173,6 @@
     "//media/capture/mojom:image_capture",
     "//media/capture/mojom:image_capture_types",
     "//media/capture/mojom:video_capture",
-    "//media/mojo/clients:jpeg_decode_accelerator",
     "//media/mojo/interfaces:interfaces",
     "//services/service_manager/public/cpp",
     "//third_party/libyuv",
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 33433b8d..e301e41 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -64,8 +64,7 @@
   *y_plane_stride = dimensions.width();
   *uv_plane_stride = *y_plane_stride / 2;
 }
-
-}  // anonymous namespace
+}
 
 namespace media {
 
@@ -99,10 +98,10 @@
 VideoCaptureDeviceClient::VideoCaptureDeviceClient(
     std::unique_ptr<VideoFrameReceiver> receiver,
     scoped_refptr<VideoCaptureBufferPool> buffer_pool,
-    VideoCaptureJpegDecoderFactoryCB optional_jpeg_decoder_factory_callback)
+    const VideoCaptureJpegDecoderFactoryCB& jpeg_decoder_factory)
     : receiver_(std::move(receiver)),
-      optional_jpeg_decoder_factory_callback_(
-          std::move(optional_jpeg_decoder_factory_callback)),
+      jpeg_decoder_factory_callback_(jpeg_decoder_factory),
+      external_jpeg_decoder_initialized_(false),
       buffer_pool_(std::move(buffer_pool)),
       last_captured_pixel_format_(PIXEL_FORMAT_UNKNOWN) {
   on_started_using_gpu_cb_ =
@@ -139,7 +138,6 @@
     base::TimeTicks reference_time,
     base::TimeDelta timestamp,
     int frame_feedback_id) {
-  DFAKE_SCOPED_RECURSIVE_LOCK(call_from_producer_);
   TRACE_EVENT0("media", "VideoCaptureDeviceClient::OnIncomingCapturedData");
 
   if (last_captured_pixel_format_ != format.pixel_format) {
@@ -147,11 +145,11 @@
     last_captured_pixel_format_ = format.pixel_format;
 
     if (format.pixel_format == PIXEL_FORMAT_MJPEG &&
-        optional_jpeg_decoder_factory_callback_) {
-      external_jpeg_decoder_ =
-          std::move(optional_jpeg_decoder_factory_callback_).Run();
-      DCHECK(external_jpeg_decoder_);
-      external_jpeg_decoder_->Initialize();
+        !external_jpeg_decoder_initialized_) {
+      external_jpeg_decoder_initialized_ = true;
+      external_jpeg_decoder_ = jpeg_decoder_factory_callback_.Run();
+      if (external_jpeg_decoder_)
+        external_jpeg_decoder_->Initialize();
     }
   }
 
diff --git a/media/capture/video/video_capture_device_client.h b/media/capture/video/video_capture_device_client.h
index 778d46a1..7708de9 100644
--- a/media/capture/video/video_capture_device_client.h
+++ b/media/capture/video/video_capture_device_client.h
@@ -23,15 +23,11 @@
 class VideoCaptureJpegDecoder;
 
 using VideoCaptureJpegDecoderFactoryCB =
-    base::OnceCallback<std::unique_ptr<VideoCaptureJpegDecoder>()>;
+    base::Callback<std::unique_ptr<VideoCaptureJpegDecoder>()>;
 
 // Implementation of VideoCaptureDevice::Client that uses a buffer pool
 // to provide buffers and converts incoming data to the I420 format for
-// consumption by a given VideoFrameReceiver. If
-// |optional_jpeg_decoder_factory_callback| is provided, the
-// VideoCaptureDeviceClient will attempt to use it for decoding of MJPEG frames.
-// Otherwise, it will use libyuv to perform MJPEG to I420 conversion in
-// software.
+// consumption by a given VideoFrameReceiver.
 //
 // Methods of this class may be called from any thread, and in practice will
 // often be called on some auxiliary thread depending on the platform and the
@@ -45,7 +41,7 @@
   VideoCaptureDeviceClient(
       std::unique_ptr<VideoFrameReceiver> receiver,
       scoped_refptr<VideoCaptureBufferPool> buffer_pool,
-      VideoCaptureJpegDecoderFactoryCB optional_jpeg_decoder_factory_callback);
+      const VideoCaptureJpegDecoderFactoryCB& jpeg_decoder_factory);
   ~VideoCaptureDeviceClient() override;
 
   static Buffer MakeBufferStruct(
@@ -103,8 +99,11 @@
   const std::unique_ptr<VideoFrameReceiver> receiver_;
   std::vector<int> buffer_ids_known_by_receiver_;
 
-  VideoCaptureJpegDecoderFactoryCB optional_jpeg_decoder_factory_callback_;
+  const VideoCaptureJpegDecoderFactoryCB jpeg_decoder_factory_callback_;
   std::unique_ptr<VideoCaptureJpegDecoder> external_jpeg_decoder_;
+
+  // Whether |external_jpeg_decoder_| has been initialized.
+  bool external_jpeg_decoder_initialized_;
   base::OnceClosure on_started_using_gpu_cb_;
 
   // The pool of shared-memory buffers used for capturing.
diff --git a/media/capture/video/video_capture_jpeg_decoder.h b/media/capture/video/video_capture_jpeg_decoder.h
index e2da00d..c5b32cf 100644
--- a/media/capture/video/video_capture_jpeg_decoder.h
+++ b/media/capture/video/video_capture_jpeg_decoder.h
@@ -13,8 +13,6 @@
 
 namespace media {
 
-// All methods are allowed to be called from any thread, but calls must be
-// non-concurrently.
 class CAPTURE_EXPORT VideoCaptureJpegDecoder {
  public:
   // Enumeration of decoder status. The enumeration is published for clients to
@@ -26,7 +24,7 @@
                    // decode error.
   };
 
-  using DecodeDoneCB = base::RepeatingCallback<void(
+  using DecodeDoneCB = base::Callback<void(
       int buffer_id,
       int frame_feedback_id,
       std::unique_ptr<VideoCaptureDevice::Client::Buffer::
diff --git a/media/capture/video/video_capture_jpeg_decoder_impl.cc b/media/capture/video/video_capture_jpeg_decoder_impl.cc
deleted file mode 100644
index bb5d16f..0000000
--- a/media/capture/video/video_capture_jpeg_decoder_impl.cc
+++ /dev/null
@@ -1,260 +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 "media/capture/video/video_capture_jpeg_decoder_impl.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "media/base/media_switches.h"
-
-namespace media {
-
-VideoCaptureJpegDecoderImpl::VideoCaptureJpegDecoderImpl(
-    MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory,
-    scoped_refptr<base::SingleThreadTaskRunner> decoder_task_runner,
-    DecodeDoneCB decode_done_cb,
-    base::RepeatingCallback<void(const std::string&)> send_log_message_cb)
-    : jpeg_decoder_factory_(std::move(jpeg_decoder_factory)),
-      decoder_task_runner_(std::move(decoder_task_runner)),
-      decode_done_cb_(std::move(decode_done_cb)),
-      send_log_message_cb_(std::move(send_log_message_cb)),
-      has_received_decoded_frame_(false),
-      next_bitstream_buffer_id_(0),
-      in_buffer_id_(media::JpegDecodeAccelerator::kInvalidBitstreamBufferId),
-      decoder_status_(INIT_PENDING),
-      weak_ptr_factory_(this) {}
-
-VideoCaptureJpegDecoderImpl::~VideoCaptureJpegDecoderImpl() {
-  // |this| was set as |decoder_|'s client. |decoder_| has to be deleted on
-  // |decoder_task_runner_| before this destructor returns to ensure that it
-  // doesn't call back into its client.
-
-  if (!decoder_)
-    return;
-
-  if (decoder_task_runner_->RunsTasksInCurrentSequence()) {
-    decoder_.reset();
-    return;
-  }
-
-  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
-                            base::WaitableEvent::InitialState::NOT_SIGNALED);
-  // base::Unretained is safe because |this| will be valid until |event|
-  // is signaled.
-  decoder_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VideoCaptureJpegDecoderImpl::DestroyDecoderOnIOThread,
-                     base::Unretained(this), &event));
-  event.Wait();
-}
-
-void VideoCaptureJpegDecoderImpl::Initialize() {
-  if (!IsVideoCaptureAcceleratedJpegDecodingEnabled()) {
-    decoder_status_ = FAILED;
-    RecordInitDecodeUMA_Locked();
-    return;
-  }
-
-  decoder_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&VideoCaptureJpegDecoderImpl::FinishInitialization,
-                     weak_ptr_factory_.GetWeakPtr()));
-}
-
-VideoCaptureJpegDecoderImpl::STATUS VideoCaptureJpegDecoderImpl::GetStatus()
-    const {
-  base::AutoLock lock(lock_);
-  return decoder_status_;
-}
-
-void VideoCaptureJpegDecoderImpl::DecodeCapturedData(
-    const uint8_t* data,
-    size_t in_buffer_size,
-    const media::VideoCaptureFormat& frame_format,
-    base::TimeTicks reference_time,
-    base::TimeDelta timestamp,
-    media::VideoCaptureDevice::Client::Buffer out_buffer) {
-  DCHECK(decoder_);
-
-  TRACE_EVENT_ASYNC_BEGIN0("jpeg", "VideoCaptureJpegDecoderImpl decoding",
-                           next_bitstream_buffer_id_);
-  TRACE_EVENT0("jpeg", "VideoCaptureJpegDecoderImpl::DecodeCapturedData");
-
-  // TODO(kcwu): enqueue decode requests in case decoding is not fast enough
-  // (say, if decoding time is longer than 16ms for 60fps 4k image)
-  {
-    base::AutoLock lock(lock_);
-    if (IsDecoding_Locked()) {
-      DVLOG(1) << "Drop captured frame. Previous jpeg frame is still decoding";
-      return;
-    }
-  }
-
-  // Enlarge input buffer if necessary.
-  if (!in_shared_memory_.get() ||
-      in_buffer_size > in_shared_memory_->mapped_size()) {
-    // Reserve 2x space to avoid frequent reallocations for initial frames.
-    const size_t reserved_size = 2 * in_buffer_size;
-    in_shared_memory_.reset(new base::SharedMemory);
-    if (!in_shared_memory_->CreateAndMapAnonymous(reserved_size)) {
-      base::AutoLock lock(lock_);
-      decoder_status_ = FAILED;
-      LOG(WARNING) << "CreateAndMapAnonymous failed, size=" << reserved_size;
-      return;
-    }
-  }
-  memcpy(in_shared_memory_->memory(), data, in_buffer_size);
-
-  // No need to lock for |in_buffer_id_| since IsDecoding_Locked() is false.
-  in_buffer_id_ = next_bitstream_buffer_id_;
-  media::BitstreamBuffer in_buffer(in_buffer_id_, in_shared_memory_->handle(),
-                                   in_buffer_size);
-  // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
-  next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
-
-  // The API of |decoder_| requires us to wrap the |out_buffer| in a VideoFrame.
-  const gfx::Size dimensions = frame_format.frame_size;
-  std::unique_ptr<media::VideoCaptureBufferHandle> out_buffer_access =
-      out_buffer.handle_provider->GetHandleForInProcessAccess();
-  base::SharedMemoryHandle out_handle =
-      out_buffer.handle_provider->GetNonOwnedSharedMemoryHandleForLegacyIPC();
-  scoped_refptr<media::VideoFrame> out_frame =
-      media::VideoFrame::WrapExternalSharedMemory(
-          media::PIXEL_FORMAT_I420,          // format
-          dimensions,                        // coded_size
-          gfx::Rect(dimensions),             // visible_rect
-          dimensions,                        // natural_size
-          out_buffer_access->data(),         // data
-          out_buffer_access->mapped_size(),  // data_size
-          out_handle,                        // handle
-          0,                                 // shared_memory_offset
-          timestamp);                        // timestamp
-  if (!out_frame) {
-    base::AutoLock lock(lock_);
-    decoder_status_ = FAILED;
-    LOG(ERROR) << "DecodeCapturedData: WrapExternalSharedMemory failed";
-    return;
-  }
-  // Hold onto the buffer access handle for the lifetime of the VideoFrame, to
-  // ensure the data pointers remain valid.
-  out_frame->AddDestructionObserver(base::BindOnce(
-      [](std::unique_ptr<media::VideoCaptureBufferHandle> handle) {},
-      std::move(out_buffer_access)));
-  out_frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE,
-                                   frame_format.frame_rate);
-
-  out_frame->metadata()->SetTimeTicks(media::VideoFrameMetadata::REFERENCE_TIME,
-                                      reference_time);
-
-  media::mojom::VideoFrameInfoPtr out_frame_info =
-      media::mojom::VideoFrameInfo::New();
-  out_frame_info->timestamp = timestamp;
-  out_frame_info->pixel_format = media::PIXEL_FORMAT_I420;
-  out_frame_info->coded_size = dimensions;
-  out_frame_info->visible_rect = gfx::Rect(dimensions);
-  out_frame_info->metadata = out_frame->metadata()->GetInternalValues().Clone();
-
-  {
-    base::AutoLock lock(lock_);
-    decode_done_closure_ = base::BindOnce(
-        decode_done_cb_, out_buffer.id, out_buffer.frame_feedback_id,
-        base::Passed(&out_buffer.access_permission),
-        base::Passed(&out_frame_info));
-  }
-
-  // base::Unretained is safe because |decoder_| is deleted on
-  // |decoder_task_runner_|.
-  decoder_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&media::JpegDecodeAccelerator::Decode,
-                                base::Unretained(decoder_.get()), in_buffer,
-                                std::move(out_frame)));
-}
-
-void VideoCaptureJpegDecoderImpl::VideoFrameReady(int32_t bitstream_buffer_id) {
-  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
-  TRACE_EVENT0("jpeg", "VideoCaptureJpegDecoderImpl::VideoFrameReady");
-  if (!has_received_decoded_frame_) {
-    send_log_message_cb_.Run("Received decoded frame from Gpu Jpeg decoder");
-    has_received_decoded_frame_ = true;
-  }
-  base::AutoLock lock(lock_);
-
-  if (!IsDecoding_Locked()) {
-    LOG(ERROR) << "Got decode response while not decoding";
-    return;
-  }
-
-  if (bitstream_buffer_id != in_buffer_id_) {
-    LOG(ERROR) << "Unexpected bitstream_buffer_id " << bitstream_buffer_id
-               << ", expected " << in_buffer_id_;
-    return;
-  }
-  in_buffer_id_ = media::JpegDecodeAccelerator::kInvalidBitstreamBufferId;
-
-  std::move(decode_done_closure_).Run();
-
-  TRACE_EVENT_ASYNC_END0("jpeg", "VideoCaptureJpegDecoderImpl decoding",
-                         bitstream_buffer_id);
-}
-
-void VideoCaptureJpegDecoderImpl::NotifyError(
-    int32_t bitstream_buffer_id,
-    media::JpegDecodeAccelerator::Error error) {
-  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
-  LOG(ERROR) << "Decode error, bitstream_buffer_id=" << bitstream_buffer_id
-             << ", error=" << error;
-  send_log_message_cb_.Run("Gpu Jpeg decoder failed");
-  base::AutoLock lock(lock_);
-  decode_done_closure_.Reset();
-  decoder_status_ = FAILED;
-}
-
-void VideoCaptureJpegDecoderImpl::FinishInitialization() {
-  TRACE_EVENT0("gpu", "VideoCaptureJpegDecoderImpl::FinishInitialization");
-  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
-
-  media::mojom::JpegDecodeAcceleratorPtr remote_decoder;
-  jpeg_decoder_factory_.Run(mojo::MakeRequest(&remote_decoder));
-
-  base::AutoLock lock(lock_);
-  decoder_ = std::make_unique<media::MojoJpegDecodeAccelerator>(
-      decoder_task_runner_, remote_decoder.PassInterface());
-
-  decoder_->InitializeAsync(
-      this,
-      base::BindRepeating(&VideoCaptureJpegDecoderImpl::OnInitializationDone,
-                          weak_ptr_factory_.GetWeakPtr()));
-}
-
-void VideoCaptureJpegDecoderImpl::OnInitializationDone(bool success) {
-  TRACE_EVENT0("gpu", "VideoCaptureJpegDecoderImpl::OnInitializationDone");
-  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
-
-  base::AutoLock lock(lock_);
-  if (!success) {
-    decoder_.reset();
-    DLOG(ERROR) << "Failed to initialize JPEG decoder";
-  }
-
-  decoder_status_ = success ? INIT_PASSED : FAILED;
-  RecordInitDecodeUMA_Locked();
-}
-
-bool VideoCaptureJpegDecoderImpl::IsDecoding_Locked() const {
-  lock_.AssertAcquired();
-  return !decode_done_closure_.is_null();
-}
-
-void VideoCaptureJpegDecoderImpl::RecordInitDecodeUMA_Locked() {
-  UMA_HISTOGRAM_BOOLEAN("Media.VideoCaptureGpuJpegDecoder.InitDecodeSuccess",
-                        decoder_status_ == INIT_PASSED);
-}
-
-void VideoCaptureJpegDecoderImpl::DestroyDecoderOnIOThread(
-    base::WaitableEvent* event) {
-  DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
-  decoder_.reset();
-  event->Signal();
-}
-
-}  // namespace media
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index fb42ff5..8e03ae1 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -152,6 +152,8 @@
       "android/android_image_reader_compat.h",
       "android/android_video_decode_accelerator.cc",
       "android/android_video_decode_accelerator.h",
+      "android/android_video_encode_accelerator.cc",
+      "android/android_video_encode_accelerator.h",
       "android/android_video_surface_chooser.h",
       "android/android_video_surface_chooser_impl.cc",
       "android/android_video_surface_chooser_impl.h",
@@ -202,14 +204,8 @@
       # TODO(crbug.com/789435): This can be removed once CdmManager is removed.
       "//media/mojo:buildflags",
       "//services/service_manager/public/cpp:cpp",
+      "//third_party/libyuv",
     ]
-    if (enable_webrtc) {
-      deps += [ "//third_party/libyuv" ]
-      sources += [
-        "android/android_video_encode_accelerator.cc",
-        "android/android_video_encode_accelerator.h",
-      ]
-    }
 
     # TODO(crbug.com/789435): This is needed for AVDA to access the CDM
     # directly.  Remove this dependency after VDAs are also running as part of
diff --git a/media/media_options.gni b/media/media_options.gni
index 07c5551..17faf8d 100644
--- a/media/media_options.gni
+++ b/media/media_options.gni
@@ -71,8 +71,6 @@
   # which are encoded using HEVC require |enable_hevc_demuxing| to be enabled.
   enable_dolby_vision_demuxing = proprietary_codecs && is_chromecast
 
-  enable_webrtc = true
-
   # Enable HLS with SAMPLE-AES decryption.
   enable_hls_sample_aes = proprietary_codecs && is_chromecast
 
diff --git a/media/mojo/clients/BUILD.gn b/media/mojo/clients/BUILD.gn
index 90b391ac..5100e5c 100644
--- a/media/mojo/clients/BUILD.gn
+++ b/media/mojo/clients/BUILD.gn
@@ -79,10 +79,7 @@
 }
 
 source_set("jpeg_decode_accelerator") {
-  visibility = [
-    "//content/browser",
-    "//media/capture:capture_lib",
-  ]
+  visibility = [ "//content/browser" ]
 
   sources = [
     "mojo_jpeg_decode_accelerator.cc",
diff --git a/media/mojo/services/BUILD.gn b/media/mojo/services/BUILD.gn
index 511e8723..938b4c6 100644
--- a/media/mojo/services/BUILD.gn
+++ b/media/mojo/services/BUILD.gn
@@ -32,8 +32,6 @@
     "mojo_audio_decoder_service.h",
     "mojo_audio_input_stream.cc",
     "mojo_audio_input_stream.h",
-    "mojo_audio_input_stream_observer.cc",
-    "mojo_audio_input_stream_observer.h",
     "mojo_audio_output_stream.cc",
     "mojo_audio_output_stream.h",
     "mojo_audio_output_stream_provider.cc",
@@ -164,7 +162,6 @@
   sources = [
     "deferred_destroy_strong_binding_set_unittest.cc",
     "media_metrics_provider_unittest.cc",
-    "mojo_audio_input_stream_observer_unittest.cc",
     "mojo_audio_input_stream_unittest.cc",
     "mojo_audio_output_stream_provider_unittest.cc",
     "mojo_audio_output_stream_unittest.cc",
diff --git a/media/mojo/services/mojo_audio_input_stream_observer.cc b/media/mojo/services/mojo_audio_input_stream_observer.cc
deleted file mode 100644
index d22d092..0000000
--- a/media/mojo/services/mojo_audio_input_stream_observer.cc
+++ /dev/null
@@ -1,58 +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 "media/mojo/services/mojo_audio_input_stream_observer.h"
-
-#include <utility>
-
-#include "media/base/user_input_monitor.h"
-
-namespace media {
-
-MojoAudioInputStreamObserver::MojoAudioInputStreamObserver(
-    mojom::AudioInputStreamObserverRequest request,
-    base::OnceClosure recording_started_callback,
-    base::OnceClosure connection_error_callback,
-    media::UserInputMonitor* user_input_monitor)
-    : binding_(this, std::move(request)),
-      recording_started_callback_(std::move(recording_started_callback)),
-      connection_error_callback_(std::move(connection_error_callback)),
-      user_input_monitor_(user_input_monitor) {
-  DCHECK(recording_started_callback_);
-  if (user_input_monitor_)
-    user_input_monitor_->EnableKeyPressMonitoring();
-
-  // Unretained is safe because |this| owns |binding_|.
-  binding_.set_connection_error_handler(
-      base::BindOnce(&MojoAudioInputStreamObserver::OnConnectionError,
-                     base::Unretained(this)));
-}
-
-MojoAudioInputStreamObserver::~MojoAudioInputStreamObserver() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
-
-  // Check that DisableKeyPressMonitoring() was called.
-  DCHECK(!user_input_monitor_);
-}
-
-void MojoAudioInputStreamObserver::DidStartRecording() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
-  DCHECK(recording_started_callback_);
-  std::move(recording_started_callback_).Run();
-}
-
-void MojoAudioInputStreamObserver::OnConnectionError() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
-  if (user_input_monitor_) {
-    user_input_monitor_->DisableKeyPressMonitoring();
-
-    // Set to nullptr to check that DisableKeyPressMonitoring() was called
-    // before destructor.
-    user_input_monitor_ = nullptr;
-  }
-
-  std::move(connection_error_callback_).Run();
-}
-
-}  // namespace media
diff --git a/media/mojo/services/mojo_audio_input_stream_observer.h b/media/mojo/services/mojo_audio_input_stream_observer.h
deleted file mode 100644
index 2cd48b3..0000000
--- a/media/mojo/services/mojo_audio_input_stream_observer.h
+++ /dev/null
@@ -1,42 +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 MEDIA_MOJO_SERVICES_MOJO_AUDIO_INPUT_STREAM_OBSERVER_H_
-#define MEDIA_MOJO_SERVICES_MOJO_AUDIO_INPUT_STREAM_OBSERVER_H_
-
-#include "media/mojo/interfaces/audio_input_stream.mojom.h"
-#include "media/mojo/services/media_mojo_export.h"
-#include "mojo/public/cpp/bindings/binding.h"
-
-namespace media {
-
-class UserInputMonitor;
-
-class MEDIA_MOJO_EXPORT MojoAudioInputStreamObserver
-    : public mojom::AudioInputStreamObserver {
- public:
-  MojoAudioInputStreamObserver(mojom::AudioInputStreamObserverRequest request,
-                               base::OnceClosure recording_started_callback,
-                               base::OnceClosure connection_error_callback,
-                               media::UserInputMonitor* user_input_monitor);
-  ~MojoAudioInputStreamObserver() override;
-
-  void DidStartRecording() override;
-
- private:
-  void OnConnectionError();
-
-  mojo::Binding<AudioInputStreamObserver> binding_;
-  base::OnceClosure recording_started_callback_;
-  base::OnceClosure connection_error_callback_;
-  media::UserInputMonitor* user_input_monitor_;
-
-  SEQUENCE_CHECKER(owning_sequence_);
-
-  DISALLOW_COPY_AND_ASSIGN(MojoAudioInputStreamObserver);
-};
-
-}  // namespace media
-
-#endif  // MEDIA_MOJO_SERVICES_MOJO_AUDIO_INPUT_STREAM_OBSERVER_H_
diff --git a/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc b/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc
deleted file mode 100644
index ab450be0..0000000
--- a/media/mojo/services/mojo_audio_input_stream_observer_unittest.cc
+++ /dev/null
@@ -1,65 +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 "media/mojo/services/mojo_audio_input_stream_observer.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/run_loop.h"
-#include "base/test/scoped_task_environment.h"
-#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace media {
-
-class MojoAudioInputStreamObserverTest : public testing::Test {
- public:
-  MojoAudioInputStreamObserverTest() {}
-  ~MojoAudioInputStreamObserverTest() override {}
-
-  std::unique_ptr<MojoAudioInputStreamObserver> CreateObserver(
-      media::mojom::AudioInputStreamObserverRequest request) {
-    return std::make_unique<MojoAudioInputStreamObserver>(
-        std::move(request),
-        base::BindOnce(
-            &MojoAudioInputStreamObserverTest::RecordingStartedCallback,
-            base::Unretained(this)),
-        base::BindOnce(
-            &MojoAudioInputStreamObserverTest::BindingConnectionError,
-            base::Unretained(this)),
-        nullptr /*user_input_monitor*/);
-  }
-
-  MOCK_METHOD0(RecordingStartedCallback, void());
-  MOCK_METHOD0(BindingConnectionError, void());
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_env_;
-
-  DISALLOW_COPY_AND_ASSIGN(MojoAudioInputStreamObserverTest);
-};
-
-TEST_F(MojoAudioInputStreamObserverTest, DidStartRecording) {
-  media::mojom::AudioInputStreamObserverPtr observer_ptr;
-  std::unique_ptr<MojoAudioInputStreamObserver> observer =
-      CreateObserver(mojo::MakeRequest(&observer_ptr));
-
-  EXPECT_CALL(*this, RecordingStartedCallback());
-  observer_ptr->DidStartRecording();
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(MojoAudioInputStreamObserverTest, BindingConnectionError) {
-  media::mojom::AudioInputStreamObserverPtr observer_ptr;
-  std::unique_ptr<MojoAudioInputStreamObserver> observer =
-      CreateObserver(mojo::MakeRequest(&observer_ptr));
-
-  EXPECT_CALL(*this, BindingConnectionError());
-  observer_ptr.reset();
-  base::RunLoop().RunUntilIdle();
-}
-
-}  // namespace media
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 5654088..0a5b5207 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1271,8 +1271,6 @@
       "quic/core/quic_tag.h",
       "quic/core/quic_time.cc",
       "quic/core/quic_time.h",
-      "quic/core/quic_trace_visitor.cc",
-      "quic/core/quic_trace_visitor.h",
       "quic/core/quic_transmission_info.cc",
       "quic/core/quic_transmission_info.h",
       "quic/core/quic_types.cc",
@@ -2281,7 +2279,6 @@
 
   sources = [
     "quic/core/proto/cached_network_parameters.proto",
-    "quic/core/proto/quic_trace.proto",
     "quic/core/proto/source_address_token.proto",
   ]
   cc_generator_options = "dllexport_decl=NET_EXPORT_PRIVATE:"
@@ -2293,6 +2290,17 @@
   extra_configs = [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
+proto_library("net_quic_trace_proto") {
+  visibility = [ ":simple_quic_tools" ]
+
+  sources = [
+    "quic/core/proto/quic_trace.proto",
+  ]
+  component_build_force_source_set = true
+
+  extra_configs = [ "//build/config/compiler:wexit_time_destructors" ]
+}
+
 if (!is_proto_quic) {
   static_library("extras") {
     sources = [
@@ -3305,6 +3313,8 @@
 
 source_set("simple_quic_tools") {
   sources = [
+    "quic/core/quic_trace_visitor.cc",
+    "quic/core/quic_trace_visitor.h",
     "tools/quic/chlo_extractor.cc",
     "tools/quic/chlo_extractor.h",
     "tools/quic/quic_client_base.cc",
@@ -3353,8 +3363,10 @@
   ]
   deps = [
     ":net",
+    ":net_quic_trace_proto",
     "//base",
     "//base/third_party/dynamic_annotations",
+    "//third_party/protobuf:protobuf_lite",
     "//url",
   ]
 }
diff --git a/net/quic/core/quic_trace_visitor.h b/net/quic/core/quic_trace_visitor.h
index 9ec17d2..18206ecb8 100644
--- a/net/quic/core/quic_trace_visitor.h
+++ b/net/quic/core/quic_trace_visitor.h
@@ -14,7 +14,7 @@
 // Records a QUIC trace protocol buffer for a QuicConnection.  It's the
 // responsibility of the user of this visitor to process or store the resulting
 // trace, which can be accessed via trace().
-class QUIC_EXPORT_PRIVATE QuicTraceVisitor : public QuicConnectionDebugVisitor {
+class QuicTraceVisitor : public QuicConnectionDebugVisitor {
  public:
   explicit QuicTraceVisitor(const QuicConnection* connection);
 
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index b25fa0db..41409fc 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -966,6 +966,7 @@
   // optimization. See https://crbug.com/boringssl/123.
   SSL_set_renegotiate_mode(ssl_.get(), ssl_renegotiate_freely);
 
+  SSL_set_shed_handshake_config(ssl_.get(), 1);
   return OK;
 }
 
diff --git a/net/socket/ssl_server_socket_impl.cc b/net/socket/ssl_server_socket_impl.cc
index b00dfc4..dbe0da5 100644
--- a/net/socket/ssl_server_socket_impl.cc
+++ b/net/socket/ssl_server_socket_impl.cc
@@ -216,6 +216,7 @@
       weak_factory_(this) {
   ssl_.reset(SSL_new(context_->ssl_ctx_.get()));
   SSL_set_app_data(ssl_.get(), this);
+  SSL_set_shed_handshake_config(ssl_.get(), 1);
 }
 
 SSLServerContextImpl::SocketImpl::~SocketImpl() {
diff --git a/remoting/BUILD.gn b/remoting/BUILD.gn
index 32bba3d6..f05a26f 100644
--- a/remoting/BUILD.gn
+++ b/remoting/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//components/nacl/features.gni")
-import("//media/media_options.gni")
 import("//remoting/build/config/remoting_build.gni")
 
 group("remoting_all") {
@@ -176,9 +175,7 @@
     ]
   }
 
-  if (enable_webrtc) {
-    deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
-  }
+  deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
 
   if (is_android) {
     deps += [ "//net/android:net_java" ]
@@ -221,9 +218,7 @@
       "//third_party/webrtc_overrides",
     ]
 
-    if (enable_webrtc) {
-      deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
-    }
+    deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
 
     if (is_win) {
       defines += [ "_ALT_NO_EXCEPTIONS" ]
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 08abc30..b0fca33b 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/util/process_version.gni")
-import("//media/media_options.gni")
 import("//remoting/build/config/remoting_build.gni")
 
 group("all_tests") {
@@ -305,6 +304,7 @@
     "//remoting/host/security_key",
     "//remoting/protocol",
     "//remoting/resources",
+    "//third_party/webrtc/modules/desktop_capture",
 
     # //remoting uses the power_save_blocker directly. See crbug.com/689423
     "//services/device/wake_lock/power_save_blocker",
@@ -411,10 +411,6 @@
 
     public_deps += [ "//remoting/host/win" ]
   }
-
-  if (enable_webrtc) {
-    deps += [ "//third_party/webrtc/modules/desktop_capture" ]
-  }
 }
 
 static_library("test_support") {
@@ -441,16 +437,11 @@
   public_deps = [
     ":host",
     "//remoting/base:test_support",
+    "//third_party/libjingle_xmpp",
     "//third_party/protobuf:protobuf_lite",
+    "//third_party/webrtc/modules/desktop_capture",
+    "//third_party/webrtc_overrides:init_webrtc",
   ]
-
-  if (enable_webrtc) {
-    public_deps += [
-      "//third_party/libjingle_xmpp",
-      "//third_party/webrtc/modules/desktop_capture",
-      "//third_party/webrtc_overrides:init_webrtc",
-    ]
-  }
 }
 
 # The host portions of the remoting unit tests.
@@ -583,6 +574,8 @@
 
     deps = [
       "//build/config:exe_and_shlib_deps",
+      "//third_party/libjingle_xmpp",
+      "//third_party/webrtc_overrides:init_webrtc",
     ]
 
     configs += [ "//build/config/compiler:wexit_time_destructors" ]
@@ -610,13 +603,6 @@
         "//remoting/host/setup",
       ]
     }
-
-    if (enable_webrtc) {
-      deps += [
-        "//third_party/libjingle_xmpp",
-        "//third_party/webrtc_overrides:init_webrtc",
-      ]
-    }
   }
 
   action_foreach("remoting_native_messaging_manifests") {
@@ -718,7 +704,9 @@
       "//remoting/base",
       "//remoting/host",
       "//remoting/proto",
+      "//third_party/libjingle_xmpp",
       "//third_party/webrtc/modules/desktop_capture",
+      "//third_party/webrtc_overrides:init_webrtc",
     ]
 
     if (is_posix) {
@@ -732,13 +720,6 @@
       deps += [ "//components/policy:generated" ]
     }
 
-    if (enable_webrtc) {
-      deps += [
-        "//third_party/libjingle_xmpp",
-        "//third_party/webrtc_overrides:init_webrtc",
-      ]
-    }
-
     if (is_desktop_linux) {
       deps += [ "//build/config/linux/gtk" ]
     }
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn
index 69e9583..7e2df5c 100644
--- a/remoting/host/it2me/BUILD.gn
+++ b/remoting/host/it2me/BUILD.gn
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 import("//build/config/features.gni")
-import("//media/media_options.gni")
 import("//remoting/remoting_options.gni")
 import("//remoting/remoting_locales.gni")
 import("//remoting/remoting_version.gni")
@@ -207,6 +206,7 @@
         "//remoting/host",
         "//remoting/host/native_messaging",
         "//remoting/proto",
+        "//third_party/webrtc_overrides:init_webrtc",
         "//ui/gfx",
       ]
       if (is_mac) {
@@ -227,10 +227,6 @@
         }
       }
 
-      if (enable_webrtc) {
-        deps += [ "//third_party/webrtc_overrides:init_webrtc" ]
-      }
-
       if (is_desktop_linux) {
         deps += [ "//build/config/linux/gtk" ]
       }
diff --git a/remoting/host/win/BUILD.gn b/remoting/host/win/BUILD.gn
index 875df9b5..461f190 100644
--- a/remoting/host/win/BUILD.gn
+++ b/remoting/host/win/BUILD.gn
@@ -131,6 +131,7 @@
     "//remoting/protocol",
     "//remoting/resources",
     "//services/device/wake_lock/power_save_blocker",
+    "//third_party/webrtc/modules/desktop_capture",
     "//ui/base",
     "//ui/events:dom_keycode_converter",
     "//ui/events/platform",
@@ -139,10 +140,6 @@
   if (!is_ios) {
     deps += [ "//components/policy:generated" ]
   }
-
-  if (enable_webrtc) {
-    deps += [ "//third_party/webrtc/modules/desktop_capture" ]
-  }
 }
 
 source_set("unit_tests") {
diff --git a/remoting/remoting_enable.gni b/remoting/remoting_enable.gni
index 8a24badb..879214d 100644
--- a/remoting/remoting_enable.gni
+++ b/remoting/remoting_enable.gni
@@ -3,12 +3,11 @@
 # found in the LICENSE file.
 
 import("//build/config/ui.gni")
-import("//media/media_options.gni")
 
 if (is_ios) {
   import("//build/config/ios/ios_sdk.gni")
 }
 
 declare_args() {
-  enable_remoting = !is_chromecast && !is_fuchsia && enable_webrtc
+  enable_remoting = !is_chromecast && !is_fuchsia
 }
diff --git a/sandbox/win/src/process_mitigations_win32k_unittest.cc b/sandbox/win/src/process_mitigations_win32k_unittest.cc
index 70247ae..48adf00 100644
--- a/sandbox/win/src/process_mitigations_win32k_unittest.cc
+++ b/sandbox/win/src/process_mitigations_win32k_unittest.cc
@@ -633,7 +633,8 @@
 // along with the policy to fake user32 and gdi32 initialization successfully
 // launches the target process.
 // The test process itself links against user32/gdi32.
-TEST(ProcessMitigationsWin32kTest, CheckWin8LockDownSuccess) {
+// Flaky. https://crbug.com/840335
+TEST(ProcessMitigationsWin32kTest, DISABLED_CheckWin8LockDownSuccess) {
   if (base::win::GetVersion() < base::win::VERSION_WIN8)
     return;
 
@@ -659,7 +660,8 @@
 
 // This test validates the even though we're running under win32k lockdown
 // we can use the IPC redirection to enumerate the list of monitors.
-TEST(ProcessMitigationsWin32kTest, CheckWin8Redirection) {
+// Flaky. https://crbug.com/840335
+TEST(ProcessMitigationsWin32kTest, DISABLED_CheckWin8Redirection) {
   if (base::win::GetVersion() < base::win::VERSION_WIN8)
     return;
 
diff --git a/services/audio/input_stream.cc b/services/audio/input_stream.cc
index 2c6ebee1..ba5ced8 100644
--- a/services/audio/input_stream.cc
+++ b/services/audio/input_stream.cc
@@ -37,13 +37,15 @@
     : binding_(this, std::move(request)),
       client_(std::move(client)),
       observer_(std::move(observer)),
-      log_(media::mojom::ThreadSafeAudioLogPtr::Create(std::move(log))),
+      log_(log ? media::mojom::ThreadSafeAudioLogPtr::Create(std::move(log))
+               : nullptr),
       created_callback_(std::move(created_callback)),
       delete_callback_(std::move(delete_callback)),
       foreign_socket_(),
       writer_(media::AudioInputSyncWriter::Create(
-          base::BindRepeating(&media::mojom::AudioLog::OnLogMessage,
-                              base::Unretained(log_->get())),
+          log_ ? base::BindRepeating(&media::mojom::AudioLog::OnLogMessage,
+                                     base::Unretained(log_->get()))
+               : base::RepeatingCallback<void(const std::string&)>(),
           shared_memory_count,
           params,
           &foreign_socket_)),
@@ -52,7 +54,6 @@
   DCHECK(audio_manager);
   DCHECK(binding_.is_bound());
   DCHECK(client_.is_bound());
-  DCHECK(observer_.is_bound());
   DCHECK(created_callback_);
   DCHECK(delete_callback_);
 
@@ -61,8 +62,12 @@
       base::BindRepeating(&InputStream::OnStreamError, base::Unretained(this));
   binding_.set_connection_error_handler(error_handler);
   client_.set_connection_error_handler(error_handler);
-  observer_.set_connection_error_handler(std::move(error_handler));
-  log_->get()->OnCreated(params, device_id);
+
+  if (observer_)
+    observer_.set_connection_error_handler(std::move(error_handler));
+
+  if (log_)
+    log_->get()->OnCreated(params, device_id);
 
   // Only MONO, STEREO and STEREO_AND_KEYBOARD_MIC channel layouts are expected,
   // see AudioManagerBase::MakeAudioInputStream().
@@ -84,7 +89,8 @@
 InputStream::~InputStream() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
 
-  log_->get()->OnClosed();
+  if (log_)
+    log_->get()->OnClosed();
 
   if (created_callback_) {
     // Didn't manage to create the stream. Call the callback anyways as mandated
@@ -106,8 +112,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   DCHECK(controller_);
   controller_->Record();
-  observer_->DidStartRecording();
-  log_->get()->OnStarted();
+  if (observer_)
+    observer_->DidStartRecording();
+  if (log_)
+    log_->get()->OnStarted();
 }
 
 void InputStream::SetVolume(double volume) {
@@ -121,7 +129,8 @@
   }
 
   controller_->SetVolume(volume);
-  log_->get()->OnSetVolume(volume);
+  if (log_)
+    log_->get()->OnSetVolume(volume);
 }
 
 void InputStream::OnCreated(bool initially_muted) {
@@ -153,13 +162,15 @@
 void InputStream::OnError(media::AudioInputController::ErrorCode error_code) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   client_->OnError();
-  log_->get()->OnError();
+  if (log_)
+    log_->get()->OnError();
   OnStreamError();
 }
 
 void InputStream::OnLog(base::StringPiece message) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
-  log_->get()->OnLogMessage(message.as_string());
+  if (log_)
+    log_->get()->OnLogMessage(message.as_string());
 }
 
 void InputStream::OnMuted(bool is_muted) {
diff --git a/services/audio/owning_audio_manager_accessor.cc b/services/audio/owning_audio_manager_accessor.cc
index 2ac6da63..81dcd9f 100644
--- a/services/audio/owning_audio_manager_accessor.cc
+++ b/services/audio/owning_audio_manager_accessor.cc
@@ -4,8 +4,12 @@
 
 #include "services/audio/owning_audio_manager_accessor.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
 #include "media/audio/audio_manager.h"
 #include "media/audio/audio_thread.h"
 
@@ -14,7 +18,8 @@
 namespace {
 
 // Thread class for hosting owned AudioManager on the main thread of the
-// service.
+// service, with a separate worker thread (started on-demand) for running things
+// that shouldn't be blocked by main-thread tasks.
 class MainThread : public media::AudioThread {
  public:
   MainThread();
@@ -27,10 +32,17 @@
 
  private:
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // This is not started until the first time GetWorkerTaskRunner() is called.
+  base::Thread worker_thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner_;
+
   DISALLOW_COPY_AND_ASSIGN(MainThread);
 };
 
-MainThread::MainThread() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+MainThread::MainThread()
+    : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      worker_thread_("AudioWorkerThread") {}
 
 MainThread::~MainThread() {
   DCHECK(task_runner_->BelongsToCurrentThread());
@@ -38,6 +50,10 @@
 
 void MainThread::Stop() {
   DCHECK(task_runner_->BelongsToCurrentThread());
+  if (worker_task_runner_) {
+    worker_task_runner_ = nullptr;
+    worker_thread_.Stop();
+  }
 }
 
 base::SingleThreadTaskRunner* MainThread::GetTaskRunner() {
@@ -45,8 +61,12 @@
 }
 
 base::SingleThreadTaskRunner* MainThread::GetWorkerTaskRunner() {
-  NOTREACHED();
-  return task_runner_.get();
+  DCHECK(task_runner_->BelongsToCurrentThread());
+  if (!worker_task_runner_) {
+    CHECK(worker_thread_.Start());
+    worker_task_runner_ = worker_thread_.task_runner();
+  }
+  return worker_task_runner_.get();
 }
 
 }  // namespace
@@ -69,7 +89,7 @@
     // TODO(http://crbug/812557): pass AudioLogFactory (needed for output
     // streams).
     audio_manager_ = std::move(audio_manager_factory_cb_)
-                         .Run(std::make_unique<MainThread>(), nullptr);
+                         .Run(std::make_unique<MainThread>(), &log_factory_);
     DCHECK(audio_manager_);
   }
   DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread());
diff --git a/services/audio/owning_audio_manager_accessor.h b/services/audio/owning_audio_manager_accessor.h
index 5f4766a..d978eff 100644
--- a/services/audio/owning_audio_manager_accessor.h
+++ b/services/audio/owning_audio_manager_accessor.h
@@ -5,9 +5,12 @@
 #ifndef SERVICES_AUDIO_OWNING_AUDIO_MANAGER_ACCESSOR_H_
 #define SERVICES_AUDIO_OWNING_AUDIO_MANAGER_ACCESSOR_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "base/threading/thread_checker.h"
 #include "build/build_config.h"
+#include "media/audio/fake_audio_log_factory.h"
 #include "services/audio/service.h"
 
 #if defined(OS_WIN)
@@ -46,6 +49,11 @@
 #endif
   AudioManagerFactoryCallback audio_manager_factory_cb_;
   std::unique_ptr<media::AudioManager> audio_manager_;
+
+  // TODO(http://crbug/812557): Use a real AudioLogFactory (needed for output
+  // streams).
+  media::FakeAudioLogFactory log_factory_;
+
   THREAD_CHECKER(thread_checker_);
   DISALLOW_COPY_AND_ASSIGN(OwningAudioManagerAccessor);
 };
diff --git a/services/audio/public/mojom/stream_factory.mojom b/services/audio/public/mojom/stream_factory.mojom
index 0732e2b..61713e6 100644
--- a/services/audio/public/mojom/stream_factory.mojom
+++ b/services/audio/public/mojom/stream_factory.mojom
@@ -40,8 +40,8 @@
   CreateInputStream(
     media.mojom.AudioInputStream& stream,
     media.mojom.AudioInputStreamClient client,
-    media.mojom.AudioInputStreamObserver observer,
-    media.mojom.AudioLog log,
+    media.mojom.AudioInputStreamObserver? observer,
+    media.mojom.AudioLog? log,
     string device_id, media.mojom.AudioParameters params,
     uint32 shared_memory_count, bool enable_agc,
     handle<shared_buffer>? key_press_count_buffer)
diff --git a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
index 474295c..d0df1c7 100644
--- a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
+++ b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
@@ -36,8 +36,7 @@
 
 }  // namespace
 
-ClientGpuMemoryBufferManager::ClientGpuMemoryBufferManager(
-    mojom::GpuMemoryBufferFactoryPtr gpu)
+ClientGpuMemoryBufferManager::ClientGpuMemoryBufferManager(mojom::GpuPtr gpu)
     : thread_("GpuMemoryThread"),
       gpu_memory_buffer_support_(
           std::make_unique<gpu::GpuMemoryBufferSupport>()),
@@ -56,18 +55,9 @@
       FROM_HERE, base::Bind(&ClientGpuMemoryBufferManager::TearDownThread,
                             base::Unretained(this)));
   thread_.Stop();
-  if (optional_destruction_callback_)
-    std::move(optional_destruction_callback_).Run();
 }
 
-void ClientGpuMemoryBufferManager::SetOptionalDestructionCallback(
-    base::OnceClosure callback) {
-  DCHECK(!optional_destruction_callback_);
-  optional_destruction_callback_ = std::move(callback);
-}
-
-void ClientGpuMemoryBufferManager::InitThread(
-    mojom::GpuMemoryBufferFactoryPtrInfo gpu_info) {
+void ClientGpuMemoryBufferManager::InitThread(mojom::GpuPtrInfo gpu_info) {
   gpu_.Bind(std::move(gpu_info));
   gpu_.set_connection_error_handler(
       base::Bind(&ClientGpuMemoryBufferManager::DisconnectGpuOnThread,
diff --git a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h
index f332406..04f51b7a 100644
--- a/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h
+++ b/services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h
@@ -26,17 +26,13 @@
 
 namespace ui {
 
-// Implements gpu::GpuMemoryBufferManager based on a given
-// ui.mojom.GpuMemoryBufferFactory
 class ClientGpuMemoryBufferManager : public gpu::GpuMemoryBufferManager {
  public:
-  explicit ClientGpuMemoryBufferManager(mojom::GpuMemoryBufferFactoryPtr gpu);
+  explicit ClientGpuMemoryBufferManager(mojom::GpuPtr gpu);
   ~ClientGpuMemoryBufferManager() override;
 
-  void SetOptionalDestructionCallback(base::OnceClosure callback);
-
  private:
-  void InitThread(mojom::GpuMemoryBufferFactoryPtrInfo gpu_info);
+  void InitThread(mojom::GpuPtrInfo gpu_info);
   void TearDownThread();
   void DisconnectGpuOnThread();
   void AllocateGpuMemoryBufferOnThread(const gfx::Size& size,
@@ -63,8 +59,7 @@
   int counter_ = 0;
   // TODO(sad): Explore the option of doing this from an existing thread.
   base::Thread thread_;
-  mojom::GpuMemoryBufferFactoryPtr gpu_;
-  base::OnceClosure optional_destruction_callback_;
+  mojom::GpuPtr gpu_;
   base::WeakPtr<ClientGpuMemoryBufferManager> weak_ptr_;
   std::set<base::WaitableEvent*> pending_allocation_waiters_;
   std::unique_ptr<gpu::GpuMemoryBufferSupport> gpu_memory_buffer_support_;
diff --git a/services/ui/public/cpp/gpu/gpu.cc b/services/ui/public/cpp/gpu/gpu.cc
index 3ec62d5..823ba44 100644
--- a/services/ui/public/cpp/gpu/gpu.cc
+++ b/services/ui/public/cpp/gpu/gpu.cc
@@ -15,7 +15,6 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/common/scheduling_priority.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
@@ -242,26 +241,16 @@
          scoped_refptr<base::SingleThreadTaskRunner> task_runner)
     : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
       io_task_runner_(std::move(task_runner)),
+      gpu_memory_buffer_manager_(
+          std::make_unique<ClientGpuMemoryBufferManager>(factory.Run())),
       gpu_(new GpuPtrIO(), base::OnTaskRunnerDeleter(io_task_runner_)) {
   DCHECK(main_task_runner_);
   DCHECK(io_task_runner_);
 
-  mojom::GpuMemoryBufferFactoryPtr gpu_memory_buffer_factory;
-  auto gpu_for_buffer_factory = factory.Run();
-  gpu_for_buffer_factory->CreateGpuMemoryBufferFactory(
-      mojo::MakeRequest(&gpu_memory_buffer_factory));
-  gpu_memory_buffer_manager_ = std::make_unique<ClientGpuMemoryBufferManager>(
-      std::move(gpu_memory_buffer_factory));
-  // Attach ownership of |gpu_for_buffer_factory| to
-  // |gpu_memory_buffer_manager_| to ensure |gpu_memory_buffer_factory| stays
-  // alive.
-  gpu_memory_buffer_manager_->SetOptionalDestructionCallback(
-      base::BindOnce([](mojom::GpuPtr) {}, std::move(gpu_for_buffer_factory)));
-
-  // Initialize mojom::GpuPtr on the IO thread. |gpu_| can only be used on
-  // the IO thread after this point. It is safe to use base::Unretained with
-  // |gpu_| for IO thread tasks as |gpu_| is destroyed by an IO thread task
-  // posted from the destructor.
+  // Initialize mojom::GpuPtr on the IO thread. |gpu_| can only be used on the
+  // IO thread after this point. It is safe to use base::Unretained with |gpu_|
+  // for IO thread tasks as |gpu_| is destroyed by an IO thread task posted from
+  // the destructor.
   io_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&GpuPtrIO::Initialize, base::Unretained(gpu_.get()),
diff --git a/services/ui/public/cpp/tests/gpu_unittest.cc b/services/ui/public/cpp/tests/gpu_unittest.cc
index 0dbdc57..197e3c3d 100644
--- a/services/ui/public/cpp/tests/gpu_unittest.cc
+++ b/services/ui/public/cpp/tests/gpu_unittest.cc
@@ -35,9 +35,6 @@
   }
 
   // ui::mojom::Gpu overrides:
-  void CreateGpuMemoryBufferFactory(
-      ui::mojom::GpuMemoryBufferFactoryRequest request) override {}
-
   void EstablishGpuChannel(EstablishGpuChannelCallback callback) override {
     if (close_binding_on_request_) {
       // Don't run |callback| and trigger a connection error on the other end.
@@ -63,6 +60,16 @@
   void CreateVideoEncodeAcceleratorProvider(
       media::mojom::VideoEncodeAcceleratorProviderRequest request) override {}
 
+  void CreateGpuMemoryBuffer(
+      gfx::GpuMemoryBufferId id,
+      const gfx::Size& size,
+      gfx::BufferFormat format,
+      gfx::BufferUsage usage,
+      ui::mojom::Gpu::CreateGpuMemoryBufferCallback callback) override {}
+
+  void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
+                              const gpu::SyncToken& sync_token) override {}
+
  private:
   bool request_will_succeed_ = true;
   bool close_binding_on_request_ = false;
diff --git a/services/ui/public/interfaces/gpu.mojom b/services/ui/public/interfaces/gpu.mojom
index 7af6b32..f0806bb 100644
--- a/services/ui/public/interfaces/gpu.mojom
+++ b/services/ui/public/interfaces/gpu.mojom
@@ -12,25 +12,7 @@
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "ui/gfx/mojo/buffer_types.mojom";
 
-interface GpuMemoryBufferFactory {
-  // Tells the GPU service to create a new GPU memory buffer.
-  CreateGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferId id,
-                        gfx.mojom.Size size,
-                        gfx.mojom.BufferFormat format,
-                        gfx.mojom.BufferUsage usage)
-      => (gfx.mojom.GpuMemoryBufferHandle buffer_handle);
-
-  // Tells the GPU process to destroy GPU memory buffer.
-  DestroyGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferId id,
-                         gpu.mojom.SyncToken sync_token);
-};
-
-// API exposed to clients with lower level of trust, e.g. Renderers.
 interface Gpu {
-  // Note: The bound-to GpuMemoryBufferFactory instance is only guaranteed to
-  // stay alive for as long as the invoked Gpu instance.
-  CreateGpuMemoryBufferFactory(GpuMemoryBufferFactory& request);
-
   // Tells the GPU service to create a new channel for communication with a
   // client. The GPU service responds with client ID, IPC handle and
   // GPUInfo.
@@ -45,4 +27,15 @@
   // Creates a VideoEncodeAcceleratorProvider and binds it to |vea_provider|.
   CreateVideoEncodeAcceleratorProvider(
       media.mojom.VideoEncodeAcceleratorProvider& vea_provider);
+
+  // Tells the GPU service to create a new GPU memory buffer.
+  CreateGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferId id,
+                        gfx.mojom.Size size,
+                        gfx.mojom.BufferFormat format,
+                        gfx.mojom.BufferUsage usage)
+      => (gfx.mojom.GpuMemoryBufferHandle buffer_handle);
+
+  // Tells the GPU process to destroy GPU memory buffer.
+  DestroyGpuMemoryBuffer(gfx.mojom.GpuMemoryBufferId id,
+                         gpu.mojom.SyncToken sync_token);
 };
diff --git a/services/ui/ws/gpu_client.cc b/services/ui/ws/gpu_client.cc
index 285576e4..0dc12bf 100644
--- a/services/ui/ws/gpu_client.cc
+++ b/services/ui/ws/gpu_client.cc
@@ -75,7 +75,7 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback) {
+    mojom::Gpu::CreateGpuMemoryBufferCallback callback) {
   gpu_memory_buffer_manager_->AllocateGpuMemoryBuffer(
       id, client_id_, size, format, usage, gpu::kNullSurfaceHandle,
       std::move(callback));
@@ -87,10 +87,5 @@
                                                      sync_token);
 }
 
-void GpuClient::CreateGpuMemoryBufferFactory(
-    ui::mojom::GpuMemoryBufferFactoryRequest request) {
-  gpu_memory_buffer_factory_bindings_.AddBinding(this, std::move(request));
-}
-
 }  // namespace ws
 }  // namespace ui
diff --git a/services/ui/ws/gpu_client.h b/services/ui/ws/gpu_client.h
index 9a9d246..a1cb6790 100644
--- a/services/ui/ws/gpu_client.h
+++ b/services/ui/ws/gpu_client.h
@@ -8,7 +8,6 @@
 #include "base/memory/weak_ptr.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_info.h"
-#include "mojo/public/cpp/bindings/binding_set.h"
 #include "services/ui/public/interfaces/gpu.mojom.h"
 
 namespace viz {
@@ -29,8 +28,7 @@
 
 // The implementation that relays requests from clients to the real
 // service implementation in the GPU process over mojom.GpuService.
-class GpuClient : public ui::mojom::GpuMemoryBufferFactory,
-                  public ui::mojom::Gpu {
+class GpuClient : public mojom::Gpu {
  public:
   GpuClient(int client_id,
             gpu::GPUInfo* gpu_info,
@@ -45,30 +43,22 @@
   // EstablishGpuChannelCallback:
   void OnGpuChannelEstablished(mojo::ScopedMessagePipeHandle channel_handle);
 
-  // ui::mojom::GpuMemoryBufferFactory overrides:
+  // mojom::Gpu overrides:
+  void EstablishGpuChannel(EstablishGpuChannelCallback callback) override;
+  void CreateJpegDecodeAccelerator(
+      media::mojom::JpegDecodeAcceleratorRequest jda_request) override;
+  void CreateVideoEncodeAcceleratorProvider(
+      media::mojom::VideoEncodeAcceleratorProviderRequest request) override;
   void CreateGpuMemoryBuffer(
       gfx::GpuMemoryBufferId id,
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      ui::mojom::GpuMemoryBufferFactory::CreateGpuMemoryBufferCallback callback)
-      override;
+      mojom::Gpu::CreateGpuMemoryBufferCallback callback) override;
   void DestroyGpuMemoryBuffer(gfx::GpuMemoryBufferId id,
                               const gpu::SyncToken& sync_token) override;
 
-  // ui::mojom::Gpu overrides:
-  void CreateGpuMemoryBufferFactory(
-      ui::mojom::GpuMemoryBufferFactoryRequest request) override;
-  void EstablishGpuChannel(EstablishGpuChannelCallback callback) override;
-  void CreateJpegDecodeAccelerator(
-      media::mojom::JpegDecodeAcceleratorRequest jda_request) override;
-  void CreateVideoEncodeAcceleratorProvider(
-      media::mojom::VideoEncodeAcceleratorProviderRequest vea_provider_request)
-      override;
-
   const int client_id_;
-  mojo::BindingSet<ui::mojom::GpuMemoryBufferFactory>
-      gpu_memory_buffer_factory_bindings_;
 
   // The objects these pointers refer to are owned by the GpuHost object.
   const gpu::GPUInfo* gpu_info_;
diff --git a/services/video_capture/BUILD.gn b/services/video_capture/BUILD.gn
index 48a56bf..c205814a 100644
--- a/services/video_capture/BUILD.gn
+++ b/services/video_capture/BUILD.gn
@@ -57,7 +57,6 @@
     "//media/mojo/common:common",
     "//mojo/public/cpp/system",
     "//services/service_manager/public/cpp",
-    "//services/ui/public/cpp/gpu:gpu",
     "//services/video_capture/public/cpp",
     "//services/video_capture/public/mojom",
     "//services/video_capture/public/mojom:constants",
diff --git a/services/video_capture/DEPS b/services/video_capture/DEPS
index 1f82d6b..5c28bf2 100644
--- a/services/video_capture/DEPS
+++ b/services/video_capture/DEPS
@@ -1,8 +1,6 @@
 include_rules = [
-  "+gpu/command_buffer/client",
   "+media/base",
   "+media/mojo",
   "+media/capture",
   "+ui/gfx/geometry",
-  "+services/ui/public/cpp/gpu",
 ]
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.cc b/services/video_capture/device_factory_media_to_mojo_adapter.cc
index e95d3ea..d8bdb0b8 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.cc
@@ -79,10 +79,11 @@
 DeviceFactoryMediaToMojoAdapter::DeviceFactoryMediaToMojoAdapter(
     std::unique_ptr<service_manager::ServiceContextRef> service_ref,
     std::unique_ptr<media::VideoCaptureSystem> capture_system,
-    media::MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback)
+    const media::VideoCaptureJpegDecoderFactoryCB&
+        jpeg_decoder_factory_callback)
     : service_ref_(std::move(service_ref)),
       capture_system_(std::move(capture_system)),
-      jpeg_decoder_factory_callback_(std::move(jpeg_decoder_factory_callback)),
+      jpeg_decoder_factory_callback_(jpeg_decoder_factory_callback),
       has_called_get_device_infos_(false),
       weak_factory_(this) {}
 
diff --git a/services/video_capture/device_factory_media_to_mojo_adapter.h b/services/video_capture/device_factory_media_to_mojo_adapter.h
index 1104838..46c2206 100644
--- a/services/video_capture/device_factory_media_to_mojo_adapter.h
+++ b/services/video_capture/device_factory_media_to_mojo_adapter.h
@@ -26,7 +26,8 @@
   DeviceFactoryMediaToMojoAdapter(
       std::unique_ptr<service_manager::ServiceContextRef> service_ref,
       std::unique_ptr<media::VideoCaptureSystem> capture_system,
-      media::MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback);
+      const media::VideoCaptureJpegDecoderFactoryCB&
+          jpeg_decoder_factory_callback);
   ~DeviceFactoryMediaToMojoAdapter() override;
 
   // mojom::DeviceFactory implementation.
@@ -63,8 +64,7 @@
 
   const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   const std::unique_ptr<media::VideoCaptureSystem> capture_system_;
-  const media::MojoJpegDecodeAcceleratorFactoryCB
-      jpeg_decoder_factory_callback_;
+  const media::VideoCaptureJpegDecoderFactoryCB jpeg_decoder_factory_callback_;
   std::map<std::string, ActiveDeviceEntry> active_devices_by_id_;
   bool has_called_get_device_infos_;
 
diff --git a/services/video_capture/device_factory_provider_impl.cc b/services/video_capture/device_factory_provider_impl.cc
index 930f237..900903b 100644
--- a/services/video_capture/device_factory_provider_impl.cc
+++ b/services/video_capture/device_factory_provider_impl.cc
@@ -7,36 +7,31 @@
 #include "media/capture/video/fake_video_capture_device_factory.h"
 #include "media/capture/video/video_capture_buffer_pool.h"
 #include "media/capture/video/video_capture_buffer_tracker.h"
+#include "media/capture/video/video_capture_jpeg_decoder.h"
 #include "media/capture/video/video_capture_system_impl.h"
-#include "services/ui/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
-#include "services/ui/public/cpp/gpu/gpu.h"
 #include "services/video_capture/device_factory_media_to_mojo_adapter.h"
 #include "services/video_capture/virtual_device_enabled_device_factory.h"
 
+namespace {
+
+// TODO(chfremer): Replace with an actual decoder factory.
+// https://crbug.com/584797
+std::unique_ptr<media::VideoCaptureJpegDecoder> CreateJpegDecoder() {
+  return nullptr;
+}
+
+}  // anonymous namespace
+
 namespace video_capture {
 
 DeviceFactoryProviderImpl::DeviceFactoryProviderImpl(
     std::unique_ptr<service_manager::ServiceContextRef> service_ref,
     base::Callback<void(float)> set_shutdown_delay_cb)
     : service_ref_(std::move(service_ref)),
-      set_shutdown_delay_cb_(std::move(set_shutdown_delay_cb)),
-      weak_factory_(this) {}
+      set_shutdown_delay_cb_(std::move(set_shutdown_delay_cb)) {}
 
 DeviceFactoryProviderImpl::~DeviceFactoryProviderImpl() {}
 
-void DeviceFactoryProviderImpl::InjectGpuDependencies(
-    ui::mojom::GpuMemoryBufferFactoryPtr memory_buffer_factory,
-    mojom::AcceleratorFactoryPtr accelerator_factory) {
-  accelerator_factory_ = std::move(accelerator_factory);
-// Since the instance of ui::ClientGpuMemoryBufferManager seems kind of
-// expensive, we only create it on platforms where it gets actually used.
-#if defined(OS_CHROMEOS)
-  gpu_memory_buffer_manager_ =
-      std::make_unique<ui::ClientGpuMemoryBufferManager>(
-          std::move(memory_buffer_factory));
-#endif
-}
-
 void DeviceFactoryProviderImpl::ConnectToDeviceFactory(
     mojom::DeviceFactoryRequest request) {
   LazyInitializeDeviceFactory();
@@ -57,13 +52,15 @@
   // Chrome OS.
   std::unique_ptr<media::VideoCaptureDeviceFactory> media_device_factory =
       media::VideoCaptureDeviceFactory::CreateFactory(
-          base::ThreadTaskRunnerHandle::Get(), gpu_memory_buffer_manager_.get(),
+          base::ThreadTaskRunnerHandle::Get(),
+          // TODO(jcliang): Create a GpuMemoryBufferManager from GpuService
+          // here.
+          nullptr,
+          // TODO(mojahsu): Create a GpuJpegDecoderMojoFactoryCB here.
           base::BindRepeating(
-              &DeviceFactoryProviderImpl::CreateJpegDecodeAccelerator,
-              weak_factory_.GetWeakPtr()),
+              [](media::mojom::JpegDecodeAcceleratorRequest) {}),
           base::BindRepeating(
-              &DeviceFactoryProviderImpl::CreateJpegEncodeAccelerator,
-              weak_factory_.GetWeakPtr()));
+              [](media::mojom::JpegEncodeAcceleratorRequest) {}));
   auto video_capture_system = std::make_unique<media::VideoCaptureSystemImpl>(
       std::move(media_device_factory));
 
@@ -71,23 +68,7 @@
       service_ref_->Clone(),
       std::make_unique<DeviceFactoryMediaToMojoAdapter>(
           service_ref_->Clone(), std::move(video_capture_system),
-          base::BindRepeating(
-              &DeviceFactoryProviderImpl::CreateJpegDecodeAccelerator,
-              weak_factory_.GetWeakPtr())));
-}
-
-void DeviceFactoryProviderImpl::CreateJpegDecodeAccelerator(
-    media::mojom::JpegDecodeAcceleratorRequest request) {
-  if (!accelerator_factory_)
-    return;
-  accelerator_factory_->CreateJpegDecodeAccelerator(std::move(request));
-}
-
-void DeviceFactoryProviderImpl::CreateJpegEncodeAccelerator(
-    media::mojom::JpegEncodeAcceleratorRequest request) {
-  if (!accelerator_factory_)
-    return;
-  accelerator_factory_->CreateJpegEncodeAccelerator(std::move(request));
+          base::Bind(CreateJpegDecoder)));
 }
 
 }  // namespace video_capture
diff --git a/services/video_capture/device_factory_provider_impl.h b/services/video_capture/device_factory_provider_impl.h
index de633d4..5987091 100644
--- a/services/video_capture/device_factory_provider_impl.h
+++ b/services/video_capture/device_factory_provider_impl.h
@@ -7,10 +7,7 @@
 
 #include <memory>
 
-#include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
-#include "media/capture/video/video_capture_jpeg_decoder.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
-#include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/mojom/device_factory_provider.mojom.h"
@@ -25,27 +22,17 @@
   ~DeviceFactoryProviderImpl() override;
 
   // mojom::DeviceFactoryProvider implementation.
-  void InjectGpuDependencies(
-      ui::mojom::GpuMemoryBufferFactoryPtr memory_buffer_factory,
-      mojom::AcceleratorFactoryPtr accelerator_factory) override;
   void ConnectToDeviceFactory(mojom::DeviceFactoryRequest request) override;
   void SetShutdownDelayInSeconds(float seconds) override;
 
  private:
   void LazyInitializeDeviceFactory();
-  void CreateJpegDecodeAccelerator(
-      media::mojom::JpegDecodeAcceleratorRequest request);
-  void CreateJpegEncodeAccelerator(
-      media::mojom::JpegEncodeAcceleratorRequest request);
 
   mojo::BindingSet<mojom::DeviceFactory> factory_bindings_;
   std::unique_ptr<mojom::DeviceFactory> device_factory_;
 
   const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
-  mojom::AcceleratorFactoryPtr accelerator_factory_;
-  std::unique_ptr<gpu::GpuMemoryBufferManager> gpu_memory_buffer_manager_;
   base::Callback<void(float)> set_shutdown_delay_cb_;
-  base::WeakPtrFactory<DeviceFactoryProviderImpl> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceFactoryProviderImpl);
 };
diff --git a/services/video_capture/device_media_to_mojo_adapter.cc b/services/video_capture/device_media_to_mojo_adapter.cc
index 26edf53..397cd0b 100644
--- a/services/video_capture/device_media_to_mojo_adapter.cc
+++ b/services/video_capture/device_media_to_mojo_adapter.cc
@@ -8,33 +8,20 @@
 #include "media/base/bind_to_current_loop.h"
 #include "media/capture/video/video_capture_buffer_pool_impl.h"
 #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h"
-#include "media/capture/video/video_capture_jpeg_decoder_impl.h"
-#include "media/capture/video/video_frame_receiver_on_task_runner.h"
+#include "media/capture/video/video_capture_jpeg_decoder.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "services/video_capture/receiver_mojo_to_media_adapter.h"
 
-namespace {
-
-std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder(
-    media::MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback,
-    media::VideoCaptureJpegDecoder::DecodeDoneCB decode_done_cb,
-    base::RepeatingCallback<void(const std::string&)> send_log_message_cb) {
-  return std::make_unique<media::VideoCaptureJpegDecoderImpl>(
-      jpeg_decoder_factory_callback, base::ThreadTaskRunnerHandle::Get(),
-      std::move(decode_done_cb), std::move(send_log_message_cb));
-}
-
-}  // anonymous namespace
-
 namespace video_capture {
 
 DeviceMediaToMojoAdapter::DeviceMediaToMojoAdapter(
     std::unique_ptr<service_manager::ServiceContextRef> service_ref,
     std::unique_ptr<media::VideoCaptureDevice> device,
-    media::MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback)
+    const media::VideoCaptureJpegDecoderFactoryCB&
+        jpeg_decoder_factory_callback)
     : service_ref_(std::move(service_ref)),
       device_(std::move(device)),
-      jpeg_decoder_factory_callback_(std::move(jpeg_decoder_factory_callback)),
+      jpeg_decoder_factory_callback_(jpeg_decoder_factory_callback),
       device_started_(false),
       weak_factory_(this) {}
 
@@ -52,9 +39,10 @@
       base::Bind(&DeviceMediaToMojoAdapter::OnClientConnectionErrorOrClose,
                  weak_factory_.GetWeakPtr()));
 
-  receiver_ = std::make_unique<ReceiverMojoToMediaAdapter>(std::move(receiver));
-  auto media_receiver = std::make_unique<media::VideoFrameReceiverOnTaskRunner>(
-      receiver_->GetWeakPtr(), base::ThreadTaskRunnerHandle::Get());
+  auto receiver_adapter =
+      std::make_unique<ReceiverMojoToMediaAdapter>(std::move(receiver));
+  auto media_receiver = std::make_unique<ReceiverOnTaskRunner>(
+      std::move(receiver_adapter), base::ThreadTaskRunnerHandle::Get());
 
   // Create a dedicated buffer pool for the device usage session.
   auto buffer_tracker_factory =
@@ -64,13 +52,7 @@
                                             max_buffer_pool_buffer_count()));
 
   auto device_client = std::make_unique<media::VideoCaptureDeviceClient>(
-      std::move(media_receiver), buffer_pool,
-      base::BindRepeating(
-          &CreateGpuJpegDecoder, jpeg_decoder_factory_callback_,
-          base::BindRepeating(&media::VideoFrameReceiver::OnFrameReadyInBuffer,
-                              receiver_->GetWeakPtr()),
-          base::BindRepeating(&media::VideoFrameReceiver::OnLog,
-                              receiver_->GetWeakPtr())));
+      std::move(media_receiver), buffer_pool, jpeg_decoder_factory_callback_);
 
   device_->AllocateAndStart(requested_settings, std::move(device_client));
   device_started_ = true;
@@ -131,7 +113,6 @@
   device_started_ = false;
   weak_factory_.InvalidateWeakPtrs();
   device_->StopAndDeAllocate();
-  receiver_.reset();
 }
 
 void DeviceMediaToMojoAdapter::OnClientConnectionErrorOrClose() {
diff --git a/services/video_capture/device_media_to_mojo_adapter.h b/services/video_capture/device_media_to_mojo_adapter.h
index fb218bf..2d0f4de 100644
--- a/services/video_capture/device_media_to_mojo_adapter.h
+++ b/services/video_capture/device_media_to_mojo_adapter.h
@@ -8,16 +8,12 @@
 #include "base/threading/thread_checker.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video/video_capture_device_client.h"
-#include "media/capture/video/video_capture_device_factory.h"
-#include "media/capture/video/video_capture_jpeg_decoder.h"
 #include "media/capture/video_capture_types.h"
 #include "services/service_manager/public/cpp/service_context_ref.h"
 #include "services/video_capture/public/mojom/device.mojom.h"
 
 namespace video_capture {
 
-class ReceiverMojoToMediaAdapter;
-
 // Implementation of mojom::Device backed by a given instance of
 // media::VideoCaptureDevice.
 class DeviceMediaToMojoAdapter : public mojom::Device {
@@ -25,7 +21,8 @@
   DeviceMediaToMojoAdapter(
       std::unique_ptr<service_manager::ServiceContextRef> service_ref,
       std::unique_ptr<media::VideoCaptureDevice> device,
-      media::MojoJpegDecodeAcceleratorFactoryCB jpeg_decoder_factory_callback);
+      const media::VideoCaptureJpegDecoderFactoryCB&
+          jpeg_decoder_factory_callback);
   ~DeviceMediaToMojoAdapter() override;
 
   // mojom::Device implementation.
@@ -51,9 +48,7 @@
  private:
   const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
   const std::unique_ptr<media::VideoCaptureDevice> device_;
-  const media::MojoJpegDecodeAcceleratorFactoryCB
-      jpeg_decoder_factory_callback_;
-  std::unique_ptr<ReceiverMojoToMediaAdapter> receiver_;
+  media::VideoCaptureJpegDecoderFactoryCB jpeg_decoder_factory_callback_;
   bool device_started_;
   base::ThreadChecker thread_checker_;
   base::WeakPtrFactory<DeviceMediaToMojoAdapter> weak_factory_;
diff --git a/services/video_capture/device_media_to_mojo_adapter_unittest.cc b/services/video_capture/device_media_to_mojo_adapter_unittest.cc
index 5c24915a6..f88f204 100644
--- a/services/video_capture/device_media_to_mojo_adapter_unittest.cc
+++ b/services/video_capture/device_media_to_mojo_adapter_unittest.cc
@@ -28,7 +28,7 @@
     mock_device_ptr_ = mock_device.get();
     adapter_ = std::make_unique<DeviceMediaToMojoAdapter>(
         std::unique_ptr<service_manager::ServiceContextRef>(),
-        std::move(mock_device), base::DoNothing());
+        std::move(mock_device), media::VideoCaptureJpegDecoderFactoryCB());
   }
 
   void TearDown() override {
diff --git a/services/video_capture/public/mojom/BUILD.gn b/services/video_capture/public/mojom/BUILD.gn
index c3b0257..6948edf 100644
--- a/services/video_capture/public/mojom/BUILD.gn
+++ b/services/video_capture/public/mojom/BUILD.gn
@@ -20,7 +20,6 @@
     "//media/capture/mojom:image_capture",
     "//media/capture/mojom:video_capture",
     "//media/mojo/interfaces",
-    "//services/ui/public/interfaces",
     "//ui/gfx/geometry/mojo",
   ]
 }
diff --git a/services/video_capture/public/mojom/device_factory_provider.mojom b/services/video_capture/public/mojom/device_factory_provider.mojom
index 92fd783b..857251b 100644
--- a/services/video_capture/public/mojom/device_factory_provider.mojom
+++ b/services/video_capture/public/mojom/device_factory_provider.mojom
@@ -6,26 +6,11 @@
 
 import "services/video_capture/public/mojom/device_factory.mojom";
 
-import "media/mojo/interfaces/jpeg_decode_accelerator.mojom";
-import "media/mojo/interfaces/jpeg_encode_accelerator.mojom";
-import "services/ui/public/interfaces/gpu.mojom";
-
-interface AcceleratorFactory {
-  // Creates a new JpegDecodeAccelerator and binds it to |jda|.
-  CreateJpegDecodeAccelerator(media.mojom.JpegDecodeAccelerator& jda);
-
-  // Creates a new JpegEncodeAccelerator and binds it to |jea|.
-  CreateJpegEncodeAccelerator(media.mojom.JpegEncodeAccelerator& jea);
-};
-
 // Entry point to the Video Capture Service API.
 // The factory provides access to the capture devices connected to the system.
 // By using command-line switches, it is possible to replace this with a "fake"
 // factory, which provides access to fake devices that generates test frames.
 interface DeviceFactoryProvider  {
-  InjectGpuDependencies(ui.mojom.GpuMemoryBufferFactory memory_buffer_factory,
-                        AcceleratorFactory accelerator_factory);
-
   ConnectToDeviceFactory(DeviceFactory& request);
 
   // The service shuts down after a certain delay when no client is connected.
diff --git a/services/video_capture/receiver_mojo_to_media_adapter.cc b/services/video_capture/receiver_mojo_to_media_adapter.cc
index 4212430b..927eaaf 100644
--- a/services/video_capture/receiver_mojo_to_media_adapter.cc
+++ b/services/video_capture/receiver_mojo_to_media_adapter.cc
@@ -9,17 +9,75 @@
 
 namespace video_capture {
 
+ReceiverOnTaskRunner::ReceiverOnTaskRunner(
+    std::unique_ptr<media::VideoFrameReceiver> receiver,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+    : receiver_(std::move(receiver)), task_runner_(std::move(task_runner)) {}
+
+ReceiverOnTaskRunner::~ReceiverOnTaskRunner() {
+  task_runner_->DeleteSoon(FROM_HERE, receiver_.release());
+}
+
+void ReceiverOnTaskRunner::OnNewBuffer(
+    int buffer_id,
+    media::mojom::VideoBufferHandlePtr buffer_handle) {
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&media::VideoFrameReceiver::OnNewBuffer,
+                                base::Unretained(receiver_.get()), buffer_id,
+                                base::Passed(&buffer_handle)));
+}
+
+void ReceiverOnTaskRunner::OnFrameReadyInBuffer(
+    int buffer_id,
+    int frame_feedback_id,
+    std::unique_ptr<
+        media::VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>
+        buffer_read_permission,
+    media::mojom::VideoFrameInfoPtr frame_info) {
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&media::VideoFrameReceiver::OnFrameReadyInBuffer,
+                 base::Unretained(receiver_.get()), buffer_id,
+                 frame_feedback_id, base::Passed(&buffer_read_permission),
+                 base::Passed(&frame_info)));
+}
+
+void ReceiverOnTaskRunner::OnBufferRetired(int buffer_id) {
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(&media::VideoFrameReceiver::OnBufferRetired,
+                            base::Unretained(receiver_.get()), buffer_id));
+}
+
+void ReceiverOnTaskRunner::OnError() {
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&media::VideoFrameReceiver::OnError,
+                                    base::Unretained(receiver_.get())));
+}
+
+void ReceiverOnTaskRunner::OnLog(const std::string& message) {
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(&media::VideoFrameReceiver::OnLog,
+                            base::Unretained(receiver_.get()), message));
+}
+
+void ReceiverOnTaskRunner::OnStarted() {
+  task_runner_->PostTask(FROM_HERE,
+                         base::Bind(&media::VideoFrameReceiver::OnStarted,
+                                    base::Unretained(receiver_.get())));
+}
+
+void ReceiverOnTaskRunner::OnStartedUsingGpuDecode() {
+  task_runner_->PostTask(
+      FROM_HERE, base::Bind(&media::VideoFrameReceiver::OnStartedUsingGpuDecode,
+                            base::Unretained(receiver_.get())));
+}
+
 ReceiverMojoToMediaAdapter::ReceiverMojoToMediaAdapter(
     mojom::ReceiverPtr receiver)
-    : receiver_(std::move(receiver)), weak_factory_(this) {}
+    : receiver_(std::move(receiver)) {}
 
 ReceiverMojoToMediaAdapter::~ReceiverMojoToMediaAdapter() = default;
 
-base::WeakPtr<media::VideoFrameReceiver>
-ReceiverMojoToMediaAdapter::GetWeakPtr() {
-  return weak_factory_.GetWeakPtr();
-}
-
 void ReceiverMojoToMediaAdapter::OnNewBuffer(
     int buffer_id,
     media::mojom::VideoBufferHandlePtr buffer_handle) {
diff --git a/services/video_capture/receiver_mojo_to_media_adapter.h b/services/video_capture/receiver_mojo_to_media_adapter.h
index 00609b4..e6eaa10 100644
--- a/services/video_capture/receiver_mojo_to_media_adapter.h
+++ b/services/video_capture/receiver_mojo_to_media_adapter.h
@@ -11,6 +11,33 @@
 
 namespace video_capture {
 
+class ReceiverOnTaskRunner : public media::VideoFrameReceiver {
+ public:
+  ReceiverOnTaskRunner(std::unique_ptr<media::VideoFrameReceiver> receiver,
+                       scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+  ~ReceiverOnTaskRunner() override;
+
+  // media::VideoFrameReceiver implementation.
+  void OnNewBuffer(int buffer_id,
+                   media::mojom::VideoBufferHandlePtr buffer_handle) override;
+  void OnFrameReadyInBuffer(
+      int buffer_id,
+      int frame_feedback_id,
+      std::unique_ptr<
+          media::VideoCaptureDevice::Client::Buffer::ScopedAccessPermission>
+          buffer_read_permission,
+      media::mojom::VideoFrameInfoPtr frame_info) override;
+  void OnBufferRetired(int buffer_id) override;
+  void OnError() override;
+  void OnLog(const std::string& message) override;
+  void OnStarted() override;
+  void OnStartedUsingGpuDecode() override;
+
+ private:
+  std::unique_ptr<media::VideoFrameReceiver> receiver_;
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
 // Adapter that allows a mojom::VideoFrameReceiver to be used in place of
 // a media::VideoFrameReceiver.
 class ReceiverMojoToMediaAdapter : public media::VideoFrameReceiver {
@@ -18,8 +45,6 @@
   ReceiverMojoToMediaAdapter(mojom::ReceiverPtr receiver);
   ~ReceiverMojoToMediaAdapter() override;
 
-  base::WeakPtr<media::VideoFrameReceiver> GetWeakPtr();
-
   // media::VideoFrameReceiver implementation.
   void OnNewBuffer(int buffer_id,
                    media::mojom::VideoBufferHandlePtr buffer_handle) override;
@@ -38,7 +63,6 @@
 
  private:
   mojom::ReceiverPtr receiver_;
-  base::WeakPtrFactory<ReceiverMojoToMediaAdapter> weak_factory_;
 };
 
 }  // namespace video_capture
diff --git a/services/video_capture/test/mock_device_test.cc b/services/video_capture/test/mock_device_test.cc
index 24c26466..62a25cd 100644
--- a/services/video_capture/test/mock_device_test.cc
+++ b/services/video_capture/test/mock_device_test.cc
@@ -14,6 +14,14 @@
 using testing::Invoke;
 using testing::InvokeWithoutArgs;
 
+namespace {
+
+std::unique_ptr<media::VideoCaptureJpegDecoder> CreateJpegDecoder() {
+  return nullptr;
+}
+
+}  // anonymous namespace
+
 namespace video_capture {
 
 MockDeviceTest::MockDeviceTest() : ref_factory_(base::DoNothing()) {}
@@ -32,7 +40,7 @@
   mock_device_factory_adapter_ =
       std::make_unique<DeviceFactoryMediaToMojoAdapter>(
           ref_factory_.CreateRef(), std::move(video_capture_system),
-          base::DoNothing());
+          base::Bind(CreateJpegDecoder));
 
   mock_factory_binding_ = std::make_unique<mojo::Binding<mojom::DeviceFactory>>(
       mock_device_factory_adapter_.get(), mojo::MakeRequest(&factory_));
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 69f40692..462be33 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -5207,11 +5207,6 @@
         "results_handler": "layout tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-14.04"
-            }
-          ],
           "shards": 12
         }
       }
@@ -5224,6 +5219,18 @@
     "gtest_tests": [
       {
         "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_browser_tests.filter"
+        ],
+        "name": "network_service_browser_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 10
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
           "--enable-surface-synchronization"
         ],
         "name": "surface_sync_browser_tests",
@@ -5247,6 +5254,27 @@
       },
       {
         "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_components_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "components_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter"
+        ],
+        "name": "network_service_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-surface-synchronization"
         ],
         "name": "surface_sync_content_browsertests",
@@ -5280,11 +5308,53 @@
         "test": "content_unittests"
       },
       {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_extensions_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "extensions_browsertests"
+      },
+      {
+        "args": [
+          "--enable-features=NetworkService"
+        ],
+        "name": "network_service_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
         "swarming": {
           "can_use_on_swarming_builders": true
         },
         "test": "services_unittests"
       }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "--additional-driver-flag=--enable-features=NetworkService",
+          "--zero-tests-executed-ok"
+        ],
+        "isolate_name": "webkit_layout_tests_exparchive",
+        "merge": {
+          "args": [
+            "--verbose"
+          ],
+          "script": "//third_party/blink/tools/merge_web_test_results.py"
+        },
+        "name": "webkit_layout_tests",
+        "only_retry_failed_tests": true,
+        "results_handler": "layout tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 12
+        }
+      }
     ]
   },
   "Out of Process Profiling Android": {
diff --git a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
index dbfeee93a..7b5d4c1 100644
--- a/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
+++ b/testing/buildbot/filters/mac_window_server_killers.browser_tests.filter
@@ -1,47 +1,7 @@
 # https://crbug.com/828031
 
 # Suites implicated in window_server deaths.
-# -PDFExtensionTest.*
-# -SubresourceFilterBrowserTest.*
-# -DevToolsBeforeUnloadTest.*
-# -PushMessagingBrowserTest.*
-# -TaskManagerBrowserTest.*
-# -AppBannerManagerBrowserTest.*
-# -SpellingMenuObserverTest.*
-# -PluginPowerSaverBrowserTest.*
-# -SubresourceFilterPopupBrowserTest.*
-# -FindInPageControllerTest.*
-# -TestStatsDictionaryTest.*
-# -PlatformAppWithFileBrowserTest.*
-# -PermissionDialogTest.*
-# -SecurityStateTabHelperTest.*
-# -PrerenderBrowserTest.*
-# -DomDistillerViewerSourceBrowserTest.*
-# -PredictorBrowserTest.*
-# -TabManagerTest.*
-# -PasswordManagerBrowserTestBase.*
-# -DevToolsSanityTest.*
-# -WebRtcBrowserTest.*
-# -LoginPromptBrowserTest.*
-# -ReferrerPolicyTest.*
-# -PlatformAppBrowserTest.*
-# -BrowserNavigatorTest.*
-# -ChromeResourceDispatcherHostDelegateBrowserTest.*
-# -NoStatePrefetchBrowserTest.*
-# -PageLoadMetricsBrowserTest.*
-# -DnsProbeBrowserTest.*
-# -FullscreenControllerTest.*
-# -InfoBarUiTest.*
-# -PrefsFunctionalTest.*
-# -BrowserTest.*
-# -DownloadTest.*
-# -PolicyTest.*
-# -SessionRestoreTest.*
-# -InstallableManagerBrowserTest.*
-# -ContextMenuBrowserTest.*
-# -ChromeSitePerProcessTest.*
-# -InterstitialUITest.*
-#-ConstrainedWebDialogBrowserTest.BasicTest
-#-ConstrainedWebDialogBrowserTest.ReleaseWebContents
+# -ConstrainedWebDialogBrowserTest.BasicTest
+-ConstrainedWebDialogBrowserTest.ReleaseWebContents
 -ConstrainedWebDialogBrowserTest.ContentResizeInAutoResizingDialog
 -ConstrainedWebDialogBrowserTest.ContentResizeInNonAutoResizingDialog
diff --git a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
index 6acc1e97..eca129f 100644
--- a/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_browser_tests.filter
@@ -14,8 +14,6 @@
 -EnabledSignInIsolationBrowserTest.SyntheticTrial
 -ExpectCTBrowserTest.TestDynamicExpectCTHeaderProcessing
 -ExpectCTBrowserTest.TestDynamicExpectCTReporting
--ExtensionApiTest.Debugger
--ExtensionApiTestWithSwitch.ExtensionDebugger
 -ExtensionUnloadBrowserTest.UnloadWithContentScripts
 -MediaGalleriesPlatformAppBrowserTest.ToURL
 -NetInternalsTest.netInternalsSessionBandwidthSucceed
@@ -150,6 +148,8 @@
 -ExtensionWebRequestApiTest.WebRequestTypes
 -ExtensionWebRequestApiTest.WebRequestTestOSDD
 -ExtensionWebRequestApiTest.WebRequestURLFetcherInterception
+# Note WebRequestUnloadImmediately is disabled on Linux
+-ExtensionWebRequestApiTest.WebRequestUnloadImmediately
 
 # https://crbug.com/721400
 # WebSocket with the network service
@@ -215,6 +215,11 @@
 -ServiceWorkerTestWithNativeBindings/ServiceWorkerTest.WebAccessibleResourcesFetch/0
 -ServiceWorkerTestWithNativeBindings/ServiceWorkerTest.WebAccessibleResourcesIframeSrc/0
 
+# Support URLLoaderFactories from embedder in shared workers.
+# https://crbug.com/839982
+-ExtensionApiTest.Debugger
+-ExtensionApiTestWithSwitch.ExtensionDebugger
+
 # https://crbug.com/832749
 # Add DMServer header
 -ChromeResourceDispatcherHostDelegateBrowserTest.PolicyHeader
@@ -255,6 +260,9 @@
 -CertificateTransparencyBrowserTest.ProfileRequest
 -CertificateTransparencyBrowserTest.SystemRequest
 
+# Fail on Windows only
+-ConditionalCacheCountingHelperBrowserTest.Count
+
 # NOTE: if adding an exclusion for an existing failure (e.g. additional test for
 # feature X that is already not working), please add it beside the existing
 # failures. Otherwise please reach out to network-service-dev@.
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 5734a4a..75ab68df 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1096,7 +1096,7 @@
     },
   },
 
-  'mojo_linux_isolated_scripts': {
+  'mojo_network_isolated_scripts': {
     'webkit_layout_tests': {
       'args': [
         '--additional-driver-flag=--enable-features=NetworkService',
@@ -1112,11 +1112,6 @@
       'only_retry_failed_tests': True,
       'results_handler': 'layout tests',
       'swarming': {
-        'dimension_sets': [
-          {
-            'os': 'Ubuntu-14.04',
-          }
-        ],
         'shards': 12,
       },
     },
@@ -2252,6 +2247,7 @@
 
   'mojo_windows_gtests': [
     'mojo_windows_specific_gtests',
+    'network_service_gtests',
     'viz_fyi_gtests',
   ],
 
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 5a73d765..ed13bb9 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -1182,7 +1182,7 @@
       'Mojo Linux': {
         'test_suites': {
           'gtest_tests': 'network_service_gtests',
-          'isolated_scripts': 'mojo_linux_isolated_scripts',
+          'isolated_scripts': 'mojo_network_isolated_scripts',
         },
       },
       'Mojo Windows': {
@@ -1191,6 +1191,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'mojo_windows_gtests',
+          'isolated_scripts': 'mojo_network_isolated_scripts',
         },
       },
       'Out of Process Profiling Android': {
diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni
index 20f6ec93..1e41df2 100644
--- a/testing/libfuzzer/fuzzer_test.gni
+++ b/testing/libfuzzer/fuzzer_test.gni
@@ -18,6 +18,7 @@
 # - dict - a dictionary file for the fuzzer.
 # - libfuzzer_options - options for the fuzzer (e.g. -max_len or -timeout).
 # - seed_corpus - a directory with seed corpus.
+# - seed_corpus_deps - dependencies for generating the seed corpus.
 # - skip_owners - if true, skips writing the owners file.
 #
 # If use_libfuzzer gn flag is defined, then proper fuzzer would be build.
@@ -48,6 +49,12 @@
 
       out = "$root_build_dir/$target_name" + "_seed_corpus.zip"
 
+      seed_corpus_deps = []
+
+      if (defined(invoker.seed_corpus_deps)) {
+        seed_corpus_deps += invoker.seed_corpus_deps
+      }
+
       action(target_name + "_seed_corpus") {
         script = "//testing/libfuzzer/archive_corpus.py"
 
@@ -70,9 +77,7 @@
           out,
         ]
 
-        deps = [
-          "//testing/libfuzzer:seed_corpus",
-        ]
+        deps = [ "//testing/libfuzzer:seed_corpus" ] + seed_corpus_deps
       }
 
       test_deps += [ ":" + target_name + "_seed_corpus" ]
@@ -195,6 +200,9 @@
     if (defined(invoker.seed_corpuses)) {
       assert(invoker.seed_corpuses == [] || invoker.seed_corpuses != [])
     }
+    if (defined(invoker.seed_corpus_deps)) {
+      assert(invoker.seed_corpus_deps == [] || invoker.seed_corpus_deps != [])
+    }
     assert(!defined(invoker.check_includes) || invoker.check_includes != [])
     assert(!defined(invoker.include_dirs) || invoker.include_dirs != [])
     assert(!defined(invoker.defines) || invoker.defines != [])
diff --git a/testing/trigger_scripts/perf_device_trigger.py b/testing/trigger_scripts/perf_device_trigger.py
index f745833..29fb973 100755
--- a/testing/trigger_scripts/perf_device_trigger.py
+++ b/testing/trigger_scripts/perf_device_trigger.py
@@ -222,9 +222,11 @@
       # We queried with a limit of 1 so we could only get back
       # the most recent which is what we care about.
       task = tasks[0]
+      if 'bot_id' in task:
+        return task['bot_id']
       for tag in task['tags']:
-        if 'id' in tag:
-          return tag[len('id;'):]
+        if tag.startswith('id:'):
+          return tag[len('id:'):]
     # No eligible shard for this bot
     return None
 
diff --git a/testing/trigger_scripts/perf_device_trigger_unittest.py b/testing/trigger_scripts/perf_device_trigger_unittest.py
index ee8f8e6..b583cfe 100755
--- a/testing/trigger_scripts/perf_device_trigger_unittest.py
+++ b/testing/trigger_scripts/perf_device_trigger_unittest.py
@@ -99,7 +99,7 @@
     for i in xrange(num_shards):
       bot_id = previous_task_assignment_map.get(i)
       files['base_trigger_dimensions%d.json' % file_index] = (
-          self.generate_last_task_to_shard_query_response(bot_id))
+          self.generate_last_task_to_shard_query_response(i, bot_id))
       file_index = file_index + 1
     for i in xrange(num_shards):
       task = {
@@ -120,9 +120,14 @@
       file_index = file_index + 1
     return files
 
-  def generate_last_task_to_shard_query_response(self, bot_id):
+  def generate_last_task_to_shard_query_response(self, shard, bot_id):
     if len(bot_id):
-      return {'items': [{'tags': [('id:%s' % bot_id)]}]}
+      # Test both cases where bot_id is present and you have to parse
+      # out of the tags.
+      if shard % 2:
+        return {'items': [{'bot_id': bot_id}]}
+      else:
+        return {'items': [{'tags': [('id:%s' % bot_id)]}]}
     return {}
 
   def generate_list_of_eligible_bots_query_response(
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 17e8fe3f..39c54f7 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -497,11 +497,11 @@
 
 # Crashes/asserts due to inline item reuse.
 crbug.com/636993 virtual/layout_ng/fast/block/float/add-float-back-to-anonymous-block-previous.html [ Crash ]
-crbug.com/636993 virtual/layout_ng/fast/inline/inline-empty-block-continuation-remove.html [ Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-from-content-after-spanner.html [ Failure Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-from-content-before-spanner.html [ Failure Crash ]
-crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-from-content-between-spanners.html [ Failure Crash ]
-crbug.com/636993 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-inline-and-spanner-after-spanner-foreignObject.html [ Crash ]
+crbug.com/636993 virtual/layout_ng/fast/inline/inline-empty-block-continuation-remove.html [ Crash Timeout ]
+crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-from-content-after-spanner.html [ Failure Crash Timeout ]
+crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-from-content-before-spanner.html [ Failure Crash Timeout ]
+crbug.com/591099 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-block-from-content-between-spanners.html [ Failure Crash Timeout ]
+crbug.com/636993 virtual/layout_ng_experimental/fast/multicol/dynamic/remove-inline-and-spanner-after-spanner-foreignObject.html [ Crash Timeout ]
 
 ### 1px diff with ref files.
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-sub-001.xht [ Failure ]
@@ -1720,7 +1720,6 @@
 # ====== IncrementalShadowDOM-only failures from here ======
 
 crbug.com/776656 virtual/incremental-shadow-dom/external/wpt/shadow-dom/untriaged/styles/test-003.html [ Failure ]
-crbug.com/776656 virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/shadow-distribution.js [ Failure ]
 
 # ====== IncrementalShadowDOM-only failures until here ======
 
@@ -2360,66 +2359,23 @@
 crbug.com/807152 vr/VRDisplay_rAF_fires_with_window_rAF.html [ Pass Failure ]
 crbug.com/813697 vr/getFrameData_oneframeupdate.html [ Pass Failure ]
 
+crbug.com/806357 [ Win Debug ] fast/events/pointerevents/pointerevent_touch-action-pinch_zoom_touch.html [ Pass Failure ]
+crbug.com/839038 [ Mac ] external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node-manual.html [ Skip ]
+
 # These tests are skipped as there is no touch support on Mac.
 crbug.com/613672 [ Mac ] fast/events/touch/multi-touch-user-gesture.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/touch/multi-touch-user-gesture.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/events/touch/multi-touch-user-gesture.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/pointerevents/multi-pointer-event-in-slop-region.html [ Skip ]
-crbug.com/613672 [ Mac ] fast/events/pointerevents/pointerevent_touch-action-pinch_zoom_touch.html [ Skip ]
-crbug.com/806357 [ Win Debug ] fast/events/pointerevents/pointerevent_touch-action-pinch_zoom_touch.html [ Pass Failure ]
-crbug.com/613672 [ Mac ] fast/events/pointerevents/pointerevent_touch-adjustment_click_target.html [ Skip ]
-crbug.com/613672 [ Mac ] fast/events/pointerevents/pointer-event-consumed-touchstart-in-slop-region.html [ Skip ]
-crbug.com/613672 [ Mac ] fast/events/pointerevents/pointer-event-in-slop-region.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerlock/pointerevent_movementxy-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/extension/pointerevent_coalesced_events_attributes-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_sequence_at_implicit_release_on_drag-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_sequence_at_implicit_release_on_click-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_boundary_events_in_capturing-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_lostpointercapture_for_disconnected_node-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_attributes_nohover_pointers-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_change-touch-action-onpointerdown_touch-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_pointerleave_after_pointercancel_touch-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_pointercancel_touch-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_releasepointercapture_events_to_original_target-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_releasepointercapture_onpointercancel_touch-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_pointerout_after_pointercancel_touch-manual.html [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-auto-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/extension/pointerevent_touch-action-pan-left-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/extension/pointerevent_touch-action-pan-right-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/extension/pointerevent_touch-action-pan-up-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/extension/pointerevent_touch-action-pan-down-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-pan-x-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-button-test_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-table-test_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-pan-y-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-span-test_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-svg-test_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-none-css_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-inherit_parent-none_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-none_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerevent_fractional_coordinates-manual.html  [ Skip ]
-crbug.com/613672 [ Mac ] fast/events/synthetic-events/tap-on-scaled-screen.html  [ Skip ]
-crbug.com/613672 [ Mac ] virtual/scalefactor150/fast/events/synthetic-events/tap-on-scaled-screen.html  [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/multi-pointer-event-in-slop-region.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/pointerevent_touch-action-pinch_zoom_touch.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/pointerevent_touch-adjustment_click_target.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/pointer-event-consumed-touchstart-in-slop-region.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/pointerevents/pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/pointer-event-in-slop-region.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/synthetic-events/tap-on-scaled-screen.html  [ Skip ]
-crbug.com/613672 [ Mac ] virtual/scroll_customization/fast/scroll-behavior/scroll-customization/scroll-customization-property.html [ Skip ]
-crbug.com/613672 [ Mac ] media/controls/modern/tap-to-hide-controls.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/video-surface-layer/media/controls/modern/tap-to-hide-controls.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/new-remote-playback-pipeline/media/controls/modern/tap-to-hide-controls.html [ Skip ]
-crbug.com/613672 [ Mac ] media/controls/modern/singletouch-on-play-button.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/video-surface-layer/media/controls/modern/singletouch-on-play-button.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/new-remote-playback-pipeline/media/controls/modern/singletouch-on-play-button.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/pointerevents/pointerevent_touch-action-pinch_zoom_touch.html [ Skip ]
+crbug.com/613672 [ Mac ] virtual/mouseevent_fractional/fast/events/pointerevents/pointerevent_touch-action-pinch_zoom_touch.html [ Skip ]
+crbug.com/613672 [ Mac ] external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual.html [ Skip ]
+
+crbug.com/802067 [ Mac ] external/wpt/pointerlock/movementX_Y_basic-manual.html [ Failure ]
+crbug.com/802067 [ Mac ] external/wpt/pointerevents/pointerlock/pointerevent_movementxy-manual.html [ Failure ]
 
 # We should send PointerLeave events for stylus devices.
 crbug.com/583413 external/wpt/pointerevents/pointerevent_pointerleave_pen-manual.html  [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 659df7f..4d52231 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -103056,6 +103056,56 @@
      {}
     ]
    ],
+   "cookies/prefix/__host.document-cookie.non-secure-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/prefix/__host.http.non-secure-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/prefix/__secure.document-cookie.non-secure-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/prefix/__secure.http.non-secure-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/prefix/__secure.http.secure-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/prefix/document-cookie.non-secure-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/cookie-helper.sub.js": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/drop.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/dropSameSite.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/dropSecure.py": [
+    [
+     {}
+    ]
+   ],
    "cookies/resources/echo-cookie.html": [
     [
      {}
@@ -103066,16 +103116,116 @@
      {}
     ]
    ],
+   "cookies/resources/helpers.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/imgIfMatch.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/list.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/postToParent.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/redirectWithCORSHeaders.py": [
+    [
+     {}
+    ]
+   ],
    "cookies/resources/set-cookie.py": [
     [
      {}
     ]
    ],
+   "cookies/resources/set.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/setSameSite.py": [
+    [
+     {}
+    ]
+   ],
+   "cookies/resources/setSecure.py": [
+    [
+     {}
+    ]
+   ],
    "cookies/resources/testharness-helpers.js": [
     [
      {}
     ]
    ],
+   "cookies/samesite/fetch-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/form-get-blank-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/form-get-blank-reload-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/form-post-blank-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/form-post-blank-reload-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/iframe-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/iframe-reload-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/img-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/window-open-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/samesite/window-open-reload-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/secure/cookie-forcing-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "cookies/secure/create-cookie-http-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "cookies/secure/set-from-http.https.sub.html.headers": [
     [
      {}
@@ -164456,6 +164606,11 @@
      {}
     ]
    ],
+   "web-animations/interfaces/Document/getAnimations-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "web-animations/interfaces/KeyframeEffect/constructor-expected.txt": [
     [
      {}
@@ -181786,6 +181941,114 @@
      {}
     ]
    ],
+   "cookies/prefix/__host.document-cookie.non-secure.html": [
+    [
+     "/cookies/prefix/__host.document-cookie.non-secure.html",
+     {}
+    ]
+   ],
+   "cookies/prefix/__host.http.non-secure.html": [
+    [
+     "/cookies/prefix/__host.http.non-secure.html",
+     {}
+    ]
+   ],
+   "cookies/prefix/__secure.document-cookie.non-secure.html": [
+    [
+     "/cookies/prefix/__secure.document-cookie.non-secure.html",
+     {}
+    ]
+   ],
+   "cookies/prefix/__secure.http.non-secure.html": [
+    [
+     "/cookies/prefix/__secure.http.non-secure.html",
+     {}
+    ]
+   ],
+   "cookies/prefix/__secure.http.secure.html": [
+    [
+     "/cookies/prefix/__secure.http.secure.html",
+     {}
+    ]
+   ],
+   "cookies/prefix/document-cookie.non-secure.html": [
+    [
+     "/cookies/prefix/document-cookie.non-secure.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/fetch.html": [
+    [
+     "/cookies/samesite/fetch.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/form-get-blank-reload.html": [
+    [
+     "/cookies/samesite/form-get-blank-reload.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/form-get-blank.html": [
+    [
+     "/cookies/samesite/form-get-blank.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/form-post-blank-reload.html": [
+    [
+     "/cookies/samesite/form-post-blank-reload.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/form-post-blank.html": [
+    [
+     "/cookies/samesite/form-post-blank.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/iframe-reload.html": [
+    [
+     "/cookies/samesite/iframe-reload.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/iframe.html": [
+    [
+     "/cookies/samesite/iframe.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/img.html": [
+    [
+     "/cookies/samesite/img.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/window-open-reload.html": [
+    [
+     "/cookies/samesite/window-open-reload.html",
+     {}
+    ]
+   ],
+   "cookies/samesite/window-open.html": [
+    [
+     "/cookies/samesite/window-open.html",
+     {}
+    ]
+   ],
+   "cookies/secure/cookie-forcing.html": [
+    [
+     "/cookies/secure/cookie-forcing.html",
+     {}
+    ]
+   ],
+   "cookies/secure/create-cookie-http.html": [
+    [
+     "/cookies/secure/create-cookie-http.html",
+     {}
+    ]
+   ],
    "cookies/secure/set-from-dom.https.sub.html": [
     [
      "/cookies/secure/set-from-dom.https.sub.html",
@@ -250206,7 +250469,7 @@
    "support"
   ],
   "./README.md": [
-   "ab5642d1f91579c4a86fbc56a10de5dbe6623cf3",
+   "2ad80e88dfd9bd3ce7cf4d1b9e53b7d61c104881",
    "support"
   ],
   "./lint.whitelist": [
@@ -262349,6 +262612,70 @@
    "a54ac9afd9c176da2c8844389feb3cb0150fcf5c",
    "testharness"
   ],
+  "cookies/prefix/__host.document-cookie.non-secure-expected.txt": [
+   "8b49d4c16114f18a25ce9a7a8a0cf8a9df6cbc37",
+   "support"
+  ],
+  "cookies/prefix/__host.document-cookie.non-secure.html": [
+   "a072be8d6f7759b238229a7da84f303a71972427",
+   "testharness"
+  ],
+  "cookies/prefix/__host.http.non-secure-expected.txt": [
+   "2fd7bd684881edc2a531ff3878f22f9f86e74467",
+   "support"
+  ],
+  "cookies/prefix/__host.http.non-secure.html": [
+   "9eaab5c903e5d43cdc5e370dd27de5a718185df9",
+   "testharness"
+  ],
+  "cookies/prefix/__secure.document-cookie.non-secure-expected.txt": [
+   "8b49d4c16114f18a25ce9a7a8a0cf8a9df6cbc37",
+   "support"
+  ],
+  "cookies/prefix/__secure.document-cookie.non-secure.html": [
+   "279ff6ce295ec02371c5cfc48b316cfc8536c375",
+   "testharness"
+  ],
+  "cookies/prefix/__secure.http.non-secure-expected.txt": [
+   "2fd7bd684881edc2a531ff3878f22f9f86e74467",
+   "support"
+  ],
+  "cookies/prefix/__secure.http.non-secure.html": [
+   "4e17d8f6a8bdf7f1cc9c2b4b37853a36a13fb19f",
+   "testharness"
+  ],
+  "cookies/prefix/__secure.http.secure-expected.txt": [
+   "398e4a0746acf29f8ba426a9b36c3fca3688de42",
+   "support"
+  ],
+  "cookies/prefix/__secure.http.secure.html": [
+   "5406914f2316c92381dc696f56d20880cd1c85c8",
+   "testharness"
+  ],
+  "cookies/prefix/document-cookie.non-secure-expected.txt": [
+   "34effe9ed6dc641c6d44026916755537a6eb8c7c",
+   "support"
+  ],
+  "cookies/prefix/document-cookie.non-secure.html": [
+   "24d3cd4ad985061202e51a2a78f0ad242c98f54a",
+   "testharness"
+  ],
+  "cookies/resources/cookie-helper.sub.js": [
+   "a5cfe0b65ee4c71ea97731624974ab1b6077b60c",
+   "support"
+  ],
+  "cookies/resources/drop.py": [
+   "aa45e485a184b8dd198961e44115d1d99055dc8e",
+   "support"
+  ],
+  "cookies/resources/dropSameSite.py": [
+   "4307153c427b3e8f5f7648f2c8bb792e7993b87c",
+   "support"
+  ],
+  "cookies/resources/dropSecure.py": [
+   "1a98ab7a248905451bca88cfd3aa0520bccb632a",
+   "support"
+  ],
   "cookies/resources/echo-cookie.html": [
    "ac15291ad85804f61bf690525bad958bb2672ca0",
    "support"
@@ -262357,14 +262684,142 @@
    "c5b3d11bdfc3a4092d0ed53648d58e0e3bbd2d91",
    "support"
   ],
+  "cookies/resources/helpers.py": [
+   "0556713af62d78b7bfcda814c798397346290e31",
+   "support"
+  ],
+  "cookies/resources/imgIfMatch.py": [
+   "7a716e43648f8f9adcc4d04fdc10e278edd976fa",
+   "support"
+  ],
+  "cookies/resources/list.py": [
+   "e979b357defcf76e9783d394943b8d31a64d7470",
+   "support"
+  ],
+  "cookies/resources/postToParent.py": [
+   "01ee8dfd0c1c1261f46df1eb4198b8222a2e8507",
+   "support"
+  ],
+  "cookies/resources/redirectWithCORSHeaders.py": [
+   "a49a81930eeb58c75a1a3eda7fdd813dbe151f55",
+   "support"
+  ],
   "cookies/resources/set-cookie.py": [
    "6588680f5635aa742b8f0cb6956fe0a0521d6b09",
    "support"
   ],
+  "cookies/resources/set.py": [
+   "a4140b21cd6e33dd862b9e0222b50726c85308f0",
+   "support"
+  ],
+  "cookies/resources/setSameSite.py": [
+   "30bc88a6cea9468d3f09a41614d75798f62ed1e9",
+   "support"
+  ],
+  "cookies/resources/setSecure.py": [
+   "bfa50db57417501b8594a22d1c9b957b35701842",
+   "support"
+  ],
   "cookies/resources/testharness-helpers.js": [
    "f379feb76ff0d44c5ec32d4d5b3b0b443c85805f",
    "support"
   ],
+  "cookies/samesite/fetch-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/fetch.html": [
+   "c0e779b35da6d159808e20e9bb33bdbf49710480",
+   "testharness"
+  ],
+  "cookies/samesite/form-get-blank-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/form-get-blank-reload-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/form-get-blank-reload.html": [
+   "b99dcb698dcd9680dabf2d794cb64de35aded40c",
+   "testharness"
+  ],
+  "cookies/samesite/form-get-blank.html": [
+   "28193985017094bb89ff73987f16a5b7493e21a4",
+   "testharness"
+  ],
+  "cookies/samesite/form-post-blank-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/form-post-blank-reload-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/form-post-blank-reload.html": [
+   "c33495a3010b7a872592e0afb3c6aec1e072db30",
+   "testharness"
+  ],
+  "cookies/samesite/form-post-blank.html": [
+   "040e6533a608448f6c184eb4aaea99594b2ff99c",
+   "testharness"
+  ],
+  "cookies/samesite/iframe-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/iframe-reload-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/iframe-reload.html": [
+   "9004c0afa1c0938810fa6b127b5c74a668d252d8",
+   "testharness"
+  ],
+  "cookies/samesite/iframe.html": [
+   "0b0de2fe93ae013f0339fc18ecc02b74139e7085",
+   "testharness"
+  ],
+  "cookies/samesite/img-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/img.html": [
+   "0de8d54b2d3b19378de9b62273cd92e326f0eff4",
+   "testharness"
+  ],
+  "cookies/samesite/window-open-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/window-open-reload-expected.txt": [
+   "a32fd4eaa3120d52fb9c884a2b3470dacc48b084",
+   "support"
+  ],
+  "cookies/samesite/window-open-reload.html": [
+   "8effc29255860c066e7bb6e8355721fc14708674",
+   "testharness"
+  ],
+  "cookies/samesite/window-open.html": [
+   "97bb86fc8d992f8d639c9cc0af8ec77d0f810843",
+   "testharness"
+  ],
+  "cookies/secure/cookie-forcing-expected.txt": [
+   "47145d57050f1c9058a8c0954b497ece232b9a1d",
+   "support"
+  ],
+  "cookies/secure/cookie-forcing.html": [
+   "2c556af03603bc1022780620336e9de42526d187",
+   "testharness"
+  ],
+  "cookies/secure/create-cookie-http-expected.txt": [
+   "5fedc3c7e7bcaa5cccc55486d1aac3ddc7ccdafe",
+   "support"
+  ],
+  "cookies/secure/create-cookie-http.html": [
+   "db6b86d508c4e3db703f190af579865b3a010fe9",
+   "testharness"
+  ],
   "cookies/secure/set-from-dom.https.sub.html": [
    "6532cda9322290949bedc06cdc45ce80521068aa",
    "testharness"
@@ -341854,7 +342309,7 @@
    "support"
   ],
   "html/dom/elements/the-innertext-idl-attribute/getter-expected.txt": [
-   "5e4ba8f6f21667ad950d8ef2f2ce04455ee8ca95",
+   "4a4bfed9f4bac0cfbb9582c3adb578c5bceefb70",
    "support"
   ],
   "html/dom/elements/the-innertext-idl-attribute/getter-tests.js": [
@@ -383805,8 +384260,12 @@
    "d9fc177ebbc3fa0317125912e38a4bfd65f727c8",
    "testharness"
   ],
+  "web-animations/interfaces/Document/getAnimations-expected.txt": [
+   "93fc114032d930aab86038c969ca7389faa49294",
+   "support"
+  ],
   "web-animations/interfaces/Document/getAnimations.html": [
-   "41edbdf1f03889156068f38d87875387f129924f",
+   "6efcb382f3203dc51487b366c13d463d697ac100",
    "testharness"
   ],
   "web-animations/interfaces/Document/timeline.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/README.md b/third_party/WebKit/LayoutTests/external/wpt/README.md
index 182e4e7..e43a05f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/README.md
+++ b/third_party/WebKit/LayoutTests/external/wpt/README.md
@@ -44,10 +44,10 @@
 ./wpt make-hosts-file | sudo tee -a /etc/hosts
 ```
 
-And on Windows (note this requires an Administrator privileged shell):
+And on Windows (this must be run in a PowerShell session with Administrator privileges):
 
 ```bash
-python wpt make-hosts-file >> %SystemRoot%\System32\drivers\etc\hosts
+python wpt make-hosts-file | Out-File %SystemRoot%\System32\drivers\etc\hosts -Encoding ascii -Append
 ```
 
 If you are behind a proxy, you also need to make sure the domains above are
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.document-cookie.non-secure-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.document-cookie.non-secure-expected.txt
new file mode 100644
index 0000000..ba0c795
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.document-cookie.non-secure-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: set_prefixed_cookie_via_dom_test is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.document-cookie.non-secure.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.document-cookie.non-secure.html
new file mode 100644
index 0000000..e1a272a2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.document-cookie.non-secure.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Host-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Host-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+
+  set_prefixed_cookie_via_dom_test({
+    prefix: "__Host-",
+    params: "Secure; Path=/cookies/resources/list.py",
+    shouldExistInDOM: false,
+    shouldExistViaHTTP: false,
+    title: "__Host: Non-secure origin: 'Secure; Path=/cookies/resources/list.py'"
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.http.non-secure-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.http.non-secure-expected.txt
new file mode 100644
index 0000000..4a03101
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.http.non-secure-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: set_prefixed_cookie_via_http_test is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.http.non-secure.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.http.non-secure.html
new file mode 100644
index 0000000..3ce58735
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__host.http.non-secure.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Host-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Host-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Host: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+
+  set_prefixed_cookie_via_http_test({
+    prefix: "__Host-",
+    params: "Secure; Path=/cookies/resources/list.py",
+    shouldExistInDOM: false,
+    shouldExistViaHTTP: false,
+    title: "__Host: Non-secure origin: 'Secure; Path=/cookies/resources/list.py'"
+  });
+</script>
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.document-cookie.non-secure-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.document-cookie.non-secure-expected.txt
new file mode 100644
index 0000000..ba0c795
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.document-cookie.non-secure-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: set_prefixed_cookie_via_dom_test is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.document-cookie.non-secure.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.document-cookie.non-secure.html
new file mode 100644
index 0000000..bf898f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.document-cookie.non-secure.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Secure-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_dom_test({
+      prefix: "__Secure-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.non-secure-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.non-secure-expected.txt
new file mode 100644
index 0000000..4a03101
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.non-secure-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: set_prefixed_cookie_via_http_test is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.non-secure.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.non-secure.html
new file mode 100644
index 0000000..af844a9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.non-secure.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Secure-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_http_test({
+      prefix: "__Secure-",
+      params: "Secure; Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: Non-secure origin: 'Secure; Path=/;" + extraParams + "'"
+    });
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.secure-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.secure-expected.txt
new file mode 100644
index 0000000..3c992678
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.secure-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: CROSS_SITE_HOST is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.secure.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.secure.html
new file mode 100644
index 0000000..4b413e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/__secure.http.secure.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  ["", "domain="+CROSS_SITE_HOST, "MaxAge=10", "HttpOnly"].forEach(extraParams => {
+    // Without 'secure'
+    set_prefixed_cookie_via_http_test({
+      origin: SECURE_CROSS_SITE_ORIGIN,
+      prefix: "__Secure-",
+      params: "Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: false,
+      title: "__Secure: secure origin: 'Path=/;" + extraParams + "'"
+    });
+
+    // With 'secure'
+    set_prefixed_cookie_via_http_test({
+      origin: SECURE_CROSS_SITE_ORIGIN,
+      prefix: "__Secure-",
+      params: "Secure;Path=/;" + extraParams,
+      shouldExistInDOM: false,
+      shouldExistViaHTTP: true,
+      title: "__Secure: secure origin: 'Secure;Path=/;" + extraParams + "'"
+    });
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/document-cookie.non-secure-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/document-cookie.non-secure-expected.txt
new file mode 100644
index 0000000..f7fc644
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/document-cookie.non-secure-expected.txt
@@ -0,0 +1,22 @@
+This is a testharness.js-based test.
+FAIL No prefix, root path, no special behavior erase_cookie_from_js is not defined
+FAIL No prefix, domain, no special behavior erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Path=/;' erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Secure; Path=/;' erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Path=/;domain=web-platform.test' erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Secure; Path=/;domain=web-platform.test' erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Path=/;MaxAge=10' erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Secure; Path=/;MaxAge=10' erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Path=/;HttpOnly' erase_cookie_from_js is not defined
+FAIL __Secure: Non-secure origin: 'Secure; Path=/;HttpOnly' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Path=/; ' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Secure; Path=/; ' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Path=/; domain=web-platform.test' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Secure; Path=/; domain=web-platform.test' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Path=/; MaxAge=10' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Secure; Path=/; MaxAge=10' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Path=/; HttpOnly' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Secure; Path=/; HttpOnly' erase_cookie_from_js is not defined
+FAIL __Host: Non-secure origin: 'Path=/cookies/resources/list.py;Secure' erase_cookie_from_js is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/document-cookie.non-secure.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/document-cookie.non-secure.html
new file mode 100644
index 0000000..bc6832b15
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/prefix/document-cookie.non-secure.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(prefix, params, shouldExistInDOM, shouldExistViaHTTP, title) {
+    promise_test(t => {
+      var name = prefix + "prefixtestcookie";
+      erase_cookie_from_js(name);
+      var value = "" + Math.random();
+      document.cookie = name + "=" + value + ";" + params;
+
+      assert_dom_cookie(name, value, shouldExistInDOM);
+
+      return credFetch("/cookies/rfx6265bis/resources/list.py")
+        .then(r => r.json())
+        .then(cookies => assert_equals(cookies[name], shouldExistViaHTTP ? value : undefined));
+    }, title);
+  }
+
+  // No prefix
+  create_test("", "path=/", true, true, "No prefix, root path, no special behavior");
+  create_test("", "path=/;domain=" + document.location.hostname, true, true, "No prefix, domain, no special behavior");
+
+  // `__Secure-` Prefix
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(params => {
+    create_test("__Secure-", "Path=/;" + params, false, false, "__Secure: Non-secure origin: 'Path=/;" + params + "'");
+    create_test("__Secure-", "Secure; Path=/;" + params, false, false, "__Secure: Non-secure origin: 'Secure; Path=/;" + params + "'");
+  });
+
+  // `__Host-` Prefix
+  ["", "domain="+document.location.hostname, "MaxAge=10", "HttpOnly"].forEach(params => {
+    create_test("__Secure-", "Path=/;" + params, false, false, "__Host: Non-secure origin: 'Path=/; " + params + "'");
+    create_test("__Secure-", "Secure; Path=/;" + params, false, false, "__Host: Non-secure origin: 'Secure; Path=/; " + params + "'");
+  });
+  create_test("__Secure-", "Path=/cookies/resources/list.py;Secure", false, false, "__Host: Non-secure origin: 'Path=/cookies/resources/list.py;Secure'");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/cookie-helper.sub.js b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/cookie-helper.sub.js
new file mode 100644
index 0000000..852fbb6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/cookie-helper.sub.js
@@ -0,0 +1,206 @@
+// Set up exciting global variables for cookie tests.
+(_ => {
+  var HOST = "{{host}}";
+  var SECURE_PORT = ":{{ports[https][0]}}";
+  var PORT = ":{{ports[http][0]}}";
+  var CROSS_ORIGIN_HOST = "{{hosts[alt][]}}";
+  var SECURE_CROSS_ORIGIN_HOST = "{{hosts[alt][]}}";
+
+  //For secure cookie verification
+  window.SECURE_ORIGIN = "https://" + HOST + SECURE_PORT;
+  window.INSECURE_ORIGIN = "http://" + HOST + PORT;
+
+  //standard references
+  window.ORIGIN = "http://" + HOST + PORT;
+  window.WWW_ORIGIN = "http://{{domains[www]}}" + PORT;
+  window.SUBDOMAIN_ORIGIN = "http://{{domains[www1]}}" + PORT;
+  window.CROSS_SITE_ORIGIN = "http://" + CROSS_ORIGIN_HOST + PORT;
+  window.SECURE_CROSS_SITE_ORIGIN = "https://" + SECURE_CROSS_ORIGIN_HOST + SECURE_PORT;
+  window.CROSS_SITE_HOST = SECURE_CROSS_ORIGIN_HOST;
+
+  // Set the global cookie name.
+  window.HTTP_COOKIE = "cookie_via_http";
+
+  // If we're not on |HOST|, move ourselves there:
+  if (window.location.hostname != HOST)
+    window.location.hostname = HOST;
+})();
+
+// A tiny helper which returns the result of fetching |url| with credentials.
+function credFetch(url) {
+  return fetch(url, {"credentials": "include"});
+}
+
+// Returns a URL on |origin| which redirects to a given absolute URL.
+function redirectTo(origin, url) {
+  return origin + "/cookies/resources/redirectWithCORSHeaders.py?status=307&location=" + encodeURIComponent(url);
+}
+
+// Asserts that `document.cookie` contains or does not contain (according to
+// the value of |present|) a cookie named |name| with a value of |value|.
+function assert_dom_cookie(name, value, present) {
+  var re = new RegExp("(?:^|; )" + name + "=" + value + "(?:$|;)");
+  assert_equals(re.test(document.cookie), present, "`" + name + "=" + value + "` in `document.cookie`");
+}
+
+function assert_cookie(origin, obj, name, value, present) {
+  assert_equals(obj[name], present ? value : undefined, "`" + name + "=" + value + "` in request to `" + origin + "`.");
+}
+
+// Remove the cookie named |name| from |origin|, then set it on |origin| anew.
+// If |origin| matches `document.origin`, also assert (via `document.cookie`) that
+// the cookie was correctly removed and reset.
+function create_cookie(origin, name, value, extras) {
+  alert("Create_cookie: " + origin + "/cookies/resources/drop.py?name=" + name);
+  return credFetch(origin + "/cookies/resources/drop.py?name=" + name)
+    .then(_ => {
+      if (origin == document.origin)
+        assert_dom_cookie(name, value, false);
+    })
+    .then(_ => {
+      return credFetch(origin + "/cookies/resources/set.py?" + name + "=" + value + ";path=/;" + extras)
+        .then(_ => {
+          if (origin == document.origin)
+            assert_dom_cookie(name, value, true);
+        });
+    });
+}
+
+//
+// Prefix-specific test helpers
+//
+function set_prefixed_cookie_via_dom_test(options) {
+  promise_test(t => {
+    var name = options.prefix + "prefixtestcookie";
+    erase_cookie_from_js(name);
+    var value = "" + Math.random();
+    document.cookie = name + "=" + value + ";" + options.params;
+
+    assert_dom_cookie(name, value, options.shouldExistInDOM);
+
+    return credFetch("/cookies/resources/list.py")
+      .then(r => r.json())
+      .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
+  }, options.title);
+}
+
+function set_prefixed_cookie_via_http_test(options) {
+  promise_test(t => {
+    var postDelete = _ => {
+      var value = "" + Math.random();
+      return credFetch(options.origin + "/cookies/resources/set.py?" + name + "=" + value + ";" + options.params)
+        .then(_ => credFetch(options.origin + "/cookies/resources/list.py"))
+        .then(r => r.json())
+        .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
+    };
+
+    var name = options.prefix + "prefixtestcookie";
+    if (!options.origin) {
+      options.origin = document.origin;
+      erase_cookie_from_js(name);
+      return postDelete;
+    } else {
+      return credFetch(options.origin + "/cookies/resources/drop.py?name=" + name)
+        .then(_ => postDelete());
+    }
+  }, options.title);
+}
+
+//
+// SameSite-specific test helpers:
+//
+
+window.SameSiteStatus = {
+  CROSS_SITE: "cross-site",
+  LAX: "lax",
+  STRICT: "strict"
+};
+
+// Reset SameSite test cookies on |origin|. If |origin| matches `document.origin`, assert
+// (via `document.cookie`) that they were properly removed and reset.
+function resetSameSiteCookies(origin, value) {
+  return credFetch(origin + "/cookies/resources/dropSameSite.py")
+    .then(_ => {
+      if (origin == document.origin) {
+        assert_dom_cookie("samesite_strict", value, false);
+        assert_dom_cookie("samesite_lax", value, false);
+        assert_dom_cookie("samesite_none", value, false);
+      }
+    })
+    .then(_ => {
+      return credFetch(origin + "/cookies/resources/setSameSite.py?" + value)
+        .then(_ => {
+          if (origin == document.origin) {
+            assert_dom_cookie("samesite_strict", value, true);
+            assert_dom_cookie("samesite_lax", value, true);
+            assert_dom_cookie("samesite_none", value, true);
+          }
+        })
+    })
+}
+
+// Given an |expectedStatus| and |expectedValue|, assert the |cookies| contains the
+// proper set of cookie names and values.
+function verifySameSiteCookieState(expectedStatus, expectedValue, cookies) {
+    assert_equals(cookies["samesite_none"], expectedValue, "Non-SameSite cookies are always sent.");
+    if (expectedStatus == SameSiteStatus.CROSS_SITE) {
+      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with cross-site requests.");
+      assert_not_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are not sent with cross-site requests.");
+    } else if (expectedStatus == SameSiteStatus.LAX) {
+      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with lax requests.");
+      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with lax requests.");
+    } else if (expectedStatus == SameSiteStatus.STRICT) {
+      assert_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are sent with strict requests.");
+      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with strict requests.");
+    }
+}
+
+//
+// LeaveSecureCookiesAlone-specific test helpers:
+//
+
+window.SecureStatus = {
+  INSECURE_COOKIE_ONLY: "1",
+  BOTH_COOKIES: "2",
+};
+
+//Reset SameSite test cookies on |origin|. If |origin| matches `document.origin`, assert
+//(via `document.cookie`) that they were properly removed and reset.
+function resetSecureCookies(origin, value) {
+return credFetch(origin + "/cookies/resources/dropSecure.py")
+ .then(_ => {
+   if (origin == document.origin) {
+     assert_dom_cookie("alone_secure", value, false);
+     assert_dom_cookie("alone_insecure", value, false);
+   }
+ })
+ .then(_ => {
+     return credFetch(origin + "/cookie/resources/setSecure.py?" + value)
+ })
+}
+
+//
+// DOM based cookie manipulation API's
+//
+
+// borrowed from http://www.quirksmode.org/js/cookies.html
+function create_cookie_from_js(name, value, days, secure_flag) {
+  if (days) {
+    var date = new Date();
+    date.setTime(date.getTime()+(days*24*60*60*1000));
+    var expires = "; expires="+date.toGMTString();
+  }
+  else var expires = "";
+
+  var secure = "";
+  if (secure_flag == true) {
+    secure = "secure; ";
+  }
+  document.cookie = name+"="+value+expires+"; "+secure+"path=/";
+}
+
+// erase cookie value and set for expiration
+function erase_cookie_from_js(name) {
+  create_cookie_from_js(name,"",-1);
+  assert_dom_cookie(name, "", false);
+}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/drop.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/drop.py
new file mode 100644
index 0000000..7491dad
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/drop.py
@@ -0,0 +1,15 @@
+from helpers import makeDropCookie, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/drop?name={name}` by expiring the cookie named `{name}`."""
+    headers = setNoCacheAndCORSHeaders(request, response)
+    try:
+        # Expire the named cookie, and return a JSON-encoded success code.
+        name = readParameter(request, paramName="name", requireValue=True)
+        scheme = request.url_parts.scheme
+        headers.append(makeDropCookie(name,  "https" == scheme))
+        return headers, '{"success": true}'
+    except:
+        return 500, headers, '{"error" : "Empty or missing name parameter."}'
+
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/dropSameSite.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/dropSameSite.py
new file mode 100644
index 0000000..803dbeb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/dropSameSite.py
@@ -0,0 +1,12 @@
+from helpers import makeDropCookie, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/same-site/resources/dropSameSite.py by dropping the
+    three cookies set by setSameSiteCookies.py"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+
+    # Expire the cookies, and return a JSON-encoded success code.
+    headers.append(makeDropCookie("samesite_strict", False))
+    headers.append(makeDropCookie("samesite_lax", False))
+    headers.append(makeDropCookie("samesite_none", False))
+    return headers, '{"success": true}'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/dropSecure.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/dropSecure.py
new file mode 100644
index 0000000..f95e9a9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/dropSecure.py
@@ -0,0 +1,11 @@
+from helpers import makeDropCookie, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/drop/secure` by dropping the two cookie set by
+    `setSecureTestCookies()`"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+
+    # Expire the cookies, and return a JSON-encoded success code.
+    headers.append(makeDropCookie("alone_secure", False))
+    headers.append(makeDropCookie("alone_insecure", False))
+    return headers, '{"success": true}'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/helpers.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/helpers.py
new file mode 100644
index 0000000..145f2fe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/helpers.py
@@ -0,0 +1,55 @@
+import urlparse
+
+def setNoCacheAndCORSHeaders(request, response):
+    """Set Cache-Control, CORS and Content-Type headers appropriate for the cookie tests."""
+    headers = [("Content-Type", "application/json"),
+               ("Access-Control-Allow-Credentials", "true")]
+
+    origin = "*"
+    if "origin" in request.headers:
+        origin = request.headers["origin"]
+
+    headers.append(("Access-Control-Allow-Origin", origin))
+    #headers.append(("Access-Control-Allow-Credentials", "true"))
+    headers.append(("Cache-Control", "no-cache"))
+    headers.append(("Expires", "Fri, 01 Jan 1990 00:00:00 GMT"))
+
+    return headers
+
+def makeCookieHeader(name, value, otherAttrs):
+    """Make a Set-Cookie header for a cookie with the name, value and attributes provided."""
+    def makeAV(a, v):
+        if None == v or "" == v:
+            return a
+        return "%s=%s" % (a, v)
+
+    # ensure cookie name is always first
+    attrs = ["%s=%s" % (name, value)]
+    attrs.extend(makeAV(a, v) for (a,v) in otherAttrs.iteritems())
+    return ("Set-Cookie", "; ".join(attrs))
+
+def makeDropCookie(name, secure):
+    attrs = {"MaxAge": 0, "path": "/"}
+    if secure:
+        attrs["secure"] = ""
+    return makeCookieHeader(name, "", attrs)
+
+def readParameter(request, paramName, requireValue):
+    """Read a parameter from the request. Raise if requireValue is set and the
+    parameter has an empty value or is not present."""
+    params = urlparse.parse_qs(request.url_parts.query)
+    param = params[paramName][0].strip()
+    if len(param) == 0:
+        raise Exception("Empty or missing name parameter.")
+    return param
+
+def readCookies(request):
+    """Read the cookies from the client present in the request."""
+    cookies = {}
+    for key in request.cookies:
+        for cookie in request.cookies.get_list(key):
+            # do we care we'll clobber cookies here? If so, do we
+            # need to modify the test to take cookie names and value lists?
+            cookies[key] = cookie.value
+    return cookies
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/imgIfMatch.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/imgIfMatch.py
new file mode 100644
index 0000000..08664415
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/imgIfMatch.py
@@ -0,0 +1,16 @@
+import helpers
+
+def main(request, response):
+    """Respond to `/cookie/imgIfMatch?name={name}&value={value}` with a 404 if
+       the cookie isn't present, and a transparent GIF otherwise."""
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    name = helpers.readParameter(request, paramName="name", requireValue=True)
+    value = helpers.readParameter(request, paramName="value", requireValue=True)
+    cookiesWithMatchingNames = request.cookies.get_list(name)
+    for cookie in cookiesWithMatchingNames:
+        if cookie.value == value:
+            # From https://github.com/mathiasbynens/small/blob/master/gif-transparent.gif
+            headers.append(("Content-Type","image/gif"))
+            gif = "\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xFF\xFF\xFF\x00\x00\x00\x21\xF9\x04\x01\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"
+            return headers, gif
+    return 500, headers, '{"error": {"message": "The cookie\'s value did not match the given value."}}'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/list.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/list.py
new file mode 100644
index 0000000..3fe7dd6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/list.py
@@ -0,0 +1,7 @@
+import json
+import helpers
+
+def main(request, response):
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    cookies = helpers.readCookies(request)
+    return headers, json.dumps(cookies)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/postToParent.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/postToParent.py
new file mode 100644
index 0000000..68e85d3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/postToParent.py
@@ -0,0 +1,27 @@
+import json
+import helpers
+
+def main(request, response):
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    cookies = helpers.readCookies(request)
+    headers.append(("Content-Type", "text/html; charset=utf-8"))
+
+    tmpl = """
+<!DOCTYPE html>
+<script>
+  var data = %s;
+
+  if (window.parent != window)
+    window.parent.postMessage(data, "*");
+
+  if (window.opener)
+    window.opener.postMessage(data, "*");
+
+  window.addEventListener("message", e => {
+    console.log(e);
+    if (e.data == "reload")
+      window.location.reload();
+  });
+</script>
+"""
+    return headers, tmpl % json.dumps(cookies)
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/redirectWithCORSHeaders.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/redirectWithCORSHeaders.py
new file mode 100644
index 0000000..89ca1af
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/redirectWithCORSHeaders.py
@@ -0,0 +1,22 @@
+from helpers import setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Simple handler that causes redirection.
+
+    The request should typically have two query parameters:
+    status - The status to use for the redirection. Defaults to 302.
+    location - The resource to redirect to.
+    """
+    status = 302
+    if "status" in request.GET:
+        try:
+            status = int(request.GET.first("status"))
+        except ValueError:
+            pass
+    headers = setNoCacheAndCORSHeaders(request, response)
+
+    location = request.GET.first("location")
+
+    headers.append(("Location", location))
+
+    return status, headers, ""
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/set.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/set.py
new file mode 100644
index 0000000..abfb8c8d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/set.py
@@ -0,0 +1,7 @@
+import helpers
+
+def main(request, response):
+    """Respond to `/cookie/set?{cookie}` by echoing `{cookie}` as a `Set-Cookie` header."""
+    headers = helpers.setNoCacheAndCORSHeaders(request, response)
+    headers.append(("Set-Cookie", request.url_parts.query))
+    return headers, '{"success": true}'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/setSameSite.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/setSameSite.py
new file mode 100644
index 0000000..8ae1776
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/setSameSite.py
@@ -0,0 +1,14 @@
+from helpers import makeCookieHeader, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/set/samesite?{value}` by setting three cookies:
+    1. `samesite_strict={value};SameSite=Strict;path=/`
+    2. `samesite_lax={value};SameSite=Lax;path=/`
+    3. `samesite_none={value};path=/`"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+    value = request.url_parts.query
+
+    headers.append(makeCookieHeader("samesite_strict", value, {"SameSite":"Strict","path":"/"}))
+    headers.append(makeCookieHeader("samesite_lax", value, {"SameSite":"Lax","path":"/"}))
+    headers.append(makeCookieHeader("samesite_none", value, {"path":"/"}))
+    return headers, '{"success": true}'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/setSecure.py b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/setSecure.py
new file mode 100644
index 0000000..c8ec017
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/resources/setSecure.py
@@ -0,0 +1,12 @@
+from helpers import makeCookieHeader, readParameter, setNoCacheAndCORSHeaders
+
+def main(request, response):
+    """Respond to `/cookie/set/secure?{value}` by setting two cookies:
+    alone_secure={value};secure;path=/`
+    alone_insecure={value};path=/"""
+    headers = setNoCacheAndCORSHeaders(request, response)
+    value = request.url_parts.query
+
+    headers.append(makeCookieHeader("alone_secure", value, {"secure": "","path": "/"}))
+    headers.append(makeCookieHeader("alone_insecure", value, {"path": "/"}))
+    return headers, '{"success": true}'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/fetch-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/fetch-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/fetch-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/fetch.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/fetch.html
new file mode 100644
index 0000000..734462a3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/fetch.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return credFetch(target + "/cookies/resources/list.py")
+
+            .then(r => r.json())
+            .then(cookies => verifySameSiteCookieState(expectedStatus, value, cookies));
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain fetches are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site fetches are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site fetches are cross-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-reload-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-reload-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-reload-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-reload.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-reload.html
new file mode 100644
index 0000000..09f3ee9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank-reload.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "GET";
+
+            // If |target| contains a `redir` parameter, extract it, and add it
+            // to the form so it doesn't get dropped in the submission.
+            var url = new URL(f.action);
+            if (url.pathname = "/cookies/rfc6265/resources/redirectWithCORSHeaders.py") {
+              var i = document.createElement("input");
+              i.name = "location";
+              i.value = url.searchParams.get("location");
+              i.type = "hidden";
+              f.appendChild(i);
+            }
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                e.source.close();
+                resolve("Popup received the cookie.");
+              } else {
+                reloaded = true;
+                e.source.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+            document.body.appendChild(f);
+
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain top-level form GETs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Reloaded cross-site top-level form GETs are laxly same-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank.html
new file mode 100644
index 0000000..a86f34b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-get-blank.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "GET";
+
+            // If |target| contains a `redir` parameter, extract it, and add it
+            // to the form so it doesn't get dropped in the submission.
+            var url = new URL(f.action);
+            if (url.pathname == "/cookies/resources/redirectWithCORSHeaders.py") {
+              var i = document.createElement("input");
+              i.name = "location";
+              i.type="hidden";
+              i.value = url.searchParams.get("location");
+              f.appendChild(i);
+            }
+
+            var msgHandler = e => {
+              window.removeEventListener("message", msgHandler);
+              e.source.close();
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+                resolve("Popup received the cookie.");
+              } catch (e) {
+                reject(e);
+              }
+            };
+            window.addEventListener("message", msgHandler);
+            document.body.appendChild(f);
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain top-level form GETs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Cross-site top-level form GETs are laxly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host top-level form GETs are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host top-level form GETs are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host top-level form GETs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain top-level form GETs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain top-level form GETs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Same-host redirecting to cross-site top-level form GETs are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Subdomain redirecting to cross-site top-level form GETs are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Cross-site redirecting to cross-site top-level form GETs are laxly same-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-reload-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-reload-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-reload-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-reload.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-reload.html
new file mode 100644
index 0000000..f9449bf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank-reload.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "POST";
+
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                e.source.close();
+                resolve("Popup received the cookie.");
+              } else {
+                reloaded = true;
+                e.source.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            document.body.appendChild(f);
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain top-level form POSTs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Reloaded cross-site top-level form POSTs are not same-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank.html
new file mode 100644
index 0000000..115c6a1
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/form-post-blank.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var f = document.createElement('form');
+            f.action = target + "/cookies/resources/postToParent.py";
+            f.target = "_blank";
+            f.method = "POST";
+
+            var msgHandler = e => {
+              window.removeEventListener("message", msgHandler);
+              e.source.close();
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+                resolve("Popup received the cookie.");
+              } catch (e) {
+                reject(e);
+              }
+            };
+            window.addEventListener("message", msgHandler);
+            document.body.appendChild(f);
+            f.submit();
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain top-level form POSTs are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site top-level form POSTs are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host top-level form POSTs are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host top-level form POSTs are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host top-level form POSTs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain top-level form POSTs are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain top-level form POSTs are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site top-level form POSTs are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site top-level form POSTs are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site top-level form POSTs are cross-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-reload-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-reload-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-reload-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-reload.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-reload.html
new file mode 100644
index 0000000..759fc7b0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe-reload.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<!-- We're appending an <iframe> to the document's body, so execute tests after we have a body -->
+<body>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var iframe = document.createElement("iframe");
+            iframe.onerror = _ => reject("IFrame could not be loaded.");
+
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                document.body.removeChild(iframe);
+                resolve("IFrame received the cookie.");
+              } else {
+                reloaded = true;
+                e.source.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            iframe.src = target + "/cookies/resources/postToParent.py";
+            document.body.appendChild(iframe);
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain fetches are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Reloaded cross-site fetches are cross-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe.html
new file mode 100644
index 0000000..38a7701
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/iframe.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<!-- We're appending an <iframe> to the document's body, so execute tests after we have a body -->
+<body>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var iframe = document.createElement("iframe");
+            iframe.onerror = _ => reject("IFrame could not be loaded.");
+
+            var msgHandler = e => {
+              if (e.source == iframe.contentWindow) {
+                // Cleanup, then verify cookie state:
+                document.body.removeChild(iframe);
+                window.removeEventListener("message", msgHandler);
+                try {
+                  verifySameSiteCookieState(expectedStatus, value, e.data);
+                  resolve();
+                } catch(e) {
+                  reject(e);
+                }
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            iframe.src = target + "/cookies/resources/postToParent.py";
+            document.body.appendChild(iframe);
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain fetches are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site fetches are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host fetches are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain fetches are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain fetches are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site fetches are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site fetches are cross-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/img-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/img-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/img-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/img.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/img.html
new file mode 100644
index 0000000..b1b04340
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/img.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function assert_cookie_present(origin, name, value) {
+    return new Promise((resolve, reject) => {
+      var img = document.createElement("img");
+      img.onload = _ => resolve("'" + name + "=" + value + "' present on " + origin);
+      img.onerror = _ => reject("'" + name + "=" + value + "' not present on " + origin);
+
+      // We need to URL encode the destination path/query if we're redirecting:
+      if (origin.match(/\/redir/))
+        img.src = origin + encodeURIComponent("/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value);
+      else
+        img.src = origin + "/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value;
+    });
+  }
+
+  function assert_cookie_absent(origin, name, value) {
+    return new Promise((resolve, reject) => {
+      var img = document.createElement("img");
+      img.onload = _ => reject("'" + name + "=" + value + "' present on " + origin);
+      img.onerror = _ => resolve("'" + name + "=" + value + "' not present on " + origin);
+
+      // We need to URL encode the destination path/query if we're redirecting:
+      if (origin.match(/\/redir/))
+        img.src = origin + encodeURIComponent("/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value);
+      else
+        img.src = origin + "/cookies/resources/imgIfMatch.py?name=" + name + "&value=" + value;
+    });
+  }
+
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return Promise.all([
+            assert_cookie_present(target, "samesite_none", value),
+            expectedStatus == SameSiteStatus.STRICT ?
+              assert_cookie_present(target, "samesite_strict", value) :
+              assert_cookie_absent(target, "samesite_strict", value),
+            expectedStatus == SameSiteStatus.CROSS_SITE ?
+              assert_cookie_absent(target, "samesite_lax", value) :
+              assert_cookie_present(target, "samesite_lax", value)
+          ]);
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host images are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain images are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.CROSS_SITE, "Cross-site images are cross-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host images are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host images are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host images are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain images are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain images are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain images are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Same-host redirecting to cross-site images are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Subdomain redirecting to cross-site images are cross-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.CROSS_SITE, "Cross-site redirecting to cross-site images are cross-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-reload-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-reload-expected.txt
new file mode 100644
index 0000000..88cc5d0
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-reload-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-reload.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-reload.html
new file mode 100644
index 0000000..b37cff8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open-reload.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var w = window.open(origin + "/cookies/resources/postToParent.py");
+
+            var reloaded = false;
+            var msgHandler = e => {
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+              } catch (e) {
+                reject(e);
+              }
+
+              if (reloaded) {
+                window.removeEventListener("message", msgHandler);
+                w.close();
+                resolve("Popup received the cookie.");
+              } else {
+                reloaded = true;
+                w.postMessage("reload", "*");
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            if (!w)
+              reject("Popup could not be opened (did you whitelist the test site in your popup blocker?).");
+          });
+        });
+    }, title);
+  }
+
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Reloaded same-host auxiliary navigations are strictly same-site.");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Reloaded subdomain auxiliary navigations are strictly same-site.");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Reloaded ross-site auxiliary navigations are laxly same-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open.html
new file mode 100644
index 0000000..1aa8e5e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/samesite/window-open.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSameSiteCookies(origin, value)
+        .then(_ => {
+          return new Promise((resolve, reject) => {
+            var w = window.open(origin + "/cookies/resources/postToParent.py");
+
+            var msgHandler = e => {
+              window.removeEventListener("message", msgHandler);
+              w.close();
+              try {
+                verifySameSiteCookieState(expectedStatus, value, e.data);
+                resolve("Popup received the cookie.");
+              } catch (e) {
+                reject(e);
+              }
+            };
+            window.addEventListener("message", msgHandler);
+
+            if (!w)
+              reject("Popup could not be opened (did you whitelist the test site in your popup blocker?).");
+          });
+        });
+    }, title);
+  }
+
+  // No redirect:
+  create_test(ORIGIN, ORIGIN, SameSiteStatus.STRICT, "Same-host auxiliary navigations are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN, SameSiteStatus.STRICT, "Subdomain auxiliary navigations are strictly same-site");
+  create_test(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN, SameSiteStatus.LAX, "Cross-site auxiliary navigations are laxly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(ORIGIN, redirectTo(ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to same-host auxiliary navigations are strictly same-site");
+  create_test(ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to same-host auxiliary navigations are strictly same-site");
+  create_test(ORIGIN, redirectTo(CROSS_SITE_ORIGIN, ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to same-host auxiliary navigations are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to same-host:
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Same-host redirecting to subdomain auxiliary navigations are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Subdomain redirecting to subdomain auxiliary navigations are strictly same-site");
+  create_test(SUBDOMAIN_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, SUBDOMAIN_ORIGIN), SameSiteStatus.STRICT, "Cross-site redirecting to subdomain auxiliary navigations are strictly same-site");
+
+  // Redirect from {same-host,subdomain,cross-site} to cross-site:
+  create_test(CROSS_SITE_ORIGIN, redirectTo(ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Same-host redirecting to cross-site auxiliary navigations are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(SUBDOMAIN_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Subdomain redirecting to cross-site auxiliary navigations are laxly same-site");
+  create_test(CROSS_SITE_ORIGIN, redirectTo(CROSS_SITE_ORIGIN, CROSS_SITE_ORIGIN), SameSiteStatus.LAX, "Cross-site redirecting to cross-site auxiliary navigations are laxly same-site");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/cookie-forcing-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/cookie-forcing-expected.txt
new file mode 100644
index 0000000..4b5d3f28
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/cookie-forcing-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL non-secure origins should be able to force out insecure cookies. create_cookie_from_js is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/cookie-forcing.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/cookie-forcing.html
new file mode 100644
index 0000000..3ea59e13
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/cookie-forcing.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  function cookie_force_test(secure_origin, secure_cookie, present, title) {
+    var counter = 0;
+    promise_test(t => {
+
+      var testCookieValue = "" + Math.random();
+      var markerCookieName = "marker";
+      var markerCookieValue = "markerVal";
+
+      var brakes = 5000; //limit cookie setting limit in case browers are magic
+
+      // Set an initial cookie as a marker
+      create_cookie_from_js(markerCookieName, markerCookieValue, 10, secure_cookie);
+      //TODO we cant trust document.cookie to set secure cookies. Need a round trip to a secure origin.
+      assert_dom_cookie(markerCookieName, markerCookieValue, true);
+
+      // Set new cookies until marker is gone
+      try {
+        for (i = 0; i < brakes; i++) {
+          create_cookie_from_js(markerCookieName + counter++, markerCookieValue, 10, secure_cookie);
+          assert_dom_cookie(markerCookieName, markerCookieValue, true);
+        }
+      } catch(err) {
+        //shame on me, just fiddling for now
+      }
+
+      assert_dom_cookie(markerCookieName, markerCookieValue, present);
+
+      if (present == false) {
+        alert("It took " + counter + " cookies to force out the marker cookie");
+      } else {
+        alert("Even after " + counter + " cookies the marker cookie was not forced out. Try incresing the current limit of " + brakes);
+      }
+
+    }, title);
+  }
+
+
+  //actual tests to verify that non-secure origins should "leave secure cookies alone"
+  cookie_force_test(false, false, false, "non-secure origins should be able to force out insecure cookies.");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/create-cookie-http-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/create-cookie-http-expected.txt
new file mode 100644
index 0000000..eccdfa58
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/create-cookie-http-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Untitled Uncaught ReferenceError: INSECURE_ORIGIN is not defined
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/create-cookie-http.html b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/create-cookie-http.html
new file mode 100644
index 0000000..425f66f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/cookies/secure/create-cookie-http.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/cookies/resources/cookie-helper.sub.js"></script>
+<script>
+  //origin is who sets the cookie
+  //target is who tries to send cookie
+  function create_test(origin, target, expectedStatus, title) {
+    promise_test(t => {
+      var value = "" + Math.random();
+      return resetSecureCookies(origin, value)
+        .then(_ => {
+          return credFetch(target + "/cookies/resources/list.py")
+            .then(r => r.json())
+            .then(cookies => verifySecureCookieState(expectedStatus, value, cookies));
+        });
+    }, title);
+  }
+
+  //Given an |expectedStatus| and |expectedValue|, assert the |cookies| contains the
+  //proper set of cookie names and values.
+  function verifySecureCookieState(expectedStatus, expectedValue, cookies) {
+    assert_equals(cookies["alone_insecure"], expectedValue, "Insecure cookies are always present");
+    if (expectedStatus == SecureStatus.INSECURE_COOKIE_ONLY) {
+    	assert_equals(cookies["alone_secure"], undefined, "Secure cookies are not present");
+    } else if (expectedStatus == SecureStatus.BOTH_COOKIES) {
+    	assert_equals(cookies["alone_secure"], expectedValue, "Secure cookies are present");
+    }
+  }
+
+  //cookies set by insecure origins
+  create_test(INSECURE_ORIGIN, INSECURE_ORIGIN, SecureStatus.INSECURE_COOKIE_ONLY, "Secure cookies cannot be set by insecure origins");
+  //create_test(INSECURE_ORIGIN, SECURE_ORIGIN, SecureStatus.INSECURE_COOKIE_ONLY, "Secure cookies cannot be set by insecure origins, even if read from a secure origin");
+
+  //This test should set the secure cookie right but not be able to read it from the secure origin
+  //create_test(SECURE_ORIGIN, INSECURE_ORIGIN, SecureStatus.INSECURE_COOKIE_ONLY, "Secure cookies should not be read by insecure origins");
+  //create_test(SECURE_ORIGIN, SECURE_ORIGIN, SecureStatus.BOTH_COOKIES, "Secure cookies should be set and read by secure domains")
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements/the-innertext-idl-attribute/getter-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements/the-innertext-idl-attribute/getter-expected.txt
index 7055109..2bf5969 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements/the-innertext-idl-attribute/getter-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/html/dom/elements/the-innertext-idl-attribute/getter-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 213 tests; 132 PASS, 81 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 213 tests; 133 PASS, 80 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Simplest possible test ("<div>abc")
 PASS Leading whitespace removed ("<div> abc")
 PASS Trailing whitespace removed ("<div>abc ")
@@ -50,7 +50,7 @@
 PASS display:none child of svg ("<div style='display:none' id='target'>abc")
 PASS child of display:none child of svg ("<div style='display:none'><div id='target'>abc")
 PASS display:contents container ("<div style='display:contents'>abc")
-FAIL display:contents container ("<div><div style='display:contents'>abc") assert_equals: expected "abc" but got "abc\n"
+PASS display:contents container ("<div><div style='display:contents'>abc")
 PASS display:contents rendered ("<div>123<span style='display:contents'>abc")
 FAIL display:contents not processed via textContent ("<div style='display:contents'>   ") assert_equals: expected "" but got "   "
 PASS display:contents not processed via textContent ("<div><div style='display:contents'>   ")
diff --git a/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-getCapabilities.https.html b/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-getCapabilities.https.html
index 257578f..d928370 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-getCapabilities.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/mediacapture-streams/MediaStreamTrack-getCapabilities.https.html
@@ -15,6 +15,7 @@
       assert_true(undefined !== audioCapabilities.autoGainControl, "MediaTrackCapabilities's autoGainControl should exist for an audio track.");
       assert_true(undefined !== audioCapabilities.noiseSuppression, "MediaTrackCapabilities's noiseSuppression should exist for an audio track.");
       assert_true(undefined !== videoCapabilities.deviceId, "MediaTrackCapabilities's deviceId should exist for a video track.");
+      assert_true(undefined !== videoCapabilities.groupId, "MediaTrackCapabilities's groupId should exist for a video track.");
     });
   });
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/Document/getAnimations-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/Document/getAnimations-expected.txt
new file mode 100644
index 0000000..92d85dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/Document/getAnimations-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS Test document.getAnimations for non-animated content
+PASS Test document.getAnimations for script-generated animations
+PASS Test the order of document.getAnimations with script generated animations
+PASS Test document.getAnimations for a disconnected node
+PASS Test document.getAnimations with null target
+FAIL Test document.getAnimations for elements inside same-origin iframes assert_equals: expected 1 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/Document/getAnimations.html b/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/Document/getAnimations.html
index 381d954..6b8534b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/Document/getAnimations.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/Document/getAnimations.html
@@ -64,5 +64,26 @@
                 'elements in this document');
 }, 'Test document.getAnimations with null target');
 
+async_test(t => {
+  const iframe = document.createElement('iframe');
+
+  iframe.addEventListener("load", t.step_func_done(function() {
+    const div = createDiv(t, iframe.contentDocument)
+    const effect = new KeyframeEffect(div, null, 100 * MS_PER_SEC);
+    const anim = new Animation(effect, document.timeline);
+    anim.play();
+
+    // The animation's timeline is from the main document, but the effect's
+    // target element is part of the iframe document and that is what matters
+    // for getAnimations.
+    assert_equals(document.getAnimations().length, 0);
+    assert_equals(iframe.contentDocument.getAnimations().length, 1);
+    anim.finish();
+  }));
+
+  document.body.appendChild(iframe);
+  t.add_cleanup(function() { document.body.removeChild(iframe); });
+}, 'Test document.getAnimations for elements inside same-origin iframes');
+
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/README.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/README.txt
new file mode 100644
index 0000000..062db854
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/README.txt
@@ -0,0 +1,21 @@
+This directory contains files that test for behavior relevant to webrtc,
+but which is specified in protocol specifications from the IETF, not in
+API recommendations from the W3C.
+
+The main specifications are given in the following internet-drafts:
+
+- draft-ietf-rtcweb-overview
+- draft-ietf-rtcweb-transports
+- draft-ietf-rtcweb-security-arch
+- draft-ietf-rtcweb-security
+- draft-ietf-rtcweb-rtp-usage
+- draft-ietf-rtcweb-jsep
+- draft-ietf-rtcweb-ip-handling
+- draft-ietf-rtcweb-fec
+- draft-ietf-rtcweb-data-protocol
+- draft-ietf-rtcweb-data-channel
+
+- RFC 7742, "WebRTC Video Processing and Codec Requirements"
+- RFC 7874, "WebRTC Audio Codec and Processing Requirements"
+
+An overview of the dependencies involved is in draft-jennings-rtcweb-deps
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/video-codecs.html b/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/video-codecs.html
new file mode 100644
index 0000000..cc15ced
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/protocol/video-codecs.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCPeerConnection.prototype.createOffer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/webrtc/RTCPeerConnection-helper.js"></script>
+<script>
+'use strict';
+
+// Tests for conformance to RFC 7742,
+// "WebRTC Video Processing and Codec Requirements"
+// The document was formerly known as draft-ietf-rtcweb-video-codecs.
+//
+// This tests that the browser is a WebRTC Browser as defined there.
+
+// TODO: Section 3.2: screen capture video MUST be prepared
+// to handle resolution changes.
+
+// TODO: Section 4: MUST support generating CVO (orientation)
+
+// Section 5: Browsers MUST implement VP8 and H.264 Constrained Baseline
+promise_test(async t => {
+  const pc = new RTCPeerConnection();
+  const offer = await pc.createOffer({offerToReceiveVideo: true});
+  let video_section_found = false;
+  for (let section of offer.sdp.split(/\r\nm=/)) {
+    if (section.search('video') != 0) {
+      continue;
+    }
+    video_section_found = true;
+    // RTPMAP lines have the format a=rtpmap:<pt> <codec>/<clock rate>
+    let rtpmap_regex = /\r\na=rtpmap:(\d+) (\S+)\/\d+\r\n/g;
+    let match = rtpmap_regex.exec(offer.sdp);
+    let payload_type_map = new Array();
+    while (match) {
+      payload_type_map[match[1]] = match[2];
+      match = rtpmap_regex.exec(offer.sdp);
+    }
+    assert_true(payload_type_map.indexOf('VP8') > -1,
+                'VP8 is supported');
+    assert_true(payload_type_map.indexOf('H264') > -1,
+                'H.264 is supported');
+    // TODO: Verify that one of the H.264 PTs supports constrained baseline
+  }
+  assert_true(video_section_found);
+}, 'H.264 and VP8 should be supported in initial offer');
+
+// TODO: Section 6: Recipients MUST be able to decode 320x240@20 fps
+// TODO: Section 6.1: VP8 MUST support RFC 7741 payload formats
+// TODO: Section 6.1: VP8 MUST respect max-fr/max-fs
+// TODO: Section 6.1: VP8 MUST encode and decode square pixels
+// TODO: Section 6.2: H.264 MUST support RFC 6184 payload formats
+// TODO: Section 6.2: MUST support Constrained Baseline level 1.2
+// TODO: Section 6.2: SHOULD support Constrained High level 1.3
+// TODO: Section 6.2: MUST support packetization mode 1.
+// TODO: Section 6.2: MUST include profile-level-id
+// TODO: Section 6.2: SHOULD interpret max-mbps, max-smbps, max-fs et al
+// TODO: Section 6.2: MUST NOT include sprop-parameter-sets
+// TODO: Section 6.2: MUST support SEI "filler payload"
+// TODO: Section 6.2: MUST support SEI "full frame freeze"
+// TODO: Section 6.2: MUST be prepared to receive User Data messages
+// TODO: Section 6.2: MUST encode and decode square pixels unless signaled
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getCapabilities.html b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getCapabilities.html
index 7ad96c5..78ccfb8 100644
--- a/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getCapabilities.html
+++ b/third_party/WebKit/LayoutTests/fast/mediastream/MediaStreamTrack-getCapabilities.html
@@ -18,6 +18,7 @@
     var capabilities = stream.getVideoTracks()[0].getCapabilities();
     assert_greater_than(Object.keys(capabilities).length, 0);
     assert_true(capabilities.hasOwnProperty('deviceId'));
+    assert_true(capabilities.hasOwnProperty('groupId'));
     assert_true(capabilities.hasOwnProperty('facingMode'));
     verifyVideoRangeProperties(capabilities);
   });
diff --git a/third_party/WebKit/LayoutTests/http/tests/misc/resource-timing-sizes-multipart.html b/third_party/WebKit/LayoutTests/http/tests/misc/resource-timing-sizes-multipart.html
index d0606796..9edf5e6 100644
--- a/third_party/WebKit/LayoutTests/http/tests/misc/resource-timing-sizes-multipart.html
+++ b/third_party/WebKit/LayoutTests/http/tests/misc/resource-timing-sizes-multipart.html
@@ -26,10 +26,8 @@
 function checkTransferSize(entry) {
     // These comparisons are not strict because multipart responses are
     // non-standard. Any reasonable answer is acceptable.
-    // TODO(ricea): encodedBodySize and decodedBodySize don't pass.
-    // Fix the code or make this test even more relaxed. See crbug.com/631004.
-    // assert_greater_than(entry.encodedBodySize, abePngSize - 1, 'encodedBodySize');
-    // assert_greater_than(entry.decodedBodySize, abePngSize - 1, 'decodedBodySize');
+    assert_greater_than(entry.encodedBodySize, abePngSize - 1, 'encodedBodySize');
+    assert_greater_than(entry.decodedBodySize, abePngSize - 1, 'decodedBodySize');
     assert_greater_than(entry.transferSize, abePngSize + minHeaderSize, 'transferSize');
     t.done();
 }
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-block-crossorigin-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-block-crossorigin-expected.txt
deleted file mode 100644
index 9881237..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-block-crossorigin-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Downloading URL with suggested filename "foo.pdf"
-Tests that a suggested filename on a download attribute is passed along even if the link is cross origin.
-
-The suggested filename at the top should be non-empty. The actual cross-origin check will be done in the browser process.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-block-crossorigin.html b/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-block-crossorigin.html
deleted file mode 100644
index 3366b33..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/security/anchor-download-block-crossorigin.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src="/js-test-resources/js-test.js"></script>
-<script type='text/javascript'>
-if (window.testRunner) {
-  // The test will end when loadURLExternally() is invoked.
-  testRunner.waitUntilExternalURLLoad();
-}
-</script>
-</head>
-<body>
-<p>
-Tests that a suggested filename on a download attribute is passed along even if
-<a id="dl" href="http://localhost:8080/security/resources/attachment.php" download="foo.pdf">the link</a> is cross origin.
-<p>
-The suggested filename at the top should be non-empty. The actual cross-origin
-check will be done in the browser process.
-<script>
-function click(elmt)
-{
-    if (!window.eventSender) {
-        return;
-    }
-    eventSender.mouseMoveTo(elmt.offsetLeft + 5, elmt.offsetTop + 5);
-    eventSender.mouseDown();
-    eventSender.mouseUp();
-}
-
-function runTest()
-{
-    var link = document.getElementById("dl");
-    click(link);
-}
-runTest();
-</script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/pointerevents/extension/pointerevent_coalesced_events_attributes-manual-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/pointerevents/extension/pointerevent_coalesced_events_attributes-manual-expected.txt
new file mode 100644
index 0000000..e84ebdb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/pointerevents/extension/pointerevent_coalesced_events_attributes-manual-expected.txt
@@ -0,0 +1,16 @@
+This is a testharness.js-based test.
+PASS touch coalesced events attributes in pointerevents
+PASS PointerEvent Automation
+PASS touch pointercancel should not have any coalesced events
+PASS touch pointerover should not have any coalesced events
+PASS touch pointerenter should not have any coalesced events
+PASS touch pointerdown should not have any coalesced events
+FAIL touch pointermove should have >2 coalesced events as main thread is busy. assert_greater_than: pointermove should have at least 2 coalesced events. expected a number greater than 1 but got 1
+PASS touch pointermove coalesced events should all be marked as trusted.
+PASS touch time stamps of coalesced events must be ascending.
+PASS touch pointermove coalesced events should all bubbles and cancelable as false.
+PASS touch pointerup should not have any coalesced events
+PASS touch pointerout should not have any coalesced events
+PASS touch pointerleave should not have any coalesced events
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/README.txt b/third_party/WebKit/LayoutTests/virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/README.txt
index 06ee685f9..204f08b 100644
--- a/third_party/WebKit/LayoutTests/virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/README.txt
+++ b/third_party/WebKit/LayoutTests/virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/README.txt
@@ -1,3 +1,4 @@
 # This suite runs the tests in http/tests/devtools/elements/shadow with
 # --enable-blink-features=IncrementalShadowDOM.
 # See crbug.com/776656 for details.
+# See crbug.com/840238 for http/tests/devtools/elements/shadow-distribution.js
diff --git a/third_party/WebKit/LayoutTests/virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/shadow-distribution-expected.txt b/third_party/WebKit/LayoutTests/virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/shadow-distribution-expected.txt
new file mode 100644
index 0000000..0e10558
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/incremental-shadow-dom/http/tests/devtools/elements/shadow/shadow-distribution-expected.txt
@@ -0,0 +1,257 @@
+Tests that elements panel updates dom tree structure upon distribution in shadow dom.
+
+
+Running: createHost1
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+          </slot>
+        - <slot id="slot2" name="slot2">
+          </slot>
+        - <slot id="slot3">
+          </slot>
+  </div>
+
+Running: createChild1
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+          </slot>
+        - <slot id="slot3">
+          </slot>
+      <span id="child1" slot="slot2"></span>
+  </div>
+
+Running: createChild2
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot2"></span>
+      <div id="child2"></div>
+  </div>
+
+Running: createChild3
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+              ↪ <h1>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot2"></span>
+      <div id="child2"></div>
+      <h1 id="child3" slot="slot2"></h1>
+  </div>
+
+Running: createChild4
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+              ↪ <h2>
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+              ↪ <h1>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot2"></span>
+      <div id="child2"></div>
+      <h1 id="child3" slot="slot2"></h1>
+      <h2 id="child4" slot="slot1"></h2>
+  </div>
+
+Running: createChild5
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+              ↪ <h2>
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+              ↪ <h1>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot2"></span>
+      <div id="child2"></div>
+      <h1 id="child3" slot="slot2"></h1>
+      <h2 id="child4" slot="slot1"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: modifyChild1
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+              ↪ <h2>
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+              ↪ <h1>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot1"></span>
+      <div id="child2"></div>
+      <h1 id="child3" slot="slot2"></h1>
+      <h2 id="child4" slot="slot1"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: modifyChild4
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot1">
+              ↪ <h2>
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+              ↪ <h1>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot1"></span>
+      <div id="child2"></div>
+      <h1 id="child3" slot="slot2"></h1>
+      <h2 id="child4"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: modifySlot1
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot3">
+              ↪ <h2>
+          </slot>
+        - <slot id="slot2" name="slot2">
+              ↪ <span>
+              ↪ <h1>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot1"></span>
+      <div id="child2"></div>
+      <h1 id="child3" slot="slot2"></h1>
+      <h2 id="child4"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: modifySlot2
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot3">
+              ↪ <h2>
+          </slot>
+        - <slot id="slot2" name="slot1">
+              ↪ <span>
+              ↪ <h1>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+          </slot>
+      <span id="child1" slot="slot1"></span>
+      <div id="child2"></div>
+      <h1 id="child3" slot="slot2"></h1>
+      <h2 id="child4"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: removeChild3
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot3">
+              ↪ <h3>
+          </slot>
+        - <slot id="slot2" name="slot1">
+              ↪ <span>
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+              ↪ <h2>
+          </slot>
+      <span id="child1" slot="slot1"></span>
+      <div id="child2"></div>
+      <h2 id="child4"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: removeChild1
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot3">
+              ↪ <h3>
+          </slot>
+        - <slot id="slot2" name="slot1">
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+              ↪ <h2>
+          </slot>
+      <div id="child2"></div>
+      <h2 id="child4"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: removeSlot1
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot2" name="slot1">
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+              ↪ <h2>
+          </slot>
+      <div id="child2"></div>
+      <h2 id="child4"></h2>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: createHost2
+- <div id="host2">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot3">
+          </slot>
+  </div>
+
+Running: moveChild5FromHost1ToHost2
+- <div id="host2">
+    - #shadow-root (open)
+        - <slot id="slot1" name="slot3">
+              ↪ <h3>
+          </slot>
+      <h3 id="child5" slot="slot3"></h3>
+  </div>
+
+Running: modifyChild4
+- <div id="host1">
+    - #shadow-root (open)
+        - <slot id="slot2" name="slot1">
+          </slot>
+        - <slot id="slot3">
+              ↪ <div>
+              ↪ <h2>
+          </slot>
+      <div id="child2"></div>
+      <h2 id="child4" slot="slot1"></h2>
+  </div>
+
diff --git a/third_party/abseil-cpp/absl/base/dynamic_annotations.cc b/third_party/abseil-cpp/absl/base/dynamic_annotations.cc
index 08c27e51..5be25338 100644
--- a/third_party/abseil-cpp/absl/base/dynamic_annotations.cc
+++ b/third_party/abseil-cpp/absl/base/dynamic_annotations.cc
@@ -42,29 +42,29 @@
 extern "C" {
 #endif
 
-void AnnotateRWLockCreate(const char *, int,
+void AbslAnnotateRWLockCreate(const char *, int,
                           const volatile void *){}
-void AnnotateRWLockDestroy(const char *, int,
+void AbslAnnotateRWLockDestroy(const char *, int,
                            const volatile void *){}
-void AnnotateRWLockAcquired(const char *, int,
+void AbslAnnotateRWLockAcquired(const char *, int,
                             const volatile void *, long){}
-void AnnotateRWLockReleased(const char *, int,
+void AbslAnnotateRWLockReleased(const char *, int,
                             const volatile void *, long){}
-void AnnotateBenignRace(const char *, int,
+void AbslAnnotateBenignRace(const char *, int,
                         const volatile void *,
                         const char *){}
-void AnnotateBenignRaceSized(const char *, int,
+void AbslAnnotateBenignRaceSized(const char *, int,
                              const volatile void *,
                              size_t,
                              const char *) {}
-void AnnotateThreadName(const char *, int,
+void AbslAnnotateThreadName(const char *, int,
                         const char *){}
-void AnnotateIgnoreReadsBegin(const char *, int){}
-void AnnotateIgnoreReadsEnd(const char *, int){}
-void AnnotateIgnoreWritesBegin(const char *, int){}
-void AnnotateIgnoreWritesEnd(const char *, int){}
-void AnnotateEnableRaceDetection(const char *, int, int){}
-void AnnotateMemoryIsInitialized(const char *, int,
+void AbslAnnotateIgnoreReadsBegin(const char *, int){}
+void AbslAnnotateIgnoreReadsEnd(const char *, int){}
+void AbslAnnotateIgnoreWritesBegin(const char *, int){}
+void AbslAnnotateIgnoreWritesEnd(const char *, int){}
+void AbslAnnotateEnableRaceDetection(const char *, int, int){}
+void AbslAnnotateMemoryIsInitialized(const char *, int,
                                  const volatile void *mem, size_t size) {
 #if __has_feature(memory_sanitizer)
   __msan_unpoison(mem, size);
@@ -74,7 +74,7 @@
 #endif
 }
 
-void AnnotateMemoryIsUninitialized(const char *, int,
+void AbslAnnotateMemoryIsUninitialized(const char *, int,
                                    const volatile void *mem, size_t size) {
 #if __has_feature(memory_sanitizer)
   __msan_allocated_memory(mem, size);
@@ -84,7 +84,7 @@
 #endif
 }
 
-static int GetRunningOnValgrind(void) {
+static int GetAbslRunningOnValgrind(void) {
 #ifdef RUNNING_ON_VALGRIND
   if (RUNNING_ON_VALGRIND) return 1;
 #endif
@@ -96,24 +96,24 @@
 }
 
 /* See the comments in dynamic_annotations.h */
-int RunningOnValgrind(void) {
+int AbslRunningOnValgrind(void) {
   static volatile int running_on_valgrind = -1;
   int local_running_on_valgrind = running_on_valgrind;
   /* C doesn't have thread-safe initialization of statics, and we
      don't want to depend on pthread_once here, so hack it. */
   ANNOTATE_BENIGN_RACE(&running_on_valgrind, "safe hack");
   if (local_running_on_valgrind == -1)
-    running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind();
+    running_on_valgrind = local_running_on_valgrind = GetAbslRunningOnValgrind();
   return local_running_on_valgrind;
 }
 
 /* See the comments in dynamic_annotations.h */
-double ValgrindSlowdown(void) {
-  /* Same initialization hack as in RunningOnValgrind(). */
+double AbslValgrindSlowdown(void) {
+  /* Same initialization hack as in AbslRunningOnValgrind(). */
   static volatile double slowdown = 0.0;
   double local_slowdown = slowdown;
   ANNOTATE_BENIGN_RACE(&slowdown, "safe hack");
-  if (RunningOnValgrind() == 0) {
+  if (AbslRunningOnValgrind() == 0) {
     return 1.0;
   }
   if (local_slowdown == 0.0) {
diff --git a/third_party/abseil-cpp/absl/base/dynamic_annotations.h b/third_party/abseil-cpp/absl/base/dynamic_annotations.h
index 3b6d6ef4..c7c36e2c 100644
--- a/third_party/abseil-cpp/absl/base/dynamic_annotations.h
+++ b/third_party/abseil-cpp/absl/base/dynamic_annotations.h
@@ -81,26 +81,26 @@
      point where "pointer" has been allocated, preferably close to the point
      where the race happens.  See also ANNOTATE_BENIGN_RACE_STATIC. */
   #define ANNOTATE_BENIGN_RACE(pointer, description) \
-    AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \
+    AbslAnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \
                             sizeof(*(pointer)), description)
 
   /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to
      the memory range [address, address+size). */
   #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
-    AnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description)
+    AbslAnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description)
 
   /* Enable (enable!=0) or disable (enable==0) race detection for all threads.
      This annotation could be useful if you want to skip expensive race analysis
      during some period of program execution, e.g. during initialization. */
   #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \
-    AnnotateEnableRaceDetection(__FILE__, __LINE__, enable)
+    AbslAnnotateEnableRaceDetection(__FILE__, __LINE__, enable)
 
   /* -------------------------------------------------------------
      Annotations useful for debugging. */
 
   /* Report the current thread name to a race detector. */
   #define ANNOTATE_THREAD_NAME(name) \
-    AnnotateThreadName(__FILE__, __LINE__, name)
+    AbslAnnotateThreadName(__FILE__, __LINE__, name)
 
   /* -------------------------------------------------------------
      Annotations useful when implementing locks.  They are not
@@ -109,29 +109,29 @@
 
   /* Report that a lock has been created at address "lock". */
   #define ANNOTATE_RWLOCK_CREATE(lock) \
-    AnnotateRWLockCreate(__FILE__, __LINE__, lock)
+    AbslAnnotateRWLockCreate(__FILE__, __LINE__, lock)
 
   /* Report that a linker initialized lock has been created at address "lock".
    */
 #ifdef THREAD_SANITIZER
   #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) \
-    AnnotateRWLockCreateStatic(__FILE__, __LINE__, lock)
+    AbslAnnotateRWLockCreateStatic(__FILE__, __LINE__, lock)
 #else
   #define ANNOTATE_RWLOCK_CREATE_STATIC(lock) ANNOTATE_RWLOCK_CREATE(lock)
 #endif
 
   /* Report that the lock at address "lock" is about to be destroyed. */
   #define ANNOTATE_RWLOCK_DESTROY(lock) \
-    AnnotateRWLockDestroy(__FILE__, __LINE__, lock)
+    AbslAnnotateRWLockDestroy(__FILE__, __LINE__, lock)
 
   /* Report that the lock at address "lock" has been acquired.
      is_w=1 for writer lock, is_w=0 for reader lock. */
   #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
-    AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w)
+    AbslAnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w)
 
   /* Report that the lock at address "lock" is about to be released. */
   #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
-    AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w)
+    AbslAnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w)
 
 #else  /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
 
@@ -150,10 +150,10 @@
 /* These annotations are also made available to LLVM's Memory Sanitizer */
 #if DYNAMIC_ANNOTATIONS_ENABLED == 1 || defined(MEMORY_SANITIZER)
   #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \
-    AnnotateMemoryIsInitialized(__FILE__, __LINE__, address, size)
+    AbslAnnotateMemoryIsInitialized(__FILE__, __LINE__, address, size)
 
   #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) \
-    AnnotateMemoryIsUninitialized(__FILE__, __LINE__, address, size)
+    AbslAnnotateMemoryIsUninitialized(__FILE__, __LINE__, address, size)
 #else
   #define ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */
   #define ANNOTATE_MEMORY_IS_UNINITIALIZED(address, size) /* empty */
@@ -190,19 +190,19 @@
      other reads and all writes.
      See also ANNOTATE_UNPROTECTED_READ. */
   #define ANNOTATE_IGNORE_READS_BEGIN() \
-    AnnotateIgnoreReadsBegin(__FILE__, __LINE__)
+    AbslAnnotateIgnoreReadsBegin(__FILE__, __LINE__)
 
   /* Stop ignoring reads. */
   #define ANNOTATE_IGNORE_READS_END() \
-    AnnotateIgnoreReadsEnd(__FILE__, __LINE__)
+    AbslAnnotateIgnoreReadsEnd(__FILE__, __LINE__)
 
   /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes instead. */
   #define ANNOTATE_IGNORE_WRITES_BEGIN() \
-    AnnotateIgnoreWritesBegin(__FILE__, __LINE__)
+    AbslAnnotateIgnoreWritesBegin(__FILE__, __LINE__)
 
   /* Stop ignoring writes. */
   #define ANNOTATE_IGNORE_WRITES_END() \
-    AnnotateIgnoreWritesEnd(__FILE__, __LINE__)
+    AbslAnnotateIgnoreWritesEnd(__FILE__, __LINE__)
 
 /* Clang provides limited support for static thread-safety analysis
    through a feature called Annotalysis. We configure macro-definitions
@@ -210,16 +210,16 @@
 #elif defined(ANNOTALYSIS_ENABLED)
 
   #define ANNOTATE_IGNORE_READS_BEGIN() \
-    StaticAnnotateIgnoreReadsBegin(__FILE__, __LINE__)
+    StaticAbslAnnotateIgnoreReadsBegin(__FILE__, __LINE__)
 
   #define ANNOTATE_IGNORE_READS_END() \
-    StaticAnnotateIgnoreReadsEnd(__FILE__, __LINE__)
+    StaticAbslAnnotateIgnoreReadsEnd(__FILE__, __LINE__)
 
   #define ANNOTATE_IGNORE_WRITES_BEGIN() \
-    StaticAnnotateIgnoreWritesBegin(__FILE__, __LINE__)
+    StaticAbslAnnotateIgnoreWritesBegin(__FILE__, __LINE__)
 
   #define ANNOTATE_IGNORE_WRITES_END() \
-    StaticAnnotateIgnoreWritesEnd(__FILE__, __LINE__)
+    StaticAbslAnnotateIgnoreWritesEnd(__FILE__, __LINE__)
 
 #else
   #define ANNOTATE_IGNORE_READS_BEGIN()  /* empty */
@@ -256,41 +256,41 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
-void AnnotateRWLockCreate(const char *file, int line,
+void AbslAnnotateRWLockCreate(const char *file, int line,
                           const volatile void *lock);
-void AnnotateRWLockCreateStatic(const char *file, int line,
+void AbslAnnotateRWLockCreateStatic(const char *file, int line,
                           const volatile void *lock);
-void AnnotateRWLockDestroy(const char *file, int line,
+void AbslAnnotateRWLockDestroy(const char *file, int line,
                            const volatile void *lock);
-void AnnotateRWLockAcquired(const char *file, int line,
+void AbslAnnotateRWLockAcquired(const char *file, int line,
                             const volatile void *lock, long is_w);  /* NOLINT */
-void AnnotateRWLockReleased(const char *file, int line,
+void AbslAnnotateRWLockReleased(const char *file, int line,
                             const volatile void *lock, long is_w);  /* NOLINT */
-void AnnotateBenignRace(const char *file, int line,
+void AbslAnnotateBenignRace(const char *file, int line,
                         const volatile void *address,
                         const char *description);
-void AnnotateBenignRaceSized(const char *file, int line,
+void AbslAnnotateBenignRaceSized(const char *file, int line,
                         const volatile void *address,
                         size_t size,
                         const char *description);
-void AnnotateThreadName(const char *file, int line,
+void AbslAnnotateThreadName(const char *file, int line,
                         const char *name);
-void AnnotateEnableRaceDetection(const char *file, int line, int enable);
-void AnnotateMemoryIsInitialized(const char *file, int line,
+void AbslAnnotateEnableRaceDetection(const char *file, int line, int enable);
+void AbslAnnotateMemoryIsInitialized(const char *file, int line,
                                  const volatile void *mem, size_t size);
-void AnnotateMemoryIsUninitialized(const char *file, int line,
+void AbslAnnotateMemoryIsUninitialized(const char *file, int line,
                                    const volatile void *mem, size_t size);
 
 /* Annotations expand to these functions, when Dynamic Annotations are enabled.
    These functions are either implemented as no-op calls, if no Sanitizer is
    attached, or provided with externally-linked implementations by a library
    like ThreadSanitizer. */
-void AnnotateIgnoreReadsBegin(const char *file, int line)
+void AbslAnnotateIgnoreReadsBegin(const char *file, int line)
     ATTRIBUTE_IGNORE_READS_BEGIN;
-void AnnotateIgnoreReadsEnd(const char *file, int line)
+void AbslAnnotateIgnoreReadsEnd(const char *file, int line)
     ATTRIBUTE_IGNORE_READS_END;
-void AnnotateIgnoreWritesBegin(const char *file, int line);
-void AnnotateIgnoreWritesEnd(const char *file, int line);
+void AbslAnnotateIgnoreWritesBegin(const char *file, int line);
+void AbslAnnotateIgnoreWritesEnd(const char *file, int line);
 
 #if defined(ANNOTALYSIS_ENABLED)
 /* When Annotalysis is enabled without Dynamic Annotations, the use of
@@ -301,13 +301,13 @@
    allows IGNORE_READS_AND_WRITES to work properly. */
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-function"
-static inline void StaticAnnotateIgnoreReadsBegin(const char *file, int line)
+static inline void StaticAbslAnnotateIgnoreReadsBegin(const char *file, int line)
     ATTRIBUTE_IGNORE_READS_BEGIN { (void)file; (void)line; }
-static inline void StaticAnnotateIgnoreReadsEnd(const char *file, int line)
+static inline void StaticAbslAnnotateIgnoreReadsEnd(const char *file, int line)
     ATTRIBUTE_IGNORE_READS_END { (void)file; (void)line; }
-static inline void StaticAnnotateIgnoreWritesBegin(
+static inline void StaticAbslAnnotateIgnoreWritesBegin(
     const char *file, int line) { (void)file; (void)line; }
-static inline void StaticAnnotateIgnoreWritesEnd(
+static inline void StaticAbslAnnotateIgnoreWritesEnd(
     const char *file, int line) { (void)file; (void)line; }
 #pragma GCC diagnostic pop
 #endif
@@ -324,23 +324,23 @@
   If for some reason you can't use "valgrind.h" or want to fake valgrind,
   there are two ways to make this function return non-zero:
     - Use environment variable: export RUNNING_ON_VALGRIND=1
-    - Make your tool intercept the function RunningOnValgrind() and
+    - Make your tool intercept the function AbslRunningOnValgrind() and
       change its return value.
  */
-int RunningOnValgrind(void);
+int AbslRunningOnValgrind(void);
 
 /* ValgrindSlowdown returns:
-    * 1.0, if (RunningOnValgrind() == 0)
-    * 50.0, if (RunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") == NULL)
+    * 1.0, if (AbslRunningOnValgrind() == 0)
+    * 50.0, if (AbslRunningOnValgrind() != 0 && getenv("VALGRIND_SLOWDOWN") == NULL)
     * atof(getenv("VALGRIND_SLOWDOWN")) otherwise
    This function can be used to scale timeout values:
    EXAMPLE:
    for (;;) {
      DoExpensiveBackgroundTask();
-     SleepForSeconds(5 * ValgrindSlowdown());
+     SleepForSeconds(5 * AbslValgrindSlowdown());
    }
  */
-double ValgrindSlowdown(void);
+double AbslValgrindSlowdown(void);
 
 #ifdef __cplusplus
 }
diff --git a/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc b/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc
index 44ec7c0..e8129e8 100644
--- a/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc
+++ b/third_party/abseil-cpp/absl/debugging/internal/vdso_support.cc
@@ -79,7 +79,7 @@
     // on stack, and so glibc works as if VDSO was not present.
     // But going directly to kernel via /proc/self/auxv below bypasses
     // Valgrind zapping. So we check for Valgrind separately.
-    if (RunningOnValgrind()) {
+    if (AbslRunningOnValgrind()) {
       vdso_base_.store(nullptr, std::memory_order_relaxed);
       getcpu_fn_.store(&GetCPUViaSyscall, std::memory_order_relaxed);
       return nullptr;
diff --git a/third_party/abseil-cpp/absl/strings/string_view_test.cc b/third_party/abseil-cpp/absl/strings/string_view_test.cc
index bb149d5..41621024 100644
--- a/third_party/abseil-cpp/absl/strings/string_view_test.cc
+++ b/third_party/abseil-cpp/absl/strings/string_view_test.cc
@@ -1052,7 +1052,7 @@
 
 #ifndef THREAD_SANITIZER  // Allocates too much memory for tsan.
 TEST(HugeStringView, TwoPointTwoGB) {
-  if (sizeof(size_t) <= 4 || RunningOnValgrind())
+  if (sizeof(size_t) <= 4 || AbslRunningOnValgrind())
     return;
   // Try a huge std::string piece.
   const size_t size = size_t{2200} * 1000 * 1000;
diff --git a/third_party/blink/public/platform/web_media_stream_source.h b/third_party/blink/public/platform/web_media_stream_source.h
index 3f37effe..d5e1502 100644
--- a/third_party/blink/public/platform/web_media_stream_source.h
+++ b/third_party/blink/public/platform/web_media_stream_source.h
@@ -88,6 +88,7 @@
     WebMediaStreamTrack::FacingMode facing_mode =
         WebMediaStreamTrack::FacingMode::kNone;
     WebString device_id;
+    WebString group_id;
   };
 
   WebMediaStreamSource() = default;
diff --git a/third_party/blink/public/platform/web_url_load_timing.h b/third_party/blink/public/platform/web_url_load_timing.h
index a7dd594..389a8c6f 100644
--- a/third_party/blink/public/platform/web_url_load_timing.h
+++ b/third_party/blink/public/platform/web_url_load_timing.h
@@ -31,6 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_URL_LOAD_TIMING_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_URL_LOAD_TIMING_H_
 
+#include "base/time/time.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_private_ptr.h"
 
@@ -62,53 +63,53 @@
 
   bool IsNull() const { return private_.IsNull(); }
 
-  BLINK_PLATFORM_EXPORT double RequestTime() const;
-  BLINK_PLATFORM_EXPORT void SetRequestTime(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks RequestTime() const;
+  BLINK_PLATFORM_EXPORT void SetRequestTime(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double ProxyStart() const;
-  BLINK_PLATFORM_EXPORT void SetProxyStart(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks ProxyStart() const;
+  BLINK_PLATFORM_EXPORT void SetProxyStart(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double ProxyEnd() const;
-  BLINK_PLATFORM_EXPORT void SetProxyEnd(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks ProxyEnd() const;
+  BLINK_PLATFORM_EXPORT void SetProxyEnd(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double DnsStart() const;
-  BLINK_PLATFORM_EXPORT void SetDNSStart(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks DnsStart() const;
+  BLINK_PLATFORM_EXPORT void SetDNSStart(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double DnsEnd() const;
-  BLINK_PLATFORM_EXPORT void SetDNSEnd(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks DnsEnd() const;
+  BLINK_PLATFORM_EXPORT void SetDNSEnd(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double ConnectStart() const;
-  BLINK_PLATFORM_EXPORT void SetConnectStart(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks ConnectStart() const;
+  BLINK_PLATFORM_EXPORT void SetConnectStart(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double ConnectEnd() const;
-  BLINK_PLATFORM_EXPORT void SetConnectEnd(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks ConnectEnd() const;
+  BLINK_PLATFORM_EXPORT void SetConnectEnd(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double WorkerStart() const;
-  BLINK_PLATFORM_EXPORT void SetWorkerStart(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks WorkerStart() const;
+  BLINK_PLATFORM_EXPORT void SetWorkerStart(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double WorkerReady() const;
-  BLINK_PLATFORM_EXPORT void SetWorkerReady(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks WorkerReady() const;
+  BLINK_PLATFORM_EXPORT void SetWorkerReady(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double SendStart() const;
-  BLINK_PLATFORM_EXPORT void SetSendStart(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks SendStart() const;
+  BLINK_PLATFORM_EXPORT void SetSendStart(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double SendEnd() const;
-  BLINK_PLATFORM_EXPORT void SetSendEnd(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks SendEnd() const;
+  BLINK_PLATFORM_EXPORT void SetSendEnd(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double ReceiveHeadersEnd() const;
-  BLINK_PLATFORM_EXPORT void SetReceiveHeadersEnd(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks ReceiveHeadersEnd() const;
+  BLINK_PLATFORM_EXPORT void SetReceiveHeadersEnd(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double SslStart() const;
-  BLINK_PLATFORM_EXPORT void SetSSLStart(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks SslStart() const;
+  BLINK_PLATFORM_EXPORT void SetSSLStart(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double SslEnd() const;
-  BLINK_PLATFORM_EXPORT void SetSSLEnd(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks SslEnd() const;
+  BLINK_PLATFORM_EXPORT void SetSSLEnd(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double PushStart() const;
-  BLINK_PLATFORM_EXPORT void SetPushStart(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks PushStart() const;
+  BLINK_PLATFORM_EXPORT void SetPushStart(base::TimeTicks);
 
-  BLINK_PLATFORM_EXPORT double PushEnd() const;
-  BLINK_PLATFORM_EXPORT void SetPushEnd(double);
+  BLINK_PLATFORM_EXPORT base::TimeTicks PushEnd() const;
+  BLINK_PLATFORM_EXPORT void SetPushEnd(base::TimeTicks);
 
 #if INSIDE_BLINK
   BLINK_PLATFORM_EXPORT WebURLLoadTiming(scoped_refptr<ResourceLoadTiming>);
diff --git a/third_party/blink/renderer/core/dom/idle_deadline_test.cc b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
index c5cd0f6a..4e46e7e 100644
--- a/third_party/blink/renderer/core/dom/idle_deadline_test.cc
+++ b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
@@ -23,7 +23,7 @@
   base::SingleThreadTaskRunner* V8TaskRunner() override { return nullptr; }
   void Shutdown() override {}
   bool ShouldYieldForHighPriorityWork() override { return true; }
-  bool CanExceedIdleDeadlineIfRequired() override { return false; }
+  bool CanExceedIdleDeadlineIfRequired() const override { return false; }
   void PostIdleTask(const base::Location&, WebThread::IdleTask) override {}
   void PostNonNestableIdleTask(const base::Location&,
                                WebThread::IdleTask) override {}
diff --git a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
index 12bb5ec6..91cebebf 100644
--- a/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
+++ b/third_party/blink/renderer/core/dom/scripted_idle_task_controller_test.cc
@@ -30,7 +30,7 @@
   base::SingleThreadTaskRunner* V8TaskRunner() override { return nullptr; }
   void Shutdown() override {}
   bool ShouldYieldForHighPriorityWork() override { return should_yield_; }
-  bool CanExceedIdleDeadlineIfRequired() override { return false; }
+  bool CanExceedIdleDeadlineIfRequired() const override { return false; }
   void PostIdleTask(const base::Location&,
                     WebThread::IdleTask idle_task) override {
     idle_task_ = std::move(idle_task);
diff --git a/third_party/blink/renderer/core/editing/iterators/search_buffer_test.cc b/third_party/blink/renderer/core/editing/iterators/search_buffer_test.cc
index 9e28b155..2a5d4864 100644
--- a/third_party/blink/renderer/core/editing/iterators/search_buffer_test.cc
+++ b/third_party/blink/renderer/core/editing/iterators/search_buffer_test.cc
@@ -81,4 +81,25 @@
   }
 }
 
+TEST_F(SearchBufferTest, DisplayInline) {
+  SetBodyContent("<span>fi</span>nd");
+  GetDocument().UpdateStyleAndLayout();
+  auto match_range = FindPlainText(EphemeralRange(GetBodyRange()), "find", 0);
+  EXPECT_FALSE(match_range.IsCollapsed());
+}
+
+TEST_F(SearchBufferTest, DisplayBlock) {
+  SetBodyContent("<div>fi</div>nd");
+  GetDocument().UpdateStyleAndLayout();
+  auto match_range = FindPlainText(EphemeralRange(GetBodyRange()), "find", 0);
+  EXPECT_TRUE(match_range.IsCollapsed());
+}
+
+TEST_F(SearchBufferTest, DisplayContents) {
+  SetBodyContent("<div style='display: contents'>fi</div>nd");
+  GetDocument().UpdateStyleAndLayout();
+  auto match_range = FindPlainText(EphemeralRange(GetBodyRange()), "find", 0);
+  EXPECT_FALSE(match_range.IsCollapsed());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc
index 79d4cb03..2590c93 100644
--- a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.cc
@@ -302,12 +302,11 @@
 template <typename Strategy>
 bool SimplifiedBackwardsTextIteratorAlgorithm<
     Strategy>::HandleReplacedElement() {
-  unsigned index = Strategy::Index(*node_);
   // We want replaced elements to behave like punctuation for boundary
   // finding, and to simply take up space for the selection preservation
   // code in moveParagraphs, so we use a comma. Unconditionally emit
   // here because this iterator is only used for boundary finding.
-  EmitCharacter(',', Strategy::Parent(*node_), index, index + 1);
+  text_state_.EmitChar16AsNode(',', *node_);
   return true;
 }
 
@@ -319,11 +318,10 @@
   if (TextIterator::ShouldEmitNewlineForNode(*node_, false) ||
       TextIterator::ShouldEmitNewlineAfterNode(*node_) ||
       TextIterator::ShouldEmitTabBeforeNode(*node_)) {
-    unsigned index = Strategy::Index(*node_);
-    // The start of this emitted range is wrong. Ensuring correctness would
-    // require VisiblePositions and so would be slow. previousBoundary expects
-    // this.
-    EmitCharacter('\n', Strategy::Parent(*node_), index + 1, index + 1);
+    // TODO(editing-dev):The start of this emitted range is wrong. Ensuring
+    // correctness would require |VisiblePositions| and so would be slow.
+    // |previousBoundary expects this.
+    text_state_.EmitChar16AfterNode('\n', *node_);
   }
   return true;
 }
@@ -333,23 +331,18 @@
   if (TextIterator::ShouldEmitNewlineForNode(*node_, false) ||
       TextIterator::ShouldEmitNewlineBeforeNode(*node_) ||
       TextIterator::ShouldEmitTabBeforeNode(*node_)) {
-    // The start of this emitted range is wrong. Ensuring correctness would
-    // require VisiblePositions and so would be slow. previousBoundary expects
-    // this.
-    EmitCharacter('\n', node_, 0, 0);
+    // TODO(editing-dev): When we want to use |EmitChar16BeforeNode()| when
+    // test[1] and and test[2] failures are addressed.
+    // [1] readonly-disabled-text-selection.html
+    // [2] extend_selection_05_ltr_backward_word.html
+    // TODO(editing-dev): The start of this emitted range is wrong. Ensuring
+    // correctness would require |VisiblePositions| and so would be slow.
+    // previousBoundary expects this.
+    text_state_.EmitChar16BeforeChildren('\n', ToContainerNode(*node_));
   }
 }
 
 template <typename Strategy>
-void SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EmitCharacter(
-    UChar c,
-    const Node* node,
-    int start_offset,
-    int end_offset) {
-  text_state_.SpliceBuffer(c, node, node, start_offset, end_offset);
-}
-
-template <typename Strategy>
 bool SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::AdvanceRespectingRange(
     const Node* next) {
   if (!next)
@@ -362,40 +355,54 @@
 }
 
 template <typename Strategy>
+void SimplifiedBackwardsTextIteratorAlgorithm<
+    Strategy>::EnsurePositionContainer() const {
+  DCHECK(text_state_.PositionNode());
+  if (text_state_.PositionContainerNode())
+    return;
+  const Node& node = *text_state_.PositionNode();
+  const ContainerNode* parent = Strategy::Parent(node);
+  DCHECK(parent);
+  text_state_.UpdatePositionOffsets(*parent, Strategy::Index(node));
+}
+
+template <typename Strategy>
 const Node* SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartContainer()
     const {
-  if (text_state_.PositionNode())
-    return text_state_.PositionNode();
-  return start_node_;
+  if (!text_state_.PositionNode())
+    return start_node_;
+  EnsurePositionContainer();
+  return text_state_.PositionContainerNode();
+}
+
+template <typename Strategy>
+int SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartOffset() const {
+  if (!text_state_.PositionNode())
+    return start_offset_;
+  EnsurePositionContainer();
+  return text_state_.PositionStartOffset();
 }
 
 template <typename Strategy>
 int SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndOffset() const {
-  if (text_state_.PositionNode())
-    return text_state_.PositionEndOffset();
-  return start_offset_;
+  if (!text_state_.PositionNode())
+    return start_offset_;
+  EnsurePositionContainer();
+  return text_state_.PositionEndOffset();
 }
 
 template <typename Strategy>
 PositionTemplate<Strategy>
 SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::StartPosition() const {
-  if (text_state_.PositionNode()) {
-    return PositionTemplate<Strategy>::EditingPositionOf(
-        text_state_.PositionNode(), text_state_.PositionStartOffset());
-  }
-  return PositionTemplate<Strategy>::EditingPositionOf(start_node_.Get(),
-                                                       start_offset_);
+  return PositionTemplate<Strategy>::EditingPositionOf(StartContainer(),
+                                                       StartOffset());
 }
 
 template <typename Strategy>
 PositionTemplate<Strategy>
 SimplifiedBackwardsTextIteratorAlgorithm<Strategy>::EndPosition() const {
-  if (text_state_.PositionNode()) {
-    return PositionTemplate<Strategy>::EditingPositionOf(
-        text_state_.PositionNode(), text_state_.PositionEndOffset());
-  }
-  return PositionTemplate<Strategy>::EditingPositionOf(start_node_.Get(),
-                                                       start_offset_);
+  return PositionTemplate<Strategy>::EditingPositionOf(StartContainer(),
+                                                       EndOffset());
 }
 
 template <typename Strategy>
diff --git a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h
index 5eb6225f..ad1b8d6 100644
--- a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h
+++ b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h
@@ -73,6 +73,14 @@
                  int min_length) const;
   int CopyTextTo(BackwardsTextBuffer* output, int position = 0) const;
 
+  // TODO(editing-dev): We should consider code sharing between |TextIterator|
+  // and |SimplifiedBackwardsTextIterator| for
+  //  - StartContainer()
+  //  - EndOffset()
+  //  - StartContainer()
+  //  - EndPosition()
+  // TODO(editing-dev): We should rename |StartContainer()| to
+  // |CurrentContainer()| as |TextIterator|.
   const Node* StartContainer() const;
   int EndOffset() const;
   PositionTemplate<Strategy> StartPosition() const;
@@ -88,11 +96,17 @@
   LayoutText* HandleFirstLetter(int& start_offset, int& offset_in_node);
   bool HandleReplacedElement();
   bool HandleNonTextNode();
-  void EmitCharacter(UChar, const Node*, int start_offset, int end_offset);
   bool AdvanceRespectingRange(const Node*);
 
   bool IsBetweenSurrogatePair(int position) const;
 
+  // TODO(editing-dev): We should consider code sharing between |TextIterator|
+  // and |SimplifiedBackwardsTextIterator| for
+  //  - EnsurePositionContainer()
+  //  - StartOffset()
+  void EnsurePositionContainer() const;
+  int StartOffset() const;
+
   TextIteratorBehavior behavior_;
 
   // Contains state of emitted text.
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
index de7d55a..5d28508 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
@@ -249,7 +249,7 @@
     // iteration, instead of using m_needsAnotherNewline.
     Node* last_child = Strategy::LastChild(*node_);
     const Node* base_node = last_child ? last_child : node_.Get();
-    SpliceBuffer('\n', Strategy::Parent(*base_node), base_node, 1, 1);
+    EmitChar16AfterNode('\n', *base_node);
     needs_another_newline_ = false;
     return true;
   }
@@ -508,15 +508,13 @@
   }
 
   if (EmitsObjectReplacementCharacter()) {
-    SpliceBuffer(kObjectReplacementCharacter, Strategy::Parent(*node_), node_,
-                 0, 1);
+    EmitChar16AsNode(kObjectReplacementCharacter, *node_);
     return;
   }
 
   DCHECK_EQ(last_text_node_, text_node_handler_.GetNode());
   if (last_text_node_) {
-    if (text_node_handler_.FixLeadingWhiteSpaceForReplacedElement(
-            Strategy::Parent(*last_text_node_))) {
+    if (text_node_handler_.FixLeadingWhiteSpaceForReplacedElement()) {
       needs_handle_replaced_element_ = true;
       return;
     }
@@ -531,17 +529,18 @@
     // We want replaced elements to behave like punctuation for boundary
     // finding, and to simply take up space for the selection preservation
     // code in moveParagraphs, so we use a comma.
-    SpliceBuffer(',', Strategy::Parent(*node_), node_, 0, 1);
+    EmitChar16AsNode(',', *node_);
     return;
   }
 
-  text_state_.UpdateForReplacedElement(*Strategy::Parent(*node_), *node_);
-
   if (EmitsImageAltText() && TextIterator::SupportsAltText(*node_)) {
-    text_state_.EmitAltText(node_);
-    if (text_state_.length())
-      return;
+    text_state_.EmitAltText(ToHTMLElement(*node_));
+    return;
   }
+  // TODO(editing-dev): We can remove |UpdateForReplacedElement()| call when
+  // we address layout test failures (text diff by newlines only) and unit
+  // tests, e.g. TextIteratorTest.IgnoreAltTextInTextControls.
+  text_state_.UpdateForReplacedElement(*node_);
 }
 
 template <typename Strategy>
@@ -576,6 +575,8 @@
   // a newline both before and after the element.
   LayoutObject* r = node.GetLayoutObject();
   if (!r) {
+    if (HasDisplayContents(node))
+      return false;
     return (node.HasTagName(blockquoteTag) || node.HasTagName(ddTag) ||
             node.HasTagName(divTag) || node.HasTagName(dlTag) ||
             node.HasTagName(dtTag) || node.HasTagName(h1Tag) ||
@@ -727,6 +728,8 @@
 void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() {
   // Emit a character to show the positioning of m_node.
 
+  // TODO(editing-dev): We should rewrite this below code fragment to utilize
+  // early-return style.
   // When we haven't been emitting any characters,
   // shouldRepresentNodeOffsetZero() can create VisiblePositions, which is
   // expensive. So, we perform the inexpensive checks on m_node to see if it
@@ -734,23 +737,23 @@
   // encountering shouldRepresentNodeOffsetZero()s worse case behavior.
   if (ShouldEmitTabBeforeNode(*node_)) {
     if (ShouldRepresentNodeOffsetZero())
-      SpliceBuffer('\t', Strategy::Parent(*node_), node_, 0, 0);
+      EmitChar16BeforeNode('\t', *node_);
   } else if (ShouldEmitNewlineBeforeNode(*node_)) {
     if (ShouldRepresentNodeOffsetZero())
-      SpliceBuffer('\n', Strategy::Parent(*node_), node_, 0, 0);
+      EmitChar16BeforeNode('\n', *node_);
   } else if (ShouldEmitSpaceBeforeAndAfterNode(*node_)) {
     if (ShouldRepresentNodeOffsetZero())
-      SpliceBuffer(kSpaceCharacter, Strategy::Parent(*node_), node_, 0, 0);
+      EmitChar16BeforeNode(kSpaceCharacter, *node_);
   }
 }
 
 template <typename Strategy>
 void TextIteratorAlgorithm<Strategy>::HandleNonTextNode() {
   if (ShouldEmitNewlineForNode(*node_, EmitsOriginalText()))
-    SpliceBuffer('\n', Strategy::Parent(*node_), node_, 0, 1);
+    EmitChar16AsNode('\n', *node_);
   else if (EmitsCharactersBetweenAllVisiblePositions() &&
            node_->GetLayoutObject() && node_->GetLayoutObject()->IsHR())
-    SpliceBuffer(kSpaceCharacter, Strategy::Parent(*node_), node_, 0, 1);
+    EmitChar16AsNode(kSpaceCharacter, *node_);
   else
     RepresentNodeOffsetZero();
 }
@@ -784,32 +787,39 @@
     // contain a VisiblePosition when doing selection preservation.
     if (text_state_.LastCharacter() != '\n') {
       // insert a newline with a position following this block's contents.
-      SpliceBuffer(kNewlineCharacter, Strategy::Parent(*base_node), base_node,
-                   1, 1);
+      EmitChar16AfterNode(kNewlineCharacter, *base_node);
       // remember whether to later add a newline for the current node
       DCHECK(!needs_another_newline_);
       needs_another_newline_ = add_newline;
     } else if (add_newline) {
       // insert a newline with a position following this block's contents.
-      SpliceBuffer(kNewlineCharacter, Strategy::Parent(*base_node), base_node,
-                   1, 1);
+      EmitChar16AfterNode(kNewlineCharacter, *base_node);
     }
   }
 
   // If nothing was emitted, see if we need to emit a space.
   if (!text_state_.PositionNode() && ShouldEmitSpaceBeforeAndAfterNode(*node_))
-    SpliceBuffer(kSpaceCharacter, Strategy::Parent(*base_node), base_node, 1,
-                 1);
+    EmitChar16AfterNode(kSpaceCharacter, *base_node);
 }
 
 template <typename Strategy>
-void TextIteratorAlgorithm<Strategy>::SpliceBuffer(UChar c,
-                                                   const Node* text_node,
-                                                   const Node* offset_base_node,
-                                                   unsigned text_start_offset,
-                                                   unsigned text_end_offset) {
-  text_state_.SpliceBuffer(c, text_node, offset_base_node, text_start_offset,
-                           text_end_offset);
+void TextIteratorAlgorithm<Strategy>::EmitChar16AfterNode(UChar code_unit,
+                                                          const Node& node) {
+  text_state_.EmitChar16AfterNode(code_unit, node);
+  text_node_handler_.ResetCollapsedWhiteSpaceFixup();
+}
+
+template <typename Strategy>
+void TextIteratorAlgorithm<Strategy>::EmitChar16AsNode(UChar code_unit,
+                                                       const Node& node) {
+  text_state_.EmitChar16AsNode(code_unit, node);
+  text_node_handler_.ResetCollapsedWhiteSpaceFixup();
+}
+
+template <typename Strategy>
+void TextIteratorAlgorithm<Strategy>::EmitChar16BeforeNode(UChar code_unit,
+                                                           const Node& node) {
+  text_state_.EmitChar16BeforeNode(code_unit, node);
   text_node_handler_.ResetCollapsedWhiteSpaceFixup();
 }
 
@@ -842,30 +852,37 @@
 
 template <typename Strategy>
 int TextIteratorAlgorithm<Strategy>::StartOffsetInCurrentContainer() const {
-  if (const Node* node = text_state_.PositionNode()) {
-    if (const Node* base_node = text_state_.PositionOffsetBaseNode())
-      text_state_.UpdatePositionOffsets(Strategy::Index(*base_node));
-    return text_state_.PositionStartOffset();
-  }
-  return end_offset_;
+  if (!text_state_.PositionNode())
+    return end_offset_;
+  EnsurePositionContainer();
+  return text_state_.PositionStartOffset();
 }
 
 template <typename Strategy>
 int TextIteratorAlgorithm<Strategy>::EndOffsetInCurrentContainer() const {
-  if (const Node* node = text_state_.PositionNode()) {
-    if (const Node* base_node = text_state_.PositionOffsetBaseNode())
-      text_state_.UpdatePositionOffsets(Strategy::Index(*base_node));
-    return text_state_.PositionEndOffset();
-  }
-  return end_offset_;
+  if (!text_state_.PositionNode())
+    return end_offset_;
+  EnsurePositionContainer();
+  return text_state_.PositionEndOffset();
 }
 
 template <typename Strategy>
 const Node* TextIteratorAlgorithm<Strategy>::CurrentContainer() const {
-  if (text_state_.PositionNode()) {
-    return text_state_.PositionNode();
-  }
-  return end_container_;
+  if (!text_state_.PositionNode())
+    return end_container_;
+  EnsurePositionContainer();
+  return text_state_.PositionContainerNode();
+}
+
+template <typename Strategy>
+void TextIteratorAlgorithm<Strategy>::EnsurePositionContainer() const {
+  DCHECK(text_state_.PositionNode());
+  if (text_state_.PositionContainerNode())
+    return;
+  const Node& node = *text_state_.PositionNode();
+  const ContainerNode* parent = Strategy::Parent(node);
+  DCHECK(parent);
+  text_state_.UpdatePositionOffsets(*parent, Strategy::Index(node));
 }
 
 template <typename Strategy>
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.h b/third_party/blink/renderer/core/editing/iterators/text_iterator.h
index 76031a378..6bf64aca 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.h
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.h
@@ -130,6 +130,10 @@
     kHandledChildren
   };
 
+  void EmitChar16AfterNode(UChar code_unit, const Node& node);
+  void EmitChar16AsNode(UChar code_unit, const Node& node);
+  void EmitChar16BeforeNode(UChar code_unit, const Node& node);
+
   void ExitNode();
   bool ShouldRepresentNodeOffsetZero();
   bool ShouldEmitSpaceBeforeAndAfterNode(const Node&);
@@ -141,11 +145,6 @@
   void HandleTextNode();
   void HandleReplacedElement();
   void HandleNonTextNode();
-  void SpliceBuffer(UChar,
-                    const Node* text_node,
-                    const Node* offset_base_node,
-                    unsigned text_start_offset,
-                    unsigned text_end_offset);
 
   // Used by selection preservation code. There should be one character emitted
   // between every VisiblePosition in the Range used to create the TextIterator.
@@ -202,6 +201,9 @@
                        unsigned position,
                        unsigned copy_length) const;
 
+  // Ensure container node of current text run for computing position.
+  void EnsurePositionContainer() const;
+
   // The range.
   const Member<const Node> start_container_;
   const unsigned start_offset_;
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
index b743954..99118c4 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.cc
@@ -181,7 +181,7 @@
       HasVisibleTextNode(layout_object)) {
     if (!behavior_.CollapseTrailingSpace() ||
         (offset_ > 0 && str[offset_ - 1] == ' ')) {
-      SpliceBuffer(kSpaceCharacter, text_node_, nullptr, offset_, offset_);
+      EmitChar16Before(kSpaceCharacter, offset_);
       needs_handle_pre_formatted_text_node_ = true;
       return;
     }
@@ -377,8 +377,7 @@
             --space_run_start;
           EmitText(layout_object, space_run_start, space_run_start + 1);
         } else {
-          SpliceBuffer(kSpaceCharacter, text_node_, nullptr, run_start,
-                       run_start);
+          EmitChar16Before(kSpaceCharacter, run_start);
         }
         return;
       }
@@ -416,10 +415,9 @@
           // We need to preserve new lines in case of PreLine.
           // See bug crbug.com/317365.
           if (layout_object->Style()->WhiteSpace() == EWhiteSpace::kPreLine) {
-            SpliceBuffer('\n', text_node_, nullptr, run_start, run_start);
+            EmitChar16Before('\n', run_start);
           } else {
-            SpliceBuffer(kSpaceCharacter, text_node_, nullptr, run_start,
-                         run_start + 1);
+            EmitReplacmentCodeUnit(kSpaceCharacter, run_start);
           }
           offset_ = text_start_offset + run_start + 1;
         } else {
@@ -537,11 +535,10 @@
   return offset_ > 0 && str[offset_ - 1] == ' ';
 }
 
-bool TextIteratorTextNodeHandler::FixLeadingWhiteSpaceForReplacedElement(
-    const Node* parent) {
+bool TextIteratorTextNodeHandler::FixLeadingWhiteSpaceForReplacedElement() {
   if (!ShouldFixLeadingWhiteSpaceForReplacedElement())
     return false;
-  text_state_.SpliceBuffer(kSpaceCharacter, parent, text_node_, 1, 1);
+  text_state_.EmitChar16AfterNode(kSpaceCharacter, *text_node_);
   ResetCollapsedWhiteSpaceFixup();
   return true;
 }
@@ -552,13 +549,15 @@
   last_text_node_ended_with_collapsed_space_ = false;
 }
 
-void TextIteratorTextNodeHandler::SpliceBuffer(UChar c,
-                                               const Node* text_node,
-                                               const Node* offset_base_node,
-                                               unsigned text_start_offset,
-                                               unsigned text_end_offset) {
-  text_state_.SpliceBuffer(c, text_node, offset_base_node, text_start_offset,
-                           text_end_offset);
+void TextIteratorTextNodeHandler::EmitChar16Before(UChar code_unit,
+                                                   unsigned offset) {
+  text_state_.EmitChar16Before(code_unit, *text_node_, offset);
+  ResetCollapsedWhiteSpaceFixup();
+}
+
+void TextIteratorTextNodeHandler::EmitReplacmentCodeUnit(UChar code_unit,
+                                                         unsigned offset) {
+  text_state_.EmitReplacmentCodeUnit(code_unit, *text_node_, offset);
   ResetCollapsedWhiteSpaceFixup();
 }
 
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h
index 5dfe27d..d050895 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_node_handler.h
@@ -32,7 +32,7 @@
   bool HandleRemainingTextRuns();
 
   // Returns true if a leading white space is emitted before a replaced element.
-  bool FixLeadingWhiteSpaceForReplacedElement(const Node*);
+  bool FixLeadingWhiteSpaceForReplacedElement();
 
   void ResetCollapsedWhiteSpaceFixup();
 
@@ -65,12 +65,12 @@
 
   bool ShouldFixLeadingWhiteSpaceForReplacedElement() const;
 
-  // Emit a character before |offset| of characters in |text_node_|.
-  void SpliceBuffer(UChar,
-                    const Node* text_node,
-                    const Node* offset_base_node,
-                    unsigned text_start_offset,
-                    unsigned text_end_offset);
+  // Emits |code_unit| before |offset| of characters in |text_node_|.
+  void EmitChar16Before(UChar code_unit, unsigned offset);
+  // Emits |code_unit| as replacement of a code unit after |offset| in
+  // |text_node_|.
+  void EmitReplacmentCodeUnit(UChar code_unit, unsigned offset);
+
   void EmitText(const LayoutText* layout_object,
                 unsigned text_start_offset,
                 unsigned text_end_offset);
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc
index 101ade9..7d443a32 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.cc
@@ -49,6 +49,16 @@
     const TextIteratorBehavior& behavior)
     : behavior_(behavior) {}
 
+unsigned TextIteratorTextState::PositionStartOffset() const {
+  DCHECK(position_container_node_);
+  return position_start_offset_.value();
+}
+
+unsigned TextIteratorTextState::PositionEndOffset() const {
+  DCHECK(position_container_node_);
+  return position_end_offset_.value();
+}
+
 UChar TextIteratorTextState::CharacterAt(unsigned index) const {
   SECURITY_DCHECK(index < length());
   if (!(index < length()))
@@ -93,59 +103,116 @@
   }
 }
 
-void TextIteratorTextState::UpdateForReplacedElement(const Node& parent_node,
-                                                     const Node& base_node) {
-  has_emitted_ = true;
-  position_node_ = &parent_node;
-  position_offset_base_node_ = &base_node;
+void TextIteratorTextState::UpdateForReplacedElement(const Node& node) {
+  ResetPositionContainerNode(PositionNodeType::kAsNode, node);
+  PopulateStringBuffer("", 0, 0);
+}
+
+void TextIteratorTextState::ResetPositionContainerNode(
+    PositionNodeType node_type,
+    const Node& node) {
+  DCHECK_NE(node_type, PositionNodeType::kBeforeChildren);
+  DCHECK_NE(node_type, PositionNodeType::kInText);
+  DCHECK_NE(node_type, PositionNodeType::kNone);
+  position_node_type_ = node_type;
+  position_container_node_ = nullptr;
+  position_node_ = node;
+  position_start_offset_ = base::nullopt;
+  position_end_offset_ = base::nullopt;
+}
+
+void TextIteratorTextState::UpdatePositionOffsets(
+    const ContainerNode& container_node,
+    unsigned node_index) const {
+  DCHECK(!position_container_node_);
+  DCHECK(!position_start_offset_.has_value());
+  DCHECK(!position_end_offset_.has_value());
+  switch (position_node_type_) {
+    case PositionNodeType::kAfterNode:
+      position_container_node_ = &container_node;
+      position_start_offset_ = node_index + 1;
+      position_end_offset_ = node_index + 1;
+      return;
+    case PositionNodeType::kAltText:
+    case PositionNodeType::kAsNode:
+      position_container_node_ = &container_node;
+      position_start_offset_ = node_index;
+      position_end_offset_ = node_index + 1;
+      return;
+    case PositionNodeType::kBeforeNode:
+      position_container_node_ = &container_node;
+      position_start_offset_ = node_index;
+      position_end_offset_ = node_index;
+      return;
+    case PositionNodeType::kBeforeChildren:
+    case PositionNodeType::kInText:
+    case PositionNodeType::kNone:
+      NOTREACHED();
+      return;
+  }
+  NOTREACHED() << static_cast<int>(position_node_type_);
+}
+
+void TextIteratorTextState::EmitAltText(const HTMLElement& element) {
+  ResetPositionContainerNode(PositionNodeType::kAltText, element);
+  const String text = element.AltText();
+  PopulateStringBuffer(text, 0, text.length());
+}
+
+void TextIteratorTextState::EmitChar16AfterNode(UChar code_unit,
+                                                const Node& node) {
+  ResetPositionContainerNode(PositionNodeType::kAfterNode, node);
+  PopulateStringBufferFromChar16(code_unit);
+}
+
+void TextIteratorTextState::EmitChar16AsNode(UChar code_unit,
+                                             const Node& node) {
+  ResetPositionContainerNode(PositionNodeType::kAsNode, node);
+  PopulateStringBufferFromChar16(code_unit);
+}
+
+void TextIteratorTextState::EmitChar16BeforeChildren(
+    UChar code_unit,
+    const ContainerNode& container_node) {
+  position_node_type_ = PositionNodeType::kBeforeChildren;
+  position_container_node_ = &container_node;
+  position_node_ = &container_node;
   position_start_offset_ = 0;
-  position_end_offset_ = 1;
-  single_character_buffer_ = 0;
-
-  text_length_ = 0;
-  text_start_offset_ = 0;
-  last_character_ = 0;
+  position_end_offset_ = 0;
+  PopulateStringBufferFromChar16(code_unit);
 }
 
-void TextIteratorTextState::EmitAltText(const Node* node) {
-  text_ = ToHTMLElement(node)->AltText();
-  text_start_offset_ = 0;
-  text_length_ = text_.length();
-  last_character_ = text_length_ ? text_[text_length_ - 1] : 0;
+void TextIteratorTextState::EmitChar16BeforeNode(UChar code_unit,
+                                                 const Node& node) {
+  ResetPositionContainerNode(PositionNodeType::kBeforeNode, node);
+  PopulateStringBufferFromChar16(code_unit);
 }
 
-void TextIteratorTextState::UpdatePositionOffsets(unsigned index) const {
-  DCHECK(position_offset_base_node_);
-  position_start_offset_ += index;
-  position_end_offset_ += index;
-  position_offset_base_node_ = nullptr;
+void TextIteratorTextState::EmitChar16Before(UChar code_unit,
+                                             const Text& text_node,
+                                             unsigned offset) {
+  SetTextNodePosition(text_node, offset, offset);
+  PopulateStringBufferFromChar16(code_unit);
 }
 
-void TextIteratorTextState::SpliceBuffer(UChar c,
-                                         const Node* text_node,
-                                         const Node* offset_base_node,
-                                         unsigned text_start_offset,
-                                         unsigned text_end_offset) {
-  DCHECK(text_node);
+void TextIteratorTextState::EmitReplacmentCodeUnit(UChar code_unit,
+                                                   const Text& text_node,
+                                                   unsigned offset) {
+  SetTextNodePosition(text_node, offset, offset + 1);
+  PopulateStringBufferFromChar16(code_unit);
+}
+
+void TextIteratorTextState::PopulateStringBufferFromChar16(UChar code_unit) {
   has_emitted_ = true;
-
-  // Remember information with which to construct the TextIterator::range().
-  // NOTE: textNode is often not a text node, so the range will specify child
-  // nodes of positionNode
-  position_node_ = text_node;
-  position_offset_base_node_ = offset_base_node;
-  position_start_offset_ = text_start_offset;
-  position_end_offset_ = text_end_offset;
-
   // remember information with which to construct the TextIterator::characters()
   // and length()
-  single_character_buffer_ = c;
+  single_character_buffer_ = code_unit;
   DCHECK(single_character_buffer_);
   text_length_ = 1;
   text_start_offset_ = 0;
 
   // remember some iteration state
-  last_character_ = c;
+  last_character_ = code_unit;
 }
 
 void TextIteratorTextState::EmitText(const Text& text_node,
@@ -155,31 +222,48 @@
                                      unsigned text_start_offset,
                                      unsigned text_end_offset) {
   DCHECK_LE(position_start_offset, position_end_offset);
-  // TODO(editing-dev): text-transform:uppercase can make text longer, e.g.
-  // "U+00DF" to "SS". See "fast/css/case-transform.html"
-  // DCHECK_LE(position_end_offset, text_node.length());
-  text_ =
+  const String text =
       behavior_.EmitsSmallXForTextSecurity() && IsTextSecurityNode(text_node)
           ? RepeatString("x", string.length())
-          : string,
+          : string;
 
-  DCHECK(!text_.IsEmpty());
-  DCHECK_LT(text_start_offset, text_.length());
-  DCHECK_LE(text_end_offset, text_.length());
+  DCHECK(!text.IsEmpty());
+  DCHECK_LT(text_start_offset, text.length());
+  DCHECK_LE(text_end_offset, text.length());
   DCHECK_LE(text_start_offset, text_end_offset);
 
-  position_node_ = &text_node;
-  position_offset_base_node_ = nullptr;
-  position_start_offset_ = position_start_offset;
-  position_end_offset_ = position_end_offset;
+  SetTextNodePosition(text_node, position_start_offset, position_end_offset);
+  PopulateStringBuffer(text, text_start_offset, text_end_offset);
+}
+
+void TextIteratorTextState::PopulateStringBuffer(const String& text,
+                                                 unsigned text_start_offset,
+                                                 unsigned text_end_offset) {
+  DCHECK_LE(text_start_offset, text_end_offset);
+  DCHECK_LE(text_end_offset, text.length());
+  text_ = text;
   single_character_buffer_ = 0;
   text_start_offset_ = text_start_offset;
   text_length_ = text_end_offset - text_start_offset;
-  last_character_ = text_[text_end_offset - 1];
+  last_character_ = text_end_offset == 0 ? 0 : text_[text_end_offset - 1];
 
   has_emitted_ = true;
 }
 
+void TextIteratorTextState::SetTextNodePosition(const Text& text_node,
+                                                unsigned position_start_offset,
+                                                unsigned position_end_offset) {
+  DCHECK_LE(position_start_offset, position_end_offset);
+  // TODO(editing-dev): text-transform:uppercase can make text longer, e.g.
+  // "U+00DF" to "SS". See "fast/css/case-transform.html"
+  // DCHECK_LE(position_end_offset, text_node.length());
+  position_node_type_ = PositionNodeType::kInText;
+  position_container_node_ = &text_node;
+  position_node_ = &text_node;
+  position_start_offset_ = position_start_offset;
+  position_end_offset_ = position_end_offset;
+}
+
 void TextIteratorTextState::AppendTextTo(ForwardsTextBuffer* output,
                                          unsigned position,
                                          unsigned length_to_append) const {
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h
index 0758992..8f1f040 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_text_state.h
@@ -26,6 +26,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_ITERATORS_TEXT_ITERATOR_TEXT_STATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_ITERATORS_TEXT_ITERATOR_TEXT_STATE_H_
 
+#include "base/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/editing/iterators/forwards_text_buffer.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_iterator_behavior.h"
@@ -35,6 +36,8 @@
 namespace blink {
 
 class BackwardsTextBuffer;
+class ContainerNode;
+class HTMLElement;
 class Text;
 
 class CORE_EXPORT TextIteratorTextState {
@@ -57,28 +60,37 @@
                      unsigned position,
                      unsigned length_to_prepend) const;
 
-  void SpliceBuffer(UChar,
-                    const Node* text_node,
-                    const Node* offset_base_node,
-                    unsigned text_start_offset,
-                    unsigned text_end_offset);
+  // Emits code unit relative to |node|.
+  void EmitChar16AfterNode(UChar code_unit, const Node& node);
+  void EmitChar16AsNode(UChar code_unit, const Node& node);
+  void EmitChar16BeforeChildren(UChar code_unit,
+                                const ContainerNode& container_node);
+  void EmitChar16BeforeNode(UChar code_unit, const Node& node);
+
+  // Emits |code_unit| before |offset| in |text_node|.
+  void EmitChar16Before(UChar code_unit,
+                        const Text& text_node,
+                        unsigned offset);
+  // Emits |code_unit| as replacement of code unit at |offset| in |text_node|.
+  void EmitReplacmentCodeUnit(UChar code_unit,
+                              const Text& text_node,
+                              unsigned offset);
   void EmitText(const Text&,
                 unsigned position_start_offset,
                 unsigned position_end_offset,
                 const String&,
                 unsigned text_start_offset,
                 unsigned text_end_offset);
-  void EmitAltText(const Node*);
-  void UpdateForReplacedElement(const Node& parent_node, const Node& base_node);
+  void EmitAltText(const HTMLElement&);
+  void UpdateForReplacedElement(const Node& node);
 
   // Return position of the current text.
-  void UpdatePositionOffsets(unsigned node_index) const;
-  unsigned PositionStartOffset() const { return position_start_offset_; }
-  unsigned PositionEndOffset() const { return position_end_offset_; }
+  void UpdatePositionOffsets(const ContainerNode& container_node,
+                             unsigned index) const;
+  unsigned PositionStartOffset() const;
+  unsigned PositionEndOffset() const;
   const Node* PositionNode() const { return position_node_; }
-  const Node* PositionOffsetBaseNode() const {
-    return position_offset_base_node_;
-  }
+  const Node* PositionContainerNode() const { return position_container_node_; }
 
   bool HasEmitted() const { return has_emitted_; }
   UChar LastCharacter() const { return last_character_; }
@@ -88,9 +100,29 @@
   }
 
  private:
+  // Location of text run relative to |position_node_|.
+  enum class PositionNodeType {
+    kNone,
+    kAfterNode,
+    kAltText,
+    kAsNode,
+    kBeforeChildren,
+    kBeforeNode,
+    kInText,
+  };
+
+  void ResetPositionContainerNode(PositionNodeType node_type, const Node& node);
+  void SetTextNodePosition(const Text& text_node,
+                           unsigned start_offset,
+                           unsigned end_offset);
+  void PopulateStringBuffer(const String& text, unsigned start, unsigned end);
+  void PopulateStringBufferFromChar16(UChar code_unit);
+
   const TextIteratorBehavior behavior_;
   unsigned text_length_ = 0;
 
+  // TODO(editing-dev): We should integrate single character buffer and
+  // string buffer.
   // Used for whitespace characters that aren't in the DOM, so we can point at
   // them.
   // If non-zero, overrides |text_|.
@@ -99,13 +131,17 @@
   // The current text when |single_character_buffer_| is zero, in which case it
   // is |text_.Substring(text_start_offset_, text_length_)|.
   String text_;
+  // TODO(editing-dev): We should make |text_| to hold substring instead of
+  // entire string with |text_start_offset_| and |text_length_|.
   unsigned text_start_offset_ = 0;
 
   // Position of the current text, in the form to be returned from the iterator.
   Member<const Node> position_node_;
-  mutable Member<const Node> position_offset_base_node_;
-  mutable unsigned position_start_offset_ = 0;
-  mutable unsigned position_end_offset_ = 0;
+  // |Text| node when |position_node_type_ == kInText| or |ContainerNode|.
+  mutable Member<const Node> position_container_node_;
+  mutable base::Optional<unsigned> position_start_offset_;
+  mutable base::Optional<unsigned> position_end_offset_;
+  PositionNodeType position_node_type_ = PositionNodeType::kNone;
 
   // Used when deciding whether to emit a "positioning" (e.g. newline) before
   // any other content
diff --git a/third_party/blink/renderer/core/html/html_slot_element.cc b/third_party/blink/renderer/core/html/html_slot_element.cc
index 936d8dd..00e871b 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.cc
+++ b/third_party/blink/renderer/core/html/html_slot_element.cc
@@ -553,8 +553,6 @@
 
 void HTMLSlotElement::LazyReattachDistributedNodesIfNeeded() {
   DCHECK(!RuntimeEnabledFeatures::IncrementalShadowDOMEnabled());
-  // TODO(hayato): Move this probe to a better place.
-  probe::didPerformSlotDistribution(this);
 
   LazyReattachNodesIfNeeded(old_distributed_nodes_, distributed_nodes_);
   old_distributed_nodes_.clear();
@@ -565,6 +563,7 @@
     const HeapVector<Member<Node>>& nodes2) {
   if (nodes1 == nodes2)
     return;
+  probe::didPerformSlotDistribution(this);
 
   if (nodes1.size() + 1 > kLCSTableSizeLimit ||
       nodes2.size() + 1 > kLCSTableSizeLimit) {
diff --git a/third_party/blink/renderer/core/html/html_slot_element.h b/third_party/blink/renderer/core/html/html_slot_element.h
index b7327f1..d3743d0 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.h
+++ b/third_party/blink/renderer/core/html/html_slot_element.h
@@ -145,8 +145,8 @@
 
   const HeapVector<Member<Node>>& ChildrenInFlatTreeIfAssignmentIsSupported();
 
-  static void LazyReattachNodesIfNeeded(const HeapVector<Member<Node>>& nodes1,
-                                        const HeapVector<Member<Node>>& nodes2);
+  void LazyReattachNodesIfNeeded(const HeapVector<Member<Node>>& nodes1,
+                                 const HeapVector<Member<Node>>& nodes2);
   static void LazyReattachNodesNaive(const HeapVector<Member<Node>>& nodes1,
                                      const HeapVector<Member<Node>>& nodes2);
   static void LazyReattachNodesByDynamicProgramming(
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index f1e77c0..16e8a85 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -1656,12 +1656,25 @@
 
 std::unique_ptr<protocol::Array<protocol::DOM::BackendNode>>
 InspectorDOMAgent::BuildDistributedNodesForSlot(HTMLSlotElement* slot_element) {
+  // TODO(hayato): In Shadow DOM v1, the concept of distributed nodes should
+  // not be used anymore. DistributedNodes should be replaced with
+  // AssignedNodes() when IncrementalShadowDOM becomes stable and Shadow DOM v0
+  // is removed.
   std::unique_ptr<protocol::Array<protocol::DOM::BackendNode>>
       distributed_nodes = protocol::Array<protocol::DOM::BackendNode>::create();
   if (RuntimeEnabledFeatures::IncrementalShadowDOMEnabled()) {
-    // TODO(hayato): Support distributed_nodes for IncrementalShadowDOM.
-    // We might use HTMLSlotElement::flat_tree_children here, however, we don't
-    // want to expose it, as of now.
+    for (auto& node : slot_element->AssignedNodes()) {
+      if (IsWhitespace(node))
+        continue;
+
+      std::unique_ptr<protocol::DOM::BackendNode> backend_node =
+          protocol::DOM::BackendNode::create()
+              .setNodeType(node->getNodeType())
+              .setNodeName(node->nodeName())
+              .setBackendNodeId(DOMNodeIds::IdForNode(node))
+              .build();
+      distributed_nodes->addItem(std::move(backend_node));
+    }
     return distributed_nodes;
   }
   for (Node* node = slot_element->FirstDistributedNode(); node;
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
index e473ae2..bfef042 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.cc
@@ -210,14 +210,4 @@
          !ToLayoutInline(GetLayoutObject())->Continuation();
 }
 
-NGInlineItemRange::NGInlineItemRange(Vector<NGInlineItem>* items,
-                                     unsigned start_index,
-                                     unsigned end_index)
-    : start_item_(&(*items)[start_index]),
-      size_(end_index - start_index),
-      start_index_(start_index) {
-  CHECK_LE(start_index, end_index);
-  CHECK_LE(end_index, items->size());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
index 7ca8834..f3111c7 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item.h
@@ -172,43 +172,6 @@
   DCHECK_LE(offset, end_offset_);
 }
 
-// A vector-like object that points to a subset of an array of |NGInlineItem|.
-// The source vector must keep alive and must not resize while this object
-// is alive.
-class NGInlineItemRange {
-  STACK_ALLOCATED();
-
- public:
-  NGInlineItemRange(Vector<NGInlineItem>*,
-                    unsigned start_index,
-                    unsigned end_index);
-
-  unsigned StartIndex() const { return start_index_; }
-  unsigned EndIndex() const { return start_index_ + size_; }
-  unsigned Size() const { return size_; }
-
-  NGInlineItem& operator[](unsigned index) {
-    CHECK_LT(index, size_);
-    return start_item_[index];
-  }
-  const NGInlineItem& operator[](unsigned index) const {
-    CHECK_LT(index, size_);
-    return start_item_[index];
-  }
-
-  using iterator = NGInlineItem*;
-  using const_iterator = const NGInlineItem*;
-  iterator begin() { return start_item_; }
-  iterator end() { return start_item_ + size_; }
-  const_iterator begin() const { return start_item_; }
-  const_iterator end() const { return start_item_ + size_; }
-
- private:
-  NGInlineItem* start_item_;
-  unsigned size_;
-  unsigned start_index_;
-};
-
 }  // namespace blink
 
 #endif  // NGInlineItem_h
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
index d343263..29dfb0a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.cc
@@ -205,10 +205,6 @@
   return *data.first_line_items_;
 }
 
-NGInlineItemRange NGInlineNode::Items(unsigned start, unsigned end) {
-  return NGInlineItemRange(&MutableData()->items_, start, end);
-}
-
 void NGInlineNode::InvalidatePrepareLayoutForTest() {
   GetLayoutBlockFlow()->ResetNGInlineNodeData();
   DCHECK(!IsPrepareLayoutFinished());
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
index 81f1d59..81019b5 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h
@@ -22,7 +22,6 @@
 struct MinMaxSize;
 class NGConstraintSpace;
 class NGInlineItem;
-class NGInlineItemRange;
 using NGInlineItemsBuilder =
     NGInlineItemsBuilderTemplate<EmptyOffsetMappingBuilder>;
 struct NGInlineNodeData;
@@ -64,7 +63,6 @@
   }
 
   const Vector<NGInlineItem>& Items(bool is_first_line = false) const;
-  NGInlineItemRange Items(unsigned start_index, unsigned end_index);
 
   // Returns the DOM to text content offset mapping of this block. If it is not
   // computed before, compute and store it in NGInlineNodeData.
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 33d9b9e..d154a67fe 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -726,7 +726,6 @@
       !request.GetResourceRequest().IsSameDocumentNavigation() &&
       !frame_->Client()->AllowContentInitiatedDataUrlNavigations(
           request.OriginDocument()->Url()) &&
-      !request.GetResourceRequest().GetSuggestedFilename().has_value() &&
       (url.ProtocolIs("filesystem") ||
        (url.ProtocolIsData() &&
         NetworkUtils::IsDataURLMimeTypeSupported(url)))) {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
index d1bf239..88430ca 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
@@ -318,6 +318,8 @@
 
   auto platform_capabilities = component_->Source()->GetCapabilities();
   capabilities.setDeviceId(platform_capabilities.device_id);
+  if (!platform_capabilities.group_id.IsNull())
+    capabilities.setGroupId(platform_capabilities.group_id);
 
   if (component_->Source()->GetType() == MediaStreamSource::kTypeAudio) {
     Vector<bool> echo_cancellation, auto_gain_control, noise_suppression;
diff --git a/third_party/blink/renderer/platform/exported/web_url_load_timing.cc b/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
index 43310e1b..567469e 100644
--- a/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
@@ -47,148 +47,132 @@
   private_ = other.private_;
 }
 
-double WebURLLoadTiming::RequestTime() const {
-  return TimeTicksInSeconds(private_->RequestTime());
+base::TimeTicks WebURLLoadTiming::RequestTime() const {
+  return private_->RequestTime();
 }
 
-void WebURLLoadTiming::SetRequestTime(double time) {
-  DCHECK_GE(time, 0.0);
-  private_->SetRequestTime(TimeTicksFromSeconds(time));
+void WebURLLoadTiming::SetRequestTime(base::TimeTicks time) {
+  private_->SetRequestTime(time);
 }
 
-double WebURLLoadTiming::ProxyStart() const {
-  return TimeTicksInSeconds(private_->ProxyStart());
+base::TimeTicks WebURLLoadTiming::ProxyStart() const {
+  return private_->ProxyStart();
 }
 
-void WebURLLoadTiming::SetProxyStart(double start) {
-  DCHECK_GE(start, 0.0);
-  private_->SetProxyStart(TimeTicksFromSeconds(start));
+void WebURLLoadTiming::SetProxyStart(base::TimeTicks start) {
+  private_->SetProxyStart(start);
 }
 
-double WebURLLoadTiming::ProxyEnd() const {
-  return TimeTicksInSeconds(private_->ProxyEnd());
+base::TimeTicks WebURLLoadTiming::ProxyEnd() const {
+  return private_->ProxyEnd();
 }
 
-void WebURLLoadTiming::SetProxyEnd(double end) {
-  DCHECK_GE(end, 0.0);
-  private_->SetProxyEnd(TimeTicksFromSeconds(end));
+void WebURLLoadTiming::SetProxyEnd(base::TimeTicks end) {
+  private_->SetProxyEnd(end);
 }
 
-double WebURLLoadTiming::DnsStart() const {
-  return TimeTicksInSeconds(private_->DnsStart());
+base::TimeTicks WebURLLoadTiming::DnsStart() const {
+  return private_->DnsStart();
 }
 
-void WebURLLoadTiming::SetDNSStart(double start) {
-  DCHECK_GE(start, 0.0);
-  private_->SetDnsStart(TimeTicksFromSeconds(start));
+void WebURLLoadTiming::SetDNSStart(base::TimeTicks start) {
+  private_->SetDnsStart(start);
 }
 
-double WebURLLoadTiming::DnsEnd() const {
-  return TimeTicksInSeconds(private_->DnsEnd());
+base::TimeTicks WebURLLoadTiming::DnsEnd() const {
+  return private_->DnsEnd();
 }
 
-void WebURLLoadTiming::SetDNSEnd(double end) {
-  DCHECK_GE(end, 0.0);
-  private_->SetDnsEnd(TimeTicksFromSeconds(end));
+void WebURLLoadTiming::SetDNSEnd(base::TimeTicks end) {
+  private_->SetDnsEnd(end);
 }
 
-double WebURLLoadTiming::ConnectStart() const {
-  return TimeTicksInSeconds(private_->ConnectStart());
+base::TimeTicks WebURLLoadTiming::ConnectStart() const {
+  return private_->ConnectStart();
 }
 
-void WebURLLoadTiming::SetConnectStart(double start) {
-  DCHECK_GE(start, 0.0);
-  private_->SetConnectStart(TimeTicksFromSeconds(start));
+void WebURLLoadTiming::SetConnectStart(base::TimeTicks start) {
+  private_->SetConnectStart(start);
 }
 
-double WebURLLoadTiming::ConnectEnd() const {
-  return TimeTicksInSeconds(private_->ConnectEnd());
+base::TimeTicks WebURLLoadTiming::ConnectEnd() const {
+  return private_->ConnectEnd();
 }
 
-void WebURLLoadTiming::SetConnectEnd(double end) {
-  DCHECK_GE(end, 0.0);
-  private_->SetConnectEnd(TimeTicksFromSeconds(end));
+void WebURLLoadTiming::SetConnectEnd(base::TimeTicks end) {
+  private_->SetConnectEnd(end);
 }
 
-double WebURLLoadTiming::WorkerStart() const {
-  return TimeTicksInSeconds(private_->WorkerStart());
+base::TimeTicks WebURLLoadTiming::WorkerStart() const {
+  return private_->WorkerStart();
 }
 
-void WebURLLoadTiming::SetWorkerStart(double start) {
-  DCHECK_GE(start, 0.0);
-  private_->SetWorkerStart(TimeTicksFromSeconds(start));
+void WebURLLoadTiming::SetWorkerStart(base::TimeTicks start) {
+  private_->SetWorkerStart(start);
 }
 
-double WebURLLoadTiming::WorkerReady() const {
-  return TimeTicksInSeconds(private_->WorkerReady());
+base::TimeTicks WebURLLoadTiming::WorkerReady() const {
+  return private_->WorkerReady();
 }
 
-void WebURLLoadTiming::SetWorkerReady(double ready) {
-  DCHECK_GE(ready, 0.0);
-  private_->SetWorkerReady(TimeTicksFromSeconds(ready));
+void WebURLLoadTiming::SetWorkerReady(base::TimeTicks ready) {
+  private_->SetWorkerReady(ready);
 }
 
-double WebURLLoadTiming::SendStart() const {
-  return TimeTicksInSeconds(private_->SendStart());
+base::TimeTicks WebURLLoadTiming::SendStart() const {
+  return private_->SendStart();
 }
 
-void WebURLLoadTiming::SetSendStart(double start) {
-  DCHECK_GE(start, 0.0);
-  private_->SetSendStart(TimeTicksFromSeconds(start));
+void WebURLLoadTiming::SetSendStart(base::TimeTicks start) {
+  private_->SetSendStart(start);
 }
 
-double WebURLLoadTiming::SendEnd() const {
-  return TimeTicksInSeconds(private_->SendEnd());
+base::TimeTicks WebURLLoadTiming::SendEnd() const {
+  return private_->SendEnd();
 }
 
-void WebURLLoadTiming::SetSendEnd(double end) {
-  DCHECK_GE(end, 0.0);
-  private_->SetSendEnd(TimeTicksFromSeconds(end));
+void WebURLLoadTiming::SetSendEnd(base::TimeTicks end) {
+  private_->SetSendEnd(end);
 }
 
-double WebURLLoadTiming::ReceiveHeadersEnd() const {
-  return TimeTicksInSeconds(private_->ReceiveHeadersEnd());
+base::TimeTicks WebURLLoadTiming::ReceiveHeadersEnd() const {
+  return private_->ReceiveHeadersEnd();
 }
 
-void WebURLLoadTiming::SetReceiveHeadersEnd(double end) {
-  DCHECK_GE(end, 0.0);
-  private_->SetReceiveHeadersEnd(TimeTicksFromSeconds(end));
+void WebURLLoadTiming::SetReceiveHeadersEnd(base::TimeTicks end) {
+  private_->SetReceiveHeadersEnd(end);
 }
 
-double WebURLLoadTiming::SslStart() const {
-  return TimeTicksInSeconds(private_->SslStart());
+base::TimeTicks WebURLLoadTiming::SslStart() const {
+  return private_->SslStart();
 }
 
-void WebURLLoadTiming::SetSSLStart(double start) {
-  DCHECK_GE(start, 0.0);
-  private_->SetSslStart(TimeTicksFromSeconds(start));
+void WebURLLoadTiming::SetSSLStart(base::TimeTicks start) {
+  private_->SetSslStart(start);
 }
 
-double WebURLLoadTiming::SslEnd() const {
-  return TimeTicksInSeconds(private_->SslEnd());
+base::TimeTicks WebURLLoadTiming::SslEnd() const {
+  return private_->SslEnd();
 }
 
-void WebURLLoadTiming::SetSSLEnd(double end) {
-  DCHECK_GE(end, 0.0);
-  private_->SetSslEnd(TimeTicksFromSeconds(end));
+void WebURLLoadTiming::SetSSLEnd(base::TimeTicks end) {
+  private_->SetSslEnd(end);
 }
 
-double WebURLLoadTiming::PushStart() const {
-  return TimeTicksInSeconds(private_->PushStart());
+base::TimeTicks WebURLLoadTiming::PushStart() const {
+  return private_->PushStart();
 }
 
-void WebURLLoadTiming::SetPushStart(double start) {
-  DCHECK_GE(start, 0.0);
-  private_->SetPushStart(TimeTicksFromSeconds(start));
+void WebURLLoadTiming::SetPushStart(base::TimeTicks start) {
+  private_->SetPushStart(start);
 }
 
-double WebURLLoadTiming::PushEnd() const {
-  return TimeTicksInSeconds(private_->PushEnd());
+base::TimeTicks WebURLLoadTiming::PushEnd() const {
+  return private_->PushEnd();
 }
 
-void WebURLLoadTiming::SetPushEnd(double end) {
-  DCHECK_GE(end, 0.0);
-  private_->SetPushEnd(TimeTicksFromSeconds(end));
+void WebURLLoadTiming::SetPushEnd(base::TimeTicks end) {
+  private_->SetPushEnd(end);
 }
 
 WebURLLoadTiming::WebURLLoadTiming(scoped_refptr<ResourceLoadTiming> value)
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index d196bb5..4614d56 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -8,14 +8,16 @@
 import("//testing/test.gni")
 
 declare_args() {
-  # Build Blink with incremental marking infrastructure for Oilpan.
+  # Enables incremental marking in Oilpan.
   #
-  # To turn on incremental marking also use
-  #   --enable-blink-features=HeapIncrementalMarking
-  enable_blink_heap_incremental_marking = true
+  # Note: Incremental marking is currently considered experimental and also
+  # enables 'enable_blink_heap_incremental_marking'. See default value below.
+  enable_blink_heap_incremental_marking = false
+}
 
+declare_args() {
   # Enables heap verification.
-  enable_blink_heap_verification = false
+  enable_blink_heap_verification = enable_blink_heap_incremental_marking
 }
 
 buildflag_header("blink_heap_buildflags") {
@@ -30,6 +32,8 @@
 
 blink_platform_sources("heap") {
   sources = [
+    "address_cache.cc",
+    "address_cache.h",
     "blink_gc.h",
     "blink_gc_memory_dump_provider.cc",
     "blink_gc_memory_dump_provider.h",
@@ -104,6 +108,7 @@
 jumbo_source_set("blink_heap_unittests_sources") {
   testonly = true
   sources = [
+    "address_cache_test.cc",
     "blink_gc_memory_dump_provider_test.cc",
     "heap_compact_test.cc",
     "heap_test.cc",
diff --git a/third_party/blink/renderer/platform/heap/address_cache.cc b/third_party/blink/renderer/platform/heap/address_cache.cc
new file mode 100644
index 0000000..06c24e01
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/address_cache.cc
@@ -0,0 +1,58 @@
+// 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/platform/heap/address_cache.h"
+
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+
+namespace blink {
+
+void AddressCache::Flush() {
+  if (has_entries_) {
+    for (size_t i = 0; i < kNumberOfEntries; ++i)
+      entries_[i] = nullptr;
+    has_entries_ = false;
+  }
+  dirty_ = false;
+}
+
+void AddressCache::FlushIfDirty() {
+  if (dirty_) {
+    Flush();
+    dirty_ = false;
+  }
+}
+
+size_t AddressCache::GetHash(Address address) {
+  size_t value = (reinterpret_cast<size_t>(address) >> kBlinkPageSizeLog2);
+  value ^= value >> kNumberOfEntriesLog2;
+  value ^= value >> (kNumberOfEntriesLog2 * 2);
+  value &= kNumberOfEntries - 1;
+  return value & ~1;  // Returns only even number.
+}
+
+bool AddressCache::Lookup(Address address) {
+  DCHECK(enabled_);
+  DCHECK(!dirty_);
+
+  size_t index = GetHash(address);
+  DCHECK(!(index & 1));
+  Address cache_page = RoundToBlinkPageStart(address);
+  if (entries_[index] == cache_page)
+    return entries_[index];
+  if (entries_[index + 1] == cache_page)
+    return entries_[index + 1];
+  return false;
+}
+
+void AddressCache::AddEntry(Address address) {
+  has_entries_ = true;
+  size_t index = GetHash(address);
+  DCHECK(!(index & 1));
+  Address cache_page = RoundToBlinkPageStart(address);
+  entries_[index + 1] = entries_[index];
+  entries_[index] = cache_page;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/address_cache.h b/third_party/blink/renderer/platform/heap/address_cache.h
new file mode 100644
index 0000000..85c676c
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/address_cache.h
@@ -0,0 +1,55 @@
+// 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_PLATFORM_HEAP_ADDRESS_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_ADDRESS_CACHE_H_
+
+#include "third_party/blink/renderer/platform/heap/blink_gc.h"
+
+namespace blink {
+
+// Negative cache for addresses outside of Blink's garbage collected heap.
+// - Internally maps to pages (NormalPage) to cover a larger range of addresses.
+// - Requires flushing when adding new addresses.
+class PLATFORM_EXPORT AddressCache {
+  USING_FAST_MALLOC(AddressCache);
+
+ public:
+  AddressCache() : enabled_(false), has_entries_(false), dirty_(false) {
+    // Start by flushing the cache in a non-empty state to initialize all the
+    // cache entries.
+    for (size_t i = 0; i < kNumberOfEntries; ++i)
+      entries_[i] = nullptr;
+  }
+
+  void EnableLookup() { enabled_ = true; }
+  void DisableLookup() { enabled_ = false; }
+
+  void MarkDirty() { dirty_ = true; }
+  void Flush();
+  void FlushIfDirty();
+  bool IsEmpty() { return !has_entries_; }
+
+  // Perform a lookup in the cache. Returns true if the address is guaranteed
+  // to not in Blink's heap and false otherwise.
+  bool Lookup(Address);
+
+  // Add an entry to the cache.
+  void AddEntry(Address);
+
+ private:
+  static constexpr size_t kNumberOfEntriesLog2 = 12;
+  static constexpr size_t kNumberOfEntries = 1 << kNumberOfEntriesLog2;
+
+  static size_t GetHash(Address);
+
+  Address entries_[kNumberOfEntries];
+  bool enabled_ : 1;
+  bool has_entries_ : 1;
+  bool dirty_ : 1;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_ADDRESS_CACHE_H_
diff --git a/third_party/blink/renderer/platform/heap/address_cache_test.cc b/third_party/blink/renderer/platform/heap/address_cache_test.cc
new file mode 100644
index 0000000..001b32ce
--- /dev/null
+++ b/third_party/blink/renderer/platform/heap/address_cache_test.cc
@@ -0,0 +1,74 @@
+// 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/platform/heap/address_cache.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/heap/heap_page.h"
+
+namespace blink {
+
+namespace {
+
+const Address kObjectAddress = reinterpret_cast<Address>(kBlinkPageSize);
+
+}  // namespace
+
+TEST(AddressCacheTest, InitialIsEmpty) {
+  AddressCache cache;
+  cache.EnableLookup();
+  EXPECT_TRUE(cache.IsEmpty());
+}
+
+TEST(AddressCacheTest, LookupOnEmpty) {
+  AddressCache cache;
+  cache.EnableLookup();
+  EXPECT_FALSE(cache.Lookup(kObjectAddress));
+}
+
+TEST(AddressCacheTest, LookupAfterAddEntry) {
+  AddressCache cache;
+  cache.EnableLookup();
+  cache.AddEntry(kObjectAddress);
+  EXPECT_TRUE(cache.Lookup(kObjectAddress));
+}
+
+TEST(AddressCacheTest, AddEntryAddsWholePage) {
+  AddressCache cache;
+  cache.EnableLookup();
+  cache.AddEntry(kObjectAddress);
+  for (Address current = kObjectAddress;
+       current < (kObjectAddress + kBlinkPageSize); current++) {
+    EXPECT_TRUE(cache.Lookup(current));
+  }
+}
+
+TEST(AddressCacheTest, AddEntryOnlyAddsPageForGivenAddress) {
+  AddressCache cache;
+  cache.EnableLookup();
+  cache.AddEntry(kObjectAddress);
+  EXPECT_FALSE(cache.Lookup(kObjectAddress - 1));
+  EXPECT_FALSE(cache.Lookup(kObjectAddress + kBlinkPageSize + 1));
+}
+
+TEST(AddressCacheTest, FlushIfDirtyIgnoresNonDirty) {
+  AddressCache cache;
+  cache.EnableLookup();
+  cache.AddEntry(kObjectAddress);
+  cache.FlushIfDirty();
+  // Cannot do lookup in dirty cache.
+  EXPECT_FALSE(cache.IsEmpty());
+}
+
+TEST(AddressCacheTest, FlushIfDirtyHandlesDirty) {
+  AddressCache cache;
+  cache.EnableLookup();
+  cache.AddEntry(kObjectAddress);
+  cache.MarkDirty();
+  cache.FlushIfDirty();
+  // Cannot do lookup in dirty cache.
+  EXPECT_TRUE(cache.IsEmpty());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index f4509a7e..77c074f 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -37,6 +37,7 @@
 #include "base/trace_event/process_memory_dump.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/heap/address_cache.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
 #include "third_party/blink/renderer/platform/heap/heap_compact.h"
 #include "third_party/blink/renderer/platform/heap/marking_visitor.h"
@@ -58,10 +59,6 @@
 HeapAllocHooks::AllocationHook* HeapAllocHooks::allocation_hook_ = nullptr;
 HeapAllocHooks::FreeHook* HeapAllocHooks::free_hook_ = nullptr;
 
-void ThreadHeap::FlushHeapDoesNotContainCache() {
-  heap_does_not_contain_cache_->Flush();
-}
-
 ThreadHeapStats::ThreadHeapStats()
     : allocated_space_(0),
       allocated_object_size_(0),
@@ -132,14 +129,13 @@
 ThreadHeap::ThreadHeap(ThreadState* thread_state)
     : thread_state_(thread_state),
       region_tree_(std::make_unique<RegionTree>()),
-      heap_does_not_contain_cache_(std::make_unique<HeapDoesNotContainCache>()),
+      address_cache_(std::make_unique<AddressCache>()),
       free_page_pool_(std::make_unique<PagePool>()),
       marking_worklist_(nullptr),
       not_fully_constructed_worklist_(nullptr),
       weak_callback_worklist_(nullptr),
       vector_backing_arena_index_(BlinkGC::kVector1ArenaIndex),
-      current_arena_ages_(0),
-      should_flush_heap_does_not_contain_cache_(false) {
+      current_arena_ages_(0) {
   if (ThreadState::Current()->IsMainThread())
     main_thread_heap_ = this;
 
@@ -164,7 +160,7 @@
   DCHECK(thread_state_->InAtomicMarkingPause());
 
 #if !DCHECK_IS_ON()
-  if (heap_does_not_contain_cache_->Lookup(address))
+  if (address_cache_->Lookup(address))
     return nullptr;
 #endif
 
@@ -172,17 +168,17 @@
 #if DCHECK_IS_ON()
     DCHECK(page->Contains(address));
 #endif
-    DCHECK(!heap_does_not_contain_cache_->Lookup(address));
+    DCHECK(!address_cache_->Lookup(address));
     DCHECK(&visitor->Heap() == &page->Arena()->GetThreadState()->Heap());
     visitor->ConservativelyMarkAddress(page, address);
     return address;
   }
 
 #if !DCHECK_IS_ON()
-  heap_does_not_contain_cache_->AddEntry(address);
+  address_cache_->AddEntry(address);
 #else
-  if (!heap_does_not_contain_cache_->Lookup(address))
-    heap_does_not_contain_cache_->AddEntry(address);
+  if (!address_cache_->Lookup(address))
+    address_cache_->AddEntry(address);
 #endif
   return nullptr;
 }
@@ -199,13 +195,13 @@
 
   if (BasePage* page = LookupPageForAddress(address)) {
     DCHECK(page->Contains(address));
-    DCHECK(!heap_does_not_contain_cache_->Lookup(address));
+    DCHECK(!address_cache_->Lookup(address));
     DCHECK(&visitor->Heap() == &page->Arena()->GetThreadState()->Heap());
     visitor->ConservativelyMarkAddress(page, address, callback);
     return address;
   }
-  if (!heap_does_not_contain_cache_->Lookup(address))
-    heap_does_not_contain_cache_->AddEntry(address);
+  if (!address_cache_->Lookup(address))
+    address_cache_->AddEntry(address);
   return nullptr;
 }
 #endif  // DCHECK_IS_ON()
@@ -449,26 +445,6 @@
   return object_payload_size;
 }
 
-void ThreadHeap::ShouldFlushHeapDoesNotContainCache() {
-  should_flush_heap_does_not_contain_cache_ = true;
-}
-
-void ThreadHeap::FlushHeapDoesNotContainCacheIfNeeded() {
-  if (should_flush_heap_does_not_contain_cache_) {
-    FlushHeapDoesNotContainCache();
-    should_flush_heap_does_not_contain_cache_ = false;
-  }
-}
-
-bool ThreadHeap::IsAddressInHeapDoesNotContainCache(Address address) {
-  // If the cache has been marked as invalidated, it's cleared prior
-  // to performing the next GC. Hence, consider the cache as being
-  // effectively empty.
-  if (should_flush_heap_does_not_contain_cache_)
-    return false;
-  return heap_does_not_contain_cache_->Lookup(address);
-}
-
 void ThreadHeap::VisitPersistentRoots(Visitor* visitor) {
   DCHECK(thread_state_->InAtomicMarkingPause());
   TRACE_EVENT0("blink_gc", "ThreadHeap::visitPersistentRoots");
@@ -478,7 +454,10 @@
 void ThreadHeap::VisitStackRoots(MarkingVisitor* visitor) {
   DCHECK(thread_state_->InAtomicMarkingPause());
   TRACE_EVENT0("blink_gc", "ThreadHeap::visitStackRoots");
+  address_cache_->FlushIfDirty();
+  address_cache_->EnableLookup();
   thread_state_->VisitStack(visitor);
+  address_cache_->DisableLookup();
 }
 
 BasePage* ThreadHeap::LookupPageForAddress(Address address) {
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index f18a6f95..bb25750b 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -55,6 +55,7 @@
 class IncrementalMarkingScopeBase;
 }  // namespace incremental_marking_test
 
+class AddressCache;
 class PagePool;
 class RegionTree;
 
@@ -358,10 +359,7 @@
 
   size_t ObjectPayloadSizeForTesting();
 
-  void FlushHeapDoesNotContainCache();
-  bool IsAddressInHeapDoesNotContainCache(Address);
-  void FlushHeapDoesNotContainCacheIfNeeded();
-  void ShouldFlushHeapDoesNotContainCache();
+  AddressCache* address_cache() { return address_cache_.get(); }
 
   PagePool* GetFreePagePool() { return free_page_pool_.get(); }
 
@@ -499,7 +497,7 @@
   ThreadState* thread_state_;
   ThreadHeapStats stats_;
   std::unique_ptr<RegionTree> region_tree_;
-  std::unique_ptr<HeapDoesNotContainCache> heap_does_not_contain_cache_;
+  std::unique_ptr<AddressCache> address_cache_;
   std::unique_ptr<PagePool> free_page_pool_;
   std::unique_ptr<MarkingWorklist> marking_worklist_;
   std::unique_ptr<NotFullyConstructedWorklist> not_fully_constructed_worklist_;
@@ -515,7 +513,6 @@
   int vector_backing_arena_index_;
   size_t arena_ages_[BlinkGC::kNumberOfArenas];
   size_t current_arena_ages_;
-  bool should_flush_heap_does_not_contain_cache_;
 
   // Ideally we want to allocate an array of size |gcInfoTableMax| but it will
   // waste memory. Thus we limit the array size to 2^8 and share one entry
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index b73e828..181745da 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -33,6 +33,7 @@
 #include "base/trace_event/process_memory_dump.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
+#include "third_party/blink/renderer/platform/heap/address_cache.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.h"
 #include "third_party/blink/renderer/platform/heap/heap_compact.h"
 #include "third_party/blink/renderer/platform/heap/marking_verifier.h"
@@ -659,7 +660,7 @@
 }
 
 void NormalPageArena::AllocatePage() {
-  GetThreadState()->Heap().ShouldFlushHeapDoesNotContainCache();
+  GetThreadState()->Heap().address_cache()->MarkDirty();
   PageMemory* page_memory =
       GetThreadState()->Heap().GetFreePagePool()->Take(ArenaIndex());
 
@@ -1014,7 +1015,7 @@
   large_object_size += kAllocationGranularity;
 #endif
 
-  GetThreadState()->Heap().ShouldFlushHeapDoesNotContainCache();
+  GetThreadState()->Heap().address_cache()->MarkDirty();
   PageMemory* page_memory = PageMemory::Allocate(
       large_object_size, GetThreadState()->Heap().GetRegionTree());
   Address large_object_address = page_memory->WritableStart();
@@ -1775,44 +1776,4 @@
 }
 #endif
 
-void HeapDoesNotContainCache::Flush() {
-  if (has_entries_) {
-    for (size_t i = 0; i < kNumberOfEntries; ++i)
-      entries_[i] = nullptr;
-    has_entries_ = false;
-  }
-}
-
-size_t HeapDoesNotContainCache::GetHash(Address address) {
-  size_t value = (reinterpret_cast<size_t>(address) >> kBlinkPageSizeLog2);
-  value ^= value >> kNumberOfEntriesLog2;
-  value ^= value >> (kNumberOfEntriesLog2 * 2);
-  value &= kNumberOfEntries - 1;
-  return value & ~1;  // Returns only even number.
-}
-
-bool HeapDoesNotContainCache::Lookup(Address address) {
-  DCHECK(ThreadState::Current()->InAtomicMarkingPause());
-
-  size_t index = GetHash(address);
-  DCHECK(!(index & 1));
-  Address cache_page = RoundToBlinkPageStart(address);
-  if (entries_[index] == cache_page)
-    return entries_[index];
-  if (entries_[index + 1] == cache_page)
-    return entries_[index + 1];
-  return false;
-}
-
-void HeapDoesNotContainCache::AddEntry(Address address) {
-  DCHECK(ThreadState::Current()->InAtomicMarkingPause());
-
-  has_entries_ = true;
-  size_t index = GetHash(address);
-  DCHECK(!(index & 1));
-  Address cache_page = RoundToBlinkPageStart(address);
-  entries_[index + 1] = entries_[index];
-  entries_[index] = cache_page;
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_page.h b/third_party/blink/renderer/platform/heap/heap_page.h
index 1aa7cf55..89943847 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.h
+++ b/third_party/blink/renderer/platform/heap/heap_page.h
@@ -646,54 +646,6 @@
 #endif
 };
 
-// |HeapDoesNotContainCache| provides a fast way to determine whether an
-// aribtrary pointer-sized word can be interpreted as a pointer to an area that
-// is managed by the garbage collected Blink heap. This is a cache of 'pages'
-// that have previously been determined to be wholly outside of the heap. The
-// size of these pages must be smaller than the allocation alignment of the heap
-// pages. We determine off-heap-ness by rounding down the pointer to the nearest
-// page and looking up the page in the cache. If there is a miss in the cache we
-// can determine the status of the pointer precisely using the heap
-// |RegionTree|.
-//
-// This is a negative cache, so it must be flushed when memory is added to the
-// heap.
-class HeapDoesNotContainCache {
-  USING_FAST_MALLOC(HeapDoesNotContainCache);
-
- public:
-  HeapDoesNotContainCache() : has_entries_(false) {
-    // Start by flushing the cache in a non-empty state to initialize all the
-    // cache entries.
-    for (size_t i = 0; i < kNumberOfEntries; ++i)
-      entries_[i] = nullptr;
-  }
-
-  void Flush();
-  bool IsEmpty() { return !has_entries_; }
-
-  // Perform a lookup in the cache.
-  //
-  // If lookup returns false, the argument address was not found in the cache
-  // and it is unknown if the address is in the Blink heap.
-  //
-  // If lookup returns true, the argument address was found in the cache which
-  // means the address is not in the heap.
-  PLATFORM_EXPORT bool Lookup(Address);
-
-  // Add an entry to the cache.
-  PLATFORM_EXPORT void AddEntry(Address);
-
- private:
-  static constexpr size_t kNumberOfEntriesLog2 = 12;
-  static constexpr size_t kNumberOfEntries = 1 << kNumberOfEntriesLog2;
-
-  static size_t GetHash(Address);
-
-  Address entries_[kNumberOfEntries];
-  bool has_entries_;
-};
-
 class FreeList {
   DISALLOW_NEW();
 
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index a42cd6c4..bc33272d 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -38,6 +38,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/heap/address_cache.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_linked_stack.h"
@@ -4010,7 +4011,8 @@
     TestGCScope scope(BlinkGC::kHeapPointersOnStack);
     MarkingVisitor visitor(ThreadState::Current(),
                            MarkingVisitor::kGlobalMarking);
-    heap.FlushHeapDoesNotContainCache();
+    heap.address_cache()->EnableLookup();
+    heap.address_cache()->Flush();
     for (size_t i = 0; i < object_addresses.size(); i++) {
       EXPECT_TRUE(heap.CheckAndMarkPointer(&visitor, object_addresses[i],
                                            ReportMarkedPointer));
@@ -4034,7 +4036,8 @@
     TestGCScope scope(BlinkGC::kHeapPointersOnStack);
     MarkingVisitor visitor(ThreadState::Current(),
                            MarkingVisitor::kGlobalMarking);
-    heap.FlushHeapDoesNotContainCache();
+    heap.address_cache()->EnableLookup();
+    heap.address_cache()->Flush();
     for (size_t i = 0; i < object_addresses.size(); i++) {
       // We would like to assert that checkAndMarkPointer returned false
       // here because the pointers no longer point into a valid object
diff --git a/third_party/blink/renderer/platform/heap/page_memory.h b/third_party/blink/renderer/platform/heap/page_memory.h
index 7f4ca6ef..1fbed84 100644
--- a/third_party/blink/renderer/platform/heap/page_memory.h
+++ b/third_party/blink/renderer/platform/heap/page_memory.h
@@ -169,15 +169,6 @@
 
   WARN_UNUSED_RESULT bool Commit() {
     reserved_->MarkPageUsed(WritableStart());
-    // Check that in-use page isn't also marked as being a non-heap page
-    // by the current heap's negative cache. That cache is invalidated
-    // when allocating new pages, but crbug.com/649485 suggests that
-    // we do get out of sync somehow.
-    //
-    // TODO(sof): consider removing check once bug has been diagnosed
-    // and addressed.
-    CHECK(!ThreadState::Current()->Heap().IsAddressInHeapDoesNotContainCache(
-        WritableStart()));
     return writable_.Commit();
   }
 
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index db409f1..31d89242 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -1423,7 +1423,6 @@
 
   DCHECK(InAtomicMarkingPause());
   Heap().MakeConsistentForGC();
-  Heap().FlushHeapDoesNotContainCacheIfNeeded();
   Heap().ClearArenaAges();
 
   if (marking_type != BlinkGC::kTakeSnapshot)
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index 7d2372f9..704ab965 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -86,16 +86,35 @@
     "common/throttling/throttled_time_domain.h",
     "common/throttling/wake_up_budget_pool.cc",
     "common/throttling/wake_up_budget_pool.h",
+    "main_thread/auto_advancing_virtual_time_domain.cc",
+    "main_thread/auto_advancing_virtual_time_domain.h",
+    "main_thread/deadline_task_runner.cc",
+    "main_thread/deadline_task_runner.h",
     "main_thread/frame_origin_type.cc",
     "main_thread/frame_origin_type.h",
     "main_thread/frame_scheduler_impl.cc",
     "main_thread/frame_scheduler_impl.h",
+    "main_thread/idle_time_estimator.cc",
+    "main_thread/idle_time_estimator.h",
     "main_thread/main_thread_scheduler_helper.cc",
     "main_thread/main_thread_scheduler_helper.h",
     "main_thread/main_thread_scheduler_impl.cc",
     "main_thread/main_thread_scheduler_impl.h",
+    "main_thread/main_thread_task_queue.cc",
+    "main_thread/main_thread_task_queue.h",
     "main_thread/page_scheduler_impl.cc",
     "main_thread/page_scheduler_impl.h",
+    "main_thread/queueing_time_estimator.cc",
+    "main_thread/queueing_time_estimator.h",
+    "main_thread/render_widget_signals.cc",
+    "main_thread/render_widget_signals.h",
+    "main_thread/renderer_metrics_helper.cc",
+    "main_thread/renderer_metrics_helper.h",
+    "main_thread/task_cost_estimator.cc",
+    "main_thread/task_cost_estimator.h",
+    "main_thread/use_case.h",
+    "main_thread/user_model.cc",
+    "main_thread/user_model.h",
     "main_thread/web_main_thread_scheduler.cc",
     "main_thread/web_render_widget_scheduling_state.cc",
     "public/frame_or_worker_scheduler.h",
@@ -104,28 +123,10 @@
     "public/page_scheduler.h",
     "public/thread_scheduler.h",
     "public/web_main_thread_scheduler.h",
-    "renderer/auto_advancing_virtual_time_domain.cc",
-    "renderer/auto_advancing_virtual_time_domain.h",
-    "renderer/deadline_task_runner.cc",
-    "renderer/deadline_task_runner.h",
     "renderer/frame_status.cc",
     "renderer/frame_status.h",
-    "renderer/idle_time_estimator.cc",
-    "renderer/idle_time_estimator.h",
-    "renderer/main_thread_task_queue.cc",
-    "renderer/main_thread_task_queue.h",
-    "renderer/queueing_time_estimator.cc",
-    "renderer/queueing_time_estimator.h",
-    "renderer/render_widget_signals.cc",
-    "renderer/render_widget_signals.h",
-    "renderer/renderer_metrics_helper.cc",
-    "renderer/renderer_metrics_helper.h",
     "renderer/renderer_web_scheduler_impl.cc",
     "renderer/renderer_web_scheduler_impl.h",
-    "renderer/task_cost_estimator.cc",
-    "renderer/task_cost_estimator.h",
-    "renderer/user_model.cc",
-    "renderer/user_model.h",
     "renderer/web_scoped_virtual_time_pauser.cc",
     "renderer/webthread_impl_for_renderer_scheduler.cc",
     "renderer/webthread_impl_for_renderer_scheduler.h",
@@ -208,17 +209,17 @@
     "common/scheduler_helper_unittest.cc",
     "common/throttling/budget_pool_unittest.cc",
     "common/throttling/task_queue_throttler_unittest.cc",
+    "main_thread/auto_advancing_virtual_time_domain_unittest.cc",
+    "main_thread/deadline_task_runner_unittest.cc",
     "main_thread/frame_scheduler_impl_unittest.cc",
+    "main_thread/idle_time_estimator_unittest.cc",
     "main_thread/main_thread_scheduler_impl_unittest.cc",
     "main_thread/page_scheduler_impl_unittest.cc",
-    "renderer/auto_advancing_virtual_time_domain_unittest.cc",
-    "renderer/deadline_task_runner_unittest.cc",
-    "renderer/idle_time_estimator_unittest.cc",
-    "renderer/queueing_time_estimator_unittest.cc",
-    "renderer/render_widget_signals_unittest.cc",
-    "renderer/renderer_metrics_helper_unittest.cc",
-    "renderer/task_cost_estimator_unittest.cc",
-    "renderer/user_model_unittest.cc",
+    "main_thread/queueing_time_estimator_unittest.cc",
+    "main_thread/render_widget_signals_unittest.cc",
+    "main_thread/renderer_metrics_helper_unittest.cc",
+    "main_thread/task_cost_estimator_unittest.cc",
+    "main_thread/user_model_unittest.cc",
     "renderer/webthread_impl_for_renderer_scheduler_unittest.cc",
     "util/task_duration_metric_reporter_unittest.cc",
     "util/thread_load_tracker_unittest.cc",
diff --git a/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h b/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h
index e7025d3..a89da1c5 100644
--- a/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h
@@ -28,8 +28,6 @@
   // base::SingleThreadTaskRunner implementation:
   bool RunsTasksInCurrentSequence() const override;
 
-  TaskQueue* GetTaskQueue() const { return task_queue_.get(); }
-
  protected:
   bool PostDelayedTask(const base::Location&,
                        base::OnceClosure,
diff --git a/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc
index eb441b5..869ca0a 100644
--- a/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.cc
@@ -34,7 +34,7 @@
   return thread_scheduler_->ShouldYieldForHighPriorityWork();
 }
 
-bool WebSchedulerImpl::CanExceedIdleDeadlineIfRequired() {
+bool WebSchedulerImpl::CanExceedIdleDeadlineIfRequired() const {
   return thread_scheduler_->CanExceedIdleDeadlineIfRequired();
 }
 
diff --git a/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h
index 8cc6b4bf..fe9f863 100644
--- a/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h
@@ -32,7 +32,7 @@
   // ThreadScheduler implementation:
   void Shutdown() override;
   bool ShouldYieldForHighPriorityWork() override;
-  bool CanExceedIdleDeadlineIfRequired() override;
+  bool CanExceedIdleDeadlineIfRequired() const override;
   void PostIdleTask(const base::Location& location,
                     WebThread::IdleTask task) override;
   void PostNonNestableIdleTask(const base::Location& location,
diff --git a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
index 42bd5126..dbc9fef 100644
--- a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.cc
@@ -11,7 +11,6 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/time/default_tick_clock.h"
 #include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
-#include "third_party/blink/renderer/platform/scheduler/child/web_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
 #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
 
@@ -65,10 +64,6 @@
   non_main_thread_scheduler_->Init();
   task_queue_ = non_main_thread_scheduler_->DefaultTaskQueue();
   idle_task_runner_ = non_main_thread_scheduler_->IdleTaskRunner();
-  web_scheduler_.reset(
-      new WebSchedulerImpl(non_main_thread_scheduler_.get(),
-                           non_main_thread_scheduler_->IdleTaskRunner(),
-                           non_main_thread_scheduler_->DefaultTaskQueue()));
   base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
   completion->Signal();
 }
@@ -79,7 +74,6 @@
 
   task_queue_ = nullptr;
   idle_task_runner_ = nullptr;
-  web_scheduler_ = nullptr;
   non_main_thread_scheduler_ = nullptr;
 
   if (completion)
@@ -101,7 +95,7 @@
 }
 
 blink::ThreadScheduler* WebThreadImplForWorkerScheduler::Scheduler() const {
-  return web_scheduler_.get();
+  return non_main_thread_scheduler_.get();
 }
 
 SingleThreadIdleTaskRunner* WebThreadImplForWorkerScheduler::GetIdleTaskRunner()
diff --git a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
index a38fce4..89cbc3d 100644
--- a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler.h
@@ -25,7 +25,6 @@
 namespace scheduler {
 class SingleThreadIdleTaskRunner;
 class TaskQueue;
-class WebSchedulerImpl;
 class NonMainThreadScheduler;
 class WorkerSchedulerProxy;
 
@@ -76,7 +75,6 @@
   const WebThreadType thread_type_;
   std::unique_ptr<scheduler::WorkerSchedulerProxy> worker_scheduler_proxy_;
   std::unique_ptr<scheduler::NonMainThreadScheduler> non_main_thread_scheduler_;
-  std::unique_ptr<scheduler::WebSchedulerImpl> web_scheduler_;
   scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner_;
   scoped_refptr<TaskQueue> task_queue_;
   scoped_refptr<scheduler::SingleThreadIdleTaskRunner> idle_task_runner_;
diff --git a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
index c7c4470..3ec646d6 100644
--- a/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/child/webthread_impl_for_worker_scheduler_unittest.cc
@@ -62,9 +62,7 @@
 }
 
 void ShutdownOnThread(WebThreadImplForWorkerScheduler* thread) {
-  WebSchedulerImpl* web_scheduler_impl =
-      static_cast<WebSchedulerImpl*>(thread->Scheduler());
-  web_scheduler_impl->Shutdown();
+  thread->Scheduler()->Shutdown();
 }
 
 class WebThreadImplForWorkerSchedulerTest : public testing::Test {
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
index 53ce2e1..6bfce5aed 100644
--- a/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler_unittest.cc
@@ -20,9 +20,9 @@
 #include "third_party/blink/renderer/platform/scheduler/base/task_queue_manager.h"
 #include "third_party/blink/renderer/platform/scheduler/base/test/task_queue_manager_for_test.h"
 #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
 
 using testing::ElementsAre;
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/DEPS b/third_party/blink/renderer/platform/scheduler/main_thread/DEPS
index 0308a86..92b89d6e 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/DEPS
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/DEPS
@@ -1,6 +1,8 @@
 include_rules = [
   "+base/metrics/single_sample_metrics.h",
+  "+cc",
   "+components/viz/common",
+  "+services/resource_coordinator/public/cpp/resource_coordinator_features.h",
 ]
 
 specific_include_rules = {
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.cc
similarity index 97%
rename from third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.cc
index 92390ab..1322636 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
 
 #include "base/time/time_override.h"
 #include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h
similarity index 93%
rename from third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h
index e32dea0..c31692f 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
 
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
@@ -117,4 +117,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_AUTO_ADVANCING_VIRTUAL_TIME_DOMAIN_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc
similarity index 98%
rename from third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc
index 0d78251..9aba100d8 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
 
 #include <memory>
 #include "base/test/simple_test_tick_clock.h"
@@ -71,7 +71,7 @@
   MOCK_METHOD0(OnVirtualTimeAdvanced, void());
 };
 
-}  // namesapce
+}  // namespace
 
 TEST_F(AutoAdvancingVirtualTimeDomainTest, VirtualTimeAdvances) {
   MockObserver mock_observer;
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.cc b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.cc
similarity index 92%
rename from third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.cc
index e945595a..a9d758e2 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h"
 
 #include "base/bind.h"
 
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h
similarity index 85%
rename from third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h
index c37ec85dc..2cf9c70a 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_DEADLINE_TASK_RUNNER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_DEADLINE_TASK_RUNNER_H_
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -48,4 +48,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_DEADLINE_TASK_RUNNER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_DEADLINE_TASK_RUNNER_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner_unittest.cc
similarity index 97%
rename from third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner_unittest.cc
index 7cf087a..f56d0e87 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h"
 
 #include <memory>
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 6e616f4..daea9dfbf 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -17,9 +17,9 @@
 #include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
 #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.cc b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.cc
similarity index 96%
rename from third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.cc
index 4ee87d3..c58b6b6c 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h"
 
 #include "base/time/default_tick_clock.h"
 
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h
similarity index 87%
rename from third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h
index 43cdbcb8..f4b3942 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_IDLE_TIME_ESTIMATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_IDLE_TIME_ESTIMATOR_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_IDLE_TIME_ESTIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_IDLE_TIME_ESTIMATOR_H_
 
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
@@ -57,4 +57,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_IDLE_TIME_ESTIMATOR_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_IDLE_TIME_ESTIMATOR_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc
similarity index 98%
rename from third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc
index f02498c..55a617e 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h"
 
 #include <memory>
 #include "base/memory/scoped_refptr.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
index 5d23977..dcd5de0 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h
index f82acb3..8f76304 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h
@@ -7,7 +7,7 @@
 
 #include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 46b3900..4162f4a 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -34,8 +34,8 @@
 #include "third_party/blink/renderer/platform/scheduler/child/features.h"
 #include "third_party/blink/renderer/platform/scheduler/child/process_state.h"
 #include "third_party/blink/renderer/platform/scheduler/common/throttling/task_queue_throttler.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/renderer/webthread_impl_for_renderer_scheduler.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index f221d9d0..271353e 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -23,18 +23,18 @@
 #include "third_party/blink/renderer/platform/scheduler/child/idle_canceled_delayed_task_sweeper.h"
 #include "third_party/blink/renderer/platform/scheduler/child/idle_helper.h"
 #include "third_party/blink/renderer/platform/scheduler/child/pollable_thread_safe_flag.h"
-#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/deadline_task_runner.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/idle_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/deadline_task_runner.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/idle_time_estimator.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/use_case.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/user_model.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/use_case.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
 #include "third_party/blink/renderer/platform/scheduler/util/tracing_helper.h"
 
 namespace base {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index 0fa0f8c..9b7fefc 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -25,8 +25,8 @@
 #include "third_party/blink/renderer/platform/scheduler/base/test/task_queue_manager_for_test.h"
 #include "third_party/blink/renderer/platform/scheduler/child/features.h"
 #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
similarity index 98%
rename from third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
index a5a3f8a..34c19f7 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 
 #include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
similarity index 95%
rename from third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index 507284e0..6662138c1 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_MAIN_THREAD_TASK_QUEUE_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_MAIN_THREAD_TASK_QUEUE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_
 
 #include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
@@ -221,4 +221,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_MAIN_THREAD_TASK_QUEUE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_MAIN_THREAD_TASK_QUEUE_H_
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 0493fbe..16c5bf0 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -12,10 +12,10 @@
 #include "third_party/blink/renderer/platform/scheduler/base/virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/child/default_params.h"
 #include "third_party/blink/renderer/platform/scheduler/common/throttling/budget_pool.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/auto_advancing_virtual_time_domain.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.cc b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.cc
similarity index 98%
rename from third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.cc
index a509852..b31f398 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h"
 
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h
similarity index 91%
rename from third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h
index 7fdde94..976998e6 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_QUEUEING_TIME_ESTIMATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_QUEUEING_TIME_ESTIMATOR_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_QUEUEING_TIME_ESTIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_QUEUEING_TIME_ESTIMATOR_H_
 
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "third_party/blink/public/common/page/launching_process_state.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.h"
 
 #include <array>
 #include <vector>
@@ -141,4 +141,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_QUEUEING_TIME_ESTIMATOR_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_QUEUEING_TIME_ESTIMATOR_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator_unittest.cc
similarity index 99%
rename from third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator_unittest.cc
index f8a7c06..bc489ae9 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/queueing_time_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.cc b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.cc
similarity index 95%
rename from third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.cc
index fc1f003..b1f8cb8a 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h"
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h
similarity index 86%
rename from third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h
index e19b213..453e3b6 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDER_WIDGET_SIGNALS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDER_WIDGET_SIGNALS_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_RENDER_WIDGET_SIGNALS_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_RENDER_WIDGET_SIGNALS_H_
 
 #include <memory>
 
@@ -58,4 +58,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDER_WIDGET_SIGNALS_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_RENDER_WIDGET_SIGNALS_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals_unittest.cc
similarity index 98%
rename from third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals_unittest.cc
index ff80c051..478e09f5 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h"
 
 #include "base/macros.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.cc b/third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.cc
similarity index 99%
rename from third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.cc
index eda829c..03b129c 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.h"
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h b/third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.h
similarity index 92%
rename from third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.h
index f1f1167..72b66f8 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_RENDERER_METRICS_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_RENDERER_METRICS_HELPER_H_
 
 #include "base/macros.h"
 #include "base/optional.h"
@@ -12,9 +12,9 @@
 #include "third_party/blink/public/platform/web_thread_type.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/child/metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/use_case.h"
 #include "third_party/blink/renderer/platform/scheduler/renderer/frame_status.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/use_case.h"
 #include "third_party/blink/renderer/platform/scheduler/util/task_duration_metric_reporter.h"
 #include "third_party/blink/renderer/platform/scheduler/util/thread_load_tracker.h"
 
@@ -141,4 +141,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_RENDERER_METRICS_HELPER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_RENDERER_METRICS_HELPER_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper_unittest.cc
similarity index 99%
rename from third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper_unittest.cc
index a4ee590e..f8655c9 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/renderer_metrics_helper.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/renderer_metrics_helper.h"
 
 #include <memory>
 #include "base/macros.h"
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.cc
similarity index 93%
rename from third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.cc
index f9ce0c4..0686a1eb 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h"
 
 #include "base/time/default_tick_clock.h"
 
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h
similarity index 83%
rename from third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h
index 2214c772..c3e54a3 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_TASK_COST_ESTIMATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_TASK_COST_ESTIMATOR_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_TASK_COST_ESTIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_TASK_COST_ESTIMATOR_H_
 
 #include "base/macros.h"
 #include "base/message_loop/message_loop.h"
@@ -49,4 +49,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_TASK_COST_ESTIMATOR_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_TASK_COST_ESTIMATOR_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator_unittest.cc
similarity index 95%
rename from third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator_unittest.cc
index 45ff499..6d7b3bf 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/task_cost_estimator.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/task_cost_estimator.h"
 
 #include <memory>
 
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/use_case.h b/third_party/blink/renderer/platform/scheduler/main_thread/use_case.h
similarity index 85%
rename from third_party/blink/renderer/platform/scheduler/renderer/use_case.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/use_case.h
index ce4ad48..7ab1c4d 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/use_case.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/use_case.h
@@ -1,5 +1,5 @@
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USE_CASE_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USE_CASE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_USE_CASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_USE_CASE_H_
 
 namespace blink {
 namespace scheduler {
@@ -39,4 +39,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_USE_CASE_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/user_model.cc b/third_party/blink/renderer/platform/scheduler/main_thread/user_model.cc
similarity index 98%
rename from third_party/blink/renderer/platform/scheduler/renderer/user_model.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/user_model.cc
index aeb7ba5..361b57e 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/user_model.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/user_model.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/user_model.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/user_model.h b/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h
similarity index 92%
rename from third_party/blink/renderer/platform/scheduler/renderer/user_model.h
rename to third_party/blink/renderer/platform/scheduler/main_thread/user_model.h
index 247acf43..afbab65cd 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/user_model.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/user_model.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USER_MODEL_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USER_MODEL_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_USER_MODEL_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_USER_MODEL_H_
 
 #include "base/macros.h"
 #include "base/trace_event/trace_event.h"
@@ -83,4 +83,4 @@
 }  // namespace scheduler
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_RENDERER_USER_MODEL_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_USER_MODEL_H_
diff --git a/third_party/blink/renderer/platform/scheduler/renderer/user_model_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/user_model_unittest.cc
similarity index 98%
rename from third_party/blink/renderer/platform/scheduler/renderer/user_model_unittest.cc
rename to third_party/blink/renderer/platform/scheduler/main_thread/user_model_unittest.cc
index 6d75270..b0ae0e3 100644
--- a/third_party/blink/renderer/platform/scheduler/renderer/user_model_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/user_model_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/user_model.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/user_model.h"
 
 #include "base/test/simple_test_tick_clock.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc b/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc
index 2cfea4a..1b645c39 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/web_render_widget_scheduling_state.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
 
-#include "third_party/blink/renderer/platform/scheduler/renderer/render_widget_signals.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h
index 038aea8b..cf5205a 100644
--- a/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/non_main_thread_scheduler.h
@@ -14,13 +14,19 @@
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/base/task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/child/worker_task_queue.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler_helper.h"
 
 namespace blink {
 namespace scheduler {
+class TaskRunnerImpl;
 class WorkerSchedulerProxy;
 
-class PLATFORM_EXPORT NonMainThreadScheduler : public WebThreadScheduler {
+// TODO(yutak): Remove the dependency to WebThreadScheduler. We want to
+// separate interfaces to Chromium (in blink/public/platform/scheduler) from
+// interfaces to Blink (in blink/renderer/platform/scheduler/public).
+class PLATFORM_EXPORT NonMainThreadScheduler : public WebThreadScheduler,
+                                               public ThreadScheduler {
  public:
   ~NonMainThreadScheduler() override;
 
@@ -34,7 +40,7 @@
 
   // Must be called before the scheduler can be used. Does any post construction
   // initialization needed such as initializing idle period detection.
-  virtual void Init() = 0;
+  void Init();
 
   virtual void OnTaskCompleted(WorkerTaskQueue* worker_task_queue,
                                const TaskQueue::Task& task,
@@ -42,14 +48,54 @@
                                base::TimeTicks end,
                                base::Optional<base::TimeDelta> thread_time) = 0;
 
+  // ThreadScheduler implementation.
+  // TODO(yutak): Some functions are only meaningful in main thread. Move them
+  // to MainThreadScheduler.
+  void PostIdleTask(const base::Location& location,
+                    WebThread::IdleTask task) override;
+  void PostNonNestableIdleTask(const base::Location& location,
+                               WebThread::IdleTask task) override;
+  base::SingleThreadTaskRunner* V8TaskRunner() override;
+  base::SingleThreadTaskRunner* CompositorTaskRunner() override;
+  std::unique_ptr<PageScheduler> CreatePageScheduler(
+      PageScheduler::Delegate*) override;
+  std::unique_ptr<RendererPauseHandle> PauseScheduler() override
+      WARN_UNUSED_RESULT;
+  void AddPendingNavigation(
+      scheduler::WebMainThreadScheduler::NavigatingFrameType type) override {}
+  void RemovePendingNavigation(
+      scheduler::WebMainThreadScheduler::NavigatingFrameType type) override {}
+
+  // Returns TimeTicks::Now() by default.
+  base::TimeTicks MonotonicallyIncreasingVirtualTime() const override;
+
+  // The following virtual methods are defined in *both* WebThreadScheduler
+  // and ThreadScheduler, with identical interfaces and semantics. They are
+  // overriden in a subclass, effectively implementing the virtual methods
+  // in both classes at the same time. This is allowed in C++, as long as
+  // there is only one final overrider (i.e. definitions in base classes are
+  // not used in instantiated objects, since otherwise they may have multiple
+  // definitions of the virtual function in question).
+  //
+  // virtual void Shutdown();
+  // virtual bool ShouldYieldForHighPriorityWork();
+  // virtual bool CanExceedIdleDeadlineIfRequired() const;
+
   scoped_refptr<WorkerTaskQueue> CreateTaskRunner();
 
  protected:
   explicit NonMainThreadScheduler(
       std::unique_ptr<NonMainThreadSchedulerHelper> helper);
 
+  // Called during Init() for delayed initialization for subclasses.
+  virtual void InitImpl() = 0;
+
   std::unique_ptr<NonMainThreadSchedulerHelper> helper_;
 
+ private:
+  static void RunIdleTask(WebThread::IdleTask task, base::TimeTicks deadline);
+  scoped_refptr<TaskRunnerImpl> v8_task_runner_;
+
   DISALLOW_COPY_AND_ASSIGN(NonMainThreadScheduler);
 };
 
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
index 154103f..964ad9d 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -42,7 +42,7 @@
   // its deadline has expired - post a new idle task for the continuation of
   // the work in this case.
   // Must be called from the associated WebThread.
-  virtual bool CanExceedIdleDeadlineIfRequired() = 0;
+  virtual bool CanExceedIdleDeadlineIfRequired() const = 0;
 
   // Schedule an idle task to run the associated WebThread. For non-critical
   // tasks which may be reordered relative to other task types and may be
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h b/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
index 1a5a51f..0f830e82 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_frame_scheduler.h
@@ -8,8 +8,8 @@
 #include <deque>
 
 #include "third_party/blink/renderer/platform/scheduler/child/worker_scheduler_proxy.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
 
 namespace blink {
 namespace scheduler {
diff --git a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
index 419b349f..f25fee3d 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.cc
@@ -31,7 +31,7 @@
   return helper_->DefaultWorkerTaskQueue();
 }
 
-void CompositorThreadScheduler::Init() {}
+void CompositorThreadScheduler::InitImpl() {}
 
 void CompositorThreadScheduler::OnTaskCompleted(
     WorkerTaskQueue* worker_task_queue,
diff --git a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
index 8768e3b..8ef09729 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/compositor_thread_scheduler.h
@@ -32,16 +32,15 @@
 
   ~CompositorThreadScheduler() override;
 
-  // WorkerScheduler:
+  // NonMainThreadScheduler:
   scoped_refptr<WorkerTaskQueue> DefaultTaskQueue() override;
-  void Init() override;
   void OnTaskCompleted(WorkerTaskQueue* worker_task_queue,
                        const TaskQueue::Task& task,
                        base::TimeTicks start,
                        base::TimeTicks end,
                        base::Optional<base::TimeDelta> thread_time) override;
 
-  // ChildScheduler:
+  // WebThreadScheduler:
   scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override;
   scoped_refptr<scheduler::SingleThreadIdleTaskRunner> IdleTaskRunner()
       override;
@@ -59,6 +58,10 @@
   void DidProcessIdleTask() override;
   base::TimeTicks NowTicks() override;
 
+ protected:
+  // NonMainThreadScheduler:
+  void InitImpl() override;
+
  private:
   base::Thread* thread_;
 
diff --git a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc
index 37474106..370ffada 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/non_main_thread_scheduler.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
 
 namespace blink {
@@ -25,6 +26,17 @@
       thread_type, TaskQueueManager::TakeOverCurrentThread(), proxy);
 }
 
+void NonMainThreadScheduler::Init() {
+  InitImpl();
+
+  // DefaultTaskQueue() is a virtual function, so it can't be called in the
+  // constructor. Also, DefaultTaskQueue() checks if InitImpl() is called.
+  // Therefore, v8_task_runner_ needs to be initialized here.
+  // TODO(kraynov): Ditch kDeprecatedNone here.
+  v8_task_runner_ =
+      TaskRunnerImpl::Create(DefaultTaskQueue(), TaskType::kDeprecatedNone);
+}
+
 scoped_refptr<WorkerTaskQueue> NonMainThreadScheduler::CreateTaskRunner() {
   helper_->CheckOnValidThread();
   return helper_->NewTaskQueue(TaskQueue::Spec("worker_tq")
@@ -32,5 +44,49 @@
                                    .SetTimeDomain(nullptr));
 }
 
+void NonMainThreadScheduler::RunIdleTask(blink::WebThread::IdleTask task,
+                                         base::TimeTicks deadline) {
+  std::move(task).Run((deadline - base::TimeTicks()).InSecondsF());
+}
+
+void NonMainThreadScheduler::PostIdleTask(const base::Location& location,
+                                          blink::WebThread::IdleTask task) {
+  IdleTaskRunner()->PostIdleTask(
+      location,
+      base::BindOnce(&NonMainThreadScheduler::RunIdleTask, std::move(task)));
+}
+
+void NonMainThreadScheduler::PostNonNestableIdleTask(
+    const base::Location& location,
+    blink::WebThread::IdleTask task) {
+  IdleTaskRunner()->PostNonNestableIdleTask(
+      location,
+      base::BindOnce(&NonMainThreadScheduler::RunIdleTask, std::move(task)));
+}
+
+base::SingleThreadTaskRunner* NonMainThreadScheduler::V8TaskRunner() {
+  return v8_task_runner_.get();
+}
+
+base::SingleThreadTaskRunner* NonMainThreadScheduler::CompositorTaskRunner() {
+  return nullptr;
+}
+
+std::unique_ptr<blink::PageScheduler>
+NonMainThreadScheduler::CreatePageScheduler(PageScheduler::Delegate* delegate) {
+  NOTREACHED();
+  return nullptr;
+}
+
+std::unique_ptr<NonMainThreadScheduler::RendererPauseHandle>
+NonMainThreadScheduler::PauseScheduler() {
+  return nullptr;
+}
+
+base::TimeTicks NonMainThreadScheduler::MonotonicallyIncreasingVirtualTime()
+    const {
+  return base::TimeTicks::Now();
+}
+
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
index ab47029..d552717 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
@@ -145,7 +145,7 @@
   return helper_->DefaultWorkerTaskQueue();
 }
 
-void WorkerThreadScheduler::Init() {
+void WorkerThreadScheduler::InitImpl() {
   initialized_ = true;
   idle_helper_.EnableLongIdlePeriod();
 }
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
index 7391e257..45dd4d2 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h
@@ -47,7 +47,6 @@
 
   // NonMainThreadScheduler implementation:
   scoped_refptr<WorkerTaskQueue> DefaultTaskQueue() override;
-  void Init() override;
   void OnTaskCompleted(WorkerTaskQueue* worker_task_queue,
                        const TaskQueue::Task& task,
                        base::TimeTicks start,
@@ -71,6 +70,9 @@
   scoped_refptr<WorkerTaskQueue> ControlTaskQueue();
 
  protected:
+  // NonMainThreadScheduler implementation:
+  void InitImpl() override;
+
   // IdleHelper::Delegate implementation:
   bool CanEnterLongIdlePeriod(
       base::TimeTicks now,
diff --git a/third_party/blink/renderer/platform/timer_test.cc b/third_party/blink/renderer/platform/timer_test.cc
index def75ee..a1ed6d6 100644
--- a/third_party/blink/renderer/platform/timer_test.cc
+++ b/third_party/blink/renderer/platform/timer_test.cc
@@ -15,8 +15,8 @@
 #include "third_party/blink/renderer/platform/scheduler/base/task_queue_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/child/task_runner_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
-#include "third_party/blink/renderer/platform/scheduler/renderer/main_thread_task_queue.h"
 #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index fdbdf8e9..b500ea18 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -30,6 +30,7 @@
  *   thumbnailUrl: (string|undefined),
  *   croppedThumbnailUrl: (string|undefined),
  *   externalFileUrl: (string|undefined),
+ *   alternateUrl: (string|undefined),
  *   imageWidth: (number|undefined),
  *   imageHeight: (number|undefined),
  *   imageRotation: (number|undefined),
diff --git a/third_party/rnnoise/README.chromium b/third_party/rnnoise/README.chromium
index 2c2d316..8efe059 100644
--- a/third_party/rnnoise/README.chromium
+++ b/third_party/rnnoise/README.chromium
@@ -25,3 +25,4 @@
 * rnn_vad_weights.h: output layer sizes + weights scaling factor
 * removing unwanted extern from constants in rnn_vad_weights.h and using
   constants to declare array sizes
+* Add braces around arrays in unit test.
\ No newline at end of file
diff --git a/third_party/rnnoise/src/kiss_fft_unittest.cc b/third_party/rnnoise/src/kiss_fft_unittest.cc
index fd99052e..3132445 100644
--- a/third_party/rnnoise/src/kiss_fft_unittest.cc
+++ b/third_party/rnnoise/src/kiss_fft_unittest.cc
@@ -94,50 +94,41 @@
 
 TEST(RnnVadTest, KissFftBitExactness) {
   constexpr std::array<float, 32> samples = {
-      0.3524301946163177490234375f,  0.891803801059722900390625f,
-      0.07706542313098907470703125f, 0.699530780315399169921875f,
-      0.3789891898632049560546875f,  0.5438187122344970703125f,
-      0.332781612873077392578125f,   0.449340641498565673828125f,
-      0.105229437351226806640625f,   0.722373783588409423828125f,
-      0.13155306875705718994140625f, 0.340857982635498046875f,
-      0.970204889774322509765625f,   0.53061950206756591796875f,
-      0.91507828235626220703125f,    0.830274522304534912109375f,
-      0.74468600749969482421875f,    0.24320767819881439208984375f,
-      0.743998110294342041015625f,   0.17574800550937652587890625f,
-      0.1834825575351715087890625f,  0.63317775726318359375f,
-      0.11414264142513275146484375f, 0.1612723171710968017578125f,
-      0.80316197872161865234375f,    0.4979794919490814208984375f,
-      0.554282128810882568359375f,   0.67189347743988037109375f,
-      0.06660757958889007568359375f, 0.89568817615509033203125f,
-      0.29327380657196044921875f,    0.3472573757171630859375f};
+      {0.3524301946163177490234375f,  0.891803801059722900390625f,
+       0.07706542313098907470703125f, 0.699530780315399169921875f,
+       0.3789891898632049560546875f,  0.5438187122344970703125f,
+       0.332781612873077392578125f,   0.449340641498565673828125f,
+       0.105229437351226806640625f,   0.722373783588409423828125f,
+       0.13155306875705718994140625f, 0.340857982635498046875f,
+       0.970204889774322509765625f,   0.53061950206756591796875f,
+       0.91507828235626220703125f,    0.830274522304534912109375f,
+       0.74468600749969482421875f,    0.24320767819881439208984375f,
+       0.743998110294342041015625f,   0.17574800550937652587890625f,
+       0.1834825575351715087890625f,  0.63317775726318359375f,
+       0.11414264142513275146484375f, 0.1612723171710968017578125f,
+       0.80316197872161865234375f,    0.4979794919490814208984375f,
+       0.554282128810882568359375f,   0.67189347743988037109375f,
+       0.06660757958889007568359375f, 0.89568817615509033203125f,
+       0.29327380657196044921875f,    0.3472573757171630859375f}};
   constexpr std::array<float, 17> expected_real = {
-      0.4813065826892852783203125f,       -0.0246877372264862060546875f,
-      0.04095232486724853515625f,         -0.0401695556938648223876953125f,
-      0.00500857271254062652587890625f,   0.0160773508250713348388671875f,
-      -0.011385642923414707183837890625f, -0.008461721241474151611328125f,
-      0.01383177936077117919921875f,      0.0117270611226558685302734375f,
-      -0.0164460353553295135498046875f,   0.0585579685866832733154296875f,
-      0.02038039825856685638427734375f,   -0.0209107734262943267822265625f,
-      0.01046995259821414947509765625f,   -0.09019653499126434326171875f,
-      -0.0583711564540863037109375f};
+      {0.4813065826892852783203125f, -0.0246877372264862060546875f,
+       0.04095232486724853515625f, -0.0401695556938648223876953125f,
+       0.00500857271254062652587890625f, 0.0160773508250713348388671875f,
+       -0.011385642923414707183837890625f, -0.008461721241474151611328125f,
+       0.01383177936077117919921875f, 0.0117270611226558685302734375f,
+       -0.0164460353553295135498046875f, 0.0585579685866832733154296875f,
+       0.02038039825856685638427734375f, -0.0209107734262943267822265625f,
+       0.01046995259821414947509765625f, -0.09019653499126434326171875f,
+       -0.0583711564540863037109375f}};
   constexpr std::array<float, 17> expected_imag = {
-      0.f,
-      -0.010482530109584331512451171875f,
-      0.04762755334377288818359375f,
-      -0.0558677613735198974609375f,
-      0.007908363826572895050048828125f,
-      -0.0071932487189769744873046875f,
-      0.01322011835873126983642578125f,
-      -0.011227893643081188201904296875f,
-      -0.0400779247283935546875f,
-      -0.0290451310575008392333984375f,
-      0.01519204117357730865478515625f,
-      -0.09711246192455291748046875f,
-      -0.00136523949913680553436279296875f,
-      0.038602568209171295166015625f,
-      -0.009693108499050140380859375f,
-      -0.0183933563530445098876953125f,
-      0.f};
+      {0.f, -0.010482530109584331512451171875f, 0.04762755334377288818359375f,
+       -0.0558677613735198974609375f, 0.007908363826572895050048828125f,
+       -0.0071932487189769744873046875f, 0.01322011835873126983642578125f,
+       -0.011227893643081188201904296875f, -0.0400779247283935546875f,
+       -0.0290451310575008392333984375f, 0.01519204117357730865478515625f,
+       -0.09711246192455291748046875f, -0.00136523949913680553436279296875f,
+       0.038602568209171295166015625f, -0.009693108499050140380859375f,
+       -0.0183933563530445098876953125f, 0.f}};
 
   KissFft fft(32);
   std::array<std::complex<float>, 32> fft_buf_in;
diff --git a/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp b/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp
index 3a93a90..5d68093 100644
--- a/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp
+++ b/tools/clang/traffic_annotation_extractor/traffic_annotation_extractor.cpp
@@ -12,13 +12,7 @@
 // 2) Extracts all calls of the following network request creation functions
 //    and returns their source location and availability of a
 //    net::[Partial]NetworkTrafficAnnotation parameter in them:
-//     - SSLClientSocket::SSLClientSocket
-//     - TCPClientSocket::TCPClientSocket
-//     - UDPClientSocket::UDPClientSocket
 //     - URLFetcher::Create
-//     - ClientSocketFactory::CreateDatagramClientSocket
-//     - ClientSocketFactory::CreateSSLClientSocket
-//     - ClientSocketFactory::CreateTransportClientSocket
 //     - URLRequestContext::CreateRequest
 // 3) Finds all instances of initializing any of the following classes with list
 //    expressions or assignment of a value to |unique_id_hash_code| of the
@@ -349,18 +343,11 @@
 
   // Setup patterns to find functions that should be monitored.
   match_finder.addMatcher(
-      callExpr(
-          hasDeclaration(functionDecl(
-              anyOf(hasName("SSLClientSocket::SSLClientSocket"),
-                    hasName("TCPClientSocket::TCPClientSocket"),
-                    hasName("UDPClientSocket::UDPClientSocket"),
-                    hasName("URLFetcher::Create"),
-                    hasName("ClientSocketFactory::CreateDatagramClientSocket"),
-                    hasName("ClientSocketFactory::CreateSSLClientSocket"),
-                    hasName("ClientSocketFactory::CreateTransportClientSocket"),
-                    hasName("URLRequestContext::CreateRequest")),
-              has_annotation_parameter)),
-          bind_function_context_if_present)
+      callExpr(hasDeclaration(functionDecl(
+                   anyOf(hasName("URLFetcher::Create"),
+                         hasName("URLRequestContext::CreateRequest")),
+                   has_annotation_parameter)),
+               bind_function_context_if_present)
           .bind("monitored_function"),
       &callback);
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index ede841c..056b0424 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -11122,6 +11122,14 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="Mobile_FocusedDefocusedOmnibox_WithNoAction">
+  <owner>stkhapugin@chromium.org</owner>
+  <description>
+    Records when a user focuses the omnibox and then defocuses it without
+    modifying or copying the URL. iOS only.
+  </description>
+</action>
+
 <action name="MobileActionBarShown">
   <owner>donnd@chromium.org</owner>
   <description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index d2cf623..04f9c87 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19489,6 +19489,8 @@
   <int value="11" label="DRIVE_RECENT"/>
   <int value="12" label="MEDIA_VIEW"/>
   <int value="13" label="RECENT"/>
+  <int value="14" label="DRIVE_FAKE_ROOT"/>
+  <int value="15" label="ADD_NEW_SERVICES_MENU"/>
 </enum>
 
 <enum name="FileManagerTaskType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8fd68f7..4fdd2a2 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -27932,6 +27932,44 @@
   </summary>
 </histogram>
 
+<histogram name="FileBrowser.Location.OnEntryExpandedOrCollapsed.NonTopLevel"
+    enum="FileManagerRootType">
+  <owner>sashab@google.com</owner>
+  <summary>
+    Chrome OS Files App: The locations (root types) of non-top-level entries
+    when they are expanded or collapsed (expand icon clicked) in the directory
+    tree.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.Location.OnEntryExpandedOrCollapsed.TopLevel"
+    enum="FileManagerRootType">
+  <owner>sashab@google.com</owner>
+  <summary>
+    Chrome OS Files App: The locations (root types) of top-level entries (root
+    entries) when they are expanded or collapsed (expand icon clicked) in the
+    directory tree.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.Location.OnEntrySelected.NonTopLevel"
+    enum="FileManagerRootType">
+  <owner>sashab@google.com</owner>
+  <summary>
+    Chrome OS Files App: The locations (root types) of non-top-level entries
+    when they are clicked in the directory tree.
+  </summary>
+</histogram>
+
+<histogram name="FileBrowser.Location.OnEntrySelected.TopLevel"
+    enum="FileManagerRootType">
+  <owner>sashab@google.com</owner>
+  <summary>
+    Chrome OS Files App: The locations (root types) of top-level entries (root
+    entries) when they are clicked in the directory tree.
+  </summary>
+</histogram>
+
 <histogram name="FileBrowser.MenuItemSelected" enum="FileManagerMenuCommands">
   <owner>sashab@chromium.org</owner>
   <summary>
diff --git a/tools/traffic_annotation/auditor/traffic_annotation_exporter.cc b/tools/traffic_annotation/auditor/traffic_annotation_exporter.cc
index d47a101..3ac5342 100644
--- a/tools/traffic_annotation/auditor/traffic_annotation_exporter.cc
+++ b/tools/traffic_annotation/auditor/traffic_annotation_exporter.cc
@@ -276,6 +276,7 @@
 std::string TrafficAnnotationExporter::GenerateSerializedXML() const {
   XmlWriter writer;
   writer.StartWriting();
+  writer.AppendElementContent(kXmlComment);
   writer.StartElement("annotations");
 
   for (const auto& item : archive_) {
@@ -334,11 +335,8 @@
   writer.EndElement();
 
   writer.StopWriting();
-  std::string xml_content = writer.GetWrittenString();
-  // Add comment before annotation tag (and after xml version).
-  xml_content.insert(xml_content.find("<annotations>"), kXmlComment);
 
-  return xml_content;
+  return writer.GetWrittenString();
 }
 
 bool TrafficAnnotationExporter::SaveAnnotationsXML() const {
diff --git a/ui/base/models/table_model.h b/ui/base/models/table_model.h
index 110ae3c..088cda5 100644
--- a/ui/base/models/table_model.h
+++ b/ui/base/models/table_model.h
@@ -22,16 +22,19 @@
 // The model driving the TableView.
 class UI_BASE_EXPORT TableModel {
  public:
+  // Size of the table row icon, if used.
+  static constexpr int kIconSize = 16;
+
   // Number of rows in the model.
   virtual int RowCount() = 0;
 
   // Returns the value at a particular location in text.
   virtual base::string16 GetText(int row, int column_id) = 0;
 
-  // Returns the small icon (16x16) that should be displayed in the first
-  // column before the text. This is only used when the TableView was created
-  // with the ICON_AND_TEXT table type. Returns an isNull() image if there is
-  // no image.
+  // Returns the small icon (|kIconSize| x |kIconSize|) that should be displayed
+  // in the first column before the text. This is only used when the TableView
+  // was created with the ICON_AND_TEXT table type. Returns an isNull() image if
+  // there is no image.
   virtual gfx::ImageSkia GetIcon(int row);
 
   // Returns the tooltip, if any, to show for a particular row.  If there are
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index 6c856cb..4e07d1b 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -130,6 +130,7 @@
     "event_modifiers.h",
     "event_processor.cc",
     "event_processor.h",
+    "event_rewriter.cc",
     "event_rewriter.h",
     "event_sink.h",
     "event_source.cc",
diff --git a/ui/events/event_rewriter.cc b/ui/events/event_rewriter.cc
new file mode 100644
index 0000000..d9efe7e
--- /dev/null
+++ b/ui/events/event_rewriter.cc
@@ -0,0 +1,16 @@
+// 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 "ui/events/event_rewriter.h"
+
+#include "ui/events/event_source.h"
+
+namespace ui {
+
+EventDispatchDetails EventRewriter::SendEventToEventSource(EventSource* source,
+                                                           Event* event) const {
+  return source->SendEventToSinkFromRewriter(event, this);
+}
+
+}  // namespace ui
diff --git a/ui/events/event_rewriter.h b/ui/events/event_rewriter.h
index dbdb9e61..a096aa9 100644
--- a/ui/events/event_rewriter.h
+++ b/ui/events/event_rewriter.h
@@ -7,11 +7,14 @@
 
 #include <memory>
 
+#include "base/macros.h"
+#include "ui/events/event_dispatcher.h"
 #include "ui/events/events_export.h"
 
 namespace ui {
 
 class Event;
+class EventSource;
 
 // Return status of EventRewriter operations; see that class below.
 enum EventRewriteStatus {
@@ -42,7 +45,8 @@
 // before being dispatched from EventSource to EventSink.
 class EVENTS_EXPORT EventRewriter {
  public:
-  virtual ~EventRewriter() {}
+  EventRewriter() = default;
+  virtual ~EventRewriter() = default;
 
   // Potentially rewrites (replaces) an event, or requests it be discarded.
   // or discards an event. If the rewriter wants to rewrite an event, and
@@ -62,6 +66,15 @@
   virtual EventRewriteStatus NextDispatchEvent(
       const Event& last_event,
       std::unique_ptr<Event>* new_event) = 0;
+
+ protected:
+  // A helper that calls a protected EventSource function, which sends the event
+  // to subsequent event rewriters on the source and onto its event sink.
+  EventDispatchDetails SendEventToEventSource(EventSource* source,
+                                              Event* event) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EventRewriter);
 };
 
 }  // namespace ui
diff --git a/ui/events/event_source.cc b/ui/events/event_source.cc
index e45be7c..21e160b3 100644
--- a/ui/events/event_source.cc
+++ b/ui/events/event_source.cc
@@ -41,6 +41,12 @@
 }
 
 EventDispatchDetails EventSource::SendEventToSink(Event* event) {
+  return SendEventToSinkFromRewriter(event, nullptr);
+}
+
+EventDispatchDetails EventSource::SendEventToSinkFromRewriter(
+    Event* event,
+    const EventRewriter* rewriter) {
   std::unique_ptr<ui::Event> event_for_rewriting_ptr;
   Event* event_for_rewriting = event;
   if (!rewriter_list_.empty() && IsLocatedEventWithDifferentLocations(*event)) {
@@ -56,7 +62,13 @@
   EventRewriteStatus status = EVENT_REWRITE_CONTINUE;
   EventRewriterList::const_iterator it = rewriter_list_.begin(),
                                     end = rewriter_list_.end();
+  // If a rewriter reposted |event|, only send it to subsequent rewriters.
+  bool send_to_rewriter = rewriter == nullptr;
   for (; it != end; ++it) {
+    if (!send_to_rewriter) {
+      send_to_rewriter |= (*it == rewriter);
+      continue;
+    }
     status = (*it)->RewriteEvent(*event_for_rewriting, &rewritten_event);
     if (status == EVENT_REWRITE_DISCARD) {
       CHECK(!rewritten_event);
diff --git a/ui/events/event_source.h b/ui/events/event_source.h
index d2f223dc..d5b9b1c 100644
--- a/ui/events/event_source.h
+++ b/ui/events/event_source.h
@@ -34,15 +34,25 @@
   void RemoveEventRewriter(EventRewriter* rewriter);
 
  protected:
+  // Sends the event through all rewriters and onto the source's EventSink.
   EventDispatchDetails SendEventToSink(Event* event);
 
+  // Sends the event through the rewriters and onto the source's EventSink.
+  // If |rewriter| is valid, |event| is only sent to the subsequent rewriters.
+  // This is used for asynchronous reposting of events processed by |rewriter|.
+  EventDispatchDetails SendEventToSinkFromRewriter(
+      Event* event,
+      const EventRewriter* rewriter);
+
  private:
+  friend class EventRewriter;
   friend class EventSourceTestApi;
 
   EventDispatchDetails DeliverEventToSink(Event* event);
 
   typedef std::vector<EventRewriter*> EventRewriterList;
   EventRewriterList rewriter_list_;
+
   DISALLOW_COPY_AND_ASSIGN(EventSource);
 };
 
diff --git a/ui/file_manager/file_manager/common/js/volume_manager_common.js b/ui/file_manager/file_manager/common/js/volume_manager_common.js
index ddf8357..ad8df50 100644
--- a/ui/file_manager/file_manager/common/js/volume_manager_common.js
+++ b/ui/file_manager/file_manager/common/js/volume_manager_common.js
@@ -95,6 +95,13 @@
 
   // Fake root for the mixed "Recent" view.
   RECENT: 'recent',
+
+  // 'Google Drive' fake parent entry of 'My Drive', 'Shared with me' and
+  // 'Offline'.
+  DRIVE_FAKE_ROOT: 'drive_fake_root',
+
+  // 'Add new services' menu item.
+  ADD_NEW_SERVICES_MENU: 'add_new_services_menu',
 };
 Object.freeze(VolumeManagerCommon.RootType);
 
@@ -122,6 +129,8 @@
   VolumeManagerCommon.RootType.DRIVE_RECENT,
   VolumeManagerCommon.RootType.MEDIA_VIEW,
   VolumeManagerCommon.RootType.RECENT,
+  VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT,
+  VolumeManagerCommon.RootType.ADD_NEW_SERVICES_MENU,
 ];
 console.assert(
     Object.keys(VolumeManagerCommon.RootType).length ===
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model.js b/ui/file_manager/file_manager/foreground/js/actions_model.js
index bb1d033..54e0cb3 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model.js
@@ -3,16 +3,22 @@
 // found in the LICENSE file.
 
 /**
+ * A single action, that can be taken on a set of entries.
  * @interface
  */
 function Action() {
 }
 
+/**
+ * Executes this action on the set of entries.
+ */
 Action.prototype.execute = function() {
 };
 
 /**
- * @return {boolean}
+ * Checks whether this action can execute on the set of entries.
+ *
+ * @return {boolean} True if the function can execute, false if not.
  */
 Action.prototype.canExecute = function() {
 };
@@ -400,7 +406,97 @@
   return null;
 };
 
+
 /**
+ * Opens the entry in Drive Web for the user to manage permissions etc.
+ *
+ * @param {!Entry} entry The entry to open the 'Manage' page for.
+ * @param {!ActionModelUI} ui
+ * @param {!VolumeManagerWrapper} volumeManager
+ * @implements {Action}
+ * @constructor
+ * @struct
+ */
+function DriveManageAction(entry, volumeManager, ui) {
+  /**
+   * The entry to open the 'Manage' page for.
+   *
+   * @private {!Entry}
+   * @const
+   */
+  this.entry_ = entry;
+
+  /**
+   * @private {!VolumeManagerWrapper}
+   * @const
+   */
+  this.volumeManager_ = volumeManager;
+
+  /**
+   * @private {!ActionModelUI}
+   * @const
+   */
+  this.ui_ = ui;
+}
+
+/**
+ * Creates a new DriveManageAction object.
+ * |entries| must contain only a single entry.
+ *
+ * @param {!Array<!Entry>} entries
+ * @param {!ActionModelUI} ui
+ * @param {!VolumeManagerWrapper} volumeManager
+ * @return {DriveManageAction}
+ */
+DriveManageAction.create = function(entries, volumeManager, ui) {
+  if (entries.length !== 1)
+    return null;
+
+  return new DriveManageAction(entries[0], volumeManager, ui);
+};
+
+/**
+ * @override
+ */
+DriveManageAction.prototype.execute = function() {
+  chrome.fileManagerPrivate.getEntryProperties(
+      [this.entry_], ['alternateUrl'], function(results) {
+        if (chrome.runtime.lastError) {
+          console.error(chrome.runtime.lastError.message);
+          return;
+        }
+        if (results.length == 1) {
+          console.error(
+              'getEntryProperties for alternateUrl should return 1 entry ' +
+              '(returned ' + results.length + ')');
+          return;
+        }
+        util.visitURL(results[0].alternateUrl);
+      }.bind(this));
+};
+
+/**
+ * @override
+ */
+DriveManageAction.prototype.canExecute = function() {
+  // For now, only allow managing of regular files (not directories).
+  return !this.entry_.isDirectory &&
+      this.volumeManager_.getDriveConnectionState().type !==
+      VolumeManagerCommon.DriveConnectionType.OFFLINE &&
+      !util.isTeamDriveRoot(this.entry_);
+};
+
+/**
+ * @return {?string}
+ */
+DriveManageAction.prototype.getTitle = function() {
+  return null;
+};
+
+
+/**
+ * A custom action set by the FSP API.
+ *
  * @param {!Array<!Entry>} entries
  * @param {string} id
  * @param {?string} title
@@ -464,7 +560,8 @@
 };
 
 /**
- * Represents a set of actions for a set of entries.
+ * Represents a set of actions for a set of entries. Includes actions set
+ * locally in JS, as well as those retrieved from the FSP API.
  *
  * @param {!VolumeManagerWrapper} volumeManager
  * @param {!MetadataModel} metadataModel
@@ -556,10 +653,13 @@
  */
 ActionsModel.InternalActionId = {
   CREATE_FOLDER_SHORTCUT: 'create-folder-shortcut',
-  REMOVE_FOLDER_SHORTCUT: 'remove-folder-shortcut'
+  REMOVE_FOLDER_SHORTCUT: 'remove-folder-shortcut',
+  MANAGE_IN_DRIVE: 'manage-in-drive'
 };
 
 /**
+ * Initializes the ActionsModel, including populating the list of available
+ * actions for the given entries.
  * @return {!Promise}
  */
 ActionsModel.prototype.initialize = function() {
@@ -632,6 +732,14 @@
           actions[ActionsModel.InternalActionId.REMOVE_FOLDER_SHORTCUT] =
               removeFolderShortcutAction;
         }
+
+        var manageInDriveAction = DriveManageAction.create(
+            this.entries_, this.volumeManager_, this.ui_);
+        if (manageInDriveAction) {
+          actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE] =
+              manageInDriveAction;
+        }
+
         fulfill(actions);
         break;
 
diff --git a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
index 6fbf6b04..8ef31746 100644
--- a/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/actions_model_unittest.js
@@ -66,7 +66,7 @@
     showHtml: function() {
     }
   };
-};
+}
 
 function setUp() {
   window.chrome = {
@@ -99,6 +99,9 @@
   ui = new MockUI();
 }
 
+/**
+ * Tests that the correct actions are available for a directory in Google Drive.
+ */
 function testDriveDirectoryEntry(callback) {
   driveFileSystem.entries['/test'] =
       new MockDirectoryEntry(driveFileSystem, '/test', {});
@@ -111,8 +114,9 @@
   });
   return reportPromise(model.initialize().then(function() {
     var actions = model.getActions();
-    assertEquals(2, Object.keys(actions).length);
+    assertEquals(3, Object.keys(actions).length);
 
+    // 'Share' should be disabled in offline mode.
     var shareAction = actions[ActionsModel.CommonActionId.SHARE];
     assertTrue(!!shareAction);
     volumeManager.driveConnectionState = {
@@ -120,6 +124,13 @@
     };
     assertFalse(shareAction.canExecute());
 
+    // 'Manage in Drive' should be disabled for directories.
+    var manageInDriveAction =
+        actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+    assertTrue(!!manageInDriveAction);
+    assertFalse(manageInDriveAction.canExecute());
+
+    // 'Create Shortcut' should be enabled, until it's executed, then disabled.
     var createFolderShortcutAction =
         actions[ActionsModel.InternalActionId.CREATE_FOLDER_SHORTCUT];
     assertTrue(!!createFolderShortcutAction);
@@ -138,8 +149,12 @@
     return model.initialize();
   }).then(function() {
     var actions = model.getActions();
-    assertEquals(3, Object.keys(actions).length);
+    assertEquals(4, Object.keys(actions).length);
+    assertTrue(!!actions[ActionsModel.CommonActionId.SHARE]);
+    assertTrue(!!actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE]);
+    assertTrue(!!actions[ActionsModel.InternalActionId.REMOVE_FOLDER_SHORTCUT]);
 
+    // 'Create shortcut' should be disabled.
     var createFolderShortcutAction =
         actions[ActionsModel.InternalActionId.CREATE_FOLDER_SHORTCUT];
     assertTrue(!!createFolderShortcutAction);
@@ -148,6 +163,9 @@
   }), callback);
 }
 
+/**
+ * Tests that the correct actions are available for a file in Google Drive.
+ */
 function testDriveFileEntry(callback) {
   driveFileSystem.entries['/test.txt'] =
       new MockFileEntry(driveFileSystem, '/test.txt', {});
@@ -161,14 +179,21 @@
   };
   return reportPromise(model.initialize().then(function() {
     var actions = model.getActions();
-    assertEquals(2, Object.keys(actions).length);
+    assertEquals(3, Object.keys(actions).length);
     assertTrue(!!actions[ActionsModel.CommonActionId.SHARE]);
 
+    // 'Save for Offline' should be enabled.
     var saveForOfflineAction =
         actions[ActionsModel.CommonActionId.SAVE_FOR_OFFLINE];
     assertTrue(!!saveForOfflineAction);
     assertTrue(saveForOfflineAction.canExecute());
 
+    // 'Manage in Drive' should be enabled.
+    var manageInDriveAction =
+        actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+    assertTrue(!!manageInDriveAction);
+    assertTrue(manageInDriveAction.canExecute());
+
     chrome.fileManagerPrivate.pinDriveFile = function(entry, pin, callback) {
       metadataModel.properties.pinned = true;
       assertEquals(driveFileSystem.entries['/test.txt'], entry);
@@ -196,14 +221,21 @@
     return model.initialize();
   }).then(function() {
     var actions = model.getActions();
-    assertEquals(2, Object.keys(actions).length);
+    assertEquals(3, Object.keys(actions).length);
     assertTrue(!!actions[ActionsModel.CommonActionId.SHARE]);
 
+    // 'Offline not Necessary' should be enabled.
     var offlineNotNecessaryAction =
         actions[ActionsModel.CommonActionId.OFFLINE_NOT_NECESSARY];
     assertTrue(!!offlineNotNecessaryAction);
     assertTrue(offlineNotNecessaryAction.canExecute());
 
+    // 'Manage in Drive' should be enabled.
+    var manageInDriveAction =
+        actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+    assertTrue(!!manageInDriveAction);
+    assertTrue(manageInDriveAction.canExecute());
+
     chrome.fileManagerPrivate.pinDriveFile = function(entry, pin, callback) {
       metadataModel.properties.pinned = false;
       assertEquals(driveFileSystem.entries['/test.txt'], entry);
@@ -224,7 +256,10 @@
   }), callback);
 }
 
-function testTeamDriveEntry(callback) {
+/**
+ * Tests that a Team Drive Root entry has the correct actions available.
+ */
+function testTeamDriveRootEntry(callback) {
   driveFileSystem.entries['/team_drives/ABC Team'] =
       new MockDirectoryEntry(driveFileSystem, '/team_drives/ABC Team', {});
 
@@ -234,16 +269,99 @@
   return reportPromise(
       model.initialize().then(function() {
         var actions = model.getActions();
-        assertEquals(1, Object.keys(actions).length);
+        assertEquals(2, Object.keys(actions).length);
 
-        // "share" action is disabled for Team Drive entries.
+        // "share" action is disabled for Team Drive Root entries.
         var shareAction = actions[ActionsModel.CommonActionId.SHARE];
         assertTrue(!!shareAction);
         assertFalse(shareAction.canExecute());
+
+        // "manage in drive" action is disabled for Team Drive Root entries.
+        var manageAction =
+            actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+        assertTrue(!!manageAction);
+        assertFalse(manageAction.canExecute());
       }),
       callback);
 }
 
+/**
+ * Tests that a Team Drive directory entry has the correct actions available.
+ */
+function testTeamDriveDirectoryEntry(callback) {
+  driveFileSystem.entries['/team_drives/ABC Team/Folder 1'] =
+      new MockDirectoryEntry(
+          driveFileSystem, '/team_drives/ABC Team/Folder 1', {});
+
+  var model = new ActionsModel(
+      volumeManager, metadataModel, shortcutsModel, driveSyncHandler, ui,
+      [driveFileSystem.entries['/team_drives/ABC Team/Folder 1']]);
+  return reportPromise(
+      model.initialize().then(function() {
+        var actions = model.getActions();
+        assertEquals(3, Object.keys(actions).length);
+
+        // "share" action is enabled for Team Drive directories.
+        var shareAction = actions[ActionsModel.CommonActionId.SHARE];
+        assertTrue(!!shareAction);
+        assertTrue(shareAction.canExecute());
+
+        // "manage in drive" action is disabled for Team Drive directories.
+        var manageAction =
+            actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+        assertTrue(!!manageAction);
+        assertFalse(manageAction.canExecute());
+
+        // 'Create shortcut' should be enabled.
+        var createFolderShortcutAction =
+            actions[ActionsModel.InternalActionId.CREATE_FOLDER_SHORTCUT];
+        assertTrue(!!createFolderShortcutAction);
+        assertTrue(createFolderShortcutAction.canExecute());
+      }),
+      callback);
+}
+
+/**
+ * Tests that a Team Drive file entry has the correct actions available.
+ */
+function testTeamDriveFileEntry(callback) {
+  driveFileSystem.entries['/team_drives/ABC Team/Folder 1/test.txt'] =
+      new MockFileEntry(
+          driveFileSystem, '/team_drives/ABC Team/Folder 1/test.txt', {});
+
+  var model = new ActionsModel(
+      volumeManager, metadataModel, shortcutsModel, driveSyncHandler, ui,
+      [driveFileSystem.entries['/team_drives/ABC Team/Folder 1/test.txt']]);
+  metadataModel.properties = {hosted: false, pinned: false};
+  return reportPromise(
+      model.initialize().then(function() {
+        var actions = model.getActions();
+        assertEquals(3, Object.keys(actions).length);
+
+        // "save for offline" action is enabled for Team Drive file entries.
+        var saveForOfflineAction =
+            actions[ActionsModel.CommonActionId.SAVE_FOR_OFFLINE];
+        assertTrue(!!saveForOfflineAction);
+        assertTrue(saveForOfflineAction.canExecute());
+
+        // "share" action is enabled for Team Drive file entries.
+        var shareAction = actions[ActionsModel.CommonActionId.SHARE];
+        assertTrue(!!shareAction);
+        assertTrue(shareAction.canExecute());
+
+        // "manage in drive" action is enabled for Team Drive file entries.
+        var manageAction =
+            actions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+        assertTrue(!!manageAction);
+        assertTrue(manageAction.canExecute());
+      }),
+      callback);
+}
+
+/**
+ * Tests that if actions are provided with getCustomActions(), they appear
+ * correctly for the file.
+ */
 function testProvidedEntry(callback) {
   providedFileSystem.entries['/test'] =
       new MockDirectoryEntry(providedFileSystem, '/test', {});
@@ -311,6 +429,9 @@
   }), callback);
 }
 
+/**
+ * Tests that no actions are available when getCustomActions() throws an error.
+ */
 function testProvidedEntryWithError(callback) {
   providedFileSystem.entries['/test'] =
       new MockDirectoryEntry(providedFileSystem, '/test', {});
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 45c82ce..5046ba8 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1505,6 +1505,41 @@
 });
 
 /**
+ * Opens the file in Drive for the user to manage sharing permissions etc.
+ * @type {Command}
+ */
+CommandHandler.COMMANDS_['manage-in-drive'] = /** @type {Command} */ ({
+  /**
+   * @param {!Event} event Command event.
+   * @param {!CommandHandlerDeps} fileManager The file manager instance.
+   */
+  execute: function(event, fileManager) {
+    var actionsModel =
+        fileManager.actionsController.getActionsModelFor(event.target);
+    var action = actionsModel ?
+        actionsModel.getAction(ActionsModel.InternalActionId.MANAGE_IN_DRIVE) :
+        null;
+    if (action)
+      action.execute();
+  },
+  /**
+   * @param {!Event} event Command event.
+   * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
+   */
+  canExecute: function(event, fileManager) {
+    var actionsModel =
+        fileManager.actionsController.getActionsModelFor(event.target);
+    var action = actionsModel ?
+        actionsModel.getAction(ActionsModel.InternalActionId.MANAGE_IN_DRIVE) :
+        null;
+    event.canExecute = action && action.canExecute();
+    if (actionsModel)
+      event.command.setHidden(!action);
+  }
+});
+
+
+/**
  * Creates a shortcut of the selected folder (single only).
  * @type {Command}
  */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/actions_submenu.js b/ui/file_manager/file_manager/foreground/js/ui/actions_submenu.js
index 98238a95..147a74bb 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/actions_submenu.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/actions_submenu.js
@@ -67,6 +67,18 @@
   }
   util.queryDecoratedElement('#share', cr.ui.Command).canExecuteChange();
 
+  // Then add the Manage in Drive item (if available).
+  var manageInDriveAction =
+      remainingActions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+  if (manageInDriveAction) {
+    var menuItem = this.addMenuItem_({});
+    menuItem.command = '#manage-in-drive';
+    menuItem.classList.toggle('hide-on-toolbar', true);
+    delete remainingActions[ActionsModel.InternalActionId.MANAGE_IN_DRIVE];
+  }
+  util.queryDecoratedElement('#manage-in-drive', cr.ui.Command)
+      .canExecuteChange();
+
   // Managing shortcuts is shown just before custom actions.
   var createFolderShortcutAction = remainingActions[
       ActionsModel.InternalActionId.CREATE_FOLDER_SHORTCUT];
diff --git a/ui/file_manager/file_manager/foreground/js/ui/actions_submenu_unittest.html b/ui/file_manager/file_manager/foreground/js/ui/actions_submenu_unittest.html
index 0f3313a..f78fc34 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/actions_submenu_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/ui/actions_submenu_unittest.html
@@ -13,6 +13,7 @@
 </script>
 
 <command id="share" label="Share"></command>
+<command id="manage-in-drive" i18n-values="Manage in Drive"></command>
 <command id="toggle-pinned" label="Toggle pinned"></command>
 <command id="create-folder-shortcut" label="Create folder shortcut"></command>
 <command id="remove-folder-shortcut" label="Remove folder shortcut"></command>
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 73400f6..3c44789 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -69,6 +69,32 @@
   }
   return false;
 };
+/**
+ * Records UMA for the selected entry at {@code location}. Records slightly
+ * differently if the expand icon is selected and {@code expandIconSelected} is
+ * true.
+ *
+ * @param {Event} e The click event.
+ * @param {VolumeManagerCommon.RootType} rootType The root type to record.
+ * @param {boolean} isRootEntry Whether the entry selected was a root entry.
+ * @return
+ */
+DirectoryItemTreeBaseMethods.recordUMASelectedEntry = function(
+    e, rootType, isRootEntry) {
+  var expandIconSelected = e.target.classList.contains('expand-icon');
+  var metricName = 'Location.OnEntrySelected.TopLevel';
+  if (!expandIconSelected && isRootEntry) {
+    metricName = 'Location.OnEntrySelected.TopLevel';
+  } else if (!expandIconSelected && !isRootEntry) {
+    metricName = 'Location.OnEntrySelected.NonTopLevel';
+  } else if (expandIconSelected && isRootEntry) {
+    metricName = 'Location.OnEntryExpandedOrCollapsed.TopLevel';
+  } else if (expandIconSelected && !isRootEntry) {
+    metricName = 'Location.OnEntryExpandedOrCollapsed.NonTopLevel';
+  }
+
+  metrics.recordEnum(metricName, rootType, VolumeManagerCommon.RootTypesForUMA);
+};
 
 Object.freeze(DirectoryItemTreeBaseMethods);
 
@@ -324,12 +350,20 @@
 DirectoryItem.prototype.handleClick = function(e) {
   cr.ui.TreeItem.prototype.handleClick.call(this, e);
 
-  if (!this.entry || e.button === 2 ||
-      e.target.classList.contains('expand-icon')) {
+  if (!this.entry || e.button === 2) {
     return;
   }
 
-  this.directoryModel_.activateDirectoryEntry(this.entry);
+  if (!e.target.classList.contains('expand-icon')) {
+    this.directoryModel_.activateDirectoryEntry(this.entry);
+  }
+
+  // If this is DriveVolumeItem, the UMA has already been recorded.
+  if (!(this instanceof DriveVolumeItem)) {
+    var location = this.tree.volumeManager.getLocationInfo(this.entry);
+    DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
+        this, e, location.rootType, location.isRootEntry);
+  }
 };
 
 /**
@@ -761,6 +795,9 @@
       this.searchAndSelectByEntry(displayRoot);
     }.bind(this));
   }
+
+  DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
+      this, e, VolumeManagerCommon.RootType.DRIVE_FAKE_ROOT, true);
 };
 
 /**
@@ -923,10 +960,14 @@
   // Do not activate with right click.
   if (e.button === 2)
     return;
-
   this.activate();
+
   // Resets file selection when a volume is clicked.
   this.parentTree_.directoryModel.clearSelection();
+
+  var location = this.tree.volumeManager.getLocationInfo(this.entry);
+  DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
+      this, e, location.rootType, location.isRootEntry);
 };
 
 /**
@@ -1035,6 +1076,9 @@
  */
 MenuItem.prototype.handleClick = function(e) {
   this.activate();
+
+  DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
+      this, e, VolumeManagerCommon.RootType.ADD_NEW_SERVICES_MENU, true);
 };
 
 /**
@@ -1111,6 +1155,9 @@
  */
 RecentItem.prototype.handleClick = function(e) {
   this.activate();
+
+  DirectoryItemTreeBaseMethods.recordUMASelectedEntry.call(
+      this, e, VolumeManagerCommon.RootType.RECENT, true);
 };
 
 /**
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index 22b9307..b745cd9 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -152,6 +152,8 @@
 
       <command id="share" i18n-values="label:SHARE_BUTTON_LABEL" disabled hidden
                shortcut="." hide-shortcut-text>
+      <command id="manage-in-drive"
+               i18n-values="label:MANAGE_IN_DRIVE_BUTTON_LABEL" disabled hidden>
 
       <command id="zoom-in" shortcut="=|Ctrl">
       <command id="zoom-out" shortcut="-|Ctrl">
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc
index d7f37cf1..c4325de2 100644
--- a/ui/views/controls/table/table_view.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -31,15 +31,12 @@
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/style/typography.h"
 
-// Size of images.
-static const int kImageSize = 16;
-
-static const int kGroupingIndicatorSize = 6;
-
 namespace views {
 
 namespace {
 
+constexpr int kGroupingIndicatorSize = 6;
+
 // Returns result, unless ascending is false in which case -result is returned.
 int SwapCompareResult(int result, bool ascending) {
   return ascending ? result : -result;
@@ -588,14 +585,15 @@
       if (j == 0 && table_type_ == ICON_AND_TEXT) {
         gfx::ImageSkia image = model_->GetIcon(model_index);
         if (!image.isNull()) {
-          int image_x = GetMirroredXWithWidthInView(text_x, kImageSize);
+          int image_x =
+              GetMirroredXWithWidthInView(text_x, ui::TableModel::kIconSize);
           canvas->DrawImageInt(
-              image, 0, 0, image.width(), image.height(),
-              image_x,
-              cell_bounds.y() + (cell_bounds.height() - kImageSize) / 2,
-              kImageSize, kImageSize, true);
+              image, 0, 0, image.width(), image.height(), image_x,
+              cell_bounds.y() +
+                  (cell_bounds.height() - ui::TableModel::kIconSize) / 2,
+              ui::TableModel::kIconSize, ui::TableModel::kIconSize, true);
         }
-        text_x += kImageSize + cell_element_spacing;
+        text_x += ui::TableModel::kIconSize + cell_element_spacing;
       }
       if (text_x < cell_bounds.right() - cell_margin) {
         canvas->DrawStringRectWithFlags(
@@ -743,7 +741,7 @@
     if (grouper_)
       text_x += kGroupingIndicatorSize + cell_element_spacing;
     if (table_type_ == ICON_AND_TEXT)
-      text_x += kImageSize + cell_element_spacing;
+      text_x += ui::TableModel::kIconSize + cell_element_spacing;
   }
   bounds->set_x(text_x);
   bounds->set_width(std::max(0, bounds->right() - cell_margin - text_x));
@@ -770,7 +768,7 @@
   const int cell_element_spacing = GetCellElementSpacing();
   int first_column_padding = 0;
   if (table_type_ == ICON_AND_TEXT && header_)
-    first_column_padding += kImageSize + cell_element_spacing;
+    first_column_padding += ui::TableModel::kIconSize + cell_element_spacing;
   if (grouper_)
     first_column_padding += kGroupingIndicatorSize + cell_element_spacing;