diff --git a/DEPS b/DEPS
index 86521ab2..7b35167 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'b423247bc2395e0f0172369078cdb227ae99c7a8',
+  'skia_revision': '0b0043392bfdf13f74ed64ba729f9c1e0fbae94f',
   # 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': '6014ce844cec377c154605a0b848c8b2faf8b853',
+  'v8_revision': 'd7b61abe7b48928aed739f02bf7695732d359e7e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -117,7 +117,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '639bc90867325cfcc95b9365c66b6128fcd1b654',
+  'angle_revision': 'da92a476b50aa5db1c5549c404435c8b1ce47f4f',
   # 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.
@@ -165,7 +165,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': 'c3318d5f367c021a5cebb24baeb04a9bc96a9ed5',
+  'catapult_revision': '2ec2b508e61067ba5bfaf79ecc2e3b1dc79971d1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -252,7 +252,7 @@
     Var('chromium_git') + '/external/github.com/immersive-web/webxr-samples.git' + '@' + 'cf02f19c4ff6894705a9407722ab52551e010c60',
 
   'src/ios/third_party/earl_grey/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '91c27bb8a15e723df974f620f7f576a30a6a7484',
+      'url': Var('chromium_git') + '/external/github.com/google/EarlGrey.git' + '@' + '451b6497352d0731e9827a338f32024e564078c6',
       'condition': 'checkout_ios',
   },
 
@@ -539,7 +539,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ed4c83aac3f10d767455d86430904e7e70fe8241',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e34766b47b67724b9b4c71e7463eca17ad42abc1',
       'condition': 'checkout_linux',
   },
 
@@ -912,7 +912,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '1b013420703b9496b1777aea5609bee7d9f3ecec',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c6f7ba31d2550fad984f8a7be0199acd1975e2d9',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
diff --git a/ash/login/login_screen_controller.cc b/ash/login/login_screen_controller.cc
index 65d8208..eb5d40d 100644
--- a/ash/login/login_screen_controller.cc
+++ b/ash/login/login_screen_controller.cc
@@ -237,12 +237,6 @@
   login_screen_client_->ShowFeedback();
 }
 
-void LoginScreenController::ShowResetScreen() {
-  if (!login_screen_client_)
-    return;
-  login_screen_client_->ShowResetScreen();
-}
-
 void LoginScreenController::AddObserver(
     LoginScreenControllerObserver* observer) {
   observers_.AddObserver(observer);
diff --git a/ash/login/login_screen_controller.h b/ash/login/login_screen_controller.h
index 20a54f9b..df23ead 100644
--- a/ash/login/login_screen_controller.h
+++ b/ash/login/login_screen_controller.h
@@ -82,7 +82,6 @@
   void RequestPublicSessionKeyboardLayouts(const AccountId& account_id,
                                            const std::string& locale);
   void ShowFeedback();
-  void ShowResetScreen();
   void LaunchKioskApp(const std::string& app_id);
   void LaunchArcKioskApp(const AccountId& account_id);
 
diff --git a/ash/login/mock_login_screen_client.h b/ash/login/mock_login_screen_client.h
index 00d9ea0f..b765c3cb2 100644
--- a/ash/login/mock_login_screen_client.h
+++ b/ash/login/mock_login_screen_client.h
@@ -68,7 +68,6 @@
   MOCK_METHOD2(RequestPublicSessionKeyboardLayouts,
                void(const AccountId& account_id, const std::string& locale));
   MOCK_METHOD0(ShowFeedback, void());
-  MOCK_METHOD0(ShowResetScreen, void());
   MOCK_METHOD1(LaunchKioskApp, void(const std::string& app_id));
   MOCK_METHOD1(LaunchArcKioskApp, void(const AccountId& account_id));
 
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 3641ed8..65d864f 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -38,13 +38,10 @@
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/user_manager/user_type.h"
 #include "ui/accessibility/ax_node_data.h"
-#include "ui/base/accelerators/accelerator.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/manager/managed_display_info.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -1446,9 +1443,6 @@
       AcceleratorAction::kFocusNextUser;
   accel_map_[ui::Accelerator(ui::VKEY_LEFT, 0)] =
       AcceleratorAction::kFocusPreviousUser;
-  accel_map_[ui::Accelerator(
-      ui::VKEY_R, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN)] =
-      AcceleratorAction::kShowResetScreen;
 
   AcceleratorController* controller = Shell::Get()->accelerator_controller();
   for (const auto& item : accel_map_)
@@ -1466,9 +1460,6 @@
     case AcceleratorAction::kFocusPreviousUser:
       FocusPreviousUser();
       return;
-    case AcceleratorAction::kShowResetScreen:
-      Shell::Get()->login_screen_controller()->ShowResetScreen();
-      return;
     default:
       NOTREACHED();
   }
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index 7c9c3ccc..52d7ba7a 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -104,7 +104,6 @@
     kShowFeedback,
     kFocusNextUser,
     kFocusPreviousUser,
-    kShowResetScreen,
   };
 
   // Number of login attempts before a login dialog is shown. For example, if
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 215d95c..74ebd63b2 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -35,7 +35,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/manager/display_manager.h"
 #include "ui/display/test/display_manager_test_api.h"
-#include "ui/events/event_constants.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -1723,15 +1722,4 @@
   EXPECT_TRUE(login_views_utils::HasFocusInAnyChildView(primary_password_view));
 }
 
-TEST_F(LockContentsViewKeyboardUnitTest, PowerwashShortcutShowsResetScreen) {
-  ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
-  std::unique_ptr<MockLoginScreenClient> client = BindMockLoginScreenClient();
-  EXPECT_CALL(*client, ShowResetScreen()).Times(1);
-
-  ui::test::EventGenerator* generator = GetEventGenerator();
-  generator->PressKey(
-      ui::VKEY_R, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
-  base::RunLoop().RunUntilIdle();
-}
-
 }  // namespace ash
diff --git a/ash/public/interfaces/login_screen.mojom b/ash/public/interfaces/login_screen.mojom
index 0c78ae5..a3eb969 100644
--- a/ash/public/interfaces/login_screen.mojom
+++ b/ash/public/interfaces/login_screen.mojom
@@ -226,9 +226,6 @@
   // Request to show a feedback report dialog in chrome.
   ShowFeedback();
 
-  // Show the reset (powerwash) screen.
-  ShowResetScreen();
-
   // Launch the specific kiosk app.
   LaunchKioskApp(string app_id);
 
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 0617981..b12e72fe 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -21,7 +21,6 @@
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/command_line.h"
 #include "base/i18n/time_formatting.h"
 #include "ui/base/ui_base_features.h"
@@ -189,11 +188,12 @@
 }
 
 TrayBackgroundView* StatusAreaWidget::GetSystemTrayAnchor() const {
-  if (Shell::Get()
-          ->tablet_mode_controller()
-          ->IsTabletModeWindowManagerEnabled()) {
+  // Use the target visibility of the layer instead of the visibility of the
+  // view because the view is still visible when fading away, but we do not want
+  // to anchor to this element in that case.
+  if (overview_button_tray_->layer()->GetTargetVisibility())
     return overview_button_tray_.get();
-  }
+
   if (unified_system_tray_)
     return unified_system_tray_.get();
   return system_tray_.get();
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc
index 235bb8d..6342b0e5 100644
--- a/base/files/important_file_writer.cc
+++ b/base/files/important_file_writer.cc
@@ -308,7 +308,7 @@
   serializer_ = nullptr;
 }
 
-void ImportantFileWriter::SetTimerForTesting(Timer* timer_override) {
+void ImportantFileWriter::SetTimerForTesting(OneShotTimer* timer_override) {
   timer_override_ = timer_override;
 }
 
diff --git a/base/files/important_file_writer.h b/base/files/important_file_writer.h
index 08a7ee34..f0cbfd22 100644
--- a/base/files/important_file_writer.h
+++ b/base/files/important_file_writer.h
@@ -114,14 +114,13 @@
   }
 
   // Overrides the timer to use for scheduling writes with |timer_override|.
-  void SetTimerForTesting(Timer* timer_override);
+  void SetTimerForTesting(OneShotTimer* timer_override);
 
  private:
-  const Timer& timer() const {
-    return timer_override_ ? const_cast<const Timer&>(*timer_override_)
-                           : timer_;
+  const OneShotTimer& timer() const {
+    return timer_override_ ? *timer_override_ : timer_;
   }
-  Timer& timer() { return timer_override_ ? *timer_override_ : timer_; }
+  OneShotTimer& timer() { return timer_override_ ? *timer_override_ : timer_; }
 
   void ClearPendingWrite();
 
@@ -139,7 +138,7 @@
   OneShotTimer timer_;
 
   // An override for |timer_| used for testing.
-  Timer* timer_override_ = nullptr;
+  OneShotTimer* timer_override_ = nullptr;
 
   // Serializer which will provide the data to be saved.
   DataSerializer* serializer_;
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index e1587ef0..6550f825 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -3208,23 +3208,32 @@
 
 namespace {
 
-void DoNothing() {}
-
-class PostTaskInDestructor {
+class RunOnDestructionHelper {
  public:
-  explicit PostTaskInDestructor(scoped_refptr<TaskQueue> task_queue)
-      : task_queue_(task_queue) {}
+  explicit RunOnDestructionHelper(base::OnceClosure task)
+      : task_(std::move(task)) {}
 
-  ~PostTaskInDestructor() {
-    task_queue_->PostTask(FROM_HERE, BindOnce(&DoNothing));
-  }
-
-  void Do() {}
+  ~RunOnDestructionHelper() { std::move(task_).Run(); }
 
  private:
-  scoped_refptr<TaskQueue> task_queue_;
+  base::OnceClosure task_;
 };
 
+base::OnceClosure RunOnDestruction(base::OnceClosure task) {
+  return base::BindOnce(
+      [](std::unique_ptr<RunOnDestructionHelper>) {},
+      base::Passed(std::make_unique<RunOnDestructionHelper>(std::move(task))));
+}
+
+base::OnceClosure PostOnDestructon(scoped_refptr<TestTaskQueue> task_queue,
+                                   base::OnceClosure task) {
+  return RunOnDestruction(base::BindOnce(
+      [](base::OnceClosure task, scoped_refptr<TestTaskQueue> task_queue) {
+        task_queue->PostTask(FROM_HERE, std::move(task));
+      },
+      base::Passed(std::move(task)), task_queue));
+}
+
 }  // namespace
 
 TEST_P(SequenceManagerTest, TaskQueueUsedInTaskDestructorAfterShutdown) {
@@ -3241,19 +3250,35 @@
 
   thread->task_runner()->PostTask(
       FROM_HERE, BindOnce(
-                     [](scoped_refptr<SingleThreadTaskRunner> task_queue,
-                        std::unique_ptr<PostTaskInDestructor> test_object,
+                     [](scoped_refptr<TestTaskQueue> task_queue,
                         WaitableEvent* test_executed) {
-                       task_queue->PostTask(FROM_HERE,
-                                            BindOnce(&PostTaskInDestructor::Do,
-                                                     std::move(test_object)));
+                       task_queue->PostTask(
+                           FROM_HERE, PostOnDestructon(
+                                          task_queue, base::BindOnce([]() {})));
                        test_executed->Signal();
                      },
-                     main_tq, std::make_unique<PostTaskInDestructor>(main_tq),
-                     &test_executed));
+                     main_tq, &test_executed));
   test_executed.Wait();
 }
 
+TEST_P(SequenceManagerTest, DestructorPostChainDuringShutdown) {
+  // Checks that a chain of closures which post other closures on destruction do
+  // thing on shutdown.
+  scoped_refptr<TestTaskQueue> task_queue = CreateTaskQueue();
+  bool run = false;
+  task_queue->PostTask(
+      FROM_HERE,
+      PostOnDestructon(
+          task_queue,
+          PostOnDestructon(task_queue,
+                           RunOnDestruction(base::BindOnce(
+                               [](bool* run) { *run = true; }, &run)))));
+
+  manager_.reset();
+
+  EXPECT_TRUE(run);
+}
+
 }  // namespace sequence_manager_impl_unittest
 }  // namespace internal
 }  // namespace sequence_manager
diff --git a/base/timer/mock_timer.h b/base/timer/mock_timer.h
index a2263d4..30a605e9 100644
--- a/base/timer/mock_timer.h
+++ b/base/timer/mock_timer.h
@@ -12,7 +12,7 @@
 
 class TestSimpleTaskRunner;
 
-// A mock implementation of base::Timer which requires being explicitly
+// A mock implementation of base::OneShotTimer which requires being explicitly
 // Fire()'d.
 // Prefer using ScopedTaskEnvironment::MOCK_TIME + FastForward*() to this when
 // possible.
diff --git a/base/timer/timer.cc b/base/timer/timer.cc
index 0c26971..aa2e0d4 100644
--- a/base/timer/timer.cc
+++ b/base/timer/timer.cc
@@ -16,6 +16,7 @@
 #include "base/time/tick_clock.h"
 
 namespace base {
+namespace internal {
 
 // BaseTimerTaskInternal is a simple delegate for scheduling a callback to Timer
 // on the current sequence. It also handles the following edge cases:
@@ -23,9 +24,7 @@
 // - abandoned (orphaned) by Timer.
 class BaseTimerTaskInternal {
  public:
-  explicit BaseTimerTaskInternal(Timer* timer)
-      : timer_(timer) {
-  }
+  explicit BaseTimerTaskInternal(TimerBase* timer) : timer_(timer) {}
 
   ~BaseTimerTaskInternal() {
     // This task may be getting cleared because the task runner has been
@@ -45,7 +44,7 @@
 
     // Although Timer should not call back into |this|, let's clear |timer_|
     // first to be pedantic.
-    Timer* timer = timer_;
+    TimerBase* timer = timer_;
     timer_ = nullptr;
     timer->RunScheduledTask();
   }
@@ -54,17 +53,17 @@
   void Abandon() { timer_ = nullptr; }
 
  private:
-  Timer* timer_;
+  TimerBase* timer_;
 
   DISALLOW_COPY_AND_ASSIGN(BaseTimerTaskInternal);
 };
 
-Timer::Timer(bool retain_user_task, bool is_repeating)
-    : Timer(retain_user_task, is_repeating, nullptr) {}
+TimerBase::TimerBase(bool retain_user_task, bool is_repeating)
+    : TimerBase(retain_user_task, is_repeating, nullptr) {}
 
-Timer::Timer(bool retain_user_task,
-             bool is_repeating,
-             const TickClock* tick_clock)
+TimerBase::TimerBase(bool retain_user_task,
+                     bool is_repeating,
+                     const TickClock* tick_clock)
     : scheduled_task_(nullptr),
       is_repeating_(is_repeating),
       retain_user_task_(retain_user_task),
@@ -77,17 +76,17 @@
   origin_sequence_checker_.DetachFromSequence();
 }
 
-Timer::Timer(const Location& posted_from,
-             TimeDelta delay,
-             const base::Closure& user_task,
-             bool is_repeating)
-    : Timer(posted_from, delay, user_task, is_repeating, nullptr) {}
+TimerBase::TimerBase(const Location& posted_from,
+                     TimeDelta delay,
+                     const base::Closure& user_task,
+                     bool is_repeating)
+    : TimerBase(posted_from, delay, user_task, is_repeating, nullptr) {}
 
-Timer::Timer(const Location& posted_from,
-             TimeDelta delay,
-             const base::Closure& user_task,
-             bool is_repeating,
-             const TickClock* tick_clock)
+TimerBase::TimerBase(const Location& posted_from,
+                     TimeDelta delay,
+                     const base::Closure& user_task,
+                     bool is_repeating,
+                     const TickClock* tick_clock)
     : scheduled_task_(nullptr),
       posted_from_(posted_from),
       delay_(delay),
@@ -100,22 +99,22 @@
   origin_sequence_checker_.DetachFromSequence();
 }
 
-Timer::~Timer() {
+TimerBase::~TimerBase() {
   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
   AbandonAndStop();
 }
 
-bool Timer::IsRunning() const {
+bool TimerBase::IsRunning() const {
   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
   return is_running_;
 }
 
-TimeDelta Timer::GetCurrentDelay() const {
+TimeDelta TimerBase::GetCurrentDelay() const {
   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
   return delay_;
 }
 
-void Timer::SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) {
+void TimerBase::SetTaskRunner(scoped_refptr<SequencedTaskRunner> task_runner) {
   // Do not allow changing the task runner when the Timer is running.
   // Don't check for |origin_sequence_checker_.CalledOnValidSequence()| here to
   // allow the use case of constructing the Timer and immediatetly invoking
@@ -127,9 +126,9 @@
   task_runner_.swap(task_runner);
 }
 
-void Timer::Start(const Location& posted_from,
-                  TimeDelta delay,
-                  const base::Closure& user_task) {
+void TimerBase::Start(const Location& posted_from,
+                      TimeDelta delay,
+                      const base::Closure& user_task) {
   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
 
   posted_from_ = posted_from;
@@ -139,7 +138,7 @@
   Reset();
 }
 
-void Timer::Stop() {
+void TimerBase::Stop() {
   // TODO(gab): Enable this when it's no longer called racily from
   // RunScheduledTask(): https://crbug.com/587199.
   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
@@ -155,7 +154,7 @@
   // |user_task_|.
 }
 
-void Timer::Reset() {
+void TimerBase::Reset() {
   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
   DCHECK(!user_task_.is_null());
 
@@ -183,14 +182,14 @@
   PostNewScheduledTask(delay_);
 }
 
-TimeTicks Timer::Now() const {
+TimeTicks TimerBase::Now() const {
   // TODO(gab): Enable this when it's no longer called racily from
   // RunScheduledTask(): https://crbug.com/587199.
   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
   return tick_clock_ ? tick_clock_->NowTicks() : TimeTicks::Now();
 }
 
-void Timer::PostNewScheduledTask(TimeDelta delay) {
+void TimerBase::PostNewScheduledTask(TimeDelta delay) {
   // TODO(gab): Enable this when it's no longer called racily from
   // RunScheduledTask(): https://crbug.com/587199.
   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
@@ -214,11 +213,11 @@
   }
 }
 
-scoped_refptr<SequencedTaskRunner> Timer::GetTaskRunner() {
+scoped_refptr<SequencedTaskRunner> TimerBase::GetTaskRunner() {
   return task_runner_.get() ? task_runner_ : SequencedTaskRunnerHandle::Get();
 }
 
-void Timer::AbandonScheduledTask() {
+void TimerBase::AbandonScheduledTask() {
   // TODO(gab): Enable this when it's no longer called racily from
   // RunScheduledTask() -> Stop(): https://crbug.com/587199.
   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
@@ -228,7 +227,7 @@
   }
 }
 
-void Timer::RunScheduledTask() {
+void TimerBase::RunScheduledTask() {
   // TODO(gab): Enable this when it's no longer called racily:
   // https://crbug.com/587199.
   // DCHECK(origin_sequence_checker_.CalledOnValidSequence());
@@ -265,6 +264,8 @@
   // No more member accesses here: |this| could be deleted at this point.
 }
 
+}  // namespace internal
+
 void OneShotTimer::FireNow() {
   DCHECK(origin_sequence_checker_.CalledOnValidSequence());
   DCHECK(!task_runner_) << "FireNow() is incompatible with SetTaskRunner()";
diff --git a/base/timer/timer.h b/base/timer/timer.h
index 14ba3e3..3f666b6 100644
--- a/base/timer/timer.h
+++ b/base/timer/timer.h
@@ -2,16 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// OneShotTimer and RepeatingTimer provide a simple timer API.  As the names
-// suggest, OneShotTimer calls you back once after a time delay expires.
+// OneShotTimer, RepeatingTimer and RetainingOneShotTimer provide a simple timer
+// API.  As the names suggest, OneShotTimer calls you back once after a time
+// delay expires.
 // RepeatingTimer on the other hand calls you back periodically with the
 // prescribed time interval.
+// RetainingOneShotTimer doesn't repeat the task itself like OneShotTimer, but
+// retains the given task after the time out. You can restart it with Reset
+// again without giving new task to Start.
 //
-// OneShotTimer and RepeatingTimer both cancel the timer when they go out of
-// scope, which makes it easy to ensure that you do not get called when your
-// object has gone out of scope.  Just instantiate a OneShotTimer or
-// RepeatingTimer as a member variable of the class for which you wish to
-// receive timer events.
+// All of OneShotTimer, RepeatingTimer and RetainingOneShotTimer cancel the
+// timer when they go out of scope, which makes it easy to ensure that you do
+// not get called when your object has gone out of scope.  Just instantiate a
+// timer as a member variable of the class for which you wish to receive timer
+// events.
 //
 // Sample RepeatingTimer usage:
 //
@@ -32,12 +36,11 @@
 //     base::RepeatingTimer timer_;
 //   };
 //
-// Both OneShotTimer and RepeatingTimer also support a Reset method, which
-// allows you to easily defer the timer event until the timer delay passes once
-// again.  So, in the above example, if 0.5 seconds have already passed,
-// calling Reset on |timer_| would postpone DoStuff by another 1 second.  In
-// other words, Reset is shorthand for calling Stop and then Start again with
-// the same arguments.
+// Timers also support a Reset method, which allows you to easily defer the
+// timer event until the timer delay passes once again.  So, in the above
+// example, if 0.5 seconds have already passed, calling Reset on |timer_|
+// would postpone DoStuff by another 1 second.  In other words, Reset is
+// shorthand for calling Stop and then Start again with the same arguments.
 //
 // These APIs are not thread safe. All methods must be called from the same
 // sequence (not necessarily the construction sequence), except for the
@@ -75,35 +78,42 @@
 
 namespace base {
 
-class BaseTimerTaskInternal;
 class TickClock;
 
+namespace internal {
+
+class BaseTimerTaskInternal;
+
 //-----------------------------------------------------------------------------
 // This class wraps TaskRunner::PostDelayedTask to manage delayed and repeating
 // tasks. See meta comment above for thread-safety requirements.
+// Do not use this class directly. Use one of OneShotTimer, RepeatingTimer or
+// RetainingOneShotTimer.
 //
-class BASE_EXPORT Timer {
+class BASE_EXPORT TimerBase {
  public:
   // Construct a timer in repeating or one-shot mode. Start must be called later
   // to set task info. |retain_user_task| determines whether the user_task is
   // retained or reset when it runs or stops. If |tick_clock| is provided, it is
   // used instead of TimeTicks::Now() to get TimeTicks when scheduling tasks.
-  Timer(bool retain_user_task, bool is_repeating);
-  Timer(bool retain_user_task, bool is_repeating, const TickClock* tick_clock);
+  TimerBase(bool retain_user_task, bool is_repeating);
+  TimerBase(bool retain_user_task,
+            bool is_repeating,
+            const TickClock* tick_clock);
 
   // Construct a timer with retained task info. If |tick_clock| is provided, it
   // is used instead of TimeTicks::Now() to get TimeTicks when scheduling tasks.
-  Timer(const Location& posted_from,
-        TimeDelta delay,
-        const base::Closure& user_task,
-        bool is_repeating);
-  Timer(const Location& posted_from,
-        TimeDelta delay,
-        const base::Closure& user_task,
-        bool is_repeating,
-        const TickClock* tick_clock);
+  TimerBase(const Location& posted_from,
+            TimeDelta delay,
+            const base::Closure& user_task,
+            bool is_repeating);
+  TimerBase(const Location& posted_from,
+            TimeDelta delay,
+            const base::Closure& user_task,
+            bool is_repeating,
+            const TickClock* tick_clock);
 
-  virtual ~Timer();
+  virtual ~TimerBase();
 
   // Returns true if the timer is running (i.e., not stopped).
   bool IsRunning() const;
@@ -231,16 +241,18 @@
   // If true, |user_task_| is scheduled to run sometime in the future.
   bool is_running_;
 
-  DISALLOW_COPY_AND_ASSIGN(Timer);
+  DISALLOW_COPY_AND_ASSIGN(TimerBase);
 };
 
+}  // namespace internal
+
 //-----------------------------------------------------------------------------
 // A simple, one-shot timer.  See usage notes at the top of the file.
-class BASE_EXPORT OneShotTimer : public Timer {
+class BASE_EXPORT OneShotTimer : public internal::TimerBase {
  public:
   OneShotTimer() : OneShotTimer(nullptr) {}
   explicit OneShotTimer(const TickClock* tick_clock)
-      : Timer(false, false, tick_clock) {}
+      : internal::TimerBase(false, false, tick_clock) {}
 
   // Run the scheduled task immediately, and stop the timer. The timer needs to
   // be running.
@@ -249,41 +261,49 @@
 
 //-----------------------------------------------------------------------------
 // A simple, repeating timer.  See usage notes at the top of the file.
-class RepeatingTimer : public Timer {
+class RepeatingTimer : public internal::TimerBase {
  public:
   RepeatingTimer() : RepeatingTimer(nullptr) {}
   explicit RepeatingTimer(const TickClock* tick_clock)
-      : Timer(true, true, tick_clock) {}
+      : internal::TimerBase(true, true, tick_clock) {}
 
   RepeatingTimer(const Location& posted_from,
                  TimeDelta delay,
                  RepeatingClosure user_task)
-      : Timer(posted_from, delay, std::move(user_task), true) {}
+      : internal::TimerBase(posted_from, delay, std::move(user_task), true) {}
   RepeatingTimer(const Location& posted_from,
                  TimeDelta delay,
                  RepeatingClosure user_task,
                  const TickClock* tick_clock)
-      : Timer(posted_from, delay, std::move(user_task), true, tick_clock) {}
+      : internal::TimerBase(posted_from,
+                            delay,
+                            std::move(user_task),
+                            true,
+                            tick_clock) {}
 };
 
 //-----------------------------------------------------------------------------
 // A simple, one-shot timer with the retained user task.  See usage notes at the
 // top of the file.
-class RetainingOneShotTimer : public Timer {
+class RetainingOneShotTimer : public internal::TimerBase {
  public:
   RetainingOneShotTimer() : RetainingOneShotTimer(nullptr) {}
   explicit RetainingOneShotTimer(const TickClock* tick_clock)
-      : Timer(true, false, tick_clock) {}
+      : internal::TimerBase(true, false, tick_clock) {}
 
   RetainingOneShotTimer(const Location& posted_from,
                         TimeDelta delay,
                         RepeatingClosure user_task)
-      : Timer(posted_from, delay, std::move(user_task), false) {}
+      : internal::TimerBase(posted_from, delay, std::move(user_task), false) {}
   RetainingOneShotTimer(const Location& posted_from,
                         TimeDelta delay,
                         RepeatingClosure user_task,
                         const TickClock* tick_clock)
-      : Timer(posted_from, delay, std::move(user_task), false, tick_clock) {}
+      : internal::TimerBase(posted_from,
+                            delay,
+                            std::move(user_task),
+                            false,
+                            tick_clock) {}
 };
 
 //-----------------------------------------------------------------------------
diff --git a/base/timer/timer_unittest.cc b/base/timer/timer_unittest.cc
index aaab237..3ebf301 100644
--- a/base/timer/timer_unittest.cc
+++ b/base/timer/timer_unittest.cc
@@ -603,7 +603,7 @@
 TEST(TimerTest, NonRepeatIsRunning) {
   {
     MessageLoop loop;
-    Timer timer(false, false);
+    OneShotTimer timer;
     EXPECT_FALSE(timer.IsRunning());
     timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback));
     EXPECT_TRUE(timer.IsRunning());
@@ -613,7 +613,7 @@
   }
 
   {
-    Timer timer(true, false);
+    RetainingOneShotTimer timer;
     MessageLoop loop;
     EXPECT_FALSE(timer.IsRunning());
     timer.Start(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback));
@@ -627,7 +627,7 @@
 }
 
 TEST(TimerTest, NonRepeatMessageLoopDeath) {
-  Timer timer(false, false);
+  OneShotTimer timer;
   {
     MessageLoop loop;
     EXPECT_FALSE(timer.IsRunning());
@@ -640,8 +640,8 @@
 
 TEST(TimerTest, RetainRepeatIsRunning) {
   MessageLoop loop;
-  Timer timer(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback),
-              true);
+  RepeatingTimer timer(FROM_HERE, TimeDelta::FromDays(1),
+                       Bind(&TimerTestCallback));
   EXPECT_FALSE(timer.IsRunning());
   timer.Reset();
   EXPECT_TRUE(timer.IsRunning());
@@ -653,8 +653,8 @@
 
 TEST(TimerTest, RetainNonRepeatIsRunning) {
   MessageLoop loop;
-  Timer timer(FROM_HERE, TimeDelta::FromDays(1), Bind(&TimerTestCallback),
-              false);
+  RetainingOneShotTimer timer(FROM_HERE, TimeDelta::FromDays(1),
+                              Bind(&TimerTestCallback));
   EXPECT_FALSE(timer.IsRunning());
   timer.Reset();
   EXPECT_TRUE(timer.IsRunning());
@@ -692,7 +692,7 @@
   {
     ClearAllCallbackHappened();
     MessageLoop loop;
-    Timer timer(false, false);
+    OneShotTimer timer;
     timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
                 Bind(&SetCallbackHappened1));
     timer.Stop();
@@ -708,7 +708,7 @@
   {
     ClearAllCallbackHappened();
     MessageLoop loop;
-    Timer timer(false, false);
+    OneShotTimer timer;
     timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
                 Bind(&SetCallbackHappened1));
     timer.Reset();
diff --git a/build/config/mips.gni b/build/config/mips.gni
index 28194a4..6365088b 100644
--- a/build/config/mips.gni
+++ b/build/config/mips.gni
@@ -8,6 +8,11 @@
 # MIPS code is being compiled.  But they can also be relevant in the
 # other contexts when the code will change its behavior based on the
 # cpu it wants to generate code for.
+declare_args() {
+  # MIPS MultiMedia Instruction compilation flag.
+  mips_use_mmi = false
+}
+
 if (current_cpu == "mipsel" || v8_current_cpu == "mipsel" ||
     current_cpu == "mips" || v8_current_cpu == "mips") {
   declare_args() {
diff --git a/build/fuchsia/sdk.sha1 b/build/fuchsia/sdk.sha1
index f440306..980afd99 100644
--- a/build/fuchsia/sdk.sha1
+++ b/build/fuchsia/sdk.sha1
@@ -1 +1 @@
-7524a90f78ba1ba27c4b9ab38c3bf418afd210ef
+138f39e8ec632173fa8d82466242bf36032cdcce
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
index b46ccc5..48765f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java
@@ -102,6 +102,10 @@
         return tabViewBinder;
     }
 
+    public void closeActiveTab() {
+        mMediator.closeActiveTab();
+    }
+
     /**
      * Called by the {@link LazyViewBinderAdapter} as soon as the view is inflated so it can be
      * initialized. This call happens before the {@link KeyboardAccessoryViewBinder} is called for
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
index 0c649fd..9ec6d078 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java
@@ -91,6 +91,11 @@
         updateVisibility();
     }
 
+    void closeActiveTab() {
+        mModel.setActiveTab(null);
+        mVisibilityDelegate.onCloseAccessorySheet();
+    }
+
     @VisibleForTesting
     KeyboardAccessoryModel getModelForTesting() {
         return mModel;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
index 06ca190..fbc6958 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
@@ -62,6 +62,10 @@
         mMediator.registerPasswordProvider(itemProvider);
     }
 
+    public void closeAccessorySheet() {
+        mMediator.getKeyboardAccessory().closeActiveTab();
+    }
+
     // TODO(fhorschig): Should be @VisibleForTesting.
     /**
      * Allows access to the keyboard accessory. This can be used to explicitly modify the the bar of
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java
index ddc1283..bfd604b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryBridge.java
@@ -72,6 +72,11 @@
     }
 
     @CalledByNative
+    private void closeAccessorySheet() {
+        mManualFillingCoordinator.closeAccessorySheet();
+    }
+
+    @CalledByNative
     private void destroy() {
         mItemProvider.notifyObservers(new Item[] {}); // There are no more items available!
         mNativeView = 0;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java
index 42c90cd..26a0c73 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterModel.java
@@ -19,6 +19,8 @@
         static final PropertyKey CONTENT_VIEW = new PropertyKey();
         static final PropertyKey SELECTED_TAB = new PropertyKey();
         static final PropertyKey CHANGE_LISTENER = new PropertyKey();
+
+        private PropertyKey() {}
     }
 
     private View mContentView;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
index 7c2e955..d1b7f5c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/home/filter/Filters.java
@@ -104,4 +104,6 @@
 
         return filter;
     }
+
+    private Filters() {}
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
index 9942891..8eb6aa9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkInfo.java
@@ -9,10 +9,11 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
 import android.os.Bundle;
 import android.text.TextUtils;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.blink_public.platform.WebDisplayMode;
@@ -139,10 +140,10 @@
         Map<String, String> iconUrlToMurmur2HashMap = getIconUrlAndIconMurmur2HashMap(bundle);
 
         int primaryIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.ICON_ID, 0);
-        Bitmap primaryIcon = decodeImageResource(res, primaryIconId);
+        Bitmap primaryIcon = decodeBitmapFromDrawable(res, primaryIconId);
 
         int badgeIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.BADGE_ICON_ID, 0);
-        Bitmap badgeIcon = decodeImageResource(res, badgeIconId);
+        Bitmap badgeIcon = decodeBitmapFromDrawable(res, badgeIconId);
 
         return create(WebApkConstants.WEBAPK_ID_PREFIX + webApkPackageName, url, scope,
                 new Icon(primaryIcon), new Icon(badgeIcon), name, shortName, displayMode,
@@ -276,10 +277,19 @@
     }
 
     /**
-     * Decodes bitmap from WebAPK's resources.
+     * Decodes bitmap drawable from WebAPK's resources. This should also be used for XML aliases.
      */
-    private static Bitmap decodeImageResource(Resources webApkResources, int resourceId) {
-        return BitmapFactory.decodeResource(webApkResources, resourceId);
+    private static Bitmap decodeBitmapFromDrawable(Resources webApkResources, int resourceId) {
+        if (resourceId == 0) {
+            return null;
+        }
+        try {
+            BitmapDrawable bitmapDrawable =
+                    (BitmapDrawable) ApiCompatibilityUtils.getDrawable(webApkResources, resourceId);
+            return bitmapDrawable != null ? bitmapDrawable.getBitmap() : null;
+        } catch (Resources.NotFoundException e) {
+            return null;
+        }
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java
index a200179..8cdf905 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/AutofillKeyboardAccessoryIntegrationTest.java
@@ -111,6 +111,10 @@
     @Test
     @MediumTest
     @Feature({"keyboard-accessory"})
+    @DisabledTest(message = "crbug.com/854224")
+    // TODO(fhorschig): Figure out why this test exists. If a keyboard is shown, the accessory
+    // should be there. If there is no keyboard, there shouldn't be an accessory. Looks more like a
+    // keyboard test than an accessory test.
     public void testAutofocusedFieldDoesNotShowKeyboardAccessory()
             throws ExecutionException, InterruptedException, TimeoutException {
         loadTestPage(false);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/EmulatedVrController.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/EmulatedVrController.java
index 59e7e29e..8e24809 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/EmulatedVrController.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/EmulatedVrController.java
@@ -34,6 +34,17 @@
         return mApi;
     }
 
+    /**
+     * Touch and release the touchpad to perform a controller click.
+     */
+    public void performControllerClick() {
+        // pressReleaseTouchpadButton() appears to be flaky for clicking on things, as sometimes
+        // it happens too fast for Chrome to register. So, manually press and release with a delay
+        sendClickButtonToggleEvent();
+        SystemClock.sleep(50);
+        sendClickButtonToggleEvent();
+    }
+
     public void sendClickButtonToggleEvent() {
         mApi.buttonEvent.sendClickButtonToggleEvent();
     }
@@ -78,30 +89,6 @@
     }
 
     /**
-     * Simulates a touch down-drag-touch up sequence on the touchpad between two points.
-     *
-     * @param xStart the x coordinate to start the touch sequence at, in range [0.0f, 1.0f]
-     * @param yStart the y coordinate to start the touch sequence at, in range [0.0f, 1.0f]
-     * @param xEnd the x coordinate to end the touch sequence at, in range [0.0f, 1.0f]
-     * @param yEnd the y coordinate to end the touch sequence at, in range [0.0f, 1.0f]
-     * @param steps the number of steps the drag will have
-     * @param speed how long to wait between steps in the sequence. Generally, higher numbers
-     * result in faster movement, e.g. when used for scrolling, a higher number results in faster
-     * scrolling.
-     */
-    public void performLinearTouchpadMovement(
-            float xStart, float yStart, float xEnd, float yEnd, int steps, int speed) {
-        // Touchpad events have timestamps attached to them in nanoseconds - for smooth scrolling,
-        // the timestamps should increase at a similar rate to the amount of time we actually wait
-        // between sending events, which is determined by the given speed.
-        long simulatedDelay = TimeUnit.MILLISECONDS.toNanos(speed);
-        long timestamp = mApi.touchEvent.startTouchSequence(xStart, yStart, simulatedDelay, speed);
-        timestamp = mApi.touchEvent.dragFromTo(
-                xStart, yStart, xEnd, yEnd, steps, timestamp, simulatedDelay, speed);
-        mApi.touchEvent.endTouchSequence(xEnd, yEnd, timestamp, simulatedDelay, speed);
-    }
-
-    /**
      * Performs an swipe on the touchpad in order to scroll in the specified
      * direction while in the VR browser.
      * Note that scrolling this way is not consistent, i.e. scrolling down then
@@ -153,6 +140,30 @@
     }
 
     /**
+     * Simulates a touch down-drag-touch up sequence on the touchpad between two points.
+     *
+     * @param xStart the x coordinate to start the touch sequence at, in range [0.0f, 1.0f]
+     * @param yStart the y coordinate to start the touch sequence at, in range [0.0f, 1.0f]
+     * @param xEnd the x coordinate to end the touch sequence at, in range [0.0f, 1.0f]
+     * @param yEnd the y coordinate to end the touch sequence at, in range [0.0f, 1.0f]
+     * @param steps the number of steps the drag will have
+     * @param speed how long to wait between steps in the sequence. Generally, higher numbers
+     * result in faster movement, e.g. when used for scrolling, a higher number results in faster
+     * scrolling.
+     */
+    public void performLinearTouchpadMovement(
+            float xStart, float yStart, float xEnd, float yEnd, int steps, int speed) {
+        // Touchpad events have timestamps attached to them in nanoseconds - for smooth scrolling,
+        // the timestamps should increase at a similar rate to the amount of time we actually wait
+        // between sending events, which is determined by the given speed.
+        long simulatedDelay = TimeUnit.MILLISECONDS.toNanos(speed);
+        long timestamp = mApi.touchEvent.startTouchSequence(xStart, yStart, simulatedDelay, speed);
+        timestamp = mApi.touchEvent.dragFromTo(
+                xStart, yStart, xEnd, yEnd, steps, timestamp, simulatedDelay, speed);
+        mApi.touchEvent.endTouchSequence(xEnd, yEnd, timestamp, simulatedDelay, speed);
+    }
+
+    /**
      * Instantly moves the controller to the specified quaternion coordinates.
      *
      * @param x the x component of the quaternion
@@ -181,17 +192,4 @@
                 new float[] {startAngles[1], endAngles[1]},
                 new float[] {startAngles[2], endAngles[2]}, steps, delayBetweenSteps);
     }
-
-    /**
-     * Touch and release the touchpad to perform a controller click.
-     */
-    public void performControllerClick() {
-        // pressReleaseTouchpadButton() appears to be flaky for clicking on things, as sometimes
-        // it happens too fast for Chrome to register. So, manually press and release with a delay
-        sendClickButtonToggleEvent();
-        SystemClock.sleep(50);
-        sendClickButtonToggleEvent();
-    }
-
-    // TODO(bsheedy): Add support for more complex actions, e.g. click/drag/release
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/TestVrShellDelegate.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/TestVrShellDelegate.java
index a79186f..ebb8218 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/TestVrShellDelegate.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/TestVrShellDelegate.java
@@ -23,10 +23,6 @@
     private boolean mExpectingIntent;
     private Boolean mAllow2dIntents;
 
-    protected TestVrShellDelegate(ChromeActivity activity) {
-        super(activity);
-    }
-
     public static void createTestVrShellDelegate(final ChromeActivity activity) {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
@@ -40,12 +36,16 @@
         return sInstance;
     }
 
+    public static VrShell getVrShellForTesting() {
+        return TestVrShellDelegate.getInstance().getVrShell();
+    }
+
     public static boolean isDisplayingUrlForTesting() {
         return TestVrShellDelegate.getInstance().getVrShell().isDisplayingUrlForTesting();
     }
 
-    public static VrShell getVrShellForTesting() {
-        return TestVrShellDelegate.getInstance().getVrShell();
+    protected TestVrShellDelegate(ChromeActivity activity) {
+        super(activity);
     }
 
     public void overrideDaydreamApiForTesting(VrDaydreamApi api) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java
index 4590060a..876334f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserControllerInputTest.java
@@ -56,8 +56,7 @@
     @Before
     public void setUp() throws Exception {
         mVrBrowserTestFramework = new VrBrowserTestFramework(mVrTestRule);
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         mController = new EmulatedVrController(mVrTestRule.getActivity());
         mController.recenterView();
     }
@@ -208,8 +207,7 @@
     @Test
     @MediumTest
     public void testControllerScrollingNative() throws InterruptedException {
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         // Fill history with enough items to scroll
         mVrTestRule.loadUrl(
                 VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"),
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java
index d106a96..ed88891 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserDialogTest.java
@@ -107,8 +107,7 @@
                 PAGE_LOAD_TIMEOUT_S);
 
         // Display the given permission prompt.
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         mVrBrowserTestFramework.runJavaScriptOrFail(navigationCommand, POLL_TIMEOUT_SHORT_MS);
         VrBrowserTransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS);
 
@@ -123,8 +122,7 @@
                 VrBrowserTestFramework.getFileUrlForHtmlTestFile(initialPage), PAGE_LOAD_TIMEOUT_S);
 
         // Display the JavaScript dialog.
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         // We can't use runJavaScriptOrFail here because JavaScript execution is blocked while a
         // JS dialog is visible, so runJavaScriptOrFail will always time out.
         JavaScriptUtils.executeJavaScript(
@@ -140,8 +138,7 @@
             throws InterruptedException, TimeoutException {
         mVrBrowserTestFramework.loadUrlAndAwaitInitialization(
                 VrBrowserTestFramework.getFileUrlForHtmlTestFile(initialPage), PAGE_LOAD_TIMEOUT_S);
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         Thread.sleep(VR_ENTRY_SLEEP_MS);
         NativeUiUtils.clickElementAndWaitForUiQuiescence(elementName, new PointF(0, 0));
         // Technically not necessary, but clicking on native elements causes the laser to originate
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java
index 66d0a73..9c254d8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNativeUiTest.java
@@ -43,8 +43,7 @@
 
     @Before
     public void setUp() throws Exception {
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java
index 1b489db..df37326 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserNavigationTest.java
@@ -76,8 +76,7 @@
         mWebXrVrTestFramework = new WebXrVrTestFramework(mTestRule);
         mWebVrTestFramework = new WebVrTestFramework(mTestRule);
         mVrBrowserTestFramework = new VrBrowserTestFramework(mTestRule);
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
     }
 
     private String getUrl(Page page) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
index 8e7654c..4c134a6a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserTransitionTest.java
@@ -206,8 +206,7 @@
     @MediumTest
     public void testExitFullscreenAfterExitingVrFromCinemaMode()
             throws InterruptedException, TimeoutException {
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         mVrBrowserTestFramework.loadUrlAndAwaitInitialization(
                 VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_navigation_2d_page"),
                 PAGE_LOAD_TIMEOUT_S);
@@ -263,8 +262,7 @@
 
     private void exitPresentationToVrShellImpl(String url, WebXrVrTestFramework framework,
             String exitPresentString) throws InterruptedException {
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S);
         VrShellImpl vrShellImpl = (VrShellImpl) TestVrShellDelegate.getVrShellForTesting();
         float expectedWidth = vrShellImpl.getContentWidthForTesting();
@@ -313,8 +311,7 @@
 
     private void reEntryFromVrBrowserImpl(String url, WebXrVrTestFramework framework)
             throws InterruptedException {
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         EmulatedVrController controller = new EmulatedVrController(mTestRule.getActivity());
 
         framework.loadUrlAndAwaitInitialization(url, PAGE_LOAD_TIMEOUT_S);
@@ -352,9 +349,7 @@
 
         MockVrDaydreamApi mockApi = new MockVrDaydreamApi();
         VrShellDelegateUtils.getDelegateInstance().overrideDaydreamApiForTesting(mockApi);
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
-        Assert.assertTrue(VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete());
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         Assert.assertFalse(mockApi.getExitFromVrCalled());
         Assert.assertFalse(mockApi.getLaunchVrHomescreenCalled());
         VrShellDelegateUtils.getDelegateInstance().overrideDaydreamApiForTesting(null);
@@ -385,9 +380,7 @@
 
     private void testStartActivityTriggersDoffImpl(Context context)
             throws InterruptedException, TimeoutException {
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
-        Assert.assertTrue(VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete());
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
 
         MockVrDaydreamApi mockApi = new MockVrDaydreamApi();
         mockApi.setExitFromVrReturnValue(false);
@@ -436,9 +429,7 @@
     @MediumTest
     public void testStartActivityIfNeeded() throws InterruptedException, TimeoutException {
         Activity context = mTestRule.getActivity();
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
-        Assert.assertTrue(VrShellDelegateUtils.getDelegateInstance().isVrEntryComplete());
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
 
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
@@ -464,8 +455,7 @@
                 PAGE_LOAD_TIMEOUT_S);
 
         // Test JavaScript dialogs.
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         // Alerts block JavaScript execution until they're closed, so we can't use the normal
         // runJavaScriptOrFail, as that will time out.
         JavaScriptUtils.executeJavaScript(
@@ -474,8 +464,7 @@
         VrBrowserTransitionUtils.forceExitVr();
 
         // Test permission prompts.
-        Assert.assertTrue(VrBrowserTransitionUtils.forceEnterVrBrowser());
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         mVrBrowserTestFramework.runJavaScriptOrFail(
                 "navigator.getUserMedia({video: true}, ()=>{}, ()=>{})", POLL_TIMEOUT_SHORT_MS);
         VrBrowserTransitionUtils.waitForNativeUiPrompt(POLL_TIMEOUT_LONG_MS);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java
index c4974df0..20036048 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrBrowserWebInputEditingTest.java
@@ -63,8 +63,7 @@
         mVrTestRule.loadUrl(
                 VrBrowserTestFramework.getFileUrlForHtmlTestFile("test_web_input_editing"),
                 PAGE_LOAD_TIMEOUT_S);
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
 
         VrShellImpl vrShellImpl = (VrShellImpl) TestVrShellDelegate.getVrShellForTesting();
         MockBrowserKeyboardInterface keyboard = new MockBrowserKeyboardInterface();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java
index b3e7087..7d876a8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/VrFeedbackInfoBarTest.java
@@ -70,8 +70,7 @@
     }
 
     private void enterThenExitVr() {
-        VrBrowserTransitionUtils.forceEnterVrBrowser();
-        VrBrowserTransitionUtils.waitForVrEntry(POLL_TIMEOUT_LONG_MS);
+        VrBrowserTransitionUtils.forceEnterVrBrowserOrFail(POLL_TIMEOUT_LONG_MS);
         assertState(true /* isInVr */, false /* isInfobarVisible  */);
         VrBrowserTransitionUtils.forceExitVr();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java
index f0a69d5c..cd77b09 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArTestFramework.java
@@ -20,6 +20,21 @@
  */
 public class WebXrArTestFramework extends WebXrTestFramework {
     /**
+     * Checks whether an AR session request would prompt the user for Camera permissions.
+     * @param webContents The WebContents to check for the permission in
+     * @return True if an AR session request will cause a permission prompt, false otherwise.
+     */
+    public static boolean arSessionRequestWouldTriggerPermissionPrompt(WebContents webContents) {
+        runJavaScriptOrFail("checkIfArSessionWouldTriggerPermissionPrompt()", POLL_TIMEOUT_SHORT_MS,
+                webContents);
+        Assert.assertTrue(
+                pollJavaScriptBoolean("arSessionRequestWouldTriggerPermissionPrompt !== null",
+                        POLL_TIMEOUT_SHORT_MS, webContents));
+        return Boolean.valueOf(runJavaScriptOrFail("arSessionRequestWouldTriggerPermissionPrompt",
+                POLL_TIMEOUT_SHORT_MS, webContents));
+    }
+
+    /**
      * Must be constructed after the rule has been applied (e.g. in whatever method is
      * tagged with @Before)
      */
@@ -65,21 +80,6 @@
     }
 
     /**
-     * Checks whether an AR session request would prompt the user for Camera permissions.
-     * @param webContents The WebContents to check for the permission in
-     * @return True if an AR session request will cause a permission prompt, false otherwise.
-     */
-    public static boolean arSessionRequestWouldTriggerPermissionPrompt(WebContents webContents) {
-        runJavaScriptOrFail("checkIfArSessionWouldTriggerPermissionPrompt()", POLL_TIMEOUT_SHORT_MS,
-                webContents);
-        Assert.assertTrue(
-                pollJavaScriptBoolean("arSessionRequestWouldTriggerPermissionPrompt !== null",
-                        POLL_TIMEOUT_SHORT_MS, webContents));
-        return Boolean.valueOf(runJavaScriptOrFail("arSessionRequestWouldTriggerPermissionPrompt",
-                POLL_TIMEOUT_SHORT_MS, webContents));
-    }
-
-    /**
      * Helper function to run arSessionRequestWouldTriggerPermissionPrompt with the first tab's
      * WebContents.
      * @return True if an AR session request will cause a permission prompt, false otherwise
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java
index 6cc8c27..826e08d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrTestFramework.java
@@ -25,7 +25,9 @@
     }
 
     /**
-     * Checks whether an XRDevice was actually found.
+     * Checks whether an XRDevice was actually found. Needs to be non-static despite not using any
+     * member variables in order for the WebContents-less helper version to work properly in
+     * subclasses.
      * @param webContents The WebContents to run the JavaScript through.
      * @return Whether an XRDevice was found.
      */
@@ -42,7 +44,9 @@
     }
 
     /**
-     * Enters a WebXR or WebVR session of some kind by tapping on the canvas on the page.
+     * Enters a WebXR or WebVR session of some kind by tapping on the canvas on the page. Needs to
+     * be non-static despite not using any member variables in order for the WebContents-less helper
+     * version to work properly in subclasses.
      * @param webContents The WebContents for the tab the canvas is in.
      */
     public void enterSessionWithUserGesture(WebContents webContents) {
@@ -62,7 +66,8 @@
 
     /**
      * Enters a WebXR or WebVR session of some kind and waits until the page reports it is finished
-     * with its JavaScript step.
+     * with its JavaScript step. Needs to be non-static despite not using any member variables in
+     * order for the WebContents-less helper version to work properly in subclasses.
      * @param webContents The WebContents for the tab to enter the session in.
      */
     public void enterSessionWithUserGestureAndWait(WebContents webContents) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
index 8f14dcc..8ccc78ba 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/XrTestFramework.java
@@ -76,40 +76,6 @@
     private View mFirstTabContentView;
 
     /**
-     * Must be constructed after the rule has been applied (e.g. in whatever method is
-     * tagged with @Before)
-     */
-    public XrTestFramework(ChromeActivityTestRule rule) {
-        mRule = rule;
-        mFirstTabWebContents = mRule.getWebContents();
-        mFirstTabContentView = mRule.getActivity().getActivityTab().getContentView();
-    }
-
-    public WebContents getFirstTabWebContents() {
-        return mFirstTabWebContents;
-    }
-
-    public View getFirstTabContentView() {
-        return mFirstTabContentView;
-    }
-
-    public ChromeActivityTestRule getRule() {
-        return mRule;
-    }
-
-    public void simulateRendererKilled() {
-        final Tab tab = getRule().getActivity().getActivityTab();
-        ThreadUtils.runOnUiThreadBlocking(() -> tab.simulateRendererKilledForTesting(true));
-
-        CriteriaHelper.pollUiThread(new Criteria() {
-            @Override
-            public boolean isSatisfied() {
-                return tab.isShowingSadTab();
-            }
-        });
-    }
-
-    /**
      * Gets the file:// URL to the test file
      * @param testName The name of the test whose file will be retrieved.
      * @return The file:// URL to the specified test file.
@@ -129,22 +95,6 @@
     }
 
     /**
-     * Loads the given URL with the given timeout then waits for JavaScript to
-     * signal that it's ready for testing.
-     * @param url The URL of the page to load.
-     * @param timeoutSec The timeout of the page load in seconds.
-     * @return The return value of ChromeActivityTestRule.loadUrl()
-     */
-    public int loadUrlAndAwaitInitialization(String url, int timeoutSec)
-            throws InterruptedException {
-        int result = mRule.loadUrl(url, timeoutSec);
-        Assert.assertTrue("JavaScript initialization successful",
-                pollJavaScriptBoolean("isInitializationComplete()", POLL_TIMEOUT_LONG_MS,
-                        mRule.getWebContents()));
-        return result;
-    }
-
-    /**
      * Helper function to run the given JavaScript, return the return value,
      * and fail if a timeout/interrupt occurs so we don't have to catch or
      * declare exceptions all the time.
@@ -164,94 +114,6 @@
     }
 
     /**
-     * Helper method to run runJavaScriptOrFail with the first tab's WebContents.
-     * @param js The JavaScript to run.
-     * @param timeout The timeout in milliseconds before a failure.
-     * @return The return value of the JavaScript.
-     */
-    public String runJavaScriptOrFail(String js, int timeout) {
-        return runJavaScriptOrFail(js, timeout, mFirstTabWebContents);
-    }
-
-    /**
-     * Retrieves the current status of the JavaScript test and returns an enum corresponding to it.
-     * @param webContents The WebContents for the tab to check the status in.
-     * @return A TestStatus integer corresponding to the current state of the JavaScript test
-     */
-    @TestStatus
-    public static int checkTestStatus(WebContents webContents) {
-        String resultString =
-                runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents);
-        boolean testPassed = Boolean.parseBoolean(
-                runJavaScriptOrFail("testPassed", POLL_TIMEOUT_SHORT_MS, webContents));
-        if (testPassed) {
-            return STATUS_PASSED;
-        } else if (!testPassed && resultString.equals("\"\"")) {
-            return STATUS_RUNNING;
-        } else {
-            // !testPassed && !resultString.equals("\"\"")
-            return STATUS_FAILED;
-        }
-    }
-
-    /**
-     * Helper method to run checkTestSTatus with the first tab's WebContents.
-     * @return A TestStatus integer corresponding to the current state of the JavaScript test
-     */
-    @TestStatus
-    public int checkTestStatus() {
-        return checkTestStatus(mFirstTabWebContents);
-    }
-
-    /**
-     * Helper function to end the test harness test and assert that it passed,
-     * setting the failure reason as the description if it didn't.
-     * @param webContents The WebContents for the tab to check test results in.
-     */
-    public static void endTest(WebContents webContents) {
-        switch (checkTestStatus(webContents)) {
-            case STATUS_PASSED:
-                break;
-            case STATUS_FAILED:
-                String resultString =
-                        runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents);
-                Assert.fail("JavaScript testharness failed with result: " + resultString);
-                break;
-            case STATUS_RUNNING:
-                Assert.fail("Attempted to end test in Java without finishing in JavaScript.");
-                break;
-            default:
-                Assert.fail("Received unknown test status.");
-        }
-    }
-
-    /**
-     * Helper function to run endTest with the first tab's WebContents.
-     */
-    public void endTest() {
-        endTest(mFirstTabWebContents);
-    }
-
-    /**
-     * Helper function to make sure that the JavaScript test harness did not detect any failures.
-     * Similar to endTest, but does not fail if the test is still detected as running. This is
-     * useful because not all tests make use of the test harness' test/assert features (particularly
-     * simple enter/exit tests), but may still want to ensure that no unexpected JavaScript errors
-     * were encountered.
-     * @param webContents The Webcontents for the tab to check for failures in.
-     */
-    public static void assertNoJavaScriptErrors(WebContents webContents) {
-        Assert.assertNotEquals(checkTestStatus(webContents), STATUS_FAILED);
-    }
-
-    /**
-     * Helper function to run assertNoJavaScriptErrors with the first tab's WebContents.
-     */
-    public void assertNoJavaScriptErrors() {
-        assertNoJavaScriptErrors(mFirstTabWebContents);
-    }
-
-    /**
      * Polls the provided JavaScript boolean until the timeout is reached or
      * the boolean is true.
      * @param boolName The name of the JavaScript boolean or expression to poll.
@@ -283,13 +145,14 @@
     }
 
     /**
-     * Helper function to run pollJavaScriptBoolean with the first tab's WebContents.
-     * @param boolName The name of the JavaScript boolean or expression to poll.
-     * @param timeoutMs The polling timeout in milliseconds.
-     * @return True if the boolean evaluated to true, false if timed out.
+     * Executes a JavaScript step function using the given WebContents.
+     * @param stepFunction The JavaScript step function to call.
+     * @param webContents The WebContents for the tab the JavaScript is in.
      */
-    public boolean pollJavaScriptBoolean(String boolName, int timeoutMs) {
-        return pollJavaScriptBoolean(boolName, timeoutMs, mFirstTabWebContents);
+    public static void executeStepAndWait(String stepFunction, WebContents webContents) {
+        // Run the step and block
+        JavaScriptUtils.executeJavaScript(webContents, stepFunction);
+        waitOnJavaScriptStep(webContents);
     }
 
     /**
@@ -336,21 +199,104 @@
     }
 
     /**
-     * Helper function to run waitOnJavaScriptStep with the first tab's WebContents.
+     * Retrieves the current status of the JavaScript test and returns an enum corresponding to it.
+     * @param webContents The WebContents for the tab to check the status in.
+     * @return A TestStatus integer corresponding to the current state of the JavaScript test
      */
-    public void waitOnJavaScriptStep() {
-        waitOnJavaScriptStep(mFirstTabWebContents);
+    @TestStatus
+    public static int checkTestStatus(WebContents webContents) {
+        String resultString =
+                runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents);
+        boolean testPassed = Boolean.parseBoolean(
+                runJavaScriptOrFail("testPassed", POLL_TIMEOUT_SHORT_MS, webContents));
+        if (testPassed) {
+            return STATUS_PASSED;
+        } else if (!testPassed && resultString.equals("\"\"")) {
+            return STATUS_RUNNING;
+        } else {
+            // !testPassed && !resultString.equals("\"\"")
+            return STATUS_FAILED;
+        }
     }
 
     /**
-     * Executes a JavaScript step function using the given WebContents.
-     * @param stepFunction The JavaScript step function to call.
-     * @param webContents The WebContents for the tab the JavaScript is in.
+     * Helper function to end the test harness test and assert that it passed,
+     * setting the failure reason as the description if it didn't.
+     * @param webContents The WebContents for the tab to check test results in.
      */
-    public static void executeStepAndWait(String stepFunction, WebContents webContents) {
-        // Run the step and block
-        JavaScriptUtils.executeJavaScript(webContents, stepFunction);
-        waitOnJavaScriptStep(webContents);
+    public static void endTest(WebContents webContents) {
+        switch (checkTestStatus(webContents)) {
+            case STATUS_PASSED:
+                break;
+            case STATUS_FAILED:
+                String resultString =
+                        runJavaScriptOrFail("resultString", POLL_TIMEOUT_SHORT_MS, webContents);
+                Assert.fail("JavaScript testharness failed with result: " + resultString);
+                break;
+            case STATUS_RUNNING:
+                Assert.fail("Attempted to end test in Java without finishing in JavaScript.");
+                break;
+            default:
+                Assert.fail("Received unknown test status.");
+        }
+    }
+
+    /**
+     * Helper function to make sure that the JavaScript test harness did not detect any failures.
+     * Similar to endTest, but does not fail if the test is still detected as running. This is
+     * useful because not all tests make use of the test harness' test/assert features (particularly
+     * simple enter/exit tests), but may still want to ensure that no unexpected JavaScript errors
+     * were encountered.
+     * @param webContents The Webcontents for the tab to check for failures in.
+     */
+    public static void assertNoJavaScriptErrors(WebContents webContents) {
+        Assert.assertNotEquals(checkTestStatus(webContents), STATUS_FAILED);
+    }
+
+    /**
+     * Must be constructed after the rule has been applied (e.g. in whatever method is
+     * tagged with @Before)
+     */
+    public XrTestFramework(ChromeActivityTestRule rule) {
+        mRule = rule;
+        mFirstTabWebContents = mRule.getWebContents();
+        mFirstTabContentView = mRule.getActivity().getActivityTab().getContentView();
+    }
+
+    /**
+     * Loads the given URL with the given timeout then waits for JavaScript to
+     * signal that it's ready for testing.
+     * @param url The URL of the page to load.
+     * @param timeoutSec The timeout of the page load in seconds.
+     * @return The return value of ChromeActivityTestRule.loadUrl()
+     */
+    public int loadUrlAndAwaitInitialization(String url, int timeoutSec)
+            throws InterruptedException {
+        int result = mRule.loadUrl(url, timeoutSec);
+        Assert.assertTrue("JavaScript initialization successful",
+                pollJavaScriptBoolean("isInitializationComplete()", POLL_TIMEOUT_LONG_MS,
+                        mRule.getWebContents()));
+        return result;
+    }
+
+    /**
+     * Helper method to run runJavaScriptOrFail with the first tab's WebContents.
+     * @param js The JavaScript to run.
+     * @param timeout The timeout in milliseconds before a failure.
+     * @return The return value of the JavaScript.
+     */
+    public String runJavaScriptOrFail(String js, int timeout) {
+        return runJavaScriptOrFail(js, timeout, mFirstTabWebContents);
+    }
+
+    /**
+     * Helper function to run pollJavaScriptBoolean with the first tab's WebContents.
+     * @param boolName The name of the JavaScript boolean or expression to poll.
+     * @param timeoutMs The polling timeout in milliseconds.
+     * @return True if the boolean evaluated to true, false if timed out.
+     */
+    public boolean pollJavaScriptBoolean(String boolName, int timeoutMs) {
+        return pollJavaScriptBoolean(boolName, timeoutMs, mFirstTabWebContents);
     }
 
     /**
@@ -360,4 +306,58 @@
     public void executeStepAndWait(String stepFunction) {
         executeStepAndWait(stepFunction, mFirstTabWebContents);
     }
+
+    /**
+     * Helper function to run waitOnJavaScriptStep with the first tab's WebContents.
+     */
+    public void waitOnJavaScriptStep() {
+        waitOnJavaScriptStep(mFirstTabWebContents);
+    }
+
+    /**
+     * Helper method to run checkTestSTatus with the first tab's WebContents.
+     * @return A TestStatus integer corresponding to the current state of the JavaScript test
+     */
+    @TestStatus
+    public int checkTestStatus() {
+        return checkTestStatus(mFirstTabWebContents);
+    }
+
+    /**
+     * Helper function to run endTest with the first tab's WebContents.
+     */
+    public void endTest() {
+        endTest(mFirstTabWebContents);
+    }
+
+    /**
+     * Helper function to run assertNoJavaScriptErrors with the first tab's WebContents.
+     */
+    public void assertNoJavaScriptErrors() {
+        assertNoJavaScriptErrors(mFirstTabWebContents);
+    }
+
+    public View getFirstTabContentView() {
+        return mFirstTabContentView;
+    }
+
+    public WebContents getFirstTabWebContents() {
+        return mFirstTabWebContents;
+    }
+
+    public ChromeActivityTestRule getRule() {
+        return mRule;
+    }
+
+    public void simulateRendererKilled() {
+        final Tab tab = getRule().getActivity().getActivityTab();
+        ThreadUtils.runOnUiThreadBlocking(() -> tab.simulateRendererKilledForTesting(true));
+
+        CriteriaHelper.pollUiThread(new Criteria() {
+            @Override
+            public boolean isSatisfied() {
+                return tab.isShowingSadTab();
+            }
+        });
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/mock/MockVrDaydreamApi.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/mock/MockVrDaydreamApi.java
index fca16f7..ee96722 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/mock/MockVrDaydreamApi.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/mock/MockVrDaydreamApi.java
@@ -37,15 +37,8 @@
         return super.launchInVr(intent);
     }
 
-    @Override
-    public boolean exitFromVr(Activity activity, int requestCode, final Intent intent) {
-        mExitFromVrCalled = true;
-        if (mExitFromVrReturnValue == null) return super.exitFromVr(activity, requestCode, intent);
-        return mExitFromVrReturnValue.booleanValue();
-    }
-
-    public void setExitFromVrReturnValue(Boolean value) {
-        mExitFromVrReturnValue = value;
+    public boolean getLaunchInVrCalled() {
+        return mLaunchInVrCalled;
     }
 
     @Override
@@ -54,15 +47,22 @@
         return super.launchVrHomescreen();
     }
 
-    public boolean getLaunchInVrCalled() {
-        return mLaunchInVrCalled;
+    public boolean getLaunchVrHomescreenCalled() {
+        return mLaunchVrHomescreenCalled;
+    }
+
+    @Override
+    public boolean exitFromVr(Activity activity, int requestCode, final Intent intent) {
+        mExitFromVrCalled = true;
+        if (mExitFromVrReturnValue == null) return super.exitFromVr(activity, requestCode, intent);
+        return mExitFromVrReturnValue.booleanValue();
     }
 
     public boolean getExitFromVrCalled() {
         return mExitFromVrCalled;
     }
 
-    public boolean getLaunchVrHomescreenCalled() {
-        return mLaunchVrHomescreenCalled;
+    public void setExitFromVrReturnValue(Boolean value) {
+        mExitFromVrReturnValue = value;
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java
index ed1fb61..94895cc2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/NativeUiUtils.java
@@ -41,16 +41,6 @@
     }
 
     /**
-     * Sets the native code to start using the real controller data again instead of fake testing
-     * data.
-     */
-    public static void revertToRealController() {
-        TestVrShellDelegate.getInstance().performControllerActionForTesting(
-                0 /* elementName, unused */, VrControllerTestAction.REVERT_TO_REAL_CONTROLLER,
-                new PointF() /* position, unused */);
-    }
-
-    /**
      * Clicks on a UI element as if done via a controller and waits until all resulting
      * animations have finished and propogated to the point of being visible in screenshots.
      * @param elementName The UserFriendlyElementName that will be clicked on.
@@ -61,6 +51,30 @@
     }
 
     /**
+     * Clicks on a fallback UI element's positive button, e.g. "Allow" or "Confirm".
+     */
+    public static void clickFallbackUiPositiveButton() throws InterruptedException {
+        clickFallbackUiButton(R.id.positive_button);
+    }
+
+    /**
+     * Clicks on a fallback UI element's negative button, e.g. "Deny" or "Cancel".
+     */
+    public static void clickFallbackUiNegativeButton() throws InterruptedException {
+        clickFallbackUiButton(R.id.negative_button);
+    }
+
+    /**
+     * Sets the native code to start using the real controller data again instead of fake testing
+     * data.
+     */
+    public static void revertToRealController() {
+        TestVrShellDelegate.getInstance().performControllerActionForTesting(
+                0 /* elementName, unused */, VrControllerTestAction.REVERT_TO_REAL_CONTROLLER,
+                new PointF() /* position, unused */);
+    }
+
+    /**
      * Sets the native code to start using the real controller data again and waits for the UI to
      * update as a result.
      */
@@ -91,20 +105,6 @@
     }
 
     /**
-     * Clicks on a fallback UI element's positive button, e.g. "Allow" or "Confirm".
-     */
-    public static void clickFallbackUiPositiveButton() throws InterruptedException {
-        clickFallbackUiButton(R.id.positive_button);
-    }
-
-    /**
-     * Clicks on a fallback UI element's negative button, e.g. "Deny" or "Cancel".
-     */
-    public static void clickFallbackUiNegativeButton() throws InterruptedException {
-        clickFallbackUiButton(R.id.negative_button);
-    }
-
-    /**
      * Blocks until the specified number of frames have been triggered by the Choreographer.
      * @param numFrames The number of frames to wait for.
      */
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java
index 0719c6d..79b9ddd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/util/VrBrowserTransitionUtils.java
@@ -9,6 +9,8 @@
 import android.content.Intent;
 import android.net.Uri;
 
+import org.junit.Assert;
+
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -43,6 +45,17 @@
     }
 
     /**
+     * Forces Chrome into the VR Browser, causing a test failure if it is not entered in the
+     * allotted time.
+     * @param timeoutMs The amount of time in milliseconds to wait for VR Browser entry before
+     *    failing.
+     */
+    public static void forceEnterVrBrowserOrFail(int timeoutMs) {
+        Assert.assertTrue("Request to enter VR Browser failed", forceEnterVrBrowser());
+        waitForVrEntry(timeoutMs);
+    }
+
+    /**
      * @return Whether the VR Browser's back button is enabled.
      */
     public static Boolean isBackButtonEnabled() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
index 5e10985..2a84cf87 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryControllerTest.java
@@ -214,4 +214,18 @@
         mCoordinator.addTab(mTestTab);
         assertThat(mModel.isVisible(), is(true));
     }
+
+    @Test
+    public void testClosingTabDismissesSheet() {
+        mModel.setActiveTab(0);
+        mModel.addObserver(mMockPropertyObserver);
+        assertThat(mModel.activeTab(), is(0));
+
+        // Closing the active tab should reset the tab which should trigger the visibility delegate.
+        mCoordinator.closeActiveTab();
+        assertThat(mModel.activeTab(), is(nullValue()));
+        verify(mMockPropertyObserver)
+                .onPropertyChanged(mModel, KeyboardAccessoryModel.PropertyKey.ACTIVE_TAB);
+        verify(mMockVisibilityDelegate).onCloseAccessorySheet();
+    }
 }
\ No newline at end of file
diff --git a/chrome/android/webapk/shell_apk/AndroidManifest.xml b/chrome/android/webapk/shell_apk/AndroidManifest.xml
index 35d7db19..85b920a 100644
--- a/chrome/android/webapk/shell_apk/AndroidManifest.xml
+++ b/chrome/android/webapk/shell_apk/AndroidManifest.xml
@@ -65,7 +65,8 @@
         <meta-data android:name="org.chromium.webapk.shell_apk.orientation" android:value="{{{orientation}}}" />
         <meta-data android:name="org.chromium.webapk.shell_apk.themeColor" android:value="{{{theme_color}}}" />
         <meta-data android:name="org.chromium.webapk.shell_apk.backgroundColor" android:value="{{{background_color}}}" />
-        <meta-data android:name="org.chromium.webapk.shell_apk.iconId" android:resource="{{{splash_screen_icon}}}" />
+        <meta-data android:name="org.chromium.webapk.shell_apk.iconId" android:resource="@mipmap/app_icon" />
+        <meta-data android:name="org.chromium.webapk.shell_apk.splashId" android:resource="@drawable/splash_icon" />
 
         {{!  Hashes of icons should be taken of the icons as they are available on the web. The icon
              bytes should not be transformed (e.g. decoded / encoded) prior to taking the hash.
diff --git a/chrome/android/webapk/shell_apk/bound_manifest_config.json b/chrome/android/webapk/shell_apk/bound_manifest_config.json
index ff504f4..71e78be 100644
--- a/chrome/android/webapk/shell_apk/bound_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/bound_manifest_config.json
@@ -12,7 +12,6 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
-  "splash_screen_icon": "@mipmap/app_icon",
   "icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
   "web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
   "version_code": "1",
diff --git a/chrome/android/webapk/shell_apk/http_manifest_config.json b/chrome/android/webapk/shell_apk/http_manifest_config.json
index d99448a..1b0c9b68 100644
--- a/chrome/android/webapk/shell_apk/http_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/http_manifest_config.json
@@ -12,7 +12,6 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
-  "splash_screen_icon": "@mipmap/app_icon",
   "icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
   "web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
   "version_code": "1",
diff --git a/chrome/android/webapk/shell_apk/javatest_manifest_config.json b/chrome/android/webapk/shell_apk/javatest_manifest_config.json
index 49469109..ceff57a 100644
--- a/chrome/android/webapk/shell_apk/javatest_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/javatest_manifest_config.json
@@ -12,7 +12,6 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
-  "splash_screen_icon": "@mipmap/app_icon",
   "icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
   "web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
   "version_code": "1",
diff --git a/chrome/android/webapk/shell_apk/maps_go_manifest_config.json b/chrome/android/webapk/shell_apk/maps_go_manifest_config.json
index faf0878..8008969 100644
--- a/chrome/android/webapk/shell_apk/maps_go_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/maps_go_manifest_config.json
@@ -12,7 +12,6 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
-  "splash_screen_icon": "@mipmap/app_icon",
   "icon_urls_and_icon_murmur2_hashes": "https://maps.gstatic.com/mapfiles/maps_lite/pwa/icons/maps_pwa_icon_v0920_48x48.png 0 https://maps.gstatic.com/mapfiles/maps_lite/pwa/icons/maps_pwa_icon_v0920_72x72 0",
   "web_manifest_url": "https://maps.gstatic.com/tactile/worker/ml.json",
   "version_code": "1",
diff --git a/chrome/android/webapk/shell_apk/res/drawable-hdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-hdpi/splash_icon.xml
new file mode 100644
index 0000000..0ea2a6b
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-hdpi/splash_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/drawable-mdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-mdpi/splash_icon.xml
new file mode 100644
index 0000000..5e65be53
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-mdpi/splash_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xhdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/splash_icon.xml
new file mode 100644
index 0000000..ab81c74
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xhdpi/splash_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xxxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/splash_icon.xml
new file mode 100644
index 0000000..ab81c74
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxhdpi/splash_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xxxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/splash_icon.xml b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/splash_icon.xml
new file mode 100644
index 0000000..ab81c74
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/drawable-xxxhdpi/splash_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xxxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-hdpi/app_icon.xml b/chrome/android/webapk/shell_apk/res/mipmap-hdpi/app_icon.xml
new file mode 100644
index 0000000..07e31a49
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-hdpi/app_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_hdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-hdpi/ic_launcher.xml b/chrome/android/webapk/shell_apk/res/mipmap-hdpi/ic_launcher.xml
index e4ed5c5..77e7b552 100644
--- a/chrome/android/webapk/shell_apk/res/mipmap-hdpi/ic_launcher.xml
+++ b/chrome/android/webapk/shell_apk/res/mipmap-hdpi/ic_launcher.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon"/>
\ No newline at end of file
+    android:src="@mipmap/app_icon_hdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-mdpi/app_icon.xml b/chrome/android/webapk/shell_apk/res/mipmap-mdpi/app_icon.xml
new file mode 100644
index 0000000..5f22cd9e
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-mdpi/app_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_mdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-mdpi/ic_launcher.xml b/chrome/android/webapk/shell_apk/res/mipmap-mdpi/ic_launcher.xml
index e4ed5c5..0097029 100644
--- a/chrome/android/webapk/shell_apk/res/mipmap-mdpi/ic_launcher.xml
+++ b/chrome/android/webapk/shell_apk/res/mipmap-mdpi/ic_launcher.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon"/>
\ No newline at end of file
+    android:src="@mipmap/app_icon_mdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/app_icon.xml b/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/app_icon.xml
new file mode 100644
index 0000000..5e65be53
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/app_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/ic_launcher.xml b/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/ic_launcher.xml
index e4ed5c5..67f1839 100644
--- a/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/ic_launcher.xml
+++ b/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/ic_launcher.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon"/>
\ No newline at end of file
+    android:src="@mipmap/app_icon_xhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/app_icon.xml b/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/app_icon.xml
new file mode 100644
index 0000000..0ea2a6b
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/app_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/ic_launcher.xml b/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/ic_launcher.xml
index e4ed5c5..25cdc31 100644
--- a/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/ic_launcher.xml
+++ b/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/ic_launcher.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon"/>
\ No newline at end of file
+    android:src="@mipmap/app_icon_xxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/app_icon.xml b/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/app_icon.xml
new file mode 100644
index 0000000..ab81c74
--- /dev/null
+++ b/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/app_icon.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@mipmap/app_icon_xxxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/ic_launcher.xml b/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/ic_launcher.xml
index e4ed5c5..b26ea32 100644
--- a/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/ic_launcher.xml
+++ b/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/ic_launcher.xml
@@ -4,4 +4,4 @@
      found in the LICENSE file. -->
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@mipmap/app_icon"/>
\ No newline at end of file
+    android:src="@mipmap/app_icon_xxxhdpi"/>
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-hdpi/app_icon.png b/chrome/android/webapk/shell_apk/res/mipmap/app_icon_hdpi.png
similarity index 100%
rename from chrome/android/webapk/shell_apk/res/mipmap-hdpi/app_icon.png
rename to chrome/android/webapk/shell_apk/res/mipmap/app_icon_hdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-mdpi/app_icon.png b/chrome/android/webapk/shell_apk/res/mipmap/app_icon_mdpi.png
similarity index 100%
rename from chrome/android/webapk/shell_apk/res/mipmap-mdpi/app_icon.png
rename to chrome/android/webapk/shell_apk/res/mipmap/app_icon_mdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xhdpi/app_icon.png b/chrome/android/webapk/shell_apk/res/mipmap/app_icon_xhdpi.png
similarity index 100%
rename from chrome/android/webapk/shell_apk/res/mipmap-xhdpi/app_icon.png
rename to chrome/android/webapk/shell_apk/res/mipmap/app_icon_xhdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/app_icon.png b/chrome/android/webapk/shell_apk/res/mipmap/app_icon_xxhdpi.png
similarity index 100%
rename from chrome/android/webapk/shell_apk/res/mipmap-xxhdpi/app_icon.png
rename to chrome/android/webapk/shell_apk/res/mipmap/app_icon_xxhdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/app_icon.png b/chrome/android/webapk/shell_apk/res/mipmap/app_icon_xxxhdpi.png
similarity index 100%
rename from chrome/android/webapk/shell_apk/res/mipmap-xxxhdpi/app_icon.png
rename to chrome/android/webapk/shell_apk/res/mipmap/app_icon_xxxhdpi.png
Binary files differ
diff --git a/chrome/android/webapk/shell_apk/shell_apk_version.gni b/chrome/android/webapk/shell_apk/shell_apk_version.gni
index c4969121..92e5bd92 100644
--- a/chrome/android/webapk/shell_apk/shell_apk_version.gni
+++ b/chrome/android/webapk/shell_apk/shell_apk_version.gni
@@ -6,7 +6,7 @@
 # (including AndroidManifest.xml) is updated. This version should be incremented
 # prior to uploading a new ShellAPK to the WebAPK Minting Server.
 # Does not affect Chrome.apk
-template_shell_apk_version = 46
+template_shell_apk_version = 47
 
 # The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK
 # if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
diff --git a/chrome/android/webapk/shell_apk/unbound_manifest_config.json b/chrome/android/webapk/shell_apk/unbound_manifest_config.json
index 7c8d48f5..492ccb71 100644
--- a/chrome/android/webapk/shell_apk/unbound_manifest_config.json
+++ b/chrome/android/webapk/shell_apk/unbound_manifest_config.json
@@ -13,7 +13,6 @@
   "orientation": "portrait",
   "theme_color": "2147483648L",
   "background_color": "2147483648L",
-  "splash_screen_icon": "@mipmap/app_icon",
   "icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
   "web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
   "version_code": "1",
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 78bc0e1..d861951 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -219,6 +219,9 @@
         <message name="IDS_ABOUT_CROS_VERSION_LICENSE" desc="Additional text displayed beneath the Chromium open source URLs for Chrome OS.">
           Chromium OS is made possible by additional <ph name="BEGIN_LINK_CROS_OSS">&lt;a target="_blank" href="$1"&gt;</ph>open source software<ph name="END_LINK_CROS_OSS">&lt;/a&gt;</ph>.
         </message>
+        <message name="IDS_ABOUT_CROS_WITH_LINUX_VERSION_LICENSE" desc="Additional text displayed beneath the Chromium open source URLs for Chrome OS when Crostini is installed.">
+          Chrome OS is made possible by additional <ph name="BEGIN_LINK_CROS_OSS">&lt;a target="_blank" href="$1"&gt;</ph>open source software<ph name="END_LINK_CROS_OSS">&lt;/a&gt;</ph>, as is Linux (Beta).
+        </message>
       </if>
       <message name="IDS_ABOUT_TERMS_OF_SERVICE" desc="The terms of service label in the About box." translateable="false">
         Not used in Chromium. Placeholder to keep resource maps in sync. It expects one argument: <ph name="ARGUMENT">$1</ph>.
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 8406e9a6..549409e6 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -225,6 +225,9 @@
         <message name="IDS_ABOUT_CROS_VERSION_LICENSE" desc="Additional text displayed beneath the Chromium open source URLs for Chrome OS.">
           Chrome OS is made possible by additional <ph name="BEGIN_LINK_CROS_OSS">&lt;a target="_blank" href="$1"&gt;</ph>open source software<ph name="END_LINK_CROS_OSS">&lt;/a&gt;</ph>.
         </message>
+        <message name="IDS_ABOUT_CROS_WITH_LINUX_VERSION_LICENSE" desc="Additional text displayed beneath the Chromium open source URLs for Chrome OS when Crostini is installed.">
+          Chrome OS is made possible by additional <ph name="BEGIN_LINK_CROS_OSS">&lt;a target="_blank" href="$1"&gt;</ph>open source software<ph name="END_LINK_CROS_OSS">&lt;/a&gt;</ph>, as is Linux (Beta).
+        </message>
         <message name="IDS_ABOUT_TERMS_OF_SERVICE" desc="The terms of service label in the About box.">
           Google Chrome OS <ph name="TERMS_OF_SERVICE_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Terms of Service<ph name="END_TERMS_OF_SERVICE_LINK">&lt;/a&gt;</ph>
         </message>
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 205a71e..cba4d6e 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -98,6 +98,9 @@
   <message name="IDS_SETTINGS_SYNC_DISCONNECT_TITLE" desc="The title of the dialog to stop syncing and sign out.">
     Sign out of Chromium?
   </message>
+  <message name="IDS_DRIVE_SUGGEST_PREF_DESC" desc="The documentation string of the 'Show Drive Results in Omnibox' - full description sentence">
+    Chromium will access your Drive to make suggestions in the address bar
+  </message>
 <if expr="not chromeos">
     <message name="IDS_SETTINGS_SYNC_SIGN_IN_PROMPT_WITH_NO_ACCOUNT" desc="The text displayed to prompt users to sign in to Chromium.">
       Sign in to sync and personalize Chromium across your devices
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index cde49d3..8830a6d1 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -98,6 +98,9 @@
   <message name="IDS_SETTINGS_SYNC_DISCONNECT_TITLE" desc="The title of the dialog to stop syncing and sign out.">
     Sign out of Chrome?
   </message>
+  <message name="IDS_DRIVE_SUGGEST_PREF_DESC" desc="The documentation string of the 'Show Drive Results in Omnibox' - full description sentence">
+    Chrome will access your Drive to make suggestions in the address bar
+  </message>
 <if expr="not chromeos">
     <message name="IDS_SETTINGS_SYNC_SIGN_IN_PROMPT_WITH_NO_ACCOUNT" desc="The text displayed to prompt users to sign in to Chrome.">
       Sign in to sync and personalize Chrome across your devices
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index fd9dc5fc..76d8af7b 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2403,7 +2403,7 @@
     Autocomplete searches and URLs
   </message>
   <message name="IDS_SETTINGS_SUGGEST_PREF_DESC_UNIFIED_CONSENT" desc="The description of the 'Use Suggest' preference">
-    Sends searches from the address bar and search box and some cookies to your default search engine
+    Sends some cookies and searches from the address bar and search box to your default search engine
   </message>
   <message name="IDS_SETTINGS_NETWORK_PREDICTION_ENABLED_LABEL" desc="In the advanced options tab, the text next to the checkbox that enables prediction of network actions.  Actions include browser-initiated DNS prefetching, TCP and SSL preconnection, and prerendering of webpages.">
     Use a prediction service to load pages more quickly
@@ -2435,6 +2435,12 @@
   <message name="IDS_SETTINGS_SPELLING_PREF_UNIFIED_CONSENT" desc="The documentation string of the 'Use Spelling' preference">
     Enhanced spell check
   </message>
+  <message name="IDS_SETTINGS_SPELLING_TURN_ON_HINT_UNIFIED_CONSENT" desc="Informs user how to change their settings to be able to turn on spell check.">
+    To turn this on, first turn on spell check in <ph name="BEGIN_LINK">&lt;a href="$1" target=&quot;_blank&quot;&gt;<ex>&lt;a href="$1" target=&quot;_blank&quot;&gt;</ex></ph>Languages and input<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
+  </message>
+  <message name="IDS_SETTINGS_SPELLING_TURN_ON_HINT_UNIFIED_CONSENT_MAC" desc="Informs user how to change their settings on Mac to be able to turn on spell check.">
+    To turn this on, first select Check Spelling While Typing in the Edit menu
+  </message>
   <message name="IDS_SETTINGS_ENABLE_LOGGING" desc="The label of the checkbox to enable/disable crash and user metrics logging">
     Automatically send usage statistics and crash reports to Google
   </message>
@@ -3561,6 +3567,9 @@
   <message name="IDS_SETTINGS_OPEN_TABS_CHECKBOX_LABEL" desc="Label for the checkbox which enables or disables syncing open tabs between multiple browser instances.">
     Open Tabs
   </message>
+  <message name="IDS_DRIVE_SUGGEST_PREF" desc="The documentation string of the 'Show Drive Results in Omnibox' preference - short description">
+    Google Drive search suggestions
+  </message>
   <message name="IDS_SETTINGS_USER_EVENTS_CHECKBOX_LABEL" desc="Label for the checkbox which enables or disables recording user events.">
     Activity and interactions
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b3179cd8..f0a3206 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1504,6 +1504,8 @@
     "sync/glue/extensions_activity_monitor.h",
     "sync/glue/sync_start_util.cc",
     "sync/glue/sync_start_util.h",
+    "sync/model_type_store_service_factory.cc",
+    "sync/model_type_store_service_factory.h",
     "sync/profile_sync_service_factory.cc",
     "sync/profile_sync_service_factory.h",
     "sync/sessions/sync_sessions_router_tab_helper.cc",
@@ -2934,6 +2936,8 @@
       "themes/browser_theme_pack.h",
       "themes/custom_theme_supplier.cc",
       "themes/custom_theme_supplier.h",
+      "themes/increased_contrast_theme_supplier.cc",
+      "themes/increased_contrast_theme_supplier.h",
       "themes/theme_properties.cc",
       "themes/theme_service.cc",
       "themes/theme_service.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index efc618d4..1f7d855 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4113,6 +4113,11 @@
      FEATURE_VALUE_TYPE(app_list::features::kEnableContinueReading)},
 #endif  // OS_CHROMEOS
 
+    {"enable-bloated-renderer-detection",
+     flag_descriptions::kEnableBloatedRendererDetectionName,
+     flag_descriptions::kEnableBloatedRendererDetectionDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kBloatedRendererDetection)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/password_manager/password_accessory_view_android.cc b/chrome/browser/android/password_manager/password_accessory_view_android.cc
index 4c1a3b3..766b83b 100644
--- a/chrome/browser/android/password_manager/password_accessory_view_android.cc
+++ b/chrome/browser/android/password_manager/password_accessory_view_android.cc
@@ -61,6 +61,11 @@
       base::android::ToJavaIntArray(env, item_types));
 }
 
+void PasswordAccessoryViewAndroid::CloseAccessorySheet() {
+  Java_PasswordAccessoryBridge_closeAccessorySheet(
+      base::android::AttachCurrentThread(), java_object_);
+}
+
 void PasswordAccessoryViewAndroid::OnAutomaticGenerationStatusChanged(
     bool available) {
   if (!available && java_object_.is_null())
@@ -68,7 +73,7 @@
 
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_PasswordAccessoryBridge_onAutomaticGenerationStatusChanged(
-      env, java_object_, available /* available */);
+      env, java_object_, available);
 }
 
 void PasswordAccessoryViewAndroid::OnFillingTriggered(
diff --git a/chrome/browser/android/password_manager/password_accessory_view_android.h b/chrome/browser/android/password_manager/password_accessory_view_android.h
index c8169ae..b949114 100644
--- a/chrome/browser/android/password_manager/password_accessory_view_android.h
+++ b/chrome/browser/android/password_manager/password_accessory_view_android.h
@@ -25,6 +25,7 @@
   // PasswordAccessoryViewInterface:
   void OnItemsAvailable(const std::vector<AccessoryItem>& items) override;
   void OnAutomaticGenerationStatusChanged(bool available) override;
+  void CloseAccessorySheet() override;
 
   // Called from Java via JNI:
   void OnFillingTriggered(
diff --git a/chrome/browser/android/vr/vr_gl_thread.cc b/chrome/browser/android/vr/vr_gl_thread.cc
index 34284499..0e88057 100644
--- a/chrome/browser/android/vr/vr_gl_thread.cc
+++ b/chrome/browser/android/vr/vr_gl_thread.cc
@@ -71,10 +71,10 @@
   auto ui = std::make_unique<Ui>(this, this, keyboard_delegate_.get(),
                                  text_input_delegate_.get(),
                                  audio_delegate_.get(), ui_initial_state_);
-  text_input_delegate_->SetRequestFocusCallback(
-      base::BindRepeating(&Ui::RequestFocus, base::Unretained(ui.get())));
-  text_input_delegate_->SetRequestUnfocusCallback(
-      base::BindRepeating(&Ui::RequestUnfocus, base::Unretained(ui.get())));
+  text_input_delegate_->SetRequestFocusCallback(base::BindRepeating(
+      &UiInterface::RequestFocus, base::Unretained(ui.get())));
+  text_input_delegate_->SetRequestUnfocusCallback(base::BindRepeating(
+      &UiInterface::RequestUnfocus, base::Unretained(ui.get())));
   if (keyboard_delegate_.get()) {
     keyboard_delegate_->SetUiInterface(ui.get());
     text_input_delegate_->SetUpdateInputCallback(
diff --git a/chrome/browser/android/vr/vr_shell_gl.cc b/chrome/browser/android/vr/vr_shell_gl.cc
index e250074..3c4440d 100644
--- a/chrome/browser/android/vr/vr_shell_gl.cc
+++ b/chrome/browser/android/vr/vr_shell_gl.cc
@@ -39,6 +39,7 @@
 #include "chrome/browser/vr/pose_util.h"
 #include "chrome/browser/vr/ui.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
+#include "chrome/browser/vr/ui_interface.h"
 #include "chrome/browser/vr/ui_scene.h"
 #include "chrome/browser/vr/ui_test_input.h"
 #include "chrome/browser/vr/vr_gl_util.h"
@@ -194,7 +195,7 @@
                          const std::vector<const UiElement*>& elements,
                          const gvr::Rectf& recommended,
                          float z_near,
-                         Ui* ui) {
+                         UiInterface* ui) {
   Ui::FovRectangle rect =
       ui->GetMinimalFov(view_matrix, elements,
                         Ui::FovRectangle{recommended.left, recommended.right,
@@ -331,7 +332,7 @@
 }
 
 VrShellGl::VrShellGl(GlBrowserInterface* browser_interface,
-                     std::unique_ptr<Ui> ui,
+                     std::unique_ptr<UiInterface> ui,
                      gvr_context* gvr_api,
                      bool reprojected_rendering,
                      bool daydream_support,
@@ -1380,14 +1381,14 @@
         // UI state in the midst of frame rendering.
         base::ThreadTaskRunnerHandle::Get()->PostTask(
             FROM_HERE,
-            base::BindRepeating(&Ui::OnAppButtonSwipePerformed,
+            base::BindRepeating(&UiInterface::OnAppButtonSwipePerformed,
                                 base::Unretained(ui_.get()), direction));
       }
     }
     if (direction == PlatformController::kSwipeDirectionNone &&
         !app_button_long_pressed_) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindRepeating(&Ui::OnAppButtonClicked,
+          FROM_HERE, base::BindRepeating(&UiInterface::OnAppButtonClicked,
                                          base::Unretained(ui_.get())));
     }
   }
@@ -2039,12 +2040,11 @@
     CHECK(buffer);
 
     // Use an identity UV transform, the image is already oriented correctly.
-    ui_->ui_element_renderer()->DrawWebVr(buffer->local_texture,
-                                          kWebVrIdentityUvTransform, 0, 0);
+    ui_->DrawWebVr(buffer->local_texture, kWebVrIdentityUvTransform, 0, 0);
   } else {
     // Apply the UV transform from the SurfaceTexture, that's usually a Y flip.
-    ui_->ui_element_renderer()->DrawWebVr(
-        webvr_texture_id_, webvr_surface_texture_uv_transform_, 0, 0);
+    ui_->DrawWebVr(webvr_texture_id_, webvr_surface_texture_uv_transform_, 0,
+                   0);
   }
 }
 
@@ -2064,17 +2064,15 @@
       content_tex_buffer_size_.height() * kContentVignetteBorder - kBorder,
       content_tex_buffer_size_.width() + 2 * kBorder,
       content_tex_buffer_size_.height() + 2 * kBorder);
-  ui_->ui_element_renderer()->DrawWebVr(
-      content_texture_id_, kContentUvTransform,
-      kBorder / content_tex_buffer_size_.width(),
-      kBorder / content_tex_buffer_size_.height());
+  ui_->DrawWebVr(content_texture_id_, kContentUvTransform,
+                 kBorder / content_tex_buffer_size_.width(),
+                 kBorder / content_tex_buffer_size_.height());
   if (draw_overlay_texture) {
     glEnable(GL_BLEND);
     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-    ui_->ui_element_renderer()->DrawWebVr(
-        content_overlay_texture_id_, kContentUvTransform,
-        kBorder / content_tex_buffer_size_.width(),
-        kBorder / content_tex_buffer_size_.height());
+    ui_->DrawWebVr(content_overlay_texture_id_, kContentUvTransform,
+                   kBorder / content_tex_buffer_size_.width(),
+                   kBorder / content_tex_buffer_size_.height());
   }
 }
 
diff --git a/chrome/browser/android/vr/vr_shell_gl.h b/chrome/browser/android/vr/vr_shell_gl.h
index 3d93bc2..70ef5a5 100644
--- a/chrome/browser/android/vr/vr_shell_gl.h
+++ b/chrome/browser/android/vr/vr_shell_gl.h
@@ -68,7 +68,7 @@
 class MailboxToSurfaceBridge;
 class ScopedGpuTrace;
 class SlidingTimeDeltaAverage;
-class Ui;
+class UiInterface;
 class VrController;
 class VrShell;
 
@@ -267,7 +267,7 @@
 class VrShellGl : public device::mojom::VRPresentationProvider {
  public:
   VrShellGl(GlBrowserInterface* browser_interface,
-            std::unique_ptr<Ui> ui,
+            std::unique_ptr<UiInterface> ui,
             gvr_context* gvr_api,
             bool reprojected_rendering,
             bool daydream_support,
@@ -524,7 +524,7 @@
 
   std::unique_ptr<WebXrPresentationState> webxr_ = nullptr;
 
-  std::unique_ptr<Ui> ui_;
+  std::unique_ptr<UiInterface> ui_;
 
   bool web_vr_mode_ = false;
   bool ready_to_draw_ = false;
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.cc b/chrome/browser/chromeos/arc/arc_session_manager.cc
index 65a37eb..ab488c1 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager.cc
@@ -1006,6 +1006,9 @@
     profile_->GetPrefs()->SetBoolean(prefs::kArcPaiStarted, false);
     profile_->GetPrefs()->SetBoolean(prefs::kArcTermsAccepted, false);
     profile_->GetPrefs()->SetBoolean(prefs::kArcFastAppReinstallStarted, false);
+    profile_->GetPrefs()->SetInteger(
+        prefs::kArcSupervisionTransition,
+        static_cast<int>(ArcSupervisionTransition::NO_TRANSITION));
   }
   ShutdownSession();
   if (support_host_)
diff --git a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
index 5e20701..c06eb17 100644
--- a/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_session_manager_unittest.cc
@@ -622,6 +622,39 @@
   arc_session_manager()->Shutdown();
 }
 
+TEST_F(ArcSessionManagerTest, ClearArcTransitionOnShutdown) {
+  profile()->GetPrefs()->SetInteger(
+      prefs::kArcSupervisionTransition,
+      static_cast<int>(ArcSupervisionTransition::NO_TRANSITION));
+
+  // Initialize ARC.
+  arc_session_manager()->SetProfile(profile());
+  arc_session_manager()->Initialize();
+  arc_session_manager()->RequestEnable();
+  base::RunLoop().RunUntilIdle();
+  ASSERT_EQ(ArcSessionManager::State::NEGOTIATING_TERMS_OF_SERVICE,
+            arc_session_manager()->state());
+  arc_session_manager()->OnTermsOfServiceNegotiatedForTesting(true);
+  arc_session_manager()->StartArcForTesting();
+  arc_session_manager()->OnProvisioningFinished(ProvisioningResult::SUCCESS);
+
+  EXPECT_EQ(
+      static_cast<int>(ArcSupervisionTransition::NO_TRANSITION),
+      profile()->GetPrefs()->GetInteger(prefs::kArcSupervisionTransition));
+
+  // Child started graduation.
+  profile()->GetPrefs()->SetInteger(
+      prefs::kArcSupervisionTransition,
+      static_cast<int>(ArcSupervisionTransition::CHILD_TO_REGULAR));
+  // Simulate ARC shutdown.
+  arc_session_manager()->RequestDisable();
+  EXPECT_EQ(
+      static_cast<int>(ArcSupervisionTransition::NO_TRANSITION),
+      profile()->GetPrefs()->GetInteger(prefs::kArcSupervisionTransition));
+
+  arc_session_manager()->Shutdown();
+}
+
 TEST_F(ArcSessionManagerTest, IgnoreSecondErrorReporting) {
   arc_session_manager()->SetProfile(profile());
   arc_session_manager()->Initialize();
diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
index a6b9c378..2934691 100644
--- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
@@ -391,15 +391,13 @@
 
   // FileSystemExtensionApiTestBase override.
   void AddTestMountPoint() override {
-    EXPECT_TRUE(content::BrowserContext::GetMountPoints(browser()->profile())
-                    ->RegisterFileSystem(kLocalMountPointName,
-                                         storage::kFileSystemTypeNativeLocal,
-                                         storage::FileSystemMountOption(),
-                                         mount_point_dir_));
-    VolumeManager::Get(browser()->profile())
-        ->AddVolumeForTesting(mount_point_dir_, VOLUME_TYPE_TESTING,
-                              chromeos::DEVICE_TYPE_UNKNOWN,
-                              false /* read_only */);
+    EXPECT_TRUE(
+        content::BrowserContext::GetMountPoints(profile())->RegisterFileSystem(
+            kLocalMountPointName, storage::kFileSystemTypeNativeLocal,
+            storage::FileSystemMountOption(), mount_point_dir_));
+    VolumeManager::Get(profile())->AddVolumeForTesting(
+        mount_point_dir_, VOLUME_TYPE_TESTING, chromeos::DEVICE_TYPE_UNKNOWN,
+        false /* read_only */);
   }
 
  private:
@@ -424,15 +422,13 @@
   // FileSystemExtensionApiTestBase override.
   void AddTestMountPoint() override {
     EXPECT_TRUE(
-        content::BrowserContext::GetMountPoints(browser()->profile())
-            ->RegisterFileSystem(kRestrictedMountPointName,
-                                 storage::kFileSystemTypeRestrictedNativeLocal,
-                                 storage::FileSystemMountOption(),
-                                 mount_point_dir_));
-    VolumeManager::Get(browser()->profile())
-        ->AddVolumeForTesting(mount_point_dir_, VOLUME_TYPE_TESTING,
-                              chromeos::DEVICE_TYPE_UNKNOWN,
-                              true /* read_only */);
+        content::BrowserContext::GetMountPoints(profile())->RegisterFileSystem(
+            kRestrictedMountPointName,
+            storage::kFileSystemTypeRestrictedNativeLocal,
+            storage::FileSystemMountOption(), mount_point_dir_));
+    VolumeManager::Get(profile())->AddVolumeForTesting(
+        mount_point_dir_, VOLUME_TYPE_TESTING, chromeos::DEVICE_TYPE_UNKNOWN,
+        true /* read_only */);
   }
 
  private:
@@ -465,7 +461,7 @@
 
   // FileSystemExtensionApiTestBase override.
   void AddTestMountPoint() override {
-    test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
+    test_util::WaitUntilDriveMountPointIsAdded(profile());
   }
 
   // FileSystemExtensionApiTestBase override.
@@ -550,7 +546,7 @@
   }
 
   void AddTestMountPoint() override {
-    test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
+    test_util::WaitUntilDriveMountPointIsAdded(profile());
     test_util::WaitUntilDriveMountPointIsAdded(second_profile_);
   }
 
@@ -571,7 +567,7 @@
     const char kResourceId[] = "unique-id-for-multiprofile-copy-test";
     drive::FakeDriveService* const main_service =
         static_cast<drive::FakeDriveService*>(
-            drive::util::GetDriveServiceByProfile(browser()->profile()));
+            drive::util::GetDriveServiceByProfile(profile()));
     drive::FakeDriveService* const sub_service =
         static_cast<drive::FakeDriveService*>(
             drive::util::GetDriveServiceByProfile(second_profile_));
@@ -625,16 +621,14 @@
 
   // FileSystemExtensionApiTestBase override.
   void AddTestMountPoint() override {
-    EXPECT_TRUE(content::BrowserContext::GetMountPoints(browser()->profile())
-                    ->RegisterFileSystem(kLocalMountPointName,
-                                         storage::kFileSystemTypeNativeLocal,
-                                         storage::FileSystemMountOption(),
-                                         local_mount_point_dir_));
-    VolumeManager::Get(browser()->profile())
-        ->AddVolumeForTesting(local_mount_point_dir_, VOLUME_TYPE_TESTING,
-                              chromeos::DEVICE_TYPE_UNKNOWN,
-                              false /* read_only */);
-    test_util::WaitUntilDriveMountPointIsAdded(browser()->profile());
+    EXPECT_TRUE(
+        content::BrowserContext::GetMountPoints(profile())->RegisterFileSystem(
+            kLocalMountPointName, storage::kFileSystemTypeNativeLocal,
+            storage::FileSystemMountOption(), local_mount_point_dir_));
+    VolumeManager::Get(profile())->AddVolumeForTesting(
+        local_mount_point_dir_, VOLUME_TYPE_TESTING,
+        chromeos::DEVICE_TYPE_UNKNOWN, false /* read_only */);
+    test_util::WaitUntilDriveMountPointIsAdded(profile());
   }
 
  protected:
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 317bad1..40701a47 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -42,11 +42,23 @@
     return *this;
   }
 
+  // Show the startup browser. Some tests invoke the file picker dialog during
+  // the test. Requesting a file picker from a background page is forbidden by
+  // the apps platform, and it's a bug that these tests do so.
+  // FindRuntimeContext() in select_file_dialog_extension.cc will use the last
+  // active browser in this case, which requires a Browser to be present. See
+  // https://crbug.com/736930.
+  TestCase& WithBrowser() {
+    with_browser = true;
+    return *this;
+  }
+
   const char* test_case_name = nullptr;
   GuestMode guest_mode = NOT_IN_GUEST_MODE;
   bool trusted_events = false;
   bool tablet_mode = false;
   bool enable_drivefs = false;
+  bool with_browser = false;
 };
 
 // EventCase: FilesAppBrowserTest with trusted JS Events.
@@ -81,6 +93,9 @@
 
   GuestMode GetGuestMode() const override { return GetParam().guest_mode; }
   bool GetEnableDriveFs() const override { return GetParam().enable_drivefs; }
+  bool GetRequiresStartupBrowser() const override {
+    return GetParam().with_browser;
+  }
 
   const char* GetTestCaseName() const override {
     return GetParam().test_case_name;
@@ -407,33 +422,38 @@
         EventCase("tabindexFocusDownloads"),
         EventCase("tabindexFocusDownloads").InGuestMode(),
         EventCase("tabindexFocusDirectorySelected"),
-        EventCase("tabindexOpenDialogDrive"),
-        EventCase("tabindexOpenDialogDrive").EnableDriveFs(),
-        EventCase("tabindexOpenDialogDownloads"),
-        EventCase("tabindexOpenDialogDownloads").InGuestMode(),
-        EventCase("tabindexSaveFileDialogDrive"),
-        EventCase("tabindexSaveFileDialogDrive").EnableDriveFs(),
-        EventCase("tabindexSaveFileDialogDownloads"),
-        EventCase("tabindexSaveFileDialogDownloads").InGuestMode()));
+        EventCase("tabindexOpenDialogDrive").WithBrowser(),
+        EventCase("tabindexOpenDialogDrive").WithBrowser().EnableDriveFs(),
+        EventCase("tabindexOpenDialogDownloads").WithBrowser(),
+        EventCase("tabindexOpenDialogDownloads").WithBrowser().InGuestMode(),
+        EventCase("tabindexSaveFileDialogDrive").WithBrowser(),
+        EventCase("tabindexSaveFileDialogDrive").WithBrowser().EnableDriveFs(),
+        EventCase("tabindexSaveFileDialogDownloads").WithBrowser(),
+        EventCase("tabindexSaveFileDialogDownloads")
+            .WithBrowser()
+            .InGuestMode()));
 
 WRAPPED_INSTANTIATE_TEST_CASE_P(
     FileDialog, /* file_dialog.js */
     FilesAppBrowserTest,
     ::testing::Values(
-        TestCase("openFileDialogUnload"),
-        TestCase("openFileDialogDownloads"),
-        TestCase("openFileDialogDownloads").InGuestMode(),
-        TestCase("openFileDialogDownloads").InIncognito(),
-        TestCase("openFileDialogCancelDownloads"),
-        TestCase("openFileDialogEscapeDownloads"),
-        TestCase("openFileDialogDrive"),
-        TestCase("openFileDialogDrive").InIncognito(),
-        TestCase("openFileDialogDrive").EnableDriveFs(),
-        TestCase("openFileDialogDrive").InIncognito().EnableDriveFs(),
-        TestCase("openFileDialogCancelDrive"),
-        TestCase("openFileDialogCancelDrive").EnableDriveFs(),
-        TestCase("openFileDialogEscapeDrive"),
-        TestCase("openFileDialogEscapeDrive").EnableDriveFs()));
+        TestCase("openFileDialogUnload").WithBrowser(),
+        TestCase("openFileDialogDownloads").WithBrowser(),
+        TestCase("openFileDialogDownloads").WithBrowser().InGuestMode(),
+        TestCase("openFileDialogDownloads").WithBrowser().InIncognito(),
+        TestCase("openFileDialogCancelDownloads").WithBrowser(),
+        TestCase("openFileDialogEscapeDownloads").WithBrowser(),
+        TestCase("openFileDialogDrive").WithBrowser(),
+        TestCase("openFileDialogDrive").WithBrowser().InIncognito(),
+        TestCase("openFileDialogDrive").WithBrowser().EnableDriveFs(),
+        TestCase("openFileDialogDrive")
+            .WithBrowser()
+            .InIncognito()
+            .EnableDriveFs(),
+        TestCase("openFileDialogCancelDrive").WithBrowser(),
+        TestCase("openFileDialogCancelDrive").WithBrowser().EnableDriveFs(),
+        TestCase("openFileDialogEscapeDrive").WithBrowser(),
+        TestCase("openFileDialogEscapeDrive").WithBrowser().EnableDriveFs()));
 
 WRAPPED_INSTANTIATE_TEST_CASE_P(
     CopyBetweenWindows, /* copy_between_windows.js */
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 67bbd38..830b39d 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -890,6 +890,23 @@
   // Use a fake audio stream crbug.com/835626
   command_line->AppendSwitch(switches::kDisableAudioOutput);
 
+  if (!GetRequiresStartupBrowser()) {
+    // Don't sink time into showing an unused browser window.
+    // InProcessBrowserTest::browser() will be null.
+    command_line->AppendSwitch(switches::kNoStartupWindow);
+
+    // Without a browser window, opening an app window, then closing it will
+    // trigger browser shutdown. Usually this is fine, except it also prevents
+    // any _new_ app window being created, should a test want to do that.
+    // (At the time of writing, exactly one does).
+    // Although in this path no browser is created (and so one can never
+    // close..), setting this to false prevents InProcessBrowserTest from adding
+    // the kDisableZeroBrowsersOpenForTests flag, which would prevent
+    // chrome_browser_main_chromeos from adding the keepalive that normally
+    // stops chromeos from shutting down unexpectedly.
+    set_exit_when_last_browser_closes(false);
+  }
+
   if (IsGuestModeTest()) {
     command_line->AppendSwitch(chromeos::switches::kGuestSession);
     command_line->AppendSwitchNative(chromeos::switches::kLoginUser, "$guest");
@@ -963,6 +980,7 @@
 void FileManagerBrowserTestBase::SetUpOnMainThread() {
   extensions::ExtensionApiTest::SetUpOnMainThread();
   CHECK(profile());
+  CHECK_EQ(!!browser(), GetRequiresStartupBrowser());
 
   CHECK(local_volume_->Mount(profile()));
 
@@ -979,8 +997,7 @@
     // CustomMountPointCallback. TODO(joelhockey): It would be better if the
     // crostini interface allowed for testing without such tight coupling.
     crostini_volume_ = std::make_unique<CrostiniTestVolume>();
-    browser()->profile()->GetPrefs()->SetBoolean(
-        crostini::prefs::kCrostiniEnabled, true);
+    profile()->GetPrefs()->SetBoolean(crostini::prefs::kCrostiniEnabled, true);
     crostini::CrostiniManager::GetInstance()->set_skip_restart_for_testing();
     chromeos::DBusThreadManager* dbus_thread_manager =
         chromeos::DBusThreadManager::Get();
@@ -1018,6 +1035,10 @@
   return false;
 }
 
+bool FileManagerBrowserTestBase::GetRequiresStartupBrowser() const {
+  return false;
+}
+
 void FileManagerBrowserTestBase::StartTest() {
   LOG(INFO) << "FileManagerBrowserTest::StartTest " << GetTestCaseName();
   static const base::FilePath test_extension_dir =
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 25d62c77..1b06f15 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -41,6 +41,7 @@
   // Overrides for each FileManagerBrowserTest test extension type.
   virtual GuestMode GetGuestMode() const = 0;
   virtual bool GetEnableDriveFs() const;
+  virtual bool GetRequiresStartupBrowser() const;
   virtual const char* GetTestCaseName() const = 0;
   virtual const char* GetTestExtensionManifestName() const = 0;
 
diff --git a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
index 24fb7fe..a1a1ac8f 100644
--- a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
@@ -27,6 +27,7 @@
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chromeos/chromeos_paths.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/cryptohome/tpm_util.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_auth_policy_client.h"
 #include "chromeos/dbus/fake_cryptohome_client.h"
@@ -157,8 +158,7 @@
               loop.QuitClosure(), test_realm_));
       loop.Run();
     }
-    ASSERT_TRUE(AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting(
-        test_realm_));
+    ASSERT_TRUE(tpm_util::LockDeviceActiveDirectoryForTesting(test_realm_));
     {
       base::RunLoop loop;
       fake_auth_policy_client()->RefreshDevicePolicy(base::BindOnce(
diff --git a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
index 188942d..90d7ff9a 100644
--- a/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller_browsertest.cc
@@ -39,9 +39,9 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/cryptohome/tpm_util.h"
 #include "chromeos/dbus/fake_auth_policy_client.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
-#include "chromeos/login/auth/authpolicy_login_helper.h"
 #include "chromeos/login/auth/key.h"
 #include "chromeos/login/auth/mock_url_fetchers.h"
 #include "chromeos/login/auth/user_context.h"
@@ -840,8 +840,8 @@
   void SetUpInProcessBrowserTestFixture() override {
     ExistingUserControllerTest::SetUpInProcessBrowserTestFixture();
     fake_authpolicy_client()->DisableOperationDelayForTesting();
-    ASSERT_TRUE(AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting(
-        kActiveDirectoryRealm));
+    ASSERT_TRUE(
+        tpm_util::LockDeviceActiveDirectoryForTesting(kActiveDirectoryRealm));
     RefreshDevicePolicy();
     EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
         .WillRepeatedly(Return(true));
diff --git a/chrome/browser/chromeos/login/screens/gaia_view.h b/chrome/browser/chromeos/login/screens/gaia_view.h
index a1d623a6..665c0d94 100644
--- a/chrome/browser/chromeos/login/screens/gaia_view.h
+++ b/chrome/browser/chromeos/login/screens/gaia_view.h
@@ -43,10 +43,6 @@
                                        const std::string& password,
                                        const std::string& services) = 0;
 
-  // Cancels the request to show the sign-in screen while the asynchronous
-  // clean-up process that precedes the screen being shown is in progress.
-  virtual void CancelShowGaiaAsync() = 0;
-
  private:
   DISALLOW_COPY_AND_ASSIGN(GaiaView);
 };
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
index 5e5fcf1..5e7bd9e 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
@@ -77,35 +77,35 @@
   DCHECK(GetOobeUI());
   GetOobeUI()->signin_screen_handler()->ShowPasswordChangedDialog(
       show_password_error, email);
-  dialog_->Show(false /*closable_by_esc*/);
+  dialog_->Show();
 }
 
 void LoginDisplayHostMojo::ShowWhitelistCheckFailedError() {
   DCHECK(GetOobeUI());
   GetOobeUI()->signin_screen_handler()->ShowWhitelistCheckFailedError();
-  dialog_->Show(true /*closable_by_esc*/);
+  dialog_->Show();
 }
 
 void LoginDisplayHostMojo::ShowUnrecoverableCrypthomeErrorDialog() {
   DCHECK(GetOobeUI());
   GetOobeUI()->signin_screen_handler()->ShowUnrecoverableCrypthomeErrorDialog();
-  dialog_->Show(false /*closable_by_esc*/);
+  dialog_->Show();
 }
 
 void LoginDisplayHostMojo::ShowErrorScreen(LoginDisplay::SigninError error_id) {
   DCHECK(GetOobeUI());
   GetOobeUI()->signin_screen_handler()->ShowErrorScreen(error_id);
-  dialog_->Show(false /*closable_by_esc*/);
+  dialog_->Show();
 }
 
 void LoginDisplayHostMojo::ShowSigninUI(const std::string& email) {
   DCHECK(GetOobeUI());
   GetOobeUI()->signin_screen_handler()->ShowSigninUI(email);
-  dialog_->Show(true /*closable_by_esc*/);
+  dialog_->Show();
 }
 
 void LoginDisplayHostMojo::ShowDialogForCaptivePortal() {
-  dialog_->Show(false /*closable_by_esc*/);
+  dialog_->Show();
 }
 
 void LoginDisplayHostMojo::HideDialogForCaptivePortal() {
@@ -162,10 +162,7 @@
   wizard_controller_ = std::make_unique<WizardController>();
   wizard_controller_->Init(first_screen);
 
-  // Post login screens (aside from powerwash) should not be closable by escape
-  // key.
-  bool closable_by_esc = first_screen == OobeScreen::SCREEN_OOBE_RESET;
-  dialog_->Show(closable_by_esc);
+  dialog_->Show();
 }
 
 WizardController* LoginDisplayHostMojo::GetWizardController() {
@@ -193,14 +190,6 @@
     return;
   }
 
-  if (signin_screen_started_) {
-    dialog_->Hide();
-    GetOobeUI()->GetGaiaScreenView()->ShowGaiaAsync(base::nullopt);
-    return;
-  }
-
-  signin_screen_started_ = true;
-
   existing_user_controller_ = std::make_unique<ExistingUserController>();
   login_display_->set_delegate(existing_user_controller_.get());
 
@@ -260,15 +249,10 @@
       GetOobeUI()->GetGaiaScreenView()->ShowGaiaAsync(prefilled_account);
     LoadWallpaper(*prefilled_account);
   } else {
-    // Restore the gaia screen if the OOBE dialog is currently occupied by a
-    // wizard.
-    if (GetOobeUI()->current_screen() != OobeScreen::SCREEN_GAIA_SIGNIN)
-      GetOobeUI()->GetGaiaScreenView()->ShowGaiaAsync(base::nullopt);
-
     LoadSigninWallpaper();
   }
 
-  dialog_->Show(can_close /*closable_by_esc*/);
+  dialog_->Show();
   return;
 }
 
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.h b/chrome/browser/chromeos/login/ui/login_display_host_mojo.h
index 207347f21..1bc1ba0 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.h
+++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.h
@@ -155,11 +155,6 @@
   // Updates UI when version info is changed.
   std::unique_ptr<MojoVersionInfoDispatcher> version_info_updater_;
 
-  // Set to true on the first call to OnStartSignInScreen. Never reset to false.
-  // This is used to avoid doing redundant work (e.g. LockScreen::Show()) if the
-  // login screen is already showing and OnStartSignInScreen is called again.
-  bool signin_screen_started_ = false;
-
   base::WeakPtrFactory<LoginDisplayHostMojo> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(LoginDisplayHostMojo);
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
index 34656d7..d80fed1 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.cc
@@ -83,7 +83,7 @@
   return dialog_view_->web_contents();
 }
 
-void OobeUIDialogDelegate::Show(bool closable_by_esc) {
+void OobeUIDialogDelegate::Show() {
   LoginScreenClient::Get()->login_screen()->NotifyOobeDialogVisibility(true);
   dialog_widget_->Show();
 }
@@ -94,7 +94,7 @@
           ->GetDisplayNearestWindow(dialog_widget_->GetNativeWindow())
           .size();
   UpdateSizeAndPosition(size.width(), size.height());
-  Show(false /*closable_by_esc*/);
+  Show();
   showing_fullscreen_ = true;
 }
 
diff --git a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
index a584aa2..f45bad5 100644
--- a/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
+++ b/chrome/browser/chromeos/login/ui/oobe_ui_dialog_delegate.h
@@ -59,9 +59,7 @@
   ~OobeUIDialogDelegate() override;
 
   // Show the dialog widget.
-  // |closable_by_esc|: Whether the widget will be hidden after press escape
-  // key.
-  void Show(bool closable_by_esc);
+  void Show();
 
   // Show the dialog widget stretched to full screen.
   void ShowFullScreen();
diff --git a/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc b/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc
index 3b069d3..4e270d5c 100644
--- a/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/component_active_directory_policy_browsertest.cc
@@ -14,11 +14,11 @@
 #include "chrome/browser/policy/profile_policy_connector_factory.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/cryptohome/tpm_util.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_cryptohome_client.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
 #include "chromeos/dbus/login_manager/policy_descriptor.pb.h"
-#include "chromeos/login/auth/authpolicy_login_helper.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/policy_builder.h"
 #include "components/policy/core/common/policy_service.h"
@@ -86,15 +86,13 @@
         base::WrapUnique(session_manager_client_));
 
     // TODO(crbug.com/836857): Can probably be removed after the bug is fixed.
-    // Right now, this is necessary since AuthPolicyLoginHelper talks to
-    // CryptohomeClient through tpm_util directly instead of using
-    // InstallAttributes, but other code checks state like
-    // InstallAttributes::IsActiveDirectoryManaged().
+    // Right now, this is necessary since tpm_util talks to CryptohomeClient
+    // directly instead of using InstallAttributes, but other code checks state
+    // like InstallAttributes::IsActiveDirectoryManaged().
     chromeos::DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient(
         std::make_unique<chromeos::FakeCryptohomeClient>());
     ASSERT_TRUE(
-        chromeos::AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting(
-            kTestDomain));
+        chromeos::tpm_util::LockDeviceActiveDirectoryForTesting(kTestDomain));
     ExtensionBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc
index 2156bb7f..4e32758 100644
--- a/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc
+++ b/chrome/browser/chromeos/policy/device_policy_cros_browser_test.cc
@@ -16,9 +16,9 @@
 #include "chrome/browser/chromeos/settings/install_attributes.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/chromeos_paths.h"
+#include "chromeos/cryptohome/tpm_util.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
-#include "chromeos/login/auth/authpolicy_login_helper.h"
 #include "crypto/rsa_private_key.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -59,9 +59,7 @@
 void DevicePolicyCrosTestHelper::MarkAsActiveDirectoryEnterpriseOwned(
     const std::string& realm) {
   OverridePaths();
-  ASSERT_TRUE(
-      chromeos::AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting(
-          realm));
+  ASSERT_TRUE(chromeos::tpm_util::LockDeviceActiveDirectoryForTesting(realm));
 }
 
 void DevicePolicyCrosTestHelper::MarkAsEnterpriseOwned() {
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
index 0853e1b..2f3d8a6 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
@@ -11,9 +11,11 @@
 #include "chrome/browser/chromeos/printing/printers_sync_bridge.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -46,7 +48,9 @@
 SyncedPrintersManagerFactory::SyncedPrintersManagerFactory()
     : BrowserContextKeyedServiceFactory(
           "SyncedPrintersManager",
-          BrowserContextDependencyManager::GetInstance()) {}
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
+}
 
 SyncedPrintersManagerFactory::~SyncedPrintersManagerFactory() {}
 
@@ -55,8 +59,7 @@
   Profile* profile = Profile::FromBrowserContext(browser_context);
 
   syncer::OnceModelTypeStoreFactory store_factory =
-      browser_sync::ProfileSyncService::GetModelTypeStoreFactory(
-          profile->GetPath());
+      ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory();
 
   std::unique_ptr<PrintersSyncBridge> sync_bridge =
       std::make_unique<PrintersSyncBridge>(
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
index 8d339d5..8292c6d3 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_unittest.cc
@@ -21,6 +21,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/sync/model/fake_model_type_change_processor.h"
 #include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_store_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -95,7 +96,8 @@
       : manager_(SyncedPrintersManager::Create(
             &profile_,
             std::make_unique<PrintersSyncBridge>(
-                base::Bind(&syncer::ModelTypeStore::CreateInMemoryStoreForTest),
+                syncer::ModelTypeStoreTestUtil::
+                    FactoryForInMemoryStoreForTest(),
                 base::BindRepeating(
                     base::IgnoreResult(&base::debug::DumpWithoutCrashing))))) {
     base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/consent_auditor/consent_auditor_factory.cc b/chrome/browser/consent_auditor/consent_auditor_factory.cc
index 9967473f..bd4b625 100644
--- a/chrome/browser/consent_auditor/consent_auditor_factory.cc
+++ b/chrome/browser/consent_auditor/consent_auditor_factory.cc
@@ -4,9 +4,13 @@
 
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/bind_helpers.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "chrome/common/channel_info.h"
 #include "components/browser_sync/profile_sync_service.h"
@@ -18,6 +22,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 #include "components/version_info/version_info.h"
 
@@ -43,6 +48,7 @@
           "ConsentAuditor",
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(browser_sync::UserEventServiceFactory::GetInstance());
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
 }
 
 ConsentAuditorFactory::~ConsentAuditorFactory() {}
@@ -55,8 +61,8 @@
   syncer::UserEventService* user_event_service = nullptr;
   if (base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType)) {
     syncer::OnceModelTypeStoreFactory store_factory =
-        browser_sync::ProfileSyncService::GetModelTypeStoreFactory(
-            profile->GetPath());
+        ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory();
+
     auto change_processor =
         std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
             syncer::USER_CONSENTS,
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 178e898..b401983 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -21,6 +21,7 @@
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/drive/drive_pref_names.h"
 #include "components/language/core/browser/pref_names.h"
+#include "components/omnibox/browser/omnibox_pref_names.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/payments/core/payment_prefs.h"
 #include "components/prefs/pref_service.h"
@@ -208,6 +209,8 @@
   (*s_whitelist)
       [::unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled] =
           settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_whitelist)[::omnibox::kDocumentSuggestEnabled] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
   // Languages page
   (*s_whitelist)[spellcheck::prefs::kSpellCheckEnable] =
diff --git a/chrome/browser/extensions/external_pref_loader_unittest.cc b/chrome/browser/extensions/external_pref_loader_unittest.cc
index 20aa6b7..97abd10 100644
--- a/chrome/browser/extensions/external_pref_loader_unittest.cc
+++ b/chrome/browser/extensions/external_pref_loader_unittest.cc
@@ -31,7 +31,6 @@
   // FakeSyncService:
   int GetDisableReasons() const override { return disable_reasons_; }
   bool IsFirstSetupComplete() const override { return true; }
-  bool IsSyncActive() const override { return true; }
   syncer::ModelTypeSet GetActiveDataTypes() const override {
     switch (synced_types_) {
       case SyncedTypes::ALL:
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d7ce801..3857967 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -65,6 +65,10 @@
 const char kEnableBlinkHeapIncrementalMarkingDescription[] =
     "Enable incremental marking in Blink";
 
+const char kEnableBloatedRendererDetectionName[] = "Bloated renderer detection";
+const char kEnableBloatedRendererDetectionDescription[] =
+    "Enable bloated renderer detection";
+
 const char kSystemTrayUnifiedName[] = "New system menu";
 const char kSystemTrayUnifiedDescription[] =
     "Enable the experimental system menu.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 99e69d759..b15397f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -67,6 +67,9 @@
 extern const char kEnableBlinkHeapIncrementalMarkingName[];
 extern const char kEnableBlinkHeapIncrementalMarkingDescription[];
 
+extern const char kEnableBloatedRendererDetectionName[];
+extern const char kEnableBloatedRendererDetectionDescription[];
+
 extern const char kSystemTrayUnifiedName[];
 extern const char kSystemTrayUnifiedDescription[];
 
diff --git a/chrome/browser/net/proxy_browsertest.cc b/chrome/browser/net/proxy_browsertest.cc
index 88cb8db..53b4700 100644
--- a/chrome/browser/net/proxy_browsertest.cc
+++ b/chrome/browser/net/proxy_browsertest.cc
@@ -22,6 +22,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_source.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/common/content_switches.h"
@@ -34,8 +35,7 @@
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "net/test/test_data_directory.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
-#include "net/url_request/url_fetcher.h"
-#include "net/url_request/url_fetcher_delegate.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
 namespace {
@@ -242,20 +242,6 @@
   DISALLOW_COPY_AND_ASSIGN(HangingPacRequestProxyScriptBrowserTest);
 };
 
-// URLFetcherDelegate that expects a request to hang.
-class HangingURLFetcherDelegate : public net::URLFetcherDelegate {
- public:
-  HangingURLFetcherDelegate() {}
-  ~HangingURLFetcherDelegate() override {}
-
-  void OnURLFetchComplete(const net::URLFetcher* source) override {
-    ADD_FAILURE() << "This request should never complete.";
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(HangingURLFetcherDelegate);
-};
-
 // Check that the URLRequest for a PAC that is still alive during shutdown is
 // safely cleaned up.  This test relies on AssertNoURLRequests being called on
 // the main URLRequestContext.
@@ -263,12 +249,20 @@
   // Request that should hang while trying to request the PAC script.
   // Enough requests are created on startup that this probably isn't needed, but
   // best to be safe.
-  HangingURLFetcherDelegate hanging_request_delegate;
-  std::unique_ptr<net::URLFetcher> hanging_fetcher = net::URLFetcher::Create(
-      GURL("http://blah/"), net::URLFetcher::GET, &hanging_request_delegate,
-      TRAFFIC_ANNOTATION_FOR_TESTS);
-  hanging_fetcher->SetRequestContext(browser()->profile()->GetRequestContext());
-  hanging_fetcher->Start();
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = GURL("http://blah/");
+  auto simple_loader = network::SimpleURLLoader::Create(
+      std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  auto* storage_partition =
+      content::BrowserContext::GetDefaultStoragePartition(browser()->profile());
+  auto url_loader_factory =
+      storage_partition->GetURLLoaderFactoryForBrowserProcess();
+  simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory.get(),
+      base::BindOnce([](std::unique_ptr<std::string> body) {
+        ADD_FAILURE() << "This request should never complete.";
+      }));
 
   connection_listener_->WaitForConnections();
 }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index b5e8f733..5aafd721 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -193,8 +193,8 @@
 
     EXPECT_CALL(*mock_sync_service, IsFirstSetupComplete())
         .WillRepeatedly(Return(true));
-    EXPECT_CALL(*mock_sync_service, IsSyncActive())
-        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_sync_service, GetState())
+        .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
     return mock_sync_service;
   }
 
diff --git a/chrome/browser/password_manager/password_accessory_controller.cc b/chrome/browser/password_manager/password_accessory_controller.cc
index 6f5c332..f28b6f1 100644
--- a/chrome/browser/password_manager/password_accessory_controller.cc
+++ b/chrome/browser/password_manager/password_accessory_controller.cc
@@ -205,13 +205,17 @@
 
 void PasswordAccessoryController::OnFilledIntoFocusedField(
     autofill::FillingStatus status) {
-  // TODO(crbug/853766): Record success rate.
-  // TODO(fhorschig): Update UI by hiding the sheet or communicating the error.
+  if (status != autofill::FillingStatus::SUCCESS)
+    return;                      // TODO(crbug/853766): Record success rate.
+  view_->CloseAccessorySheet();  // The sheet's purpose is fulfilled.
 }
 
 void PasswordAccessoryController::RefreshSuggestionsForField(
     bool is_fillable,
     bool is_password_field) {
+  // When new suggestions are requested, the keyboard will pop open. To avoid
+  // that any open sheet is pushed up, close it now. Doesn't affect the bar.
+  view_->CloseAccessorySheet();
   // TODO(crbug/853766): Record CTR metric.
   SendViewItems(is_password_field);
 }
diff --git a/chrome/browser/password_manager/password_accessory_controller_unittest.cc b/chrome/browser/password_manager/password_accessory_controller_unittest.cc
index 4d7ee08..f8a41561 100644
--- a/chrome/browser/password_manager/password_accessory_controller_unittest.cc
+++ b/chrome/browser/password_manager/password_accessory_controller_unittest.cc
@@ -29,11 +29,13 @@
 #include "ui/base/l10n/l10n_util.h"
 
 namespace {
+using autofill::FillingStatus;
 using autofill::PasswordForm;
 using autofill::password_generation::PasswordGenerationUIData;
 using base::ASCIIToUTF16;
 using base::UTF16ToWide;
 using testing::_;
+using testing::AnyNumber;
 using testing::ByMove;
 using testing::ElementsAre;
 using testing::Mock;
@@ -58,6 +60,7 @@
   MOCK_METHOD1(OnFillingTriggered, void(const base::string16& textToFill));
   MOCK_METHOD0(OnViewDestroyed, void());
   MOCK_METHOD1(OnAutomaticGenerationStatusChanged, void(bool));
+  MOCK_METHOD0(CloseAccessorySheet, void());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockPasswordAccessoryView);
@@ -246,6 +249,7 @@
         mock_dialog_factory_.Get());
     NavigateAndCommit(GURL("https://example.com"));
     FocusWebContentsOnMainFrame();
+    EXPECT_CALL(*view(), CloseAccessorySheet()).Times(AnyNumber());
   }
 
   PasswordAccessoryController* controller() {
@@ -374,6 +378,31 @@
                                            /*is_password_field=*/false);
 }
 
+// TODO(fhorschig): Check for recorded metrics here or similar to this.
+TEST_F(PasswordAccessoryControllerTest, ClosesViewOnSuccessfullFillingOnly) {
+  Mock::VerifyAndClearExpectations(view());  // Clear closing expectations.
+
+  // If the filling wasn't successful, no call is expected.
+  EXPECT_CALL(*view(), CloseAccessorySheet()).Times(0);
+  controller()->OnFilledIntoFocusedField(FillingStatus::ERROR_NOT_ALLOWED);
+  controller()->OnFilledIntoFocusedField(FillingStatus::ERROR_NO_VALID_FIELD);
+
+  // If the filling completed successfully, let the view know.
+  EXPECT_CALL(*view(), CloseAccessorySheet());
+  controller()->OnFilledIntoFocusedField(FillingStatus::SUCCESS);
+}
+
+// TODO(fhorschig): Check for recorded metrics here or similar to this.
+TEST_F(PasswordAccessoryControllerTest, ClosesViewWhenRefreshingSuggestions) {
+  Mock::VerifyAndClearExpectations(view());  // Clear closing expectations.
+  // Ignore Items - only the closing calls are interesting here.
+  EXPECT_CALL(*view(), OnItemsAvailable(_)).Times(AnyNumber());
+
+  EXPECT_CALL(*view(), CloseAccessorySheet());
+  controller()->RefreshSuggestionsForField(/*is_fillable=*/false,
+                                           /*is_password_field=*/false);
+}
+
 TEST_F(PasswordAccessoryControllerTest, RelaysAutomaticGenerationAvailable) {
   EXPECT_CALL(*view(), OnAutomaticGenerationStatusChanged(true));
   controller()->OnAutomaticGenerationStatusChanged(
@@ -385,6 +414,7 @@
   controller()->OnAutomaticGenerationStatusChanged(false, base::nullopt,
                                                    nullptr);
 }
+
 // Tests that if AutomaticGenerationStatusChanged(true) is called for different
 // password forms, the form and field signatures used for password generation
 // are updated.
diff --git a/chrome/browser/password_manager/password_accessory_view_interface.h b/chrome/browser/password_manager/password_accessory_view_interface.h
index 067dd3d..971128bc 100644
--- a/chrome/browser/password_manager/password_accessory_view_interface.h
+++ b/chrome/browser/password_manager/password_accessory_view_interface.h
@@ -71,6 +71,9 @@
   // in the keyboard accessory.
   virtual void OnAutomaticGenerationStatusChanged(bool available) = 0;
 
+  // Called to inform the view that the accessory sheet should be closed now.
+  virtual void CloseAccessorySheet() = 0;
+
  private:
   friend class PasswordAccessoryController;
   // Factory function used to create a concrete instance of this view.
diff --git a/chrome/browser/prefs/OWNERS b/chrome/browser/prefs/OWNERS
index e04d3d4..512f8a73 100644
--- a/chrome/browser/prefs/OWNERS
+++ b/chrome/browser/prefs/OWNERS
@@ -3,4 +3,6 @@
 gab@chromium.org
 pam@chromium.org
 
+per-file pref_service_incognito_whitelist.cc=rhalavati@chromium.org
+
 # COMPONENT: UI>Browser>Preferences
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 98209ef7..b0d4eb0e 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -93,6 +93,7 @@
 #include "components/ntp_snippets/user_classifier.h"
 #include "components/ntp_tiles/most_visited_sites.h"
 #include "components/offline_pages/buildflags/buildflags.h"
+#include "components/omnibox/browser/document_provider.h"
 #include "components/omnibox/browser/zero_suggest_provider.h"
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_manager.h"
@@ -522,6 +523,7 @@
   chrome_browser_net::RegisterPredictionOptionsProfilePrefs(registry);
   chrome_prefs::RegisterProfilePrefs(registry);
   dom_distiller::DistilledPagePrefs::RegisterProfilePrefs(registry);
+  DocumentProvider::RegisterProfilePrefs(registry);
   DownloadPrefs::RegisterProfilePrefs(registry);
   HostContentSettingsMap::RegisterProfilePrefs(registry);
   ImportantSitesUtil::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prefs/pref_service_incognito_whitelist.h b/chrome/browser/prefs/pref_service_incognito_whitelist.h
index 15f53b0..81df013 100644
--- a/chrome/browser/prefs/pref_service_incognito_whitelist.h
+++ b/chrome/browser/prefs/pref_service_incognito_whitelist.h
@@ -10,7 +10,7 @@
 namespace prefs {
 
 // Populate a whitelist of all preferences that are stored on disk in incognito
-// mode. Please refere to the comments in .cc file.
+// mode. Please refer to the comments in .cc file.
 void GetIncognitoWhitelist(std::vector<const char*>* whitelist);
 
 }  // namespace prefs
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 0cfb84b7..b7df678 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -70,6 +70,7 @@
 #include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -143,7 +144,7 @@
 #include "chrome/browser/extensions/browser_context_keyed_service_factories.h"
 #include "chrome/browser/extensions/extension_management.h"
 #include "chrome/browser/ui/bookmarks/enhanced_bookmark_key_service_factory.h"
-#include "chrome/browser/web_applications/policy/web_app_policy_manager_factory.h"
+#include "chrome/browser/web_applications/web_app_provider_factory.h"
 #include "extensions/browser/api/networking_private/networking_private_delegate_factory.h"
 #include "extensions/browser/browser_context_keyed_service_factories.h"
 #if defined(OS_CHROMEOS)
@@ -198,7 +199,6 @@
   extensions::ExtensionManagementFactory::GetInstance();
   chrome_extensions::EnsureBrowserContextKeyedServiceFactoriesBuilt();
   chrome_apps::EnsureBrowserContextKeyedServiceFactoriesBuilt();
-  web_app::WebAppPolicyManagerFactory::GetInstance();
 #endif
 
 #if BUILDFLAG(ENABLE_APP_LIST)
@@ -234,6 +234,7 @@
   CloudPrintProxyServiceFactory::GetInstance();
 #endif
   ConsentAuditorFactory::GetInstance();
+  ContentSuggestionsServiceFactory::GetInstance();
   CookieSettingsFactory::GetInstance();
   NotifierStateTrackerFactory::GetInstance();
   data_use_measurement::ChromeDataUseAscriberServiceFactory::GetInstance();
@@ -260,6 +261,11 @@
   extensions::VerifyTrustAPI::GetFactoryInstance();
 #endif
   FaviconServiceFactory::GetInstance();
+#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
+  feature_engagement::BookmarkTrackerFactory::GetInstance();
+  feature_engagement::IncognitoWindowTrackerFactory::GetInstance();
+  feature_engagement::NewTabTrackerFactory::GetInstance();
+#endif
   feature_engagement::TrackerFactory::GetInstance();
   FindBarStateFactory::GetInstance();
   GAIAInfoUpdateServiceFactory::GetInstance();
@@ -302,18 +308,15 @@
 #endif
 #if !defined(OS_ANDROID)
   MediaGalleriesPreferencesFactory::GetInstance();
-  NTPResourceCacheFactory::GetInstance();
 #endif
-#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
-  feature_engagement::BookmarkTrackerFactory::GetInstance();
-  feature_engagement::IncognitoWindowTrackerFactory::GetInstance();
-  feature_engagement::NewTabTrackerFactory::GetInstance();
-#endif
-  ContentSuggestionsServiceFactory::GetInstance();
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
     (defined(OS_LINUX) && !defined(OS_CHROMEOS))
   metrics::DesktopProfileSessionDurationsServiceFactory::GetInstance();
 #endif
+  ModelTypeStoreServiceFactory::GetInstance();
+#if !defined(OS_ANDROID)
+  NTPResourceCacheFactory::GetInstance();
+#endif
   PasswordStoreFactory::GetInstance();
 #if !defined(OS_ANDROID)
   PinnedTabServiceFactory::GetInstance();
@@ -378,6 +381,9 @@
 #if !defined(OS_ANDROID)
   UsbChooserContextFactory::GetInstance();
 #endif
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  web_app::WebAppProviderFactory::GetInstance();
+#endif
   WebDataServiceFactory::GetInstance();
 }
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 253d2c4..263a7a7 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -228,11 +228,6 @@
   CheckFeatureUsage(reader.get(), details);
 }
 
-bool GetTabLifecyclesEnterprisePolicy() {
-  return TabLifecycleUnitSource::GetInstance()
-      ->tab_lifecycles_enterprise_policy();
-}
-
 InterventionPolicyDatabase* GetInterventionPolicyDatabase() {
   return TabLifecycleUnitSource::GetInstance()->intervention_policy_database();
 }
@@ -465,11 +460,6 @@
     return false;
   }
 
-  if (!GetTabLifecyclesEnterprisePolicy()) {
-    decision_details->AddReason(
-        DecisionFailureReason::LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT);
-  }
-
   auto intervention_policy = GetInterventionPolicyDatabase()->GetFreezingPolicy(
       url::Origin::Create(GetWebContents()->GetLastCommittedURL()));
 
@@ -532,32 +522,10 @@
     return false;
   }
 
-  // Do not discard a tab that has already been discarded. Since this is being
-  // removed there is no way to communicate that to the heuristic. Treat this
-  // as a "trivial" rejection reason for now and return with an empty decision
-  // details.
-  // TODO(fdoray): Allow tabs to be discarded more than once.
-  // https://crbug.com/794622
-  if (discard_count_ > 0) {
-#if defined(OS_CHROMEOS)
-    // On ChromeOS this can be ignored for urgent discards, where running out of
-    // memory leads to a kernel panic.
-    if (reason != DiscardReason::kUrgent)
-      return false;
-#else
-    return false;
-#endif  // defined(OS_CHROMEOS)
-  }
-
   // We deliberately run through all of the logic without early termination.
   // This ensures that the decision details lists all possible reasons that the
   // transition can be denied.
 
-  if (!GetTabLifecyclesEnterprisePolicy()) {
-    decision_details->AddReason(
-        DecisionFailureReason::LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT);
-  }
-
   if (reason == DiscardReason::kProactive) {
     auto intervention_policy =
         GetInterventionPolicyDatabase()->GetDiscardingPolicy(
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
index 3a93dc76..b2e8440d 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.cc
@@ -4,10 +4,8 @@
 
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
 
-#include "base/bind.h"
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/resource_coordinator/discard_metrics_lifecycle_unit_observer.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
@@ -16,9 +14,6 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/recently_audible_helper.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
-#include "chrome/common/pref_names.h"
-#include "components/prefs/pref_change_registrar.h"
-#include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents_user_data.h"
 
 namespace resource_coordinator {
@@ -70,17 +65,6 @@
   // this manual lifetime management.
   if (auto* page_signal_receiver = PageSignalReceiver::GetInstance())
     page_signal_receiver->AddObserver(this);
-
-#if defined(GOOGLE_CHROME_BUILD)
-  // In production builds monitor the policy override of the lifecycles feature.
-  // This class owns the monitor so it is okay to use base::Unretained.
-  tab_lifecycles_enterprise_preference_monitor_ =
-      std::make_unique<TabLifecylesEnterprisePreferenceMonitor>(
-          g_browser_process->local_state(),
-          base::BindRepeating(
-              &TabLifecycleUnitSource::SetTabLifecyclesEnterprisePolicy,
-              base::Unretained(this)));
-#endif
 }
 
 TabLifecycleUnitSource::~TabLifecycleUnitSource() {
@@ -254,43 +238,6 @@
     lifecycle_unit->UpdateLifecycleState(state);
 }
 
-void TabLifecycleUnitSource::SetTabLifecyclesEnterprisePolicy(bool enabled) {
-  tab_lifecycles_enterprise_policy_ = enabled;
-}
-
-TabLifecylesEnterprisePreferenceMonitor::
-    TabLifecylesEnterprisePreferenceMonitor(
-        PrefService* pref_service,
-        OnPreferenceChangedCallback callback)
-    : pref_service_(pref_service), callback_(callback) {
-  // Create a registrar to track changes to the setting.
-  pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
-  pref_change_registrar_->Init(pref_service_);
-  pref_change_registrar_->Add(
-      prefs::kTabLifecyclesEnabled,
-      base::BindRepeating(&TabLifecylesEnterprisePreferenceMonitor::GetPref,
-                          base::Unretained(this)));
-
-  // Do an initial check of the value.
-  GetPref();
-}
-
-TabLifecylesEnterprisePreferenceMonitor::
-    ~TabLifecylesEnterprisePreferenceMonitor() = default;
-
-void TabLifecylesEnterprisePreferenceMonitor::GetPref() {
-  bool enabled = true;
-
-  // If the preference is set to false by enterprise policy then disable the
-  // lifecycles feature.
-  const PrefService::Preference* pref =
-      pref_service_->FindPreference(prefs::kTabLifecyclesEnabled);
-  if (pref->IsManaged() && !pref->GetValue()->GetBool())
-    enabled = false;
-
-  callback_.Run(enabled);
-}
-
 }  // namespace resource_coordinator
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index 928d953..8f39843 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -15,8 +15,6 @@
 #include "chrome/browser/ui/browser_tab_strip_tracker.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 
-class PrefChangeRegistrar;
-class PrefService;
 class TabStripModel;
 
 namespace content {
@@ -26,7 +24,6 @@
 namespace resource_coordinator {
 
 class InterventionPolicyDatabase;
-class TabLifecylesEnterprisePreferenceMonitor;
 class TabLifecycleObserver;
 class TabLifecycleUnitExternal;
 class UsageClock;
@@ -61,12 +58,6 @@
     return intervention_policy_database_;
   }
 
-  // Returns the state of the tab lifecycles feature enterprise control. This
-  // returns true if the feature should be enabled, false otherwise.
-  bool tab_lifecycles_enterprise_policy() const {
-    return tab_lifecycles_enterprise_policy_;
-  }
-
   class TabLifecycleUnitHolder;
 
  private:
@@ -133,9 +124,6 @@
                                const PageNavigationIdentity& page_navigation_id,
                                mojom::LifecycleState state) override;
 
-  // Callback for TabLifecyclesEnterprisePreferenceMonitor.
-  void SetTabLifecyclesEnterprisePolicy(bool enabled);
-
   // Tracks the BrowserList and all TabStripModels.
   BrowserTabStripTracker browser_tab_strip_tracker_;
 
@@ -156,39 +144,9 @@
   // A clock that advances when Chrome is in use.
   UsageClock* const usage_clock_;
 
-  // The enterprise policy for overriding the tab lifecycles feature.
-  bool tab_lifecycles_enterprise_policy_ = true;
-
-  // In official production builds this monitors policy settings and reflects
-  // them in |tab_lifecycles_enterprise_policy_|.
-  std::unique_ptr<TabLifecylesEnterprisePreferenceMonitor>
-      tab_lifecycles_enterprise_preference_monitor_;
-
   DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitSource);
 };
 
-// Helper class used for getting and monitoring enterprise-policy controlled
-// preferences that can control the tab lifecycles feature. Exposed for testing.
-class TabLifecylesEnterprisePreferenceMonitor {
- public:
-  using OnPreferenceChangedCallback = base::RepeatingCallback<void(bool)>;
-
-  // Creates a preference monitor that monitors the provided PrefService. When
-  // the preference is initially checked or changed its value is provided via
-  // the provided callback.
-  TabLifecylesEnterprisePreferenceMonitor(PrefService* pref_service,
-                                          OnPreferenceChangedCallback callback);
-
-  ~TabLifecylesEnterprisePreferenceMonitor();
-
- private:
-  void GetPref();
-
-  PrefService* pref_service_;
-  OnPreferenceChangedCallback callback_;
-  std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
-};
-
 }  // namespace resource_coordinator
 
 #endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_UNIT_SOURCE_H_
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
index 1919953..554ff62e 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -22,10 +22,7 @@
 #include "chrome/browser/resource_coordinator/time.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
-#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/web_contents_tester.h"
@@ -413,58 +410,6 @@
                     .GetPendingEntry());
   }
 
-  void CanOnlyDiscardOnceTest(DiscardReason reason) {
-    LifecycleUnit* background_lifecycle_unit = nullptr;
-    LifecycleUnit* foreground_lifecycle_unit = nullptr;
-    CreateTwoTabs(true /* focus_tab_strip */, &background_lifecycle_unit,
-                  &foreground_lifecycle_unit);
-    content::WebContents* initial_web_contents =
-        tab_strip_model_->GetWebContentsAt(0);
-
-    // It should be possible to discard the background tab.
-    ExpectCanDiscardTrueAllReasons(background_lifecycle_unit);
-
-    // Discard the tab.
-    EXPECT_EQ(LifecycleUnitState::ACTIVE,
-              background_lifecycle_unit->GetState());
-    EXPECT_CALL(tab_observer_, OnDiscardedStateChange(::testing::_, true));
-    background_lifecycle_unit->Discard(reason);
-
-    ::testing::Mock::VerifyAndClear(&tab_observer_);
-
-    TransitionFromPendingDiscardToDiscardedIfNeeded(reason,
-                                                    background_lifecycle_unit);
-
-    EXPECT_NE(initial_web_contents, tab_strip_model_->GetWebContentsAt(0));
-    EXPECT_FALSE(tab_strip_model_->GetWebContentsAt(0)
-                     ->GetController()
-                     .GetPendingEntry());
-
-    // Explicitly reload the tab. Expect the state to be LOADED.
-    EXPECT_CALL(tab_observer_, OnDiscardedStateChange(::testing::_, false));
-    tab_strip_model_->GetWebContentsAt(0)->GetController().Reload(
-        content::ReloadType::NORMAL, false);
-    ::testing::Mock::VerifyAndClear(&tab_observer_);
-    EXPECT_EQ(LifecycleUnitState::ACTIVE,
-              background_lifecycle_unit->GetState());
-    EXPECT_TRUE(tab_strip_model_->GetWebContentsAt(0)
-                    ->GetController()
-                    .GetPendingEntry());
-
-    // It shouldn't be possible to discard the background tab again, except for
-    // an urgent discard on ChromeOS.
-    ExpectCanDiscardFalseTrivial(background_lifecycle_unit,
-                                 DiscardReason::kExternal);
-    ExpectCanDiscardFalseTrivial(background_lifecycle_unit,
-                                 DiscardReason::kProactive);
-#if defined(OS_CHROMEOS)
-    ExpectCanDiscardTrue(background_lifecycle_unit, DiscardReason::kUrgent);
-#else
-    ExpectCanDiscardFalseTrivial(background_lifecycle_unit,
-                                 DiscardReason::kUrgent);
-#endif
-  }
-
   TabLifecycleUnitSource* source_ = nullptr;
   ::testing::StrictMock<MockLifecycleUnitSourceObserver> source_observer_;
   ::testing::StrictMock<MockTabLifecycleObserver> tab_observer_;
@@ -645,18 +590,6 @@
   DiscardAndExplicitlyReloadTest(DiscardReason::kExternal);
 }
 
-TEST_F(TabLifecycleUnitSourceTest, CanOnlyDiscardOnce_Urgent) {
-  CanOnlyDiscardOnceTest(DiscardReason::kUrgent);
-}
-
-TEST_F(TabLifecycleUnitSourceTest, CanOnlyDiscardOnce_Proactive) {
-  CanOnlyDiscardOnceTest(DiscardReason::kProactive);
-}
-
-TEST_F(TabLifecycleUnitSourceTest, CanOnlyDiscardOnce_External) {
-  CanOnlyDiscardOnceTest(DiscardReason::kExternal);
-}
-
 TEST_F(TabLifecycleUnitSourceTest, CannotFreezeADiscardedTab) {
   LifecycleUnit* background_lifecycle_unit = nullptr;
   LifecycleUnit* foreground_lifecycle_unit = nullptr;
@@ -747,54 +680,4 @@
   ::testing::Mock::VerifyAndClear(&tab_observer_);
 }
 
-namespace {
-
-class MockOnPrefChanged {
- public:
-  MockOnPrefChanged() = default;
-  ~MockOnPrefChanged() = default;
-
-  MOCK_METHOD1(OnPrefChanged, void(bool));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockOnPrefChanged);
-};
-
-}  // namespace
-
-TEST(TabLifecylesEnterprisePreferenceMonitor, ObservesChanges) {
-  TestingPrefServiceSimple pref_service;
-  pref_service.registry()->RegisterBooleanPref(prefs::kTabLifecyclesEnabled,
-                                               true);
-
-  ::testing::StrictMock<MockOnPrefChanged> obs;
-
-  // Create a monitor that dispatches to the mock. The constructor should have
-  // checked the value and it should return the default.
-  EXPECT_CALL(obs, OnPrefChanged(true));
-  TabLifecylesEnterprisePreferenceMonitor monitor(
-      &pref_service, base::BindRepeating(&MockOnPrefChanged::OnPrefChanged,
-                                         base::Unretained(&obs)));
-  ::testing::Mock::VerifyAndClear(&obs);
-
-  // Set the preference in an unmanaged way to false. The preference should
-  // still be true.
-  EXPECT_CALL(obs, OnPrefChanged(true));
-  pref_service.SetUserPref(prefs::kTabLifecyclesEnabled,
-                           std::make_unique<base::Value>(false));
-  ::testing::Mock::VerifyAndClear(&obs);
-
-  // Set the preference in a managed way to false.
-  EXPECT_CALL(obs, OnPrefChanged(false));
-  pref_service.SetManagedPref(prefs::kTabLifecyclesEnabled,
-                              std::make_unique<base::Value>(false));
-  ::testing::Mock::VerifyAndClear(&obs);
-
-  // Set the preference in a managed way to true.
-  EXPECT_CALL(obs, OnPrefChanged(true));
-  pref_service.SetManagedPref(prefs::kTabLifecyclesEnabled,
-                              std::make_unique<base::Value>(true));
-  ::testing::Mock::VerifyAndClear(&obs);
-}
-
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 6bd86df..b1cdf20 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -32,6 +32,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/web_contents_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace resource_coordinator {
@@ -77,10 +78,6 @@
  protected:
   using TabLifecycleUnit = TabLifecycleUnitSource::TabLifecycleUnit;
 
-  // This is an internal class so that it is also friends with
-  // TabLifecycleUnitTest.
-  class ScopedEnterpriseOptOut;
-
   TabLifecycleUnitTest() : scoped_set_tick_clock_for_testing_(&test_clock_) {
     observers_.AddObserver(&observer_);
   }
@@ -156,26 +153,12 @@
   std::unique_ptr<UsageClock> usage_clock_;
 
  private:
-  // So that the main thread looks like the UI thread as expected.
   TestTabStripModelDelegate tab_strip_model_delegate_;
   ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
 
   DISALLOW_COPY_AND_ASSIGN(TabLifecycleUnitTest);
 };
 
-class TabLifecycleUnitTest::ScopedEnterpriseOptOut {
- public:
-  ScopedEnterpriseOptOut() {
-    TabLifecycleUnitSource::GetInstance()->SetTabLifecyclesEnterprisePolicy(
-        false);
-  }
-
-  ~ScopedEnterpriseOptOut() {
-    TabLifecycleUnitSource::GetInstance()->SetTabLifecyclesEnterprisePolicy(
-        true);
-  }
-};
-
 void TabLifecycleUnitTest::TestCannotDiscardBasedOnHeuristicUsage(
     DecisionFailureReason failure_reason,
     void (SiteCharacteristicsDataWriter::*notify_feature_usage_method)()) {
@@ -623,44 +606,4 @@
   tab_lifecycle_unit.RemoveObserver(&observer);
 }
 
-TEST_F(TabLifecycleUnitTest, CannotDiscardIfEnterpriseOptOutUsed) {
-  TabLifecycleUnit tab_lifecycle_unit(&observers_, usage_clock_.get(),
-                                      web_contents_, tab_strip_model_.get());
-
-  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
-
-  {
-    ScopedEnterpriseOptOut enterprise_opt_out;
-    ExpectCanDiscardFalseAllReasons(
-        &tab_lifecycle_unit,
-        DecisionFailureReason::LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT);
-  }
-
-  ExpectCanDiscardTrueAllReasons(&tab_lifecycle_unit);
-}
-
-TEST_F(TabLifecycleUnitTest, CannotFreezeIfEnterpriseOptOutUsed) {
-  TabLifecycleUnit tab_lifecycle_unit(&observers_, usage_clock_.get(),
-                                      web_contents_, tab_strip_model_.get());
-  TabLoadTracker::Get()->TransitionStateForTesting(web_contents_,
-                                                   LoadingState::LOADED);
-
-  DecisionDetails decision_details;
-  EXPECT_TRUE(tab_lifecycle_unit.CanFreeze(&decision_details));
-  EXPECT_TRUE(decision_details.IsPositive());
-
-  {
-    ScopedEnterpriseOptOut enterprise_opt_out;
-    decision_details.Clear();
-    EXPECT_FALSE(tab_lifecycle_unit.CanFreeze(&decision_details));
-    EXPECT_FALSE(decision_details.IsPositive());
-    EXPECT_EQ(DecisionFailureReason::LIFECYCLES_ENTERPRISE_POLICY_OPT_OUT,
-              decision_details.FailureReason());
-  }
-
-  decision_details.Clear();
-  EXPECT_TRUE(tab_lifecycle_unit.CanFreeze(&decision_details));
-  EXPECT_TRUE(decision_details.IsPositive());
-}
-
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resources/chromeos/login/md_login.js b/chrome/browser/resources/chromeos/login/md_login.js
index a0ad983..c83425da 100644
--- a/chrome/browser/resources/chromeos/login/md_login.js
+++ b/chrome/browser/resources/chromeos/login/md_login.js
@@ -69,6 +69,9 @@
       login.TopHeaderBar.decorate($('top-header-bar'));
 
       chrome.send('screenStateInitialize');
+
+      if (Oobe.getInstance().showingViewsLogin)
+        chrome.send('showAddUser');
     },
 
     // Dummy Oobe functions not present with stripped login UI.
diff --git a/chrome/browser/resources/conflicts/about_conflicts.html b/chrome/browser/resources/conflicts/about_conflicts.html
index 4843fa9..70a1ca72 100644
--- a/chrome/browser/resources/conflicts/about_conflicts.html
+++ b/chrome/browser/resources/conflicts/about_conflicts.html
@@ -199,6 +199,9 @@
               <span dir="ltr">Version</span>
             </td>
             <td class="datacell">
+              <span dir="ltr">Code Id</span>
+            </td>
+            <td class="datacell">
               <span dir="ltr">Location</span>
             </td>
             <td jsdisplay="thirdPartyFeatureEnabled" class="datacell">
@@ -220,6 +223,9 @@
               <span dir="ltr" jscontent="version" class="nowrap">VERSION</span>
             </td>
             <td valign="top" class="datacell">
+              <span dir="ltr" jscontent="code_id" class="nowrap">CODE_ID</span>
+            </td>
+            <td valign="top" class="datacell">
               <span class="nowrap">
                 <span dir="ltr" jscontent="location">LOCATION</span>
                 <strong>
diff --git a/chrome/browser/resources/conflicts/about_conflicts.js b/chrome/browser/resources/conflicts/about_conflicts.js
index 75af797..9e3042dc 100644
--- a/chrome/browser/resources/conflicts/about_conflicts.js
+++ b/chrome/browser/resources/conflicts/about_conflicts.js
@@ -8,19 +8,16 @@
  */
 var moduleListDataFormat = {
   'moduleList': [{
-    'type': 'The type of module found',
     'type_description':
         'The type of module (string), defaults to blank for regular modules',
-    'status': 'The module status',
     'location': 'The module path, not including filename',
     'name': 'The name of the module',
     'product_name': 'The name of the product the module belongs to',
     'description': 'The module description',
     'version': 'The module version',
     'digital_signer': 'The signer of the digital certificate for the module',
-    'recommended_action': 'The help tips bitmask',
-    'possible_resolution': 'The help tips in string form',
-    'help_url': 'The link to the Help Center article'
+    'code_id': 'The code id of the module',
+    'third_party_module_status': 'The module status'
   }]
 };
 
diff --git a/chrome/browser/resources/local_ntp/local_ntp.css b/chrome/browser/resources/local_ntp/local_ntp.css
index 1baa033..b106397 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.css
+++ b/chrome/browser/resources/local_ntp/local_ntp.css
@@ -4,8 +4,8 @@
 
 html {
   /* Material Design constants */
-  --md-tile-height: 128px;
-  --md-tile-margin: 8px;
+  --md-tile-height: 112px;
+  --md-tile-margin: 16px;
 
   /* This will be overridden based on the viewport width, see below. */
   --column-count: 2;
@@ -472,8 +472,10 @@
   user-select: none;
 }
 
-.md-icons #most-visited {
-  margin-top: 30px;
+#most-visited.md-icons {
+  /* Total of 40px margin between fakebox and MV tiles: 8px fakebox
+   * margin-bottom + 22px here margin-top + 10px MV margin-top */
+  margin-top: 22px;
 }
 
 /* Non-Google pages have no Fakebox, so don't need top margin. */
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.css b/chrome/browser/resources/local_ntp/most_visited_single.css
index 126a4ae..7df2ce6 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.css
+++ b/chrome/browser/resources/local_ntp/most_visited_single.css
@@ -12,13 +12,13 @@
   --md-menu-margin-side: 2px;
   --md-menu-margin-top: 4px;
   --md-menu-size: 12px;
-  --md-tile-height: 128px;
-  --md-tile-margin: 8px;
+  --md-tile-height: 112px;
+  --md-tile-margin: 16px;
   --md-tile-padding-horizontal: 8px;
   --md-tile-padding-vertical: 16px;
-  --md-tile-width: 96px;
+  --md-tile-width: 112px;
   --md-title-font-size: 12px;
-  --md-title-height: 32px;
+  --md-title-height: 16px;
 
   /* Constants. */
   --tile-height: 128px;
@@ -506,7 +506,7 @@
   font-size: var(--md-title-font-size);
   font-weight: 500;
   height: var(--md-title-height);
-  line-height: 1.4em;
+  line-height: var(--md-title-height);
   text-align: center;
   width: 100%;
   word-break: break-word;
diff --git a/chrome/browser/resources/local_ntp/most_visited_single.js b/chrome/browser/resources/local_ntp/most_visited_single.js
index 231f2ba..b518a87e 100644
--- a/chrome/browser/resources/local_ntp/most_visited_single.js
+++ b/chrome/browser/resources/local_ntp/most_visited_single.js
@@ -334,8 +334,10 @@
 
 
 /**
- * Truncates titles that are longer than two lines and appends an ellipsis. Text
- * overflow in CSS ("text-overflow: ellipsis") cannot handle multiple lines.
+ * Truncates titles that are longer than one line and appends an ellipsis. Text
+ * overflow in CSS ("text-overflow: ellipsis") requires "overflow: hidden",
+ * which will cut off the title's text shadow. Only used for Material Design
+ * tiles.
  */
 function truncateTitleText(titles) {
   for (let i = 0; i < titles.length; i++) {
diff --git a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
index f2ed052..243d813 100644
--- a/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
+++ b/chrome/browser/resources/settings/passwords_and_forms_page/passwords_section.js
@@ -153,8 +153,18 @@
     // Create listener functions.
     const setSavedPasswordsListener = list =>
         this.updateList('savedPasswords', item => {
-          return item.entry.loginPair.urls.origin + '_' +
-              item.entry.loginPair.username;
+          // The item uid is built from index, origin, and username for the
+          // following reasons: origin and username are enough to describe and
+          // uniquely identify an entry. It is impossible to have two entries
+          // that have the same origin and username, but different passwords,
+          // as the password update logic prevents these cases. The entry is
+          // required to force a refresh of entries, after a removal or undo of
+          // a removal has taken place. All entries before the point of
+          // modification are uneffected, but the ones following need to be
+          // refreshed. Including the index in the uid achieves this effect.
+          // See https://crbug.com/862119 how this could lead to bugs otherwise.
+          return item.entry.index + '_' + item.entry.loginPair.urls.origin +
+              '_' + item.entry.loginPair.username;
         }, list.map(entry => ({
                       entry: entry,
                       password: '',
diff --git a/chrome/browser/resources/settings/people_page/sync_page.html b/chrome/browser/resources/settings/people_page/sync_page.html
index 35be65b1..9abaa4ef 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.html
+++ b/chrome/browser/resources/settings/people_page/sync_page.html
@@ -365,6 +365,15 @@
 
         <div id="other-sync-items"
             class$="[[getListFrameClass_(unifiedConsentEnabled)]]">
+          <template is="dom-if" if="[[driveSuggestAvailable_]]">
+            <settings-toggle-button
+                class$="[[getListItemClass_(unifiedConsentEnabled)]]"
+                pref="{{prefs.documentsuggest.enabled}}"
+                label="$i18n{driveSuggestPref}"
+                sub-label="$i18n{driveSuggestPrefDesc}">
+            </settings-toggle-button>
+          </template>
+
           <a class$="inherit-color no-outline
               [[getListItemClass_(unifiedConsentEnabled)]]" tabindex="-1"
               target="_blank" href="$i18n{activityControlsUrl}"
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js
index bbb117c..20d8a86 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.js
+++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -134,7 +134,8 @@
       type: Boolean,
       value: false,
       computed: 'computeSyncSectionDisabled_(' +
-          'unifiedConsentEnabled, syncStatus.signedIn, syncStatus.disabled)',
+          'unifiedConsentEnabled, syncStatus.signedIn, syncStatus.disabled, ' +
+          'syncStatus.hasError, syncStatus.statusAction)',
     },
 
     /** @private */
@@ -149,6 +150,14 @@
       value: true,
     },
 
+    /** @private */
+    driveSuggestAvailable_: {
+      type: Boolean,
+      value: function() {
+        return loadTimeData.getBoolean('driveSuggestAvailable');
+      }
+    },
+
     // <if expr="not chromeos">
     diceEnabled: Boolean,
     // </if>
@@ -217,7 +226,10 @@
    */
   computeSyncSectionDisabled_() {
     return !!this.unifiedConsentEnabled &&
-        (!this.syncStatus.signedIn || !!this.syncStatus.disabled);
+        (!this.syncStatus.signedIn || !!this.syncStatus.disabled ||
+         (!!this.syncStatus.hasError &&
+          this.syncStatus.statusAction ===
+              settings.StatusAction.REAUTHENTICATE));
   },
 
   /** @protected */
diff --git a/chrome/browser/sessions/session_restore.cc b/chrome/browser/sessions/session_restore.cc
index 8a89c0c..0cb1cc7d 100644
--- a/chrome/browser/sessions/session_restore.cc
+++ b/chrome/browser/sessions/session_restore.cc
@@ -657,6 +657,10 @@
         tab.extension_app_id, is_selected_tab, tab.pinned, true,
         last_active_time, session_storage_namespace.get(),
         tab.user_agent_override, true /* from_session_restore */);
+    // Regression check: if the current tab |is_selected_tab|, it should load
+    // immediately, otherwise, tabs should not start loading right away. The
+    // focused tab will be loaded by Browser, and TabLoader will load the rest.
+    DCHECK(is_selected_tab || web_contents->GetController().NeedsReload());
 
     // RestoreTab can return nullptr if |tab| doesn't have valid data.
     if (!web_contents)
diff --git a/chrome/browser/sessions/tab_loader.cc b/chrome/browser/sessions/tab_loader.cc
index 3cb9a66..52f54e5 100644
--- a/chrome/browser/sessions/tab_loader.cc
+++ b/chrome/browser/sessions/tab_loader.cc
@@ -207,7 +207,7 @@
       }
     }
 
-    AddTab(restored_tab.contents());
+    AddTab(restored_tab.contents(), restored_tab.is_active());
   }
 
   StartTimerIfNeeded();
@@ -347,7 +347,7 @@
   return tabs_to_load;
 }
 
-void TabLoader::AddTab(WebContents* contents) {
+void TabLoader::AddTab(WebContents* contents, bool loading_initiated) {
   DCHECK(reentry_depth_ > 0);  // This can only be called internally.
 
   // Handle tabs that have already started or finished loading.
@@ -360,7 +360,15 @@
     return;
   }
 
-  tabs_to_load_.push_back(contents);
+  // Otherwise place it in one of the |tabs_load_initiated_| or
+  // |tabs_to_load_| containers.
+  if (loading_initiated) {
+    delegate_->NotifyTabLoadStarted();
+    ++scheduled_to_load_count_;
+    tabs_load_initiated_.insert(contents);
+  } else {
+    tabs_to_load_.push_back(contents);
+  }
 }
 
 void TabLoader::RemoveTab(WebContents* contents) {
diff --git a/chrome/browser/sessions/tab_loader.h b/chrome/browser/sessions/tab_loader.h
index b83ad0f..6ddcdf9 100644
--- a/chrome/browser/sessions/tab_loader.h
+++ b/chrome/browser/sessions/tab_loader.h
@@ -127,7 +127,7 @@
 
   // Adds a tab that we are responsible for to one of the |tabs_*| containers.
   // Can invalidate self-destroy and timer invariants.
-  void AddTab(content::WebContents* contents);
+  void AddTab(content::WebContents* contents, bool loading_initiated);
 
   // Removes the tab from the set of tabs to load and list of tabs we're waiting
   // to get a load from. Can invalidate self-destroy and timer invariants.
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index b489a668..a3cf72d 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -28,6 +28,7 @@
 #include "chrome/browser/sync/bookmark_sync_service_factory.h"
 #include "chrome/browser/sync/glue/sync_start_util.h"
 #include "chrome/browser/sync/glue/theme_data_type_controller.h"
+#include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router.h"
 #include "chrome/browser/sync/sessions/sync_sessions_web_contents_router_factory.h"
@@ -280,6 +281,11 @@
   return local_sync_backend_folder;
 }
 
+syncer::ModelTypeStoreService* ChromeSyncClient::GetModelTypeStoreService() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  return ModelTypeStoreServiceFactory::GetForProfile(profile_);
+}
+
 bookmarks::BookmarkModel* ChromeSyncClient::GetBookmarkModel() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return BookmarkModelFactory::GetForBrowserContext(profile_);
diff --git a/chrome/browser/sync/chrome_sync_client.h b/chrome/browser/sync/chrome_sync_client.h
index 0762df2..1996511 100644
--- a/chrome/browser/sync/chrome_sync_client.h
+++ b/chrome/browser/sync/chrome_sync_client.h
@@ -12,6 +12,7 @@
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/sync/glue/extensions_activity_monitor.h"
 #include "components/sync/driver/sync_client.h"
+#include "components/sync/model/model_type_store_service.h"
 
 class Profile;
 
@@ -41,6 +42,7 @@
   syncer::SyncService* GetSyncService() override;
   PrefService* GetPrefService() override;
   base::FilePath GetLocalSyncBackendFolder() override;
+  syncer::ModelTypeStoreService* GetModelTypeStoreService() override;
   bookmarks::BookmarkModel* GetBookmarkModel() override;
   favicon::FaviconService* GetFaviconService() override;
   history::HistoryService* GetHistoryService() override;
diff --git a/chrome/browser/sync/model_type_store_service_factory.cc b/chrome/browser/sync/model_type_store_service_factory.cc
new file mode 100644
index 0000000..9ee0db2
--- /dev/null
+++ b/chrome/browser/sync/model_type_store_service_factory.cc
@@ -0,0 +1,40 @@
+// 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/sync/model_type_store_service_factory.h"
+
+#include "chrome/browser/profiles/incognito_helpers.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/sync/model_impl/model_type_store_service_impl.h"
+
+// static
+ModelTypeStoreServiceFactory* ModelTypeStoreServiceFactory::GetInstance() {
+  return base::Singleton<ModelTypeStoreServiceFactory>::get();
+}
+
+// static
+syncer::ModelTypeStoreService* ModelTypeStoreServiceFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<syncer::ModelTypeStoreService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+ModelTypeStoreServiceFactory::ModelTypeStoreServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "ModelTypeStoreService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+ModelTypeStoreServiceFactory::~ModelTypeStoreServiceFactory() {}
+
+KeyedService* ModelTypeStoreServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new syncer::ModelTypeStoreServiceImpl(profile->GetPath());
+}
+
+content::BrowserContext* ModelTypeStoreServiceFactory::GetBrowserContextToUse(
+    content::BrowserContext* context) const {
+  return chrome::GetBrowserContextRedirectedInIncognito(context);
+}
diff --git a/chrome/browser/sync/model_type_store_service_factory.h b/chrome/browser/sync/model_type_store_service_factory.h
new file mode 100644
index 0000000..6e881f26
--- /dev/null
+++ b/chrome/browser/sync/model_type_store_service_factory.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SYNC_MODEL_TYPE_STORE_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_SYNC_MODEL_TYPE_STORE_SERVICE_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace syncer {
+class ModelTypeStoreService;
+}  // namespace syncer
+
+class ModelTypeStoreServiceFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static syncer::ModelTypeStoreService* GetForProfile(Profile* profile);
+  static ModelTypeStoreServiceFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<ModelTypeStoreServiceFactory>;
+
+  ModelTypeStoreServiceFactory();
+  ~ModelTypeStoreServiceFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  content::BrowserContext* GetBrowserContextToUse(
+      content::BrowserContext* context) const override;
+
+  DISALLOW_COPY_AND_ASSIGN(ModelTypeStoreServiceFactory);
+};
+
+#endif  // CHROME_BROWSER_SYNC_MODEL_TYPE_STORE_SERVICE_FACTORY_H_
diff --git a/chrome/browser/sync/profile_sync_service_factory.cc b/chrome/browser/sync/profile_sync_service_factory.cc
index f5435e3..7b75fedf 100644
--- a/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/chrome/browser/sync/profile_sync_service_factory.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 
+#include <string>
 #include <utility>
 
 #include "base/memory/singleton.h"
@@ -178,7 +179,6 @@
   Profile* profile = Profile::FromBrowserContext(context);
 
   init_params.network_time_update_callback = base::Bind(&UpdateNetworkTime);
-  init_params.base_directory = profile->GetPath();
   init_params.url_request_context = profile->GetRequestContext();
   init_params.url_loader_factory =
       content::BrowserContext::GetDefaultStoragePartition(profile)
diff --git a/chrome/browser/sync/profile_sync_test_util.cc b/chrome/browser/sync/profile_sync_test_util.cc
index 9523bf6..66d8d42 100644
--- a/chrome/browser/sync/profile_sync_test_util.cc
+++ b/chrome/browser/sync/profile_sync_test_util.cc
@@ -22,7 +22,6 @@
 #include "components/sync/driver/signin_manager_wrapper.h"
 #include "components/sync/driver/startup_controller.h"
 #include "components/sync/driver/sync_api_component_factory_mock.h"
-#include "components/sync/model/model_type_store_test_util.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -56,15 +55,12 @@
   init_params.start_behavior = ProfileSyncService::MANUAL_START;
   init_params.sync_client = std::move(sync_client);
   init_params.network_time_update_callback = base::DoNothing();
-  init_params.base_directory = profile->GetPath();
   init_params.url_request_context = profile->GetRequestContext();
   init_params.url_loader_factory =
       content::BrowserContext::GetDefaultStoragePartition(profile)
           ->GetURLLoaderFactoryForBrowserProcess();
   init_params.debug_identifier = profile->GetDebugName();
   init_params.channel = chrome::GetChannel();
-  init_params.model_type_store_factory =
-      syncer::ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
 
   return init_params;
 }
diff --git a/chrome/browser/sync/sync_ui_util_unittest.cc b/chrome/browser/sync/sync_ui_util_unittest.cc
index ee9e068..a15a871b 100644
--- a/chrome/browser/sync/sync_ui_util_unittest.cc
+++ b/chrome/browser/sync/sync_ui_util_unittest.cc
@@ -103,11 +103,11 @@
 void GetDistinctCase(ProfileSyncServiceMock* service,
                      FakeSigninManagerForSyncUIUtilTest* signin,
                      ProfileOAuth2TokenService* token_service,
-                     int caseNumber) {
+                     int case_number) {
   // Auth Error object is returned by reference in mock and needs to stay in
   // scope throughout test, so it is owned by calling method. However it is
   // immutable so can only be allocated in this method.
-  switch (caseNumber) {
+  switch (case_number) {
     case STATUS_CASE_SETUP_IN_PROGRESS: {
       EXPECT_CALL(*service, IsFirstSetupComplete())
           .WillRepeatedly(Return(false));
@@ -134,7 +134,8 @@
     case STATUS_CASE_AUTHENTICATING: {
       EXPECT_CALL(*service, IsFirstSetupComplete())
           .WillRepeatedly(Return(true));
-      EXPECT_CALL(*service, IsSyncActive()).WillRepeatedly(Return(true));
+      EXPECT_CALL(*service, GetState())
+          .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
       EXPECT_CALL(*service, IsPassphraseRequired())
           .WillRepeatedly(Return(false));
       syncer::SyncEngine::Status status;
@@ -148,7 +149,8 @@
     case STATUS_CASE_AUTH_ERROR: {
       EXPECT_CALL(*service, IsFirstSetupComplete())
           .WillRepeatedly(Return(true));
-      EXPECT_CALL(*service, IsSyncActive()).WillRepeatedly(Return(true));
+      EXPECT_CALL(*service, GetState())
+          .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
       EXPECT_CALL(*service, IsPassphraseRequired())
           .WillRepeatedly(Return(false));
       syncer::SyncEngine::Status status;
@@ -168,7 +170,8 @@
     case STATUS_CASE_PROTOCOL_ERROR: {
       EXPECT_CALL(*service, IsFirstSetupComplete())
           .WillRepeatedly(Return(true));
-      EXPECT_CALL(*service, IsSyncActive()).WillRepeatedly(Return(true));
+      EXPECT_CALL(*service, GetState())
+          .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
       EXPECT_CALL(*service, IsPassphraseRequired())
           .WillRepeatedly(Return(false));
       syncer::SyncProtocolError protocolError;
@@ -194,7 +197,8 @@
     case STATUS_CASE_PASSPHRASE_ERROR: {
       EXPECT_CALL(*service, IsFirstSetupComplete())
           .WillRepeatedly(Return(true));
-      EXPECT_CALL(*service, IsSyncActive()).WillRepeatedly(Return(true));
+      EXPECT_CALL(*service, GetState())
+          .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
       syncer::SyncEngine::Status status;
       EXPECT_CALL(*service, QueryDetailedSyncStatus(_))
           .WillRepeatedly(DoAll(SetArgPointee<0>(status), Return(false)));
@@ -209,7 +213,8 @@
     case STATUS_CASE_SYNCED: {
       EXPECT_CALL(*service, IsFirstSetupComplete())
           .WillRepeatedly(Return(true));
-      EXPECT_CALL(*service, IsSyncActive()).WillRepeatedly(Return(true));
+      EXPECT_CALL(*service, GetState())
+          .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
       EXPECT_CALL(*service, IsPassphraseRequired())
           .WillRepeatedly(Return(false));
       syncer::SyncEngine::Status status;
@@ -227,7 +232,8 @@
               Return(syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY));
       EXPECT_CALL(*service, IsFirstSetupComplete())
           .WillRepeatedly(Return(false));
-      EXPECT_CALL(*service, IsSyncActive()).WillRepeatedly(Return(false));
+      EXPECT_CALL(*service, GetState())
+          .WillRepeatedly(Return(syncer::SyncService::State::DISABLED));
       EXPECT_CALL(*service, IsPassphraseRequired())
           .WillRepeatedly(Return(false));
       syncer::SyncEngine::Status status;
diff --git a/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc b/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc
index 2fbabdc0..dd4ab794 100644
--- a/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/syncable/directory.h"
 #include "sql/test/test_helpers.h"
 #include "url/gurl.h"
@@ -76,7 +77,9 @@
                        StopThenDisableDeletesDirectory) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   browser_sync::ProfileSyncService* sync_service = GetSyncService(0);
-  FilePath directory_path = sync_service->GetDirectoryPathForTest();
+  FilePath directory_path = sync_service->GetSyncClient()
+                                ->GetModelTypeStoreService()
+                                ->GetSyncDataPath();
   ASSERT_TRUE(FolderContainsFiles(directory_path));
   sync_service->RequestStop(browser_sync::ProfileSyncService::CLEAR_DATA);
 
@@ -109,7 +112,9 @@
   WaitForExistingTasksOnLoop(sync_loop);
 
   // Now corrupt the database.
-  const FilePath directory_path(sync_service->GetDirectoryPathForTest());
+  FilePath directory_path = sync_service->GetSyncClient()
+                                ->GetModelTypeStoreService()
+                                ->GetSyncDataPath();
   const FilePath sync_db(directory_path.Append(
       syncer::syncable::Directory::kSyncDatabaseFilename));
   ASSERT_TRUE(sql::test::CorruptSizeInHeaderWithLock(sync_db));
diff --git a/chrome/browser/sync/user_event_service_factory.cc b/chrome/browser/sync/user_event_service_factory.cc
index c82c3a51..cf817ba 100644
--- a/chrome/browser/sync/user_event_service_factory.cc
+++ b/chrome/browser/sync/user_event_service_factory.cc
@@ -9,12 +9,14 @@
 
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/channel_info.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/report_unrecoverable_error.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 #include "components/sync/user_events/no_op_user_event_service.h"
 #include "components/sync/user_events/user_event_service_impl.h"
@@ -38,6 +40,7 @@
     : BrowserContextKeyedServiceFactory(
           "UserEventService",
           BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
   // TODO(vitaliii): This is missing
   // DependsOn(ProfileSyncServiceFactory::GetInstance()), which we can't
   // simply add because ProfileSyncServiceFactory itself depends on this
@@ -58,8 +61,8 @@
   }
 
   syncer::OnceModelTypeStoreFactory store_factory =
-      browser_sync::ProfileSyncService::GetModelTypeStoreFactory(
-          profile->GetPath());
+      ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory();
+
   auto change_processor =
       std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
           syncer::USER_EVENTS,
diff --git a/chrome/browser/themes/custom_theme_supplier.h b/chrome/browser/themes/custom_theme_supplier.h
index f46b40a..d6cd02e5 100644
--- a/chrome/browser/themes/custom_theme_supplier.h
+++ b/chrome/browser/themes/custom_theme_supplier.h
@@ -34,6 +34,7 @@
     EXTENSION,
     NATIVE_X11,
     SUPERVISED_USER_THEME,
+    INCREASED_CONTRAST,
   };
 
   explicit CustomThemeSupplier(ThemeType type);
diff --git a/chrome/browser/themes/increased_contrast_theme_supplier.cc b/chrome/browser/themes/increased_contrast_theme_supplier.cc
new file mode 100644
index 0000000..700cbe0a
--- /dev/null
+++ b/chrome/browser/themes/increased_contrast_theme_supplier.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 "chrome/browser/themes/increased_contrast_theme_supplier.h"
+#include "chrome/browser/themes/theme_properties.h"
+
+IncreasedContrastThemeSupplier::IncreasedContrastThemeSupplier()
+    : CustomThemeSupplier(INCREASED_CONTRAST) {}
+IncreasedContrastThemeSupplier::~IncreasedContrastThemeSupplier() {}
+
+// TODO(ellyjones): Follow up with a11y designers about these color choices.
+bool IncreasedContrastThemeSupplier::GetColor(int id, SkColor* color) const {
+  switch (id) {
+    case ThemeProperties::COLOR_TAB_TEXT:
+      *color = SK_ColorBLACK;
+      return true;
+    case ThemeProperties::COLOR_BACKGROUND_TAB_TEXT:
+      *color = SK_ColorWHITE;
+      return true;
+    case ThemeProperties::COLOR_TOOLBAR:
+      *color = SK_ColorWHITE;
+      return true;
+    case ThemeProperties::COLOR_FRAME_INACTIVE:
+      *color = SK_ColorGRAY;
+      return true;
+    case ThemeProperties::COLOR_FRAME:
+      *color = SK_ColorDKGRAY;
+      return true;
+    case ThemeProperties::COLOR_FRAME_INCOGNITO:
+      *color = SK_ColorDKGRAY;
+      return true;
+    case ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR:
+      *color = SK_ColorLTGRAY;
+      return true;
+    case ThemeProperties::COLOR_TOOLBAR_BOTTOM_SEPARATOR:
+      *color = SK_ColorBLACK;
+      return true;
+    case ThemeProperties::COLOR_LOCATION_BAR_BORDER:
+      *color = SK_ColorBLACK;
+      return true;
+  }
+  return false;
+}
diff --git a/chrome/browser/themes/increased_contrast_theme_supplier.h b/chrome/browser/themes/increased_contrast_theme_supplier.h
new file mode 100644
index 0000000..41c1934
--- /dev/null
+++ b/chrome/browser/themes/increased_contrast_theme_supplier.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_THEMES_INCREASED_CONTRAST_THEME_SUPPLIER_H_
+#define CHROME_BROWSER_THEMES_INCREASED_CONTRAST_THEME_SUPPLIER_H_
+
+#include "chrome/browser/themes/custom_theme_supplier.h"
+
+// A theme supplier that maximizes the contrast between UI elements and
+// especially the visual prominence of key UI elements (omnibox, active vs
+// inactive tab distinction).
+class IncreasedContrastThemeSupplier : public CustomThemeSupplier {
+ public:
+  IncreasedContrastThemeSupplier();
+
+  bool GetColor(int id, SkColor* color) const override;
+
+ protected:
+  ~IncreasedContrastThemeSupplier() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(IncreasedContrastThemeSupplier);
+};
+
+#endif  // CHROME_BROWSER_THEMES_INCREASED_CONTRAST_THEME_SUPPLIER_H_
diff --git a/chrome/browser/themes/theme_service.cc b/chrome/browser/themes/theme_service.cc
index cffbc09..6fa779b 100644
--- a/chrome/browser/themes/theme_service.cc
+++ b/chrome/browser/themes/theme_service.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/browser_theme_pack.h"
 #include "chrome/browser/themes/custom_theme_supplier.h"
+#include "chrome/browser/themes/increased_contrast_theme_supplier.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/themes/theme_syncable_service.h"
@@ -315,6 +316,11 @@
     return;
   }
 #endif
+#if defined(OS_MACOSX)
+  // TODO(ellyjones): Expand ShouldIncreaseContrast() to non-Mac platforms.
+  if (original_theme_provider_.ShouldIncreaseContrast())
+    SetCustomDefaultTheme(new IncreasedContrastThemeSupplier);
+#endif
   ClearAllThemeData();
   NotifyThemeChanged();
 }
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 5e363d0..c6d7d9d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -105,6 +105,8 @@
       "cocoa/bookmarks/bookmark_bubble_controller.mm",
       "cocoa/bookmarks/bookmark_bubble_observer_cocoa.h",
       "cocoa/bookmarks/bookmark_bubble_observer_cocoa.mm",
+      "cocoa/bookmarks/bookmark_bar_util.h",
+      "cocoa/bookmarks/bookmark_bar_util.mm",
       "cocoa/bookmarks/bookmark_button.h",
       "cocoa/bookmarks/bookmark_button.mm",
       "cocoa/bookmarks/bookmark_button_cell.h",
@@ -163,6 +165,8 @@
       "cocoa/chrome_event_processing_window.mm",
       "cocoa/clickhold_button_cell.h",
       "cocoa/clickhold_button_cell.mm",
+      "cocoa/cocoa_util.h",
+      "cocoa/cocoa_util.mm",
       "cocoa/constrained_web_dialog_delegate_mac.mm",
       "cocoa/constrained_window/constrained_window_alert.h",
       "cocoa/constrained_window/constrained_window_alert.mm",
@@ -471,6 +475,7 @@
       "cocoa/spinner_view.mm",
       "cocoa/ssl_client_certificate_selector_cocoa.h",
       "cocoa/ssl_client_certificate_selector_cocoa.mm",
+      "cocoa/spinner_util.h",
       "cocoa/status_bubble_mac.h",
       "cocoa/status_bubble_mac.mm",
       "cocoa/styled_text_field.h",
@@ -851,6 +856,8 @@
     "webui/version_handler.h",
     "webui/version_ui.cc",
     "webui/version_ui.h",
+    "webui/webui_util.cc",
+    "webui/webui_util.h",
   ]
 
   if (safe_browsing_mode == 1) {
diff --git a/chrome/browser/ui/ash/login_screen_client.cc b/chrome/browser/ui/ash/login_screen_client.cc
index f779a76..3c9d622f 100644
--- a/chrome/browser/ui/ash/login_screen_client.cc
+++ b/chrome/browser/ui/ash/login_screen_client.cc
@@ -9,13 +9,11 @@
 #include "ash/public/interfaces/constants.mojom.h"
 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
 #include "chrome/browser/chromeos/login/reauth_stats.h"
-#include "chrome/browser/chromeos/login/screens/gaia_view.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
 #include "chrome/browser/profiles/profile_metrics.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client.h"
 #include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
-#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
 #include "components/user_manager/remove_user_delegate.h"
 #include "content/public/common/service_manager_connection.h"
 #include "services/service_manager/public/cpp/connector.h"
@@ -153,19 +151,6 @@
     chromeos::LoginDisplayHost::default_host()->ShowFeedback();
 }
 
-void LoginScreenClient::ShowResetScreen() {
-  if (chromeos::LoginDisplayHost::default_host()->GetOobeUI()) {
-    // If gaia init is in progress, cancel it so the screen doesn't race with
-    // the reset wizard.
-    chromeos::LoginDisplayHost::default_host()
-        ->GetOobeUI()
-        ->GetGaiaScreenView()
-        ->CancelShowGaiaAsync();
-    chromeos::LoginDisplayHost::default_host()->StartWizard(
-        chromeos::OobeScreen::SCREEN_OOBE_RESET);
-  }
-}
-
 void LoginScreenClient::LaunchKioskApp(const std::string& app_id) {
   chromeos::LoginDisplayHost::default_host()->StartAppLaunch(app_id, false,
                                                              false);
diff --git a/chrome/browser/ui/ash/login_screen_client.h b/chrome/browser/ui/ash/login_screen_client.h
index 0339644..4fd4e498 100644
--- a/chrome/browser/ui/ash/login_screen_client.h
+++ b/chrome/browser/ui/ash/login_screen_client.h
@@ -83,7 +83,6 @@
   void RequestPublicSessionKeyboardLayouts(const AccountId& account_id,
                                            const std::string& locale) override;
   void ShowFeedback() override;
-  void ShowResetScreen() override;
   void LaunchKioskApp(const std::string& app_id) override;
   void LaunchArcKioskApp(const AccountId& account_id) override;
 
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
index 425096a..398f7b7 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_unittest.cc
@@ -404,6 +404,11 @@
 }
 
 TEST_F(SafeBrowsingTriggeredPopupBlockerTest, ActivationPosition) {
+  // Turn on the feature to perform safebrowsing on redirects.
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      subresource_filter::kSafeBrowsingSubresourceFilterConsiderRedirects);
+
   const GURL enforce_url("https://enforce.test/");
   const GURL warn_url("https://warn.test/");
   MarkUrlAsAbusiveEnforce(enforce_url);
diff --git a/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm b/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm
index 5f220302..37a6efb 100644
--- a/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/app_menu/app_menu_controller_unittest.mm
@@ -159,8 +159,8 @@
   }
 
   void EnableSync() {
-    EXPECT_CALL(*mock_sync_service_, IsSyncActive())
-        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_sync_service_, GetState())
+        .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
     EXPECT_CALL(*mock_sync_service_,
                 IsDataTypeControllerRunning(syncer::SESSIONS))
         .WillRepeatedly(Return(true));
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm
index 6418d68e6..72422c5 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.mm
@@ -33,6 +33,7 @@
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_toolbar_view.h"
+#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"
@@ -79,6 +80,7 @@
 #include "ui/gfx/paint_vector_icon.h"
 
 using base::UserMetricsAction;
+using bookmarks::bookmark_bar_util::ValueInRangeInclusive;
 using bookmarks::BookmarkBarLayout;
 using bookmarks::BookmarkModel;
 using bookmarks::BookmarkNode;
@@ -1529,11 +1531,6 @@
 
 #pragma mark Drag & Drop
 
-// Find something like std::is_between<T>?  I can't believe one doesn't exist.
-static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
-  return ((value >= low) && (value <= high));
-}
-
 // Return the proposed drop target for a hover open button from the
 // given array, or nil if none.  We use this for distinguishing
 // between a hover-open candidate or drop-indicator draw.
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm
index e467878b..09f51e5 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.mm
@@ -18,6 +18,7 @@
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_view.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h"
+#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h"
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
@@ -32,6 +33,7 @@
 #include "ui/base/theme_provider.h"
 #include "ui/resources/grit/ui_resources.h"
 
+using bookmarks::bookmark_bar_util::ValueInRangeInclusive;
 using bookmarks::BookmarkModel;
 using bookmarks::BookmarkNode;
 using bookmarks::BookmarkNodeData;
@@ -1221,12 +1223,6 @@
 
 #pragma mark Drag & Drop
 
-// Find something like std::is_between<T>?  I can't believe one doesn't exist.
-// http://crbug.com/35966
-static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
-  return ((value >= low) && (value <= high));
-}
-
 // Return the proposed drop target for a hover open button, or nil if none.
 //
 // TODO(jrg): this is just like the version in
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.h
new file mode 100644
index 0000000..b75901b
--- /dev/null
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.h
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_UTIL_H_
+#define CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_UTIL_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/foundation_util.h"
+
+namespace bookmarks {
+namespace bookmark_bar_util {
+
+// Find something like std::is_between<T>?  I can't believe one doesn't exist.
+BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high);
+
+}  // namespace bookmark_bar_util
+}  // namespace bookmarks
+
+#endif  // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_UTIL_H_
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.mm
new file mode 100644
index 0000000..2daed0e
--- /dev/null
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.mm
@@ -0,0 +1,20 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_util.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/foundation_util.h"
+
+namespace bookmarks {
+namespace bookmark_bar_util {
+
+// Find something like std::is_between<T>?  I can't believe one doesn't exist.
+BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
+  return ((value >= low) && (value <= high));
+}
+
+}  // namespace bookmark_bar_util
+}  // namespace bookmarks
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm
index 3096d12..5b196aa 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.mm
@@ -33,7 +33,7 @@
 const int kHierarchyButtonLeadingPadding = 11;
 
 const int kIconTextSpacer = 4;
-const int kTrailingPadding = 4;
+const int kTrailingCellPadding = 4;
 const int kIconLeadingPadding = 4;
 
 const int kDefaultFontSize = 12;
@@ -133,9 +133,10 @@
       NSKernAttributeName : @(kKernAmount),
       NSFontAttributeName : [self fontForBookmarkBarCell],
     }];
-    width += kIconTextSpacer + std::ceil(titleSize.width) + kTrailingPadding;
+    width +=
+        kIconTextSpacer + std::ceil(titleSize.width) + kTrailingCellPadding;
   } else {
-    width += kTrailingPadding;
+    width += kTrailingCellPadding;
   }
   return width;
 }
@@ -383,7 +384,8 @@
   if ([title length] > 0) {
     CGFloat textWidth =
         [title sizeWithAttributes:[self titleTextAttributes]].width;
-    cellSize.width += kIconTextSpacer + std::ceil(textWidth) + kTrailingPadding;
+    cellSize.width +=
+        kIconTextSpacer + std::ceil(textWidth) + kTrailingCellPadding;
   } else {
     cellSize.width += kIconLeadingPadding;
   }
@@ -415,7 +417,8 @@
                          ? NSWidth(theRect) - NSMinX(imageRect)  // Un-flip
                          : NSMaxX(imageRect);
   textRect.origin.x = imageEnd + kIconTextSpacer;
-  textRect.size.width = NSWidth(theRect) - NSMinX(textRect) - kTrailingPadding;
+  textRect.size.width =
+      NSWidth(theRect) - NSMinX(textRect) - kTrailingCellPadding;
   if (drawFolderArrow_)
     textRect.size.width -=
         [arrowImage_ size].width + kHierarchyButtonTrailingPadding;
diff --git a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
index 4255076..c2f3efb 100644
--- a/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/browser/zoom_bubble_controller.mm
@@ -51,7 +51,7 @@
 NSTimeInterval gAutoCloseDelay = 1.5;
 
 // The height of the window.
-const CGFloat kWindowHeight = 29.0;
+const CGFloat kZoomBubbleWindowHeight = 29.0;
 
 // Width of the zoom in and zoom out buttons.
 const CGFloat kZoomInOutButtonWidth = 44.0;
@@ -216,7 +216,8 @@
   NSButton* zoomOutButton = [self addButtonWithTitleID:IDS_ZOOM_MINUS2
                                               fontSize:kZoomInOutButtonFontSize
                                                 action:@selector(zoomOut:)];
-  NSRect rect = NSMakeRect(0, 0, kZoomInOutButtonWidth, kWindowHeight);
+  NSRect rect =
+      NSMakeRect(0, 0, kZoomInOutButtonWidth, kZoomBubbleWindowHeight);
   [zoomOutButton setFrame:rect];
 
   // Zoom label.
diff --git a/chrome/browser/ui/cocoa/bubble_sync_promo_controller.mm b/chrome/browser/ui/cocoa/bubble_sync_promo_controller.mm
index 1fd8f5f..c93fe73a 100644
--- a/chrome/browser/ui/cocoa/bubble_sync_promo_controller.mm
+++ b/chrome/browser/ui/cocoa/bubble_sync_promo_controller.mm
@@ -10,6 +10,7 @@
 #include "chrome/browser/signin/signin_promo.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/cocoa/chrome_style.h"
+#include "chrome/browser/ui/cocoa/cocoa_util.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "third_party/skia/include/core/SkColor.h"
 #import "ui/base/cocoa/controls/hyperlink_text_view.h"
@@ -18,28 +19,18 @@
 
 namespace {
 
-// Remove underlining from the specified range of characters in a text view.
-void RemoveUnderlining(NSTextView* textView, int offset, int length) {
-  [textView setLinkTextAttributes:nil];
-  NSTextStorage* text = [textView textStorage];
-  NSRange range = NSMakeRange(offset, length);
-  [text addAttribute:NSUnderlineStyleAttributeName
-               value:[NSNumber numberWithInt:NSUnderlineStyleNone]
-               range:range];
-}
-
-const SkColor kTextColor = SkColorSetRGB(0x66, 0x66, 0x66);
+const SkColor kPromoTextColor = SkColorSetRGB(0x66, 0x66, 0x66);
 const SkColor kPromoViewBackgroundColor = SkColorSetRGB(0xf5, 0xf5, 0xf5);
-const SkColor kBorderColor = SkColorSetRGB(0xe5, 0xe5, 0xe5);
+const SkColor kPromoBorderColor = SkColorSetRGB(0xe5, 0xe5, 0xe5);
 
 // Vertical padding of the promo (dp).
-const CGFloat kVerticalPadding = 15;
+const CGFloat kPromoVerticalPadding = 15;
 
 // Width of the border (dp).
-const CGFloat kBorderWidth = 1.0;
+const CGFloat kPromoBorderWidth = 1.0;
 
 // Font size of the promo text (pt).
-const int kFontSize = 11;
+const int kPromoFontSize = 11;
 
 }  // namespace
 
@@ -59,16 +50,17 @@
 }
 
 - (CGFloat)borderWidth {
-  return kBorderWidth;
+  return kPromoBorderWidth;
 }
 
 - (CGFloat)preferredHeightForWidth:(CGFloat)width {
   CGFloat availableWidth =
-      width - (2 * chrome_style::kHorizontalPadding) - (2 * kBorderWidth);
+      width - (2 * chrome_style::kHorizontalPadding) - (2 * kPromoBorderWidth);
   NSRect frame = [[textView_ textStorage]
       boundingRectWithSize:NSMakeSize(availableWidth, 0.0)
                    options:NSStringDrawingUsesLineFragmentOrigin];
-  return frame.size.height + (2 * kVerticalPadding) + (2 * kBorderWidth);
+  return frame.size.height + (2 * kPromoVerticalPadding) +
+         (2 * kPromoBorderWidth);
 }
 
 - (void)loadView {
@@ -77,10 +69,10 @@
   [promoView
       setFillColor:skia::SkColorToDeviceNSColor(kPromoViewBackgroundColor)];
   [promoView setContentViewMargins:NSMakeSize(chrome_style::kHorizontalPadding,
-                                              kVerticalPadding)];
+                                              kPromoVerticalPadding)];
   [promoView setBorderType:NSLineBorder];
-  [promoView setBorderWidth:kBorderWidth];
-  [promoView setBorderColor:skia::SkColorToDeviceNSColor(kBorderColor)];
+  [promoView setBorderWidth:kPromoBorderWidth];
+  [promoView setBorderColor:skia::SkColorToDeviceNSColor(kPromoBorderColor)];
 
   // Add the sync promo text.
   size_t offset;
@@ -89,20 +81,20 @@
       l10n_util::GetStringFUTF16(promoStringId_, linkText, &offset);
   NSString* nsPromoText = base::SysUTF16ToNSString(promoText);
   NSString* nsLinkText = base::SysUTF16ToNSString(linkText);
-  NSFont* font = [NSFont labelFontOfSize:kFontSize];
+  NSFont* font = [NSFont labelFontOfSize:kPromoFontSize];
   NSColor* linkColor = skia::SkColorToCalibratedNSColor(
       chrome_style::GetLinkColor());
 
   textView_.reset([[HyperlinkTextView alloc] init]);
   [textView_ setMessage:nsPromoText
                withFont:font
-           messageColor:skia::SkColorToDeviceNSColor(kTextColor)];
+           messageColor:skia::SkColorToDeviceNSColor(kPromoTextColor)];
   [textView_ addLinkRange:NSMakeRange(offset, [nsLinkText length])
                   withURL:nil
                 linkColor:linkColor];
   [textView_ setRefusesFirstResponder:YES];
   [[textView_ textContainer] setLineFragmentPadding:0.0];
-  RemoveUnderlining(textView_, offset, linkText.size());
+  cocoa_util::RemoveUnderlining(textView_, offset, linkText.size());
   [textView_ setDelegate:self];
 
   [promoView setContentView:textView_];
diff --git a/chrome/browser/ui/cocoa/cocoa_util.h b/chrome/browser/ui/cocoa/cocoa_util.h
new file mode 100644
index 0000000..ceb95769
--- /dev/null
+++ b/chrome/browser/ui/cocoa/cocoa_util.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_COCOA_UTIL_H_
+#define CHROME_BROWSER_UI_COCOA_COCOA_UTIL_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include <limits>
+
+#include "base/mac/foundation_util.h"
+
+namespace cocoa_util {
+
+// The minimum representable time interval.  This can be used as the value
+// passed to +[NSAnimationContext setDuration:] to stop an in-progress
+// animation as quickly as possible.
+const NSTimeInterval kMinimumTimeInterval =
+    std::numeric_limits<NSTimeInterval>::min();
+
+// Remove underlining from the specified range of characters in a text view.
+void RemoveUnderlining(NSTextView* textView, int offset, int length);
+
+CGFloat LineWidthFromContext(CGContextRef context);
+
+}  // namespace cocoa_util
+
+#endif  // CHROME_BROWSER_UI_COCOA_COCOA_UTIL_H_
diff --git a/chrome/browser/ui/cocoa/cocoa_util.mm b/chrome/browser/ui/cocoa/cocoa_util.mm
new file mode 100644
index 0000000..ebf3c79
--- /dev/null
+++ b/chrome/browser/ui/cocoa/cocoa_util.mm
@@ -0,0 +1,26 @@
+// 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/ui/cocoa/cocoa_util.h"
+
+namespace cocoa_util {
+
+void RemoveUnderlining(NSTextView* textView, int offset, int length) {
+  // Clear the default link attributes that were set by the
+  // HyperlinkTextView, otherwise removing the underline doesn't matter.
+  [textView setLinkTextAttributes:nil];
+  NSTextStorage* text = [textView textStorage];
+  NSRange range = NSMakeRange(offset, length);
+  [text addAttribute:NSUnderlineStyleAttributeName
+               value:[NSNumber numberWithInt:NSUnderlineStyleNone]
+               range:range];
+}
+
+CGFloat LineWidthFromContext(CGContextRef context) {
+  CGRect unitRect = CGRectMake(0.0, 0.0, 1.0, 1.0);
+  CGRect deviceRect = CGContextConvertRectToDeviceSpace(context, unitRect);
+  return 1.0 / deviceRect.size.height;
+}
+
+}  // namespace cocoa_util
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.mm
index eb82d05..cbcdbbc2 100644
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.mm
+++ b/chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.mm
@@ -17,8 +17,8 @@
 
 namespace {
 
-const CGFloat kWindowMinWidth = 500;
-const CGFloat kButtonGap = 6;
+const CGFloat kConstrainedWindowMinWidth = 500;
+const CGFloat kConstrainedWindowButtonGap = 6;
 
 }  // namespace
 
@@ -163,21 +163,22 @@
     buttonWidth += size.width;
   }
   if ([buttons_ count])
-    buttonWidth += ([buttons_ count] - 1) * kButtonGap;
+    buttonWidth += ([buttons_ count] - 1) * kConstrainedWindowButtonGap;
 
   // Window width.
   CGFloat windowWidth = buttonWidth;
   if (accessoryView_.get())
     windowWidth = std::max(windowWidth, NSWidth([accessoryView_ frame]));
   windowWidth += chrome_style::kHorizontalPadding * 2;
-  windowWidth = std::max(windowWidth, kWindowMinWidth);
+  windowWidth = std::max(windowWidth, kConstrainedWindowMinWidth);
 
   // Layout controls.
   [self layoutButtonsWithWindowWidth:windowWidth];
   CGFloat curY = [buttons_ count] ? NSMaxY([[buttons_ lastObject] frame])
       : chrome_style::kClientBottomPadding;
-  CGFloat availableMessageWidth =
-      windowWidth - chrome_style::GetCloseButtonSize() - kButtonGap;
+  CGFloat availableMessageWidth = windowWidth -
+                                  chrome_style::GetCloseButtonSize() -
+                                  kConstrainedWindowButtonGap;
   curY = [self layoutLinkAtYPos:curY
                     windowWidth:availableMessageWidth];
   curY = [self layoutAccessoryViewAtYPos:curY];
@@ -208,7 +209,7 @@
     rect.origin.x = curX - NSWidth(rect);
     rect.origin.y = chrome_style::kClientBottomPadding;
     [button setFrameOrigin:rect.origin];
-    curX = NSMinX(rect) - kButtonGap;
+    curX = NSMinX(rect) - kConstrainedWindowButtonGap;
   }
 
   // Layout remaining buttons left to right.
@@ -217,7 +218,7 @@
     NSButton* button = [buttons_ objectAtIndex:i];
     [button setFrameOrigin:
         NSMakePoint(curX, chrome_style::kClientBottomPadding)];
-    curX += NSMaxX([button frame]) + kButtonGap;
+    curX += NSMaxX([button frame]) + kConstrainedWindowButtonGap;
   }
 }
 
diff --git a/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.mm b/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.mm
index 6a7386eb..8cd4389 100644
--- a/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.mm
+++ b/chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.mm
@@ -14,7 +14,7 @@
 namespace {
 
 // Length of the animation in seconds.
-const NSTimeInterval kAnimationDuration = 0.18;
+const NSTimeInterval kSheetAnimationDuration = 0.18;
 
 // If the machine has a DisplayLink device attached, the WindowServer has a
 // tendency to crash https://crbug.com/755516. To avoid this, probe the
@@ -68,7 +68,7 @@
 
   if (useSimpleAnimations_) {
     [NSAnimationContext beginGrouping];
-    [[NSAnimationContext currentContext] setDuration:kAnimationDuration];
+    [[NSAnimationContext currentContext] setDuration:kSheetAnimationDuration];
     [[customWindow_ animator] setAlphaValue:1.0];
     [NSAnimationContext endGrouping];
   } else {
@@ -87,7 +87,7 @@
       } ];
       animation.reset(
           [[NSViewAnimation alloc] initWithViewAnimations:animationArray]);
-      [animation setDuration:kAnimationDuration];
+      [animation setDuration:kSheetAnimationDuration];
       [animation setAnimationBlockingMode:NSAnimationBlocking];
     } else {
       animation.reset([[ConstrainedWindowAnimationHide alloc]
diff --git a/chrome/browser/ui/cocoa/download/download_item_cell.mm b/chrome/browser/ui/cocoa/download/download_item_cell.mm
index c7fe09f..c9bbfa9 100644
--- a/chrome/browser/ui/cocoa/download/download_item_cell.mm
+++ b/chrome/browser/ui/cocoa/download/download_item_cell.mm
@@ -25,60 +25,61 @@
 #include "ui/native_theme/native_theme.h"
 
 // Distance from top border to icon.
-const CGFloat kImagePaddingTop = 7;
+const CGFloat kDownloadItemImagePaddingTop = 7;
 
 // Distance from left border to icon.
-const CGFloat kImagePaddingLeft = 9;
+const CGFloat kDownloadItemImagePaddingLeft = 9;
 
 // Horizontal distance from the icon to the text.
-const CGFloat kImagePaddingRight = 7;
+const CGFloat kDownloadItemImagePaddingRight = 7;
 
 // Width of icon.
-const CGFloat kImageWidth = 16;
+const CGFloat kDownloadItemImageWidth = 16;
 
 // Height of icon.
-const CGFloat kImageHeight = 16;
+const CGFloat kDownloadItemImageHeight = 16;
 
 // x coordinate of download name string, in view coords.
-const CGFloat kTextPosLeft = kImagePaddingLeft +
-    kImageWidth + DownloadShelf::kFiletypeIconOffset + kImagePaddingRight;
+const CGFloat kDownloadItemTextPosLeft =
+    kDownloadItemImagePaddingLeft + kDownloadItemImageWidth +
+    DownloadShelf::kFiletypeIconOffset + kDownloadItemImagePaddingRight;
 
 // Distance from end of download name string to dropdown area.
-const CGFloat kTextPaddingRight = 3;
+const CGFloat kDownloadItemTextPaddingRight = 3;
 
 // y coordinate of download name string, in view coords, when status message
 // is visible.
-const CGFloat kPrimaryTextPosTop = 3;
+const CGFloat kDownloadItemPrimaryTextPosTop = 3;
 
 // y coordinate of download name string, in view coords, when status message
 // is not visible.
-const CGFloat kPrimaryTextOnlyPosTop = 10;
+const CGFloat kDownloadItemPrimaryTextOnlyPosTop = 10;
 
 // y coordinate of status message, in view coords.
-const CGFloat kSecondaryTextPosTop = 18;
+const CGFloat kDownloadItemSecondaryTextPosTop = 18;
 
 // Width of dropdown area on the right (includes 1px for the border on each
 // side).
-const CGFloat kDropdownAreaWidth = 14;
+const CGFloat kDownloadItemDropdownAreaWidth = 14;
 
 // Width of dropdown arrow.
-const CGFloat kDropdownArrowWidth = 5;
+const CGFloat kDownloadItemDropdownArrowWidth = 5;
 
 // Height of dropdown arrow.
-const CGFloat kDropdownArrowHeight = 3;
+const CGFloat kDownloadItemDropdownArrowHeight = 3;
 
 // Vertical displacement of dropdown area, relative to the "centered" position.
-const CGFloat kDropdownAreaY = -2;
+const CGFloat kDownloadItemDropdownAreaY = -2;
 
 // Duration of the two-lines-to-one-line animation, in seconds.
-NSTimeInterval kShowStatusDuration = 0.3;
-NSTimeInterval kHideStatusDuration = 0.3;
+NSTimeInterval kDownloadItemShowStatusDuration = 0.3;
+NSTimeInterval kDownloadItemHideStatusDuration = 0.3;
 
 // Duration of the 'download complete' animation, in seconds.
-const CGFloat kCompleteAnimationDuration = 2.5;
+const CGFloat kDownloadItemCompleteAnimationDuration = 2.5;
 
 // Duration of the 'download interrupted' animation, in seconds.
-const CGFloat kInterruptedAnimationDuration = 2.5;
+const CGFloat kDownloadItemInterruptedAnimationDuration = 2.5;
 
 using download::DownloadItem;
 
@@ -134,7 +135,7 @@
 
 - (void)setInitialState {
   isStatusTextVisible_ = NO;
-  titleY_ = kPrimaryTextOnlyPosTop;
+  titleY_ = kDownloadItemPrimaryTextOnlyPosTop;
   statusAlpha_ = 0.0;
 
   [self setFont:[NSFont systemFontOfSize:
@@ -211,7 +212,7 @@
         break;
       completionAnimation_.reset([[DownloadItemCellAnimation alloc]
           initWithDownloadItemCell:self
-                          duration:kCompleteAnimationDuration
+                          duration:kDownloadItemCompleteAnimationDuration
                     animationCurve:NSAnimationLinear]);
       [completionAnimation_.get() setDelegate:self];
       [completionAnimation_.get() startAnimation];
@@ -231,7 +232,7 @@
         break;
       completionAnimation_.reset([[DownloadItemCellAnimation alloc]
           initWithDownloadItemCell:self
-                          duration:kInterruptedAnimationDuration
+                          duration:kDownloadItemInterruptedAnimationDuration
                     animationCurve:NSAnimationLinear]);
       [completionAnimation_.get() setDelegate:self];
       [completionAnimation_.get() startAnimation];
@@ -278,7 +279,7 @@
   NSRect bounds = [[self controlView] bounds];
   NSRect buttonRect, dropdownRect;
   NSDivideRect(bounds, &dropdownRect, &buttonRect,
-      kDropdownAreaWidth, NSMaxXEdge);
+               kDownloadItemDropdownAreaWidth, NSMaxXEdge);
 
   trackingAreaButton_.reset([[NSTrackingArea alloc]
                   initWithRect:buttonRect
@@ -416,8 +417,9 @@
   if (![self secondaryTitle] || statusAlpha_ <= 0)
     return;
 
-  CGFloat textWidth = NSWidth(innerFrame) -
-      (kTextPosLeft + kTextPaddingRight + kDropdownAreaWidth);
+  CGFloat textWidth = NSWidth(innerFrame) - (kDownloadItemTextPosLeft +
+                                             kDownloadItemTextPaddingRight +
+                                             kDownloadItemDropdownAreaWidth);
   NSString* secondaryText = [self elideStatus:textWidth];
   NSColor* secondaryColor =
       [self titleColorForPart:kDownloadItemMouseOverButtonPart];
@@ -433,7 +435,8 @@
           [self secondaryFont], NSFontAttributeName,
           nil];
   NSPoint secondaryPos =
-      NSMakePoint(innerFrame.origin.x + kTextPosLeft, kSecondaryTextPosTop);
+      NSMakePoint(innerFrame.origin.x + kDownloadItemTextPosLeft,
+                  kDownloadItemSecondaryTextPosTop);
 
   gfx::ScopedNSGraphicsContextSaveGState contextSave;
   NSGraphicsContext* nsContext = [NSGraphicsContext currentContext];
@@ -478,7 +481,7 @@
 
   NSRect buttonDrawRect, dropdownDrawRect;
   NSDivideRect(drawFrame, &dropdownDrawRect, &buttonDrawRect,
-      kDropdownAreaWidth, NSMaxXEdge);
+               kDownloadItemDropdownAreaWidth, NSMaxXEdge);
 
   NSBezierPath* buttonInnerPath = [self
       leftRoundedPath:radius inRect:buttonDrawRect];
@@ -524,8 +527,9 @@
 
 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
   // Draw title
-  CGFloat textWidth = NSWidth(cellFrame) -
-      (kTextPosLeft + kTextPaddingRight + kDropdownAreaWidth);
+  CGFloat textWidth = NSWidth(cellFrame) - (kDownloadItemTextPosLeft +
+                                            kDownloadItemTextPaddingRight +
+                                            kDownloadItemDropdownAreaWidth);
   [self setTitle:[self elideTitle:textWidth]];
 
   NSColor* color = [self titleColorForPart:kDownloadItemMouseOverButtonPart];
@@ -536,9 +540,8 @@
           color, NSForegroundColorAttributeName,
           [self font], NSFontAttributeName,
           nil];
-  NSPoint primaryPos = NSMakePoint(
-      cellFrame.origin.x + kTextPosLeft,
-      titleY_);
+  NSPoint primaryPos =
+      NSMakePoint(cellFrame.origin.x + kDownloadItemTextPosLeft, titleY_);
 
   [primaryText drawAtPoint:primaryPos withAttributes:primaryTextAttributes];
 
@@ -595,7 +598,7 @@
                      hints:nil];
 
   // Separator between button and popup parts
-  CGFloat lx = NSMaxX(cellFrame) - kDropdownAreaWidth + 0.5;
+  CGFloat lx = NSMaxX(cellFrame) - kDownloadItemDropdownAreaWidth + 0.5;
   [[NSColor colorWithDeviceWhite:0.0 alpha:0.1] set];
   [NSBezierPath strokeLineFromPoint:NSMakePoint(lx, NSMinY(cellFrame) + 1)
                             toPoint:NSMakePoint(lx, NSMaxY(cellFrame) - 1)];
@@ -605,13 +608,16 @@
 
   // Popup arrow. Put center of mass of the arrow in the center of the
   // dropdown area.
-  CGFloat cx = NSMaxX(cellFrame) - kDropdownAreaWidth/2 + 0.5;
+  CGFloat cx = NSMaxX(cellFrame) - kDownloadItemDropdownAreaWidth / 2 + 0.5;
   CGFloat cy = NSMidY(cellFrame);
-  NSPoint p1 = NSMakePoint(cx - kDropdownArrowWidth/2,
-                           cy - kDropdownArrowHeight/3 + kDropdownAreaY);
-  NSPoint p2 = NSMakePoint(cx + kDropdownArrowWidth/2,
-                           cy - kDropdownArrowHeight/3 + kDropdownAreaY);
-  NSPoint p3 = NSMakePoint(cx, cy + kDropdownArrowHeight*2/3 + kDropdownAreaY);
+  NSPoint p1 = NSMakePoint(
+      cx - kDownloadItemDropdownArrowWidth / 2,
+      cy - kDownloadItemDropdownArrowHeight / 3 + kDownloadItemDropdownAreaY);
+  NSPoint p2 = NSMakePoint(
+      cx + kDownloadItemDropdownArrowWidth / 2,
+      cy - kDownloadItemDropdownArrowHeight / 3 + kDownloadItemDropdownAreaY);
+  NSPoint p3 = NSMakePoint(cx, cy + kDownloadItemDropdownArrowHeight * 2 / 3 +
+                                   kDownloadItemDropdownAreaY);
   NSBezierPath *triangle = [NSBezierPath bezierPath];
   [triangle moveToPoint:p1];
   [triangle lineToPoint:p2];
@@ -633,10 +639,9 @@
 }
 
 - (NSRect)imageRectForBounds:(NSRect)cellFrame {
-  return NSMakeRect(cellFrame.origin.x + kImagePaddingLeft,
-                    cellFrame.origin.y + kImagePaddingTop,
-                    kImageWidth,
-                    kImageHeight);
+  return NSMakeRect(cellFrame.origin.x + kDownloadItemImagePaddingLeft,
+                    cellFrame.origin.y + kDownloadItemImagePaddingTop,
+                    kDownloadItemImageWidth, kDownloadItemImageHeight);
 }
 
 - (void)setupToggleStatusVisibilityAnimation {
@@ -651,7 +656,7 @@
     // Don't use core animation -- text in CA layers is not subpixel antialiased
     toggleStatusVisibilityAnimation_.reset([[DownloadItemCellAnimation alloc]
         initWithDownloadItemCell:self
-                        duration:kShowStatusDuration
+                        duration:kDownloadItemShowStatusDuration
                   animationCurve:NSAnimationEaseIn]);
     [toggleStatusVisibilityAnimation_.get() setDelegate:self];
     [toggleStatusVisibilityAnimation_.get() startAnimation];
@@ -680,11 +685,12 @@
    progressed:(NSAnimationProgress)progress {
   if (animation == toggleStatusVisibilityAnimation_) {
     if (isStatusTextVisible_) {
-      titleY_ = (1 - progress)*kPrimaryTextOnlyPosTop + kPrimaryTextPosTop;
+      titleY_ = (1 - progress) * kDownloadItemPrimaryTextOnlyPosTop +
+                kDownloadItemPrimaryTextPosTop;
       statusAlpha_ = progress;
     } else {
-      titleY_ = progress*kPrimaryTextOnlyPosTop +
-          (1 - progress)*kPrimaryTextPosTop;
+      titleY_ = progress * kDownloadItemPrimaryTextOnlyPosTop +
+                (1 - progress) * kDownloadItemPrimaryTextPosTop;
       statusAlpha_ = 1 - progress;
     }
     [[self controlView] setNeedsDisplay:YES];
diff --git a/chrome/browser/ui/cocoa/download/download_show_all_cell.mm b/chrome/browser/ui/cocoa/download/download_show_all_cell.mm
index 71e846c0..39be4ce4 100644
--- a/chrome/browser/ui/cocoa/download/download_show_all_cell.mm
+++ b/chrome/browser/ui/cocoa/download/download_show_all_cell.mm
@@ -11,32 +11,33 @@
 #include "chrome/grit/theme_resources.h"
 
 // Distance from top border to icon.
-const CGFloat kImagePaddingTop = 7;
+const CGFloat kDownloadShowAllImagePaddingTop = 7;
 
 // Distance from left border to icon.
-const CGFloat kImagePaddingLeft = 11;
+const CGFloat kDownloadShowAllImagePaddingLeft = 11;
 
 // Width of icon.
-const CGFloat kImageWidth = 16;
+const CGFloat kDownloadShowAllImageWidth = 16;
 
 // Height of icon.
-const CGFloat kImageHeight = 16;
+const CGFloat kDownloadShowAllImageHeight = 16;
 
 // x distance between image and title.
-const CGFloat kImageTextPadding = 4;
+const CGFloat kDownloadShowAllImageTextPadding = 4;
 
 // x coordinate of download name string, in view coords.
-const CGFloat kTextPosLeft =
-    kImagePaddingLeft + kImageWidth + kImageTextPadding;
+const CGFloat kDownloadShowAllTextPosLeft = kDownloadShowAllImagePaddingLeft +
+                                            kDownloadShowAllImageWidth +
+                                            kDownloadShowAllImageTextPadding;
 
 // Distance from end of title to right border.
-const CGFloat kTextPaddingRight = 13;
+const CGFloat kDownloadShowAllTextPaddingRight = 13;
 
 // y coordinate of title, in view coords.
-const CGFloat kTextPosTop = 10;
+const CGFloat kDownloadShowAllTextPosTop = 10;
 
 // Width of outer stroke
-const CGFloat kOuterStrokeWidth = 1;
+const CGFloat kDownloadShowAllOuterStrokeWidth = 1;
 
 @interface DownloadShowAllCell(Private)
 - (const ui::ThemeProvider*)backgroundThemeWrappingProvider:
@@ -156,8 +157,9 @@
 
 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
   // Draw title
-  NSPoint primaryPos = NSMakePoint(
-      cellFrame.origin.x + kTextPosLeft, kTextPosTop);
+  NSPoint primaryPos =
+      NSMakePoint(cellFrame.origin.x + kDownloadShowAllTextPosLeft,
+                  kDownloadShowAllTextPosTop);
   [[self title] drawAtPoint:primaryPos withAttributes:[self textAttributes]];
 
   // Draw icon
@@ -170,10 +172,9 @@
 }
 
 - (NSRect)imageRectForBounds:(NSRect)cellFrame {
-  return NSMakeRect(cellFrame.origin.x + kImagePaddingLeft,
-                    cellFrame.origin.y + kImagePaddingTop,
-                    kImageWidth,
-                    kImageHeight);
+  return NSMakeRect(cellFrame.origin.x + kDownloadShowAllImagePaddingLeft,
+                    cellFrame.origin.y + kDownloadShowAllImagePaddingTop,
+                    kDownloadShowAllImageWidth, kDownloadShowAllImageHeight);
 }
 
 - (NSSize)cellSize {
@@ -181,8 +182,9 @@
 
   // Custom width:
   NSSize textSize = [[self title] sizeWithAttributes:[self textAttributes]];
-  size.width = kTextPosLeft + textSize.width + kTextPaddingRight +
-      kOuterStrokeWidth * 2;
+  size.width = kDownloadShowAllTextPosLeft + textSize.width +
+               kDownloadShowAllTextPaddingRight +
+               kDownloadShowAllOuterStrokeWidth * 2;
   return size;
 }
 @end
diff --git a/chrome/browser/ui/cocoa/extensions/browser_action_button.mm b/chrome/browser/ui/cocoa/extensions/browser_action_button.mm
index 5cb149c..5e09052 100644
--- a/chrome/browser/ui/cocoa/extensions/browser_action_button.mm
+++ b/chrome/browser/ui/cocoa/extensions/browser_action_button.mm
@@ -38,11 +38,11 @@
 NSString* const kBrowserActionButtonDragEndNotification =
     @"BrowserActionButtonDragEndNotification";
 
-static const CGFloat kAnimationDuration = 0.2;
-static const CGFloat kMinimumDragDistance = 5;
+static const CGFloat kBrowserActionButtonAnimationDuration = 0.2;
+static const CGFloat kBrowserActionButtonMinimumDragDistance = 5;
 
 // Mirrors ui/views/mouse_constants.h for suppressing popup activation.
-static const int kMinimumMsBetweenCloseOpenPopup = 100;
+static const int kBrowserActionButtonMinimumMsBetweenCloseOpenPopup = 100;
 
 @interface BrowserActionButton ()
 - (void)endDrag;
@@ -69,7 +69,8 @@
     // If the user clicks on the browser action button to close the bubble,
     // don't show the next popup too soon on the mouse button up.
     base::TimeDelta delta = base::TimeTicks::Now() - popup_closed_time_;
-    return delta.InMilliseconds() >= kMinimumMsBetweenCloseOpenPopup;
+    return delta.InMilliseconds() >=
+           kBrowserActionButtonMinimumMsBetweenCloseOpenPopup;
   }
 
  private:
@@ -234,7 +235,7 @@
     [self setShowsBorderOnlyWhileMouseInside:YES];
 
     moveAnimation_.reset([[NSViewAnimation alloc] init]);
-    [moveAnimation_ gtm_setDuration:kAnimationDuration
+    [moveAnimation_ gtm_setDuration:kBrowserActionButtonAnimationDuration
                           eventMask:NSLeftMouseUpMask];
     [moveAnimation_ setAnimationBlockingMode:NSAnimationNonblocking];
 
@@ -294,11 +295,13 @@
 
   NSPoint eventPoint = [theEvent locationInWindow];
   if (!isBeingDragged_) {
-    // Don't initiate a drag until it moves at least kMinimumDragDistance.
+    // Don't initiate a drag until it moves at least
+    // kBrowserActionButtonMinimumDragDistance.
     NSPoint dragStart = [self convertPoint:dragStartPoint_ toView:nil];
     CGFloat dx = eventPoint.x - dragStart.x;
     CGFloat dy = eventPoint.y - dragStart.y;
-    if (dx*dx + dy*dy < kMinimumDragDistance*kMinimumDragDistance)
+    if (dx * dx + dy * dy < kBrowserActionButtonMinimumDragDistance *
+                                kBrowserActionButtonMinimumDragDistance)
       return;
 
     // The start of a drag. Position the button above all others.
diff --git a/chrome/browser/ui/cocoa/info_bubble_window.mm b/chrome/browser/ui/cocoa/info_bubble_window.mm
index 97cc9b4..c472054 100644
--- a/chrome/browser/ui/cocoa/info_bubble_window.mm
+++ b/chrome/browser/ui/cocoa/info_bubble_window.mm
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "chrome/browser/chrome_notification_types.h"
 #import "chrome/browser/ui/cocoa/base_bubble_controller.h"
+#include "chrome/browser/ui/cocoa/cocoa_util.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "content/public/browser/notification_service.h"
@@ -22,11 +23,6 @@
 const CGFloat kOrderInSlideOffset = 10;
 const NSTimeInterval kOrderInAnimationDuration = 0.075;
 const NSTimeInterval kOrderOutAnimationDuration = 0.15;
-// The minimum representable time interval.  This can be used as the value
-// passed to +[NSAnimationContext setDuration:] to stop an in-progress
-// animation as quickly as possible.
-const NSTimeInterval kMinimumTimeInterval =
-    std::numeric_limits<NSTimeInterval>::min();
 }  // namespace
 
 @interface InfoBubbleWindow (Private)
@@ -199,7 +195,8 @@
   // Cancel the current animation so that it closes immediately, triggering
   // |finishCloseAfterAnimation|.
   [NSAnimationContext beginGrouping];
-  [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval];
+  [[NSAnimationContext currentContext]
+      setDuration:cocoa_util::kMinimumTimeInterval];
   [[self animator] setAlphaValue:0.0];
   [NSAnimationContext endGrouping];
 }
@@ -234,7 +231,8 @@
     // The star currently triggers on mouse down, not mouse up.
     NSTimeInterval duration =
         (allowedAnimations_ & info_bubble::kAnimateOrderIn)
-            ? kOrderInAnimationDuration : kMinimumTimeInterval;
+            ? kOrderInAnimationDuration
+            : cocoa_util::kMinimumTimeInterval;
     [[NSAnimationContext currentContext]
         gtm_setDuration:duration
               eventMask:NSLeftMouseUpMask | NSLeftMouseDownMask];
diff --git a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
index b1097c1..636bb40d4 100644
--- a/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
+++ b/chrome/browser/ui/cocoa/location_bar/location_bar_decoration.mm
@@ -32,7 +32,7 @@
 const SkColor kPressedDarkBackgroundColor = 0x3DFFFFFF;
 
 // Amount of inset for the background frame.
-const CGFloat kBackgroundFrameYInset = 2.0;
+const CGFloat kDecorationBackgroundFrameYInset = 2.0;
 
 }  // namespace
 
@@ -186,7 +186,7 @@
 }
 
 NSRect LocationBarDecoration::GetBackgroundFrame(NSRect frame) {
-  return NSInsetRect(frame, 0.0, kBackgroundFrameYInset);
+  return NSInsetRect(frame, 0.0, kDecorationBackgroundFrameYInset);
 }
 
 void LocationBarDecoration::UpdateAccessibilityView(NSRect apparent_frame) {
diff --git a/chrome/browser/ui/cocoa/new_tab_button_cocoa.mm b/chrome/browser/ui/cocoa/new_tab_button_cocoa.mm
index b544a5a..5721b93 100644
--- a/chrome/browser/ui/cocoa/new_tab_button_cocoa.mm
+++ b/chrome/browser/ui/cocoa/new_tab_button_cocoa.mm
@@ -6,6 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/mac/sdk_forward_declarations.h"
+#include "chrome/browser/ui/cocoa/cocoa_util.h"
 #import "chrome/browser/ui/cocoa/image_button_cell.h"
 #include "chrome/browser/ui/cocoa/l10n_util.h"
 #include "chrome/browser/ui/cocoa/tabs/tab_view.h"
@@ -118,12 +119,6 @@
   }) autorelease];
 }
 
-CGFloat LineWidthFromContext(CGContextRef context) {
-  CGRect unitRect = CGRectMake(0.0, 0.0, 1.0, 1.0);
-  CGRect deviceRect = CGContextConvertRectToDeviceSpace(context, unitRect);
-  return 1.0 / deviceRect.size.height;
-}
-
 }  // namespace
 
 @interface NewTabButtonCustomImageRep : NSCustomImageRep
@@ -412,7 +407,7 @@
 
   CGContextRef context = static_cast<CGContextRef>(
       [[NSGraphicsContext currentContext] graphicsPort]);
-  CGFloat lineWidth = LineWidthFromContext(context);
+  CGFloat lineWidth = cocoa_util::LineWidthFromContext(context);
   NSBezierPath* bezierPath =
       [self newTabButtonBezierPathWithLineWidth:lineWidth];
 
@@ -522,7 +517,7 @@
   [fillColor set];
   CGContextRef context = static_cast<CGContextRef>(
       [[NSGraphicsContext currentContext] graphicsPort]);
-  CGFloat lineWidth = LineWidthFromContext(context);
+  CGFloat lineWidth = cocoa_util::LineWidthFromContext(context);
   [[NewTabButtonCocoa newTabButtonBezierPathWithLineWidth:lineWidth] fill];
   [image unlockFocus];
   return image;
diff --git a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
index 48ae51f..586ff97 100644
--- a/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
+++ b/chrome/browser/ui/cocoa/profiles/profile_signin_confirmation_view_controller.mm
@@ -17,6 +17,7 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #import "chrome/browser/ui/cocoa/chrome_style.h"
+#include "chrome/browser/ui/cocoa/cocoa_util.h"
 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h"
 #import "chrome/browser/ui/cocoa/hover_close_button.h"
 #include "chrome/browser/ui/sync/profile_signin_confirmation_helper.h"
@@ -57,18 +58,6 @@
   [textField setAttributedStringValue:text];
 }
 
-// Remove underlining from the specified range of characters in a text view.
-void RemoveUnderlining(NSTextView* textView, int offset, int length) {
-  // Clear the default link attributes that were set by the
-  // HyperlinkTextView, otherwise removing the underline doesn't matter.
-  [textView setLinkTextAttributes:nil];
-  NSTextStorage* text = [textView textStorage];
-  NSRange range = NSMakeRange(offset, length);
-  [text addAttribute:NSUnderlineStyleAttributeName
-               value:[NSNumber numberWithInt:NSUnderlineStyleNone]
-               range:range];
-}
-
 // Create a new NSTextView and add it to the specified parent.
 NSTextView* AddTextView(
     NSView* parent,
@@ -94,7 +83,7 @@
   [textView addLinkRange:NSMakeRange(offset, [linkString length])
                  withURL:nil
                linkColor:linkColor];
-  RemoveUnderlining(textView, offset, link.size());
+  cocoa_util::RemoveUnderlining(textView, offset, link.size());
   [textView setDelegate:delegate];
   [parent addSubview:textView];
   return textView.autorelease();
diff --git a/chrome/browser/ui/cocoa/spinner_util.h b/chrome/browser/ui/cocoa/spinner_util.h
new file mode 100644
index 0000000..25c73da
--- /dev/null
+++ b/chrome/browser/ui/cocoa/spinner_util.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COCOA_SPINNER_UTIL_H_
+#define CHROME_BROWSER_UI_COCOA_SPINNER_UTIL_H_
+
+#include "ui/gfx/geometry/angle_conversions.h"
+
+namespace cocoa_spinner_util {
+
+constexpr CGFloat kDegrees90 = gfx::DegToRad(90.0f);
+constexpr CGFloat kDegrees135 = gfx::DegToRad(135.0f);
+constexpr CGFloat kDegrees180 = gfx::DegToRad(180.0f);
+constexpr CGFloat kDegrees270 = gfx::DegToRad(270.0f);
+constexpr CGFloat kDegrees360 = gfx::DegToRad(360.0f);
+constexpr CGFloat kSpinnerViewUnitWidth = 28.0;
+constexpr CGFloat kSpinnerUnitInset = 2.0;
+constexpr CGFloat kArcDiameter =
+    (kSpinnerViewUnitWidth - kSpinnerUnitInset * 2.0);
+constexpr CGFloat kArcRadius = kArcDiameter / 2.0;
+constexpr CGFloat kArcLength =
+    kDegrees135 * kArcDiameter;  // 135 degrees of circumference.
+constexpr CGFloat kArcStrokeWidth = 3.0;
+constexpr CGFloat kArcAnimationTime = 1.333;
+constexpr CGFloat kRotationTime = 1.56863;
+NSString* const kSpinnerAnimationName = @"SpinnerAnimationName";
+NSString* const kRotationAnimationName = @"RotationAnimationName";
+constexpr CGFloat kWaitingStrokeAlpha = 0.5;
+
+}  // namespace cocoa_spinner_util
+
+#endif  // CHROME_BROWSER_UI_COCOA_SPINNER_UTIL_H_
diff --git a/chrome/browser/ui/cocoa/spinner_view.mm b/chrome/browser/ui/cocoa/spinner_view.mm
index f1c89167..098ac95 100644
--- a/chrome/browser/ui/cocoa/spinner_view.mm
+++ b/chrome/browser/ui/cocoa/spinner_view.mm
@@ -9,30 +9,27 @@
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/sdk_forward_declarations.h"
 #import "chrome/browser/ui/cocoa/md_util.h"
+#include "chrome/browser/ui/cocoa/spinner_util.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/geometry/angle_conversions.h"
 #include "ui/native_theme/native_theme.h"
 
-namespace {
-constexpr CGFloat kDegrees90 = gfx::DegToRad(90.0f);
-constexpr CGFloat kDegrees135 = gfx::DegToRad(135.0f);
-constexpr CGFloat kDegrees180 = gfx::DegToRad(180.0f);
-constexpr CGFloat kDegrees270 = gfx::DegToRad(270.0f);
-constexpr CGFloat kDegrees360 = gfx::DegToRad(360.0f);
-constexpr CGFloat kSpinnerViewUnitWidth = 28.0;
-constexpr CGFloat kSpinnerUnitInset = 2.0;
-constexpr CGFloat kArcDiameter =
-    (kSpinnerViewUnitWidth - kSpinnerUnitInset * 2.0);
-constexpr CGFloat kArcRadius = kArcDiameter / 2.0;
-constexpr CGFloat kArcLength =
-    kDegrees135 * kArcDiameter;  // 135 degrees of circumference.
-constexpr CGFloat kArcStrokeWidth = 3.0;
-constexpr CGFloat kArcAnimationTime = 1.333;
-constexpr CGFloat kRotationTime = 1.56863;
-NSString* const kSpinnerAnimationName  = @"SpinnerAnimationName";
-NSString* const kRotationAnimationName = @"RotationAnimationName";
-}
+using cocoa_spinner_util::kDegrees90;
+using cocoa_spinner_util::kDegrees135;
+using cocoa_spinner_util::kDegrees180;
+using cocoa_spinner_util::kDegrees270;
+using cocoa_spinner_util::kDegrees360;
+using cocoa_spinner_util::kSpinnerViewUnitWidth;
+using cocoa_spinner_util::kSpinnerUnitInset;
+using cocoa_spinner_util::kArcDiameter;
+using cocoa_spinner_util::kArcRadius;
+using cocoa_spinner_util::kArcLength;
+using cocoa_spinner_util::kArcStrokeWidth;
+using cocoa_spinner_util::kArcAnimationTime;
+using cocoa_spinner_util::kRotationTime;
+using cocoa_spinner_util::kSpinnerAnimationName;
+using cocoa_spinner_util::kRotationAnimationName;
 
 @implementation SpinnerView {
   CAShapeLayer* shapeLayer_;  // Weak.
diff --git a/chrome/browser/ui/cocoa/status_bubble_mac.mm b/chrome/browser/ui/cocoa/status_bubble_mac.mm
index a5b912b..a519186d 100644
--- a/chrome/browser/ui/cocoa/status_bubble_mac.mm
+++ b/chrome/browser/ui/cocoa/status_bubble_mac.mm
@@ -17,6 +17,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #import "chrome/browser/ui/cocoa/bubble_view.h"
+#include "chrome/browser/ui/cocoa/cocoa_util.h"
 #include "components/url_formatter/elide_url.h"
 #include "components/url_formatter/url_formatter.h"
 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSAnimation+Duration.h"
@@ -54,12 +55,6 @@
 const NSTimeInterval kShowFadeInDurationSeconds = 0.120;
 const NSTimeInterval kHideFadeOutDurationSeconds = 0.200;
 
-// The minimum representable time interval.  This can be used as the value
-// passed to +[NSAnimationContext setDuration:] to stop an in-progress
-// animation as quickly as possible.
-const NSTimeInterval kMinimumTimeInterval =
-    std::numeric_limits<NSTimeInterval>::min();
-
 // How quickly the status bubble should expand.
 const CGFloat kExpansionDurationSeconds = 0.125;
 
@@ -283,9 +278,10 @@
 
     if (!immediate_) {
       // An animation is in progress.  Cancel it by starting a new animation.
-      // Use kMinimumTimeInterval to set the opacity as rapidly as possible.
+      // Use cocoa_util::kMinimumTimeInterval to set the opacity as rapidly as
+      // possible.
       fade_out = true;
-      AnimateWindowAlpha(0.0, kMinimumTimeInterval);
+      AnimateWindowAlpha(0.0, cocoa_util::kMinimumTimeInterval);
     }
   }
 
@@ -300,7 +296,8 @@
   // Stop any width animation and reset the bubble size.
   if (!immediate_) {
     [NSAnimationContext beginGrouping];
-    [[NSAnimationContext currentContext] setDuration:kMinimumTimeInterval];
+    [[NSAnimationContext currentContext]
+        setDuration:cocoa_util::kMinimumTimeInterval];
     [[window_ animator] setFrame:frame display:NO];
     [NSAnimationContext endGrouping];
   } else {
@@ -538,7 +535,7 @@
 
   // 0.0 will not cancel an in-progress animation.
   if (duration == 0.0)
-    duration = kMinimumTimeInterval;
+    duration = cocoa_util::kMinimumTimeInterval;
 
   // Cancel an in-progress transition and replace it with this fade.
   AnimateWindowAlpha(opacity, duration);
diff --git a/chrome/browser/ui/cocoa/tabbed_browser_window.mm b/chrome/browser/ui/cocoa/tabbed_browser_window.mm
index 1d7f07d..9778a58 100644
--- a/chrome/browser/ui/cocoa/tabbed_browser_window.mm
+++ b/chrome/browser/ui/cocoa/tabbed_browser_window.mm
@@ -6,6 +6,7 @@
 
 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
 #import "chrome/browser/ui/cocoa/browser_window_layout.h"
+#include "ui/views/widget/util_mac.h"
 
 // Implementer's note: Moving the window controls is tricky. When altering the
 // code, ensure that:
@@ -29,9 +30,7 @@
 - (CGFloat)fullScreenButtonOriginAdjustment;
 @end
 
-// Weak so that Chrome will launch if a future macOS doesn't have NSThemeFrame.
-WEAK_IMPORT_ATTRIBUTE
-@interface NSThemeFrame : NSView
+@interface NSThemeFrame (PrivateTabbedBrowserWindowAPI)
 - (NSView*)fullScreenButton
     __attribute__((availability(macos, obsoleted = 10.10)));
 @end
diff --git a/chrome/browser/ui/cocoa/tabs/tab_spinner_view.mm b/chrome/browser/ui/cocoa/tabs/tab_spinner_view.mm
index 857e9d4..a71c07dd 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_spinner_view.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_spinner_view.mm
@@ -6,18 +6,17 @@
 
 #include "base/mac/scoped_cftyperef.h"
 #include "chrome/browser/themes/theme_properties.h"
+#include "chrome/browser/ui/cocoa/spinner_util.h"
 #include "skia/ext/skia_utils_mac.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/geometry/angle_conversions.h"
 #include "ui/native_theme/native_theme.h"
 
-namespace {
-constexpr CGFloat kDegrees90 = gfx::DegToRad(90.0f);
-constexpr CGFloat kDegrees180 = gfx::DegToRad(180.0f);
-constexpr CGFloat kDegrees270 = gfx::DegToRad(270.0f);
-constexpr CGFloat kDegrees360 = gfx::DegToRad(360.0f);
-constexpr CGFloat kWaitingStrokeAlpha = 0.5;
-}  // namespace
+using cocoa_spinner_util::kDegrees90;
+using cocoa_spinner_util::kDegrees180;
+using cocoa_spinner_util::kDegrees270;
+using cocoa_spinner_util::kDegrees360;
+using cocoa_spinner_util::kWaitingStrokeAlpha;
 
 @implementation TabSpinnerView {
   BOOL spinReverse_;
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
index 515cf29..b2f3104 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_controller.mm
@@ -94,7 +94,7 @@
 // The amount by which the new tab button is offset (from the tabs).
 const CGFloat kNewTabButtonOffset = 10.0;
 
-const NSTimeInterval kAnimationDuration = 0.125;
+const NSTimeInterval kTabAnimationDuration = 0.125;
 
 // Helper class for doing NSAnimationContext calls that takes a bool to disable
 // all the work.  Useful for code that wants to conditionally animate.
@@ -412,7 +412,7 @@
 @synthesize trailingIndentForControls = trailingIndentForControls_;
 
 + (CGFloat)tabAnimationDuration {
-  return kAnimationDuration;
+  return kTabAnimationDuration;
 }
 
 - (id)initWithView:(TabStripView*)view
@@ -948,7 +948,7 @@
 
   NSRect enclosingRect = NSZeroRect;
   ScopedNSAnimationContextGroup mainAnimationGroup(animate);
-  mainAnimationGroup.SetCurrentContextDuration(kAnimationDuration);
+  mainAnimationGroup.SetCurrentContextDuration(kTabAnimationDuration);
 
   // Update the current subviews and their z-order if requested.
   if (doUpdate)
@@ -1168,7 +1168,7 @@
       // display the wrong content when using a theme that creates transparent
       // tabs.
       ScopedNSAnimationContextGroup subAnimationGroup(animate);
-      subAnimationGroup.SetCurrentContextDuration(kAnimationDuration);
+      subAnimationGroup.SetCurrentContextDuration(kTabAnimationDuration);
       NSView* tabView = [tab view];
       [[NSAnimationContext currentContext] setCompletionHandler:^{
         [tabView setNeedsDisplay:YES];
@@ -1513,7 +1513,7 @@
   NSRect newFrame = [tabView frame];
   newFrame = NSOffsetRect(newFrame, 0, -newFrame.size.height);
   ScopedNSAnimationContextGroup animationGroup(true);
-  animationGroup.SetCurrentContextDuration(kAnimationDuration);
+  animationGroup.SetCurrentContextDuration(kTabAnimationDuration);
   [[tabView animator] setFrame:newFrame];
 }
 
diff --git a/chrome/browser/ui/cocoa/tabs/tab_view.mm b/chrome/browser/ui/cocoa/tabs/tab_view.mm
index 917cbd6..58889963 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_view.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_view.mm
@@ -88,7 +88,8 @@
 + (void)setTabEdgeStrokeColor;
 @end
 
-extern NSString* const _Nonnull NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification;
+extern NSString* const
+    NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification;
 
 namespace {
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
index 3a16903..0166d1d3 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model_unittest.cc
@@ -83,8 +83,8 @@
 
   // FakeSyncService:
   int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
+  State GetState() const override { return State::ACTIVE; }
   bool IsFirstSetupComplete() const override { return true; }
-  bool IsSyncActive() const override { return true; }
   syncer::ModelTypeSet GetActiveDataTypes() const override {
     switch (synced_types_) {
       case SyncedTypes::ALL:
@@ -106,6 +106,8 @@
 
  private:
   SyncedTypes synced_types_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncService);
 };
 
 std::unique_ptr<KeyedService> TestingSyncFactoryFunction(
diff --git a/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc b/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc
index ea9611e..0515de3 100644
--- a/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc
+++ b/chrome/browser/ui/sync/one_click_signin_sync_observer_unittest.cc
@@ -67,24 +67,22 @@
     return first_setup_in_progress_;
   }
 
-  bool IsSyncActive() const override { return sync_active_; }
+  State GetState() const override { return state_; }
 
   void set_first_setup_in_progress(bool in_progress) {
     first_setup_in_progress_ = in_progress;
   }
 
-  void set_sync_active(bool active) {
-    sync_active_ = active;
-  }
+  void set_state(State state) { state_ = state; }
 
  private:
   explicit OneClickTestProfileSyncService(InitParams init_params)
       : browser_sync::TestProfileSyncService(std::move(init_params)),
         first_setup_in_progress_(false),
-        sync_active_(false) {}
+        state_(State::INITIALIZING) {}
 
   bool first_setup_in_progress_;
-  bool sync_active_;
+  State state_;
 
   DISALLOW_COPY_AND_ASSIGN(OneClickTestProfileSyncService);
 };
@@ -201,7 +199,7 @@
        OnSyncStateChanged_SyncConfiguredSuccessfully) {
   CreateSyncObserver(kContinueUrl);
   sync_service_->set_first_setup_in_progress(false);
-  sync_service_->set_sync_active(true);
+  sync_service_->set_state(syncer::SyncService::State::ACTIVE);
 
   EXPECT_CALL(*web_contents_observer_, DidStartNavigation(_));
   sync_service_->NotifyObservers();
@@ -214,7 +212,7 @@
        OnSyncStateChanged_SyncConfigurationFailed) {
   CreateSyncObserver(kContinueUrl);
   sync_service_->set_first_setup_in_progress(false);
-  sync_service_->set_sync_active(false);
+  sync_service_->set_state(syncer::SyncService::State::INITIALIZING);
 
   EXPECT_CALL(*web_contents_observer_, DidStartNavigation(_)).Times(0);
   sync_service_->NotifyObservers();
@@ -227,7 +225,7 @@
        OnSyncStateChanged_SyncConfigurationInProgress) {
   CreateSyncObserver(kContinueUrl);
   sync_service_->set_first_setup_in_progress(true);
-  sync_service_->set_sync_active(false);
+  sync_service_->set_state(syncer::SyncService::State::INITIALIZING);
 
   EXPECT_CALL(*web_contents_observer_, DidStartNavigation(_)).Times(0);
   sync_service_->NotifyObservers();
@@ -246,7 +244,7 @@
       signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, false);
   CreateSyncObserver(continue_url.spec());
   sync_service_->set_first_setup_in_progress(false);
-  sync_service_->set_sync_active(true);
+  sync_service_->set_state(syncer::SyncService::State::ACTIVE);
 
   EXPECT_CALL(*web_contents_observer_, DidStartNavigation(_)).Times(0);
   sync_service_->NotifyObservers();
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
index 710f8fe..a781a13f 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
@@ -199,8 +199,11 @@
   void WaitForLoadFromLastSession() { content::RunAllTasksUntilIdle(); }
 
   void DisableSync() {
-    EXPECT_CALL(*mock_sync_service_, IsSyncActive())
-        .WillRepeatedly(Return(false));
+    EXPECT_CALL(*mock_sync_service_, GetDisableReasons())
+        .WillRepeatedly(
+            Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE));
+    EXPECT_CALL(*mock_sync_service_, GetState())
+        .WillRepeatedly(Return(syncer::SyncService::State::DISABLED));
     EXPECT_CALL(*mock_sync_service_, IsDataTypeControllerRunning(_))
         .WillRepeatedly(Return(false));
     EXPECT_CALL(*mock_sync_service_, GetOpenTabsUIDelegateMock())
@@ -208,8 +211,10 @@
   }
 
   void EnableSync() {
-    EXPECT_CALL(*mock_sync_service_, IsSyncActive())
-        .WillRepeatedly(Return(true));
+    EXPECT_CALL(*mock_sync_service_, GetDisableReasons())
+        .WillRepeatedly(Return(syncer::SyncService::DISABLE_REASON_NONE));
+    EXPECT_CALL(*mock_sync_service_, GetState())
+        .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
     EXPECT_CALL(*mock_sync_service_,
                 IsDataTypeControllerRunning(syncer::SESSIONS))
         .WillRepeatedly(Return(true));
@@ -252,6 +257,8 @@
   FakeSyncServiceObserverList fake_sync_service_observer_list_;
   browser_sync::ProfileSyncServiceMock* mock_sync_service_ = nullptr;
   std::unique_ptr<sync_sessions::SessionsSyncManager> manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(RecentTabsSubMenuModelTest);
 };
 
 // Test disabled "Recently closed" header with no foreign tabs.
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
index 4c90a98..61311a2 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.cc
@@ -16,6 +16,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/ui_features.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/native_theme/native_theme.h"
 #include "ui/views/border.h"
 #include "ui/views/bubble/bubble_border.h"
@@ -24,6 +25,17 @@
 
 namespace autofill {
 
+// TODO(crbug.com/831603): Determine how colors should be shared with menus
+// and/or omnibox, and how these should interact (if at all) with native
+// theme colors.
+const SkColor AutofillPopupBaseView::kBackgroundColor = SK_ColorWHITE;
+const SkColor AutofillPopupBaseView::kSelectedBackgroundColor =
+    gfx::kGoogleGrey200;
+const SkColor AutofillPopupBaseView::kFooterBackgroundColor =
+    gfx::kGoogleGrey050;
+const SkColor AutofillPopupBaseView::kSeparatorColor = gfx::kGoogleGrey200;
+const SkColor AutofillPopupBaseView::kWarningColor = gfx::kGoogleRed600;
+
 int AutofillPopupBaseView::GetCornerRadius() {
   return ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
       views::EMPHASIS_MEDIUM);
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
index 23ca49b1..8019675 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
+++ b/chrome/browser/ui/views/autofill/autofill_popup_base_view.h
@@ -31,6 +31,12 @@
                               public views::WidgetFocusChangeListener,
                               public views::WidgetObserver {
  public:
+  static const SkColor kBackgroundColor;
+  static const SkColor kSelectedBackgroundColor;
+  static const SkColor kFooterBackgroundColor;
+  static const SkColor kSeparatorColor;
+  static const SkColor kWarningColor;
+
   static int GetCornerRadius();
 
  protected:
diff --git a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
index f01fd522..e42239b7 100644
--- a/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_popup_view_native_views.cc
@@ -19,7 +19,6 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/accessibility/platform/ax_platform_node.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/color_palette.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/shadow_value.h"
@@ -46,15 +45,6 @@
 const int kAutofillPopupMinWidth = 64;
 const int kAutofillPopupMaxWidth = 456;
 
-// TODO(crbug.com/831603): Determine how colors should be shared with menus
-// and/or omnibox, and how these should interact (if at all) with native
-// theme colors.
-const SkColor kAutofillPopupBackgroundColor = SK_ColorWHITE;
-const SkColor kAutofillPopupSelectedBackgroundColor = gfx::kGoogleGrey200;
-const SkColor kAutofillPopupFooterBackgroundColor = gfx::kGoogleGrey050;
-const SkColor kAutofillPopupSeparatorColor = gfx::kGoogleGrey200;
-const SkColor kAutofillPopupWarningColor = gfx::kGoogleRed600;
-
 // A space between the input element and the dropdown, so that the dropdown's
 // border doesn't look too close to the element.
 constexpr int kElementBorderPadding = 1;
@@ -278,7 +268,7 @@
       views::BoxLayout::kHorizontal, gfx::Insets(0, GetHorizontalMargin())));
 
   layout->set_cross_axis_alignment(
-      views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_CENTER);
+      views::BoxLayout::CrossAxisAlignment::CROSS_AXIS_ALIGNMENT_STRETCH);
   layout->set_minimum_cross_axis_size(
       views::MenuConfig::instance().touchable_menu_height + extra_height_);
 
@@ -350,8 +340,8 @@
 std::unique_ptr<views::Background>
 AutofillPopupSuggestionView::CreateBackground() {
   return views::CreateSolidBackground(
-      is_selected_ ? kAutofillPopupSelectedBackgroundColor
-                   : kAutofillPopupBackgroundColor);
+      is_selected_ ? AutofillPopupBaseView::kSelectedBackgroundColor
+                   : AutofillPopupBaseView::kBackgroundColor);
 }
 
 int AutofillPopupSuggestionView::GetPrimaryTextStyle() {
@@ -381,14 +371,14 @@
       /*left=*/0,
       /*bottom=*/0,
       /*right=*/0,
-      /*color=*/kAutofillPopupSeparatorColor));
+      /*color=*/AutofillPopupBaseView::kSeparatorColor));
   AutofillPopupItemView::CreateContent();
 }
 
 std::unique_ptr<views::Background> AutofillPopupFooterView::CreateBackground() {
   return views::CreateSolidBackground(
-      is_selected_ ? kAutofillPopupSelectedBackgroundColor
-                   : kAutofillPopupFooterBackgroundColor);
+      is_selected_ ? AutofillPopupBaseView::kSelectedBackgroundColor
+                   : AutofillPopupBaseView::kFooterBackgroundColor);
 }
 
 int AutofillPopupFooterView::GetPrimaryTextStyle() {
@@ -422,7 +412,7 @@
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   views::Separator* separator = new views::Separator();
-  separator->SetColor(kAutofillPopupSeparatorColor);
+  separator->SetColor(AutofillPopupBaseView::kSeparatorColor);
   // Add some spacing between the the previous item and the separator.
   separator->SetPreferredHeight(
       views::MenuConfig::instance().separator_thickness);
@@ -484,7 +474,7 @@
       controller->GetElidedValueAt(line_number_),
       {views::style::GetFont(ChromeTextContext::CONTEXT_BODY_TEXT_LARGE,
                              ChromeTextStyle::STYLE_RED)});
-  text_label->SetEnabledColor(kAutofillPopupWarningColor);
+  text_label->SetEnabledColor(AutofillPopupBaseView::kWarningColor);
   text_label->SetMultiLine(true);
   int max_width =
       std::min(kAutofillPopupMaxWidth,
@@ -544,7 +534,7 @@
   layout_->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
 
   CreateChildViews();
-  SetBackground(views::CreateSolidBackground(kAutofillPopupBackgroundColor));
+  SetBackground(views::CreateSolidBackground(kBackgroundColor));
 }
 
 AutofillPopupViewNativeViews::~AutofillPopupViewNativeViews() {}
@@ -668,7 +658,7 @@
   if (has_footer) {
     views::View* footer_container = new views::View();
     footer_container->SetBackground(
-        views::CreateSolidBackground(kAutofillPopupFooterBackgroundColor));
+        views::CreateSolidBackground(kFooterBackgroundColor));
 
     views::BoxLayout* footer_layout = footer_container->SetLayoutManager(
         std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
diff --git a/chrome/browser/ui/views/frame/browser_native_widget_window_mac.mm b/chrome/browser/ui/views/frame/browser_native_widget_window_mac.mm
index 13b3ec6..3b5b673 100644
--- a/chrome/browser/ui/views/frame/browser_native_widget_window_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_native_widget_window_mac.mm
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "ui/views/widget/util_mac.h"
 #include "ui/views/widget/widget.h"
 
 @interface NSWindow (PrivateBrowserNativeWidgetAPI)
diff --git a/chrome/browser/ui/views/harmony/chrome_typography.cc b/chrome/browser/ui/views/harmony/chrome_typography.cc
index 2a5649e..de8c33f3 100644
--- a/chrome/browser/ui/views/harmony/chrome_typography.cc
+++ b/chrome/browser/ui/views/harmony/chrome_typography.cc
@@ -51,7 +51,8 @@
                            gfx::Font::Weight* weight) {
   switch (context) {
 #if !defined(OS_MACOSX) || BUILDFLAG(MAC_VIEWS_BROWSER)
-    case CONTEXT_OMNIBOX_PRIMARY: {
+    case CONTEXT_OMNIBOX_PRIMARY:
+    case CONTEXT_OMNIBOX_DEEMPHASIZED: {
       constexpr int kDesiredFontSizeRegular = 14;
       constexpr int kDesiredFontSizeTouchable = 15;
       static const int omnibox_primary_delta =
@@ -61,6 +62,9 @@
                   ? kDesiredFontSizeTouchable
                   : kDesiredFontSizeRegular);
       *size_delta = omnibox_primary_delta;
+      if (context == CONTEXT_OMNIBOX_DEEMPHASIZED) {
+        (*size_delta)--;
+      }
       break;
     }
     case CONTEXT_OMNIBOX_DECORATION: {
diff --git a/chrome/browser/ui/views/harmony/chrome_typography.h b/chrome/browser/ui/views/harmony/chrome_typography.h
index 0b2b67c..fbda359 100644
--- a/chrome/browser/ui/views/harmony/chrome_typography.h
+++ b/chrome/browser/ui/views/harmony/chrome_typography.h
@@ -37,6 +37,9 @@
   // Text that goes inside location bar decorations such as the keyword hint.
   CONTEXT_OMNIBOX_DECORATION,
 
+  // Text in omnibox answer results that is slightly smaller than primary font.
+  CONTEXT_OMNIBOX_DEEMPHASIZED,
+
   // Text for titles, body text and buttons that appear in dialogs attempting to
   // mimic the native Windows 10 look and feel.
   CONTEXT_WINDOWS10_NATIVE,
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 7c7917c..50f6b21 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -118,9 +118,15 @@
 
 // Helper function to create a rounded rect background (no stroke).
 std::unique_ptr<views::Background> CreateRoundRectBackground(SkColor bg_color,
-                                                             float radius) {
-  std::unique_ptr<views::Background> background = CreateBackgroundFromPainter(
-      views::Painter::CreateSolidRoundRectPainter(bg_color, radius));
+                                                             float radius,
+                                                             SkColor fg_color) {
+  auto painter =
+      fg_color != SK_ColorTRANSPARENT
+          ? views::Painter::CreateRoundRectWith1PxBorderPainter(
+                bg_color, fg_color, radius)
+          : views::Painter::CreateSolidRoundRectPainter(bg_color, radius);
+  std::unique_ptr<views::Background> background =
+      CreateBackgroundFromPainter(std::move(painter));
   background->SetNativeControlColor(bg_color);
   return background;
 }
@@ -789,8 +795,12 @@
   if (is_popup_mode_) {
     SetBackground(views::CreateSolidBackground(background_color));
   } else if (IsRounded()) {
-    SetBackground(
-        CreateRoundRectBackground(background_color, GetBorderRadius()));
+    // High contrast schemes get a border stroke even on a rounded omnibox.
+    SkColor fg_color = GetNativeTheme()->UsesHighContrastColors()
+                           ? border_color
+                           : SK_ColorTRANSPARENT;
+    SetBackground(CreateRoundRectBackground(background_color, GetBorderRadius(),
+                                            fg_color));
   } else {
     SetBackground(std::make_unique<BackgroundWith1PxBorder>(background_color,
                                                             border_color));
diff --git a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
index 5ca02217..f6115ed 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_result_view.cc
@@ -145,7 +145,7 @@
     if (reverse) {
       suggestion_view_->content()->SetText(match_.answer->second_line());
       suggestion_view_->description()->SetText(match_.contents,
-                                               match_.contents_class, -1);
+                                               match_.contents_class, true);
       suggestion_view_->description()->AppendExtraText(
           match_.answer->first_line());
     } else {
@@ -153,7 +153,7 @@
                                            match_.contents_class);
       suggestion_view_->content()->AppendExtraText(match_.answer->first_line());
       suggestion_view_->description()->SetText(match_.answer->second_line(),
-                                               -1);
+                                               true);
     }
     // AppendExtraText has side effect on color, so explicitly set color.
     // TODO(orinj): Consolidate text color specification in one place.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
index d98f8de..4d0cb05 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_text_view.cc
@@ -139,7 +139,7 @@
 OmniboxTextView::OmniboxTextView(OmniboxResultView* result_view)
     : result_view_(result_view),
       font_height_(0),
-      font_size_delta_(0),
+      use_deemphasized_font_(false),
       wrap_text_lines_(false) {}
 
 OmniboxTextView::~OmniboxTextView() {}
@@ -193,17 +193,17 @@
   return render_text_->text();
 }
 
-void OmniboxTextView::SetText(const base::string16& text, int size_delta) {
+void OmniboxTextView::SetText(const base::string16& text, bool deemphasize) {
   if (cached_classifications_) {
     cached_classifications_.reset();
   } else if (render_text_ && render_text_->text() == text &&
-             size_delta == font_size_delta_) {
+             deemphasize == use_deemphasized_font_) {
     // Only exit early if |cached_classifications_| was empty,
     // i.e. the last time text was set was through this method.
     return;
   }
 
-  font_size_delta_ = size_delta;
+  use_deemphasized_font_ = deemphasize;
   render_text_.reset();
   render_text_ = CreateRenderText(text);
   UpdateLineHeight();
@@ -212,13 +212,13 @@
 
 void OmniboxTextView::SetText(const base::string16& text,
                               const ACMatchClassifications& classifications,
-                              int size_delta) {
+                              bool deemphasize) {
   if (render_text_ && render_text_->text() == text && cached_classifications_ &&
       classifications == *cached_classifications_ &&
-      size_delta == font_size_delta_)
+      deemphasize == use_deemphasized_font_)
     return;
 
-  font_size_delta_ = size_delta;
+  use_deemphasized_font_ = deemphasize;
 
   cached_classifications_ =
       std::make_unique<ACMatchClassifications>(classifications);
@@ -257,8 +257,8 @@
 }
 
 void OmniboxTextView::SetText(const SuggestionAnswer::ImageLine& line,
-                              int size_delta) {
-  font_size_delta_ = size_delta;
+                              bool deemphasize) {
+  use_deemphasized_font_ = deemphasize;
   cached_classifications_.reset();
   wrap_text_lines_ = line.num_text_lines() > 1;
   render_text_.reset();
@@ -315,18 +315,11 @@
   render_text->SetDisplayRect(gfx::Rect(gfx::Size(INT_MAX, 0)));
   render_text->SetCursorEnabled(false);
   render_text->SetElideBehavior(gfx::ELIDE_TAIL);
-  const gfx::FontList& font =
-      views::style::GetFont(CONTEXT_OMNIBOX_PRIMARY, kTextStyle);
-  if (font_size_delta_ == 0) {
-    render_text->SetFontList(font);
-  } else {
-    const gfx::FontList& omnibox_font =
-        views::style::GetFont(CONTEXT_OMNIBOX_PRIMARY, kTextStyle);
-    render_text->SetFontList(
-        ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
-            omnibox_font.GetFontSize() - gfx::FontList().GetFontSize() +
-            font_size_delta_));
-  }
+  const gfx::FontList& font = views::style::GetFont(
+      (use_deemphasized_font_ ? CONTEXT_OMNIBOX_DEEMPHASIZED
+                              : CONTEXT_OMNIBOX_PRIMARY),
+      kTextStyle);
+  render_text->SetFontList(font);
   render_text->SetText(text);
   return render_text;
 }
diff --git a/chrome/browser/ui/views/omnibox/omnibox_text_view.h b/chrome/browser/ui/views/omnibox/omnibox_text_view.h
index 020633e..4a5bc29 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_text_view.h
+++ b/chrome/browser/ui/views/omnibox/omnibox_text_view.h
@@ -48,13 +48,13 @@
   // Sets the render text with default rendering for the given |text|. The
   // |classifications| are used to style the text. An ImageLine incorporates
   // both the text and the styling.
-  // The size_delta is specified here so it can be known in advance of creating
-  // the render text. Applying later would kill bold (clear weights BreakList).
-  void SetText(const base::string16& text, int size_delta = 0);
+  // |deemphasize| specifies whether to use a slightly smaller font than normal.
+  void SetText(const base::string16& text, bool deemphasize = false);
   void SetText(const base::string16& text,
                const ACMatchClassifications& classifications,
-               int font_size_delta = 0);
-  void SetText(const SuggestionAnswer::ImageLine& line, int size_delta = 0);
+               bool deemphasize = false);
+  void SetText(const SuggestionAnswer::ImageLine& line,
+               bool deemphasize = false);
 
   // Adds the "additional" and "status" text from |line|, if any.
   void AppendExtraText(const SuggestionAnswer::ImageLine& line);
@@ -82,8 +82,10 @@
   // Font settings for this view.
   int font_height_;
 
-  // Delta (in px) from default font size.
-  int font_size_delta_;
+  // Whether to apply deemphasized font instead of primary omnibox font.
+  // TODO(orinj): Use a more general ChromeTextContext for flexibility, or
+  // otherwise clean up & unify the different ways of selecting fonts & styles.
+  bool use_deemphasized_font_;
 
   // Whether to wrap lines if the width is too narrow for the whole string.
   bool wrap_text_lines_;
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
index 7825ac4..41463bc 100644
--- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
@@ -24,17 +24,6 @@
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/widget/widget.h"
 
-namespace {
-
-// Background color of the bottom part of the prompt.
-constexpr SkColor kPasswordGenerationPopupFooterBackgroundColor =
-    gfx::kGoogleGrey050;
-
-// Color of the separator between the password and help sections.
-constexpr SkColor kPasswordGenerationPopupSeparatorColor = gfx::kGoogleGrey200;
-
-}  // namespace
-
 // Class that shows the generated password and associated UI (currently an
 // explanatory text).
 class PasswordGenerationPopupViewViews::GeneratedPasswordBox
@@ -141,11 +130,10 @@
   if (controller_->password_selected())
     NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
 
-  password_view_->SetBackground(views::CreateThemedSolidBackground(
-      password_view_,
-      controller_->password_selected()
-          ? ui::NativeTheme::kColorId_ResultsTableHoveredBackground
-          : ui::NativeTheme::kColorId_ResultsTableNormalBackground));
+  password_view_->SetBackground(views::CreateSolidBackground(
+      controller_->password_selected() ? kSelectedBackgroundColor
+                                       : kBackgroundColor));
+  SchedulePaint();
 }
 
 bool PasswordGenerationPopupViewViews::IsPointInPasswordBounds(
@@ -191,8 +179,8 @@
   link_style.disable_line_wrapping = false;
   help_label->AddStyleRange(controller_->HelpTextLinkRange(), link_style);
 
-  help_label->SetBackground(views::CreateSolidBackground(
-      kPasswordGenerationPopupFooterBackgroundColor));
+  help_label->SetBackground(
+      views::CreateSolidBackground(kFooterBackgroundColor));
   help_label->SetBorder(
       views::CreateEmptyBorder(kVerticalPadding, kHorizontalMargin,
                                kVerticalPadding, kHorizontalMargin));
@@ -211,7 +199,7 @@
   if (password_view_) {
     gfx::Rect divider_bounds(0, password_view_->bounds().bottom(),
                              password_view_->width(), 1);
-    canvas->FillRect(divider_bounds, kPasswordGenerationPopupSeparatorColor);
+    canvas->FillRect(divider_bounds, kSeparatorColor);
   }
 }
 
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index fbac12d..bf21e55 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -9,6 +9,8 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/app/vector_icons/vector_icons.h"
@@ -338,6 +340,7 @@
   profile_bubble_->SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
   profile_bubble_->SetArrowPaintType(views::BubbleBorder::PAINT_NONE);
   widget->Show();
+  base::RecordAction(base::UserMetricsAction("ProfileChooser_Show"));
   if (is_source_keyboard)
     profile_bubble_->FocusFirstProfileButton();
 }
@@ -608,16 +611,23 @@
 void ProfileChooserView::ButtonPressed(views::Button* sender,
                                        const ui::Event& event) {
   if (sender == passwords_button_) {
+    base::RecordAction(
+        base::UserMetricsAction("ProfileChooser_PasswordsClicked"));
     chrome::ShowSettingsSubPage(browser_, chrome::kPasswordManagerSubPage);
   } else if (sender == credit_cards_button_) {
+    base::RecordAction(
+        base::UserMetricsAction("ProfileChooser_PaymentsClicked"));
     chrome::ShowSettingsSubPage(browser_, chrome::kAutofillSubPage);
   } else if (sender == addresses_button_) {
+    base::RecordAction(
+        base::UserMetricsAction("ProfileChooser_AddressesClicked"));
     chrome::ShowSettingsSubPage(browser_, chrome::kAutofillSubPage);
   } else if (sender == guest_profile_button_) {
     PrefService* service = g_browser_process->local_state();
     DCHECK(service);
     DCHECK(service->GetBoolean(prefs::kBrowserGuestModeEnabled));
     profiles::SwitchToGuestProfile(ProfileManager::CreateCallback());
+    base::RecordAction(base::UserMetricsAction("ProfileChooser_GuestClicked"));
   } else if (sender == users_button_) {
     // If this is a guest session, close all the guest browser windows.
     if (browser_->profile()->IsGuestSession()) {
@@ -636,6 +646,8 @@
     PostActionPerformed(ProfileMetrics::PROFILE_DESKTOP_MENU_LOCK);
   } else if (sender == close_all_windows_button_) {
     profiles::CloseProfileWindows(browser_->profile());
+    base::RecordAction(
+        base::UserMetricsAction("ProfileChooser_CloseAllClicked"));
   } else if (sender == sync_error_button_) {
     sync_ui_util::AvatarSyncErrorType error =
         static_cast<sync_ui_util::AvatarSyncErrorType>(sender->id());
@@ -670,6 +682,8 @@
         NOTREACHED();
         break;
     }
+    base::RecordAction(
+        base::UserMetricsAction("ProfileChooser_SignInAgainClicked"));
   } else if (sender == remove_account_button_) {
     RemoveAccount();
   } else if (sender == account_removal_cancel_button_) {
@@ -703,6 +717,7 @@
     ShowViewFromMode(view_mode_ == profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT
                          ? profiles::BUBBLE_VIEW_MODE_PROFILE_CHOOSER
                          : profiles::BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT);
+    base::RecordAction(base::UserMetricsAction("ProfileChooser_ManageClicked"));
   } else if (sender == signin_current_profile_button_) {
     ShowViewFromMode(profiles::BUBBLE_VIEW_MODE_GAIA_SIGNIN);
   } else if (sender == signin_with_gaia_account_button_) {
@@ -737,6 +752,8 @@
           profile_match->second, ui::DispositionFromEventFlags(event.flags()) ==
                                      WindowOpenDisposition::NEW_WINDOW,
           ProfileMetrics::SWITCH_PROFILE_ICON);
+      base::RecordAction(
+          base::UserMetricsAction("ProfileChooser_ProfileClicked"));
       Hide();
     } else {
       // This was a profile accounts button.
@@ -1001,6 +1018,8 @@
   sync_error_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
       this, l10n_util::GetStringUTF16(button_string_id));
   sync_error_button_->set_id(error);
+  base::RecordAction(
+      base::UserMetricsAction("ProfileChooser_SignInAgainDisplayed"));
   // Add horizontal and bottom margin to blue button.
   views::View* padded_view = new views::View();
   padded_view->SetLayoutManager(std::make_unique<views::FillLayout>());
@@ -1263,6 +1282,9 @@
     }
   }
 
+  UMA_HISTOGRAM_BOOLEAN("ProfileChooser.HasProfilesShown",
+                        first_profile_button_);
+
   // Add the "Guest" button for browsing as guest
   if (!is_guest && !browser_->profile()->IsSupervised()) {
     PrefService* service = g_browser_process->local_state();
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc
index e98cc806..7bbdaadb 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.cc
+++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -73,13 +73,13 @@
 
 // Returns the ID of the resource that should be used for the button fill if
 // any. |has_custom_image| will be set to true if the images of either the
-// tab, the frame background, (or the toolbar if |is_touch_ui| is true) have
-// been customized.
+// tab, the frame background, (or the toolbar if |is_non_refresh_touch_ui| is
+// true) have been customized.
 int GetButtonFillResourceIdIfAny(const TabStrip* tab_strip,
                                  const ui::ThemeProvider* theme_provider,
-                                 bool is_touch_ui,
+                                 bool is_non_refresh_touch_ui,
                                  bool* has_custom_image) {
-  if (!is_touch_ui)
+  if (!is_non_refresh_touch_ui)
     return tab_strip->GetBackgroundResourceId(has_custom_image);
 
   constexpr int kTouchBackgroundId = IDR_THEME_TOOLBAR;
@@ -262,16 +262,19 @@
 
   // Fill.
   SkPath fill, stroke;
-  if (!MD::IsRefreshUi()) {
-    fill = MD::IsTouchOptimizedUiEnabled()
-               ? GetTouchOptimizedButtonPath(0, scale, false, true)
-               : GetNonTouchOptimizedButtonPath(0, contents_bounds.height(),
-                                                scale, false, true);
-    PaintFill(pressed, scale, fill, canvas);
+  const bool non_refresh_touch_ui =
+      MD::GetMode() == ui::MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED;
+  fill =
+      MD::IsNewerMaterialUi()
+          ? GetNewerMaterialUiButtonPath(0, scale, false, non_refresh_touch_ui)
+          : GetMaterialUiButtonPath(0, contents_bounds.height(), scale, false,
+                                    true);
+  // The ink drop is used to represent the pressed state under Refresh.
+  PaintFill(!MD::IsRefreshUi() && pressed, scale, fill, canvas);
 
-    // Stroke.
+  // Stroke.
+  if (!MD::IsRefreshUi())
     GetBorderPath(0, scale, false, &stroke);
-  }
 
   if (MD::IsNewerMaterialUi()) {
     const int plus_icon_size = MD::IsTouchOptimizedUiEnabled() ? 14 : 12;
@@ -422,42 +425,43 @@
 
   *path =
       MD::IsTouchOptimizedUiEnabled()
-          ? GetTouchOptimizedButtonPath(button_y, scale, extend_to_top, false)
-          : GetNonTouchOptimizedButtonPath(button_y, contents_bounds.height(),
-                                           scale, extend_to_top, false);
+          ? GetNewerMaterialUiButtonPath(button_y, scale, extend_to_top, false)
+          : GetMaterialUiButtonPath(button_y, contents_bounds.height(), scale,
+                                    extend_to_top, false);
 }
 
 void NewTabButton::PaintFill(bool pressed,
                              float scale,
                              const SkPath& fill,
                              gfx::Canvas* canvas) const {
-  DCHECK(!MD::IsRefreshUi());
   gfx::ScopedCanvas scoped_canvas(canvas);
   canvas->UndoDeviceScaleFactor();
   cc::PaintFlags flags;
   flags.setAntiAlias(true);
 
   // For unpressed buttons, draw the fill and its shadow.
-  // Note that for touch-optimized UI, we always draw the fill since the button
-  // has a flat design with no hover highlight.
-  const bool is_touch_ui = MD::IsTouchOptimizedUiEnabled();
-  if (is_touch_ui || !pressed) {
+  // Note that for newer UI, we always draw the fill since the button
+  // has a flat design. Hover highlights are handled by the ink drop.
+  const bool is_newer_ui = MD::IsNewerMaterialUi();
+  if (is_newer_ui || !pressed) {
+    const bool non_refresh_touch_ui =
+        MD::GetMode() == ui::MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED;
     // First we compute the background image coordinates and scale, in case we
     // need to draw a custom background image.
     const ui::ThemeProvider* tp = GetThemeProvider();
     bool custom_image;
-    const int bg_id = GetButtonFillResourceIdIfAny(tab_strip_, tp, is_touch_ui,
-                                                   &custom_image);
+    const int bg_id = GetButtonFillResourceIdIfAny(
+        tab_strip_, tp, non_refresh_touch_ui, &custom_image);
     if (custom_image && !new_tab_promo_observer_.IsObservingSources()) {
-      // For non-touch, the background starts at |background_offset_| unless
-      // there's a custom tab background image, which starts at the top of
-      // the tabstrip (which is also the top of this button, i.e. y = 0).
-      const int non_touch_offset_y =
-          tp->HasCustomImage(bg_id) ? 0 : background_offset_.y();
-      // For touch, the background matches the active tab background
-      // positioning in Tab::PaintTab().
-      const int offset_y =
-          is_touch_ui ? -Tab::GetStrokeHeight() : non_touch_offset_y;
+      // For non-refresh touch UI, the background is that of the active tab, so
+      // the positioning must match that in Tab::PaintTab().  For all other
+      // cases, the background is that of the inactive tab, so the positioning
+      // must match that in Tab::PaintInactiveTabBackground().
+      int offset_y;
+      if (non_refresh_touch_ui)
+        offset_y = -Tab::GetStrokeHeight();
+      else
+        offset_y = tp->HasCustomImage(bg_id) ? 0 : background_offset_.y();
       // The new tab background is mirrored in RTL mode, but the theme
       // background should never be mirrored. Mirror it here to compensate.
       float x_scale = 1.0f;
@@ -478,16 +482,19 @@
       flags.setColor(GetButtonFillColor());
     }
 
-    const SkColor stroke_color = tab_strip_->GetToolbarTopSeparatorColor();
-    const SkAlpha alpha =
-        static_cast<SkAlpha>(std::round(SkColorGetA(stroke_color) * 0.59375f));
     cc::PaintFlags shadow_flags = flags;
-    shadow_flags.setLooper(
-        CreateShadowDrawLooper(SkColorSetA(stroke_color, alpha)));
+    // For Refresh, don't draw a shadow.
+    if (!MD::IsRefreshUi()) {
+      const SkColor stroke_color = tab_strip_->GetToolbarTopSeparatorColor();
+      const SkAlpha alpha = static_cast<SkAlpha>(
+          std::round(SkColorGetA(stroke_color) * 0.59375f));
+      shadow_flags.setLooper(
+          CreateShadowDrawLooper(SkColorSetA(stroke_color, alpha)));
+    }
     canvas->DrawPath(fill, shadow_flags);
 
-    if (is_touch_ui) {
-      // We don't have hover/pressed states in the touch-optimized UI design.
+    if (is_newer_ui) {
+      // We don't have hover/pressed states in the newer UI design.
       // Instead we are using an ink drop effect.
       return;
     }
@@ -545,9 +552,8 @@
 }
 
 SkColor NewTabButton::GetIconColor() const {
-  return color_utils::IsDark(tab_strip_->GetTabForegroundColor(TAB_INACTIVE))
-             ? gfx::kChromeIconGrey
-             : SK_ColorWHITE;
+  return tab_strip_->GetTabForegroundColor(MD::IsRefreshUi() ? TAB_INACTIVE
+                                                             : TAB_ACTIVE);
 }
 
 void NewTabButton::InitIncognitoIcon() {
@@ -556,12 +562,11 @@
       gfx::CreateVectorIcon(kNewTabButtonIncognitoIcon, GetIconColor());
 }
 
-SkPath NewTabButton::GetTouchOptimizedButtonPath(float button_y,
-                                                 float scale,
-                                                 bool extend_to_top,
-                                                 bool for_fill) const {
-  DCHECK(MD::IsTouchOptimizedUiEnabled());
-  DCHECK(!MD::IsRefreshUi());
+SkPath NewTabButton::GetNewerMaterialUiButtonPath(float button_y,
+                                                  float scale,
+                                                  bool extend_to_top,
+                                                  bool for_fill) const {
+  DCHECK(MD::IsNewerMaterialUi());
 
   const float radius = GetCornerRadius() * scale;
   const float rect_width =
@@ -592,11 +597,11 @@
   return path;
 }
 
-SkPath NewTabButton::GetNonTouchOptimizedButtonPath(int button_y,
-                                                    int button_height,
-                                                    float scale,
-                                                    bool extend_to_top,
-                                                    bool for_fill) const {
+SkPath NewTabButton::GetMaterialUiButtonPath(int button_y,
+                                             int button_height,
+                                             float scale,
+                                             bool extend_to_top,
+                                             bool for_fill) const {
   const float inverse_slope = Tab::GetInverseDiagonalSlope();
   float bottom = (button_height - 2) * scale;
   const float diag_height = bottom - 3.5 * scale;
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h
index c35c84a..f214e723 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.h
+++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -116,22 +116,22 @@
   // In the touch-optimized UI, initializes the incognito button icon.
   void InitIncognitoIcon();
 
-  // Returns the path for the touch-optimized new tab button for the given
+  // Returns the path for the newer material ui new tab button for the given
   // |scale|. |button_y| is the button's top y-cordinate. If |for_fill| is true,
   // the path will be shrunk by 1px from all sides to allow room for the stroke
   // to show up. If |extend_to_top| is true, the path is extended vertically to
   // y = 0.
-  SkPath GetTouchOptimizedButtonPath(float button_y,
-                                     float scale,
-                                     bool extend_to_top,
-                                     bool for_fill) const;
+  SkPath GetNewerMaterialUiButtonPath(float button_y,
+                                      float scale,
+                                      bool extend_to_top,
+                                      bool for_fill) const;
 
-  // Similar, but for the non-touch-optimized button.
-  SkPath GetNonTouchOptimizedButtonPath(int button_y,
-                                        int button_height,
-                                        float scale,
-                                        bool extend_to_top,
-                                        bool for_fill) const;
+  // Similar, but for the non-new material ui button.
+  SkPath GetMaterialUiButtonPath(int button_y,
+                                 int button_height,
+                                 float scale,
+                                 bool extend_to_top,
+                                 bool for_fill) const;
 
   void UpdateInkDropBaseColor();
 
diff --git a/chrome/browser/ui/webui/browsing_history_handler_unittest.cc b/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
index 75d38c7..d789c14 100644
--- a/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler_unittest.cc
@@ -61,21 +61,23 @@
   explicit TestSyncService(Profile* profile)
       : browser_sync::TestProfileSyncService(
             CreateProfileSyncServiceParamsForTest(profile)),
-        sync_active_(true) {}
+        state_(State::ACTIVE) {}
 
-  bool IsSyncActive() const override { return sync_active_; }
+  State GetState() const override { return state_; }
 
   syncer::ModelTypeSet GetActiveDataTypes() const override {
     return syncer::ModelTypeSet::All();
   }
 
-  void SetSyncActive(bool active) {
-    sync_active_ = active;
+  void SetState(State state) {
+    state_ = state;
     NotifyObservers();
   }
 
  private:
-  bool sync_active_;
+  State state_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncService);
 };
 
 class BrowsingHistoryHandlerWithWebUIForTesting
@@ -91,6 +93,8 @@
 
  private:
   base::SimpleTestClock test_clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(BrowsingHistoryHandlerWithWebUIForTesting);
 };
 
 }  // namespace
@@ -165,7 +169,7 @@
   // BrowsingHistoryHandler is informed about WebHistoryService history
   // deletions.
   {
-    sync_service()->SetSyncActive(true);
+    sync_service()->SetState(syncer::SyncService::State::ACTIVE);
     BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
     handler.RegisterMessages();
 
@@ -180,10 +184,10 @@
   // BrowsingHistoryHandler will be informed about WebHistoryService deletions
   // even if history sync is activated later.
   {
-    sync_service()->SetSyncActive(false);
+    sync_service()->SetState(syncer::SyncService::State::INITIALIZING);
     BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
     handler.RegisterMessages();
-    sync_service()->SetSyncActive(true);
+    sync_service()->SetState(syncer::SyncService::State::ACTIVE);
 
     web_history_service()->ExpireHistoryBetween(
         std::set<GURL>(), base::Time(), base::Time::Max(), callback,
@@ -196,7 +200,7 @@
   // BrowsingHistoryHandler does not fire historyDeleted while a web history
   // delete request is happening.
   {
-    sync_service()->SetSyncActive(true);
+    sync_service()->SetState(syncer::SyncService::State::ACTIVE);
     BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
     handler.RegisterMessages();
 
@@ -218,7 +222,7 @@
   // deletions. The WebHistoryService object still exists (because it's a
   // BrowserContextKeyedService), but is not visible to BrowsingHistoryHandler.
   {
-    sync_service()->SetSyncActive(false);
+    sync_service()->SetState(syncer::SyncService::State::INITIALIZING);
     BrowsingHistoryHandlerWithWebUIForTesting handler(web_ui());
     handler.RegisterMessages();
 
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 5f5b794..bce29043 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -829,6 +829,7 @@
   TRACE_EVENT_ASYNC_STEP_INTO0("ui", "ShowLoginWebUI",
                                LoginDisplayHostWebUI::kShowLoginWebUIid,
                                "ShowAddUser");
+
   std::string email;
   // |args| can be null if it's OOBE.
   if (args)
@@ -836,7 +837,6 @@
   set_populated_email(email);
   if (!email.empty())
     SendReauthReason(AccountId::FromUserEmail(email));
-
   OnShowAddUser();
 }
 
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
index 3dc5d68..84306fd 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h
@@ -56,7 +56,6 @@
   void ShowSigninScreenForTest(const std::string& username,
                                const std::string& password,
                                const std::string& services) override;
-  void CancelShowGaiaAsync() override;
 
  private:
   // TODO (xiaoyinh): remove this dependency.
@@ -172,6 +171,10 @@
   // principals API was used during SAML login.
   void SetSAMLPrincipalsAPIUsed(bool api_used);
 
+  // Cancels the request to show the sign-in screen while the asynchronous
+  // clean-up process that precedes the screen showing is in progress.
+  void CancelShowGaiaAsync();
+
   // Shows signin screen after dns cache and cookie cleanup operations finish.
   void ShowGaiaScreenIfReady();
 
diff --git a/chrome/browser/ui/webui/conflicts_handler.cc b/chrome/browser/ui/webui/conflicts_handler.cc
index 0885281..4e278e7 100644
--- a/chrome/browser/ui/webui/conflicts_handler.cc
+++ b/chrome/browser/ui/webui/conflicts_handler.cc
@@ -168,6 +168,7 @@
   data->SetString("description", inspection_result.description);
   data->SetString("version", inspection_result.version);
   data->SetString("digital_signer", inspection_result.certificate_info.subject);
+  data->SetString("code_id", GenerateCodeId(module_key));
 
   module_list_->Append(std::move(data));
 }
diff --git a/chrome/browser/ui/webui/flash_ui.cc b/chrome/browser/ui/webui/flash_ui.cc
index c7aeadb..2fdf914 100644
--- a/chrome/browser/ui/webui/flash_ui.cc
+++ b/chrome/browser/ui/webui/flash_ui.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
@@ -59,6 +60,7 @@
 using content::PluginService;
 using content::WebContents;
 using content::WebUIMessageHandler;
+using webui::webui_util::AddPair;
 
 namespace {
 
diff --git a/chrome/browser/ui/webui/nacl_ui.cc b/chrome/browser/ui/webui/nacl_ui.cc
index 21e1d11..49b29dfe 100644
--- a/chrome/browser/ui/webui/nacl_ui.cc
+++ b/chrome/browser/ui/webui/nacl_ui.cc
@@ -28,6 +28,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/plugins/plugin_prefs.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
@@ -53,6 +54,7 @@
 using content::BrowserThread;
 using content::PluginService;
 using content::WebUIMessageHandler;
+using webui::webui_util::AddPair;
 
 namespace {
 
@@ -156,17 +158,6 @@
                           base::Unretained(this)));
 }
 
-// Helper functions for collecting a list of key-value pairs that will
-// be displayed.
-void AddPair(base::ListValue* list,
-             const base::string16& key,
-             const base::string16& value) {
-  std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
-  results->SetString("key", key);
-  results->SetString("value", value);
-  list->Append(std::move(results));
-}
-
 // Generate an empty data-pair which acts as a line break.
 void AddLineBreak(base::ListValue* list) {
   AddPair(list, ASCIIToUTF16(""), ASCIIToUTF16(""));
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index d287399..0d21d27 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -31,6 +31,7 @@
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/content_settings/core/common/features.h"
 #include "components/google/core/browser/google_util.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/safe_browsing/common/safe_browsing_prefs.h"
@@ -1668,6 +1669,8 @@
     {"openTabsCheckboxLabel", IDS_SETTINGS_OPEN_TABS_CHECKBOX_LABEL},
     {"userEventsCheckboxLabel", IDS_SETTINGS_USER_EVENTS_CHECKBOX_LABEL},
     {"userEventsCheckboxText", IDS_SETTINGS_USER_EVENTS_CHECKBOX_TEXT},
+    {"driveSuggestPref", IDS_DRIVE_SUGGEST_PREF},
+    {"driveSuggestPrefDesc", IDS_DRIVE_SUGGEST_PREF_DESC},
     {"manageSyncedDataTitle", IDS_SETTINGS_MANAGE_SYNCED_DATA_TITLE},
     {"encryptionOptionsTitle", IDS_SETTINGS_ENCRYPTION_OPTIONS},
     {"syncDataEncryptedText", IDS_SETTINGS_SYNC_DATA_ENCRYPTED_TEXT},
@@ -1801,6 +1804,10 @@
   html_source->AddBoolean(
       "changePictureVideoModeEnabled",
       base::FeatureList::IsEnabled(features::kChangePictureVideoMode));
+
+  html_source->AddBoolean(
+      "driveSuggestAvailable",
+      base::FeatureList::IsEnabled(omnibox::kDocumentProvider));
 }
 
 void AddPrintingStrings(content::WebUIDataSource* html_source) {
diff --git a/chrome/browser/ui/webui/webui_util.cc b/chrome/browser/ui/webui/webui_util.cc
new file mode 100644
index 0000000..654538d
--- /dev/null
+++ b/chrome/browser/ui/webui/webui_util.cc
@@ -0,0 +1,29 @@
+// 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/ui/webui/webui_util.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+
+namespace webui {
+namespace webui_util {
+
+void AddPair(base::ListValue* list,
+             const base::string16& key,
+             const base::string16& value) {
+  std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
+  results->SetString("key", key);
+  results->SetString("value", value);
+  list->Append(std::move(results));
+}
+
+void AddPair(base::ListValue* list,
+             const base::string16& key,
+             const std::string& value) {
+  AddPair(list, key, base::UTF8ToUTF16(value));
+}
+
+}  // namespace webui_util
+}  // namespace webui
diff --git a/chrome/browser/ui/webui/webui_util.h b/chrome/browser/ui/webui/webui_util.h
new file mode 100644
index 0000000..b9d42796
--- /dev/null
+++ b/chrome/browser/ui/webui/webui_util.h
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_WEBUI_UTIL_H_
+#define CHROME_BROWSER_UI_WEBUI_WEBUI_UTIL_H_
+
+#include "base/values.h"
+
+namespace webui {
+namespace webui_util {
+
+// Helper functions for collecting a list of key-value pairs that will
+// be displayed.
+void AddPair(base::ListValue* list,
+             const base::string16& key,
+             const base::string16& value);
+
+void AddPair(base::ListValue* list,
+             const base::string16& key,
+             const std::string& value);
+
+}  // namespace webui_util
+}  // namespace webui
+
+#endif  // CHROME_BROWSER_UI_WEBUI_WEBUI_UTIL_H_
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 87782ef..ac40b82 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -230,6 +230,7 @@
     "ui_initial_state.h",
     "ui_input_manager.cc",
     "ui_input_manager.h",
+    "ui_interface.h",
     "ui_renderer.cc",
     "ui_renderer.h",
     "ui_scene.cc",
diff --git a/chrome/browser/vr/elements/content_element_unittest.cc b/chrome/browser/vr/elements/content_element_unittest.cc
index 9498dcb..ad6ea48f 100644
--- a/chrome/browser/vr/elements/content_element_unittest.cc
+++ b/chrome/browser/vr/elements/content_element_unittest.cc
@@ -96,8 +96,8 @@
         std::make_unique<StrictMock<MockTextInputDelegate>>();
 
     input_forwarder_ = std::make_unique<TestPlatformInputHandler>();
-    ui_->GetContentInputDelegateForTest()->SetPlatformInputHandlerForTest(
-        input_forwarder_.get());
+    ui_instance_->GetContentInputDelegateForTest()
+        ->SetPlatformInputHandlerForTest(input_forwarder_.get());
 
     auto* content =
         static_cast<ContentElement*>(scene_->GetUiElementByName(kContentQuad));
@@ -204,7 +204,7 @@
     text_input_delegate_ =
         std::make_unique<StrictMock<MockTextInputDelegate>>();
     input_forwarder_ = std::make_unique<TestPlatformInputHandler>();
-    content_delegate_ = ui_->GetContentInputDelegateForTest();
+    content_delegate_ = ui_instance_->GetContentInputDelegateForTest();
     content_delegate_->SetPlatformInputHandlerForTest(input_forwarder_.get());
 
     content_ =
diff --git a/chrome/browser/vr/test/ui_pixel_test.cc b/chrome/browser/vr/test/ui_pixel_test.cc
index 3a395ab..46ac9d52 100644
--- a/chrome/browser/vr/test/ui_pixel_test.cc
+++ b/chrome/browser/vr/test/ui_pixel_test.cc
@@ -5,14 +5,9 @@
 #include "chrome/browser/vr/test/ui_pixel_test.h"
 
 #include "build/build_config.h"
-#include "chrome/browser/vr/browser_ui_interface.h"
 #include "chrome/browser/vr/model/model.h"
 #include "chrome/browser/vr/test/animation_utils.h"
 #include "chrome/browser/vr/test/constants.h"
-#include "chrome/browser/vr/ui_browser_interface.h"
-#include "chrome/browser/vr/ui_input_manager.h"
-#include "chrome/browser/vr/ui_renderer.h"
-#include "chrome/browser/vr/ui_scene.h"
 #include "third_party/skia/include/core/SkImageEncoder.h"
 #include "third_party/skia/include/core/SkStream.h"
 #include "ui/gl/gl_bindings.h"
@@ -92,12 +87,11 @@
 
   InputEventList input_event_list;
   ReticleModel reticle_model;
-  EXPECT_TRUE(
-      ui_->scene()->OnBeginFrame(base::TimeTicks(), render_info.head_pose));
-  ui_->input_manager()->HandleInput(MsToTicks(1), render_info, controller_model,
-                                    &reticle_model, &input_event_list);
+  EXPECT_TRUE(ui_->OnBeginFrame(base::TimeTicks(), render_info.head_pose));
+  ui_->HandleInput(MsToTicks(1), render_info, controller_model, &reticle_model,
+                   &input_event_list);
   ui_->OnControllerUpdated(controller_model, reticle_model);
-  ui_->ui_renderer()->Draw(render_info);
+  ui_->Draw(render_info);
 
   // We produce GL errors while rendering. Clear them all so that we can check
   // for errors of subsequent calls.
diff --git a/chrome/browser/vr/test/ui_pixel_test.h b/chrome/browser/vr/test/ui_pixel_test.h
index 70919c1..55530d32 100644
--- a/chrome/browser/vr/test/ui_pixel_test.h
+++ b/chrome/browser/vr/test/ui_pixel_test.h
@@ -45,7 +45,7 @@
   GLuint content_texture_ = 0;
   GLuint content_overlay_texture_ = 0;
   gfx::Size frame_buffer_size_;
-  std::unique_ptr<Ui> ui_;
+  std::unique_ptr<UiInterface> ui_;
 };
 
 }  // namespace vr
diff --git a/chrome/browser/vr/test/ui_test.cc b/chrome/browser/vr/test/ui_test.cc
index 98cad8e..0b8edcf1 100644
--- a/chrome/browser/vr/test/ui_test.cc
+++ b/chrome/browser/vr/test/ui_test.cc
@@ -80,11 +80,12 @@
     const UiInitialState& state,
     std::unique_ptr<MockContentInputDelegate> content_input_delegate) {
   content_input_delegate_ = content_input_delegate.get();
-  ui_ = std::make_unique<Ui>(std::move(browser_.get()),
-                             std::move(content_input_delegate), nullptr,
-                             nullptr, nullptr, state);
-  scene_ = ui_->scene();
-  model_ = ui_->model_for_test();
+  ui_instance_ = std::make_unique<Ui>(std::move(browser_.get()),
+                                      std::move(content_input_delegate),
+                                      nullptr, nullptr, nullptr, state);
+  ui_ = ui_instance_.get();
+  scene_ = ui_instance_->scene();
+  model_ = ui_instance_->model_for_test();
   model_->controller.transform.Translate3d(kStartControllerPosition);
 
   OnBeginFrame();
@@ -251,15 +252,15 @@
   controller_model.laser_origin = origin;
 
   controller_model.touchpad_button_state = UiInputManager::ButtonState::DOWN;
-  ui_->input_manager()->HandleInput(current_time_, render_info,
-                                    controller_model, &reticle_model,
-                                    &input_event_list);
+  ui_instance_->input_manager()->HandleInput(current_time_, render_info,
+                                             controller_model, &reticle_model,
+                                             &input_event_list);
   OnBeginFrame();
 
   controller_model.touchpad_button_state = UiInputManager::ButtonState::UP;
-  ui_->input_manager()->HandleInput(current_time_, render_info,
-                                    controller_model, &reticle_model,
-                                    &input_event_list);
+  ui_instance_->input_manager()->HandleInput(current_time_, render_info,
+                                             controller_model, &reticle_model,
+                                             &input_event_list);
   OnBeginFrame();
 }
 
diff --git a/chrome/browser/vr/test/ui_test.h b/chrome/browser/vr/test/ui_test.h
index 1c9864e..5a182ee 100644
--- a/chrome/browser/vr/test/ui_test.h
+++ b/chrome/browser/vr/test/ui_test.h
@@ -105,7 +105,8 @@
   // clicks on elements, that's true to hit testability, visbility, etc.
   void ClickElement(UiElement* element);
 
-  std::unique_ptr<Ui> ui_;
+  std::unique_ptr<Ui> ui_instance_;
+  UiInterface* ui_ = nullptr;
   std::unique_ptr<MockUiBrowserInterface> browser_;
   MockContentInputDelegate* content_input_delegate_ = nullptr;
   Model* model_ = nullptr;
diff --git a/chrome/browser/vr/testapp/vr_test_context.cc b/chrome/browser/vr/testapp/vr_test_context.cc
index 3a13d89..f768360e 100644
--- a/chrome/browser/vr/testapp/vr_test_context.cc
+++ b/chrome/browser/vr/testapp/vr_test_context.cc
@@ -110,23 +110,25 @@
 
   UiInitialState ui_initial_state;
   ui_initial_state.create_tabs_view = true;
-  ui_ = std::make_unique<Ui>(this, nullptr, keyboard_delegate_.get(),
-                             text_input_delegate_.get(), nullptr,
-                             ui_initial_state);
+  ui_instance_ = std::make_unique<Ui>(this, nullptr, keyboard_delegate_.get(),
+                                      text_input_delegate_.get(), nullptr,
+                                      ui_initial_state);
+  ui_ = ui_instance_.get();
+
   LoadAssets();
 
-  text_input_delegate_->SetRequestFocusCallback(
-      base::BindRepeating(&vr::Ui::RequestFocus, base::Unretained(ui_.get())));
+  text_input_delegate_->SetRequestFocusCallback(base::BindRepeating(
+      &vr::UiInterface::RequestFocus, base::Unretained(ui_)));
   text_input_delegate_->SetRequestUnfocusCallback(base::BindRepeating(
-      &vr::Ui::RequestUnfocus, base::Unretained(ui_.get())));
+      &vr::UiInterface::RequestUnfocus, base::Unretained(ui_)));
   text_input_delegate_->SetUpdateInputCallback(
       base::BindRepeating(&TestKeyboardDelegate::UpdateInput,
                           base::Unretained(keyboard_delegate_.get())));
-  keyboard_delegate_->SetUiInterface(ui_.get());
+  keyboard_delegate_->SetUiInterface(ui_instance_.get());
 
   touchpad_touch_position_ = kInitialTouchPosition;
 
-  model_ = ui_->model_for_test();
+  model_ = ui_instance_->model_for_test();
 
   CycleOrigin();
   ui_->SetHistoryButtonsEnabled(true, true);
@@ -138,7 +140,7 @@
   capturing_state.bluetooth_connected = true;
   capturing_state.location_access_enabled = true;
   ui_->SetCapturingState(capturing_state);
-  ui_->input_manager()->set_hit_test_strategy(
+  ui_instance_->input_manager()->set_hit_test_strategy(
       UiInputManager::PROJECT_TO_LASER_ORIGIN_FOR_TEST);
   for (size_t i = 0; i < 5; i++) {
     ui_->AddOrUpdateTab(tab_id_++, false,
@@ -159,13 +161,13 @@
   RenderInfo render_info = GetRenderInfo();
 
   // Update the render position of all UI elements (including desktop).
-  ui_->scene()->OnBeginFrame(current_time, head_pose_);
+  ui_->OnBeginFrame(current_time, head_pose_);
   ui_->OnProjMatrixChanged(render_info.left_eye_model.proj_matrix);
 
   UpdateController(render_info, current_time);
 
   graphics_delegate_->MakeSkiaContextCurrent();
-  ui_->scene()->UpdateTextures();
+  ui_->UpdateSceneTextures();
   graphics_delegate_->MakeMainContextCurrent();
 
   auto load_progress = (current_time - page_load_start_).InMilliseconds() /
@@ -174,9 +176,9 @@
   ui_->SetLoadProgress(std::min(load_progress, 1.0f));
 
   if (web_vr_mode_ && ui_->ShouldRenderWebVr() && webvr_frames_received_) {
-    ui_->ui_renderer()->DrawWebVrOverlayForeground(render_info);
+    ui_->DrawWebVrOverlayForeground(render_info);
   } else {
-    ui_->ui_renderer()->Draw(render_info);
+    ui_->Draw(render_info);
   }
 }
 
@@ -222,10 +224,10 @@
         CycleIndicators();
         break;
       case ui::DomCode::US_D:
-        ui_->Dump(false);
+        ui_instance_->Dump(false);
         break;
       case ui::DomCode::US_B:
-        ui_->Dump(true);
+        ui_instance_->Dump(true);
         break;
       case ui::DomCode::US_V:
         CreateFakeVoiceSearchResult();
@@ -419,9 +421,9 @@
     input_event_lists_.push(InputEventList());
   }
   ReticleModel reticle_model;
-  ui_->input_manager()->HandleInput(current_time, render_info, controller_model,
-                                    &reticle_model,
-                                    &input_event_lists_.front());
+  ui_instance_->input_manager()->HandleInput(current_time, render_info,
+                                             controller_model, &reticle_model,
+                                             &input_event_lists_.front());
   input_event_lists_.pop();
 
   // Now that we have accurate hit information, we use this to construct a
@@ -456,8 +458,9 @@
                        content_texture_id,
                        UiElementRenderer::kTextureLocationLocal, ui_texture_id);
 
-  keyboard_delegate_->Initialize(ui_->scene()->SurfaceProviderForTesting(),
-                                 ui_->ui_element_renderer());
+  keyboard_delegate_->Initialize(
+      ui_instance_->scene()->SurfaceProviderForTesting(),
+      ui_instance_->ui_element_renderer());
 }
 
 unsigned int VrTestContext::CreateTexture(SkColor color) {
@@ -523,9 +526,9 @@
     UiInitialState state;
     state.in_web_vr = true;
     state.web_vr_autopresentation_expected = true;
-    ui_->ReinitializeForTest(state);
+    ui_instance_->ReinitializeForTest(state);
   } else {
-    ui_->ReinitializeForTest(UiInitialState());
+    ui_instance_->ReinitializeForTest(UiInitialState());
   }
   show_web_vr_splash_screen_ = !show_web_vr_splash_screen_;
 }
diff --git a/chrome/browser/vr/testapp/vr_test_context.h b/chrome/browser/vr/testapp/vr_test_context.h
index ca17860..1f4a42a 100644
--- a/chrome/browser/vr/testapp/vr_test_context.h
+++ b/chrome/browser/vr/testapp/vr_test_context.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/vr/content_input_delegate.h"
 #include "chrome/browser/vr/model/controller_model.h"
 #include "chrome/browser/vr/ui_browser_interface.h"
+#include "chrome/browser/vr/ui_interface.h"
 #include "chrome/browser/vr/ui_renderer.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/transform.h"
@@ -89,7 +90,8 @@
   gfx::Point3F LaserOrigin() const;
   void LoadAssets();
 
-  std::unique_ptr<Ui> ui_;
+  std::unique_ptr<Ui> ui_instance_;
+  UiInterface* ui_;
   gfx::Size window_size_;
 
   gfx::Transform head_pose_;
diff --git a/chrome/browser/vr/ui.cc b/chrome/browser/vr/ui.cc
index eb2485d..f9211b8 100644
--- a/chrome/browser/vr/ui.cc
+++ b/chrome/browser/vr/ui.cc
@@ -188,10 +188,6 @@
   model_->pop_mode(kModeEditingOmnibox);
 }
 
-void Ui::SetFloorHeight(float floor_height) {
-  model_->floor_height = floor_height;
-}
-
 void Ui::SetSpeechRecognitionEnabled(bool enabled) {
   if (enabled) {
     model_->speech.recognition_result.clear();
@@ -652,6 +648,14 @@
   ui_renderer_->Draw(info);
 }
 
+void Ui::DrawWebVr(int texture_data_handle,
+                   const float (&uv_transform)[16],
+                   float xborder,
+                   float yborder) {
+  ui_element_renderer_->DrawWebVr(texture_data_handle, uv_transform, xborder,
+                                  yborder);
+}
+
 void Ui::DrawWebVrOverlayForeground(const vr::RenderInfo& info) {
   ui_renderer_->DrawWebVrOverlayForeground(info);
 }
diff --git a/chrome/browser/vr/ui.h b/chrome/browser/vr/ui.h
index 956a6b04..c83a19e1 100644
--- a/chrome/browser/vr/ui.h
+++ b/chrome/browser/vr/ui.h
@@ -18,10 +18,11 @@
 #include "chrome/browser/vr/platform_controller.h"
 #include "chrome/browser/vr/ui_element_renderer.h"
 #include "chrome/browser/vr/ui_initial_state.h"
+#include "chrome/browser/vr/ui_interface.h"
 #include "chrome/browser/vr/ui_renderer.h"
 #include "chrome/browser/vr/ui_scene.h"
 #include "chrome/browser/vr/ui_test_input.h"
-#include "chrome/browser/vr/vr_export.h"
+#include "chrome/browser/vr/vr_ui_export.h"
 
 namespace vr {
 
@@ -45,7 +46,7 @@
 
 // This class manages all GLThread owned objects and GL rendering for VrShell.
 // It is not threadsafe and must only be used on the GL thread.
-class VR_EXPORT Ui : public BrowserUiInterface, public KeyboardUiInterface {
+class VR_EXPORT Ui : public UiInterface, public KeyboardUiInterface {
  public:
   Ui(UiBrowserInterface* browser,
      PlatformInputHandler* content_input_forwarder,
@@ -63,6 +64,14 @@
 
   ~Ui() override;
 
+  void OnUiRequestedNavigation();
+
+  void ReinitializeForTest(const UiInitialState& ui_initial_state);
+  ContentInputDelegate* GetContentInputDelegateForTest() {
+    return content_input_delegate_.get();
+  }
+
+  void Dump(bool include_bindings);
   // TODO(crbug.com/767957): Refactor to hide these behind the UI interface.
   UiScene* scene() { return scene_.get(); }
   UiElementRenderer* ui_element_renderer() {
@@ -70,9 +79,9 @@
   }
   UiRenderer* ui_renderer() { return ui_renderer_.get(); }
   UiInputManager* input_manager() { return input_manager_.get(); }
+  Model* model_for_test() { return model_.get(); }
 
-  base::WeakPtr<BrowserUiInterface> GetBrowserUiWeakPtr();
-
+ private:
   // BrowserUiInterface
   void SetWebVrMode(bool enabled) override;
   void SetFullscreen(bool enabled) override;
@@ -106,110 +115,89 @@
   void RemoveTab(int id, bool incognito) override;
   void RemoveAllTabs() override;
 
-  bool CanSendWebVrVSync();
-
+  // UiInterface
+  base::WeakPtr<BrowserUiInterface> GetBrowserUiWeakPtr() override;
+  bool CanSendWebVrVSync() override;
   void SetAlertDialogEnabled(bool enabled,
                              PlatformUiInputDelegate* delegate,
                              float width,
-                             float height);
+                             float height) override;
   void SetContentOverlayAlertDialogEnabled(bool enabled,
                                            PlatformUiInputDelegate* delegate,
                                            float width_percentage,
-                                           float height_percentage);
-  void SetAlertDialogSize(float width, float height);
+                                           float height_percentage) override;
+  void SetAlertDialogSize(float width, float height) override;
   void SetContentOverlayAlertDialogSize(float width_percentage,
-                                        float height_percentage);
-  void SetDialogLocation(float x, float y);
-  void SetDialogFloating(bool floating);
-  void ShowPlatformToast(const base::string16& text);
-  void CancelPlatformToast();
-  bool ShouldRenderWebVr();
-
+                                        float height_percentage) override;
+  void SetDialogLocation(float x, float y) override;
+  void SetDialogFloating(bool floating) override;
+  void ShowPlatformToast(const base::string16& text) override;
+  void CancelPlatformToast() override;
+  bool ShouldRenderWebVr() override;
   void OnGlInitialized(
       unsigned int content_texture_id,
       UiElementRenderer::TextureLocation content_location,
       unsigned int content_overlay_texture_id,
       UiElementRenderer::TextureLocation content_overlay_location,
-      unsigned int ui_texture_id);
+      unsigned int ui_texture_id) override;
 
-  void OnPause();
-  void OnAppButtonClicked();
-  void OnAppButtonSwipePerformed(PlatformController::SwipeDirection direction);
+  void OnPause() override;
+  void OnAppButtonClicked() override;
+  void OnAppButtonSwipePerformed(
+      PlatformController::SwipeDirection direction) override;
   void OnControllerUpdated(const ControllerModel& controller_model,
-                           const ReticleModel& reticle_model);
-  void OnProjMatrixChanged(const gfx::Transform& proj_matrix);
-  void OnWebVrFrameAvailable();
-  void OnWebVrTimedOut();
-  void OnWebVrTimeoutImminent();
-  bool IsControllerVisible() const;
-  bool IsAppButtonLongPressed() const;
-  bool SkipsRedrawWhenNotDirty() const;
-  void OnSwapContents(int new_content_id);
-  void OnContentBoundsChanged(int width, int height);
-  void OnUiRequestedNavigation();
-  void SetFloorHeight(float floor_height);
+                           const ReticleModel& reticle_model) override;
+  void OnProjMatrixChanged(const gfx::Transform& proj_matrix) override;
+  void OnWebVrFrameAvailable() override;
+  void OnWebVrTimedOut() override;
+  void OnWebVrTimeoutImminent() override;
+  bool IsControllerVisible() const override;
+  bool IsAppButtonLongPressed() const override;
+  bool SkipsRedrawWhenNotDirty() const override;
+  void OnSwapContents(int new_content_id) override;
+  void OnContentBoundsChanged(int width, int height) override;
 
-  Model* model_for_test() { return model_.get(); }
-
-  void ReinitializeForTest(const UiInitialState& ui_initial_state);
-  ContentInputDelegate* GetContentInputDelegateForTest() {
-    return content_input_delegate_.get();
-  }
-
-  void Dump(bool include_bindings);
-
-  // Keyboard input related.
-  void RequestFocus(int element_id);
-  void RequestUnfocus(int element_id);
-  void OnInputEdited(const EditedText& info) override;
-  void OnInputCommitted(const EditedText& info) override;
-  void OnKeyboardHidden() override;
-
-  void AcceptDoffPromptForTesting();
+  void AcceptDoffPromptForTesting() override;
   void PerformControllerActionForTesting(
       ControllerTestInput controller_input,
-      std::queue<ControllerModel>& controller_model_queue);
+      std::queue<ControllerModel>& controller_model_queue) override;
 
-  bool IsContentVisibleAndOpaque();
-  bool IsContentOverlayTextureEmpty();
-  void SetContentUsesQuadLayer(bool uses_quad_buffers);
-  gfx::Transform GetContentWorldSpaceTransform();
+  bool IsContentVisibleAndOpaque() override;
+  bool IsContentOverlayTextureEmpty() override;
+  void SetContentUsesQuadLayer(bool uses_quad_buffers) override;
+  gfx::Transform GetContentWorldSpaceTransform() override;
 
-  bool OnBeginFrame(const base::TimeTicks&, const gfx::Transform&);
-  bool SceneHasDirtyTextures() const;
-  void UpdateSceneTextures();
-  void Draw(const RenderInfo& render_info);
-  void DrawWebVrOverlayForeground(const RenderInfo& render_info);
-  UiScene::Elements GetWebVrOverlayElementsToDraw();
+  bool OnBeginFrame(const base::TimeTicks&, const gfx::Transform&) override;
+  bool SceneHasDirtyTextures() const override;
+  void UpdateSceneTextures() override;
+  void Draw(const RenderInfo& render_info) override;
+  void DrawWebVr(int texture_data_handle,
+                 const float (&uv_transform)[16],
+                 float xborder,
+                 float yborder) override;
+  void DrawWebVrOverlayForeground(const RenderInfo& render_info) override;
+  UiScene::Elements GetWebVrOverlayElementsToDraw() override;
   gfx::Rect CalculatePixelSpaceRect(const gfx::Size& texture_size,
-                                    const gfx::RectF& texture_rect);
+                                    const gfx::RectF& texture_rect) override;
+
   void HandleInput(base::TimeTicks current_time,
                    const RenderInfo& render_info,
                    const ControllerModel& controller_model,
                    ReticleModel* reticle_model,
-                   InputEventList* input_event_list);
+                   InputEventList* input_event_list) override;
 
-  // This function calculates the minimal FOV (in degrees) which covers all
-  // visible |elements| as if it was viewing from fov_recommended. For example,
-  // if fov_recommended is {20.f, 20.f, 20.f, 20.f}. And all elements appear on
-  // screen within a FOV of {-11.f, 19.f, 9.f, 9.f} if we use fov_recommended.
-  // Ideally, the calculated minimal FOV should be the same. In practice, the
-  // elements might get clipped near the edge sometimes due to float precison.
-  // To fix this, we add a small margin (1 degree) to all directions. So the
-  // |out_fov| set by this function should be {-10.f, 20.f, 10.f, 10.f} in the
-  // example case.
-  // Using a smaller FOV could improve the performance a lot while we are
-  // showing UIs on top of WebVR content.
-  struct FovRectangle {
-    float left;
-    float right;
-    float bottom;
-    float top;
-  };
   FovRectangle GetMinimalFov(const gfx::Transform& view_matrix,
                              const std::vector<const UiElement*>& elements,
                              const FovRectangle& fov_recommended,
-                             float z_near);
+                             float z_near) override;
+
+  void RequestFocus(int element_id) override;
+  void RequestUnfocus(int element_id) override;
+
+  // KeyboardUiInterface
+  void OnInputEdited(const EditedText& info) override;
+  void OnInputCommitted(const EditedText& info) override;
+  void OnKeyboardHidden() override;
 
  private:
   void OnSpeechRecognitionEnded();
diff --git a/chrome/browser/vr/ui_input_manager_unittest.cc b/chrome/browser/vr/ui_input_manager_unittest.cc
index 0378fe3..3600e7f5e 100644
--- a/chrome/browser/vr/ui_input_manager_unittest.cc
+++ b/chrome/browser/vr/ui_input_manager_unittest.cc
@@ -150,7 +150,7 @@
   void SetUp() override {
     UiTest::SetUp();
     CreateScene(kNotInWebVr);
-    input_manager_ = ui_->input_manager();
+    input_manager_ = ui_instance_->input_manager();
   }
 
  protected:
diff --git a/chrome/browser/vr/ui_interface.h b/chrome/browser/vr/ui_interface.h
new file mode 100644
index 0000000..b33ec65c
--- /dev/null
+++ b/chrome/browser/vr/ui_interface.h
@@ -0,0 +1,134 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_VR_UI_INTERFACE_H_
+#define CHROME_BROWSER_VR_UI_INTERFACE_H_
+
+#include <memory>
+#include <queue>
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/vr/browser_ui_interface.h"
+#include "chrome/browser/vr/platform_controller.h"
+#include "chrome/browser/vr/ui_element_renderer.h"
+#include "chrome/browser/vr/ui_input_manager.h"
+#include "chrome/browser/vr/ui_scene.h"
+#include "chrome/browser/vr/ui_test_input.h"
+
+namespace vr {
+
+class BrowserUiInterface;
+class PlatformUiInputDelegate;
+struct ControllerModel;
+struct RenderInfo;
+struct ReticleModel;
+
+// This interface represents the methods that should be called by its owner, and
+// also serves to make all such methods virtual for the sake of separating a UI
+// feature module.
+class UiInterface : public BrowserUiInterface {
+ public:
+  ~UiInterface() override {}
+
+  virtual base::WeakPtr<BrowserUiInterface> GetBrowserUiWeakPtr() = 0;
+
+  // TODO(ymalik): We expose this to stop sending VSync to the WebVR page until
+  // the splash screen has been visible for its minimum duration. The visibility
+  // logic currently lives in the UI, and it'd be much cleaner if the UI didn't
+  // have to worry about this, and if it were told to hide the splash screen
+  // like other WebVR phases (e.g. OnWebVrFrameAvailable below).
+  virtual bool CanSendWebVrVSync() = 0;
+  virtual void SetAlertDialogEnabled(bool enabled,
+                                     PlatformUiInputDelegate* delegate,
+                                     float width,
+                                     float height) = 0;
+  virtual void SetContentOverlayAlertDialogEnabled(
+      bool enabled,
+      PlatformUiInputDelegate* delegate,
+      float width_percentage,
+      float height_percentage) = 0;
+  virtual void SetAlertDialogSize(float width, float height) = 0;
+  virtual void SetContentOverlayAlertDialogSize(float width_percentage,
+                                                float height_percentage) = 0;
+  virtual void SetDialogLocation(float x, float y) = 0;
+  virtual void SetDialogFloating(bool floating) = 0;
+  virtual void ShowPlatformToast(const base::string16& text) = 0;
+  virtual void CancelPlatformToast() = 0;
+  virtual bool ShouldRenderWebVr() = 0;
+  virtual void OnGlInitialized(
+      unsigned int content_texture_id,
+      UiElementRenderer::TextureLocation content_location,
+      unsigned int content_overlay_texture_id,
+      UiElementRenderer::TextureLocation content_overlay_location,
+      unsigned int ui_texture_id) = 0;
+  virtual void OnPause() = 0;
+  virtual void OnAppButtonClicked() = 0;
+  virtual void OnAppButtonSwipePerformed(
+      PlatformController::SwipeDirection direction) = 0;
+  virtual void OnControllerUpdated(const ControllerModel& controller_model,
+                                   const ReticleModel& reticle_model) = 0;
+  virtual void OnProjMatrixChanged(const gfx::Transform& proj_matrix) = 0;
+  virtual void OnWebVrFrameAvailable() = 0;
+  virtual void OnWebVrTimedOut() = 0;
+  virtual void OnWebVrTimeoutImminent() = 0;
+  virtual bool IsControllerVisible() const = 0;
+  virtual bool IsAppButtonLongPressed() const = 0;
+  virtual bool SkipsRedrawWhenNotDirty() const = 0;
+  virtual void OnSwapContents(int new_content_id) = 0;
+  virtual void OnContentBoundsChanged(int width, int height) = 0;
+  virtual void AcceptDoffPromptForTesting() = 0;
+  virtual void PerformControllerActionForTesting(
+      ControllerTestInput controller_input,
+      std::queue<ControllerModel>& controller_model_queue) = 0;
+  virtual bool IsContentVisibleAndOpaque() = 0;
+  virtual bool IsContentOverlayTextureEmpty() = 0;
+  virtual void SetContentUsesQuadLayer(bool uses_quad_buffers) = 0;
+  virtual gfx::Transform GetContentWorldSpaceTransform() = 0;
+  virtual bool OnBeginFrame(const base::TimeTicks&, const gfx::Transform&) = 0;
+  virtual bool SceneHasDirtyTextures() const = 0;
+  virtual void UpdateSceneTextures() = 0;
+  virtual void Draw(const RenderInfo&) = 0;
+  virtual void DrawWebVr(int texture_data_handle,
+                         const float (&uv_transform)[16],
+                         float xborder,
+                         float yborder) = 0;
+  virtual void DrawWebVrOverlayForeground(const RenderInfo&) = 0;
+  virtual UiScene::Elements GetWebVrOverlayElementsToDraw() = 0;
+  virtual gfx::Rect CalculatePixelSpaceRect(const gfx::Size&,
+                                            const gfx::RectF&) = 0;
+  virtual void HandleInput(base::TimeTicks current_time,
+                           const RenderInfo& render_info,
+                           const ControllerModel& controller_model,
+                           ReticleModel* reticle_model,
+                           InputEventList* input_event_list) = 0;
+  virtual void RequestFocus(int element_id) = 0;
+  virtual void RequestUnfocus(int element_id) = 0;
+
+  // This function calculates the minimal FOV (in degrees) which covers all
+  // visible |elements| as if it was viewing from fov_recommended. For example,
+  // if fov_recommended is {20.f, 20.f, 20.f, 20.f}. And all elements appear on
+  // screen within a FOV of {-11.f, 19.f, 9.f, 9.f} if we use fov_recommended.
+  // Ideally, the calculated minimal FOV should be the same. In practice, the
+  // elements might get clipped near the edge sometimes due to float precison.
+  // To fix this, we add a small margin (1 degree) to all directions. So the
+  // |out_fov| set by this function should be {-10.f, 20.f, 10.f, 10.f} in the
+  // example case.
+  // Using a smaller FOV could improve the performance a lot while we are
+  // showing UIs on top of WebVR content.
+  struct FovRectangle {
+    float left;
+    float right;
+    float bottom;
+    float top;
+  };
+  virtual FovRectangle GetMinimalFov(
+      const gfx::Transform& view_matrix,
+      const std::vector<const UiElement*>& elements,
+      const FovRectangle& fov_recommended,
+      float z_near) = 0;
+};
+
+}  // namespace vr
+
+#endif  // CHROME_BROWSER_VR_UI_INTERFACE_H_
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 1930917..b44066a 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -6,6 +6,12 @@
 
 source_set("web_applications") {
   sources = [
+    "web_app_provider.cc",
+    "web_app_provider.h",
+    "web_app_provider_factory.cc",
+    "web_app_provider_factory.h",
+
+    # TODO(loyso): move this code (and everything other than "KEEP") elsewhere.
     "web_app.cc",
     "web_app.h",
     "web_app_chromeos.cc",
@@ -26,11 +32,15 @@
   allow_circular_includes_from = [ "//chrome/browser/extensions" ]
 
   deps = [
+    # TODO(loyso): KEEP.
+    "//chrome/browser/web_applications/bookmark_apps",
+    "//chrome/common",
+    "//skia",
+
+    # TODO(loyso): move this code elsewhere.
     "//base",
     "//chrome/browser/extensions",
-    "//chrome/browser/web_applications/bookmark_apps",
     "//chrome/browser/web_applications/components",
-    "//chrome/common",
     "//components/keyed_service/content",
     "//components/prefs",
     "//extensions/browser",
diff --git a/chrome/browser/web_applications/bookmark_apps/BUILD.gn b/chrome/browser/web_applications/bookmark_apps/BUILD.gn
index a0d4c7fee..12dc550 100644
--- a/chrome/browser/web_applications/bookmark_apps/BUILD.gn
+++ b/chrome/browser/web_applications/bookmark_apps/BUILD.gn
@@ -7,17 +7,30 @@
 assert(enable_extensions)
 
 source_set("bookmark_apps") {
+  sources = [
+    "policy/web_app_policy_constants.cc",
+    "policy/web_app_policy_constants.h",
+    "policy/web_app_policy_manager.cc",
+    "policy/web_app_policy_manager.h",
+  ]
+
   deps = [
-    # TODO(ortuno): Move policy to bookmark_apps/policy
-    "//chrome/browser/web_applications/policy",
+    "//chrome/browser/extensions",
+    "//chrome/common",
+    "//skia",
   ]
 }
 
 source_set("unit_tests") {
   testonly = true
 
+  sources = [
+    "policy/web_app_policy_manager_unittest.cc",
+  ]
+
   deps = [
-    # TODO(ortuno): Move policy to bookmark_apps/policy
-    "//chrome/browser/web_applications/policy:unit_tests",
+    ":bookmark_apps",
+    "//skia",
+    "//testing/gmock",
   ]
 }
diff --git a/chrome/browser/web_applications/policy/OWNERS b/chrome/browser/web_applications/bookmark_apps/policy/OWNERS
similarity index 100%
rename from chrome/browser/web_applications/policy/OWNERS
rename to chrome/browser/web_applications/bookmark_apps/policy/OWNERS
diff --git a/chrome/browser/web_applications/policy/web_app_policy_constants.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.cc
similarity index 81%
rename from chrome/browser/web_applications/policy/web_app_policy_constants.cc
rename to chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.cc
index 6aed80c..2101371 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_constants.cc
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.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 "chrome/browser/web_applications/policy/web_app_policy_constants.h"
+#include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.h"
 
 namespace web_app {
 
diff --git a/chrome/browser/web_applications/policy/web_app_policy_constants.h b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.h
similarity index 62%
rename from chrome/browser/web_applications/policy/web_app_policy_constants.h
rename to chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.h
index dbf2a67..f7a32e7f 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_constants.h
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.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 CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_CONSTANTS_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_CONSTANTS_H_
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_POLICY_WEB_APP_POLICY_CONSTANTS_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_POLICY_WEB_APP_POLICY_CONSTANTS_H_
 
 namespace web_app {
 
@@ -15,4 +15,4 @@
 
 }  // namespace web_app
 
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_CONSTANTS_H_
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_POLICY_WEB_APP_POLICY_CONSTANTS_H_
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.cc
similarity index 92%
rename from chrome/browser/web_applications/policy/web_app_policy_manager.cc
rename to chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.cc
index 213e476..4d10d1f 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h"
 
 #include <utility>
 #include <vector>
 
 #include "base/values.h"
+#include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.h"
 #include "chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h"
-#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.h b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h
similarity index 86%
rename from chrome/browser/web_applications/policy/web_app_policy_manager.h
rename to chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h
index 5b97134..f3616305 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.h
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_MANAGER_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_MANAGER_H_
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_POLICY_WEB_APP_POLICY_MANAGER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_POLICY_WEB_APP_POLICY_MANAGER_H_
 
 #include <memory>
 #include <vector>
 
 #include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
 #include "url/gurl.h"
 
 class PrefService;
@@ -22,7 +21,7 @@
 // which apps need to be installed, uninstalled, and updated. It uses
 // WebAppPolicyManager::PendingAppManager to actually install, uninstall,
 // and update apps.
-class WebAppPolicyManager : public KeyedService {
+class WebAppPolicyManager {
  public:
   class PendingAppManager;
 
@@ -55,7 +54,7 @@
       PrefService* pref_service,
       std::unique_ptr<PendingAppManager> pending_app_manager);
 
-  ~WebAppPolicyManager() override;
+  ~WebAppPolicyManager();
 
   const PendingAppManager& pending_app_manager() {
     return *pending_app_manager_;
@@ -89,4 +88,4 @@
 
 }  // namespace web_app
 
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_MANAGER_H_
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_BOOKMARK_APPS_POLICY_WEB_APP_POLICY_MANAGER_H_
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
similarity index 95%
rename from chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc
rename to chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
index 74d19315..8e528d9 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager_unittest.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h"
 
 #include <utility>
 #include <vector>
 
 #include "base/values.h"
 #include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
+#include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_constants.h"
 #include "chrome/common/pref_names.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
index 02846a6c..3a2b789 100644
--- a/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
+++ b/chrome/browser/web_applications/extensions/pending_bookmark_app_manager.h
@@ -8,7 +8,7 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h"
 
 namespace extensions {
 
diff --git a/chrome/browser/web_applications/policy/BUILD.gn b/chrome/browser/web_applications/policy/BUILD.gn
deleted file mode 100644
index eedea42..0000000
--- a/chrome/browser/web_applications/policy/BUILD.gn
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//extensions/buildflags/buildflags.gni")
-
-assert(enable_extensions)
-
-source_set("policy") {
-  sources = [
-    "web_app_policy_constants.cc",
-    "web_app_policy_constants.h",
-    "web_app_policy_manager.cc",
-    "web_app_policy_manager.h",
-    "web_app_policy_manager_factory.cc",
-    "web_app_policy_manager_factory.h",
-  ]
-
-  deps = [
-    "//chrome/browser/extensions",
-    "//chrome/common",
-    "//components/keyed_service/content",
-    "//skia",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-
-  sources = [
-    "web_app_policy_manager_unittest.cc",
-  ]
-
-  deps = [
-    ":policy",
-    "//skia",
-    "//testing/gmock",
-  ]
-}
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager_factory.cc b/chrome/browser/web_applications/policy/web_app_policy_manager_factory.cc
deleted file mode 100644
index acd23f32..0000000
--- a/chrome/browser/web_applications/policy/web_app_policy_manager_factory.cc
+++ /dev/null
@@ -1,48 +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 "chrome/browser/web_applications/policy/web_app_policy_manager_factory.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "extensions/browser/extension_system_provider.h"
-#include "extensions/browser/extensions_browser_client.h"
-
-namespace web_app {
-
-// static
-WebAppPolicyManager* WebAppPolicyManagerFactory::GetForProfile(
-    Profile* profile) {
-  return static_cast<WebAppPolicyManager*>(
-      WebAppPolicyManagerFactory::GetInstance()->GetServiceForBrowserContext(
-          profile, true /* create */));
-}
-
-// static
-WebAppPolicyManagerFactory* WebAppPolicyManagerFactory::GetInstance() {
-  return base::Singleton<WebAppPolicyManagerFactory>::get();
-}
-
-WebAppPolicyManagerFactory::WebAppPolicyManagerFactory()
-    : BrowserContextKeyedServiceFactory(
-          "WebAppPolicyManager",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(
-      extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
-}
-
-WebAppPolicyManagerFactory::~WebAppPolicyManagerFactory() = default;
-
-KeyedService* WebAppPolicyManagerFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  Profile* profile = Profile::FromBrowserContext(context);
-  return new WebAppPolicyManager(profile->GetPrefs());
-}
-
-bool WebAppPolicyManagerFactory::ServiceIsCreatedWithBrowserContext() const {
-  return true;
-}
-
-}  //  namespace web_app
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager_factory.h b/chrome/browser/web_applications/policy/web_app_policy_manager_factory.h
deleted file mode 100644
index 3a69d9f..0000000
--- a/chrome/browser/web_applications/policy/web_app_policy_manager_factory.h
+++ /dev/null
@@ -1,46 +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 CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_MANAGER_FACTORY_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_MANAGER_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-namespace content {
-class BrowserContext;
-}
-
-class Profile;
-
-namespace web_app {
-
-class WebAppPolicyManager;
-
-// Singleton that owns all WebAppPolicyManagerFactories and associates them with
-// Profile.
-class WebAppPolicyManagerFactory : public BrowserContextKeyedServiceFactory {
- public:
-  static WebAppPolicyManager* GetForProfile(Profile* profile);
-
-  static WebAppPolicyManagerFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<WebAppPolicyManagerFactory>;
-
-  WebAppPolicyManagerFactory();
-  ~WebAppPolicyManagerFactory() override;
-
-  // BrowserContextKeyedServiceFactory
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(WebAppPolicyManagerFactory);
-};
-
-}  // namespace web_app
-
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_POLICY_WEB_APP_POLICY_MANAGER_FACTORY_H_
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
new file mode 100644
index 0000000..6ccec79
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/web_app_provider.h"
+
+#include "chrome/browser/web_applications/bookmark_apps/policy/web_app_policy_manager.h"
+#include "chrome/browser/web_applications/web_app_provider_factory.h"
+
+namespace web_app {
+
+// static
+WebAppProvider* WebAppProvider::Get(Profile* profile) {
+  return WebAppProviderFactory::GetForProfile(profile);
+}
+
+WebAppProvider::WebAppProvider(PrefService* pref_service)
+    : web_app_policy_manager_(
+          std::make_unique<WebAppPolicyManager>(pref_service)) {
+  // TODO(nigeltao): install default web apps as per http://crbug.com/855281
+}
+
+WebAppProvider::~WebAppProvider() = default;
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
new file mode 100644
index 0000000..f2bec23
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_PROVIDER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_PROVIDER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/prefs/pref_service.h"
+
+class Profile;
+
+namespace web_app {
+
+class WebAppPolicyManager;
+
+// Connects Web App features, such as the installation of default and
+// policy-managed web apps, with Profiles (as WebAppProvider is a
+// Profile-linked KeyedService) and their associated PrefService.
+class WebAppProvider : public KeyedService {
+ public:
+  static WebAppProvider* Get(Profile* profile);
+
+  explicit WebAppProvider(PrefService* pref_service);
+
+  ~WebAppProvider() override;
+
+ private:
+  std::unique_ptr<WebAppPolicyManager> web_app_policy_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAppProvider);
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_PROVIDER_H_
diff --git a/chrome/browser/web_applications/web_app_provider_factory.cc b/chrome/browser/web_applications/web_app_provider_factory.cc
new file mode 100644
index 0000000..bbf0533
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_provider_factory.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 "chrome/browser/web_applications/web_app_provider_factory.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "extensions/browser/extension_system_provider.h"
+#include "extensions/browser/extensions_browser_client.h"
+
+namespace web_app {
+
+// static
+WebAppProvider* WebAppProviderFactory::GetForProfile(Profile* profile) {
+  return static_cast<WebAppProvider*>(
+      WebAppProviderFactory::GetInstance()->GetServiceForBrowserContext(
+          profile, true /* create */));
+}
+
+// static
+WebAppProviderFactory* WebAppProviderFactory::GetInstance() {
+  return base::Singleton<WebAppProviderFactory>::get();
+}
+
+WebAppProviderFactory::WebAppProviderFactory()
+    : BrowserContextKeyedServiceFactory(
+          "WebAppProvider",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(
+      extensions::ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
+}
+
+WebAppProviderFactory::~WebAppProviderFactory() = default;
+
+KeyedService* WebAppProviderFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new WebAppProvider(profile->GetPrefs());
+}
+
+bool WebAppProviderFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+}  //  namespace web_app
diff --git a/chrome/browser/web_applications/web_app_provider_factory.h b/chrome/browser/web_applications/web_app_provider_factory.h
new file mode 100644
index 0000000..d2de433
--- /dev/null
+++ b/chrome/browser/web_applications/web_app_provider_factory.h
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_PROVIDER_FACTORY_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_PROVIDER_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}
+
+class Profile;
+
+namespace web_app {
+
+class WebAppProvider;
+
+// Singleton that owns all WebAppProviderFactories and associates them with
+// Profile.
+class WebAppProviderFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static WebAppProvider* GetForProfile(Profile* profile);
+
+  static WebAppProviderFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<WebAppProviderFactory>;
+
+  WebAppProviderFactory();
+  ~WebAppProviderFactory() override;
+
+  // BrowserContextKeyedServiceFactory
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAppProviderFactory);
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_PROVIDER_FACTORY_H_
diff --git a/chrome/renderer/DEPS b/chrome/renderer/DEPS
index b0ece31..6e3cc4d 100644
--- a/chrome/renderer/DEPS
+++ b/chrome/renderer/DEPS
@@ -36,6 +36,7 @@
   "+components/strings/grit",
   "+components/subresource_filter/content/common",
   "+components/subresource_filter/content/renderer",
+  "+components/subresource_filter/core/common/common_features.h",
   "+components/task_scheduler_util",
   "+components/translate/content/common",
   "+components/translate/content/renderer",
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 6a15716..2951ee28 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -86,6 +86,7 @@
 #include "components/startup_metric_utils/common/startup_metric.mojom.h"
 #include "components/subresource_filter/content/renderer/subresource_filter_agent.h"
 #include "components/subresource_filter/content/renderer/unverified_ruleset_dealer.h"
+#include "components/subresource_filter/core/common/common_features.h"
 #include "components/task_scheduler_util/variations_util.h"
 #include "components/variations/variations_switches.h"
 #include "components/version_info/version_info.h"
@@ -1577,6 +1578,9 @@
 #if defined(OS_ANDROID)
   blink::WebRuntimeFeatures::EnableWebShare(true);
 #endif
+
+  if (base::FeatureList::IsEnabled(subresource_filter::kAdTagging))
+    blink::WebRuntimeFeatures::EnableAdTagging(true);
 }
 
 void ChromeContentRendererClient::
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.js b/chrome/test/data/webui/settings/people_page_sync_page_test.js
index e05b4fa5..1a8260f0 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_page_test.js
@@ -155,7 +155,12 @@
       // When unified-consent is enabled and signed in, sync-section should be
       // visible and open by default. Accordion toggle row should be present,
       // and bottom items should have classes used for indentation.
-      syncPage.syncStatus = {signedIn: true, disabled: false};
+      syncPage.syncStatus = {
+        signedIn: true,
+        disabled: false,
+        hasError: false,
+        statusAction: settings.StatusAction.NO_ACTION,
+      };
       syncPage.unifiedConsentEnabled = true;
       Polymer.dom.flush();
       assertTrue(ironCollapse.opened);
@@ -180,6 +185,15 @@
 
       // The unified consent toggle should be visible.
       assertFalse(unifiedConsentToggle.hidden);
+
+      // Test sync paused state.
+      syncPage.syncStatus = {
+        signedIn: true,
+        disabled: false,
+        hasError: true,
+        statusAction: settings.StatusAction.REAUTHENTICATE
+      };
+      assertTrue(ironCollapse.hidden);
     });
 
     test('SyncSectionLayout_UnifiedConsentEnabled_SignedOut', function() {
@@ -190,7 +204,12 @@
 
       // When unified-consent is enabled and signed out, sync-section should be
       // hidden, and the accordion toggle row should be visible not actionable.
-      syncPage.syncStatus = {signedIn: false, disabled: false};
+      syncPage.syncStatus = {
+        signedIn: false,
+        disabled: false,
+        hasError: false,
+        statusAction: settings.StatusAction.NO_ACTION,
+      };
       syncPage.unifiedConsentEnabled = true;
       Polymer.dom.flush();
       assertTrue(ironCollapse.hidden);
@@ -210,7 +229,12 @@
 
       // When unified-consent is enabled and sync is disabled, the sync-section
       // should be hidden.
-      syncPage.syncStatus = {signedIn: false, disabled: true};
+      syncPage.syncStatus = {
+        signedIn: false,
+        disabled: true,
+        hasError: false,
+        statusAction: settings.StatusAction.NO_ACTION,
+      };
       syncPage.unifiedConsentEnabled = true;
       Polymer.dom.flush();
       assertTrue(ironCollapse.hidden);
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index c0f8da9..55a96f8 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -179,6 +179,8 @@
     "dbus/dbus_clients_common.h",
     "dbus/dbus_method_call_status.cc",
     "dbus/dbus_method_call_status.h",
+    "dbus/dbus_switches.cc",
+    "dbus/dbus_switches.h",
     "dbus/dbus_thread_manager.cc",
     "dbus/dbus_thread_manager.h",
     "dbus/debug_daemon_client.cc",
diff --git a/chromeos/audio/cras_audio_handler.cc b/chromeos/audio/cras_audio_handler.cc
index 6bc5adb5..4f255b1da 100644
--- a/chromeos/audio/cras_audio_handler.cc
+++ b/chromeos/audio/cras_audio_handler.cc
@@ -910,6 +910,7 @@
 
   cras_service_available_ = true;
   GetDefaultOutputBufferSizeInternal();
+  GetSystemAecSupported();
   GetNodes();
   GetNumberOfOutputStreams();
 }
@@ -1675,4 +1676,28 @@
   default_output_buffer_size_ = buffer_size.value();
 }
 
+bool CrasAudioHandler::system_aec_supported() const {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  return system_aec_supported_;
+}
+
+// GetSystemAecSupported() is only called in the same thread
+// as the CrasAudioHanler constructor. We are safe here without
+// thread check, because unittest may not have the task runner
+// for the current thread.
+void CrasAudioHandler::GetSystemAecSupported() {
+  GetCrasAudioClient()->GetSystemAecSupported(
+      base::BindOnce(&CrasAudioHandler::HandleGetSystemAecSupported,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CrasAudioHandler::HandleGetSystemAecSupported(
+    base::Optional<bool> system_aec_supported) {
+  if (!system_aec_supported.has_value()) {
+    LOG(ERROR) << "Failed to retrieve system aec supported";
+    return;
+  }
+  system_aec_supported_ = system_aec_supported.value();
+}
+
 }  // namespace chromeos
diff --git a/chromeos/audio/cras_audio_handler.h b/chromeos/audio/cras_audio_handler.h
index 59bc50c..1e63615b 100644
--- a/chromeos/audio/cras_audio_handler.h
+++ b/chromeos/audio/cras_audio_handler.h
@@ -274,6 +274,9 @@
   // the use case. It should be called from a user initiated action.
   void SwitchToFrontOrRearMic();
 
+  // Returns if system AEC is supported in CRAS.
+  bool system_aec_supported() const;
+
  protected:
   explicit CrasAudioHandler(
       scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler);
@@ -475,6 +478,15 @@
   // Handle dbus callback for GetDefaultOutputBufferSize.
   void HandleGetDefaultOutputBufferSize(base::Optional<int> buffer_size);
 
+  // Calling dbus to get system AEC supported flag.
+  void GetSystemAecSupported();
+
+  // Calling dbus to get system AEC supported flag on main thread.
+  void GetSystemAecSupportedOnMainThread();
+
+  // Handle dbus callback for GetSystemAecSupported.
+  void HandleGetSystemAecSupported(base::Optional<bool> system_aec_supported);
+
   void OnVideoCaptureStartedOnMainThread(media::VideoFacingMode facing);
   void OnVideoCaptureStoppedOnMainThread(media::VideoFacingMode facing);
 
@@ -521,6 +533,8 @@
   // Default output buffer size in frames.
   int32_t default_output_buffer_size_;
 
+  bool system_aec_supported_ = false;
+
   int num_active_output_streams_ = 0;
 
   // Task runner of browser main thread. All member variables should be accessed
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index c933b0e..e1261f15 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -126,9 +126,6 @@
 // Possible values: parallel|postpone. Default: parallel.
 const char kAshWebUIInit[] = "ash-webui-init";
 
-// Determines which Google Privacy CA to use for attestation.
-const char kAttestationServer[] = "attestation-server";
-
 // If this flag is set, it indicates that this device is a "Cellular First"
 // device. Cellular First devices use cellular telephone data networks as
 // their primary means of connecting to the internet.
@@ -166,9 +163,6 @@
 // Optional value for Data Saver prompt on cellular networks.
 const char kDataSaverPromptDemoMode[] = "demo";
 
-// Forces the stub implementation of dbus clients.
-const char kDbusStub[] = "dbus-stub";
-
 // Indicates that the wallpaper images specified by
 // kAshDefaultWallpaper{Large,Small} are OEM-specific (i.e. they are not
 // downloadable from Google).
@@ -557,23 +551,6 @@
 // Resdesigned shelf UI.
 const char kShelfNewUi[] = "shelf-new-ui";
 
-// Overrides network stub behavior. By default, ethernet, wifi and vpn are
-// enabled, and transitions occur instantaneously. Multiple options can be
-// comma separated (no spaces). Note: all options are in the format 'foo=x'.
-// Values are case sensitive and based on Shill names in service_constants.h.
-// See FakeShillManagerClient::SetInitialNetworkState for implementation.
-// Examples:
-//  'clear=1' - Clears all default configurations
-//  'wifi=on' - A wifi network is initially connected ('1' also works)
-//  'wifi=off' - Wifi networks are all initially disconnected ('0' also works)
-//  'wifi=disabled' - Wifi is initially disabled
-//  'wifi=none' - Wifi is unavailable
-//  'wifi=portal' - Wifi connection will be in Portal state
-//  'cellular=1' - Cellular is initially connected
-//  'cellular=LTE' - Cellular is initially connected, technology is LTE
-//  'interactive=3' - Interactive mode, connect/scan/etc requests take 3 secs
-const char kShillStub[] = "shill-stub";
-
 // If true, files in Android internal storage will be shown in Files app.
 const char kShowAndroidFilesInFilesApp[] = "show-android-files-in-files-app";
 
@@ -588,9 +565,6 @@
 // This makes it easier to test layout logic.
 const char kShowLoginDevOverlay[] = "show-login-dev-overlay";
 
-// Sends test messages on first call to RequestUpdate (stub only).
-const char kSmsTestMessages[] = "sms-test-messages";
-
 // Indicates that a stub implementation of CrosSettings that stores settings in
 // memory without signing should be used, treating current user as the owner.
 // This also modifies OwnerSettingsServiceChromeOS::HandlesSetting such that no
@@ -598,10 +572,6 @@
 // This option is for testing the chromeos build of chrome on the desktop only.
 const char kStubCrosSettings[] = "stub-cros-settings";
 
-// Indicates that the system is running in dev mode. The dev mode probing is
-// done by session manager.
-const char kSystemDevMode[] = "system-developer-mode";
-
 // Enables testing for encryption migration UI.
 const char kTestEncryptionMigrationUI[] = "test-encryption-migration-ui";
 
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 33063c4..53be935 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -8,6 +8,7 @@
 #include "base/feature_list.h"
 #include "base/memory/memory_pressure_monitor_chromeos.h"
 #include "chromeos/chromeos_export.h"
+#include "chromeos/dbus/dbus_switches.h"
 
 namespace chromeos {
 namespace switches {
@@ -38,7 +39,6 @@
 CHROMEOS_EXPORT extern const char kArcTransitionMigrationRequired[];
 CHROMEOS_EXPORT extern const char kArtifactsDir[];
 CHROMEOS_EXPORT extern const char kAshWebUIInit[];
-CHROMEOS_EXPORT extern const char kAttestationServer[];
 CHROMEOS_EXPORT extern const char kCellularFirst[];
 CHROMEOS_EXPORT extern const char kChildWallpaperLarge[];
 CHROMEOS_EXPORT extern const char kChildWallpaperSmall[];
@@ -49,7 +49,6 @@
 CHROMEOS_EXPORT extern const char kCrosRegionsModeHide[];
 CHROMEOS_EXPORT extern const char kCrosRegionsModeOverride[];
 CHROMEOS_EXPORT extern const char kDataSaverPromptDemoMode[];
-CHROMEOS_EXPORT extern const char kDbusStub[];
 CHROMEOS_EXPORT extern const char kDefaultWallpaperIsOem[];
 CHROMEOS_EXPORT extern const char kDefaultWallpaperLarge[];
 CHROMEOS_EXPORT extern const char kDefaultWallpaperSmall[];
@@ -156,13 +155,10 @@
 CHROMEOS_EXPORT extern const char kRlzPingDelay[];
 CHROMEOS_EXPORT extern const char kShelfHoverPreviews[];
 CHROMEOS_EXPORT extern const char kShelfNewUi[];
-CHROMEOS_EXPORT extern const char kShillStub[];
 CHROMEOS_EXPORT extern const char kShowAndroidFilesInFilesApp[];
 CHROMEOS_EXPORT extern const char kFilesAppDisableMyFilesNavigation[];
 CHROMEOS_EXPORT extern const char kShowLoginDevOverlay[];
-CHROMEOS_EXPORT extern const char kSmsTestMessages[];
 CHROMEOS_EXPORT extern const char kStubCrosSettings[];
-CHROMEOS_EXPORT extern const char kSystemDevMode[];
 CHROMEOS_EXPORT extern const char kTestEncryptionMigrationUI[];
 CHROMEOS_EXPORT extern const char kTetherStub[];
 CHROMEOS_EXPORT extern const char kVoiceInteractionLocales[];
diff --git a/chromeos/cryptohome/tpm_util.cc b/chromeos/cryptohome/tpm_util.cc
index 5a9881f..67ee24e 100644
--- a/chromeos/cryptohome/tpm_util.cc
+++ b/chromeos/cryptohome/tpm_util.cc
@@ -13,6 +13,13 @@
 namespace chromeos {
 namespace tpm_util {
 
+namespace {
+
+constexpr char kAttrMode[] = "enterprise.mode";
+constexpr char kDeviceModeEnterpriseAD[] = "enterprise_ad";
+
+}  // namespace
+
 bool TpmIsEnabled() {
   bool result = false;
   DBusThreadManager::Get()->GetCryptohomeClient()->CallTpmIsEnabledAndBlock(
@@ -78,5 +85,18 @@
   return result;
 }
 
+bool IsActiveDirectoryLocked() {
+  std::string mode;
+  return InstallAttributesGet(kAttrMode, &mode) &&
+         mode == kDeviceModeEnterpriseAD;
+}
+
+bool LockDeviceActiveDirectoryForTesting(const std::string& realm) {
+  return InstallAttributesSet("enterprise.owned", "true") &&
+         InstallAttributesSet(kAttrMode, kDeviceModeEnterpriseAD) &&
+         InstallAttributesSet("enterprise.realm", realm) &&
+         InstallAttributesFinalize();
+}
+
 }  // namespace tpm_util
 }  // namespace chromeos
diff --git a/chromeos/cryptohome/tpm_util.h b/chromeos/cryptohome/tpm_util.h
index d3b7d58..199a735 100644
--- a/chromeos/cryptohome/tpm_util.h
+++ b/chromeos/cryptohome/tpm_util.h
@@ -32,6 +32,14 @@
 CHROMEOS_EXPORT bool InstallAttributesIsInvalid();
 CHROMEOS_EXPORT bool InstallAttributesIsFirstInstall();
 
+// Checks if device is locked for Active Directory management.
+CHROMEOS_EXPORT bool IsActiveDirectoryLocked();
+
+// Sets install attributes for an Active Directory managed device and persists
+// them on disk.
+CHROMEOS_EXPORT bool LockDeviceActiveDirectoryForTesting(
+    const std::string& realm);
+
 }  // namespace tpm_util
 }  // namespace chromeos
 
diff --git a/chromeos/dbus/DEPS b/chromeos/dbus/DEPS
index d6abddad..471d5d08 100644
--- a/chromeos/dbus/DEPS
+++ b/chromeos/dbus/DEPS
@@ -1,3 +1,31 @@
+noparent = True
+
 include_rules = [
+  "+base",
+  "+chromeos/chromeos_export.h",
+  # TODO(stevenjb): Fix this. https://crbug.com/863439
+  "+chromeos/attestation",
+  # TODO(stevenjb): Fix this. https://crbug.com/863439
+  "+chromeos/cryptohome",
+  # TODO(stevenjb): Fix this. https://crbug.com/863439
+  "+chromeos/system",
+  # TODO(stevenjb): Fix this. https://crbug.com/863439
+  "+chromeos/media_perception",
+  "+components/account_id/account_id.h",
+  "+components/device_event_log",
+  "+components/policy/proto",
+  "+crypto",
   "+dbus",
+  "+net",
+  "+testing",
+  "+third_party/cros_system_api",
+  "+url",
 ]
+
+specific_include_rules = {
+  "fake_.*": [
+    # TODO(stevenjb): Remove use of chromeos_paths in fake impls.
+    # https://crbug.com/863439.
+    "+chromeos/chromeos_paths.h",
+  ],
+}
diff --git a/chromeos/dbus/cras_audio_client.cc b/chromeos/dbus/cras_audio_client.cc
index fa66fe0..ec0f3f3 100644
--- a/chromeos/dbus/cras_audio_client.cc
+++ b/chromeos/dbus/cras_audio_client.cc
@@ -59,6 +59,15 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void GetSystemAecSupported(DBusMethodCallback<bool> callback) override {
+    dbus::MethodCall method_call(cras::kCrasControlInterface,
+                                 cras::kGetSystemAecSupported);
+    cras_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&CrasAudioClientImpl::OnGetSystemAecSupported,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void GetNodes(DBusMethodCallback<AudioNodeList> callback) override {
     dbus::MethodCall method_call(cras::kCrasControlInterface,
                                  cras::kGetNodes);
@@ -434,6 +443,25 @@
     std::move(callback).Run(buffer_size);
   }
 
+  void OnGetSystemAecSupported(DBusMethodCallback<bool> callback,
+                               dbus::Response* response) {
+    if (!response) {
+      LOG(ERROR) << "Error calling " << cras::kGetSystemAecSupported;
+      std::move(callback).Run(base::nullopt);
+      return;
+    }
+    bool system_aec_supported = 0;
+    dbus::MessageReader reader(response);
+    if (!reader.PopBool(&system_aec_supported)) {
+      LOG(ERROR) << "Error reading response from cras: "
+                 << response->ToString();
+      std::move(callback).Run(base::nullopt);
+      return;
+    }
+
+    std::move(callback).Run(system_aec_supported);
+  }
+
   void OnGetNodes(DBusMethodCallback<AudioNodeList> callback,
                   dbus::Response* response) {
     if (!response) {
diff --git a/chromeos/dbus/cras_audio_client.h b/chromeos/dbus/cras_audio_client.h
index ecd3879..5c0b7bd 100644
--- a/chromeos/dbus/cras_audio_client.h
+++ b/chromeos/dbus/cras_audio_client.h
@@ -73,6 +73,9 @@
   // Gets the default output buffer size in frames.
   virtual void GetDefaultOutputBufferSize(DBusMethodCallback<int> callback) = 0;
 
+  // Gets if system AEC is supported.
+  virtual void GetSystemAecSupported(DBusMethodCallback<bool> callback) = 0;
+
   // Gets an array of audio input and output nodes.
   virtual void GetNodes(DBusMethodCallback<AudioNodeList> callback) = 0;
 
diff --git a/chromeos/dbus/cryptohome_client.cc b/chromeos/dbus/cryptohome_client.cc
index f7dac99..96f91f7 100644
--- a/chromeos/dbus/cryptohome_client.cc
+++ b/chromeos/dbus/cryptohome_client.cc
@@ -18,12 +18,12 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
-#include "chromeos/chromeos_switches.h"
 #include "chromeos/cryptohome/async_method_caller.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/blocking_method_caller.h"
 #include "chromeos/dbus/cryptohome/key.pb.h"
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_path.h"
diff --git a/chromeos/dbus/dbus_clients_common.cc b/chromeos/dbus/dbus_clients_common.cc
index 7607da0..dcc89af 100644
--- a/chromeos/dbus/dbus_clients_common.cc
+++ b/chromeos/dbus/dbus_clients_common.cc
@@ -5,12 +5,12 @@
 #include "chromeos/dbus/dbus_clients_common.h"
 
 #include "base/command_line.h"
-#include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/biod/biod_client.h"
 #include "chromeos/dbus/cec_service_client.h"
 #include "chromeos/dbus/cras_audio_client.h"
 #include "chromeos/dbus/cryptohome_client.h"
 #include "chromeos/dbus/dbus_client_implementation_type.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_cras_audio_client.h"
 #include "chromeos/dbus/fake_cryptohome_client.h"
diff --git a/chromeos/dbus/dbus_switches.cc b/chromeos/dbus/dbus_switches.cc
new file mode 100644
index 0000000..a6fdc3f
--- /dev/null
+++ b/chromeos/dbus/dbus_switches.cc
@@ -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.
+
+#include "chromeos/dbus/dbus_switches.h"
+
+namespace chromeos {
+namespace switches {
+
+// Used in CryptohomeClient to determine which Google Privacy CA to use for
+// attestation.
+const char kAttestationServer[] = "attestation-server";
+
+// Forces the stub implementation of D-Bus clients.
+const char kDbusStub[] = "dbus-stub";
+
+// Overrides Shill stub behavior. By default, ethernet, wifi and vpn are
+// enabled, and transitions occur instantaneously. Multiple options can be
+// comma separated (no spaces). Note: all options are in the format 'foo=x'.
+// Values are case sensitive and based on Shill names in service_constants.h.
+// See FakeShillManagerClient::SetInitialNetworkState for implementation.
+// Examples:
+//  'clear=1' - Clears all default configurations
+//  'wifi=on' - A wifi network is initially connected ('1' also works)
+//  'wifi=off' - Wifi networks are all initially disconnected ('0' also works)
+//  'wifi=disabled' - Wifi is initially disabled
+//  'wifi=none' - Wifi is unavailable
+//  'wifi=portal' - Wifi connection will be in Portal state
+//  'cellular=1' - Cellular is initially connected
+//  'cellular=LTE' - Cellular is initially connected, technology is LTE
+//  'interactive=3' - Interactive mode, connect/scan/etc requests take 3 secs
+const char kShillStub[] = "shill-stub";
+
+// Sends test messages on first call to RequestUpdate (stub only).
+const char kSmsTestMessages[] = "sms-test-messages";
+
+// Used by FakeDebugDaemonClient to specify that the system is running in dev
+// mode when running in a Linux environment. The dev mode probing is done by
+// session manager.
+const char kSystemDevMode[] = "system-developer-mode";
+
+}  // namespace switches
+}  // namespace chromeos
diff --git a/chromeos/dbus/dbus_switches.h b/chromeos/dbus/dbus_switches.h
new file mode 100644
index 0000000..8da95077
--- /dev/null
+++ b/chromeos/dbus/dbus_switches.h
@@ -0,0 +1,22 @@
+// 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_DBUS_DBUS_SWITCHES_H_
+#define CHROMEOS_DBUS_DBUS_SWITCHES_H_
+
+#include "chromeos/chromeos_export.h"
+
+namespace chromeos {
+namespace switches {
+
+CHROMEOS_EXPORT extern const char kAttestationServer[];
+CHROMEOS_EXPORT extern const char kDbusStub[];
+CHROMEOS_EXPORT extern const char kShillStub[];
+CHROMEOS_EXPORT extern const char kSmsTestMessages[];
+CHROMEOS_EXPORT extern const char kSystemDevMode[];
+
+}  // namespace switches
+}  // namespace chromeos
+
+#endif  // CHROMEOS_DBUS_DBUS_SWITCHES_H_
diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc
index 1514251..50811374 100644
--- a/chromeos/dbus/dbus_thread_manager.cc
+++ b/chromeos/dbus/dbus_thread_manager.cc
@@ -11,7 +11,6 @@
 #include "base/message_loop/message_loop.h"
 #include "base/sys_info.h"
 #include "base/threading/thread.h"
-#include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/arc_midis_client.h"
 #include "chromeos/dbus/arc_obb_mounter_client.h"
 #include "chromeos/dbus/arc_oemcrypto_client.h"
@@ -26,6 +25,7 @@
 #include "chromeos/dbus/dbus_client.h"
 #include "chromeos/dbus/dbus_clients_browser.h"
 #include "chromeos/dbus/dbus_clients_common.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "chromeos/dbus/debug_daemon_client.h"
 #include "chromeos/dbus/easy_unlock_client.h"
 #include "chromeos/dbus/gsm_sms_client.h"
diff --git a/chromeos/dbus/fake_auth_policy_client.cc b/chromeos/dbus/fake_auth_policy_client.cc
index 0a2393f..3c42a9f 100644
--- a/chromeos/dbus/fake_auth_policy_client.cc
+++ b/chromeos/dbus/fake_auth_policy_client.cc
@@ -19,10 +19,10 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/chromeos_paths.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
+#include "chromeos/cryptohome/tpm_util.h"
 #include "chromeos/dbus/cryptohome_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/session_manager_client.h"
-#include "chromeos/login/auth/authpolicy_login_helper.h"
 #include "components/account_id/account_id.h"
 #include "components/policy/proto/cloud_policy.pb.h"
 #include "dbus/message.h"
@@ -96,7 +96,7 @@
     const authpolicy::JoinDomainRequest& request,
     int password_fd,
     JoinCallback callback) {
-  DCHECK(!AuthPolicyLoginHelper::IsAdLocked());
+  DCHECK(!tpm_util::IsActiveDirectoryLocked());
   authpolicy::ErrorType error = authpolicy::ERROR_NONE;
   std::string machine_domain;
   if (!started_) {
@@ -137,7 +137,7 @@
     const authpolicy::AuthenticateUserRequest& request,
     int password_fd,
     AuthCallback callback) {
-  DCHECK(AuthPolicyLoginHelper::IsAdLocked());
+  DCHECK(tpm_util::IsActiveDirectoryLocked());
   authpolicy::ErrorType error = authpolicy::ERROR_NONE;
   authpolicy::ActiveDirectoryAccountInfo account_info;
   if (!started_) {
@@ -200,7 +200,7 @@
     return;
   }
 
-  if (!AuthPolicyLoginHelper::IsAdLocked()) {
+  if (!tpm_util::IsActiveDirectoryLocked()) {
     // Pretend that policy was fetched and cached inside authpolicyd.
     std::move(callback).Run(
         authpolicy::ERROR_DEVICE_POLICY_CACHED_BUT_NOT_SENT);
@@ -225,7 +225,7 @@
 
 void FakeAuthPolicyClient::RefreshUserPolicy(const AccountId& account_id,
                                              RefreshPolicyCallback callback) {
-  DCHECK(AuthPolicyLoginHelper::IsAdLocked());
+  DCHECK(tpm_util::IsActiveDirectoryLocked());
   if (!started_) {
     LOG(ERROR) << "authpolicyd not started";
     std::move(callback).Run(authpolicy::ERROR_DBUS_FAILURE);
diff --git a/chromeos/dbus/fake_auth_policy_client_unittest.cc b/chromeos/dbus/fake_auth_policy_client_unittest.cc
index 5ce788b..6a95e48 100644
--- a/chromeos/dbus/fake_auth_policy_client_unittest.cc
+++ b/chromeos/dbus/fake_auth_policy_client_unittest.cc
@@ -7,10 +7,10 @@
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "chromeos/cryptohome/tpm_util.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_cryptohome_client.h"
 #include "chromeos/dbus/fake_session_manager_client.h"
-#include "chromeos/login/auth/authpolicy_login_helper.h"
 #include "components/account_id/account_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -85,8 +85,7 @@
   }
 
   void LockDeviceActiveDirectory() {
-    EXPECT_TRUE(AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting(
-        std::string()));
+    EXPECT_TRUE(tpm_util::LockDeviceActiveDirectoryForTesting(std::string()));
   }
 
   void WaitForServiceToBeAvailable() {
diff --git a/chromeos/dbus/fake_cras_audio_client.cc b/chromeos/dbus/fake_cras_audio_client.cc
index 9de39ef..1023b0a 100644
--- a/chromeos/dbus/fake_cras_audio_client.cc
+++ b/chromeos/dbus/fake_cras_audio_client.cc
@@ -106,6 +106,11 @@
   std::move(callback).Run(512);
 }
 
+void FakeCrasAudioClient::GetSystemAecSupported(
+    DBusMethodCallback<bool> callback) {
+  std::move(callback).Run(false);
+}
+
 void FakeCrasAudioClient::GetNodes(DBusMethodCallback<AudioNodeList> callback) {
   std::move(callback).Run(node_list_);
 }
diff --git a/chromeos/dbus/fake_cras_audio_client.h b/chromeos/dbus/fake_cras_audio_client.h
index 4503a39..18b1078 100644
--- a/chromeos/dbus/fake_cras_audio_client.h
+++ b/chromeos/dbus/fake_cras_audio_client.h
@@ -28,6 +28,7 @@
   bool HasObserver(const Observer* observer) const override;
   void GetVolumeState(DBusMethodCallback<VolumeState> callback) override;
   void GetDefaultOutputBufferSize(DBusMethodCallback<int> callback) override;
+  void GetSystemAecSupported(DBusMethodCallback<bool> callback) override;
   void GetNodes(DBusMethodCallback<AudioNodeList> callback) override;
   void GetNumberOfActiveOutputStreams(
       DBusMethodCallback<int> callback) override;
diff --git a/chromeos/dbus/fake_cryptohome_client.cc b/chromeos/dbus/fake_cryptohome_client.cc
index 5e9e898..557727dd 100644
--- a/chromeos/dbus/fake_cryptohome_client.cc
+++ b/chromeos/dbus/fake_cryptohome_client.cc
@@ -10,7 +10,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
 #include "base/optional.h"
@@ -21,7 +20,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chromeos/attestation/attestation.pb.h"
 #include "chromeos/chromeos_paths.h"
-#include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/cryptohome/key.pb.h"
 #include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "components/policy/proto/install_attributes.pb.h"
@@ -577,9 +575,7 @@
   cryptohome::MountReply* mount =
       reply.MutableExtension(cryptohome::MountReply::reply);
   mount->set_sanitized_username(GetStubSanitizedUsername(cryptohome_id));
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kTestEncryptionMigrationUI) &&
-      !request.to_migrate_from_ecryptfs() &&
+  if (!request.to_migrate_from_ecryptfs() &&
       request.force_dircrypto_if_available()) {
     error = cryptohome::CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION;
   }
diff --git a/chromeos/dbus/fake_debug_daemon_client.cc b/chromeos/dbus/fake_debug_daemon_client.cc
index d453d36..636f98c 100644
--- a/chromeos/dbus/fake_debug_daemon_client.cc
+++ b/chromeos/dbus/fake_debug_daemon_client.cc
@@ -19,7 +19,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_switches.h"
 
 namespace {
 
diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc
index d47167a..4de34c3 100644
--- a/chromeos/dbus/fake_shill_manager_client.cc
+++ b/chromeos/dbus/fake_shill_manager_client.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
@@ -18,7 +19,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/fake_shill_device_client.h"
 #include "chromeos/dbus/shill_device_client.h"
diff --git a/chromeos/dbus/fake_shill_service_client.cc b/chromeos/dbus/fake_shill_service_client.cc
index 4750a6e..ece0aedb 100644
--- a/chromeos/dbus/fake_shill_service_client.cc
+++ b/chromeos/dbus/fake_shill_service_client.cc
@@ -12,6 +12,7 @@
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
@@ -20,7 +21,6 @@
 #include "chromeos/dbus/shill_manager_client.h"
 #include "chromeos/dbus/shill_profile_client.h"
 #include "chromeos/dbus/shill_property_changed_observer.h"
-#include "chromeos/network/shill_property_util.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_path.h"
@@ -343,7 +343,8 @@
     properties->SetKey(shill::kGuidProperty, base::Value(guid_to_set));
   }
   properties->SetKey(shill::kSSIDProperty, base::Value(name));
-  shill_property_util::SetSSID(name, properties);  // Sets kWifiHexSsid
+  properties->SetKey(shill::kWifiHexSsid,
+                     base::Value(base::HexEncode(name.c_str(), name.size())));
   properties->SetKey(shill::kNameProperty, base::Value(name));
   std::string device_path = DBusThreadManager::Get()
                                 ->GetShillDeviceClient()
diff --git a/chromeos/dbus/fake_sms_client.cc b/chromeos/dbus/fake_sms_client.cc
index 8079320..fc47ec1 100644
--- a/chromeos/dbus/fake_sms_client.cc
+++ b/chromeos/dbus/fake_sms_client.cc
@@ -12,7 +12,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
-#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "chromeos/dbus/fake_sms_client.h"
 #include "dbus/object_path.h"
 
diff --git a/chromeos/dbus/power_manager_client.cc b/chromeos/dbus/power_manager_client.cc
index 2332897..00b1bbb6 100644
--- a/chromeos/dbus/power_manager_client.cc
+++ b/chromeos/dbus/power_manager_client.cc
@@ -22,14 +22,13 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/platform_thread.h"
 #include "base/timer/timer.h"
-#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "chromeos/dbus/fake_power_manager_client.h"
 #include "chromeos/dbus/power_manager/backlight.pb.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
 #include "chromeos/dbus/power_manager/input_event.pb.h"
 #include "chromeos/dbus/power_manager/peripheral_battery_status.pb.h"
 #include "chromeos/dbus/power_manager/switch_states.pb.h"
-#include "chromeos/system/statistics_provider.h"
 #include "components/device_event_log/device_event_log.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
diff --git a/chromeos/dbus/session_manager_client.cc b/chromeos/dbus/session_manager_client.cc
index dd1137a..ebf8efe 100644
--- a/chromeos/dbus/session_manager_client.cc
+++ b/chromeos/dbus/session_manager_client.cc
@@ -21,7 +21,6 @@
 #include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/chromeos_paths.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "chromeos/dbus/blocking_method_caller.h"
 #include "chromeos/dbus/cryptohome_client.h"
diff --git a/chromeos/dbus/shill_client_unittest_base.cc b/chromeos/dbus/shill_client_unittest_base.cc
index 887938e..ea3f88c 100644
--- a/chromeos/dbus/shill_client_unittest_base.cc
+++ b/chromeos/dbus/shill_client_unittest_base.cc
@@ -13,8 +13,8 @@
 #include "base/json/json_writer.h"
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/values.h"
-#include "chromeos/network/shill_property_util.h"
 #include "dbus/message.h"
 #include "dbus/object_path.h"
 #include "dbus/values_util.h"
@@ -317,7 +317,9 @@
                      base::Value("00000000-0000-0000-0000-000000000000"));
   properties->SetKey(shill::kModeProperty, base::Value(shill::kModeManaged));
   properties->SetKey(shill::kTypeProperty, base::Value(shill::kTypeWifi));
-  shill_property_util::SetSSID("testssid", properties);
+  const std::string ssid = "testssid";
+  properties->SetKey(shill::kWifiHexSsid,
+                     base::Value(base::HexEncode(ssid.c_str(), ssid.size())));
   properties->SetKey(shill::kSecurityClassProperty,
                      base::Value(shill::kSecurityPsk));
   return properties;
diff --git a/chromeos/dbus/shill_service_client.cc b/chromeos/dbus/shill_service_client.cc
index 6efb51d..54be594 100644
--- a/chromeos/dbus/shill_service_client.cc
+++ b/chromeos/dbus/shill_service_client.cc
@@ -13,7 +13,7 @@
 #include "base/stl_util.h"
 #include "base/values.h"
 #include "chromeos/dbus/shill_property_changed_observer.h"
-#include "chromeos/network/network_event_log.h"
+#include "components/device_event_log/device_event_log.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
 #include "dbus/object_proxy.h"
@@ -252,8 +252,8 @@
     // Either way this would cause an invalid memory access in
     // ShillManagerClient, see crbug.com/324849.
     if (object_path == dbus::ObjectPath(shill::kFlimflamServicePath)) {
-      NET_LOG_ERROR("ShillServiceClient service has invalid path",
-                    shill::kFlimflamServicePath);
+      NET_LOG(ERROR) << "ShillServiceClient service has invalid path: "
+                     << shill::kFlimflamServicePath;
       return;
     }
     bus_->RemoveObjectProxy(shill::kFlimflamServiceName, object_path,
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc
index 29af542..0b55d87 100644
--- a/chromeos/dbus/update_engine_client.cc
+++ b/chromeos/dbus/update_engine_client.cc
@@ -17,7 +17,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/dbus_switches.h"
 #include "chromeos/system/version_loader.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
diff --git a/chromeos/login/auth/authpolicy_login_helper.cc b/chromeos/login/auth/authpolicy_login_helper.cc
index c16240b..4abbe80 100644
--- a/chromeos/login/auth/authpolicy_login_helper.cc
+++ b/chromeos/login/auth/authpolicy_login_helper.cc
@@ -22,8 +22,6 @@
 
 namespace {
 
-constexpr char kAttrMode[] = "enterprise.mode";
-constexpr char kDeviceModeEnterpriseAD[] = "enterprise_ad";
 constexpr char kDCPrefix[] = "DC=";
 constexpr char kOUPrefix[] = "OU=";
 
@@ -168,29 +166,13 @@
       base::BindOnce(&DoDecrypt, blob, password), std::move(callback));
 }
 
-// static
-bool AuthPolicyLoginHelper::IsAdLocked() {
-  std::string mode;
-  return chromeos::tpm_util::InstallAttributesGet(kAttrMode, &mode) &&
-         mode == kDeviceModeEnterpriseAD;
-}
-
-// static
-bool AuthPolicyLoginHelper::LockDeviceActiveDirectoryForTesting(
-    const std::string& realm) {
-  return tpm_util::InstallAttributesSet("enterprise.owned", "true") &&
-         tpm_util::InstallAttributesSet(kAttrMode, kDeviceModeEnterpriseAD) &&
-         tpm_util::InstallAttributesSet("enterprise.realm", realm) &&
-         tpm_util::InstallAttributesFinalize();
-}
-
 void AuthPolicyLoginHelper::JoinAdDomain(const std::string& machine_name,
                                          const std::string& distinguished_name,
                                          int encryption_types,
                                          const std::string& username,
                                          const std::string& password,
                                          JoinCallback callback) {
-  DCHECK(!IsAdLocked());
+  DCHECK(!tpm_util::IsActiveDirectoryLocked());
   DCHECK(!weak_factory_.HasWeakPtrs()) << "Another operation is in progress";
   authpolicy::JoinDomainRequest request;
   if (!ParseDomainAndOU(distinguished_name, &request)) {
@@ -237,7 +219,7 @@
 void AuthPolicyLoginHelper::OnJoinCallback(JoinCallback callback,
                                            authpolicy::ErrorType error,
                                            const std::string& machine_domain) {
-  DCHECK(!IsAdLocked());
+  DCHECK(!tpm_util::IsActiveDirectoryLocked());
   if (error != authpolicy::ERROR_NONE) {
     std::move(callback).Run(error, machine_domain);
     return;
@@ -253,7 +235,7 @@
     JoinCallback callback,
     const std::string& machine_domain,
     authpolicy::ErrorType error) {
-  DCHECK(!IsAdLocked());
+  DCHECK(!tpm_util::IsActiveDirectoryLocked());
   // First policy refresh happens before device is locked. So policy store
   // should not succeed. The error means that authpolicyd cached device policy
   // and stores it in the next call to RefreshDevicePolicy in STEP_STORE_POLICY.
diff --git a/chromeos/login/auth/authpolicy_login_helper.h b/chromeos/login/auth/authpolicy_login_helper.h
index 27cfd81..fd185aa 100644
--- a/chromeos/login/auth/authpolicy_login_helper.h
+++ b/chromeos/login/auth/authpolicy_login_helper.h
@@ -46,13 +46,6 @@
                                    const std::string& password,
                                    OnDecryptedCallback callback);
 
-  // Checks if device is locked for Active Directory management.
-  static bool IsAdLocked();
-
-  // Sets install attributes for Active Directory managed device. Persists it on
-  // disk.
-  static bool LockDeviceActiveDirectoryForTesting(const std::string& realm);
-
   // Packs arguments and calls AuthPolicyClient::JoinAdDomain. Joins machine to
   // Active directory domain. Then it calls RefreshDevicePolicy to cache the
   // policy on the authpolicyd side. |machine_name| is a name for a local
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc
index e067160..19a108d 100644
--- a/components/autofill/core/browser/test_autofill_client.cc
+++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -52,8 +52,7 @@
 }
 
 AddressNormalizer* TestAutofillClient::GetAddressNormalizer() {
-  // TODO(crbug.com/788432): Should use a TestAddressNormalizer.
-  return nullptr;
+  return &test_address_normalizer_;
 }
 
 security_state::SecurityLevel
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 246b3fa..cff3e37 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -14,6 +14,7 @@
 #include "base/i18n/rtl.h"
 #include "base/macros.h"
 #include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/test_address_normalizer.h"
 #include "components/prefs/pref_service.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
@@ -110,6 +111,7 @@
  private:
   identity::IdentityTestEnvironment identity_test_env_;
   syncer::SyncService* test_sync_service_ = nullptr;
+  TestAddressNormalizer test_address_normalizer_;
 
   // NULL by default.
   std::unique_ptr<PrefService> prefs_;
diff --git a/components/autofill/core/browser/test_sync_service.cc b/components/autofill/core/browser/test_sync_service.cc
index d35cd53..c1680e2 100644
--- a/components/autofill/core/browser/test_sync_service.cc
+++ b/components/autofill/core/browser/test_sync_service.cc
@@ -42,10 +42,6 @@
   return is_using_secondary_passphrase_;
 }
 
-bool TestSyncService::IsSyncActive() const {
-  return is_sync_active_;
-}
-
 const GoogleServiceAuthError& TestSyncService::GetAuthError() const {
   return auth_error_;
 }
diff --git a/components/autofill/core/browser/test_sync_service.h b/components/autofill/core/browser/test_sync_service.h
index 96ae944..2be40aa 100644
--- a/components/autofill/core/browser/test_sync_service.h
+++ b/components/autofill/core/browser/test_sync_service.h
@@ -22,7 +22,6 @@
   bool IsEngineInitialized() const override;
   bool IsFirstSetupComplete() const override;
   bool IsUsingSecondaryPassphrase() const override;
-  bool IsSyncActive() const override;
   syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override;
   const GoogleServiceAuthError& GetAuthError() const override;
   syncer::SyncTokenStatus GetSyncTokenStatus() const override;
@@ -43,10 +42,6 @@
     is_using_secondary_passphrase_ = is_using_secondary_passphrase;
   }
 
-  void SetIsSyncActive(bool is_sync_active) {
-    is_sync_active_ = is_sync_active;
-  }
-
   void SetSyncCycleComplete(bool complete) { sync_cycle_complete_ = complete; }
 
   void SetInAuthError(bool is_in_auth_error);
@@ -57,7 +52,6 @@
   syncer::ModelTypeSet data_types_;
   bool is_engine_initialized_ = true;
   bool is_using_secondary_passphrase_ = false;
-  bool is_sync_active_ = true;
   bool sync_cycle_complete_ = true;
   GoogleServiceAuthError auth_error_;
   bool is_in_auth_error_ = false;
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index a15e288..de446289 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -55,6 +55,7 @@
 #include "components/sync/js/js_event_details.h"
 #include "components/sync/model/change_processor.h"
 #include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model/sync_error.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 #include "components/sync/syncable/directory.h"
@@ -124,22 +125,6 @@
 
 constexpr char kSyncUnrecoverableErrorHistogram[] = "Sync.UnrecoverableErrors";
 
-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,
@@ -191,7 +176,6 @@
           base::BindRepeating(&ProfileSyncService::CredentialsChanged,
                               base::Unretained(this)))),
       channel_(init_params.channel),
-      base_directory_(init_params.base_directory),
       debug_identifier_(init_params.debug_identifier),
       sync_service_url_(
           syncer::GetSyncServiceURL(*base::CommandLine::ForCurrentProcess(),
@@ -232,12 +216,6 @@
       current_version.substr(0, current_version.find('.'))) {
     passphrase_prompt_triggered_by_version_ = true;
   }
-
-  if (init_params.model_type_store_factory.is_null()) {
-    model_type_store_factory_ = GetModelTypeStoreFactory(base_directory_);
-  } else {
-    model_type_store_factory_ = init_params.model_type_store_factory;
-  }
 }
 
 ProfileSyncService::~ProfileSyncService() {
@@ -253,6 +231,12 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   sync_client_->Initialize();
 
+  syncer::ModelTypeStoreService* model_type_store_service =
+      sync_client_->GetModelTypeStoreService();
+  DCHECK(model_type_store_service);
+  syncer::RepeatingModelTypeStoreFactory model_type_store_factory =
+      model_type_store_service->GetStoreFactory();
+
   startup_controller_ = std::make_unique<syncer::StartupController>(
       base::BindRepeating(&ProfileSyncService::GetPreferredDataTypes,
                           base::Unretained(this)),
@@ -271,7 +255,7 @@
     DCHECK(sync_client_->GetSyncSessionsClient());
     sessions_sync_manager_ = std::make_unique<sync_sessions::SessionSyncBridge>(
         sync_client_->GetSyncSessionsClient(), &sync_prefs_,
-        local_device_.get(), model_type_store_factory_,
+        local_device_.get(), model_type_store_factory,
         base::BindRepeating(&ProfileSyncService::NotifyForeignSessionUpdated,
                             sync_enabled_weak_factory_.GetWeakPtr()),
         std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
@@ -288,7 +272,7 @@
   }
 
   device_info_sync_bridge_ = std::make_unique<syncer::DeviceInfoSyncBridge>(
-      local_device_.get(), model_type_store_factory_,
+      local_device_.get(), model_type_store_factory,
       std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
           syncer::DEVICE_INFO,
           /*dump_stack=*/base::BindRepeating(&syncer::ReportUnrecoverableError,
@@ -551,7 +535,8 @@
 
   engine_ = sync_client_->GetSyncApiComponentFactory()->CreateSyncEngine(
       debug_identifier_, sync_client_->GetInvalidationService(),
-      sync_prefs_.AsWeakPtr(), FormatSyncDataPath(base_directory_));
+      sync_prefs_.AsWeakPtr(),
+      sync_client_->GetModelTypeStoreService()->GetSyncDataPath());
 
   // Clear any old errors the first time sync starts.
   if (!IsFirstSetupComplete())
@@ -661,8 +646,9 @@
       // the data directory needs to be cleaned up here.
       sync_thread_->task_runner()->PostTask(
           FROM_HERE,
-          base::BindOnce(&syncer::syncable::Directory::DeleteDirectoryFiles,
-                         FormatSyncDataPath(base_directory_)));
+          base::BindOnce(
+              &syncer::syncable::Directory::DeleteDirectoryFiles,
+              sync_client_->GetModelTypeStoreService()->GetSyncDataPath()));
     }
     return;
   }
@@ -824,8 +810,6 @@
   // false.
   DCHECK(CanConfigureDataTypes() || IsSetupInProgress());
 
-  DCHECK(IsSyncActive());
-
   if (data_type_manager_->state() != DataTypeManager::CONFIGURED) {
     return State::CONFIGURING;
   }
@@ -1323,12 +1307,6 @@
                           weak_factory_.GetWeakPtr()));
 }
 
-bool ProfileSyncService::IsSyncActive() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return engine_initialized_ && data_type_manager_ &&
-         data_type_manager_->state() != DataTypeManager::STOPPED;
-}
-
 bool ProfileSyncService::IsLocalSyncEnabled() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return sync_prefs_.IsLocalSyncEnabled();
@@ -1548,16 +1526,6 @@
   platform_sync_allowed_provider_ = platform_sync_allowed_provider;
 }
 
-// static
-syncer::RepeatingModelTypeStoreFactory
-ProfileSyncService::GetModelTypeStoreFactory(const base::FilePath& base_path) {
-  // TODO(skym): Verify using AsUTF8Unsafe is okay here. Should work as long
-  // as the Local State file is guaranteed to be UTF-8.
-  const std::string path =
-      FormatSharedModelTypeStorePath(base_path).AsUTF8Unsafe();
-  return base::BindRepeating(&syncer::ModelTypeStore::CreateStore, path);
-}
-
 void ProfileSyncService::ConfigureDataTypeManager() {
   // Don't configure datatypes if the setup UI is still on the screen - this
   // is to help multi-screen setting UIs (like iOS) where they don't want to
@@ -2137,11 +2105,6 @@
     engine_->FlushDirectory();
 }
 
-base::FilePath ProfileSyncService::GetDirectoryPathForTest() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return FormatSyncDataPath(base_directory_);
-}
-
 base::MessageLoop* ProfileSyncService::GetSyncLoopForTest() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return sync_thread_ ? sync_thread_->message_loop() : nullptr;
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 52386eb9e..d972597 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -41,7 +41,6 @@
 #include "components/sync/engine/sync_engine.h"
 #include "components/sync/engine/sync_engine_host.h"
 #include "components/sync/js/sync_js_controller.h"
-#include "components/sync/model/model_type_store.h"
 #include "components/version_info/version_info.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "url/gurl.h"
@@ -213,12 +212,10 @@
     GaiaCookieManagerService* gaia_cookie_manager_service = nullptr;
     StartBehavior start_behavior = MANUAL_START;
     syncer::NetworkTimeUpdateCallback network_time_update_callback;
-    base::FilePath base_directory;
     scoped_refptr<net::URLRequestContextGetter> url_request_context;
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
     std::string debug_identifier;
     version_info::Channel channel = version_info::Channel::UNKNOWN;
-    syncer::RepeatingModelTypeStoreFactory model_type_store_factory;
     bool user_events_separate_pref_group = false;
 
    private:
@@ -237,7 +234,6 @@
   int GetDisableReasons() const override;
   State GetState() const override;
   bool IsFirstSetupComplete() const override;
-  bool IsSyncActive() const override;
   bool IsLocalSyncEnabled() const override;
   void TriggerRefresh(const syncer::ModelTypeSet& types) override;
   void OnDataTypeRequestsSyncStartup(syncer::ModelType type) override;
@@ -465,14 +461,6 @@
   void SetPlatformSyncAllowedProvider(
       const PlatformSyncAllowedProvider& platform_sync_allowed_provider);
 
-  // Returns a function  that will create a ModelTypeStore that shares
-  // the sync LevelDB backend. |base_path| should be set to profile path.
-  static syncer::RepeatingModelTypeStoreFactory GetModelTypeStoreFactory(
-      const base::FilePath& base_path);
-
-  // Needed to test whether the directory is deleted properly.
-  base::FilePath GetDirectoryPathForTest() const;
-
   // Sometimes we need to wait for tasks on the sync thread in tests.
   base::MessageLoop* GetSyncLoopForTest() const;
 
@@ -633,10 +621,6 @@
   // 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_;
 
@@ -771,12 +755,6 @@
   // platform.
   PlatformSyncAllowedProvider platform_sync_allowed_provider_;
 
-  // The factory used to initialize the ModelTypeStore passed to
-  // sync bridges created by the ProfileSyncService. The default factory
-  // creates an on disk leveldb-backed ModelTypeStore; one might override this
-  // default to, e.g., use an in-memory db for unit tests.
-  syncer::RepeatingModelTypeStoreFactory model_type_store_factory_;
-
   // This weak factory invalidates its issued pointers when Sync is disabled.
   base::WeakPtrFactory<ProfileSyncService> sync_enabled_weak_factory_;
 
diff --git a/components/browser_sync/profile_sync_service_mock.h b/components/browser_sync/profile_sync_service_mock.h
index b252e6096..d709fda 100644
--- a/components/browser_sync/profile_sync_service_mock.h
+++ b/components/browser_sync/profile_sync_service_mock.h
@@ -70,12 +70,12 @@
   MOCK_CONST_METHOD0(GetLastCycleSnapshot, syncer::SyncCycleSnapshot());
 
   MOCK_CONST_METHOD0(GetDisableReasons, int());
+  MOCK_CONST_METHOD0(GetState, State());
   MOCK_METHOD1(QueryDetailedSyncStatus,
                bool(syncer::SyncEngine::Status* result));
   MOCK_CONST_METHOD0(GetAuthError, const GoogleServiceAuthError&());
   MOCK_CONST_METHOD0(IsFirstSetupInProgress, bool());
   MOCK_CONST_METHOD0(GetLastSyncedTime, base::Time());
-  MOCK_CONST_METHOD0(IsSyncActive, bool());
   MOCK_CONST_METHOD0(IsEngineInitialized, bool());
   MOCK_CONST_METHOD0(IsSyncConfirmationNeeded, bool());
   MOCK_METHOD1(OnActionableError, void(const syncer::SyncProtocolError&));
diff --git a/components/browser_sync/profile_sync_service_unittest.cc b/components/browser_sync/profile_sync_service_unittest.cc
index ab764fb2..4ac188d 100644
--- a/components/browser_sync/profile_sync_service_unittest.cc
+++ b/components/browser_sync/profile_sync_service_unittest.cc
@@ -26,7 +26,6 @@
 #include "components/sync/driver/sync_token_status.h"
 #include "components/sync/driver/sync_util.h"
 #include "components/sync/engine/fake_sync_engine.h"
-#include "components/sync/model/model_type_store_test_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/version_info/version_info_values.h"
 #include "google_apis/gaia/oauth2_token_service_delegate.h"
@@ -192,9 +191,6 @@
     ProfileSyncService::InitParams init_params =
         profile_sync_service_bundle_.CreateBasicInitParams(behavior,
                                                            builder.Build());
-    init_params.model_type_store_factory =
-        syncer::ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
-
     service_ = std::make_unique<ProfileSyncService>(std::move(init_params));
 
     ON_CALL(*component_factory(), CreateCommonDataTypeControllers(_, _))
diff --git a/components/browser_sync/profile_sync_test_util.cc b/components/browser_sync/profile_sync_test_util.cc
index de26d9c..69d9cf0 100644
--- a/components/browser_sync/profile_sync_test_util.cc
+++ b/components/browser_sync/profile_sync_test_util.cc
@@ -47,6 +47,7 @@
  public:
   BundleSyncClient(syncer::SyncApiComponentFactory* factory,
                    PrefService* pref_service,
+                   syncer::ModelTypeStoreService* model_type_store_service,
                    sync_sessions::SyncSessionsClient* sync_sessions_client,
                    autofill::PersonalDataManager* personal_data_manager,
                    const base::Callback<base::WeakPtr<syncer::SyncableService>(
@@ -67,6 +68,7 @@
   base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType(
       syncer::ModelType type) override;
   syncer::SyncService* GetSyncService() override;
+  syncer::ModelTypeStoreService* GetModelTypeStoreService() override;
   scoped_refptr<syncer::ModelSafeWorker> CreateModelWorkerForGroup(
       syncer::ModelSafeGroup group) override;
   history::HistoryService* GetHistoryService() override;
@@ -74,6 +76,7 @@
 
  private:
   PrefService* const pref_service_;
+  syncer::ModelTypeStoreService* const model_type_store_service_;
   sync_sessions::SyncSessionsClient* const sync_sessions_client_;
   autofill::PersonalDataManager* const personal_data_manager_;
   const base::Callback<base::WeakPtr<syncer::SyncableService>(
@@ -91,6 +94,7 @@
 BundleSyncClient::BundleSyncClient(
     syncer::SyncApiComponentFactory* factory,
     PrefService* pref_service,
+    syncer::ModelTypeStoreService* model_type_store_service,
     sync_sessions::SyncSessionsClient* sync_sessions_client,
     autofill::PersonalDataManager* personal_data_manager,
     const base::Callback<base::WeakPtr<syncer::SyncableService>(
@@ -103,6 +107,7 @@
     history::HistoryService* history_service)
     : syncer::FakeSyncClient(factory),
       pref_service_(pref_service),
+      model_type_store_service_(model_type_store_service),
       sync_sessions_client_(sync_sessions_client),
       personal_data_manager_(personal_data_manager),
       get_syncable_service_callback_(get_syncable_service_callback),
@@ -168,6 +173,10 @@
   }
 }
 
+syncer::ModelTypeStoreService* BundleSyncClient::GetModelTypeStoreService() {
+  return model_type_store_service_;
+}
+
 history::HistoryService* BundleSyncClient::GetHistoryService() {
   if (history_service_)
     return history_service_;
@@ -230,9 +239,9 @@
 ProfileSyncServiceBundle::SyncClientBuilder::Build() {
   return std::make_unique<BundleSyncClient>(
       bundle_->component_factory(), bundle_->pref_service(),
-      bundle_->sync_sessions_client(), personal_data_manager_,
-      get_syncable_service_callback_, get_sync_service_callback_,
-      get_bookmark_model_callback_,
+      &bundle_->model_type_store_service_, bundle_->sync_sessions_client(),
+      personal_data_manager_, get_syncable_service_callback_,
+      get_sync_service_callback_, get_bookmark_model_callback_,
       activate_model_creation_ ? bundle_->db_thread() : nullptr,
       activate_model_creation_ ? base::SequencedTaskRunnerHandle::Get()
                                : nullptr,
@@ -282,17 +291,12 @@
   init_params.signin_scoped_device_id_callback =
       base::BindRepeating([]() { return std::string(); });
   init_params.network_time_update_callback = base::DoNothing();
-  if (!base_directory_.IsValid())
-    EXPECT_TRUE(base_directory_.CreateUniqueTempDir());
-  init_params.base_directory = base_directory_.GetPath();
   init_params.url_request_context = url_request_context();
   init_params.url_loader_factory =
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &test_url_loader_factory_);
   init_params.debug_identifier = "dummyDebugName";
   init_params.channel = version_info::Channel::UNKNOWN;
-  init_params.model_type_store_factory =
-      syncer::ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
 
   return init_params;
 }
diff --git a/components/browser_sync/profile_sync_test_util.h b/components/browser_sync/profile_sync_test_util.h
index 935d922..9bacf1f3 100644
--- a/components/browser_sync/profile_sync_test_util.h
+++ b/components/browser_sync/profile_sync_test_util.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/callback.h"
-#include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "components/browser_sync/profile_sync_service.h"
@@ -20,6 +19,7 @@
 #include "components/signin/core/browser/test_signin_client.h"
 #include "components/sync/driver/fake_sync_client.h"
 #include "components/sync/driver/sync_api_component_factory_mock.h"
+#include "components/sync/model/test_model_type_store_service.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/sync_sessions/mock_sync_sessions_client.h"
 #include "services/identity/public/cpp/identity_manager.h"
@@ -169,6 +169,7 @@
  private:
   scoped_refptr<base::SequencedTaskRunner> db_thread_;
   sync_preferences::TestingPrefServiceSyncable pref_service_;
+  syncer::TestModelTypeStoreService model_type_store_service_;
   TestSigninClient signin_client_;
   AccountTrackerService account_tracker_;
   FakeSigninManagerType signin_manager_;
@@ -185,7 +186,6 @@
   // once the rest of the sync engine is migrated to network service.
   scoped_refptr<net::URLRequestContextGetter> url_request_context_;
   network::TestURLLoaderFactory test_url_loader_factory_;
-  base::ScopedTempDir base_directory_;
 
   DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceBundle);
 };
diff --git a/components/browsing_data/core/history_notice_utils_unittest.cc b/components/browsing_data/core/history_notice_utils_unittest.cc
index 3088976..604f041 100644
--- a/components/browsing_data/core/history_notice_utils_unittest.cc
+++ b/components/browsing_data/core/history_notice_utils_unittest.cc
@@ -25,7 +25,16 @@
 class TestSyncService : public syncer::FakeSyncService {
  public:
   // Getters (FakeSyncService implementation). ---------------------------------
-  bool IsSyncActive() const override { return sync_active_; }
+  int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
+
+  State GetState() const override {
+    return IsFirstSetupComplete() ? State::ACTIVE
+                                  : State::PENDING_DESIRED_CONFIGURATION;
+  }
+
+  bool IsFirstSetupComplete() const override {
+    return is_first_setup_complete_;
+  }
 
   syncer::ModelTypeSet GetActiveDataTypes() const override {
     return active_data_types_;
@@ -36,8 +45,8 @@
   }
 
   // Setters. ------------------------------------------------------------------
-  void set_sync_active(bool active) {
-    sync_active_ = active;
+  void set_first_setup_complete(bool complete) {
+    is_first_setup_complete_ = complete;
   }
 
   void set_active_data_types(syncer::ModelTypeSet data_types) {
@@ -49,9 +58,9 @@
   }
 
  private:
+  bool is_first_setup_complete_ = false;
   syncer::ModelTypeSet active_data_types_;
   bool using_secondary_passphrase_ = false;
-  bool sync_active_ = false;
 };
 
 }  // namespace
@@ -107,7 +116,7 @@
 }
 
 TEST_F(HistoryNoticeUtilsTest, SyncingWithWrongParameters) {
-  sync_service()->set_sync_active(true);
+  sync_service()->set_first_setup_complete(true);
 
   // Regardless of the state of the web history...
   history_service()->SetWebAndAppActivityEnabled(true);
@@ -127,7 +136,7 @@
 
 TEST_F(HistoryNoticeUtilsTest, WebHistoryStates) {
   // If history Sync is active...
-  sync_service()->set_sync_active(true);
+  sync_service()->set_first_setup_complete(true);
   sync_service()->set_active_data_types(syncer::ModelTypeSet::All());
 
   // ...the result is true if both web history queries return true...
diff --git a/components/invalidation/impl/BUILD.gn b/components/invalidation/impl/BUILD.gn
index eeda43e9..092e48c1 100644
--- a/components/invalidation/impl/BUILD.gn
+++ b/components/invalidation/impl/BUILD.gn
@@ -65,6 +65,8 @@
 
   if (!is_android) {
     sources += [
+      "fcm_network_handler.cc",
+      "fcm_network_handler.h",
       "fcm_sync_invalidation_listener.cc",
       "fcm_sync_invalidation_listener.h",
       "fcm_sync_network_channel.cc",
@@ -154,6 +156,7 @@
     # Non-Android tests.
     sources += [
       "fake_invalidator_unittest.cc",
+      "fcm_network_handler_unittests.cc",
       "fcm_sync_invalidation_listener_unittest.cc",
       "gcm_invalidation_bridge_unittest.cc",
       "gcm_network_channel_unittest.cc",
@@ -180,6 +183,7 @@
       "//components/signin/core/browser:test_support",
       "//components/sync_preferences:test_support",
       "//google_apis:test_support",
+      "//google_apis/gcm:gcm",
       "//net",
       "//services/identity/public/cpp:test_support",
     ]
diff --git a/components/invalidation/impl/fcm_network_handler.cc b/components/invalidation/impl/fcm_network_handler.cc
new file mode 100644
index 0000000..1c62d1fe
--- /dev/null
+++ b/components/invalidation/impl/fcm_network_handler.cc
@@ -0,0 +1,132 @@
+// 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 "components/invalidation/impl/fcm_network_handler.h"
+
+#include "base/base64url.h"
+#include "base/callback.h"
+#include "base/observer_list.h"
+#include "base/task_scheduler/post_task.h"
+#include "build/build_config.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/gcm_driver/gcm_profile_service.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/gcm_driver/instance_id/instance_id_driver.h"
+#include "components/invalidation/public/invalidator_state.h"
+
+using instance_id::InstanceID;
+
+namespace syncer {
+
+namespace {
+
+const char kInvalidationsAppId[] = "com.google.chrome.fcm.invalidations";
+const char kInvalidationGCMSenderId[] = "8181035976";
+const char kContentKey[] = "data";
+
+// OAuth2 Scope passed to getToken to obtain GCM registration tokens.
+// Must match Java GoogleCloudMessaging.INSTANCE_ID_SCOPE.
+const char kGCMScope[] = "GCM";
+
+}  // namespace
+
+FCMNetworkHandler::FCMNetworkHandler(
+    gcm::GCMDriver* gcm_driver,
+    instance_id::InstanceIDDriver* instance_id_driver)
+    : gcm_driver_(gcm_driver),
+      instance_id_driver_(instance_id_driver),
+      weak_ptr_factory_(this) {}
+
+FCMNetworkHandler::~FCMNetworkHandler() {
+  StopListening();
+}
+
+void FCMNetworkHandler::StartListening() {
+  instance_id_driver_->GetInstanceID(kInvalidationsAppId)
+      ->GetToken(kInvalidationGCMSenderId, kGCMScope,
+                 /*options=*/std::map<std::string, std::string>(),
+                 base::BindRepeating(&FCMNetworkHandler::DidRetrieveToken,
+                                     weak_ptr_factory_.GetWeakPtr()));
+
+  gcm_driver_->AddAppHandler(kInvalidationsAppId, this);
+}
+
+void FCMNetworkHandler::StopListening() {
+  if (gcm_driver_->GetAppHandler(kInvalidationsAppId))
+    gcm_driver_->RemoveAppHandler(kInvalidationsAppId);
+}
+
+void FCMNetworkHandler::DidRetrieveToken(const std::string& subscription_token,
+                                         InstanceID::Result result) {
+  switch (result) {
+    case InstanceID::SUCCESS:
+      DeliverToken(subscription_token);
+      return;
+    case InstanceID::INVALID_PARAMETER:
+    case InstanceID::DISABLED:
+    case InstanceID::ASYNC_OPERATION_PENDING:
+    case InstanceID::SERVER_ERROR:
+    case InstanceID::UNKNOWN_ERROR:
+    case InstanceID::NETWORK_ERROR:
+      DLOG(WARNING) << "Messaging subscription failed; InstanceID::Result = "
+                    << result;
+      UpdateGcmChannelState(false);
+      break;
+  }
+}
+
+void FCMNetworkHandler::UpdateGcmChannelState(bool online) {
+  if (gcm_channel_online_ == online)
+    return;
+  gcm_channel_online_ = online;
+  NotifyChannelStateChange(gcm_channel_online_ ? INVALIDATIONS_ENABLED
+                                               : TRANSIENT_INVALIDATION_ERROR);
+}
+
+void FCMNetworkHandler::ShutdownHandler() {}
+
+void FCMNetworkHandler::OnStoreReset() {}
+
+void FCMNetworkHandler::OnMessage(const std::string& app_id,
+                                  const gcm::IncomingMessage& message) {
+  DCHECK_EQ(app_id, kInvalidationsAppId);
+  std::string content;
+  auto it = message.data.find(kContentKey);
+  if (it != message.data.end())
+    content = it->second;
+  if (content.empty()) {
+    return;
+  }
+
+  // TODO(melandory): check if content is empty and report.
+  // TODO(melandory): decode base64 and report in case it is invalid.
+  // TODO(melandory): parse proto and record histogram in case of invalid proto.
+  // TODO(melandory): report histogram in case of success.
+
+  UpdateGcmChannelState(true);
+  DeliverIncomingMessage(content);
+}
+
+void FCMNetworkHandler::OnMessagesDeleted(const std::string& app_id) {
+  // TODO(melandory): consider notifyint the client that messages were
+  // deleted. So the client can act on it, e.g. in case of sync request
+  // GetUpdates from the server.
+}
+
+void FCMNetworkHandler::OnSendError(
+    const std::string& app_id,
+    const gcm::GCMClient::SendErrorDetails& details) {
+  // Should never be called because we don't send GCM messages to
+  // the server.
+  NOTREACHED() << "FCMNetworkHandler doesn't send GCM messages.";
+}
+
+void FCMNetworkHandler::OnSendAcknowledged(const std::string& app_id,
+                                           const std::string& message_id) {
+  // Should never be called because we don't send GCM messages to
+  // the server.
+  NOTREACHED() << "FCMNetworkHandler doesn't send GCM messages.";
+}
+
+}  // namespace syncer
diff --git a/components/invalidation/impl/fcm_network_handler.h b/components/invalidation/impl/fcm_network_handler.h
new file mode 100644
index 0000000..227a5485
--- /dev/null
+++ b/components/invalidation/impl/fcm_network_handler.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 COMPONENTS_INVALIDATION_IMPL_FCM_NETWORK_HANDLER_H_
+#define COMPONENTS_INVALIDATION_IMPL_FCM_NETWORK_HANDLER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "components/gcm_driver/gcm_app_handler.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/invalidation/impl/fcm_sync_network_channel.h"
+
+namespace gcm {
+class GCMDriver;
+}
+
+namespace instance_id {
+class InstanceIDDriver;
+}
+
+namespace syncer {
+
+/*
+ * The class responsible for communication via GCM channel:
+ *  - It retrieves the token required for the subscription
+ *  and passes it by invoking token callback.
+ *  - It receives the messages and passes them to the
+ *  invalidation infrustructure, so they can be converted to the
+ *  invalidations and consumed by listeners.
+ */
+class FCMNetworkHandler : public gcm::GCMAppHandler,
+                          public FCMSyncNetworkChannel {
+ public:
+  FCMNetworkHandler(gcm::GCMDriver* gcm_driver,
+                    instance_id::InstanceIDDriver* instance_id_driver);
+
+  ~FCMNetworkHandler() override;
+
+  void StartListening();
+  void StopListening();
+  void UpdateGcmChannelState(bool);
+
+  // GCMAppHandler overrides.
+  void ShutdownHandler() override;
+  void OnStoreReset() override;
+  void OnMessage(const std::string& app_id,
+                 const gcm::IncomingMessage& message) override;
+  void OnMessagesDeleted(const std::string& app_id) override;
+  void OnSendError(const std::string& app_id,
+                   const gcm::GCMClient::SendErrorDetails& details) override;
+  void OnSendAcknowledged(const std::string& app_id,
+                          const std::string& message_id) override;
+
+ private:
+  // Called when a subscription token is obtained from the GCM server.
+  void DidRetrieveToken(const std::string& subscription_token,
+                        instance_id::InstanceID::Result result);
+
+  gcm::GCMDriver* const gcm_driver_;
+  instance_id::InstanceIDDriver* const instance_id_driver_;
+
+  bool gcm_channel_online_ = false;
+
+  base::WeakPtrFactory<FCMNetworkHandler> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(FCMNetworkHandler);
+};
+}  // namespace syncer
+
+#endif  // COMPONENTS_INVALIDATION_IMPL_FCM_NETWORK_HANDLER_H_
diff --git a/components/invalidation/impl/fcm_network_handler_unittests.cc b/components/invalidation/impl/fcm_network_handler_unittests.cc
new file mode 100644
index 0000000..7a5f6917
--- /dev/null
+++ b/components/invalidation/impl/fcm_network_handler_unittests.cc
@@ -0,0 +1,254 @@
+// 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 "components/invalidation/impl/fcm_network_handler.h"
+
+#include <memory>
+#include <string>
+
+#include "base/bind_helpers.h"
+#include "base/files/file_path.h"
+#include "base/message_loop/message_loop.h"
+#include "base/test/mock_callback.h"
+#include "build/build_config.h"
+#include "components/gcm_driver/gcm_driver.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
+#include "components/gcm_driver/instance_id/instance_id_driver.h"
+#include "google_apis/gcm/engine/account_mapping.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using gcm::InstanceIDHandler;
+using instance_id::InstanceID;
+using instance_id::InstanceIDDriver;
+using testing::_;
+using testing::StrictMock;
+
+namespace syncer {
+
+namespace {
+
+const char kInvalidationsAppId[] = "com.google.chrome.fcm.invalidations";
+using DataCallback = base::RepeatingCallback<void(const std::string& message)>;
+
+class MockInstanceID : public InstanceID {
+ public:
+  MockInstanceID() : InstanceID("app_id", /*gcm_driver=*/nullptr) {}
+  ~MockInstanceID() override = default;
+
+  MOCK_METHOD1(GetID, void(const GetIDCallback& callback));
+  MOCK_METHOD1(GetCreationTime, void(const GetCreationTimeCallback& callback));
+  MOCK_METHOD4(GetToken,
+               void(const std::string& authorized_entity,
+                    const std::string& scope,
+                    const std::map<std::string, std::string>& options,
+                    const GetTokenCallback& callback));
+  MOCK_METHOD4(ValidateToken,
+               void(const std::string& authorized_entity,
+                    const std::string& scope,
+                    const std::string& token,
+                    const ValidateTokenCallback& callback));
+
+ protected:
+  MOCK_METHOD3(DeleteTokenImpl,
+               void(const std::string& authorized_entity,
+                    const std::string& scope,
+                    const DeleteTokenCallback& callback));
+  MOCK_METHOD1(DeleteIDImpl, void(const DeleteIDCallback& callback));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockInstanceID);
+};
+
+class MockGCMDriver : public gcm::GCMDriver {
+ public:
+  MockGCMDriver()
+      : GCMDriver(/*store_path=*/base::FilePath(),
+                  /*blocking_task_runner=*/nullptr) {}
+  ~MockGCMDriver() override = default;
+
+  MOCK_METHOD4(ValidateRegistration,
+               void(const std::string& app_id,
+                    const std::vector<std::string>& sender_ids,
+                    const std::string& registration_id,
+                    const ValidateRegistrationCallback& callback));
+  MOCK_METHOD0(OnSignedIn, void());
+  MOCK_METHOD0(OnSignedOut, void());
+  MOCK_METHOD1(AddConnectionObserver,
+               void(gcm::GCMConnectionObserver* observer));
+  MOCK_METHOD1(RemoveConnectionObserver,
+               void(gcm::GCMConnectionObserver* observer));
+  MOCK_METHOD0(Enable, void());
+  MOCK_METHOD0(Disable, void());
+  MOCK_CONST_METHOD0(GetGCMClientForTesting, gcm::GCMClient*());
+  MOCK_CONST_METHOD0(IsStarted, bool());
+  MOCK_CONST_METHOD0(IsConnected, bool());
+  MOCK_METHOD2(GetGCMStatistics,
+               void(const GetGCMStatisticsCallback& callback,
+                    ClearActivityLogs clear_logs));
+  MOCK_METHOD2(SetGCMRecording,
+               void(const GetGCMStatisticsCallback& callback, bool recording));
+  MOCK_METHOD1(SetAccountTokens,
+               void(const std::vector<gcm::GCMClient::AccountTokenInfo>&
+                        account_tokens));
+  MOCK_METHOD1(UpdateAccountMapping,
+               void(const gcm::AccountMapping& account_mapping));
+  MOCK_METHOD1(RemoveAccountMapping, void(const std::string& account_id));
+  MOCK_METHOD0(GetLastTokenFetchTime, base::Time());
+  MOCK_METHOD1(SetLastTokenFetchTime, void(const base::Time& time));
+  MOCK_METHOD1(WakeFromSuspendForHeartbeat, void(bool wake));
+  MOCK_METHOD0(GetInstanceIDHandlerInternal, InstanceIDHandler*());
+  MOCK_METHOD2(AddHeartbeatInterval,
+               void(const std::string& scope, int interval_ms));
+  MOCK_METHOD1(RemoveHeartbeatInterval, void(const std::string& scope));
+
+ protected:
+  MOCK_METHOD1(EnsureStarted,
+               gcm::GCMClient::Result(gcm::GCMClient::StartMode start_mode));
+  MOCK_METHOD2(RegisterImpl,
+               void(const std::string& app_id,
+                    const std::vector<std::string>& sender_ids));
+  MOCK_METHOD1(UnregisterImpl, void(const std::string& app_id));
+  MOCK_METHOD3(SendImpl,
+               void(const std::string& app_id,
+                    const std::string& receiver_id,
+                    const gcm::OutgoingMessage& message));
+  MOCK_METHOD2(RecordDecryptionFailure,
+               void(const std::string& app_id,
+                    gcm::GCMDecryptionResult result));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockGCMDriver);
+};
+
+class MockInstanceIDDriver : public InstanceIDDriver {
+ public:
+  MockInstanceIDDriver() : InstanceIDDriver(/*gcm_driver=*/nullptr){};
+  ~MockInstanceIDDriver() override = default;
+
+  MOCK_METHOD1(GetInstanceID, InstanceID*(const std::string& app_id));
+  MOCK_METHOD1(RemoveInstanceID, void(const std::string& app_id));
+  MOCK_CONST_METHOD1(ExistsInstanceID, bool(const std::string& app_id));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockInstanceIDDriver);
+};
+
+class MockOnDataCallback {
+ public:
+  // Workaround for gMock's lack of support for movable-only arguments.
+  void WrappedRun(const std::string& token) { Run(token); }
+
+  DataCallback Get() {
+    return base::BindRepeating(&MockOnDataCallback::WrappedRun,
+                               base::Unretained(this));
+  }
+
+  MOCK_METHOD1(Run, void(const std::string&));
+};
+
+ACTION_TEMPLATE(InvokeCallbackArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_1_VALUE_PARAMS(p0)) {
+  std::get<k>(args).Run(p0);
+}
+
+ACTION_TEMPLATE(InvokeCallbackArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_2_VALUE_PARAMS(p0, p1)) {
+  std::get<k>(args).Run(p0, p1);
+}
+
+}  // namespace
+
+class FCMNetworkHandlerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    // Our app handler obtains InstanceID through InstanceIDDriver. We mock
+    // InstanceIDDriver and return MockInstanceID through it.
+    mock_instance_id_driver_ =
+        std::make_unique<StrictMock<MockInstanceIDDriver>>();
+    mock_instance_id_ = std::make_unique<StrictMock<MockInstanceID>>();
+    mock_gcm_driver_ = std::make_unique<StrictMock<MockGCMDriver>>();
+
+    // This is called in FCMNetworkHandler.
+    EXPECT_CALL(*mock_instance_id_driver_, GetInstanceID(kInvalidationsAppId))
+        .WillRepeatedly(Return(mock_instance_id_.get()));
+  }
+
+  std::unique_ptr<FCMNetworkHandler> MakeHandler() {
+    return std::make_unique<FCMNetworkHandler>(mock_gcm_driver_.get(),
+                                               mock_instance_id_driver_.get());
+  }
+
+  StrictMock<MockInstanceID>* mock_instance_id() {
+    return mock_instance_id_.get();
+  }
+
+ private:
+  base::MessageLoop message_loop_;
+  std::unique_ptr<StrictMock<MockGCMDriver>> mock_gcm_driver_;
+  std::unique_ptr<StrictMock<MockInstanceIDDriver>> mock_instance_id_driver_;
+  std::unique_ptr<StrictMock<MockInstanceID>> mock_instance_id_;
+};
+
+TEST_F(FCMNetworkHandlerTest, ShouldPassTheTokenOnceRecieved) {
+  std::unique_ptr<FCMNetworkHandler> handler = MakeHandler();
+
+  MockOnDataCallback mock_on_token_callback;
+  handler->SetTokenReceiver(mock_on_token_callback.Get());
+
+  // Check that the handler gets the token through GetToken.
+  EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
+      .WillOnce(
+          InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
+  EXPECT_CALL(mock_on_token_callback, Run("token")).Times(1);
+  handler->StartListening();
+}
+
+TEST_F(FCMNetworkHandlerTest, ShouldPassTheTokenOnceSubscribed) {
+  std::unique_ptr<FCMNetworkHandler> handler = MakeHandler();
+
+  MockOnDataCallback mock_on_token_callback;
+
+  // Check that the handler gets the token through GetToken.
+  EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
+      .WillOnce(
+          InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
+  EXPECT_CALL(mock_on_token_callback, Run(_)).Times(0);
+  handler->StartListening();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_CALL(mock_on_token_callback, Run("token")).Times(1);
+  handler->SetTokenReceiver(mock_on_token_callback.Get());
+}
+
+TEST_F(FCMNetworkHandlerTest, ShouldNotInvokeMessageCallbackOnEmptyMessage) {
+  MockOnDataCallback mock_on_message_callback;
+
+  std::unique_ptr<FCMNetworkHandler> handler = MakeHandler();
+  EXPECT_CALL(mock_on_message_callback, Run(_)).Times(0);
+  handler->SetMessageReceiver(mock_on_message_callback.Get());
+  EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
+      .WillOnce(
+          InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
+
+  handler->StartListening();
+  handler->OnMessage(kInvalidationsAppId, gcm::IncomingMessage());
+}
+
+TEST_F(FCMNetworkHandlerTest, ShouldInvokeMessageCallbackOnValidMessage) {
+  MockOnDataCallback mock_on_message_callback;
+  gcm::IncomingMessage message;
+  message.data["data"] = "test";
+
+  std::unique_ptr<FCMNetworkHandler> handler = MakeHandler();
+  EXPECT_CALL(*mock_instance_id(), GetToken(_, _, _, _))
+      .WillOnce(
+          InvokeCallbackArgument<3>("token", InstanceID::Result::SUCCESS));
+  handler->StartListening();
+  EXPECT_CALL(mock_on_message_callback, Run("test")).Times(0);
+  handler->SetMessageReceiver(mock_on_message_callback.Get());
+  handler->OnMessage(kInvalidationsAppId, gcm::IncomingMessage());
+}
+
+}  // namespace syncer
diff --git a/components/ntp_snippets/remote/test_utils.cc b/components/ntp_snippets/remote/test_utils.cc
index 3abda28..2e734b4 100644
--- a/components/ntp_snippets/remote/test_utils.cc
+++ b/components/ntp_snippets/remote/test_utils.cc
@@ -14,8 +14,7 @@
 namespace test {
 
 FakeSyncService::FakeSyncService()
-    : is_sync_active_(true),
-      is_encrypt_everything_enabled_(false),
+    : is_encrypt_everything_enabled_(false),
       active_data_types_(syncer::HISTORY_DELETE_DIRECTIVES) {}
 
 FakeSyncService::~FakeSyncService() = default;
@@ -24,10 +23,6 @@
   return DISABLE_REASON_NONE;
 }
 
-bool FakeSyncService::IsSyncActive() const {
-  return is_sync_active_;
-}
-
 bool FakeSyncService::IsEncryptEverythingEnabled() const {
   return is_encrypt_everything_enabled_;
 }
diff --git a/components/ntp_snippets/remote/test_utils.h b/components/ntp_snippets/remote/test_utils.h
index 778331f74..afa25329 100644
--- a/components/ntp_snippets/remote/test_utils.h
+++ b/components/ntp_snippets/remote/test_utils.h
@@ -25,11 +25,9 @@
   ~FakeSyncService() override;
 
   int GetDisableReasons() const override;
-  bool IsSyncActive() const override;
   bool IsEncryptEverythingEnabled() const override;
   syncer::ModelTypeSet GetActiveDataTypes() const override;
 
-  bool is_sync_active_;
   bool is_encrypt_everything_enabled_;
   syncer::ModelTypeSet active_data_types_;
 };
diff --git a/components/omnibox/browser/document_provider.cc b/components/omnibox/browser/document_provider.cc
index 7a3c2f41..8e3379d6 100644
--- a/components/omnibox/browser/document_provider.cc
+++ b/components/omnibox/browser/document_provider.cc
@@ -23,7 +23,9 @@
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/document_suggestions_service.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/omnibox_pref_names.h"
 #include "components/omnibox/browser/search_provider.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/search_engine_type.h"
 #include "components/search_engines/template_url_service.h"
@@ -56,6 +58,12 @@
   return new DocumentProvider(client, listener);
 }
 
+// static
+void DocumentProvider::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  registry->RegisterBooleanPref(omnibox::kDocumentSuggestEnabled, true);
+}
+
 bool DocumentProvider::IsDocumentProviderAllowed(
     PrefService* prefs,
     bool is_incognito,
@@ -65,6 +73,10 @@
   if (!base::FeatureList::IsEnabled(omnibox::kDocumentProvider))
     return false;
 
+  // Client-side toggle must be enabled.
+  if (!prefs->GetBoolean(omnibox::kDocumentSuggestEnabled))
+    return false;
+
   // No incognito.
   if (is_incognito)
     return false;
diff --git a/components/omnibox/browser/document_provider.h b/components/omnibox/browser/document_provider.h
index 42cc5cf0..cbfcfd1 100644
--- a/components/omnibox/browser/document_provider.h
+++ b/components/omnibox/browser/document_provider.h
@@ -32,6 +32,10 @@
 class SimpleURLLoader;
 }
 
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
 // Autocomplete provider for personalized documents owned or readable by the
 // signed-in user. In practice this is a second request in parallel with that
 // to the default search provider.
@@ -47,9 +51,17 @@
   void DeleteMatch(const AutocompleteMatch& match) override;
   void AddProviderInfo(ProvidersInfo* provider_info) const override;
 
+  // Registers a client-side preference to enable document suggestions.
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest, CheckFeatureBehindFlag);
-  FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest, CheckFeaturePrerequisites);
+  FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest,
+                           CheckFeaturePrerequisiteNoIncognito);
+  FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest,
+                           CheckFeaturePrerequisiteClientSettingOff);
+  FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest,
+                           CheckFeaturePrerequisiteDefaultSearch);
   FRIEND_TEST_ALL_PREFIXES(DocumentProviderTest, ParseDocumentSearchResults);
   DocumentProvider(AutocompleteProviderClient* client,
                    AutocompleteProviderListener* listener);
diff --git a/components/omnibox/browser/document_provider_unittest.cc b/components/omnibox/browser/document_provider_unittest.cc
index 21a77c0..0c6df69 100644
--- a/components/omnibox/browser/document_provider_unittest.cc
+++ b/components/omnibox/browser/document_provider_unittest.cc
@@ -12,6 +12,8 @@
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/mock_autocomplete_provider_client.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/omnibox_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -20,7 +22,10 @@
 class FakeAutocompleteProviderClient : public MockAutocompleteProviderClient {
  public:
   FakeAutocompleteProviderClient()
-      : template_url_service_(new TemplateURLService(nullptr, 0)) {}
+      : template_url_service_(new TemplateURLService(nullptr, 0)) {
+    pref_service_.registry()->RegisterBooleanPref(
+        omnibox::kDocumentSuggestEnabled, true);
+  }
 
   bool SearchSuggestEnabled() const override { return true; }
 
@@ -96,26 +101,56 @@
       fake_prefs, is_incognito, is_authenticated, template_url_service));
 }
 
-TEST_F(DocumentProviderTest, CheckFeaturePrerequisites) {
+TEST_F(DocumentProviderTest, CheckFeaturePrerequisiteNoIncognito) {
   PrefService* fake_prefs = client_->GetPrefs();
   TemplateURLService* template_url_service = client_->GetTemplateURLService();
-
-  // Make sure feature is turned on when prereqs are met, then turn them off
-  // one at a time and ensure each defeats the feature.
   bool is_incognito = false;
   bool is_authenticated = true;
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(omnibox::kDocumentProvider);
+
+  // Feature starts enabled.
   EXPECT_TRUE(provider_->IsDocumentProviderAllowed(
       fake_prefs, is_incognito, is_authenticated, template_url_service));
 
-  // Don't allow in incognito mode.
+  // Feature should be disabled in incognito.
   is_incognito = true;
   EXPECT_FALSE(provider_->IsDocumentProviderAllowed(
       fake_prefs, is_incognito, is_authenticated, template_url_service));
-  is_incognito = false;
+}
 
-  // Don't allow if Google is not DSE.
+TEST_F(DocumentProviderTest, CheckFeaturePrerequisiteClientSettingOff) {
+  PrefService* fake_prefs = client_->GetPrefs();
+  TemplateURLService* template_url_service = client_->GetTemplateURLService();
+  bool is_incognito = false;
+  bool is_authenticated = true;
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(omnibox::kDocumentProvider);
+
+  // Feature starts enabled.
+  EXPECT_TRUE(provider_->IsDocumentProviderAllowed(
+      fake_prefs, is_incognito, is_authenticated, template_url_service));
+
+  // Disabling toggle in chrome://settings should be respected.
+  fake_prefs->SetBoolean(omnibox::kDocumentSuggestEnabled, false);
+  EXPECT_FALSE(provider_->IsDocumentProviderAllowed(
+      fake_prefs, is_incognito, is_authenticated, template_url_service));
+  fake_prefs->SetBoolean(omnibox::kDocumentSuggestEnabled, true);
+}
+
+TEST_F(DocumentProviderTest, CheckFeaturePrerequisiteDefaultSearch) {
+  PrefService* fake_prefs = client_->GetPrefs();
+  TemplateURLService* template_url_service = client_->GetTemplateURLService();
+  bool is_incognito = false;
+  bool is_authenticated = true;
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(omnibox::kDocumentProvider);
+
+  // Feature starts enabled.
+  EXPECT_TRUE(provider_->IsDocumentProviderAllowed(
+      fake_prefs, is_incognito, is_authenticated, template_url_service));
+
+  // Switching default search disables it.
   TemplateURLData data;
   data.SetShortName(base::ASCIIToUTF16("t"));
   data.SetURL("https://www.notgoogle.com/?q={searchTerms}");
@@ -129,10 +164,6 @@
   template_url_service->SetUserSelectedDefaultSearchProvider(
       default_template_url_);
   template_url_service->Remove(new_default_provider);
-
-  // Prereqs are met again; verify we're able to get suggestions.
-  EXPECT_TRUE(provider_->IsDocumentProviderAllowed(
-      fake_prefs, is_incognito, is_authenticated, template_url_service));
 }
 
 TEST_F(DocumentProviderTest, ParseDocumentSearchResults) {
diff --git a/components/omnibox/browser/omnibox_pref_names.cc b/components/omnibox/browser/omnibox_pref_names.cc
index 3d3a5495..a29f2cb 100644
--- a/components/omnibox/browser/omnibox_pref_names.cc
+++ b/components/omnibox/browser/omnibox_pref_names.cc
@@ -6,6 +6,10 @@
 
 namespace omnibox {
 
+// A client-side toggle for document (Drive) suggestions.
+// Also gated by a feature and server-side Admin Panel controls.
+const char kDocumentSuggestEnabled[] = "documentsuggest.enabled";
+
 // A cache of zero suggest results using JSON serialized into a string.
 const char kZeroSuggestCachedResults[] = "zerosuggest.cachedresults";
 
diff --git a/components/omnibox/browser/omnibox_pref_names.h b/components/omnibox/browser/omnibox_pref_names.h
index 1f836a0..6a3ad7d 100644
--- a/components/omnibox/browser/omnibox_pref_names.h
+++ b/components/omnibox/browser/omnibox_pref_names.h
@@ -10,6 +10,7 @@
 // Alphabetical list of preference names specific to the omnibox component.
 // Keep alphabetized, and document each in the .cc file.
 
+extern const char kDocumentSuggestEnabled[];
 extern const char kZeroSuggestCachedResults[];
 
 }  // namespace omnibox
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc
index f03a0d1..e900a5a 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -18,6 +18,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "components/autofill/core/common/autofill_regex_constants.h"
 #include "components/autofill/core/common/autofill_regexes.h"
 #include "components/autofill/core/common/form_data.h"
@@ -27,6 +28,7 @@
 using autofill::FieldPropertiesFlags;
 using autofill::FormFieldData;
 using autofill::PasswordForm;
+using base::string16;
 
 namespace password_manager {
 
@@ -497,33 +499,45 @@
   return;
 }
 
+string16 GetPlatformSpecificIdentifier(const FormFieldData& field) {
+#if defined(OS_IOS)
+  return field.id;
+#else
+  return field.name;
+#endif
+}
+
 // Set username and password fields in |password_form| based on
 // |significant_fields| .
 void SetFields(const SignificantFields& significant_fields,
                PasswordForm* password_form) {
   password_form->has_renderer_ids = true;
   if (significant_fields.username) {
-    password_form->username_element = significant_fields.username->name;
+    password_form->username_element =
+        GetPlatformSpecificIdentifier(*significant_fields.username);
     password_form->username_value = significant_fields.username->value;
     password_form->username_element_renderer_id =
         significant_fields.username->unique_renderer_id;
   }
 
   if (significant_fields.password) {
-    password_form->password_element = significant_fields.password->name;
+    password_form->password_element =
+        GetPlatformSpecificIdentifier(*significant_fields.password);
     password_form->password_value = significant_fields.password->value;
     password_form->password_element_renderer_id =
         significant_fields.password->unique_renderer_id;
   }
 
   if (significant_fields.new_password) {
-    password_form->new_password_element = significant_fields.new_password->name;
+    password_form->new_password_element =
+        GetPlatformSpecificIdentifier(*significant_fields.new_password);
     password_form->new_password_value = significant_fields.new_password->value;
   }
 
   if (significant_fields.confirmation_password) {
     password_form->confirmation_password_element =
-        significant_fields.confirmation_password->name;
+        GetPlatformSpecificIdentifier(
+            *significant_fields.confirmation_password);
   }
 }
 
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
index 14051456..90938af9 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/string16.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/password_form.h"
@@ -241,7 +242,15 @@
                                });
   ASSERT_TRUE(field_it != fields.end())
       << "Could not find a field with renderer ID " << renderer_id;
+
+// On iOS |id| is used for identifying DOM elements, so the parser should return
+// it.
+#if defined(OS_IOS)
+  EXPECT_EQ(element_name, field_it->id);
+#else
   EXPECT_EQ(element_name, field_it->name);
+#endif
+
   if (element_value)
     EXPECT_EQ(*element_value, field_it->value);
 }
diff --git a/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index 8e8cbb8..9a74df6 100644
--- a/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -62,11 +62,13 @@
     observed_form_.fields.push_back(field);
 
     field.name = ASCIIToUTF16("username");
+    field.id = field.name;
     field.form_control_type = "text";
     field.unique_renderer_id = 2;
     observed_form_.fields.push_back(field);
 
     field.name = ASCIIToUTF16("password");
+    field.id = field.name;
     field.form_control_type = "password";
     field.unique_renderer_id = 3;
     observed_form_.fields.push_back(field);
diff --git a/components/password_manager/core/browser/password_bubble_experiment_unittest.cc b/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
index 08db7dc..5ed13958 100644
--- a/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
+++ b/components/password_manager/core/browser/password_bubble_experiment_unittest.cc
@@ -27,12 +27,13 @@
   // FakeSyncService overrides.
   int GetDisableReasons() const override { return disable_reasons_; }
 
-  bool IsFirstSetupComplete() const override {
-    return is_first_setup_complete_;
+  State GetState() const override {
+    return IsFirstSetupComplete() ? State::ACTIVE
+                                  : State::PENDING_DESIRED_CONFIGURATION;
   }
 
-  bool IsSyncActive() const override {
-    return IsSyncAllowed() && is_first_setup_complete_;
+  bool IsFirstSetupComplete() const override {
+    return is_first_setup_complete_;
   }
 
   syncer::ModelTypeSet GetActiveDataTypes() const override { return type_set_; }
diff --git a/components/signin/core/browser/fake_signin_manager.cc b/components/signin/core/browser/fake_signin_manager.cc
index a59df220..d73df4f 100644
--- a/components/signin/core/browser/fake_signin_manager.cc
+++ b/components/signin/core/browser/fake_signin_manager.cc
@@ -75,7 +75,7 @@
 }
 
 void FakeSigninManager::ForceSignOut() {
-  prohibit_signout_ = false;
+  ProhibitSignout(false);
   SignOut(signin_metrics::SIGNOUT_TEST,
           signin_metrics::SignoutDelete::IGNORE_METRIC);
 }
diff --git a/components/signin/core/browser/signin_manager.cc b/components/signin/core/browser/signin_manager.cc
index 43a30e54..ffbfa714 100644
--- a/components/signin/core/browser/signin_manager.cc
+++ b/components/signin/core/browser/signin_manager.cc
@@ -214,7 +214,7 @@
     return;
   }
 
-  if (prohibit_signout_) {
+  if (IsSignoutProhibited()) {
     DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
     return;
   }
diff --git a/components/subresource_filter/core/browser/subresource_filter_features.cc b/components/subresource_filter/core/browser/subresource_filter_features.cc
index 621f12d1..d6989ab 100644
--- a/components/subresource_filter/core/browser/subresource_filter_features.cc
+++ b/components/subresource_filter/core/browser/subresource_filter_features.cc
@@ -232,7 +232,7 @@
     "SubresourceFilter", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSafeBrowsingSubresourceFilterConsiderRedirects{
-    "SubresourceFilterConsiderRedirects", base::FEATURE_ENABLED_BY_DEFAULT};
+    "SubresourceFilterConsiderRedirects", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Legacy name `activation_state` is used in variation parameters.
 const char kActivationLevelParameterName[] = "activation_state";
diff --git a/components/suggestions/suggestions_service_impl_unittest.cc b/components/suggestions/suggestions_service_impl_unittest.cc
index f897ff6c..a89a3ff 100644
--- a/components/suggestions/suggestions_service_impl_unittest.cc
+++ b/components/suggestions/suggestions_service_impl_unittest.cc
@@ -88,7 +88,6 @@
   MOCK_CONST_METHOD0(GetDisableReasons, int());
   MOCK_CONST_METHOD0(IsEngineInitialized, bool());
   MOCK_CONST_METHOD0(IsFirstSetupComplete, bool());
-  MOCK_CONST_METHOD0(IsSyncActive, bool());
   MOCK_CONST_METHOD0(ConfigurationDone, bool());
   MOCK_CONST_METHOD0(IsLocalSyncEnabled, bool());
   MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool());
@@ -164,9 +163,6 @@
     EXPECT_CALL(*sync_service(), IsFirstSetupComplete())
         .Times(AnyNumber())
         .WillRepeatedly(Return(true));
-    EXPECT_CALL(*sync_service(), IsSyncActive())
-        .Times(AnyNumber())
-        .WillRepeatedly(Return(true));
     EXPECT_CALL(*sync_service(), ConfigurationDone())
         .Times(AnyNumber())
         .WillRepeatedly(Return(true));
@@ -354,7 +350,6 @@
   // The sync service starts out inactive.
   EXPECT_CALL(*sync_service(), IsEngineInitialized())
       .WillRepeatedly(Return(false));
-  EXPECT_CALL(*sync_service(), IsSyncActive()).WillRepeatedly(Return(false));
   static_cast<SyncServiceObserver*>(suggestions_service())
       ->OnStateChanged(sync_service());
 
@@ -364,7 +359,6 @@
   // Sync getting enabled should not result in a fetch.
   EXPECT_CALL(*sync_service(), IsEngineInitialized())
       .WillRepeatedly(Return(true));
-  EXPECT_CALL(*sync_service(), IsSyncActive()).WillRepeatedly(Return(true));
   static_cast<SyncServiceObserver*>(suggestions_service())
       ->OnStateChanged(sync_service());
 
@@ -399,7 +393,6 @@
 TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) {
   EXPECT_CALL(*sync_service(), IsEngineInitialized())
       .WillRepeatedly(Return(false));
-  EXPECT_CALL(*sync_service(), IsSyncActive()).WillRepeatedly(Return(false));
   static_cast<SyncServiceObserver*>(suggestions_service())
       ->OnStateChanged(sync_service());
 
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index c30c9c8a..3a7820e 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -405,10 +405,10 @@
     "model/model_error.h",
     "model/model_type_change_processor.cc",
     "model/model_type_change_processor.h",
-    "model/model_type_store.cc",
     "model/model_type_store.h",
     "model/model_type_store_base.cc",
     "model/model_type_store_base.h",
+    "model/model_type_store_service.h",
     "model/model_type_sync_bridge.cc",
     "model/model_type_sync_bridge.h",
     "model/mutable_data_batch.cc",
@@ -440,6 +440,8 @@
     "model_impl/model_type_store_backend.h",
     "model_impl/model_type_store_impl.cc",
     "model_impl/model_type_store_impl.h",
+    "model_impl/model_type_store_service_impl.cc",
+    "model_impl/model_type_store_service_impl.h",
     "model_impl/processor_entity_tracker.cc",
     "model_impl/processor_entity_tracker.h",
     "model_impl/sync_metadata_store_change_list.cc",
@@ -550,6 +552,7 @@
     "//base",
     "//components/sync/protocol",
     "//net",
+    "//third_party/leveldatabase",
     "//url",
   ]
   deps = [
@@ -574,7 +577,6 @@
     "//sql",
     "//third_party/cacheinvalidation",
     "//third_party/crc32c",
-    "//third_party/leveldatabase",
     "//third_party/zlib",
     "//third_party/zlib/google:compression_utils",
     "//ui/base",
@@ -745,6 +747,8 @@
     "model/sync_change_processor_wrapper_for_test.h",
     "model/sync_error_factory_mock.cc",
     "model/sync_error_factory_mock.h",
+    "model/test_model_type_store_service.cc",
+    "model/test_model_type_store_service.h",
   ]
 
   defines = [ "SYNC_TEST" ]
diff --git a/components/sync/driver/fake_sync_client.cc b/components/sync/driver/fake_sync_client.cc
index cd9be849..321b253 100644
--- a/components/sync/driver/fake_sync_client.cc
+++ b/components/sync/driver/fake_sync_client.cc
@@ -45,6 +45,10 @@
   return base::FilePath();
 }
 
+ModelTypeStoreService* FakeSyncClient::GetModelTypeStoreService() {
+  return nullptr;
+}
+
 bookmarks::BookmarkModel* FakeSyncClient::GetBookmarkModel() {
   return nullptr;
 }
diff --git a/components/sync/driver/fake_sync_client.h b/components/sync/driver/fake_sync_client.h
index 3f1c760..9484f3c 100644
--- a/components/sync/driver/fake_sync_client.h
+++ b/components/sync/driver/fake_sync_client.h
@@ -29,6 +29,7 @@
   SyncService* GetSyncService() override;
   PrefService* GetPrefService() override;
   base::FilePath GetLocalSyncBackendFolder() override;
+  ModelTypeStoreService* GetModelTypeStoreService() override;
   bookmarks::BookmarkModel* GetBookmarkModel() override;
   favicon::FaviconService* GetFaviconService() override;
   history::HistoryService* GetHistoryService() override;
diff --git a/components/sync/driver/fake_sync_service.cc b/components/sync/driver/fake_sync_service.cc
index 0710e54..439459d 100644
--- a/components/sync/driver/fake_sync_service.cc
+++ b/components/sync/driver/fake_sync_service.cc
@@ -57,7 +57,6 @@
   if (!IsFirstSetupComplete()) {
     return State::PENDING_DESIRED_CONFIGURATION;
   }
-  DCHECK(IsSyncActive());
   if (!configuration_done_) {
     return State::CONFIGURING;
   }
@@ -68,10 +67,6 @@
   return false;
 }
 
-bool FakeSyncService::IsSyncActive() const {
-  return false;
-}
-
 bool FakeSyncService::IsLocalSyncEnabled() const {
   return false;
 }
diff --git a/components/sync/driver/fake_sync_service.h b/components/sync/driver/fake_sync_service.h
index fdd1b0e1..3357f69 100644
--- a/components/sync/driver/fake_sync_service.h
+++ b/components/sync/driver/fake_sync_service.h
@@ -42,7 +42,6 @@
   int GetDisableReasons() const override;
   State GetState() const override;
   bool IsFirstSetupComplete() const override;
-  bool IsSyncActive() const override;
   bool IsLocalSyncEnabled() const override;
   void TriggerRefresh(const ModelTypeSet& types) override;
   ModelTypeSet GetActiveDataTypes() const override;
diff --git a/components/sync/driver/sync_client.h b/components/sync/driver/sync_client.h
index 3d2ff8bd..6271acb 100644
--- a/components/sync/driver/sync_client.h
+++ b/components/sync/driver/sync_client.h
@@ -45,6 +45,7 @@
 namespace syncer {
 
 class LocalDeviceInfoProvider;
+class ModelTypeStoreService;
 class SyncService;
 class SyncableService;
 
@@ -68,6 +69,8 @@
   // Returns the current profile's preference service.
   virtual PrefService* GetPrefService() = 0;
 
+  virtual ModelTypeStoreService* GetModelTypeStoreService() = 0;
+
   // Returns the path to the folder used for storing the local sync database.
   // It is only used when sync is running against a local backend.
   virtual base::FilePath GetLocalSyncBackendFolder() = 0;
diff --git a/components/sync/driver/sync_client_mock.h b/components/sync/driver/sync_client_mock.h
index 67c32fb..f995ee3a 100644
--- a/components/sync/driver/sync_client_mock.h
+++ b/components/sync/driver/sync_client_mock.h
@@ -20,6 +20,7 @@
   MOCK_METHOD0(GetSyncService, SyncService*());
   MOCK_METHOD0(GetPrefService, PrefService*());
   MOCK_METHOD0(GetLocalSyncBackendFolder, base::FilePath());
+  MOCK_METHOD0(GetModelTypeStoreService, syncer::ModelTypeStoreService*());
   MOCK_METHOD0(GetBookmarkModel, bookmarks::BookmarkModel*());
   MOCK_METHOD0(GetFaviconService, favicon::FaviconService*());
   MOCK_METHOD0(GetHistoryService, history::HistoryService*());
diff --git a/components/sync/driver/sync_service.cc b/components/sync/driver/sync_service.cc
index cb0573c..cb251d3 100644
--- a/components/sync/driver/sync_service.cc
+++ b/components/sync/driver/sync_service.cc
@@ -22,6 +22,11 @@
          !HasDisableReason(DISABLE_REASON_ENTERPRISE_POLICY);
 }
 
+bool SyncService::IsSyncActive() const {
+  State state = GetState();
+  return state == State::CONFIGURING || state == State::ACTIVE;
+}
+
 bool SyncService::HasUnrecoverableError() const {
   return HasDisableReason(DISABLE_REASON_UNRECOVERABLE_ERROR);
 }
diff --git a/components/sync/driver/sync_service.h b/components/sync/driver/sync_service.h
index 9ebd74c..d70be88 100644
--- a/components/sync/driver/sync_service.h
+++ b/components/sync/driver/sync_service.h
@@ -183,12 +183,10 @@
   // !HasDisableReason(DISABLE_REASON_ENTERPRISE_POLICY)".
   bool IsSyncAllowed() const;
 
-  // Returns true if sync is fully initialized and active. This implies that
-  // an initial configuration has successfully completed, although there may
-  // be datatype specific, auth, or other transient errors. To see which
-  // datatypes are actually syncing, see GetActiveDataTypes() below.
-  // DEPRECATED! Use GetState instead.
-  virtual bool IsSyncActive() const = 0;
+  // DEPRECATED! Use GetState instead. Equivalent to
+  // "GetState() == State::CONFIGURING || GetState() == State::ACTIVE".
+  // To see which datatypes are actually syncing, see GetActiveDataTypes().
+  bool IsSyncActive() const;
 
   // Returns true if the local sync backend server has been enabled through a
   // command line flag or policy. In this case sync is considered active but any
diff --git a/components/sync/driver/sync_service_utils_unittest.cc b/components/sync/driver/sync_service_utils_unittest.cc
index 9be7052..aa29e80 100644
--- a/components/sync/driver/sync_service_utils_unittest.cc
+++ b/components/sync/driver/sync_service_utils_unittest.cc
@@ -20,7 +20,9 @@
   void SetDisableReasons(int disable_reasons) {
     disable_reasons_ = disable_reasons;
   }
-  void SetSyncActive(bool active) { sync_active_ = active; }
+  void SetEngineInitialized(bool initialized) {
+    engine_initialized_ = initialized;
+  }
   void SetLocalSyncEnabled(bool local) { local_sync_enabled_ = local; }
   void SetPreferredDataTypes(const ModelTypeSet& types) {
     preferred_data_types_ = types;
@@ -35,15 +37,14 @@
 
   // SyncService implementation.
   int GetDisableReasons() const override { return disable_reasons_; }
-  bool IsEngineInitialized() const override { return sync_active_; }
-  bool IsSyncActive() const override { return sync_active_; }
+  bool IsEngineInitialized() const override { return engine_initialized_; }
   bool IsLocalSyncEnabled() const override { return local_sync_enabled_; }
   bool IsFirstSetupComplete() const override { return true; }
   ModelTypeSet GetPreferredDataTypes() const override {
     return preferred_data_types_;
   }
   ModelTypeSet GetActiveDataTypes() const override {
-    if (!sync_active_)
+    if (!IsSyncActive())
       return ModelTypeSet();
     return active_data_types_;
   }
@@ -77,7 +78,7 @@
 
  private:
   int disable_reasons_ = DISABLE_REASON_PLATFORM_OVERRIDE;
-  bool sync_active_ = false;
+  bool engine_initialized_ = false;
   bool sync_cycle_complete_ = false;
   bool local_sync_enabled_ = false;
   ModelTypeSet preferred_data_types_;
@@ -124,7 +125,7 @@
   EXPECT_EQ(UploadState::INITIALIZING,
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 
-  service.SetSyncActive(true);
+  service.SetEngineInitialized(true);
   EXPECT_EQ(UploadState::INITIALIZING,
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 
@@ -138,7 +139,7 @@
   TestSyncService service;
   service.SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
   service.SetConfigurationDone(true);
-  service.SetSyncActive(true);
+  service.SetEngineInitialized(true);
   service.SetSyncCycleComplete(true);
 
   // Sync is enabled only for a specific model type.
@@ -162,7 +163,7 @@
   TestSyncService service;
   service.SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
   service.SetConfigurationDone(true);
-  service.SetSyncActive(true);
+  service.SetEngineInitialized(true);
   service.SetSyncCycleComplete(true);
 
   // Sync is enabled for some model types.
@@ -185,7 +186,7 @@
   service.SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
   service.SetPreferredDataTypes(ProtocolTypes());
   service.SetActiveDataTypes(ProtocolTypes());
-  service.SetSyncActive(true);
+  service.SetEngineInitialized(true);
   service.SetConfigurationDone(true);
   service.SetSyncCycleComplete(true);
 
@@ -206,7 +207,7 @@
   service.SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
   service.SetPreferredDataTypes(ProtocolTypes());
   service.SetActiveDataTypes(ProtocolTypes());
-  service.SetSyncActive(true);
+  service.SetEngineInitialized(true);
   service.SetConfigurationDone(true);
   service.SetSyncCycleComplete(true);
 
@@ -246,7 +247,7 @@
   service.SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
   service.SetPreferredDataTypes(ProtocolTypes());
   service.SetActiveDataTypes(ProtocolTypes());
-  service.SetSyncActive(true);
+  service.SetEngineInitialized(true);
   service.SetConfigurationDone(true);
   service.SetSyncCycleComplete(true);
 
diff --git a/components/sync/model/DEPS b/components/sync/model/DEPS
index 5364f3e6..cf69a82 100644
--- a/components/sync/model/DEPS
+++ b/components/sync/model/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/keyed_service/core",
   "+components/sync/base",
   "+components/sync/engine",
   "+components/sync/model_impl",
diff --git a/components/sync/model/blocking_model_type_store.h b/components/sync/model/blocking_model_type_store.h
index 8e54072..64dd8e6 100644
--- a/components/sync/model/blocking_model_type_store.h
+++ b/components/sync/model/blocking_model_type_store.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <string>
 
-#include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/model/model_error.h"
diff --git a/components/sync/model/model_type_store.cc b/components/sync/model/model_type_store.cc
deleted file mode 100644
index 004af925..0000000
--- a/components/sync/model/model_type_store.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/model/model_type_store.h"
-
-#include <utility>
-
-#include "components/sync/model_impl/model_type_store_impl.h"
-
-namespace syncer {
-
-// static
-void ModelTypeStore::CreateInMemoryStoreForTest(ModelType type,
-                                                InitCallback callback) {
-  ModelTypeStoreImpl::CreateInMemoryStoreForTest(type, std::move(callback));
-}
-
-// static
-void ModelTypeStore::CreateStore(const std::string& path,
-                                 ModelType type,
-                                 InitCallback callback) {
-  ModelTypeStoreImpl::CreateStore(type, path, std::move(callback));
-}
-
-}  // namespace syncer
diff --git a/components/sync/model/model_type_store.h b/components/sync/model/model_type_store.h
index 853b500..20579dd 100644
--- a/components/sync/model/model_type_store.h
+++ b/components/sync/model/model_type_store.h
@@ -62,15 +62,6 @@
       base::OnceCallback<void(const base::Optional<ModelError>& error,
                               std::unique_ptr<MetadataBatch> metadata_batch)>;
 
-  // CreateStore takes |path|, and will run blocking calls on a task runner
-  // scoped to the given path. Tests likely don't want to use this method.
-  static void CreateStore(const std::string& path,
-                          ModelType type,
-                          InitCallback callback);
-  // Creates store object backed by in-memory leveldb database, gets its task
-  // runner from MessageLoop::task_runner(), and should only be used in tests.
-  static void CreateInMemoryStoreForTest(ModelType type, InitCallback callback);
-
   // Read operations return records either for all entries or only for ones
   // identified in |id_list|. |error| is nullopt if all records were read
   // successfully, otherwise an empty or partial list of read records is
diff --git a/components/sync/model/model_type_store_service.h b/components/sync/model/model_type_store_service.h
new file mode 100644
index 0000000..4b91ce4
--- /dev/null
+++ b/components/sync/model/model_type_store_service.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 COMPONENTS_SYNC_MODEL_MODEL_TYPE_STORE_SERVICE_H_
+#define COMPONENTS_SYNC_MODEL_MODEL_TYPE_STORE_SERVICE_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/sync/model/model_type_store.h"
+
+namespace syncer {
+
+class BlockingModelTypeStore;
+
+// Handles the shared resources for ModelTypeStore and related classes,
+// including a shared background sequence runner.
+class ModelTypeStoreService : public KeyedService {
+ public:
+  // Returns the root directory under which sync stores data.
+  // This doesn't belong here strictly speaking, but it is convenient to
+  // centralize all storage-related paths in one class.
+  virtual const base::FilePath& GetSyncDataPath() const = 0;
+
+  // Returns a factory to create instances of ModelTypeStore.
+  virtual RepeatingModelTypeStoreFactory GetStoreFactory() = 0;
+
+  virtual scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner() = 0;
+
+  // Creates a BlockingModelTypeStore. Must be called from the backend
+  // sequence as returned in GetBackendTaskRunner(). Can return null if there
+  // was an error.
+  virtual std::unique_ptr<BlockingModelTypeStore>
+  CreateBlockingStoreFromBackendSequence(ModelType type) = 0;
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_MODEL_MODEL_TYPE_STORE_SERVICE_H_
diff --git a/components/sync/model/model_type_store_test_util.cc b/components/sync/model/model_type_store_test_util.cc
index 8e17c46..fe17a262 100644
--- a/components/sync/model/model_type_store_test_util.cc
+++ b/components/sync/model/model_type_store_test_util.cc
@@ -7,8 +7,11 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/model_impl/blocking_model_type_store_impl.h"
+#include "components/sync/model_impl/model_type_store_backend.h"
+#include "components/sync/model_impl/model_type_store_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
@@ -55,26 +58,11 @@
 // static
 std::unique_ptr<ModelTypeStore>
 ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(ModelType type) {
-  base::RunLoop loop;
-  std::unique_ptr<ModelTypeStore> store;
-
-  ModelTypeStore::CreateInMemoryStoreForTest(
+  return std::make_unique<ModelTypeStoreImpl>(
       type,
-      base::BindOnce(
-          [](base::RunLoop* loop, std::unique_ptr<ModelTypeStore>* out_store,
-             const base::Optional<ModelError>& error,
-             std::unique_ptr<ModelTypeStore> in_store) {
-            EXPECT_FALSE(error) << error->ToString();
-            *out_store = std::move(in_store);
-            loop->Quit();
-          },
-          &loop, &store));
-
-  // Force the initialization to run now, synchronously.
-  loop.Run();
-
-  EXPECT_TRUE(store);
-  return store;
+      std::make_unique<BlockingModelTypeStoreImpl>(
+          type, ModelTypeStoreBackend::GetOrCreateInMemoryForTest()),
+      base::ThreadTaskRunnerHandle::Get());
 }
 
 // static
diff --git a/components/sync/model/test_model_type_store_service.cc b/components/sync/model/test_model_type_store_service.cc
new file mode 100644
index 0000000..ea506ea
--- /dev/null
+++ b/components/sync/model/test_model_type_store_service.cc
@@ -0,0 +1,40 @@
+// 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 "components/sync/model/test_model_type_store_service.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/sync/model/model_type_store_test_util.h"
+#include "components/sync/model_impl/blocking_model_type_store_impl.h"
+#include "components/sync/model_impl/model_type_store_backend.h"
+
+namespace syncer {
+
+TestModelTypeStoreService::TestModelTypeStoreService()
+    : store_backend_(ModelTypeStoreBackend::GetOrCreateInMemoryForTest()) {
+  DCHECK(sync_data_path_.CreateUniqueTempDir());
+}
+
+TestModelTypeStoreService::~TestModelTypeStoreService() {}
+
+const base::FilePath& TestModelTypeStoreService::GetSyncDataPath() const {
+  return sync_data_path_.GetPath();
+}
+
+RepeatingModelTypeStoreFactory TestModelTypeStoreService::GetStoreFactory() {
+  return ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest();
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+TestModelTypeStoreService::GetBackendTaskRunner() {
+  return base::ThreadTaskRunnerHandle::Get();
+}
+
+std::unique_ptr<BlockingModelTypeStore>
+TestModelTypeStoreService::CreateBlockingStoreFromBackendSequence(
+    ModelType type) {
+  return std::make_unique<BlockingModelTypeStoreImpl>(type, store_backend_);
+}
+
+}  // namespace syncer
diff --git a/components/sync/model/test_model_type_store_service.h b/components/sync/model/test_model_type_store_service.h
new file mode 100644
index 0000000..bbb7476c
--- /dev/null
+++ b/components/sync/model/test_model_type_store_service.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_MODEL_TEST_MODEL_TYPE_STORE_SERVICE_H_
+#define COMPONENTS_SYNC_MODEL_TEST_MODEL_TYPE_STORE_SERVICE_H_
+
+#include <memory>
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_store_service.h"
+
+namespace syncer {
+
+class ModelTypeStoreBackend;
+
+// Test-only ModelTypeStoreService implementation that uses a temporary dir
+// for GetSyncDataPath() and uses in-memory storage for ModelTypeStore.
+class TestModelTypeStoreService : public ModelTypeStoreService {
+ public:
+  TestModelTypeStoreService();
+  ~TestModelTypeStoreService() override;
+
+  // ModelTypeStoreService:
+  const base::FilePath& GetSyncDataPath() const override;
+  RepeatingModelTypeStoreFactory GetStoreFactory() override;
+  scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner() override;
+  std::unique_ptr<BlockingModelTypeStore>
+  CreateBlockingStoreFromBackendSequence(ModelType type) override;
+
+ private:
+  const scoped_refptr<ModelTypeStoreBackend> store_backend_;
+  base::ScopedTempDir sync_data_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestModelTypeStoreService);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_MODEL_TEST_MODEL_TYPE_STORE_SERVICE_H_
diff --git a/components/sync/model_impl/blocking_model_type_store_impl.cc b/components/sync/model_impl/blocking_model_type_store_impl.cc
index 02c242f..313d35f 100644
--- a/components/sync/model_impl/blocking_model_type_store_impl.cc
+++ b/components/sync/model_impl/blocking_model_type_store_impl.cc
@@ -145,37 +145,15 @@
 
 }  // namespace
 
-// static
-std::unique_ptr<BlockingModelTypeStoreImpl>
-BlockingModelTypeStoreImpl::CreateStore(ModelType type,
-                                        const std::string& path,
-                                        base::Optional<ModelError>* error) {
-  scoped_refptr<ModelTypeStoreBackend> backend =
-      ModelTypeStoreBackend::GetOrCreateBackend(path,
-                                                /*env=*/nullptr, error);
-  if (error->has_value()) {
-    return nullptr;
-  }
-  return base::WrapUnique<BlockingModelTypeStoreImpl>(
-      new BlockingModelTypeStoreImpl(type, backend));
-}
-
-// static
-std::unique_ptr<BlockingModelTypeStoreImpl>
-BlockingModelTypeStoreImpl::CreateInMemoryStoreForTest(ModelType type) {
-  std::unique_ptr<leveldb::Env> env =
-      ModelTypeStoreBackend::CreateInMemoryEnv();
-
-  std::string path;
-  env->GetTestDirectory(&path);
-  path += "/in-memory";
-
-  base::Optional<ModelError> error;
-  scoped_refptr<ModelTypeStoreBackend> backend =
-      ModelTypeStoreBackend::GetOrCreateBackend(path, std::move(env), &error);
-  DCHECK(!error.has_value());
-  return base::WrapUnique<BlockingModelTypeStoreImpl>(
-      new BlockingModelTypeStoreImpl(type, backend));
+BlockingModelTypeStoreImpl::BlockingModelTypeStoreImpl(
+    ModelType type,
+    scoped_refptr<ModelTypeStoreBackend> backend)
+    : type_(type),
+      backend_(backend),
+      data_prefix_(FormatDataPrefix(type)),
+      metadata_prefix_(FormatMetaPrefix(type)),
+      global_metadata_key_(FormatGlobalMetadataKey(type)) {
+  DCHECK(backend_);
 }
 
 BlockingModelTypeStoreImpl::~BlockingModelTypeStoreImpl() {
@@ -282,15 +260,4 @@
   return std::make_unique<LevelDbWriteBatch>(type);
 }
 
-BlockingModelTypeStoreImpl::BlockingModelTypeStoreImpl(
-    ModelType type,
-    scoped_refptr<ModelTypeStoreBackend> backend)
-    : type_(type),
-      backend_(backend),
-      data_prefix_(FormatDataPrefix(type)),
-      metadata_prefix_(FormatMetaPrefix(type)),
-      global_metadata_key_(FormatGlobalMetadataKey(type)) {
-  DCHECK(backend_);
-}
-
 }  // namespace syncer
diff --git a/components/sync/model_impl/blocking_model_type_store_impl.h b/components/sync/model_impl/blocking_model_type_store_impl.h
index 289fe9e..6eb081b 100644
--- a/components/sync/model_impl/blocking_model_type_store_impl.h
+++ b/components/sync/model_impl/blocking_model_type_store_impl.h
@@ -19,16 +19,8 @@
 
 class BlockingModelTypeStoreImpl : public BlockingModelTypeStore {
  public:
-  // |error| must not be null and will be populated in case there is an error,
-  // where the function returns null.
-  static std::unique_ptr<BlockingModelTypeStoreImpl> CreateStore(
-      ModelType type,
-      const std::string& path,
-      base::Optional<ModelError>* error);
-
-  static std::unique_ptr<BlockingModelTypeStoreImpl> CreateInMemoryStoreForTest(
-      ModelType type);
-
+  BlockingModelTypeStoreImpl(ModelType type,
+                             scoped_refptr<ModelTypeStoreBackend> backend);
   ~BlockingModelTypeStoreImpl() override;
 
   // BlockingModelTypeStore implementation.
@@ -48,9 +40,6 @@
   static std::unique_ptr<WriteBatch> CreateWriteBatchForType(ModelType type);
 
  private:
-  BlockingModelTypeStoreImpl(ModelType type,
-                             scoped_refptr<ModelTypeStoreBackend> backend);
-
   const ModelType type_;
   const scoped_refptr<ModelTypeStoreBackend> backend_;
 
diff --git a/components/sync/model_impl/model_type_store_backend.cc b/components/sync/model_impl/model_type_store_backend.cc
index a390573..2cfcff883 100644
--- a/components/sync/model_impl/model_type_store_backend.cc
+++ b/components/sync/model_impl/model_type_store_backend.cc
@@ -6,11 +6,8 @@
 
 #include <utility>
 
-#include "base/files/file_path.h"
-#include "base/lazy_instance.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/synchronization/lock.h"
 #include "components/sync/protocol/model_type_store_schema_descriptor.pb.h"
 #include "third_party/leveldatabase/env_chromium.h"
 #include "third_party/leveldatabase/leveldb_chrome.h"
@@ -34,6 +31,10 @@
 
 namespace {
 
+// Used as a singleton instance for tests, such that two stores that coexist
+// share the same backend (and hence, the same data).
+ModelTypeStoreBackend* in_memory_instance_for_test_ = nullptr;
+
 StoreInitResultForHistogram LevelDbStatusToStoreInitResult(
     const leveldb::Status& status) {
   if (status.ok())
@@ -51,103 +52,50 @@
   return STORE_INIT_RESULT_UNKNOWN;
 }
 
-// BackendMap tracks created ModelTypeStoreBackends ensuring at most one backend
-// exists for a given path. BackendMap keeps non-owning pointer to backend
-// allowing backend lifetime to be controlled by consumers. Since backends can
-// run concurrently on different threads all map operations are guarded by lock.
-class BackendMap {
- public:
-  BackendMap() = default;
-
-  // Returns backend reference or nullptr if backend for |path| is not in the
-  // map.
-  scoped_refptr<ModelTypeStoreBackend> GetBackend(
-      const std::string& path) const;
-  // Adds backend into the map ensuring it wasn't added before.
-  void SetBackend(const std::string& path, ModelTypeStoreBackend* backend);
-  void EraseBackend(const std::string& path);
-
- private:
-  mutable base::Lock lock_;
-
-  std::unordered_map<std::string, ModelTypeStoreBackend*> backends_;
-
-  DISALLOW_COPY_AND_ASSIGN(BackendMap);
-};
-
-base::LazyInstance<BackendMap>::Leaky backend_map = LAZY_INSTANCE_INITIALIZER;
-
-scoped_refptr<ModelTypeStoreBackend> BackendMap::GetBackend(
-    const std::string& path) const {
-  base::AutoLock scoped_lock(lock_);
-  auto it = backends_.find(path);
-  return (it == backends_.end()) ? nullptr : it->second;
-}
-
-void BackendMap::SetBackend(const std::string& path,
-                            ModelTypeStoreBackend* backend) {
-  base::AutoLock scoped_lock(lock_);
-  DCHECK(backends_.find(path) == backends_.end());
-  backends_[path] = backend;
-}
-
-void BackendMap::EraseBackend(const std::string& path) {
-  base::AutoLock scoped_lock(lock_);
-  backends_.erase(path);
-}
-
 }  // namespace
 
-ModelTypeStoreBackend::ModelTypeStoreBackend(const std::string& path)
-    : path_(path) {}
-
-ModelTypeStoreBackend::~ModelTypeStoreBackend() {
-  backend_map.Get().EraseBackend(path_);
-}
-
-std::unique_ptr<leveldb::Env> ModelTypeStoreBackend::CreateInMemoryEnv() {
-  return leveldb_chrome::NewMemEnv("ModelTypeStore");
-}
-
 // static
-scoped_refptr<ModelTypeStoreBackend> ModelTypeStoreBackend::GetOrCreateBackend(
-    const std::string& path,
-    std::unique_ptr<leveldb::Env> env,
-    base::Optional<ModelError>* error) {
-  error->reset();
+scoped_refptr<ModelTypeStoreBackend>
+ModelTypeStoreBackend::GetOrCreateInMemoryForTest() {
+  if (in_memory_instance_for_test_) {
+    return in_memory_instance_for_test_;
+  }
+
+  std::unique_ptr<leveldb::Env> env =
+      leveldb_chrome::NewMemEnv("ModelTypeStore");
+
+  std::string test_directory_str;
+  env->GetTestDirectory(&test_directory_str);
+  const base::FilePath path = base::FilePath::FromUTF8Unsafe(test_directory_str)
+                                  .Append(FILE_PATH_LITERAL("in-memory"));
+
   scoped_refptr<ModelTypeStoreBackend> backend =
-      backend_map.Get().GetBackend(path);
-  if (backend) {
-    DCHECK_CALLED_ON_VALID_SEQUENCE(backend->sequence_checker_);
-    return backend;
-  }
+      new ModelTypeStoreBackend(std::move(env));
+  in_memory_instance_for_test_ = backend.get();
 
-  backend = new ModelTypeStoreBackend(path);
-
-  *error = backend->Init(path, std::move(env));
-
-  if (!error->has_value()) {
-    backend_map.Get().SetBackend(path, backend.get());
-  } else {
-    backend = nullptr;
-  }
-
+  base::Optional<ModelError> error = backend->Init(path);
+  DCHECK(!error);
   return backend;
 }
 
+// static
+scoped_refptr<ModelTypeStoreBackend>
+ModelTypeStoreBackend::CreateUninitialized() {
+  return new ModelTypeStoreBackend(/*env=*/nullptr);
+}
+
 base::Optional<ModelError> ModelTypeStoreBackend::Init(
-    const std::string& path,
-    std::unique_ptr<leveldb::Env> env) {
+    const base::FilePath& path) {
   DCHECK(sequence_checker_.CalledOnValidSequence());
+  DCHECK(!IsInitialized());
+  const std::string path_str = path.AsUTF8Unsafe();
 
-  env_ = std::move(env);
-
-  leveldb::Status status = OpenDatabase(path, env_.get());
+  leveldb::Status status = OpenDatabase(path_str, env_.get());
   if (status.IsCorruption()) {
     DCHECK(db_ == nullptr);
-    status = DestroyDatabase(path, env_.get());
+    status = DestroyDatabase(path_str, env_.get());
     if (status.ok())
-      status = OpenDatabase(path, env_.get());
+      status = OpenDatabase(path_str, env_.get());
     if (status.ok())
       RecordStoreInitResultHistogram(
           STORE_INIT_RESULT_RECOVERED_AFTER_CORRUPTION);
@@ -176,6 +124,22 @@
   return base::nullopt;
 }
 
+bool ModelTypeStoreBackend::IsInitialized() const {
+  return db_ != nullptr;
+}
+
+ModelTypeStoreBackend::ModelTypeStoreBackend(std::unique_ptr<leveldb::Env> env)
+    : env_(std::move(env)) {
+  // It's OK to construct this class in a sequence and Init() it elsewhere.
+  sequence_checker_.DetachFromSequence();
+}
+
+ModelTypeStoreBackend::~ModelTypeStoreBackend() {
+  if (this == in_memory_instance_for_test_) {
+    in_memory_instance_for_test_ = nullptr;
+  }
+}
+
 leveldb::Status ModelTypeStoreBackend::OpenDatabase(const std::string& path,
                                                     leveldb::Env* env) {
   leveldb_env::Options options;
@@ -274,6 +238,16 @@
              : base::Optional<ModelError>({FROM_HERE, status.ToString()});
 }
 
+base::Optional<ModelError> ModelTypeStoreBackend::MigrateForTest(
+    int64_t current_version,
+    int64_t desired_version) {
+  return Migrate(current_version, desired_version);
+}
+
+int64_t ModelTypeStoreBackend::GetStoreVersionForTest() {
+  return GetStoreVersion();
+}
+
 int64_t ModelTypeStoreBackend::GetStoreVersion() {
   DCHECK(db_);
   leveldb::ReadOptions read_options;
@@ -325,9 +299,4 @@
                             STORE_INIT_RESULT_COUNT);
 }
 
-// static
-bool ModelTypeStoreBackend::BackendExistsForTest(const std::string& path) {
-  return backend_map.Get().GetBackend(path) != nullptr;
-}
-
 }  // namespace syncer
diff --git a/components/sync/model_impl/model_type_store_backend.h b/components/sync/model_impl/model_type_store_backend.h
index fc29d02..df463a44 100644
--- a/components/sync/model_impl/model_type_store_backend.h
+++ b/components/sync/model_impl/model_type_store_backend.h
@@ -9,8 +9,10 @@
 #include <string>
 #include <unordered_map>
 
+#include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "components/sync/model/model_type_store.h"
@@ -56,17 +58,20 @@
 class ModelTypeStoreBackend
     : public base::RefCountedThreadSafe<ModelTypeStoreBackend> {
  public:
-  // Helper function to create in memory environment for leveldb.
-  static std::unique_ptr<leveldb::Env> CreateInMemoryEnv();
+  static scoped_refptr<ModelTypeStoreBackend> GetOrCreateInMemoryForTest();
 
-  // GetOrCreateBackend will check if |backend_map_| has a backend with same
-  // |path|. If |backend_map_| has one, wrap that backend to scoped_refptr and
-  // return. If |backend_map_| does not have, create a backend and store it into
-  // |backend_map_|.
-  static scoped_refptr<ModelTypeStoreBackend> GetOrCreateBackend(
-      const std::string& path,
-      std::unique_ptr<leveldb::Env> env,
-      base::Optional<ModelError>* error);
+  // Create a new and uninitialized instance of ModelTypeStoreBackend. Init()
+  // must be called afterwards, which binds the instance to a certain sequence.
+  static scoped_refptr<ModelTypeStoreBackend> CreateUninitialized();
+
+  // Init opens database at |path|. If database doesn't exist it creates one.
+  // It can be called from a sequence that is different to the constructing one,
+  // but from this point on the backend is bound to the current sequence, and
+  // must be used and destructed in it.
+  base::Optional<ModelError> Init(const base::FilePath& path);
+
+  // Can be called from any sequence.
+  bool IsInitialized() const;
 
   // Reads records with keys formed by prepending ids from |id_list| with
   // |prefix|. If the record is found its id (without prefix) and value is
@@ -92,24 +97,27 @@
   base::Optional<ModelError> DeleteDataAndMetadataForPrefix(
       const std::string& prefix);
 
- private:
-  friend class base::RefCountedThreadSafe<ModelTypeStoreBackend>;
-  friend class ModelTypeStoreBackendTest;
+  // Migrate the db schema from |current_version| to |desired_version|.
+  base::Optional<ModelError> MigrateForTest(int64_t current_version,
+                                            int64_t desired_version);
 
+  // Attempts to read and return the database's version.
+  int64_t GetStoreVersionForTest();
+
+  // Some constants exposed for testing.
   static const int64_t kLatestSchemaVersion;
   static const char kDBSchemaDescriptorRecordId[];
   static const char kStoreInitResultHistogramName[];
 
-  explicit ModelTypeStoreBackend(const std::string& path);
-  ~ModelTypeStoreBackend();
+ private:
+  friend class base::RefCountedThreadSafe<ModelTypeStoreBackend>;
 
-  // Init opens database at |path|. If database doesn't exist it creates one.
   // Normally |env| should be nullptr, this causes leveldb to use default disk
   // based environment from leveldb::Env::Default().
   // Providing |env| allows to override environment used by leveldb for tests
   // with in-memory or faulty environment.
-  base::Optional<ModelError> Init(const std::string& path,
-                                  std::unique_ptr<leveldb::Env> env);
+  explicit ModelTypeStoreBackend(std::unique_ptr<leveldb::Env> env);
+  ~ModelTypeStoreBackend();
 
   // Opens leveldb database passing correct options. On success sets |db_| and
   // returns ok status. On failure |db_| is nullptr and returned status reflects
@@ -136,10 +144,6 @@
   static void RecordStoreInitResultHistogram(
       StoreInitResultForHistogram result);
 
-  // Helper function for unittests to check if backend already exists for a
-  // given path.
-  static bool BackendExistsForTest(const std::string& path);
-
   // In some scenarios ModelTypeStoreBackend holds ownership of env. Typical
   // example is when test creates in memory environment with CreateInMemoryEnv
   // and wants it to be destroyed along with backend. This is achieved by
@@ -147,12 +151,10 @@
   //
   // env_ declaration should appear before declaration of db_ because
   // environment object should still be valid when db_'s destructor is called.
-  std::unique_ptr<leveldb::Env> env_;
+  const std::unique_ptr<leveldb::Env> env_;
 
   std::unique_ptr<leveldb::DB> db_;
 
-  std::string path_;
-
   // Ensures that operations with backend are performed seqentially, not
   // concurrently.
   base::SequenceChecker sequence_checker_;
diff --git a/components/sync/model_impl/model_type_store_backend_unittest.cc b/components/sync/model_impl/model_type_store_backend_unittest.cc
index 7decb27..39874aa7 100644
--- a/components/sync/model_impl/model_type_store_backend_unittest.cc
+++ b/components/sync/model_impl/model_type_store_backend_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <utility>
 
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "components/sync/protocol/model_type_store_schema_descriptor.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,93 +21,23 @@
 using sync_pb::ModelTypeStoreSchemaDescriptor;
 
 namespace syncer {
-
-class ModelTypeStoreBackendTest : public testing::Test {
- public:
-  scoped_refptr<ModelTypeStoreBackend> GetOrCreateBackend() {
-    std::string path = "/test_db";
-    return GetOrCreateBackendWithPath(path);
-  }
-
-  scoped_refptr<ModelTypeStoreBackend> GetOrCreateBackendWithPath(
-      std::string custom_path) {
-    std::unique_ptr<leveldb::Env> in_memory_env =
-        ModelTypeStoreBackend::CreateInMemoryEnv();
-    std::string path;
-    in_memory_env->GetTestDirectory(&path);
-    path += custom_path;
-
-    base::Optional<ModelError> error;
-    // In-memory store backend works on the same thread as test.
-    scoped_refptr<ModelTypeStoreBackend> backend =
-        ModelTypeStoreBackend::GetOrCreateBackend(
-            path, std::move(in_memory_env), &error);
-    EXPECT_TRUE(backend.get());
-    EXPECT_FALSE(error) << error->ToString();
-    return backend;
-  }
-
-  // Create backend with custom env. This function is used in tests that need to
-  // prepare env in some way (create files) before passing it to leveldb.
-  scoped_refptr<ModelTypeStoreBackend> CreateBackendWithEnv(
-      std::unique_ptr<leveldb::Env> env,
-      const std::string& path) {
-    EXPECT_FALSE(BackendExistsForPath(path));
-
-    base::Optional<ModelError> error;
-    scoped_refptr<ModelTypeStoreBackend> backend =
-        ModelTypeStoreBackend::GetOrCreateBackend(path, std::move(env), &error);
-    EXPECT_TRUE(backend.get());
-    EXPECT_FALSE(error) << error->ToString();
-    return backend;
-  }
-
-  bool BackendExistsForPath(const std::string& path) {
-    return ModelTypeStoreBackend::BackendExistsForTest(path);
-  }
-
-  std::string GetBackendPath(scoped_refptr<ModelTypeStoreBackend> backend) {
-    return backend->path_;
-  }
-
-  base::Optional<ModelError> Migrate(
-      scoped_refptr<ModelTypeStoreBackend> backend,
-      int64_t current_version,
-      int64_t desired_version) {
-    return backend->Migrate(current_version, desired_version);
-  }
-
-  bool Migrate0To1(scoped_refptr<ModelTypeStoreBackend> backend) {
-    return backend->Migrate0To1();
-  }
-
-  int64_t GetStoreVersion(scoped_refptr<ModelTypeStoreBackend> backend) {
-    return backend->GetStoreVersion();
-  }
-
-  int64_t LatestVersion() {
-    return ModelTypeStoreBackend::kLatestSchemaVersion;
-  }
-
-  const char* SchemaId() {
-    return ModelTypeStoreBackend::kDBSchemaDescriptorRecordId;
-  }
-
-  const char* StoreInitResultHistogramName() {
-    return ModelTypeStoreBackend::kStoreInitResultHistogramName;
-  }
-};
+namespace {
 
 // Test that after record is written to backend it can be read back even after
 // backend is destroyed and recreated in the same environment.
-TEST_F(ModelTypeStoreBackendTest, WriteThenRead) {
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+TEST(ModelTypeStoreBackendTest, WriteThenRead) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::CreateUninitialized();
+  base::Optional<ModelError> error = backend->Init(temp_dir.GetPath());
+  ASSERT_FALSE(error) << error->ToString();
 
   // Write record.
   std::unique_ptr<leveldb::WriteBatch> write_batch(new leveldb::WriteBatch());
   write_batch->Put("prefix:id1", "data1");
-  base::Optional<ModelError> error =
-      backend->WriteModifications(std::move(write_batch));
+  error = backend->WriteModifications(std::move(write_batch));
   ASSERT_FALSE(error) << error->ToString();
 
   // Read all records with prefix.
@@ -118,7 +50,11 @@
   record_list.clear();
 
   // Recreate backend and read all records with prefix.
-  backend = GetOrCreateBackend();
+  backend = nullptr;
+  backend = ModelTypeStoreBackend::CreateUninitialized();
+  error = backend->Init(temp_dir.GetPath());
+  ASSERT_FALSE(error) << error->ToString();
+
   error = backend->ReadAllRecordsWithPrefix("prefix:", &record_list);
   ASSERT_FALSE(error) << error->ToString();
   ASSERT_EQ(1ul, record_list.size());
@@ -127,8 +63,9 @@
 }
 
 // Test that ReadAllRecordsWithPrefix correclty filters records by prefix.
-TEST_F(ModelTypeStoreBackendTest, ReadAllRecordsWithPrefix) {
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+TEST(ModelTypeStoreBackendTest, ReadAllRecordsWithPrefix) {
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
 
   std::unique_ptr<leveldb::WriteBatch> write_batch(new leveldb::WriteBatch());
   write_batch->Put("prefix1:id1", "data1");
@@ -147,8 +84,9 @@
 
 // Test that deleted records are correctly marked as milling in results of
 // ReadRecordsWithPrefix.
-TEST_F(ModelTypeStoreBackendTest, ReadDeletedRecord) {
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+TEST(ModelTypeStoreBackendTest, ReadDeletedRecord) {
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
 
   // Create records, ensure they are returned by ReadRecordsWithPrefix.
   std::unique_ptr<leveldb::WriteBatch> write_batch(new leveldb::WriteBatch());
@@ -188,8 +126,9 @@
 }
 
 // Test that DeleteDataAndMetadataForPrefix correctly deletes records by prefix.
-TEST_F(ModelTypeStoreBackendTest, DeleteDataAndMetadataForPrefix) {
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+TEST(ModelTypeStoreBackendTest, DeleteDataAndMetadataForPrefix) {
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
 
   auto write_batch = std::make_unique<leveldb::WriteBatch>();
   write_batch->Put("prefix1:id1", "data1");
@@ -225,125 +164,80 @@
   }
 }
 
-// Test that only one backend got create when we ask two backend with same path,
-// and after de-reference the backend, the backend will be deleted.
-TEST_F(ModelTypeStoreBackendTest, TwoSameBackendTest) {
-  // Create two backend with same path, check if they are reference to same
-  // address.
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
-  scoped_refptr<ModelTypeStoreBackend> backend_second = GetOrCreateBackend();
-  std::string path = GetBackendPath(backend);
-  ASSERT_EQ(backend.get(), backend_second.get());
-
-  // Delete one reference, check the real backend still here.
-  backend = nullptr;
-  ASSERT_FALSE(backend);
-  ASSERT_TRUE(backend_second.get());
-  ASSERT_TRUE(backend_second->HasOneRef());
-
-  // Delete another reference, check the real backend is deleted.
-  backend_second = nullptr;
-  ASSERT_FALSE(backend_second);
-  ASSERT_FALSE(BackendExistsForPath(path));
-}
-
-// Test that two backend got create when we ask two backend with different path,
-// and after de-reference two backend, the both backend will be deleted.
-TEST_F(ModelTypeStoreBackendTest, TwoDifferentBackendTest) {
-  // Create two backend with different path, check if they are reference to
-  // different address.
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+// Test that GetOrCreateInMemoryForTest() returns the same backend, if the
+// previous exists.
+TEST(ModelTypeStoreBackendTest, TwoSameBackendTest) {
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
   scoped_refptr<ModelTypeStoreBackend> backend_second =
-      GetOrCreateBackendWithPath("/test_db2");
-  std::string path = GetBackendPath(backend);
-  ASSERT_NE(backend.get(), backend_second.get());
-  ASSERT_TRUE(backend->HasOneRef());
-  ASSERT_TRUE(backend_second->HasOneRef());
-
-  // delete one backend, check only one got deleted.
-  backend = nullptr;
-  ASSERT_FALSE(backend);
-  ASSERT_TRUE(backend_second.get());
-  ASSERT_TRUE(backend_second->HasOneRef());
-  ASSERT_FALSE(BackendExistsForPath(path));
-
-  // delete another backend.
-  backend_second = nullptr;
-  ASSERT_FALSE(backend_second);
-  ASSERT_FALSE(BackendExistsForPath("/test_db2"));
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
+  EXPECT_EQ(backend.get(), backend_second.get());
 }
 
 // Test that initializing the database migrates it to the latest schema version.
-TEST_F(ModelTypeStoreBackendTest, MigrateNoSchemaVersionToLatestVersionTest) {
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+TEST(ModelTypeStoreBackendTest, MigrateNoSchemaVersionToLatestVersionTest) {
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
 
-  ASSERT_EQ(LatestVersion(), GetStoreVersion(backend));
+  ASSERT_EQ(ModelTypeStoreBackend::kLatestSchemaVersion,
+            backend->GetStoreVersionForTest());
 }
 
 // Test that the 0 to 1 migration succeeds and sets the schema version to 1.
-TEST_F(ModelTypeStoreBackendTest, Migrate0To1Test) {
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+TEST(ModelTypeStoreBackendTest, Migrate0To1Test) {
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
 
   std::unique_ptr<leveldb::WriteBatch> write_batch(new leveldb::WriteBatch());
-  write_batch->Delete(SchemaId());
+  write_batch->Delete(ModelTypeStoreBackend::kDBSchemaDescriptorRecordId);
   base::Optional<ModelError> error =
       backend->WriteModifications(std::move(write_batch));
   ASSERT_FALSE(error) << error->ToString();
+  ASSERT_EQ(0, backend->GetStoreVersionForTest());
 
-  ASSERT_TRUE(Migrate0To1(backend));
-  ASSERT_EQ(1, GetStoreVersion(backend));
+  error = backend->MigrateForTest(0, 1);
+  EXPECT_FALSE(error) << error->ToString();
+  EXPECT_EQ(1, backend->GetStoreVersionForTest());
 }
 
 // Test that migration to an unknown version fails
-TEST_F(ModelTypeStoreBackendTest, MigrateWithHigherExistingVersionFails) {
-  scoped_refptr<ModelTypeStoreBackend> backend = GetOrCreateBackend();
+TEST(ModelTypeStoreBackendTest, MigrateWithHigherExistingVersionFails) {
+  scoped_refptr<ModelTypeStoreBackend> backend =
+      ModelTypeStoreBackend::GetOrCreateInMemoryForTest();
 
   base::Optional<ModelError> error =
-      Migrate(backend, LatestVersion() + 1, LatestVersion());
+      backend->MigrateForTest(ModelTypeStoreBackend::kLatestSchemaVersion + 1,
+                              ModelTypeStoreBackend::kLatestSchemaVersion);
   ASSERT_TRUE(error);
   EXPECT_EQ("Schema version too high", error->message());
 }
 
 // Tests that initializing store after corruption triggers recovery and results
 // in successful store initialization.
-TEST_F(ModelTypeStoreBackendTest, RecoverAfterCorruption) {
+TEST(ModelTypeStoreBackendTest, RecoverAfterCorruption) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
   base::HistogramTester tester;
   leveldb::Status s;
 
   // Prepare environment that looks corrupt to leveldb.
-  std::unique_ptr<leveldb::Env> env =
-      std::make_unique<leveldb::EnvWrapper>(leveldb::Env::Default());
-
-  std::string path;
-  env->GetTestDirectory(&path);
-  path += "/corrupt_db";
-
   // Easiest way to simulate leveldb corruption is to create empty CURRENT file.
-  {
-    s = env->CreateDir(path);
-    EXPECT_TRUE(s.ok());
-    leveldb::WritableFile* current_file_raw;
-    s = env->NewWritableFile(path + "/CURRENT", &current_file_raw);
-    EXPECT_TRUE(s.ok());
-    current_file_raw->Close();
-    delete current_file_raw;
-  }
+  base::WriteFile(temp_dir.GetPath().Append(FILE_PATH_LITERAL("CURRENT")), "",
+                  0);
 
-  // CreateBackendWithEnv will ensure backend initialization is successful.
   scoped_refptr<ModelTypeStoreBackend> backend =
-      CreateBackendWithEnv(std::move(env), path);
-
-  // Cleanup directory after the test.
-  backend = nullptr;
-  s = leveldb::DestroyDB(path, leveldb_env::Options());
-  EXPECT_TRUE(s.ok()) << s.ToString();
+      ModelTypeStoreBackend::CreateUninitialized();
+  base::Optional<ModelError> error = backend->Init(temp_dir.GetPath());
+  ASSERT_FALSE(error) << error->ToString();
 
   // Check that both recovery and consecutive initialization are recorded in
   // histograms.
-  tester.ExpectBucketCount(StoreInitResultHistogramName(),
+  tester.ExpectBucketCount(ModelTypeStoreBackend::kStoreInitResultHistogramName,
                            STORE_INIT_RESULT_SUCCESS, 1);
-  tester.ExpectBucketCount(StoreInitResultHistogramName(),
+  tester.ExpectBucketCount(ModelTypeStoreBackend::kStoreInitResultHistogramName,
                            STORE_INIT_RESULT_RECOVERED_AFTER_CORRUPTION, 1);
 }
 
+}  // namespace
 }  // namespace syncer
diff --git a/components/sync/model_impl/model_type_store_impl.cc b/components/sync/model_impl/model_type_store_impl.cc
index 39103b84..8658f297 100644
--- a/components/sync/model_impl/model_type_store_impl.cc
+++ b/components/sync/model_impl/model_type_store_impl.cc
@@ -9,57 +9,15 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/optional.h"
 #include "base/task_runner_util.h"
-#include "base/task_scheduler/post_task.h"
-#include "base/task_scheduler/task_traits.h"
-#include "base/threading/sequenced_task_runner_handle.h"
 #include "components/sync/model/model_error.h"
 #include "components/sync/model_impl/blocking_model_type_store_impl.h"
 
 namespace syncer {
 
-namespace {
-
-// Holds a one to one mapping between profile path and SequencedTaskRunner. This
-// class is expected to be accessed on any thread, and uses a lock to guarantee
-// thread safety. The task runners are held onto by scoped_refptrs, and since
-// this class is leaky, none of these task runners are ever destroyed.
-class TaskRunnerMap {
- public:
-  TaskRunnerMap() = default;
-
-  scoped_refptr<base::SequencedTaskRunner> GetOrCreateTaskRunner(
-      const std::string& path) {
-    base::AutoLock scoped_lock(lock_);
-    auto iter = task_runner_map_.find(path);
-    if (iter == task_runner_map_.end()) {
-      scoped_refptr<base::SequencedTaskRunner> task_runner =
-          base::CreateSequencedTaskRunnerWithTraits(
-              {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
-      task_runner_map_[path] = task_runner;
-      return task_runner;
-    } else {
-      return iter->second;
-    }
-  }
-
- private:
-  mutable base::Lock lock_;
-  std::map<std::string, scoped_refptr<base::SequencedTaskRunner>>
-      task_runner_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(TaskRunnerMap);
-};
-
-base::LazyInstance<TaskRunnerMap>::Leaky task_runner_map_singleton =
-    LAZY_INSTANCE_INITIALIZER;
-
-}  // namespace
-
 ModelTypeStoreImpl::ModelTypeStoreImpl(
     ModelType type,
     std::unique_ptr<BlockingModelTypeStoreImpl> backend_store,
@@ -84,60 +42,6 @@
           std::move(backend_store_)));
 }
 
-// static
-void ModelTypeStoreImpl::CreateStore(ModelType type,
-                                     const std::string& path,
-                                     InitCallback callback) {
-  DCHECK(!callback.is_null());
-  scoped_refptr<base::SequencedTaskRunner> task_runner =
-      task_runner_map_singleton.Get().GetOrCreateTaskRunner(path);
-  auto error = std::make_unique<base::Optional<ModelError>>();
-  auto task = base::BindOnce(&BlockingModelTypeStoreImpl::CreateStore, type,
-                             path, error.get());
-  auto reply =
-      base::BindOnce(&ModelTypeStoreImpl::BackendInitDone, type,
-                     std::move(error), task_runner, std::move(callback));
-  base::PostTaskAndReplyWithResult(task_runner.get(), FROM_HERE,
-                                   std::move(task), std::move(reply));
-}
-
-// static
-void ModelTypeStoreImpl::CreateInMemoryStoreForTest(ModelType type,
-                                                    InitCallback callback) {
-  DCHECK(!callback.is_null());
-
-  // In-memory store backend works on the same sequence as test.
-  scoped_refptr<base::SequencedTaskRunner> task_runner =
-      base::SequencedTaskRunnerHandle::Get();
-  DCHECK(task_runner);
-
-  auto error = std::make_unique<base::Optional<ModelError>>();
-  auto task = base::BindOnce(
-      &BlockingModelTypeStoreImpl::CreateInMemoryStoreForTest, type);
-  auto reply =
-      base::BindOnce(&ModelTypeStoreImpl::BackendInitDone, type,
-                     std::move(error), task_runner, std::move(callback));
-
-  base::PostTaskAndReplyWithResult(task_runner.get(), FROM_HERE,
-                                   std::move(task), std::move(reply));
-}
-
-// static
-void ModelTypeStoreImpl::BackendInitDone(
-    ModelType type,
-    std::unique_ptr<base::Optional<ModelError>> error,
-    scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
-    InitCallback callback,
-    std::unique_ptr<BlockingModelTypeStoreImpl> backend) {
-  std::unique_ptr<ModelTypeStoreImpl> store;
-  if (!error->has_value()) {
-    store.reset(
-        new ModelTypeStoreImpl(type, std::move(backend), backend_task_runner));
-  }
-
-  std::move(callback).Run(*error, std::move(store));
-}
-
 // Note on pattern for communicating with backend:
 //  - API function (e.g. ReadData) allocates lists for output.
 //  - API function prepares two callbacks: task that will be posted on backend
diff --git a/components/sync/model_impl/model_type_store_impl.h b/components/sync/model_impl/model_type_store_impl.h
index 1c74efa..48eb5ac 100644
--- a/components/sync/model_impl/model_type_store_impl.h
+++ b/components/sync/model_impl/model_type_store_impl.h
@@ -24,13 +24,14 @@
 // underlying ModelTypeStoreBackend).
 class ModelTypeStoreImpl : public ModelTypeStore {
  public:
+  // |backend_store| must not be null and must have been created in
+  // |backend_task_runner|.
+  ModelTypeStoreImpl(
+      ModelType type,
+      std::unique_ptr<BlockingModelTypeStoreImpl> backend_store,
+      scoped_refptr<base::SequencedTaskRunner> backend_task_runner);
   ~ModelTypeStoreImpl() override;
 
-  static void CreateStore(ModelType type,
-                          const std::string& path,
-                          InitCallback callback);
-  static void CreateInMemoryStoreForTest(ModelType type, InitCallback callback);
-
   // ModelTypeStore implementation.
   void ReadData(const IdList& id_list, ReadDataCallback callback) override;
   void ReadAllData(ReadAllDataCallback callback) override;
@@ -41,18 +42,6 @@
   void DeleteAllDataAndMetadata(CallbackWithResult callback) override;
 
  private:
-  static void BackendInitDone(
-      ModelType type,
-      std::unique_ptr<base::Optional<ModelError>> result,
-      scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
-      InitCallback callback,
-      std::unique_ptr<BlockingModelTypeStoreImpl> backend_store);
-
-  ModelTypeStoreImpl(
-      ModelType type,
-      std::unique_ptr<BlockingModelTypeStoreImpl> backend_store,
-      scoped_refptr<base::SequencedTaskRunner> backend_task_runner);
-
   // Callbacks for different calls to ModelTypeStoreBackend.
   void ReadDataDone(ReadDataCallback callback,
                     std::unique_ptr<RecordList> record_list,
diff --git a/components/sync/model_impl/model_type_store_impl_unittest.cc b/components/sync/model_impl/model_type_store_impl_unittest.cc
index 86532f24..5ad85c3c 100644
--- a/components/sync/model_impl/model_type_store_impl_unittest.cc
+++ b/components/sync/model_impl/model_type_store_impl_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_store_test_util.h"
 #include "components/sync/protocol/entity_metadata.pb.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 #include "components/sync/test/test_matchers.h"
@@ -39,78 +40,132 @@
   return metadata;
 }
 
+// Following functions capture parameters passed to callbacks into variables
+// provided by test. They can be passed as callbacks to ModelTypeStore
+// functions.
+static void CaptureError(base::Optional<ModelError>* dst,
+                         const base::Optional<ModelError>& error) {
+  *dst = error;
+}
+
+void CaptureErrorAndRecords(
+    base::Optional<ModelError>* dst_error,
+    std::unique_ptr<ModelTypeStore::RecordList>* dst_records,
+    const base::Optional<ModelError>& error,
+    std::unique_ptr<ModelTypeStore::RecordList> records) {
+  *dst_error = error;
+  *dst_records = std::move(records);
+}
+
+void CaptureErrorAndMetadataBatch(base::Optional<ModelError>* dst_error,
+                                  std::unique_ptr<MetadataBatch>* dst_batch,
+                                  const base::Optional<ModelError>& error,
+                                  std::unique_ptr<MetadataBatch> batch) {
+  *dst_error = error;
+  *dst_batch = std::move(batch);
+}
+
+void CaptureErrorRecordsAndIdList(
+    base::Optional<ModelError>* dst_error,
+    std::unique_ptr<ModelTypeStore::RecordList>* dst_records,
+    std::unique_ptr<ModelTypeStore::IdList>* dst_id_list,
+    const base::Optional<ModelError>& error,
+    std::unique_ptr<ModelTypeStore::RecordList> records,
+    std::unique_ptr<ModelTypeStore::IdList> missing_id_list) {
+  *dst_error = error;
+  *dst_records = std::move(records);
+  *dst_id_list = std::move(missing_id_list);
+}
+
+void WriteData(ModelTypeStore* store,
+               const std::string& key,
+               const std::string& data) {
+  auto write_batch = store->CreateWriteBatch();
+  write_batch->WriteData(key, data);
+  base::Optional<ModelError> error;
+  store->CommitWriteBatch(std::move(write_batch),
+                          base::BindOnce(&CaptureError, &error));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(error) << error->ToString();
+}
+
+void WriteMetadata(ModelTypeStore* store,
+                   const std::string& key,
+                   const sync_pb::EntityMetadata& metadata) {
+  auto write_batch = store->CreateWriteBatch();
+  write_batch->GetMetadataChangeList()->UpdateMetadata(key, metadata);
+
+  base::Optional<ModelError> error;
+  store->CommitWriteBatch(std::move(write_batch),
+                          base::BindOnce(&CaptureError, &error));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(error) << error->ToString();
+}
+
+void WriteModelTypeState(ModelTypeStore* store,
+                         const sync_pb::ModelTypeState& state) {
+  auto write_batch = store->CreateWriteBatch();
+  write_batch->GetMetadataChangeList()->UpdateModelTypeState(state);
+
+  base::Optional<ModelError> error;
+  store->CommitWriteBatch(std::move(write_batch),
+                          base::BindOnce(&CaptureError, &error));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(error) << error->ToString();
+}
+
+void ReadStoreContents(
+    ModelTypeStore* store,
+    std::unique_ptr<ModelTypeStore::RecordList>* data_records,
+    std::unique_ptr<MetadataBatch>* metadata_batch) {
+  base::Optional<ModelError> error;
+  store->ReadAllData(
+      base::BindOnce(&CaptureErrorAndRecords, &error, data_records));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(error) << error->ToString();
+  store->ReadAllMetadata(
+      base::BindOnce(&CaptureErrorAndMetadataBatch, &error, metadata_batch));
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(error) << error->ToString();
+}
+
+void VerifyMetadata(
+    std::unique_ptr<MetadataBatch> batch,
+    const sync_pb::ModelTypeState& state,
+    std::map<std::string, sync_pb::EntityMetadata> expected_metadata) {
+  EXPECT_EQ(state.SerializeAsString(),
+            batch->GetModelTypeState().SerializeAsString());
+  EntityMetadataMap actual_metadata = batch->TakeAllMetadata();
+  for (const auto& kv : expected_metadata) {
+    auto it = actual_metadata.find(kv.first);
+    ASSERT_TRUE(it != actual_metadata.end());
+    EXPECT_EQ(kv.second.SerializeAsString(), it->second.SerializeAsString());
+    actual_metadata.erase(it);
+  }
+  EXPECT_EQ(0U, actual_metadata.size());
+}
+
+// Matcher to verify contents of returned RecordList .
+MATCHER_P2(RecordMatches, id, value, "") {
+  return arg.id == id && arg.value == value;
+}
+
 }  // namespace
 
 class ModelTypeStoreImplTest : public testing::Test {
  public:
-  void TearDown() override {
+  ModelTypeStoreImplTest()
+      : store_(ModelTypeStoreTestUtil::CreateInMemoryStoreForTest()) {}
+
+  ~ModelTypeStoreImplTest() override {
     if (store_) {
       store_.reset();
-      PumpLoop();
+      base::RunLoop().RunUntilIdle();
     }
   }
 
   ModelTypeStore* store() { return store_.get(); }
 
-  static void OnStoreCreated(std::unique_ptr<ModelTypeStore>* store_ref,
-                             const base::Optional<ModelError>& error,
-                             std::unique_ptr<ModelTypeStore> store) {
-    ASSERT_FALSE(error) << error->ToString();
-    *store_ref = std::move(store);
-  }
-
-  static void PumpLoop() {
-    base::RunLoop run_loop;
-    run_loop.RunUntilIdle();
-  }
-
-  static void CreateStoreCaptureReference(
-      const ModelType type,
-      std::unique_ptr<ModelTypeStore>* store_ref) {
-    ModelTypeStore::CreateInMemoryStoreForTest(
-        type, base::Bind(&ModelTypeStoreImplTest::OnStoreCreated, store_ref));
-    PumpLoop();
-  }
-
-  void CreateStore() { CreateStoreCaptureReference(UNSPECIFIED, &store_); }
-
-  static void WriteData(ModelTypeStore* store,
-                        const std::string& key,
-                        const std::string& data) {
-    auto write_batch = store->CreateWriteBatch();
-    write_batch->WriteData(key, data);
-    base::Optional<ModelError> error;
-    store->CommitWriteBatch(std::move(write_batch),
-                            base::BindOnce(&CaptureError, &error));
-    PumpLoop();
-    ASSERT_FALSE(error) << error->ToString();
-  }
-
-  static void WriteMetadata(ModelTypeStore* store,
-                            const std::string& key,
-                            const sync_pb::EntityMetadata& metadata) {
-    auto write_batch = store->CreateWriteBatch();
-    write_batch->GetMetadataChangeList()->UpdateMetadata(key, metadata);
-
-    base::Optional<ModelError> error;
-    store->CommitWriteBatch(std::move(write_batch),
-                            base::BindOnce(&CaptureError, &error));
-    PumpLoop();
-    ASSERT_FALSE(error) << error->ToString();
-  }
-
-  static void WriteModelTypeState(ModelTypeStore* store,
-                                  const sync_pb::ModelTypeState& state) {
-    auto write_batch = store->CreateWriteBatch();
-    write_batch->GetMetadataChangeList()->UpdateModelTypeState(state);
-
-    base::Optional<ModelError> error;
-    store->CommitWriteBatch(std::move(write_batch),
-                            base::BindOnce(&CaptureError, &error));
-    PumpLoop();
-    ASSERT_FALSE(error) << error->ToString();
-  }
-
   void WriteTestData() {
     WriteData(store(), "id1", "data1");
     WriteMetadata(store(), "id1", CreateEntityMetadata("metadata1"));
@@ -118,101 +173,13 @@
     WriteModelTypeState(store(), CreateModelTypeState("type_state"));
   }
 
-  static void ReadStoreContents(
-      ModelTypeStore* store,
-      std::unique_ptr<ModelTypeStore::RecordList>* data_records,
-      std::unique_ptr<MetadataBatch>* metadata_batch) {
-    base::Optional<ModelError> error;
-    store->ReadAllData(
-        base::BindOnce(&CaptureErrorAndRecords, &error, data_records));
-    PumpLoop();
-    ASSERT_FALSE(error) << error->ToString();
-    store->ReadAllMetadata(
-        base::BindOnce(&CaptureErrorAndMetadataBatch, &error, metadata_batch));
-    PumpLoop();
-    ASSERT_FALSE(error) << error->ToString();
-  }
-
-  // Following functions capture parameters passed to callbacks into variables
-  // provided by test. They can be passed as callbacks to ModelTypeStore
-  // functions.
-  static void CaptureError(base::Optional<ModelError>* dst,
-                           const base::Optional<ModelError>& error) {
-    *dst = error;
-  }
-
-  static void CaptureErrorAndRecords(
-      base::Optional<ModelError>* dst_error,
-      std::unique_ptr<ModelTypeStore::RecordList>* dst_records,
-      const base::Optional<ModelError>& error,
-      std::unique_ptr<ModelTypeStore::RecordList> records) {
-    *dst_error = error;
-    *dst_records = std::move(records);
-  }
-
-  static void CaptureErrorAndMetadataBatch(
-      base::Optional<ModelError>* dst_error,
-      std::unique_ptr<MetadataBatch>* dst_batch,
-      const base::Optional<ModelError>& error,
-      std::unique_ptr<MetadataBatch> batch) {
-    *dst_error = error;
-    *dst_batch = std::move(batch);
-  }
-
-  static void CaptureErrorRecordsAndIdList(
-      base::Optional<ModelError>* dst_error,
-      std::unique_ptr<ModelTypeStore::RecordList>* dst_records,
-      std::unique_ptr<ModelTypeStore::IdList>* dst_id_list,
-      const base::Optional<ModelError>& error,
-      std::unique_ptr<ModelTypeStore::RecordList> records,
-      std::unique_ptr<ModelTypeStore::IdList> missing_id_list) {
-    *dst_error = error;
-    *dst_records = std::move(records);
-    *dst_id_list = std::move(missing_id_list);
-  }
-
-  void VerifyMetadata(
-      std::unique_ptr<MetadataBatch> batch,
-      const sync_pb::ModelTypeState& state,
-      std::map<std::string, sync_pb::EntityMetadata> expected_metadata) {
-    EXPECT_EQ(state.SerializeAsString(),
-              batch->GetModelTypeState().SerializeAsString());
-    EntityMetadataMap actual_metadata = batch->TakeAllMetadata();
-    for (const auto& kv : expected_metadata) {
-      auto it = actual_metadata.find(kv.first);
-      ASSERT_TRUE(it != actual_metadata.end());
-      EXPECT_EQ(kv.second.SerializeAsString(), it->second.SerializeAsString());
-      actual_metadata.erase(it);
-    }
-    EXPECT_EQ(0U, actual_metadata.size());
-  }
-
  private:
   base::MessageLoop message_loop_;
   std::unique_ptr<ModelTypeStore> store_;
 };
 
-// Matcher to verify contents of returned RecordList .
-MATCHER_P2(RecordMatches, id, value, "") {
-  return arg.id == id && arg.value == value;
-}
-
-// Test that CreateInMemoryStoreForTest triggers store initialization that
-// results with callback being called with valid store pointer.
-TEST_F(ModelTypeStoreImplTest, CreateInMemoryStore) {
-  std::unique_ptr<ModelTypeStore> store;
-  ModelTypeStore::CreateInMemoryStoreForTest(
-      UNSPECIFIED,
-      base::BindOnce(&ModelTypeStoreImplTest::OnStoreCreated, &store));
-  ASSERT_EQ(nullptr, store);
-  PumpLoop();
-  ASSERT_NE(nullptr, store);
-}
-
 // Test read functions on empty store.
 TEST_F(ModelTypeStoreImplTest, ReadEmptyStore) {
-  CreateStore();
-
   std::unique_ptr<ModelTypeStore::RecordList> data_records;
   std::unique_ptr<MetadataBatch> metadata_batch;
   ReadStoreContents(store(), &data_records, &metadata_batch);
@@ -223,7 +190,6 @@
 
 // Test that records that are written to store later can be read from it.
 TEST_F(ModelTypeStoreImplTest, WriteThenRead) {
-  CreateStore();
   WriteTestData();
 
   std::unique_ptr<ModelTypeStore::RecordList> data_records;
@@ -238,7 +204,6 @@
 
 // Test that records that DeleteAllDataAndMetadata() deletes everything.
 TEST_F(ModelTypeStoreImplTest, WriteThenDeleteAll) {
-  CreateStore();
   WriteTestData();
 
   {
@@ -263,7 +228,6 @@
 // Test that if ModelTypeState is not set then ReadAllMetadata still succeeds
 // and returns entry metadata records.
 TEST_F(ModelTypeStoreImplTest, MissingModelTypeState) {
-  CreateStore();
   WriteTestData();
 
   base::Optional<ModelError> error;
@@ -272,13 +236,13 @@
   write_batch->GetMetadataChangeList()->ClearModelTypeState();
   store()->CommitWriteBatch(std::move(write_batch),
                             base::BindOnce(&CaptureError, &error));
-  PumpLoop();
+  base::RunLoop().RunUntilIdle();
   ASSERT_FALSE(error) << error->ToString();
 
   std::unique_ptr<MetadataBatch> metadata_batch;
   store()->ReadAllMetadata(
       base::BindOnce(&CaptureErrorAndMetadataBatch, &error, &metadata_batch));
-  PumpLoop();
+  base::RunLoop().RunUntilIdle();
   ASSERT_FALSE(error) << error->ToString();
   VerifyMetadata(std::move(metadata_batch), sync_pb::ModelTypeState(),
                  {{"id1", CreateEntityMetadata("metadata1")}});
@@ -287,7 +251,6 @@
 // Test that when reading data records by id, if one of the ids is missing
 // operation still succeeds and missing id is returned in missing_id_list.
 TEST_F(ModelTypeStoreImplTest, ReadMissingDataRecords) {
-  CreateStore();
   WriteTestData();
 
   base::Optional<ModelError> error;
@@ -302,7 +265,7 @@
   store()->ReadData(
       id_list, base::BindOnce(&CaptureErrorRecordsAndIdList, &error, &records,
                               &missing_id_list));
-  PumpLoop();
+  base::RunLoop().RunUntilIdle();
   ASSERT_FALSE(error) << error->ToString();
   ASSERT_THAT(*records,
               testing::UnorderedElementsAre(RecordMatches("id1", "data1")));
@@ -311,11 +274,13 @@
 
 // Test that stores for different types that share the same backend don't
 // interfere with each other's records.
-TEST_F(ModelTypeStoreImplTest, TwoStoresWithSharedBackend) {
-  std::unique_ptr<ModelTypeStore> store_1;
-  std::unique_ptr<ModelTypeStore> store_2;
-  CreateStoreCaptureReference(AUTOFILL, &store_1);
-  CreateStoreCaptureReference(BOOKMARKS, &store_2);
+TEST(ModelTypeStoreImplWithTwoStoreTest, TwoStoresWithSharedBackend) {
+  base::MessageLoop message_loop;
+
+  std::unique_ptr<ModelTypeStore> store_1 =
+      ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(AUTOFILL);
+  std::unique_ptr<ModelTypeStore> store_2 =
+      ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(BOOKMARKS);
 
   const sync_pb::EntityMetadata metadata1 = CreateEntityMetadata("metadata1");
   const sync_pb::EntityMetadata metadata2 = CreateEntityMetadata("metadata2");
@@ -348,11 +313,13 @@
 
 // Test that records that DeleteAllDataAndMetadata() does not delete data from
 // another store when the backend is shared.
-TEST_F(ModelTypeStoreImplTest, DeleteAllWithSharedBackend) {
-  std::unique_ptr<ModelTypeStore> store_1;
-  std::unique_ptr<ModelTypeStore> store_2;
-  CreateStoreCaptureReference(AUTOFILL, &store_1);
-  CreateStoreCaptureReference(BOOKMARKS, &store_2);
+TEST(ModelTypeStoreImplWithTwoStoreTest, DeleteAllWithSharedBackend) {
+  base::MessageLoop message_loop;
+
+  std::unique_ptr<ModelTypeStore> store_1 =
+      ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(AUTOFILL);
+  std::unique_ptr<ModelTypeStore> store_2 =
+      ModelTypeStoreTestUtil::CreateInMemoryStoreForTest(BOOKMARKS);
 
   const sync_pb::EntityMetadata metadata1 = CreateEntityMetadata("metadata1");
   const sync_pb::EntityMetadata metadata2 = CreateEntityMetadata("metadata2");
diff --git a/components/sync/model_impl/model_type_store_service_impl.cc b/components/sync/model_impl/model_type_store_service_impl.cc
new file mode 100644
index 0000000..f6c4993
--- /dev/null
+++ b/components/sync/model_impl/model_type_store_service_impl.cc
@@ -0,0 +1,148 @@
+// 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 "components/sync/model_impl/model_type_store_service_impl.h"
+
+#include <utility>
+
+#include "base/bind_helpers.h"
+#include "base/optional.h"
+#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
+#include "base/task_scheduler/task_traits.h"
+#include "components/sync/model_impl/blocking_model_type_store_impl.h"
+#include "components/sync/model_impl/model_type_store_backend.h"
+#include "components/sync/model_impl/model_type_store_impl.h"
+
+namespace syncer {
+namespace {
+
+constexpr base::FilePath::CharType kSyncDataFolderName[] =
+    FILE_PATH_LITERAL("Sync Data");
+
+constexpr base::FilePath::CharType kLevelDBFolderName[] =
+    FILE_PATH_LITERAL("LevelDB");
+
+// Initialized ModelTypeStoreBackend, on the backend sequence.
+void InitOnBackendSequence(scoped_refptr<ModelTypeStoreBackend> store_backend,
+                           const base::FilePath& level_db_path) {
+  base::Optional<ModelError> error = store_backend->Init(level_db_path);
+  if (error) {
+    LOG(ERROR) << "Failed to initialize ModelTypeStore backend: "
+               << error->ToString();
+  }
+}
+
+// CreateBlockingModelTypeStoreOnBackendSequence() could return the store by
+// value, but that's incompatible with binding the function with a WeakPtr
+// for the reply.
+void CreateBlockingModelTypeStoreOnBackendSequence(
+    ModelType type,
+    scoped_refptr<ModelTypeStoreBackend> store_backend,
+    std::unique_ptr<BlockingModelTypeStoreImpl>* result) {
+  result->reset();
+  if (store_backend->IsInitialized()) {
+    *result = std::make_unique<BlockingModelTypeStoreImpl>(type, store_backend);
+  }
+}
+
+}  // namespace
+
+ModelTypeStoreServiceImpl::ModelTypeStoreServiceImpl(
+    const base::FilePath& base_path)
+    : sync_path_(base_path.Append(base::FilePath(kSyncDataFolderName))),
+      leveldb_path_(sync_path_.Append(base::FilePath(kLevelDBFolderName))),
+      backend_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+          {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
+      store_backend_(ModelTypeStoreBackend::CreateUninitialized()),
+      weak_ptr_factory_(this) {
+  DCHECK(backend_task_runner_);
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&InitOnBackendSequence, store_backend_, leveldb_path_));
+}
+
+ModelTypeStoreServiceImpl::~ModelTypeStoreServiceImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          base::DoNothing::Once<scoped_refptr<ModelTypeStoreBackend>>(),
+          std::move(store_backend_)));
+}
+
+const base::FilePath& ModelTypeStoreServiceImpl::GetSyncDataPath() const {
+  return sync_path_;
+}
+
+RepeatingModelTypeStoreFactory ModelTypeStoreServiceImpl::GetStoreFactory() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+  return base::BindRepeating(&ModelTypeStoreServiceImpl::CreateModelTypeStore,
+                             weak_ptr_factory_.GetWeakPtr());
+}
+
+scoped_refptr<base::SequencedTaskRunner>
+ModelTypeStoreServiceImpl::GetBackendTaskRunner() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+  return backend_task_runner_;
+}
+
+std::unique_ptr<BlockingModelTypeStore>
+ModelTypeStoreServiceImpl::CreateBlockingStoreFromBackendSequence(
+    ModelType type) {
+  DCHECK(backend_task_runner_->RunsTasksInCurrentSequence());
+  if (!store_backend_) {
+    return nullptr;
+  }
+  return std::make_unique<BlockingModelTypeStoreImpl>(type, store_backend_);
+}
+
+void ModelTypeStoreServiceImpl::CreateModelTypeStore(
+    ModelType type,
+    ModelTypeStore::InitCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+  // BlockingModelTypeStoreImpl must be instantiated in the backend sequence.
+  // This also guarantees that the creation is sequenced with the backend's
+  // initialization, since we can't know for sure that InitOnBackendSequence()
+  // has already run.
+
+  // CreateBlockingModelTypeStoreOnBackendSequence() cannot return by value
+  // weak_ptrs can only bind to methods without return values.
+  auto blocking_store_ptr =
+      std::make_unique<std::unique_ptr<BlockingModelTypeStoreImpl>>();
+
+  base::OnceClosure task =
+      base::BindOnce(&CreateBlockingModelTypeStoreOnBackendSequence, type,
+                     store_backend_, blocking_store_ptr.get());
+
+  base::OnceClosure reply =
+      base::BindOnce(&ModelTypeStoreServiceImpl::CreateModelTypeStoreOnUIThread,
+                     weak_ptr_factory_.GetWeakPtr(), type, std::move(callback),
+                     std::move(blocking_store_ptr));
+
+  backend_task_runner_->PostTaskAndReply(FROM_HERE, std::move(task),
+                                         std::move(reply));
+}
+
+void ModelTypeStoreServiceImpl::CreateModelTypeStoreOnUIThread(
+    ModelType type,
+    ModelTypeStore::InitCallback callback,
+    std::unique_ptr<std::unique_ptr<BlockingModelTypeStoreImpl>>
+        blocking_store_ptr) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(ui_sequence_checker_);
+  DCHECK(blocking_store_ptr);
+
+  if (*blocking_store_ptr) {
+    std::move(callback).Run(
+        /*error=*/base::nullopt,
+        std::make_unique<ModelTypeStoreImpl>(
+            type, std::move(*blocking_store_ptr), backend_task_runner_));
+  } else {
+    std::move(callback).Run(
+        ModelError(FROM_HERE, "ModelTypeStore backend initialization failed"),
+        /*store=*/nullptr);
+  }
+}
+
+}  // namespace syncer
diff --git a/components/sync/model_impl/model_type_store_service_impl.h b/components/sync/model_impl/model_type_store_service_impl.h
new file mode 100644
index 0000000..2c0695d8
--- /dev/null
+++ b/components/sync/model_impl/model_type_store_service_impl.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 COMPONENTS_SYNC_MODEL_IMPL_MODEL_TYPE_STORE_SERVICE_IMPL_H_
+#define COMPONENTS_SYNC_MODEL_IMPL_MODEL_TYPE_STORE_SERVICE_IMPL_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "base/sequenced_task_runner.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_store_service.h"
+
+namespace syncer {
+
+class BlockingModelTypeStoreImpl;
+class ModelTypeStoreBackend;
+
+// Handles the shared resources for ModelTypeStore and related classes,
+// including a shared background sequence runner.
+class ModelTypeStoreServiceImpl : public ModelTypeStoreService {
+ public:
+  // |base_path| represents the profile's path.
+  explicit ModelTypeStoreServiceImpl(const base::FilePath& base_path);
+  ~ModelTypeStoreServiceImpl() override;
+
+  // ModelTypeStoreService:
+  const base::FilePath& GetSyncDataPath() const override;
+  RepeatingModelTypeStoreFactory GetStoreFactory() override;
+  scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner() override;
+  std::unique_ptr<BlockingModelTypeStore>
+  CreateBlockingStoreFromBackendSequence(ModelType type) override;
+
+ private:
+  void CreateModelTypeStore(ModelType type,
+                            ModelTypeStore::InitCallback callback);
+  void CreateModelTypeStoreOnUIThread(
+      ModelType type,
+      ModelTypeStore::InitCallback callback,
+      std::unique_ptr<std::unique_ptr<BlockingModelTypeStoreImpl>>
+          blocking_store_ptr);
+
+  // The path to the base directory under which sync should store its
+  // information.
+  const base::FilePath sync_path_;
+
+  // Subdirectory where ModelTypeStore persists the leveldb database.
+  const base::FilePath leveldb_path_;
+
+  // The backend sequence or thread.
+  const scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
+
+  // Constructed in the UI thread, used and destroyed in |backend_task_runner_|.
+  scoped_refptr<ModelTypeStoreBackend> store_backend_;
+
+  SEQUENCE_CHECKER(ui_sequence_checker_);
+
+  base::WeakPtrFactory<ModelTypeStoreServiceImpl> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModelTypeStoreServiceImpl);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_MODEL_IMPL_MODEL_TYPE_STORE_SERVICE_IMPL_H_
diff --git a/components/unified_consent/unified_consent_service_unittest.cc b/components/unified_consent/unified_consent_service_unittest.cc
index 0194a93..ae50e11b 100644
--- a/components/unified_consent/unified_consent_service_unittest.cc
+++ b/components/unified_consent/unified_consent_service_unittest.cc
@@ -23,7 +23,6 @@
  public:
   int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
   bool IsFirstSetupComplete() const override { return true; }
-  bool IsSyncActive() const override { return engine_initialized_; }
   bool IsEngineInitialized() const override { return engine_initialized_; }
   void AddObserver(syncer::SyncServiceObserver* observer) override {
     observer_ = observer;
diff --git a/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc b/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
index 77aa7722..0bc74ee 100644
--- a/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
+++ b/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
@@ -37,7 +37,6 @@
   }
   bool IsFirstSetupComplete() const override { return true; }
   bool IsEngineInitialized() const override { return true; }
-  bool IsSyncActive() const override { return true; }
 
   syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override {
     if (!sync_initialized_)
diff --git a/components/zucchini/buffer_view.h b/components/zucchini/buffer_view.h
index 7bdd8ec4..66925c4 100644
--- a/components/zucchini/buffer_view.h
+++ b/components/zucchini/buffer_view.h
@@ -24,9 +24,9 @@
   size_t hi() const { return offset + size; }
 
   // Returns whether the Region fits in |[0, container_size)|. Special case:
-  // a size-0 region starting at |container_size| does not fit.
+  // a size-0 region starting at |container_size| fits.
   bool FitsIn(size_t container_size) const {
-    return offset < container_size && container_size - offset >= size;
+    return offset <= container_size && container_size - offset >= size;
   }
 
   // Returns |v| clipped to the inclusive range |[lo(), hi()]|.
diff --git a/components/zucchini/buffer_view_unittest.cc b/components/zucchini/buffer_view_unittest.cc
index b048747..7df34b27 100644
--- a/components/zucchini/buffer_view_unittest.cc
+++ b/components/zucchini/buffer_view_unittest.cc
@@ -154,7 +154,7 @@
 }
 
 TEST_F(BufferViewTest, Covers) {
-  EXPECT_FALSE(ConstBufferView().covers({0, 0}));
+  EXPECT_TRUE(ConstBufferView().covers({0, 0}));
   EXPECT_FALSE(ConstBufferView().covers({0, 1}));
 
   ConstBufferView view(bytes_.data(), bytes_.size());
@@ -168,8 +168,10 @@
   EXPECT_TRUE(view.covers({bytes_.size() - 1, 0}));
   EXPECT_TRUE(view.covers({bytes_.size() - 1, 1}));
   EXPECT_FALSE(view.covers({bytes_.size() - 1, 2}));
-  EXPECT_FALSE(view.covers({bytes_.size(), 0}));
+  EXPECT_TRUE(view.covers({bytes_.size(), 0}));
   EXPECT_FALSE(view.covers({bytes_.size(), 1}));
+  EXPECT_FALSE(view.covers({bytes_.size() + 1, 0}));
+  EXPECT_FALSE(view.covers({bytes_.size() + 1, 1}));
 
   EXPECT_FALSE(view.covers({1, size_t(-1)}));
   EXPECT_FALSE(view.covers({size_t(-1), 1}));
diff --git a/components/zucchini/disassembler_dex.cc b/components/zucchini/disassembler_dex.cc
index 6ec67d1..373d645 100644
--- a/components/zucchini/disassembler_dex.cc
+++ b/components/zucchini/disassembler_dex.cc
@@ -162,6 +162,7 @@
       return kInvalidOffset;
     DCHECK(Is32BitAligned(code_item_offset));
 
+    // TODO(huangs): Fail if |code_item->insns_size == 0| (Constraint A1).
     // Skip instruction bytes.
     if (!source_.GetArray<uint16_t>(code_item->insns_size))
       return kInvalidOffset;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 97c38fe1..abaa573 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -573,6 +573,8 @@
     "cocoa/system_hotkey_helper_mac.mm",
     "cocoa/system_hotkey_map.h",
     "cocoa/system_hotkey_map.mm",
+    "code_cache/generated_code_cache.cc",
+    "code_cache/generated_code_cache.h",
     "startup_data_impl.cc",
     "startup_data_impl.h",
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index b7308e54..615d3541 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -558,6 +558,11 @@
 }
 
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityAriaGridDynamicAddRow) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-grid-dynamic-add-row.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
                        AccessibilityAriaGridExtraWrapElems) {
   RunAriaTest(FILE_PATH_LITERAL("aria-grid-extra-wrap-elems.html"));
 }
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index 7ce7dc6..acd1d25 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -1227,7 +1227,7 @@
 
   // Cleanup should delete the registration.
   background_fetch_data_manager_->Cleanup();
-  base::RunLoop().RunUntilIdle();
+  thread_bundle_.RunUntilIdle();
   EXPECT_EQ(0u,
             GetRegistrationUserDataByKeyPrefix(sw_id, kUserDataPrefix).size());
 
diff --git a/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc b/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc
index c4e83be..81b2e3a 100644
--- a/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc
+++ b/content/browser/background_fetch/background_fetch_embedded_worker_test_helper.cc
@@ -7,7 +7,7 @@
 #include "base/callback.h"
 #include "base/time/time.h"
 #include "content/common/background_fetch/background_fetch_types.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
 
 namespace content {
diff --git a/content/browser/background_fetch/background_fetch_event_dispatcher.h b/content/browser/background_fetch/background_fetch_event_dispatcher.h
index b615355..f535820 100644
--- a/content/browser/background_fetch/background_fetch_event_dispatcher.h
+++ b/content/browser/background_fetch/background_fetch_event_dispatcher.h
@@ -13,7 +13,7 @@
 #include "base/memory/ref_counted.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/common/content_export.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 
 namespace content {
 
diff --git a/content/browser/background_fetch/storage/get_initialization_data_task.cc b/content/browser/background_fetch/storage/get_initialization_data_task.cc
index 9b47c3d8..10db38fc 100644
--- a/content/browser/background_fetch/storage/get_initialization_data_task.cc
+++ b/content/browser/background_fetch/storage/get_initialization_data_task.cc
@@ -9,7 +9,9 @@
 #include "base/task_scheduler/post_task.h"
 #include "base/task_scheduler/task_traits.h"
 #include "content/browser/background_fetch/background_fetch.pb.h"
+#include "content/browser/background_fetch/background_fetch_data_manager.h"
 #include "content/browser/background_fetch/storage/database_helpers.h"
+#include "content/browser/background_fetch/storage/mark_registration_for_deletion_task.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
 #include "ui/gfx/image/image.h"
@@ -36,7 +38,6 @@
 
     // The results to report.
     BackgroundFetchInitializationData* initialization_data;
-    blink::mojom::BackgroundFetchError* error;
   };
 
   InitializationSubTask(DatabaseTaskHost* host,
@@ -46,7 +47,6 @@
         sub_task_init_(sub_task_init),
         done_closure_(std::move(done_closure)) {
     DCHECK(sub_task_init_.initialization_data);
-    DCHECK(sub_task_init_.error);
   }
 
   ~InitializationSubTask() override = default;
@@ -54,7 +54,7 @@
  protected:
   void FinishWithError(blink::mojom::BackgroundFetchError error) override {
     if (error != blink::mojom::BackgroundFetchError::NONE)
-      *sub_task_init_.error = error;
+      sub_task_init_.initialization_data->error = error;
     std::move(done_closure_).Run();
     Finished();  // Destroys |this|.
   }
@@ -150,10 +150,10 @@
       proto::BackgroundFetchCompletedRequest completed_request;
       if (!completed_request.ParseFromString(serialized_completed_request) ||
           sub_task_init().unique_id != completed_request.unique_id()) {
-        *sub_task_init().error =
-            blink::mojom::BackgroundFetchError::STORAGE_ERROR;
-        continue;
+        FinishWithError(blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+        return;
       }
+
       active_requests_to_delete.push_back(ActiveRequestKey(
           completed_request.unique_id(), completed_request.request_index()));
     }
@@ -203,9 +203,8 @@
     for (const std::string& serialized_active_request : data) {
       proto::BackgroundFetchActiveRequest active_request;
       if (!active_request.ParseFromString(serialized_active_request)) {
-        *sub_task_init().error =
-            blink::mojom::BackgroundFetchError::STORAGE_ERROR;
-        continue;
+        FinishWithError(blink::mojom::BackgroundFetchError::STORAGE_ERROR);
+        return;
       }
       DCHECK_EQ(sub_task_init().unique_id, active_request.unique_id());
       sub_task_init().initialization_data->active_fetch_guids.push_back(
@@ -425,7 +424,8 @@
         base::BindOnce(
             [](base::WeakPtr<FillBackgroundFetchInitializationDataTask> task) {
               if (task)
-                task->FinishWithError(*task->sub_task_init().error);
+                task->FinishWithError(
+                    task->sub_task_init().initialization_data->error);
             },
             weak_factory_.GetWeakPtr()));
     AddSubTask(std::make_unique<FillFromMetadataTask>(this, sub_task_init(),
@@ -486,12 +486,10 @@
   }
 
   base::RepeatingClosure barrier_closure = base::BarrierClosure(
-      user_data.size(), base::BindOnce(
-                            [](base::WeakPtr<GetInitializationDataTask> task) {
-                              if (task)
-                                task->FinishWithError(task->error_);
-                            },
-                            weak_factory_.GetWeakPtr()));
+      user_data.size(),
+      base::BindOnce(&GetInitializationDataTask::FinishWithError,
+                     weak_factory_.GetWeakPtr(),
+                     blink::mojom::BackgroundFetchError::NONE));
 
   for (const auto& ud : user_data) {
     auto insertion_result = initialization_data_map_.emplace(
@@ -502,7 +500,7 @@
         this,
         InitializationSubTask::SubTaskInit{
             ud.first, ud.second,
-            &insertion_result.first->second /* initialization_data */, &error_},
+            &insertion_result.first->second /* initialization_data */},
         barrier_closure));
   }
 }
@@ -510,8 +508,24 @@
     blink::mojom::BackgroundFetchError error) {
   std::vector<BackgroundFetchInitializationData> results;
   results.reserve(initialization_data_map_.size());
-  for (auto& id : initialization_data_map_)
-    results.emplace_back(std::move(id.second));
+
+  for (auto& data : initialization_data_map_) {
+    if (data.second.error == blink::mojom::BackgroundFetchError::NONE) {
+      // If we successfully extracted all the data, move it to the
+      // initialization vector to be handed over to create a controller.
+      results.emplace_back(std::move(data.second));
+    } else if (!data.second.registration_id.developer_id().empty()) {
+      // There was an error in getting the initialization data
+      // (e.g. corrupt data, SWDB error). If the Developer ID of the fetch
+      // is available, mark the registration for deletion.
+      // Note that the Developer ID isn't available if the metadata extraction
+      // failed.
+      // TODO(crbug.com/865388): Getting the Developer ID should be possible
+      // since it is part of the key for when we got the Unique ID.
+      AddDatabaseTask(std::make_unique<MarkRegistrationForDeletionTask>(
+          data_manager(), data.second.registration_id, base::DoNothing()));
+    }
+  }
 
   std::move(callback_).Run(error, std::move(results));
   Finished();  // Destroys |this|.
diff --git a/content/browser/background_fetch/storage/get_initialization_data_task.h b/content/browser/background_fetch/storage/get_initialization_data_task.h
index 4e6367b..7946ba0 100644
--- a/content/browser/background_fetch/storage/get_initialization_data_task.h
+++ b/content/browser/background_fetch/storage/get_initialization_data_task.h
@@ -39,6 +39,10 @@
   std::vector<std::string> active_fetch_guids;
   std::string ui_title;
 
+  // The error, if any, when getting the registration data.
+  blink::mojom::BackgroundFetchError error =
+      blink::mojom::BackgroundFetchError::NONE;
+
   DISALLOW_COPY_AND_ASSIGN(BackgroundFetchInitializationData);
 };
 
@@ -83,10 +87,6 @@
   // Map from the unique_id to the initialization data.
   InitializationDataMap initialization_data_map_;
 
-  // The error to report with |callback_|.
-  blink::mojom::BackgroundFetchError error_ =
-      blink::mojom::BackgroundFetchError::NONE;
-
   base::WeakPtrFactory<GetInitializationDataTask>
       weak_factory_;  // Keep as last.
 
diff --git a/content/browser/background_sync/background_sync_manager.cc b/content/browser/background_sync/background_sync_manager.cc
index 1de24533..0b5b90e 100644
--- a/content/browser/background_sync/background_sync_manager.cc
+++ b/content/browser/background_sync/background_sync_manager.cc
@@ -22,7 +22,7 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_storage.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/background_sync_controller.h"
 #include "content/public/browser/browser_context.h"
diff --git a/content/browser/code_cache/generated_code_cache.cc b/content/browser/code_cache/generated_code_cache.cc
new file mode 100644
index 0000000..c74c9a3
--- /dev/null
+++ b/content/browser/code_cache/generated_code_cache.cc
@@ -0,0 +1,383 @@
+// 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/code_cache/generated_code_cache.h"
+#include "base/memory/ptr_util.h"
+#include "net/base/completion_callback.h"
+#include "net/base/completion_once_callback.h"
+#include "net/http/http_util.h"
+
+namespace content {
+
+namespace {
+// Checks if |requesting_origin| is allowed to cache code for |resource_url|.
+//   |resource_url| is the url corresponding to the requested resource.
+//   If this url is invalid we don't cache the code.
+//   |requesting_origin| is the origin that has requested the resource.
+//   If this is a unique origin, then we don't cache the code.
+// For example, if http://script.com/script1.js is requested by
+// http://example.com, then http://script.com/script.js is the resource_url
+// and example.com is the requesting_origin.
+bool IsAllowedToCache(const GURL& resource_url,
+                      const url::Origin& requesting_origin) {
+  // Don't cache the code corresponding to unique origins. The same-origin
+  // checks should always fail for unique origins but the serialized value of
+  // unique origins does not ensure this.
+  if (requesting_origin.unique())
+    return false;
+
+  // If the resource url or requesting url is invalid don't cache the code.
+  if (!resource_url.is_valid())
+    return false;
+
+  return true;
+}
+
+// Generates the cache key for the given |resource_url| and the
+// |requesting_origin|. This returns the key by concatenating the
+// serialized url and origin with a separator in between.
+std::string GetCacheKey(const GURL& resource_url,
+                        const url::Origin& requesting_origin) {
+  DCHECK(!requesting_origin.unique());
+  DCHECK(resource_url.is_valid());
+  // Add a prefix _ so it can't be parsed as a valid URL.
+  std::string key = "_key";
+  // Remove reference, username and password sections of the URL.
+  key.append(net::HttpUtil::SpecForRequest(resource_url));
+  // Add a separator between URL and origin to avoid any possibility of
+  // attacks by crafting the URL. URLs do not contain any control ASCII
+  // characters, and also space is encoded. So use ' \n' as a seperator.
+  key.append(" \n");
+  key.append(requesting_origin.Serialize());
+  return key;
+}
+}  // namespace
+
+// Stores the information about a pending request while disk backend is
+// being initialized.
+class GeneratedCodeCache::PendingOperation {
+ public:
+  PendingOperation(Operation op,
+                   std::string key,
+                   scoped_refptr<net::IOBufferWithSize>);
+  PendingOperation(Operation op, std::string key, const ReadDataCallback&);
+  PendingOperation(Operation op, std::string key);
+
+  ~PendingOperation();
+
+  Operation operation() const { return op_; }
+  const std::string& key() const { return key_; }
+  const scoped_refptr<net::IOBufferWithSize> data() const { return data_; }
+  const ReadDataCallback& callback() const { return callback_; }
+
+ private:
+  const Operation op_;
+  const std::string key_;
+  const scoped_refptr<net::IOBufferWithSize> data_;
+  const ReadDataCallback callback_;
+};
+
+GeneratedCodeCache::PendingOperation::PendingOperation(
+    Operation op,
+    std::string key,
+    scoped_refptr<net::IOBufferWithSize> buffer)
+    : op_(op),
+      key_(std::move(key)),
+      data_(buffer),
+      callback_(ReadDataCallback()) {}
+
+GeneratedCodeCache::PendingOperation::PendingOperation(
+    Operation op,
+    std::string key,
+    const ReadDataCallback& callback)
+    : op_(op),
+      key_(std::move(key)),
+      data_(scoped_refptr<net::IOBufferWithSize>()),
+      callback_(callback) {}
+
+GeneratedCodeCache::PendingOperation::PendingOperation(Operation op,
+                                                       std::string key)
+    : op_(op),
+      key_(std::move(key)),
+      data_(scoped_refptr<net::IOBufferWithSize>()),
+      callback_(ReadDataCallback()) {}
+
+GeneratedCodeCache::PendingOperation::~PendingOperation() = default;
+
+// Static factory method.
+std::unique_ptr<GeneratedCodeCache> GeneratedCodeCache::Create(
+    const base::FilePath& path,
+    int max_size_bytes) {
+  return base::WrapUnique(new GeneratedCodeCache(path, max_size_bytes));
+}
+
+GeneratedCodeCache::~GeneratedCodeCache() = default;
+
+void GeneratedCodeCache::WriteData(
+    const GURL& url,
+    const url::Origin& origin,
+    scoped_refptr<net::IOBufferWithSize> buffer) {
+  // Silently ignore the requests.
+  if (backend_state_ == kFailed)
+    return;
+
+  // If the url is invalid or if it is from a unique origin, we should not
+  // cache the code.
+  if (!IsAllowedToCache(url, origin))
+    return;
+
+  std::string key = GetCacheKey(url, origin);
+  if (backend_state_ != kInitialized) {
+    // Insert it into the list of pending operations while the backend is
+    // still being opened.
+    pending_ops_.push_back(std::make_unique<PendingOperation>(
+        Operation::kWrite, std::move(key), buffer));
+    return;
+  }
+
+  WriteDataImpl(key, buffer);
+}
+
+void GeneratedCodeCache::FetchEntry(const GURL& url,
+                                    const url::Origin& origin,
+                                    ReadDataCallback read_data_callback) {
+  if (backend_state_ == kFailed) {
+    // Silently ignore the requests.
+    std::move(read_data_callback).Run(scoped_refptr<net::IOBufferWithSize>());
+    return;
+  }
+
+  // If the url is invalid or if it is from a unique origin, we should not
+  // cache the code.
+  if (!IsAllowedToCache(url, origin)) {
+    std::move(read_data_callback).Run(scoped_refptr<net::IOBufferWithSize>());
+    return;
+  }
+
+  std::string key = GetCacheKey(url, origin);
+  if (backend_state_ != kInitialized) {
+    // Insert it into the list of pending operations while the backend is
+    // still being opened.
+    pending_ops_.push_back(std::make_unique<PendingOperation>(
+        Operation::kFetch, std::move(key), read_data_callback));
+    return;
+  }
+
+  FetchEntryImpl(key, read_data_callback);
+}
+
+void GeneratedCodeCache::DeleteEntry(const GURL& url,
+                                     const url::Origin& origin) {
+  // Silently ignore the requests.
+  if (backend_state_ == kFailed)
+    return;
+
+  // If the url is invalid or if it is from a unique origin, we should not
+  // cache the code.
+  if (!IsAllowedToCache(url, origin))
+    return;
+
+  std::string key = GetCacheKey(url, origin);
+  if (backend_state_ != kInitialized) {
+    // Insert it into the list of pending operations while the backend is
+    // still being opened.
+    pending_ops_.push_back(
+        std::make_unique<PendingOperation>(Operation::kDelete, std::move(key)));
+    return;
+  }
+
+  DeleteEntryImpl(key);
+}
+
+GeneratedCodeCache::GeneratedCodeCache(const base::FilePath& path,
+                                       int max_size_bytes)
+    : backend_state_(kUnInitialized),
+      path_(path),
+      max_size_bytes_(max_size_bytes),
+      weak_ptr_factory_(this) {
+  CreateBackend();
+}
+
+void GeneratedCodeCache::CreateBackend() {
+  // Create a new Backend pointer that cleans itself if the GeneratedCodeCache
+  // instance is not live when the CreateCacheBackend finishes.
+  scoped_refptr<base::RefCountedData<ScopedBackendPtr>> shared_backend_ptr =
+      new base::RefCountedData<ScopedBackendPtr>();
+
+  net::CompletionOnceCallback create_backend_complete =
+      base::BindOnce(&GeneratedCodeCache::DidCreateBackend,
+                     weak_ptr_factory_.GetWeakPtr(), shared_backend_ptr);
+
+  // If the initialization of the existing cache fails, this call would delete
+  // all the contents and recreates a new one.
+  int rv = disk_cache::CreateCacheBackend(
+      net::GENERATED_CODE_CACHE, net::CACHE_BACKEND_SIMPLE, path_,
+      max_size_bytes_, true, nullptr, &shared_backend_ptr->data,
+      std::move(create_backend_complete));
+  if (rv != net::ERR_IO_PENDING) {
+    DidCreateBackend(shared_backend_ptr, rv);
+  }
+}
+
+void GeneratedCodeCache::DidCreateBackend(
+    scoped_refptr<base::RefCountedData<ScopedBackendPtr>> backend_ptr,
+    int rv) {
+  if (rv != net::OK) {
+    backend_state_ = kFailed;
+    // Process pending operations to process any required callbacks.
+    IssuePendingOperations();
+    return;
+  }
+
+  backend_ = std::move(backend_ptr->data);
+  backend_state_ = kInitialized;
+  IssuePendingOperations();
+}
+
+void GeneratedCodeCache::IssuePendingOperations() {
+  DCHECK_EQ(backend_state_, kInitialized);
+  // Issue all the pending operations that were received when creating
+  // the backend.
+  for (auto const& op : pending_ops_) {
+    switch (op->operation()) {
+      case kFetch:
+        FetchEntryImpl(op->key(), op->callback());
+        break;
+      case kWrite:
+        WriteDataImpl(op->key(), op->data());
+        break;
+      case kDelete:
+        DeleteEntryImpl(op->key());
+        break;
+    }
+  }
+  pending_ops_.clear();
+}
+
+void GeneratedCodeCache::WriteDataImpl(
+    const std::string& key,
+    scoped_refptr<net::IOBufferWithSize> buffer) {
+  if (backend_state_ != kInitialized)
+    return;
+
+  scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry_ptr =
+      new base::RefCountedData<disk_cache::Entry*>();
+  net::CompletionOnceCallback callback =
+      base::BindOnce(&GeneratedCodeCache::OpenCompleteForWriteData,
+                     weak_ptr_factory_.GetWeakPtr(), buffer, key, entry_ptr);
+
+  int result =
+      backend_->OpenEntry(key, net::LOW, &entry_ptr->data, std::move(callback));
+  if (result != net::ERR_IO_PENDING) {
+    OpenCompleteForWriteData(buffer, key, entry_ptr, result);
+  }
+}
+
+void GeneratedCodeCache::OpenCompleteForWriteData(
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    const std::string& key,
+    scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
+    int rv) {
+  if (rv != net::OK) {
+    net::CompletionOnceCallback callback =
+        base::BindOnce(&GeneratedCodeCache::CreateCompleteForWriteData,
+                       weak_ptr_factory_.GetWeakPtr(), buffer, entry);
+
+    int result =
+        backend_->CreateEntry(key, net::LOW, &entry->data, std::move(callback));
+    if (result != net::ERR_IO_PENDING) {
+      CreateCompleteForWriteData(buffer, entry, result);
+    }
+    return;
+  }
+
+  DCHECK(entry->data);
+  disk_cache::ScopedEntryPtr disk_entry(entry->data);
+
+  // This call will truncate the data. This is safe to do since we read the
+  // entire data at the same time currently. If we want to read in parts we have
+  // to doom the entry first.
+  disk_entry->WriteData(kDataIndex, 0, buffer.get(), buffer->size(),
+                        net::CompletionOnceCallback(), true);
+}
+
+void GeneratedCodeCache::CreateCompleteForWriteData(
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
+    int rv) {
+  if (rv != net::OK)
+    return;
+
+  DCHECK(entry->data);
+  disk_cache::ScopedEntryPtr disk_entry(entry->data);
+  disk_entry->WriteData(kDataIndex, 0, buffer.get(), buffer->size(),
+                        net::CompletionOnceCallback(), true);
+}
+
+void GeneratedCodeCache::FetchEntryImpl(const std::string& key,
+                                        ReadDataCallback read_data_callback) {
+  if (backend_state_ != kInitialized) {
+    std::move(read_data_callback).Run(scoped_refptr<net::IOBufferWithSize>());
+    return;
+  }
+
+  scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry_ptr =
+      new base::RefCountedData<disk_cache::Entry*>();
+
+  net::CompletionOnceCallback callback = base::BindOnce(
+      &GeneratedCodeCache::OpenCompleteForReadData,
+      weak_ptr_factory_.GetWeakPtr(), read_data_callback, entry_ptr);
+
+  // This is a part of loading cycle and hence should run with a high priority.
+  int result = backend_->OpenEntry(key, net::HIGHEST, &entry_ptr->data,
+                                   std::move(callback));
+  if (result != net::ERR_IO_PENDING) {
+    OpenCompleteForReadData(read_data_callback, entry_ptr, result);
+  }
+}
+
+void GeneratedCodeCache::OpenCompleteForReadData(
+    ReadDataCallback read_data_callback,
+    scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
+    int rv) {
+  if (rv != net::OK) {
+    std::move(read_data_callback).Run(scoped_refptr<net::IOBufferWithSize>());
+    return;
+  }
+
+  // There should be a valid entry if the open was successful.
+  DCHECK(entry->data);
+
+  disk_cache::ScopedEntryPtr disk_entry(entry->data);
+  int size = disk_entry->GetDataSize(kDataIndex);
+  scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(size));
+  net::CompletionOnceCallback callback = base::BindOnce(
+      &GeneratedCodeCache::ReadDataComplete, weak_ptr_factory_.GetWeakPtr(),
+      read_data_callback, buffer);
+  int result = disk_entry->ReadData(kDataIndex, 0, buffer.get(), size,
+                                    std::move(callback));
+  if (result != net::ERR_IO_PENDING) {
+    ReadDataComplete(read_data_callback, buffer, result);
+  }
+}
+
+void GeneratedCodeCache::ReadDataComplete(
+    ReadDataCallback callback,
+    scoped_refptr<net::IOBufferWithSize> buffer,
+    int rv) {
+  if (rv != buffer->size()) {
+    std::move(callback).Run(scoped_refptr<net::IOBufferWithSize>());
+  } else {
+    std::move(callback).Run(buffer);
+  }
+}
+
+void GeneratedCodeCache::DeleteEntryImpl(const std::string& key) {
+  if (backend_state_ != kInitialized)
+    return;
+
+  backend_->DoomEntry(key, net::LOWEST, net::CompletionOnceCallback());
+}
+
+}  // namespace content
diff --git a/content/browser/code_cache/generated_code_cache.h b/content/browser/code_cache/generated_code_cache.h
new file mode 100644
index 0000000..57e92b76
--- /dev/null
+++ b/content/browser/code_cache/generated_code_cache.h
@@ -0,0 +1,125 @@
+// 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_CODE_CACHE_GENERATED_CODE_CACHE_H_
+#define CONTENT_BROWSER_CODE_CACHE_GENERATED_CODE_CACHE_H_
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "content/common/content_export.h"
+#include "net/base/io_buffer.h"
+#include "net/disk_cache/disk_cache.h"
+#include "url/origin.h"
+
+namespace content {
+
+// Cache for storing generated code from the renderer on the disk.
+// This cache is keyed on two keys: |resource_url| and |requesting_origin|.
+// The |resource_url| is the url of the resource that was requested and the
+// |requesting_origin| is the origin that requested this resource. This origin
+// is used to enforce site isolation policy on stored code. We don't cache the
+// code corresponding to unique origins or invalid URLs.
+//
+// This uses a simple disk_cache backend. It just stores one data stream and
+// stores response_time + generated code as one data blob.
+// TODO(mythria): Update this comment if the design changes.
+//
+// There exists one cache per storage partition and is owned by the storage
+// partition. This cache is created, accessed and destroyed on the I/O thread.
+class CONTENT_EXPORT GeneratedCodeCache {
+ public:
+  using ReadDataCallback =
+      base::RepeatingCallback<void(scoped_refptr<net::IOBufferWithSize>)>;
+
+  // Creates a GeneratedCodeCache with the specified path and the maximum size.
+  static std::unique_ptr<GeneratedCodeCache> Create(const base::FilePath& path,
+                                                    int max_size);
+
+  ~GeneratedCodeCache();
+
+  // Writes data to the cache. If there is an entry corresponding to
+  // <|url|, |origin|> this overwrites the existing data. If there is no entry
+  // it creates a new one.
+  void WriteData(const GURL& url,
+                 const url::Origin& origin,
+                 scoped_refptr<net::IOBufferWithSize>);
+
+  // Fetch entry corresponding to <url, origin> from the cache and pass
+  // it using the ReadDataCallback.
+  void FetchEntry(const GURL& url, const url::Origin& origin, ReadDataCallback);
+
+  // Delete the entry corresponding to <url, origin>
+  void DeleteEntry(const GURL& url, const url::Origin& origin);
+
+  const base::FilePath& path() const { return path_; }
+
+ private:
+  class PendingOperation;
+  using ScopedBackendPtr = std::unique_ptr<disk_cache::Backend>;
+
+  // State of the backend.
+  enum BackendState { kUnInitialized, kInitializing, kInitialized, kFailed };
+
+  // The operation requested.
+  enum Operation { kFetch, kWrite, kDelete };
+
+  // Data streams corresponding to each entry.
+  enum { kDataIndex = 1 };
+
+  GeneratedCodeCache(const base::FilePath& path, int max_size_bytes);
+
+  // Creates a simple_disk_cache backend.
+  void CreateBackend();
+  void DidCreateBackend(
+      scoped_refptr<base::RefCountedData<ScopedBackendPtr>> backend_ptr,
+      int rv);
+
+  // The requests that are received while tha backend is being initialized
+  // are recorded in pending operations list. This function issues all pending
+  // operations.
+  void IssuePendingOperations();
+
+  // Write entry to cache
+  void WriteDataImpl(const std::string& key,
+                     scoped_refptr<net::IOBufferWithSize> buffer);
+  void OpenCompleteForWriteData(
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      const std::string& key,
+      scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
+      int rv);
+  void CreateCompleteForWriteData(
+      scoped_refptr<net::IOBufferWithSize> buffer,
+      scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
+      int rv);
+
+  // Fetch entry from cache
+  void FetchEntryImpl(const std::string& key, ReadDataCallback);
+  void OpenCompleteForReadData(
+      ReadDataCallback callback,
+      scoped_refptr<base::RefCountedData<disk_cache::Entry*>> entry,
+      int rv);
+  void ReadDataComplete(ReadDataCallback callback,
+                        scoped_refptr<net::IOBufferWithSize> buffer,
+                        int rv);
+
+  // Delete entry from cache
+  void DeleteEntryImpl(const std::string& key);
+
+  std::unique_ptr<disk_cache::Backend> backend_;
+  BackendState backend_state_;
+
+  std::vector<std::unique_ptr<PendingOperation>> pending_ops_;
+
+  base::FilePath path_;
+  int max_size_bytes_;
+
+  base::WeakPtrFactory<GeneratedCodeCache> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GeneratedCodeCache);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_CODE_CACHE_GENERATED_CODE_CACHE_H_
diff --git a/content/browser/code_cache/generated_code_cache_unittest.cc b/content/browser/code_cache/generated_code_cache_unittest.cc
new file mode 100644
index 0000000..45ea2246
--- /dev/null
+++ b/content/browser/code_cache/generated_code_cache_unittest.cc
@@ -0,0 +1,293 @@
+// 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/code_cache/generated_code_cache.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/test/scoped_task_environment.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace content {
+
+class GeneratedCodeCacheTest : public testing::Test {
+ public:
+  static const int kMaxSizeInBytes = 1024 * 1024;
+  static constexpr char kInitialUrl[] = "http://example.com/script.js";
+  static constexpr char kInitialOrigin[] = "http://example.com";
+  static constexpr char kInitialData[] = "InitialData";
+
+  GeneratedCodeCacheTest() = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(cache_dir_.CreateUniqueTempDir());
+    cache_path_ = cache_dir_.GetPath();
+    generated_code_cache_ =
+        GeneratedCodeCache::Create(cache_path_, kMaxSizeInBytes);
+  }
+
+  void TearDown() override {
+    disk_cache::FlushCacheThreadForTesting();
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  // This function initializes the cache and waits till the transaction is
+  // finished. When this function returns, the backend is already initialized.
+  void InitializeCache() {
+    GURL url(kInitialUrl);
+    url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+    WriteToCache(url, origin, kInitialData);
+    scoped_task_environment_.RunUntilIdle();
+  }
+
+  // This function initializes the cache and reopens it. When this function
+  // returns, the backend initialization is not complete yet. This is used
+  // to test the pending operaions path.
+  void InitializeCacheAndReOpen() {
+    InitializeCache();
+    generated_code_cache_.reset();
+    generated_code_cache_ =
+        GeneratedCodeCache::Create(cache_path_, kMaxSizeInBytes);
+  }
+
+  void WriteToCache(const GURL& url,
+                    const url::Origin& origin,
+                    const std::string& data) {
+    scoped_refptr<net::IOBufferWithSize> buffer(
+        new net::IOBufferWithSize(data.length()));
+    memcpy(buffer->data(), data.c_str(), data.length());
+    generated_code_cache_->WriteData(url, origin, buffer);
+  }
+
+  void DeleteFromCache(const GURL& url, const url::Origin& origin) {
+    generated_code_cache_->DeleteEntry(url, origin);
+  }
+
+  void FetchFromCache(const GURL& url, const url::Origin& origin) {
+    received_ = false;
+    GeneratedCodeCache::ReadDataCallback callback = base::BindRepeating(
+        &GeneratedCodeCacheTest::FetchEntryCallback, base::Unretained(this));
+    generated_code_cache_->FetchEntry(url, origin, callback);
+  }
+
+  void FetchEntryCallback(scoped_refptr<net::IOBufferWithSize> buffer) {
+    if (!buffer || !buffer->data()) {
+      received_ = true;
+      received_null_ = true;
+      return;
+    }
+    std::string str(buffer->data(), buffer->size());
+    received_ = true;
+    received_null_ = false;
+    received_data_ = str;
+  }
+
+ protected:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<GeneratedCodeCache> generated_code_cache_;
+  base::ScopedTempDir cache_dir_;
+  std::string received_data_;
+  bool received_;
+  bool received_null_;
+  base::FilePath cache_path_;
+};
+
+constexpr char GeneratedCodeCacheTest::kInitialUrl[];
+constexpr char GeneratedCodeCacheTest::kInitialOrigin[];
+constexpr char GeneratedCodeCacheTest::kInitialData[];
+
+TEST_F(GeneratedCodeCacheTest, FetchEntry) {
+  GURL url(kInitialUrl);
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  InitializeCache();
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(kInitialData, received_data_);
+}
+
+TEST_F(GeneratedCodeCacheTest, WriteEntry) {
+  GURL new_url("http://example1.com/script.js");
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  InitializeCache();
+  std::string data = "SerializedCodeForScript";
+  WriteToCache(new_url, origin, data);
+  FetchFromCache(new_url, origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(data, received_data_);
+}
+
+TEST_F(GeneratedCodeCacheTest, DeleteEntry) {
+  GURL url(kInitialUrl);
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  InitializeCache();
+  DeleteFromCache(url, origin);
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  ASSERT_TRUE(received_null_);
+}
+
+TEST_F(GeneratedCodeCacheTest, FetchEntryPendingOp) {
+  GURL url(kInitialUrl);
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  InitializeCacheAndReOpen();
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(kInitialData, received_data_);
+}
+
+TEST_F(GeneratedCodeCacheTest, WriteEntryPendingOp) {
+  GURL new_url("http://example1.com/script1.js");
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  InitializeCache();
+  std::string data = "SerializedCodeForScript";
+  WriteToCache(new_url, origin, data);
+  scoped_task_environment_.RunUntilIdle();
+  FetchFromCache(new_url, origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(data, received_data_);
+}
+
+TEST_F(GeneratedCodeCacheTest, DeleteEntryPendingOp) {
+  GURL url(kInitialUrl);
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  InitializeCacheAndReOpen();
+  DeleteFromCache(url, origin);
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  ASSERT_TRUE(received_null_);
+}
+
+TEST_F(GeneratedCodeCacheTest, UpdateDataOfExistingEntry) {
+  GURL url(kInitialUrl);
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  InitializeCache();
+  std::string new_data = "SerializedCodeForScriptOverwrite";
+  WriteToCache(url, origin, new_data);
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(new_data, received_data_);
+}
+
+TEST_F(GeneratedCodeCacheTest, FetchFailsForNonexistingOrigin) {
+  InitializeCache();
+  url::Origin new_origin = url::Origin::Create(GURL("http://not-example.com"));
+  FetchFromCache(GURL(kInitialUrl), new_origin);
+  scoped_task_environment_.RunUntilIdle();
+
+  ASSERT_TRUE(received_);
+  ASSERT_TRUE(received_null_);
+}
+
+TEST_F(GeneratedCodeCacheTest, FetchEntriesFromSameOrigin) {
+  GURL url("http://example.com/script.js");
+  GURL second_url("http://script.com/one.js");
+  url::Origin origin = url::Origin::Create(GURL(kInitialOrigin));
+
+  std::string data_first_resource = "SerializedCodeForFirstResource";
+  WriteToCache(url, origin, data_first_resource);
+
+  std::string data_second_resource = "SerializedCodeForSecondResource";
+  WriteToCache(second_url, origin, data_second_resource);
+  scoped_task_environment_.RunUntilIdle();
+
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(data_first_resource, received_data_);
+
+  FetchFromCache(second_url, origin);
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(data_second_resource, received_data_);
+}
+
+TEST_F(GeneratedCodeCacheTest, FetchSucceedsFromDifferentOrigins) {
+  GURL url("http://example.com/script.js");
+  url::Origin origin = url::Origin::Create(GURL("http://example.com"));
+  url::Origin origin1 = url::Origin::Create(GURL("http://example1.com"));
+
+  std::string data_origin = "SerializedCodeForFirstOrigin";
+  WriteToCache(url, origin, data_origin);
+
+  std::string data_origin1 = "SerializedCodeForSecondOrigin";
+  WriteToCache(url, origin1, data_origin1);
+  scoped_task_environment_.RunUntilIdle();
+
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(data_origin, received_data_);
+
+  FetchFromCache(url, origin1);
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(received_);
+  EXPECT_EQ(data_origin1, received_data_);
+}
+
+TEST_F(GeneratedCodeCacheTest, FetchFailsForUniqueOrigin) {
+  GURL url("http://example.com/script.js");
+  url::Origin origin =
+      url::Origin::Create(GURL("data:text/html,<script></script>"));
+
+  std::string data = "SerializedCodeForUniqueOrigin";
+  WriteToCache(url, origin, data);
+  scoped_task_environment_.RunUntilIdle();
+
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(received_);
+  ASSERT_TRUE(received_null_);
+}
+
+TEST_F(GeneratedCodeCacheTest, FetchFailsForInvalidOrigin) {
+  GURL url("http://example.com/script.js");
+  url::Origin origin = url::Origin::Create(GURL("invalidURL"));
+
+  std::string data = "SerializedCodeForInvalidOrigin";
+  WriteToCache(url, origin, data);
+  scoped_task_environment_.RunUntilIdle();
+
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(received_);
+  ASSERT_TRUE(received_null_);
+}
+
+TEST_F(GeneratedCodeCacheTest, FetchFailsForInvalidURL) {
+  GURL url("InvalidURL");
+  url::Origin origin = url::Origin::Create(GURL("http://example.com"));
+
+  std::string data = "SerializedCodeForInvalidURL";
+  WriteToCache(url, origin, data);
+  scoped_task_environment_.RunUntilIdle();
+
+  FetchFromCache(url, origin);
+  scoped_task_environment_.RunUntilIdle();
+  ASSERT_TRUE(received_);
+  ASSERT_TRUE(received_null_);
+}
+}  // namespace content
diff --git a/content/browser/cookie_store/cookie_store_manager_unittest.cc b/content/browser/cookie_store/cookie_store_manager_unittest.cc
index b694ae0..b74748e 100644
--- a/content/browser/cookie_store/cookie_store_manager_unittest.cc
+++ b/content/browser/cookie_store/cookie_store_manager_unittest.cc
@@ -13,7 +13,7 @@
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/content/browser/payments/payment_app_content_unittest_base.cc b/content/browser/payments/payment_app_content_unittest_base.cc
index c8210a3..d604c199 100644
--- a/content/browser/payments/payment_app_content_unittest_base.cc
+++ b/content/browser/payments/payment_app_content_unittest_base.cc
@@ -15,7 +15,7 @@
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/storage_partition_impl.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index ca22a78..71f7853 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -55,7 +55,7 @@
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
 #include "media/base/media_switches.h"
-#include "media/capture/video/video_capture_device_factory.h"
+#include "media/capture/video/create_video_capture_device_factory.h"
 #include "media/capture/video/video_capture_system_impl.h"
 #include "services/video_capture/public/uma/video_capture_service_event.h"
 #include "url/gurl.h"
@@ -487,7 +487,7 @@
           video_capture::uma::BROWSER_USING_LEGACY_CAPTURE);
       video_capture_provider = InProcessVideoCaptureProvider::CreateInstance(
           std::make_unique<media::VideoCaptureSystemImpl>(
-              media::VideoCaptureDeviceFactory::CreateFactory(
+              media::CreateVideoCaptureDeviceFactory(
                   BrowserThread::GetTaskRunnerForThread(BrowserThread::UI))),
           std::move(device_task_runner),
           base::BindRepeating(&SendVideoCaptureLogMessage));
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 1345c7e2..9aac9b4 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -384,6 +384,20 @@
     lastWheelOrTouchEventLatencyInfo = ui::LatencyInfo(ui_latency);
   }
 
+  void ForwardGestureEventWithLatencyInfo(
+      const blink::WebGestureEvent& gesture_event,
+      const ui::LatencyInfo& ui_latency) override {
+    RenderWidgetHostImpl::ForwardGestureEventWithLatencyInfo(gesture_event,
+                                                             ui_latency);
+    last_forwarded_gesture_event_ = gesture_event;
+  }
+
+  base::Optional<WebGestureEvent> GetAndResetLastForwardedGestureEvent() {
+    base::Optional<WebGestureEvent> ret;
+    last_forwarded_gesture_event_.swap(ret);
+    return ret;
+  }
+
   static MockRenderWidgetHostImpl* Create(RenderWidgetHostDelegate* delegate,
                                           RenderProcessHost* process,
                                           int32_t routing_id) {
@@ -430,6 +444,7 @@
 
   bool new_content_rendering_timeout_fired_ = false;
   std::unique_ptr<MockWidgetImpl> widget_impl_;
+  base::Optional<WebGestureEvent> last_forwarded_gesture_event_;
 };
 
 class TestScopedKeyboardHook : public aura::ScopedKeyboardHook {
@@ -2440,6 +2455,43 @@
   EXPECT_EQ(blink::kWebGestureDeviceTouchscreen, gesture_event->SourceDevice());
 }
 
+TEST_F(RenderWidgetHostViewAuraTest,
+       SyntheticFlingCancelAtTouchpadScrollBegin) {
+  ui::ScrollEvent scroll_event(ui::ET_SCROLL, gfx::Point(2, 2),
+                               ui::EventTimeForNow(), 0, 0, 5, 0, 5, 2);
+
+  // Send the beginning scroll event. This should generate a synthetic fling
+  // cancel to cancel any ongoing flings before the start of this scroll.
+  view_->OnScrollEvent(&scroll_event);
+  base::RunLoop().RunUntilIdle();
+  base::Optional<WebGestureEvent> last_gesture =
+      widget_host_->GetAndResetLastForwardedGestureEvent();
+  ASSERT_TRUE(last_gesture);
+  EXPECT_EQ(WebInputEvent::kGestureFlingCancel, last_gesture->GetType());
+
+  // Consume the wheel to prevent gesture scrolls from interfering with the
+  // rest of the test.
+  MockWidgetInputHandler::MessageVector dispatched_events =
+      GetAndResetDispatchedMessages();
+  EXPECT_EQ("MouseWheel", GetMessageNames(dispatched_events));
+  dispatched_events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
+  dispatched_events = GetAndResetDispatchedMessages();
+  EXPECT_EQ(0U, dispatched_events.size());
+
+  // Send a scroll update. A synthetic fling cancel has already been sent for
+  // this sequence, so we should not generate another.
+  view_->OnScrollEvent(&scroll_event);
+  base::RunLoop().RunUntilIdle();
+  last_gesture = widget_host_->GetAndResetLastForwardedGestureEvent();
+  EXPECT_FALSE(last_gesture);
+
+  dispatched_events = GetAndResetDispatchedMessages();
+  EXPECT_EQ("MouseWheel", GetMessageNames(dispatched_events));
+  dispatched_events[0]->ToEvent()->CallCallback(INPUT_EVENT_ACK_STATE_CONSUMED);
+  dispatched_events = GetAndResetDispatchedMessages();
+  EXPECT_EQ(0U, dispatched_events.size());
+}
+
 // Checks that touch-event state is maintained correctly for multiple touch
 // points.
 TEST_F(RenderWidgetHostViewAuraTest, MultiTouchPointsStates) {
diff --git a/content/browser/renderer_host/render_widget_host_view_event_handler.cc b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
index c079001..0f9d0a1c 100644
--- a/content/browser/renderer_host/render_widget_host_view_event_handler.cc
+++ b/content/browser/renderer_host/render_widget_host_view_event_handler.cc
@@ -433,23 +433,29 @@
     if (event->finger_count() != 2)
       return;
 #endif
-    blink::WebGestureEvent gesture_event = ui::MakeWebGestureEventFlingCancel();
-    // Coordinates need to be transferred to the fling cancel gesture only
-    // for Surface-targeting to ensure that it is targeted to the correct
-    // RenderWidgetHost.
-    gesture_event.SetPositionInWidget(event->location_f());
     blink::WebMouseWheelEvent mouse_wheel_event = ui::MakeWebMouseWheelEvent(
         *event, base::Bind(&GetScreenLocationFromEvent));
     mouse_wheel_phase_handler_.AddPhaseIfNeededAndScheduleEndEvent(
         mouse_wheel_event, should_route_event);
+
+    base::Optional<blink::WebGestureEvent> maybe_synthetic_fling_cancel;
+    if (mouse_wheel_event.phase == blink::WebMouseWheelEvent::kPhaseBegan) {
+      maybe_synthetic_fling_cancel =
+          ui::MakeWebGestureEventFlingCancel(mouse_wheel_event);
+    }
+
     if (should_route_event) {
-      host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
-          host_view_, &gesture_event,
-          ui::LatencyInfo(ui::SourceEventType::WHEEL));
+      if (maybe_synthetic_fling_cancel) {
+        host_->delegate()->GetInputEventRouter()->RouteGestureEvent(
+            host_view_, &*maybe_synthetic_fling_cancel,
+            ui::LatencyInfo(ui::SourceEventType::WHEEL));
+      }
       host_->delegate()->GetInputEventRouter()->RouteMouseWheelEvent(
           host_view_, &mouse_wheel_event, *event->latency());
     } else {
-      host_->ForwardGestureEvent(gesture_event);
+      if (maybe_synthetic_fling_cancel) {
+        host_->ForwardGestureEvent(*maybe_synthetic_fling_cancel);
+      }
       host_->ForwardWheelEventWithLatencyInfo(mouse_wheel_event,
                                               *event->latency());
     }
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index 7cd81f2..531bc0f 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -26,7 +26,7 @@
 #include "content/common/content_export.h"
 #include "content/common/service_worker/controller_service_worker.mojom.h"
 #include "content/common/service_worker/embedded_worker.mojom.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "mojo/public/cpp/bindings/associated_binding.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h"
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index a7a9a31..e99cdef 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -22,7 +22,7 @@
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/common/service_worker/embedded_worker.mojom.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 3c22ac6..4942766 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -23,7 +23,7 @@
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/common/background_fetch/background_fetch_types.h"
 #include "content/common/renderer.mojom.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/public/common/push_event_payload.h"
 #include "content/public/test/mock_render_process_host.h"
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h
index 7f7b5916..9204ee9 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.h
+++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -20,7 +20,7 @@
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/common/service_worker/embedded_worker.mojom.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/cookies/cookie_change_dispatcher.h"
 #include "net/http/http_response_info.h"
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index 71d9109..dcc36e82 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -24,7 +24,7 @@
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/url_loader_factory_getter.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_thread.h"
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index 1ca92e6..29c19dc 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -15,7 +15,7 @@
 #include "base/time/time.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/common/content_export.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/resource_type.h"
 #include "mojo/public/cpp/system/data_pipe.h"
diff --git a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
index 5517d86..3b22bc4 100644
--- a/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader_unittest.cc
@@ -13,7 +13,7 @@
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/common/single_request_url_loader_factory.h"
 #include "content/public/test/mock_render_process_host.h"
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index 02f88cf..ddf1531 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -17,7 +17,7 @@
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_storage.h"
 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
diff --git a/content/browser/service_worker/service_worker_url_request_job.h b/content/browser/service_worker/service_worker_url_request_job.h
index 8b62afa..5c8f00e 100644
--- a/content/browser/service_worker/service_worker_url_request_job.h
+++ b/content/browser/service_worker/service_worker_url_request_job.h
@@ -23,7 +23,7 @@
 #include "content/browser/service_worker/service_worker_response_type.h"
 #include "content/browser/service_worker/service_worker_url_job_wrapper.h"
 #include "content/common/content_export.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/request_context_type.h"
 #include "content/public/common/resource_type.h"
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index c4316e97b..0753b834 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -37,7 +37,7 @@
 #include "content/browser/service_worker/service_worker_script_cache_map.h"
 #include "content/common/content_export.h"
 #include "content/common/service_worker/controller_service_worker.mojom.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "ipc/ipc_message.h"
 #include "mojo/public/cpp/bindings/interface_ptr.h"
@@ -326,7 +326,7 @@
   // Creates a callback that is to be used for marking simple events dispatched
   // through mojom::ServiceWorker as finished for the |request_id|.
   // Simple event means those events expecting a response with only a status
-  // code and the dispatch time. See service_worker_event_dispatcher.mojom.
+  // code and the dispatch time. See service_worker.mojom.
   SimpleEventCallback CreateSimpleEventCallback(int request_id);
 
   // This must be called when the worker is running.
diff --git a/content/browser/web_contents/web_contents_view_mac.h b/content/browser/web_contents/web_contents_view_mac.h
index cf3305f9..2e3f4f8 100644
--- a/content/browser/web_contents/web_contents_view_mac.h
+++ b/content/browser/web_contents/web_contents_view_mac.h
@@ -18,7 +18,8 @@
 #include "content/browser/web_contents/web_contents_view.h"
 #include "content/common/content_export.h"
 #include "content/common/drag_event_source_info.h"
-#include "ui/base/cocoa/base_view.h"
+#import "ui/base/cocoa/accessibility_hostable.h"
+#import "ui/base/cocoa/base_view.h"
 #include "ui/gfx/geometry/size.h"
 
 @class WebDragDest;
@@ -40,7 +41,7 @@
 }
 
 CONTENT_EXPORT
-@interface WebContentsViewCocoa : BaseView {
+@interface WebContentsViewCocoa : BaseView<AccessibilityHostable> {
  @private
   // Instances of this class are owned by both webContentsView_ and AppKit. It
   // is possible for an instance to outlive its webContentsView_. The
@@ -48,6 +49,7 @@
   content::WebContentsViewMac* webContentsView_;
   base::scoped_nsobject<WebDragSource> dragSource_;
   base::scoped_nsobject<WebDragDest> dragDest_;
+  base::scoped_nsobject<id> accessibilityParent_;
   BOOL mouseDownCanMoveWindow_;
 }
 
@@ -57,6 +59,7 @@
 // NSDraggingSource. It is supposedly deprecated, but the non-deprecated API
 // -[NSWindow dragImage:...] still relies on it.
 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal;
+
 @end
 
 namespace content {
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index 082b74e..03d7c9c 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -738,4 +738,18 @@
   [self updateWebContentsVisibility];
 }
 
+// AccessibilityHostable protocol implementation.
+- (void)setAccessibilityParentElement:(id)accessibilityParent {
+  accessibilityParent_.reset([accessibilityParent retain]);
+}
+
+// NSAccessibility informal protocol implementation.
+- (id)accessibilityAttributeValue:(NSString*)attribute {
+  if (accessibilityParent_ &&
+      [attribute isEqualToString:NSAccessibilityParentAttribute]) {
+    return accessibilityParent_;
+  }
+  return [super accessibilityAttributeValue:attribute];
+}
+
 @end
diff --git a/content/browser/web_contents/web_contents_view_mac_unittest.mm b/content/browser/web_contents/web_contents_view_mac_unittest.mm
index d18ac92..8ce3dad 100644
--- a/content/browser/web_contents/web_contents_view_mac_unittest.mm
+++ b/content/browser/web_contents/web_contents_view_mac_unittest.mm
@@ -9,6 +9,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_renderer_host.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 #include "ui/base/test/cocoa_helper.h"
 #import "ui/base/test/cocoa_helper.h"
@@ -23,6 +24,7 @@
 }  // namespace
 
 TEST_F(WebContentsViewCocoaTest, NonWebDragSourceTest) {
+  // The designated initializer is private but init should be fine in this case.
   base::scoped_nsobject<WebContentsViewCocoa> view(
       [[WebContentsViewCocoa alloc] init]);
 
@@ -36,6 +38,28 @@
       [view draggingSourceOperationMaskForLocal:NO]);
 }
 
+TEST_F(WebContentsViewCocoaTest, AccessibilityParentTest) {
+  // The designated initializer is private but init should be fine in this case.
+  base::scoped_nsobject<WebContentsViewCocoa> view(
+      [[WebContentsViewCocoa alloc] init]);
+
+  // NSBox so it participates in the a11y hierarchy.
+  base::scoped_nsobject<NSView> parent_view([[NSBox alloc] init]);
+  base::scoped_nsobject<NSView> accessibility_parent([[NSView alloc] init]);
+
+  [parent_view addSubview:view];
+  EXPECT_NSEQ([view accessibilityAttributeValue:NSAccessibilityParentAttribute],
+              parent_view);
+
+  [view setAccessibilityParentElement:accessibility_parent];
+  EXPECT_NSEQ([view accessibilityAttributeValue:NSAccessibilityParentAttribute],
+              accessibility_parent);
+
+  [view setAccessibilityParentElement:nil];
+  EXPECT_NSEQ([view accessibilityAttributeValue:NSAccessibilityParentAttribute],
+              parent_view);
+}
+
 namespace {
 
 class WebContentsViewMacTest : public RenderViewHostTestHarness {
diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
index 39951d3..80396e7ce 100644
--- a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
+++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
@@ -101,7 +101,8 @@
     return S_OK;
   }
 
-  if (!GetFontProxy().FindFamily(name, &family_index)) {
+  if (!GetFontProxyScopeWrapper().GetFontProxy().FindFamily(name,
+                                                            &family_index)) {
     LogFontProxyError(FIND_FAMILY_SEND_FAILED);
     return E_FAIL;
   }
@@ -145,7 +146,8 @@
   TRACE_EVENT0("dwrite", "FontProxy::GetFontFamilyCount");
 
   uint32_t family_count = 0;
-  if (!GetFontProxy().GetFamilyCount(&family_count)) {
+  if (!GetFontProxyScopeWrapper().GetFontProxy().GetFamilyCount(
+          &family_count)) {
     LogFontProxyError(GET_FAMILY_COUNT_SEND_FAILED);
     return 0;
   }
@@ -197,7 +199,8 @@
 
   std::vector<base::FilePath> file_names;
   std::vector<base::File> file_handles;
-  if (!GetFontProxy().GetFontFiles(*family_index, &file_names, &file_handles)) {
+  if (!GetFontProxyScopeWrapper().GetFontProxy().GetFontFiles(
+          *family_index, &file_names, &file_handles)) {
     LogFontProxyError(GET_FONT_FILES_SEND_FAILED);
     return E_FAIL;
   }
@@ -318,7 +321,8 @@
   TRACE_EVENT0("dwrite", "FontProxy::LoadFamilyNames");
 
   std::vector<mojom::DWriteStringPairPtr> pairs;
-  if (!GetFontProxy().GetFamilyNames(family_index, &pairs)) {
+  if (!GetFontProxyScopeWrapper().GetFontProxy().GetFamilyNames(family_index,
+                                                                &pairs)) {
     return false;
   }
   std::vector<std::pair<base::string16, base::string16>> strings;
@@ -360,7 +364,7 @@
                             {base::WithBaseSyncPrimitives()}));
 }
 
-mojom::DWriteFontProxy& DWriteFontCollectionProxy::GetFontProxy() {
+FontProxyScopeWrapper DWriteFontCollectionProxy::GetFontProxyScopeWrapper() {
   if (!font_proxy_) {
     mojom::DWriteFontProxyPtrInfo dwrite_font_proxy;
     if (main_task_runner_->RunsTasksInCurrentSequence()) {
@@ -377,7 +381,9 @@
     }
     SetProxy(std::move(dwrite_font_proxy));
   }
-  return **font_proxy_;
+  static base::ThreadLocalBoolean font_proxy_method_in_flight;
+
+  return FontProxyScopeWrapper(font_proxy_.get(), &font_proxy_method_in_flight);
 }
 
 DWriteFontFamilyProxy::DWriteFontFamilyProxy() = default;
diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h
index 0943acd7..63f66d27 100644
--- a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h
+++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.h
@@ -22,6 +22,39 @@
 
 class DWriteFontFamilyProxy;
 
+class FontProxyScopeWrapper {
+ public:
+  FontProxyScopeWrapper(mojom::ThreadSafeDWriteFontProxyPtr* font_proxy,
+                        base::ThreadLocalBoolean* is_in_flight)
+      : font_proxy_(font_proxy), is_in_flight_(is_in_flight) {
+    // TODO(crbug.com/561873): Turn this into a DCHECK once instances of this
+    // CHECK have been found in crash reports and the referenced bug has been
+    // root-caused.
+    CHECK(!is_in_flight_->Get());
+    is_in_flight_->Set(true);
+  }
+
+  ~FontProxyScopeWrapper() {
+    // TODO(crbug.com/561873): Turn this into a DCHECK once instances of this
+    // CHECK have been found in crash reports and the referenced bug has been
+    // root-caused.
+    CHECK(is_in_flight_->Get());
+    is_in_flight_->Set(false);
+  }
+
+  content::mojom::DWriteFontProxy& GetFontProxy() const {
+    return **font_proxy_;
+  }
+
+  FontProxyScopeWrapper(FontProxyScopeWrapper&&) = default;
+  FontProxyScopeWrapper& operator=(FontProxyScopeWrapper&&) = default;
+
+ private:
+  mojom::ThreadSafeDWriteFontProxyPtr* font_proxy_;
+  base::ThreadLocalBoolean* is_in_flight_;
+  DISALLOW_COPY_AND_ASSIGN(FontProxyScopeWrapper);
+};
+
 // Implements a DirectWrite font collection that uses IPC to the browser to do
 // font enumeration. If a matching family is found, it will be loaded locally
 // into a custom font collection.
@@ -87,7 +120,7 @@
 
   bool CreateFamily(UINT32 family_index);
 
-  mojom::DWriteFontProxy& GetFontProxy();
+  FontProxyScopeWrapper GetFontProxyScopeWrapper();
 
  private:
   void SetProxy(mojom::DWriteFontProxyPtrInfo);
diff --git a/content/child/dwrite_font_proxy/font_fallback_win.cc b/content/child/dwrite_font_proxy/font_fallback_win.cc
index 25dbef98..95c5b9f 100644
--- a/content/child/dwrite_font_proxy/font_fallback_win.cc
+++ b/content/child/dwrite_font_proxy/font_fallback_win.cc
@@ -100,7 +100,7 @@
 
   mojom::MapCharactersResultPtr result;
 
-  if (!GetFontProxy().MapCharacters(
+  if (!GetFontProxyScopeWrapper().GetFontProxy().MapCharacters(
           text_chunk,
           mojom::DWriteFontStyle::New(base_weight, base_style, base_stretch),
           locale, source->GetParagraphReadingDirection(), base_family_name,
@@ -222,8 +222,8 @@
     family_list.pop_back();
 }
 
-mojom::DWriteFontProxy& FontFallback::GetFontProxy() {
-  return collection_->GetFontProxy();
+FontProxyScopeWrapper FontFallback::GetFontProxyScopeWrapper() {
+  return collection_->GetFontProxyScopeWrapper();
 }
 
 }  // namespace content
diff --git a/content/child/dwrite_font_proxy/font_fallback_win.h b/content/child/dwrite_font_proxy/font_fallback_win.h
index ca22abc..07242035 100644
--- a/content/child/dwrite_font_proxy/font_fallback_win.h
+++ b/content/child/dwrite_font_proxy/font_fallback_win.h
@@ -65,7 +65,7 @@
                        const wchar_t* base_family_name);
 
  private:
-  mojom::DWriteFontProxy& GetFontProxy();
+  FontProxyScopeWrapper GetFontProxyScopeWrapper();
 
   Microsoft::WRL::ComPtr<DWriteFontCollectionProxy> collection_;
 
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 385777f..d82d7ef 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -106,6 +106,9 @@
   if (base::FeatureList::IsEnabled(features::kBlinkHeapIncrementalMarking))
     WebRuntimeFeatures::EnableBlinkHeapIncrementalMarking(true);
 
+  if (base::FeatureList::IsEnabled(features::kBloatedRendererDetection))
+    WebRuntimeFeatures::EnableBloatedRendererDetection(true);
+
   if (command_line.HasSwitch(switches::kDisableDatabases))
     WebRuntimeFeatures::EnableDatabase(false);
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 6ffce59..d02f2ea 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -553,8 +553,8 @@
     "renderer_host.mojom",
     "service_worker/controller_service_worker.mojom",
     "service_worker/embedded_worker.mojom",
+    "service_worker/service_worker.mojom",
     "service_worker/service_worker_container.mojom",
-    "service_worker/service_worker_event_dispatcher.mojom",
     "service_worker/service_worker_fetch_response_callback.mojom",
     "service_worker/service_worker_provider.mojom",
     "shared_worker/shared_worker.mojom",
diff --git a/content/common/background_fetch/background_fetch_struct_traits.cc b/content/common/background_fetch/background_fetch_struct_traits.cc
index 0f2d005..c399fa9 100644
--- a/content/common/background_fetch/background_fetch_struct_traits.cc
+++ b/content/common/background_fetch/background_fetch_struct_traits.cc
@@ -4,7 +4,7 @@
 
 #include "content/common/background_fetch/background_fetch_struct_traits.h"
 
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_fetch_request_mojom_traits.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "mojo/public/cpp/bindings/array_data_view.h"
diff --git a/content/common/service_worker/controller_service_worker.mojom b/content/common/service_worker/controller_service_worker.mojom
index fc89294..8c5b520 100644
--- a/content/common/service_worker/controller_service_worker.mojom
+++ b/content/common/service_worker/controller_service_worker.mojom
@@ -4,7 +4,7 @@
 
 module content.mojom;
 
-import "content/common/service_worker/service_worker_event_dispatcher.mojom";
+import "content/common/service_worker/service_worker.mojom";
 import "content/common/service_worker/service_worker_fetch_response_callback.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "services/network/public/mojom/url_loader.mojom";
diff --git a/content/common/service_worker/embedded_worker.mojom b/content/common/service_worker/embedded_worker.mojom
index a777676..c17926d6 100644
--- a/content/common/service_worker/embedded_worker.mojom
+++ b/content/common/service_worker/embedded_worker.mojom
@@ -6,7 +6,7 @@
 
 import "content/common/native_types.mojom";
 import "content/common/service_worker/controller_service_worker.mojom";
-import "content/common/service_worker/service_worker_event_dispatcher.mojom";
+import "content/common/service_worker/service_worker.mojom";
 import "content/common/service_worker/service_worker_provider.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "mojo/public/mojom/base/time.mojom";
diff --git a/content/common/service_worker/service_worker_event_dispatcher.mojom b/content/common/service_worker/service_worker.mojom
similarity index 98%
rename from content/common/service_worker/service_worker_event_dispatcher.mojom
rename to content/common/service_worker/service_worker.mojom
index 7fad040..03b7838 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.mojom
+++ b/content/common/service_worker/service_worker.mojom
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// TODO(falken): Merge this file to service_worker.mojom.
-
 module content.mojom;
 
 import "content/common/service_worker/service_worker_fetch_response_callback.mojom";
diff --git a/content/common/service_worker/service_worker_event_dispatcher.typemap b/content/common/service_worker/service_worker.typemap
similarity index 92%
rename from content/common/service_worker/service_worker_event_dispatcher.typemap
rename to content/common/service_worker/service_worker.typemap
index cdd7720..e64c14e9 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.typemap
+++ b/content/common/service_worker/service_worker.typemap
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-mojom = "//content/common/service_worker/service_worker_event_dispatcher.mojom"
+mojom = "//content/common/service_worker/service_worker.mojom"
 public_headers = [
   "//content/common/background_fetch/background_fetch_types.h",
   "//third_party/blink/public/common/service_worker/service_worker_status_code.h",
diff --git a/content/common/typemaps.gni b/content/common/typemaps.gni
index 85870f8..6219420 100644
--- a/content/common/typemaps.gni
+++ b/content/common/typemaps.gni
@@ -15,7 +15,7 @@
   "//content/common/notifications/notification_types.typemap",
   "//content/common/push_messaging.typemap",
   "//content/common/render_frame_metadata.typemap",
-  "//content/common/service_worker/service_worker_event_dispatcher.typemap",
+  "//content/common/service_worker/service_worker.typemap",
   "//content/common/service_worker/service_worker_fetch_request.typemap",
   "//content/common/service_worker/service_worker_fetch_response.typemap",
   "//content/common/url_loader_factory_bundle.typemap",
diff --git a/content/public/browser/browsing_data_remover.h b/content/public/browser/browsing_data_remover.h
index ba4694da..67596f7 100644
--- a/content/public/browser/browsing_data_remover.h
+++ b/content/public/browser/browsing_data_remover.h
@@ -168,7 +168,7 @@
 
   // Like Remove(), but in case of URL-keyed only removes data whose URL match
   // |filter_builder| (e.g. are on certain origin or domain).
-  // RemoveWithFilter() currently only works with FILTERABLE_DATATYPES.
+  // RemoveWithFilter() currently only works with FILTERABLE_DATA_TYPES.
   virtual void RemoveWithFilter(
       const base::Time& delete_begin,
       const base::Time& delete_end,
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 615bcf25..bcb5bc7e 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -55,6 +55,10 @@
 const base::Feature kBlinkHeapIncrementalMarking{
     "BlinkHeapIncrementalMarking", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable bloated renderer detection.
+const base::Feature kBloatedRendererDetection{
+    "BloatedRendererDetection", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Allows swipe left/right from touchpad change browser navigation. Currently
 // only enabled by default on CrOS.
 const base::Feature kTouchpadOverscrollHistoryNavigation {
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index b605f5c..673ec86 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -26,6 +26,7 @@
 CONTENT_EXPORT extern const base::Feature kAudioServiceLaunchOnStartup;
 CONTENT_EXPORT extern const base::Feature kAudioServiceOutOfProcess;
 CONTENT_EXPORT extern const base::Feature kBlinkHeapIncrementalMarking;
+CONTENT_EXPORT extern const base::Feature kBloatedRendererDetection;
 CONTENT_EXPORT extern const base::Feature kBlockCredentialedSubresources;
 CONTENT_EXPORT extern const base::Feature kBrotliEncoding;
 CONTENT_EXPORT extern const base::Feature kCacheInlineScriptCode;
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc
index e7bcc49..5a0e070 100644
--- a/content/public/test/test_utils.cc
+++ b/content/public/test/test_utils.cc
@@ -319,26 +319,33 @@
 WindowedNotificationObserver::WindowedNotificationObserver(
     int notification_type,
     const NotificationSource& source)
-    : source_(NotificationService::AllSources()) {
+    : seen_(false),
+      running_(false),
+      source_(NotificationService::AllSources()) {
   AddNotificationType(notification_type, source);
 }
 
 WindowedNotificationObserver::WindowedNotificationObserver(
     int notification_type,
     const ConditionTestCallback& callback)
-    : callback_(callback), source_(NotificationService::AllSources()) {
+    : seen_(false),
+      running_(false),
+      callback_(callback),
+      source_(NotificationService::AllSources()) {
   AddNotificationType(notification_type, source_);
 }
 
 WindowedNotificationObserver::WindowedNotificationObserver(
     int notification_type,
     const ConditionTestCallbackWithoutSourceAndDetails& callback)
-    : callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
+    : seen_(false),
+      running_(false),
+      callback_(base::Bind(&IgnoreSourceAndDetails, callback)),
       source_(NotificationService::AllSources()) {
   registrar_.Add(this, notification_type, source_);
 }
 
-WindowedNotificationObserver::~WindowedNotificationObserver() = default;
+WindowedNotificationObserver::~WindowedNotificationObserver() {}
 
 void WindowedNotificationObserver::AddNotificationType(
     int notification_type,
@@ -347,21 +354,30 @@
 }
 
 void WindowedNotificationObserver::Wait() {
-  if (!seen_)
-    run_loop_.Run();
+  if (seen_)
+    return;
+
+  running_ = true;
+  message_loop_runner_ = new MessageLoopRunner;
+  message_loop_runner_->Run();
   EXPECT_TRUE(seen_);
 }
 
-void WindowedNotificationObserver::Observe(int type,
-                                           const NotificationSource& source,
-                                           const NotificationDetails& details) {
+void WindowedNotificationObserver::Observe(
+    int type,
+    const NotificationSource& source,
+    const NotificationDetails& details) {
   source_ = source;
   details_ = details;
   if (!callback_.is_null() && !callback_.Run(source, details))
     return;
 
   seen_ = true;
-  run_loop_.Quit();
+  if (!running_)
+    return;
+
+  message_loop_runner_->Quit();
+  running_ = false;
 }
 
 InProcessUtilityThreadHelper::InProcessUtilityThreadHelper()
diff --git a/content/public/test/test_utils.h b/content/public/test/test_utils.h
index 514926a..46666670 100644
--- a/content/public/test/test_utils.h
+++ b/content/public/test/test_utils.h
@@ -251,14 +251,15 @@
                const NotificationDetails& details) override;
 
  private:
-  bool seen_ = false;
+  bool seen_;
+  bool running_;
   NotificationRegistrar registrar_;
 
   ConditionTestCallback callback_;
 
   NotificationSource source_;
   NotificationDetails details_;
-  base::RunLoop run_loop_;
+  scoped_refptr<MessageLoopRunner> message_loop_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowedNotificationObserver);
 };
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index d58c7068..9b9034b 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -31,6 +31,7 @@
 #include "ui/accessibility/ax_enum_util.h"
 #include "ui/accessibility/ax_event.h"
 #include "ui/accessibility/ax_node.h"
+#include "ui/accessibility/ax_role_properties.h"
 
 using blink::WebAXObject;
 using blink::WebDocument;
@@ -457,8 +458,20 @@
                                    block.AccessibilityIsIgnored())) {
       block = block.ParentObject();
     }
-    if (!block.IsDetached() && !block.Equals(obj)) {
+    if (!block.IsDetached() && !block.Equals(obj))
       serializer_.DeleteClientSubtree(block);
+
+    // Whenever there's a change within a table, invalidate the
+    // whole table so that row and cell indexes are recomputed.
+    ax::mojom::Role role = AXRoleFromBlink(obj.Role());
+    if (ui::IsTableLikeRole(role) || role == ax::mojom::Role::kRow ||
+        ui::IsCellOrTableHeaderRole(role)) {
+      auto table = obj;
+      while (!table.IsDetached() &&
+             !ui::IsTableLikeRole(AXRoleFromBlink(table.Role())))
+        table = table.ParentObject();
+      if (!table.IsDetached())
+        serializer_.DeleteClientSubtree(table);
     }
 
     VLOG(1) << "Accessibility event: " << ui::ToString(event.event_type)
diff --git a/content/renderer/media/stream/media_stream_audio_processor.cc b/content/renderer/media/stream/media_stream_audio_processor.cc
index 6d6e651..d71f259 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor.cc
@@ -36,6 +36,8 @@
 
 namespace content {
 
+using EchoCancellationType = AudioProcessingProperties::EchoCancellationType;
+
 namespace {
 
 using webrtc::AudioProcessing;
@@ -591,21 +593,10 @@
         new webrtc::ExperimentalAgc(true, startup_min_volume.value_or(0)));
   }
 
-  // Check if experimental echo canceller should be used.
-  if (properties.EchoCancellationIsWebRtcProvided()) {
-    base::Optional<bool> override_aec3;
-    // In unit tests not creating a message filter, |aec_dump_message_filter_|
-    // will be null. We can just ignore that. Other unit tests and browser tests
-    // ensure that we do get the filter when we should.
-    if (aec_dump_message_filter_)
-      override_aec3 = aec_dump_message_filter_->GetOverrideAec3();
-    using_aec3_ = override_aec3.value_or(
-        base::FeatureList::IsEnabled(features::kWebRtcUseEchoCanceller3));
-  }
-
   // Create and configure the webrtc::AudioProcessing.
   webrtc::AudioProcessingBuilder ap_builder;
-  if (using_aec3_) {
+  if (properties.echo_cancellation_type ==
+      EchoCancellationType::kEchoCancellationAec3) {
     webrtc::EchoCanceller3Config aec3_config;
     aec3_config.ep_strength.bounded_erl =
         base::FeatureList::IsEnabled(features::kWebRtcAecBoundedErlSetup);
@@ -633,8 +624,10 @@
     // Prepare for logging echo information. Do not log any echo information
     // when AEC3 is active, as the echo information then will not be properly
     // updated.
-    if (!using_aec3_)
+    if (properties.echo_cancellation_type !=
+        EchoCancellationType::kEchoCancellationAec3) {
       echo_information_ = std::make_unique<EchoInformation>();
+    }
   }
 
   if (properties.goog_noise_suppression)
diff --git a/content/renderer/media/stream/media_stream_audio_processor.h b/content/renderer/media/stream/media_stream_audio_processor.h
index c7b1d1c..c523757 100644
--- a/content/renderer/media/stream/media_stream_audio_processor.h
+++ b/content/renderer/media/stream/media_stream_audio_processor.h
@@ -120,10 +120,6 @@
  protected:
   ~MediaStreamAudioProcessor() override;
 
-  // True if AEC3 is used, false if it's not or no AEC is used at all. Used for
-  // verification in tests.
-  bool using_aec3_ = false;
-
  private:
   friend class MediaStreamAudioProcessorTest;
 
diff --git a/content/renderer/media/stream/media_stream_audio_processor_options.cc b/content/renderer/media/stream/media_stream_audio_processor_options.cc
index f950d9e..7a5a6274 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_options.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor_options.cc
@@ -85,7 +85,9 @@
 }
 
 bool AudioProcessingProperties::EchoCancellationIsWebRtcProvided() const {
-  return echo_cancellation_type == EchoCancellationType::kEchoCancellationAec2;
+  return echo_cancellation_type ==
+             EchoCancellationType::kEchoCancellationAec2 ||
+         echo_cancellation_type == EchoCancellationType::kEchoCancellationAec3;
 }
 
 EchoInformation::EchoInformation()
diff --git a/content/renderer/media/stream/media_stream_audio_processor_options.h b/content/renderer/media/stream/media_stream_audio_processor_options.h
index df07a9d..75dd60a 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_options.h
+++ b/content/renderer/media/stream/media_stream_audio_processor_options.h
@@ -38,6 +38,8 @@
     kEchoCancellationDisabled,
     // The WebRTC-provided AEC2 echo canceller.
     kEchoCancellationAec2,
+    // The WebRTC-provided AEC3 echo canceller.
+    kEchoCancellationAec3,
     // System echo canceller, for example an OS-provided or hardware echo
     // canceller.
     kEchoCancellationSystem
diff --git a/content/renderer/media/stream/media_stream_audio_processor_unittest.cc b/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
index a35695a..19dff28 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
@@ -72,35 +72,6 @@
 
 }  // namespace
 
-class AecDumpMessageFilterForTest : public AecDumpMessageFilter {
- public:
-  // This class is only used for setting |override_aec3_|, so we simply inject
-  // the current task runner.
-  AecDumpMessageFilterForTest()
-      : AecDumpMessageFilter(base::MessageLoopCurrent::Get()->task_runner(),
-                             base::MessageLoopCurrent::Get()->task_runner()) {}
-
-  void set_override_aec3(base::Optional<bool> override_aec3) {
-    override_aec3_ = override_aec3;
-  }
-
- protected:
-  ~AecDumpMessageFilterForTest() override {}
-};
-
-class MediaStreamAudioProcessorUnderTest : public MediaStreamAudioProcessor {
- public:
-  MediaStreamAudioProcessorUnderTest(
-      const AudioProcessingProperties& properties,
-      WebRtcPlayoutDataSource* playout_data_source)
-      : MediaStreamAudioProcessor(properties, playout_data_source) {}
-
-  bool using_aec3() { return using_aec3_; }
-
- protected:
-  ~MediaStreamAudioProcessorUnderTest() override {}
-};
-
 class MediaStreamAudioProcessorTest : public ::testing::Test {
  public:
   MediaStreamAudioProcessorTest()
@@ -239,8 +210,8 @@
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   AudioProcessingProperties properties;
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
+  scoped_refptr<MediaStreamAudioProcessor> audio_processor(
+      new rtc::RefCountedObject<MediaStreamAudioProcessor>(
           properties, webrtc_audio_device.get()));
   EXPECT_TRUE(audio_processor->has_audio_processing());
   audio_processor->OnCaptureFormatChanged(params_);
@@ -262,8 +233,8 @@
   properties.DisableDefaultProperties();
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
+  scoped_refptr<MediaStreamAudioProcessor> audio_processor(
+      new rtc::RefCountedObject<MediaStreamAudioProcessor>(
           properties, webrtc_audio_device.get()));
   EXPECT_FALSE(audio_processor->has_audio_processing());
   audio_processor->OnCaptureFormatChanged(params_);
@@ -288,8 +259,8 @@
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   AudioProcessingProperties properties;
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
+  scoped_refptr<MediaStreamAudioProcessor> audio_processor(
+      new rtc::RefCountedObject<MediaStreamAudioProcessor>(
           properties, webrtc_audio_device.get()));
   EXPECT_TRUE(audio_processor->has_audio_processing());
 
@@ -326,8 +297,8 @@
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   AudioProcessingProperties properties;
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
+  scoped_refptr<MediaStreamAudioProcessor> audio_processor(
+      new rtc::RefCountedObject<MediaStreamAudioProcessor>(
           properties, webrtc_audio_device.get()));
 
   EXPECT_TRUE(audio_processor->aec_dump_message_filter_.get());
@@ -348,8 +319,8 @@
   ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_directory.GetPath(),
                                              &temp_file_path));
   {
-    scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-        new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
+    scoped_refptr<MediaStreamAudioProcessor> audio_processor(
+        new rtc::RefCountedObject<MediaStreamAudioProcessor>(
             properties, webrtc_audio_device.get()));
 
     // Start and stop recording.
@@ -378,8 +349,8 @@
   // Turn off the audio processing and turn on the stereo channels mirroring.
   properties.DisableDefaultProperties();
   properties.goog_audio_mirroring = true;
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
+  scoped_refptr<MediaStreamAudioProcessor> audio_processor(
+      new rtc::RefCountedObject<MediaStreamAudioProcessor>(
           properties, webrtc_audio_device.get()));
   EXPECT_FALSE(audio_processor->has_audio_processing());
   const media::AudioParameters source_params(
@@ -439,8 +410,8 @@
   scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
       new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
   AudioProcessingProperties properties;
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
+  scoped_refptr<MediaStreamAudioProcessor> audio_processor(
+      new rtc::RefCountedObject<MediaStreamAudioProcessor>(
           properties, webrtc_audio_device.get()));
   EXPECT_TRUE(audio_processor->has_audio_processing());
 
@@ -459,48 +430,4 @@
   audio_processor->Stop();
 }
 
-// Test that setting AEC3 override has the desired effect on the APM
-// configuration.
-TEST_F(MediaStreamAudioProcessorTest, TestAec3Switch) {
-  scoped_refptr<AecDumpMessageFilterForTest> admf =
-      new AecDumpMessageFilterForTest();
-  admf->set_override_aec3(true);
-
-  scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
-      new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
-  AudioProcessingProperties properties;
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
-          properties, webrtc_audio_device.get()));
-
-  EXPECT_TRUE(audio_processor->using_aec3());
-
-  // Stop |audio_processor| so that it removes itself from
-  // |webrtc_audio_device| and clears its pointer to it.
-  audio_processor->Stop();
-}
-
-// Same test as above, but when AEC is disabled in the constraints. The expected
-// outcome is that AEC3 should be disabled in all cases.
-TEST_F(MediaStreamAudioProcessorTest, TestAec3Switch_AecOff) {
-  scoped_refptr<AecDumpMessageFilterForTest> admf =
-      new AecDumpMessageFilterForTest();
-  admf->set_override_aec3(true);
-
-  scoped_refptr<WebRtcAudioDeviceImpl> webrtc_audio_device(
-      new rtc::RefCountedObject<WebRtcAudioDeviceImpl>());
-  AudioProcessingProperties properties;
-  properties.echo_cancellation_type = AudioProcessingProperties::
-      EchoCancellationType::kEchoCancellationDisabled;
-  scoped_refptr<MediaStreamAudioProcessorUnderTest> audio_processor(
-      new rtc::RefCountedObject<MediaStreamAudioProcessorUnderTest>(
-          properties, webrtc_audio_device.get()));
-
-  EXPECT_FALSE(audio_processor->using_aec3());
-
-  // Stop |audio_processor| so that it removes itself from
-  // |webrtc_audio_device| and clears its pointer to it.
-  audio_processor->Stop();
-}
-
 }  // namespace content
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio.cc b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
index c88863d..faf854c 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio.cc
@@ -11,6 +11,7 @@
 
 #include "base/strings/string_number_conversions.h"
 #include "content/common/media/media_stream_controls.h"
+#include "content/public/common/content_features.h"
 #include "content/renderer/media/stream/media_stream_audio_source.h"
 #include "content/renderer/media/stream/media_stream_constraints_util_sets.h"
 #include "content/renderer/media/stream/media_stream_video_source.h"
@@ -151,12 +152,16 @@
   if (audio_parameters.effects() &
       (media::AudioParameters::EXPERIMENTAL_ECHO_CANCELLER |
        media::AudioParameters::ECHO_CANCELLER)) {
-    // If the system supports echo cancellation, return both echo cancellers.
+    // If the system/hardware supports echo cancellation, return all echo
+    // cancellers.
     return {blink::kEchoCancellationTypeBrowser,
+            blink::kEchoCancellationTypeAec3,
             blink::kEchoCancellationTypeSystem};
   }
-  // The browser echo canceller is always available.
-  return {blink::kEchoCancellationTypeBrowser};
+
+  // The browser and AEC3 echo cancellers are always available.
+  return {blink::kEchoCancellationTypeBrowser,
+          blink::kEchoCancellationTypeAec3};
 }
 
 // This class represents all the candidates settings for a single audio device.
@@ -225,9 +230,14 @@
         DiscreteSet<bool>({echo_cancellation_enabled});
     bool_sets_[GOOG_ECHO_CANCELLATION] = bool_sets_[ECHO_CANCELLATION];
 
-    if (properties.EchoCancellationIsWebRtcProvided()) {
+    if (properties.echo_cancellation_type ==
+        EchoCancellationType::kEchoCancellationAec2) {
       echo_cancellation_type_set_ =
           DiscreteSet<std::string>({blink::kEchoCancellationTypeBrowser});
+    } else if (properties.echo_cancellation_type ==
+               EchoCancellationType::kEchoCancellationAec3) {
+      echo_cancellation_type_set_ =
+          DiscreteSet<std::string>({blink::kEchoCancellationTypeAec3});
     } else if (system_echo_cancellation_enabled) {
       echo_cancellation_type_set_ =
           DiscreteSet<std::string>({blink::kEchoCancellationTypeSystem});
@@ -423,20 +433,35 @@
     // Return type based the selected type.
     if (selected_type == blink::kEchoCancellationTypeBrowser) {
       return EchoCancellationType::kEchoCancellationAec2;
+    } else if (selected_type == blink::kEchoCancellationTypeAec3) {
+      return EchoCancellationType::kEchoCancellationAec3;
     } else if (selected_type == blink::kEchoCancellationTypeSystem) {
       return EchoCancellationType::kEchoCancellationSystem;
     }
 
     // If no type has been selected, choose system if the device has the
-    // ECHO_CANCELLER flag set. Choose WebRTC-provided echo cancellation
-    // otherwise. Never automatically enable an experimental system echo
-    // canceller.
-    const bool has_system_echo_canceller =
-        audio_parameters.IsValid() &&
-        (audio_parameters.effects() & media::AudioParameters::ECHO_CANCELLER);
-    return has_system_echo_canceller
-               ? EchoCancellationType::kEchoCancellationSystem
-               : EchoCancellationType::kEchoCancellationAec2;
+    // ECHO_CANCELLER flag set. Never automatically enable an experimental
+    // system echo canceller.
+    if (audio_parameters.IsValid() &&
+        audio_parameters.effects() & media::AudioParameters::ECHO_CANCELLER) {
+      return EchoCancellationType::kEchoCancellationSystem;
+    }
+
+    // Finally, choose the browser provided AEC2 or AEC3 based on an optional
+    // override setting for AEC3 or feature.
+    // In unit tests not creating a message filter, |aec_dump_message_filter|
+    // will be null. We can just ignore that. Other unit tests and browser tests
+    // ensure that we do get the filter when we should.
+    base::Optional<bool> override_aec3;
+    scoped_refptr<AecDumpMessageFilter> aec_dump_message_filter =
+        AecDumpMessageFilter::Get();
+    if (aec_dump_message_filter)
+      override_aec3 = aec_dump_message_filter->GetOverrideAec3();
+    const bool use_aec3 = override_aec3.value_or(
+        base::FeatureList::IsEnabled(features::kWebRtcUseEchoCanceller3));
+
+    return use_aec3 ? EchoCancellationType::kEchoCancellationAec3
+                    : EchoCancellationType::kEchoCancellationAec2;
   }
 
   // Returns the audio-processing properties supported by this
diff --git a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
index 4a91ba3..2977c75 100644
--- a/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
+++ b/content/renderer/media/stream/media_stream_constraints_util_audio_unittest.cc
@@ -61,6 +61,22 @@
 
 }  // namespace
 
+// This class is only used for setting |override_aec3_|. We simply inject the
+// current task runner since they are not used.
+class AecDumpMessageFilterForTest : public AecDumpMessageFilter {
+ public:
+  AecDumpMessageFilterForTest()
+      : AecDumpMessageFilter(base::MessageLoopCurrent::Get()->task_runner(),
+                             base::MessageLoopCurrent::Get()->task_runner()) {}
+
+  void set_override_aec3(base::Optional<bool> override_aec3) {
+    override_aec3_ = override_aec3;
+  }
+
+ protected:
+  ~AecDumpMessageFilterForTest() override {}
+};
+
 class MediaStreamConstraintsUtilAudioTest
     : public testing::TestWithParam<std::string> {
  public:
@@ -354,7 +370,7 @@
 
   // Assumes that echoCancellation is set to true as a basic, exact constraint.
   void CheckAudioProcessingPropertiesForExactEchoCancellationType(
-      bool request_system_echo_cancellation,
+      const blink::WebString& echo_cancellation_type_constraint,
       const AudioCaptureSettings& result) {
     const AudioProcessingProperties& properties =
         result.audio_processing_properties();
@@ -364,29 +380,28 @@
     // With content capture, the echo_cancellation constraint controls
     // only the echo_cancellation properties. The other audio processing
     // properties default to false.
-    const bool enable_sw_audio_processing = IsDeviceCapture();
-
-    if (IsDeviceCapture()) {
-      const EchoCancellationType expected_echo_cancellation_type =
-          request_system_echo_cancellation
-              ? EchoCancellationType::kEchoCancellationSystem
-              : EchoCancellationType::kEchoCancellationAec2;
-      EXPECT_EQ(expected_echo_cancellation_type,
-                properties.echo_cancellation_type);
-    } else {
-      EXPECT_EQ(EchoCancellationType::kEchoCancellationAec2,
-                properties.echo_cancellation_type);
+    const EchoCancellationType expected_ec_type =
+        GetEchoCancellationTypeFromConstraintString(
+            echo_cancellation_type_constraint);
+    if (!IsDeviceCapture()) {
+      ASSERT_NE(EchoCancellationType::kEchoCancellationSystem,
+                expected_ec_type);
     }
-    EXPECT_EQ(enable_sw_audio_processing, properties.goog_auto_gain_control);
-    CheckGoogExperimentalEchoCancellationDefault(properties,
-                                                 enable_sw_audio_processing);
-    EXPECT_EQ(enable_sw_audio_processing,
+    EXPECT_EQ(expected_ec_type, properties.echo_cancellation_type);
+
+    const bool enable_webrtc_audio_processing = IsDeviceCapture();
+    EXPECT_EQ(enable_webrtc_audio_processing,
+              properties.goog_auto_gain_control);
+    CheckGoogExperimentalEchoCancellationDefault(
+        properties, enable_webrtc_audio_processing);
+    EXPECT_EQ(enable_webrtc_audio_processing,
               properties.goog_typing_noise_detection);
-    EXPECT_EQ(enable_sw_audio_processing, properties.goog_noise_suppression);
-    EXPECT_EQ(enable_sw_audio_processing,
+    EXPECT_EQ(enable_webrtc_audio_processing,
+              properties.goog_noise_suppression);
+    EXPECT_EQ(enable_webrtc_audio_processing,
               properties.goog_experimental_noise_suppression);
-    EXPECT_EQ(enable_sw_audio_processing, properties.goog_highpass_filter);
-    EXPECT_EQ(enable_sw_audio_processing,
+    EXPECT_EQ(enable_webrtc_audio_processing, properties.goog_highpass_filter);
+    EXPECT_EQ(enable_webrtc_audio_processing,
               properties.goog_experimental_auto_gain_control);
 
     // The following are not audio processing.
@@ -396,10 +411,11 @@
               result.disable_local_echo());
     EXPECT_FALSE(result.render_to_associated_sink());
     if (IsDeviceCapture()) {
-      CheckDevice(request_system_echo_cancellation
-                      ? *system_echo_canceller_device_
-                      : *default_device_,
-                  result);
+      CheckDevice(
+          expected_ec_type == EchoCancellationType::kEchoCancellationSystem
+              ? *system_echo_canceller_device_
+              : *default_device_,
+          result);
     } else {
       EXPECT_TRUE(result.device_id().empty());
     }
@@ -429,13 +445,31 @@
     CheckDevice(*system_echo_canceller_device_, result);
   }
 
+  EchoCancellationType GetEchoCancellationTypeFromConstraintString(
+      const blink::WebString& constraint_string) {
+    if (constraint_string == kEchoCancellationTypeValues[0])
+      return EchoCancellationType::kEchoCancellationAec2;
+    if (constraint_string == kEchoCancellationTypeValues[1])
+      return EchoCancellationType::kEchoCancellationAec3;
+    if (constraint_string == kEchoCancellationTypeValues[2])
+      return EchoCancellationType::kEchoCancellationSystem;
+
+    ADD_FAILURE() << "Invalid echo cancellation type constraint: "
+                  << constraint_string.Ascii();
+    return EchoCancellationType::kEchoCancellationDisabled;
+  }
+
   MockConstraintFactory constraint_factory_;
   AudioDeviceCaptureCapabilities capabilities_;
   const AudioDeviceCaptureCapability* default_device_ = nullptr;
   const AudioDeviceCaptureCapability* system_echo_canceller_device_ = nullptr;
   const std::vector<media::Point> kMicPositions = {{8, 8, 8}, {4, 4, 4}};
+
+  // TODO(grunell): Store these as separate constants and compare against those
+  // in tests, instead of indexing the vector.
   const std::vector<blink::WebString> kEchoCancellationTypeValues = {
       blink::WebString::FromASCII("browser"),
+      blink::WebString::FromASCII("aec3"),
       blink::WebString::FromASCII("system")};
 
  private:
@@ -644,7 +678,7 @@
 
 // Tests the echoCancellation constraint with a device without system echo
 // cancellation.
-TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithSw) {
+TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationWithWebRtc) {
   for (auto set_function : kBoolSetFunctions) {
     for (auto accessor : kFactoryAccessors) {
       // Ideal advanced is ignored by the SelectSettings algorithm.
@@ -672,20 +706,21 @@
                   : EchoCancellationType::kEchoCancellationDisabled;
         EXPECT_EQ(expected_echo_cancellation_type,
                   properties.echo_cancellation_type);
-        const bool enable_sw_audio_processing =
+        const bool enable_webrtc_audio_processing =
             IsDeviceCapture() ? value : false;
-        EXPECT_EQ(enable_sw_audio_processing,
+        EXPECT_EQ(enable_webrtc_audio_processing,
                   properties.goog_auto_gain_control);
         CheckGoogExperimentalEchoCancellationDefault(
-            properties, enable_sw_audio_processing);
-        EXPECT_EQ(enable_sw_audio_processing,
+            properties, enable_webrtc_audio_processing);
+        EXPECT_EQ(enable_webrtc_audio_processing,
                   properties.goog_typing_noise_detection);
-        EXPECT_EQ(enable_sw_audio_processing,
+        EXPECT_EQ(enable_webrtc_audio_processing,
                   properties.goog_noise_suppression);
-        EXPECT_EQ(enable_sw_audio_processing,
+        EXPECT_EQ(enable_webrtc_audio_processing,
                   properties.goog_experimental_noise_suppression);
-        EXPECT_EQ(enable_sw_audio_processing, properties.goog_highpass_filter);
-        EXPECT_EQ(enable_sw_audio_processing,
+        EXPECT_EQ(enable_webrtc_audio_processing,
+                  properties.goog_highpass_filter);
+        EXPECT_EQ(enable_webrtc_audio_processing,
                   properties.goog_experimental_auto_gain_control);
 
         // The following are not audio processing.
@@ -733,7 +768,7 @@
         const AudioProcessingProperties& properties =
             result.audio_processing_properties();
         // With system echo cancellation, the echo_cancellation constraint
-        // enables/disables all audio processing by default, software echo
+        // enables/disables all audio processing by default, WebRTC echo
         // cancellation is always disabled, and system echo cancellation is
         // disabled if the echo_cancellation constraint is false.
         const EchoCancellationType expected_echo_cancellation_type =
@@ -763,7 +798,7 @@
 
 // Tests the googEchoCancellation constraint with a device without system echo
 // cancellation.
-TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithSw) {
+TEST_P(MediaStreamConstraintsUtilAudioTest, GoogEchoCancellationWithWebRtc) {
   for (auto set_function : kBoolSetFunctions) {
     for (auto accessor : kFactoryAccessors) {
       // Ideal advanced is ignored by the SelectSettings algorithm.
@@ -829,7 +864,7 @@
         EXPECT_TRUE(result.HasValue());
         const AudioProcessingProperties& properties =
             result.audio_processing_properties();
-        // With system echo cancellation, software echo cancellation is always
+        // With system echo cancellation, WebRTC echo cancellation is always
         // disabled, and system echo cancellation is disabled if
         // goog_echo_cancellation is false.
         const EchoCancellationType expected_echo_cancellation_type =
@@ -849,15 +884,12 @@
 // with system echo cancellation. Tested as basic exact constraints.
 TEST_P(MediaStreamConstraintsUtilAudioTest, EchoCancellationTypeExact) {
   for (blink::WebString value : kEchoCancellationTypeValues) {
-    const bool request_system_echo_cancellation =
-        value == kEchoCancellationTypeValues[1];
-
     ResetFactory();
     constraint_factory_.basic().echo_cancellation.SetExact(true);
     constraint_factory_.basic().echo_cancellation_type.SetExact(value);
     auto result = SelectSettings();
     // If content capture and EC type "system", we expect failure.
-    if (!IsDeviceCapture() && request_system_echo_cancellation) {
+    if (!IsDeviceCapture() && value == kEchoCancellationTypeValues[2]) {
       EXPECT_FALSE(result.HasValue());
       EXPECT_EQ(result.failed_constraint_name(),
                 constraint_factory_.basic().echo_cancellation_type.GetName());
@@ -865,8 +897,7 @@
     }
     ASSERT_TRUE(result.HasValue());
 
-    CheckAudioProcessingPropertiesForExactEchoCancellationType(
-        request_system_echo_cancellation, result);
+    CheckAudioProcessingPropertiesForExactEchoCancellationType(value, result);
   }
 }
 
@@ -888,10 +919,7 @@
     constraint_factory_.basic().echo_cancellation_type.SetExact(value);
     auto result = SelectSettings();
     ASSERT_TRUE(result.HasValue());
-    const bool request_system_echo_cancellation =
-        value == kEchoCancellationTypeValues[1];
-    CheckAudioProcessingPropertiesForExactEchoCancellationType(
-        request_system_echo_cancellation, result);
+    CheckAudioProcessingPropertiesForExactEchoCancellationType(value, result);
   }
 }
 
@@ -905,7 +933,7 @@
 
   constraint_factory_.basic().echo_cancellation.SetExact(true);
   constraint_factory_.basic().echo_cancellation_type.SetIdeal(
-      kEchoCancellationTypeValues[1]);
+      kEchoCancellationTypeValues[2]);
   auto result = SelectSettings();
   ASSERT_TRUE(result.HasValue());
   CheckAudioProcessingPropertiesForIdealEchoCancellationType(result);
@@ -926,7 +954,7 @@
 
   constraint_factory_.basic().echo_cancellation.SetExact(true);
   constraint_factory_.basic().echo_cancellation_type.SetIdeal(
-      kEchoCancellationTypeValues[1]);
+      kEchoCancellationTypeValues[2]);
   auto result = SelectSettings();
   ASSERT_TRUE(result.HasValue());
   CheckAudioProcessingPropertiesForIdealEchoCancellationType(result);
@@ -986,17 +1014,15 @@
 
           // With experimental system echo cancellation (echo canceller type
           // "system"), the echo_cancellation constraint enables/disables all
-          // audio processing by default, software echo cancellation is always
+          // audio processing by default, WebRTC echo cancellation is always
           // disabled, and experimental system echo cancellation is disabled
           // if the echo_cancellation constraint is false.
-          bool request_system_echo_cancellation =
-              ec_type_value == kEchoCancellationTypeValues[1];
-
           if (ec_value) {
             const EchoCancellationType expected_echo_cancellation_type =
-                request_system_echo_cancellation
-                    ? EchoCancellationType::kEchoCancellationSystem
-                    : EchoCancellationType::kEchoCancellationAec2;
+                ec_type_value == blink::WebString()
+                    ? EchoCancellationType::kEchoCancellationAec2
+                    : GetEchoCancellationTypeFromConstraintString(
+                          ec_type_value);
             EXPECT_EQ(expected_echo_cancellation_type,
                       properties.echo_cancellation_type);
           } else {
@@ -1027,7 +1053,7 @@
 // Tests the echoCancellationType constraint with constraining to a device
 // without experimental system echo cancellation, which should fail.
 TEST_P(MediaStreamConstraintsUtilAudioTest,
-       EchoCancellationTypeWithSwDeviceConstraint) {
+       EchoCancellationTypeWithWebRtcDeviceConstraint) {
   if (!IsDeviceCapture())
     return;
 
@@ -1035,7 +1061,7 @@
       blink::WebString::FromASCII(default_device_->DeviceID()));
   constraint_factory_.basic().echo_cancellation.SetExact(true);
   constraint_factory_.basic().echo_cancellation_type.SetExact(
-      kEchoCancellationTypeValues[1]);
+      kEchoCancellationTypeValues[2]);
 
   auto result = SelectSettings();
   EXPECT_FALSE(result.HasValue());
@@ -1043,6 +1069,57 @@
             constraint_factory_.basic().device_id.GetName());
 }
 
+// Tests the echoCancellationType constraint when also the AEC3 has been
+// selected via the extension API. That selection ends up in
+// AecDumpMessageFilter and MediaStreamConstraintsUtil checks there if set and
+// to what. It can be unset, true or false. The constraint has precedence over
+// the extension API selection. Tested as basic exact constraints.
+TEST_P(MediaStreamConstraintsUtilAudioTest,
+       EchoCancellationTypeAndAec3Selection) {
+  // First test AEC3 selection when no echo cancellation type constraint has
+  // been set.
+  scoped_refptr<AecDumpMessageFilterForTest> admf =
+      new AecDumpMessageFilterForTest();
+  admf->set_override_aec3(true);
+
+  ResetFactory();
+  constraint_factory_.basic().echo_cancellation.SetExact(true);
+  auto result = SelectSettings();
+  ASSERT_TRUE(result.HasValue());
+
+  CheckAudioProcessingPropertiesForExactEchoCancellationType(
+      kEchoCancellationTypeValues[1],  // AEC3
+      result);
+
+  // Set the echo cancellation type constraint to browser and expect that as
+  // result.
+  ResetFactory();
+  constraint_factory_.basic().echo_cancellation.SetExact(true);
+  constraint_factory_.basic().echo_cancellation_type.SetExact(
+      kEchoCancellationTypeValues[0]);
+  result = SelectSettings();
+  ASSERT_TRUE(result.HasValue());
+
+  CheckAudioProcessingPropertiesForExactEchoCancellationType(
+      kEchoCancellationTypeValues[0],  // Browser
+      result);
+
+  // Set the AEC3 selection to false and echo cancellation type constraint to
+  // AEC3 and expect AEC3 as result.
+  admf->set_override_aec3(false);
+
+  ResetFactory();
+  constraint_factory_.basic().echo_cancellation.SetExact(true);
+  constraint_factory_.basic().echo_cancellation_type.SetExact(
+      kEchoCancellationTypeValues[1]);
+  result = SelectSettings();
+  ASSERT_TRUE(result.HasValue());
+
+  CheckAudioProcessingPropertiesForExactEchoCancellationType(
+      kEchoCancellationTypeValues[1],  // AEC3
+      result);
+}
+
 // Test that having differing mandatory values for echoCancellation and
 // googEchoCancellation fails.
 TEST_P(MediaStreamConstraintsUtilAudioTest, ContradictoryEchoCancellation) {
@@ -1485,6 +1562,7 @@
   const EchoCancellationType kEchoCancellationTypes[] = {
       EchoCancellationType::kEchoCancellationDisabled,
       EchoCancellationType::kEchoCancellationAec2,
+      EchoCancellationType::kEchoCancellationAec3,
       EchoCancellationType::kEchoCancellationSystem};
 
   for (EchoCancellationType ec_type : kEchoCancellationTypes) {
@@ -1503,16 +1581,12 @@
                 : media::AudioParameters::PlatformEffectsMask::NO_EFFECTS);
 
     for (blink::WebString value : kEchoCancellationTypeValues) {
-      const bool system_ec_type = value == kEchoCancellationTypeValues[1];
       constraint_factory_.Reset();
       constraint_factory_.basic().echo_cancellation_type.SetExact(value);
       auto result = SelectSettingsAudioCapture(
           source.get(), constraint_factory_.CreateWebMediaConstraints());
       const bool should_have_result_value =
-          (ec_type == EchoCancellationType::kEchoCancellationAec2 &&
-           !system_ec_type) ||
-          (ec_type == EchoCancellationType::kEchoCancellationSystem &&
-           system_ec_type);
+          ec_type == GetEchoCancellationTypeFromConstraintString(value);
       EXPECT_EQ(should_have_result_value, result.HasValue());
 
       // Setting just ideal values should always succeed.
@@ -1532,9 +1606,6 @@
     return;
 
   AudioProcessingProperties properties;
-  properties.echo_cancellation_type =
-      EchoCancellationType::kEchoCancellationAec2;
-
   std::unique_ptr<ProcessedLocalAudioSource> processed_source =
       GetProcessedLocalAudioSource(properties, false /* hotword_enabled */,
                                    false /* disable_local_echo */,
diff --git a/content/renderer/media/stream/user_media_processor.cc b/content/renderer/media/stream/user_media_processor.cc
index f88184f6..bd5929a 100644
--- a/content/renderer/media/stream/user_media_processor.cc
+++ b/content/renderer/media/stream/user_media_processor.cc
@@ -48,6 +48,9 @@
 
 namespace content {
 
+using blink::WebMediaStreamSource;
+using EchoCancellationType = AudioProcessingProperties::EchoCancellationType;
+
 namespace {
 
 void CopyFirstString(const blink::StringConstraint& constraint,
@@ -102,39 +105,48 @@
 void SurfaceAudioProcessingSettings(blink::WebMediaStreamSource* source) {
   MediaStreamAudioSource* source_impl =
       static_cast<MediaStreamAudioSource*>(source->GetExtraData());
-  bool sw_echo_cancellation = false, auto_gain_control = false,
-       noise_supression = false, system_echo_cancellation = false;
+
+  // If the source is a processed source, get the properties from it.
   if (ProcessedLocalAudioSource* processed_source =
           ProcessedLocalAudioSource::From(source_impl)) {
     AudioProcessingProperties properties =
         processed_source->audio_processing_properties();
-    sw_echo_cancellation = properties.EchoCancellationIsWebRtcProvided();
-    auto_gain_control = properties.goog_auto_gain_control;
-    noise_supression = properties.goog_noise_suppression;
-    // The ECHO_CANCELLER flag will be set if either:
-    // - The device advertises the ECHO_CANCELLER flag and
-    //   echo_cancellation_type is kEchoCancellationDisabled or
-    //   kEchoCancellationAec2 (that is, system ec is disabled);
-    //   or if
-    // - The device advertises the EXPERIMENTAL_ECHO_CANCELLER flag and
-    //   echo_cancellation_type is kEchoCancellationSystem.
-    // See: ProcessedLocalAudioSource::EnsureSourceIsStarted().
-    const media::AudioParameters& params = processed_source->device().input;
-    system_echo_cancellation =
+    WebMediaStreamSource::EchoCancellationMode echo_cancellation_mode;
+
+    switch (properties.echo_cancellation_type) {
+      case EchoCancellationType::kEchoCancellationDisabled:
+        echo_cancellation_mode =
+            WebMediaStreamSource::EchoCancellationMode::kDisabled;
+        break;
+      case EchoCancellationType::kEchoCancellationAec2:
+        echo_cancellation_mode =
+            WebMediaStreamSource::EchoCancellationMode::kBrowser;
+        break;
+      case EchoCancellationType::kEchoCancellationAec3:
+        echo_cancellation_mode =
+            WebMediaStreamSource::EchoCancellationMode::kAec3;
+        break;
+      case EchoCancellationType::kEchoCancellationSystem:
+        echo_cancellation_mode =
+            WebMediaStreamSource::EchoCancellationMode::kSystem;
+        break;
+    }
+
+    source->SetAudioProcessingProperties(echo_cancellation_mode,
+                                         properties.goog_auto_gain_control,
+                                         properties.goog_noise_suppression);
+  } else {
+    // If the source is not a processed source, it could still support system
+    // echo cancellation. Surface that if it does.
+    media::AudioParameters params = source_impl->GetAudioParameters();
+    const WebMediaStreamSource::EchoCancellationMode echo_cancellation_mode =
         params.IsValid() &&
-        (params.effects() & media::AudioParameters::ECHO_CANCELLER);
+                (params.effects() & media::AudioParameters::ECHO_CANCELLER)
+            ? WebMediaStreamSource::EchoCancellationMode::kSystem
+            : WebMediaStreamSource::EchoCancellationMode::kDisabled;
+
+    source->SetAudioProcessingProperties(echo_cancellation_mode, false, false);
   }
-
-  using blink::WebMediaStreamSource;
-  const WebMediaStreamSource::EchoCancellationMode echo_cancellation_mode =
-      system_echo_cancellation
-          ? WebMediaStreamSource::EchoCancellationMode::kHardware
-          : sw_echo_cancellation
-                ? WebMediaStreamSource::EchoCancellationMode::kSoftware
-                : WebMediaStreamSource::EchoCancellationMode::kDisabled;
-
-  source->SetAudioProcessingProperties(echo_cancellation_mode,
-                                       auto_gain_control, noise_supression);
 }
 
 }  // namespace
@@ -869,9 +881,11 @@
 
   blink::WebMediaStreamSource::Capabilities capabilities;
   capabilities.echo_cancellation = {true, false};
-  capabilities.echo_cancellation_type.reserve(2);
+  capabilities.echo_cancellation_type.reserve(3);
   capabilities.echo_cancellation_type.emplace_back(
       blink::WebString::FromASCII(blink::kEchoCancellationTypeBrowser));
+  capabilities.echo_cancellation_type.emplace_back(
+      blink::WebString::FromASCII(blink::kEchoCancellationTypeAec3));
   if (device.input.effects() &
       (media::AudioParameters::ECHO_CANCELLER |
        media::AudioParameters::EXPERIMENTAL_ECHO_CANCELLER)) {
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index a0e9f34..2feba296 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -109,8 +109,10 @@
 static std::string SerializeTransceiver(
     const blink::WebRTCRtpSender* sender,
     const blink::WebRTCRtpReceiver* receiver) {
-  DCHECK((sender != nullptr) != (receiver != nullptr));
+  DCHECK(sender || receiver);
   std::string result;
+  if (sender && receiver)
+    result += "{ ";
   if (sender) {
     result += "sender: { ";
     if (sender->Track().IsNull())
@@ -119,13 +121,17 @@
       result += "track: " + SerializeMediaStreamComponent(sender->Track());
     result += ", streams: " + SerializeMediaStreamIds(sender->StreamIds());
     result += " }";
-  } else {
-    DCHECK(receiver);
+  }
+  if (receiver) {
+    if (sender)
+      result += ", ";
     result += "receiver: { ";
     DCHECK(!receiver->Track().IsNull());
     result += "track: " + SerializeMediaStreamComponent(receiver->Track());
     result += ", streams: " + SerializeMediaStreamIds(receiver->StreamIds());
     result += " }";
+    if (sender)
+      result += " }";
   }
   return result;
 }
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
index 6dabc853..f8ea74d 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler.cc
@@ -778,8 +778,8 @@
         tracker_(tracker),
         action_(action),
         sdp_semantics_(sdp_semantics) {
-    // TODO(hbos): Support kUnifiedPlan. https://crbug.com/777617
-    DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kPlanB);
+    DCHECK(sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB ||
+           sdp_semantics_ == blink::WebRTCSdpSemantics::kUnifiedPlan);
   }
 
   void OnSetDescriptionComplete(
@@ -804,8 +804,6 @@
         ProcessStateChangesPlanB(std::move(states));
       } else {
         DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kUnifiedPlan);
-        // TODO(hbos): Exercise this codepath. https://crbug.com/777617
-        NOTREACHED();
         ProcessStateChangesUnifiedPlan(std::move(states));
       }
 
@@ -843,6 +841,7 @@
   }
 
   void ProcessStateChangesPlanB(WebRtcSetDescriptionObserver::States states) {
+    DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kPlanB);
     // Determine which receivers have been removed before processing the
     // removal as to not invalidate the iterator.
     std::vector<RTCRtpReceiver*> removed_receivers;
@@ -886,6 +885,7 @@
 
   void ProcessStateChangesUnifiedPlan(
       WebRtcSetDescriptionObserver::States states) {
+    DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kUnifiedPlan);
     handler_->OnModifyTransceivers(
         std::move(states.transceiver_states),
         action_ == PeerConnectionTracker::ACTION_SET_REMOTE_DESCRIPTION);
@@ -1278,15 +1278,14 @@
       new WebRtcSetDescriptionObserverImpl(
           weak_factory_.GetWeakPtr(), request, peer_connection_tracker_,
           task_runner_, PeerConnectionTracker::ACTION_SET_LOCAL_DESCRIPTION,
-          // TODO(hbos): Pass the value of |sdp_semantics_| when "kUnifiedPlan"
-          // behavior is supported. https://crbug.com/777617
-          blink::WebRTCSdpSemantics::kPlanB));
+          sdp_semantics_));
 
+  bool surface_receivers_only =
+      (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB);
   scoped_refptr<webrtc::SetSessionDescriptionObserver> webrtc_observer(
       WebRtcSetLocalDescriptionObserverHandler::Create(
           task_runner_, signaling_thread(), native_peer_connection_,
-          track_adapter_map_, content_observer,
-          true /* surface_receivers_only*/)
+          track_adapter_map_, content_observer, surface_receivers_only)
           .get());
 
   signaling_thread()->PostTask(
@@ -1348,15 +1347,15 @@
       new WebRtcSetDescriptionObserverImpl(
           weak_factory_.GetWeakPtr(), request, peer_connection_tracker_,
           task_runner_, PeerConnectionTracker::ACTION_SET_REMOTE_DESCRIPTION,
-          // TODO(hbos): Pass the value of |sdp_semantics_| when "kUnifiedPlan"
-          // behavior is supported. https://crbug.com/777617
-          blink::WebRTCSdpSemantics::kPlanB));
+          sdp_semantics_));
 
+  bool surface_receivers_only =
+      (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB);
   rtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface>
       webrtc_observer(WebRtcSetRemoteDescriptionObserverHandler::Create(
                           task_runner_, signaling_thread(),
                           native_peer_connection_, track_adapter_map_,
-                          content_observer, true /* surface_receivers_only*/)
+                          content_observer, surface_receivers_only)
                           .get());
 
   signaling_thread()->PostTask(
@@ -1593,10 +1592,7 @@
   std::unique_ptr<blink::WebRTCRtpTransceiver> web_transceiver;
   const blink::WebRTCRtpSender* web_sender = nullptr;
   const blink::WebRTCRtpReceiver* web_receiver = nullptr;
-  // TODO(hbos): Exercise the "else" codepath if "kUnifiedPlan" is used.
-  // https://crbug.com/777617
-  if (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB ||
-      sdp_semantics_ == blink::WebRTCSdpSemantics::kUnifiedPlan) {
+  if (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB) {
     // Plan B: Create sender only.
     DCHECK(transceiver_state.sender_state());
     auto webrtc_sender = transceiver_state.sender_state()->webrtc_sender();
@@ -1610,8 +1606,6 @@
     web_transceiver = std::make_unique<RTCRtpSenderOnlyTransceiver>(
         rtp_senders_.back()->ShallowCopy());
   } else {
-    // TODO(hbos): Exercise this codepath. https://crbug.com/777617
-    NOTREACHED();
     DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kUnifiedPlan);
     // Unified Plan: Create or recycle a transceiver.
     auto transceiver = CreateOrUpdateTransceiver(std::move(transceiver_state));
@@ -1643,15 +1637,10 @@
   std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>> transceivers;
   if (error_or_sender->ok()) {
     auto sender = error_or_sender->value();
-    // TODO(hbos): Exercise the "else" codepath if "kUnifiedPlan" is used.
-    // https://crbug.com/777617
-    if (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB ||
-        sdp_semantics_ == blink::WebRTCSdpSemantics::kUnifiedPlan) {
+    if (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB) {
       transceivers = {new SurfaceSenderStateOnly(sender)};
     } else {
       DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kUnifiedPlan);
-      // TODO(hbos): Exercise this codepath. https://crbug.com/777617
-      NOTREACHED();
       rtc::scoped_refptr<webrtc::RtpTransceiverInterface>
           transceiver_for_sender = nullptr;
       for (const auto& transceiver :
@@ -1673,10 +1662,7 @@
 RTCPeerConnectionHandler::RemoveTrack(blink::WebRTCRtpSender* web_sender) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   TRACE_EVENT0("webrtc", "RTCPeerConnectionHandler::RemoveTrack");
-  // TODO(hbos): Exercise the "else" codepath if "kUnifiedPlan" is used.
-  // https://crbug.com/777617
-  if (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB ||
-      sdp_semantics_ == blink::WebRTCSdpSemantics::kUnifiedPlan) {
+  if (sdp_semantics_ == blink::WebRTCSdpSemantics::kPlanB) {
     if (RemoveTrackPlanB(web_sender)) {
       // In Plan B, null indicates success.
       std::unique_ptr<blink::WebRTCRtpTransceiver> web_transceiver = nullptr;
@@ -1687,13 +1673,12 @@
     return webrtc::RTCError(webrtc::RTCErrorType::INVALID_STATE);
   }
   DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kUnifiedPlan);
-  // TODO(hbos): Exercise this codepath. https://crbug.com/777617
-  NOTREACHED();
   return RemoveTrackUnifiedPlan(web_sender);
 }
 
 bool RTCPeerConnectionHandler::RemoveTrackPlanB(
     blink::WebRTCRtpSender* web_sender) {
+  DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kPlanB);
   auto web_track = web_sender->Track();
   auto it = FindSender(web_sender->Id());
   if (it == rtp_senders_.end())
@@ -1723,6 +1708,7 @@
 RTCPeerConnectionHandler::RemoveTrackUnifiedPlan(
     blink::WebRTCRtpSender* web_sender) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kUnifiedPlan);
   auto it = FindSender(web_sender->Id());
   if (it == rtp_senders_.end())
     return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER);
@@ -2024,8 +2010,6 @@
     std::vector<RtpTransceiverState> transceiver_states,
     bool is_remote_description) {
   DCHECK_EQ(sdp_semantics_, blink::WebRTCSdpSemantics::kUnifiedPlan);
-  // TODO(hbos): Exercise this codepath. https://crbug.com/777617
-  NOTREACHED();
   std::vector<std::unique_ptr<blink::WebRTCRtpTransceiver>> web_transceivers(
       transceiver_states.size());
   for (size_t i = 0; i < transceiver_states.size(); ++i) {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index bb93141..ca6a21b 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1254,7 +1254,7 @@
   v8::Isolate* isolate = blink::MainThreadIsolate();
   isolate->SetCreateHistogramFunction(CreateHistogram);
   isolate->SetAddHistogramSampleFunction(AddHistogramSample);
-  main_thread_scheduler_->SetRAILModeObserver(this);
+  main_thread_scheduler_->AddRAILModeObserver(this);
 
   main_thread_compositor_task_runner_ =
       main_thread_scheduler_->CompositorTaskRunner();
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 3cb6d31..12dffc2 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -19,7 +19,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/common/content_features.h"
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 924cddd..b48aa9a 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -21,7 +21,7 @@
 #include "base/time/time.h"
 #include "content/common/service_worker/controller_service_worker.mojom.h"
 #include "content/common/service_worker/embedded_worker.mojom.h"
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_provider.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "ipc/ipc_listener.h"
diff --git a/content/renderer/service_worker/service_worker_type_converters.h b/content/renderer/service_worker/service_worker_type_converters.h
index ebcbda2..796c746 100644
--- a/content/renderer/service_worker/service_worker_type_converters.h
+++ b/content/renderer/service_worker/service_worker_type_converters.h
@@ -5,7 +5,7 @@
 #ifndef CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_TYPE_CONVERTERS_H_
 #define CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_TYPE_CONVERTERS_H_
 
-#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
+#include "content/common/service_worker/service_worker.mojom.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
 #include "third_party/blink/public/platform/modules/payments/payment_app.mojom.h"
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 832c68b8..b5f3547 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1291,6 +1291,7 @@
     "../browser/cache_storage/cache_storage_scheduler_unittest.cc",
     "../browser/child_process_security_policy_unittest.cc",
     "../browser/cocoa/system_hotkey_map_unittest.mm",
+    "../browser/code_cache/generated_code_cache_unittest.cc",
     "../browser/compositor/reflector_impl_unittest.cc",
     "../browser/compositor/software_browser_compositor_output_surface_unittest.cc",
     "../browser/cookie_store/cookie_store_manager_unittest.cc",
diff --git a/content/test/data/accessibility/aria/aria-grid-dynamic-add-row-expected-blink.txt b/content/test/data/accessibility/aria/aria-grid-dynamic-add-row-expected-blink.txt
new file mode 100644
index 0000000..2b4fca0d
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-grid-dynamic-add-row-expected-blink.txt
@@ -0,0 +1,17 @@
+rootWebArea
+++grid
+++++row
+++++++columnHeader name='Turtle' tableCellRowIndex=0
+++++++columnHeader name='Weapon' tableCellRowIndex=0
+++++row
+++++++cell name='Donatello' tableCellRowIndex=1
+++++++cell name='Bo' tableCellRowIndex=1
+++++row
+++++++cell name='Leonardo' tableCellRowIndex=2
+++++++cell name='Twin Katana' tableCellRowIndex=2
+++++row
+++++++cell name='Michelangelo' tableCellRowIndex=3
+++++++cell name='Nunchaku' tableCellRowIndex=3
+++++row
+++++++cell name='Raphael' tableCellRowIndex=4
+++++++cell name='Sai' tableCellRowIndex=4
diff --git a/content/test/data/accessibility/aria/aria-grid-dynamic-add-row.html b/content/test/data/accessibility/aria/aria-grid-dynamic-add-row.html
new file mode 100644
index 0000000..fa64f7a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-grid-dynamic-add-row.html
@@ -0,0 +1,41 @@
+<!--
+@BLINK-ALLOW:tableCellRowIndex*
+@BLINK-DENY:*selected*
+@WAIT-FOR:Leonardo
+-->
+<!DOCTYPE html>
+<html>
+<body>
+<div role="grid">
+  <div role="row">
+    <span role="columnheader" aria-label="Turtle"></span>
+    <span role="columnheader" aria-label="Weapon"></span>
+  </div>
+  <div id="rowgroup" role="rowgroup">
+  <div role="row">
+    <span role="gridcell" aria-label="Donatello"></span>
+    <span role="gridcell" aria-label="Bo"></span>
+  </div>
+  <div role="row">
+    <span role="gridcell" aria-label="Michelangelo"></span>
+    <span role="gridcell" aria-label="Nunchaku"></span>
+  </div>
+  <div role="row">
+    <span role="gridcell" aria-label="Raphael"></span>
+    <span role="gridcell" aria-label="Sai"></span>
+  </div>
+  </div>
+</div>
+<script>
+  window.setTimeout(function() {
+    var row = document.createElement('div');
+    row.setAttribute('role', 'row');
+    row.innerHTML = '<span role="gridcell" aria-label="Leonardo"></span>' +
+                    '<span role="gridcell" aria-label="Twin Katana"></span>';
+    var rowgroup = document.getElementById('rowgroup');
+    rowgroup.insertBefore(row,
+                          rowgroup.firstElementChild.nextElementSibling);
+  }, 500);
+</script>
+</body>
+</html>
diff --git a/docs/infra/new_builder.md b/docs/infra/new_builder.md
new file mode 100644
index 0000000..70e250f
--- /dev/null
+++ b/docs/infra/new_builder.md
@@ -0,0 +1,340 @@
+# Creating a new builder
+
+This doc describes how to set up a new builder on LUCI. It's focused
+on chromium builders, but parts may be applicable to other projects.
+
+[TOC]
+
+## TL;DR
+
+For a typical chromium builder using the chromium recipe,
+you'll need to acquire hardware and then land **three** CLs:
+
+1. in [infradata/config][16], modifying swarming's bots.cfg.
+2. in [chromium/tools/build][17], modifying the chromium\_tests
+   configuration.
+3. in [chromium/src][18], modifying all of the following:
+    1. LUCI service configurations in `//infra/config/global`
+    2. Compile configuration in `//tools/mb`
+    3. Test configuration in `//testing/buildbot`
+
+## Obtain hardware
+
+If you're setting up a new builder, you'll typically need hardware to run it.
+For CI / waterfall builders or manually triggered try builders,
+[file a labs bug][1] (internal).
+For CQ try bots, please file a [capacity bug][2] (internal) first.
+In both cases, note that your builder will be running on swarming
+(not on buildbot) and should be provisioned accordingly.
+
+## Pick a name and a master
+
+Your new builder's name should follow the [chromium builder naming scheme][3].
+
+We still use master names to group builders in a variety of places (even
+though buildbot itself is largely deprecated). FYI builders should use
+`chromium.fyi`, while other builders should mostly use `chromium.$OS`.
+
+> **Note:** If you're creating a try builder, its name should match the
+> name of the CI builder it mirrors.
+
+## Register hardware with swarming
+
+Once you've obtained hardware, you'll need to associate it with your
+new builder in swarming. You can do so by modifying the relevant swarming
+instance's configuration.
+
+Swarming's bots.cfg schema is [here][20].
+chromium-swarm's bots.cfg instance is [here][4].
+
+You'll want to add something like the following:
+
+``` sh
+bot_group {
+  dimensions: "builder:$BUILDER_NAME"
+  # Add a brief comment about hardware, particularly if you're doing
+  # anything unique or atypical.
+  # $COMMENT_ABOUT_HARDWARE
+  bot_id: "$HARDWARE"
+
+  # luci-eng@google.com is typically fine for generic chromium builders.
+  # If you're doing something more specialized, or if you're creating
+  # a non-chromium builder, consider a different list.
+  owners: "$OWNER_EMAIL"
+
+  # See the schema for more information on these options. The values
+  # listed below should be reasonable defaults for chromium builders.
+  auth {
+    require_luci_machine_token: true
+    ip_whitelist: "chromium-swarm-bots"
+  }
+
+  # This is the service account used by the swarming bot to authenticate to
+  # LUCI services for system purposes (i.e., not within tasks).
+  # For chromium builders, the bots-chrome@ account below should be fine.
+  system_service_account: "bots-chrome@chromium-swarm.iam.gserviceaccount.com"
+
+  # POOL_NAME should be:
+  #   - luci.chromium.ci for public chromium CI / waterfall builders
+  #   - luci.chromium.try for public chromium try builders
+  dimensions: "pool:$POOL_NAME"
+}
+```
+
+## Recipe configuration
+
+Recipes tell your builder what to do. Many require some degree of
+per-builder configuration outside of the chromium repo, though the
+specifics vary. The recipe you use depends on what you want your
+builder to do.
+
+For typical chromium compile and/or test builders, the chromium and
+chromium\_trybot recipes should be sufficient.
+
+To configure a chromium CI builder, you'll want to add a config block
+to the file in [recipe\_modules/chromium\_tests][5] corresponding
+to your new builder's master name. The format is somewhat in flux
+and is not very consistent among the different masters, but something
+like this should suffice:
+
+``` py
+'your-new-builder': {
+  'chromium_config': 'chromium',
+  'gclient_config': 'chromium',
+  'chromium_apply_config': ['mb', 'ninja_confirm_noop'],
+  'chromium_config_kwargs': {
+    'BUILD_CONFIG': 'Release', # or 'Debug', as appropriate
+    'TARGET_BITS': 64, # or 32, for some mobile builders
+  },
+  'testing': {
+    'platform': '$PLATFORM', # one of 'mac', 'win', or 'linux'
+  },
+
+  # Optional: where to upload test results. Valid values include:
+  #   'public_server' for test-results.appspot.com
+  #   'staging_server' for test-results-test.appspot.com
+  #   'no_server' to disable upload
+  'test_results_config': 'public_server',
+
+  # There are a variety of other options; most of them are either
+  # unnecessary in most cases or are deprecated. If you think one
+  # may be applicable, please reach out or ask your reviewer.
+}
+```
+
+For chromium try builders, you'll also want to set up mirroring.
+You can do so by adding your new try builder to [trybots.py][21].
+
+A typical entry will just reference the matching CI builder, e.g.:
+
+``` py
+TRYBOTS = freeze({
+  # ...
+
+  'tryserver.chromium.example': {
+    'builders': {
+      # If you want to build and test the same targets as one
+      # CI builder, you can just do this:
+      'your-new-builder': simple_bot({
+        'mastername': 'chromium.example',
+        'buildername': 'your-new-builder'
+      }),
+
+      # If you want to build the same targets as one CI builder
+      # but not test anything, you can do this:
+      'your-new-compile-builder': simple_bot({
+        'mastername': 'chromium.example',
+        'buildername': 'your-new-builder',
+      }, analyze_mode='compile'),
+
+      # If you want to build and test the same targets as a builder/tester
+      # CI pair, you can do this:
+      'your-new-tester': simple_bot({
+        'mastername': 'chromium.example',
+        'buildername': 'your-new-builder',
+        'tester': 'your-new-tester',
+      }),
+
+      # If you want to mirror multiple try bots, please reach out.
+    },
+  },
+
+  # ...
+})
+```
+
+## Chromium configuration
+
+Lastly, you need to configure a variety of things in the chromium repo.
+It's generally ok to land all of them in a single CL.
+
+### LUCI services
+
+LUCI services used by chromium are configured in [//infra/config/global][6].
+
+#### Buildbucket
+
+Buildbucket is responsible for taking a build scheduled by a user or
+an agent and translating it into a swarming task. Its configuration
+includes things like:
+
+  * ACLs for scheduling and viewing builds
+  * Swarming dimensions
+  * Recipe name and properties
+
+Buildbucket's configuration schema is [here][7].
+Chromium's buildbucket configuration is [here][8].
+
+A typical chromium builder won't need to configure much. Adding a
+`builders` entry to the appropriate bucket
+(`luci.chromium.ci` for CI / waterfall, `luci.chromium.try` for try)
+with the new builder's name, the mixin containing the appropriate
+master name, and perhaps one or two dimensions should be sufficient,
+e.g.:
+
+``` sh
+buckets {
+  name: "luci.chromium.ci"
+  ...
+
+  swarming {
+    ...
+
+    builders {
+      name: "your-new-builder"
+
+      # To determine what you should include here, look for an
+      # existing mixin containing
+      #
+      #   recipe {
+      #     properties: "mastername:$MASTER_NAME"
+      #   }
+      #
+      mixins: "$MASTER_NAME_MIXIN"
+
+      # Add other mixins and dimensions as necessary. You will
+      # usually at least want an os dimension configured, so if
+      # none of your included mixins have one, consider adding one.
+    }
+  }
+}
+```
+
+#### Milo
+
+Milo is responsible for displaying builders and build histories on a
+set of consoles. Its configuration includes the definitions of those
+consoles.
+
+Milo's configuration schema is [here][9].
+Chromium's milo configuration is [here][10].
+
+A typical chromium builder should be added to one or two consoles 
+at most: one corresponding to its master, and possibly the main
+console, e.g.
+
+``` sh
+consoles {
+  ...
+  name: "$MASTER_NAME"
+  ...
+  builders {
+    name: "buildbucket/$BUCKET_NAME/$BUILDER_NAME"
+
+    # A builder's category is a pipe-delimited list of strings
+    # that determines how a builder is grouped on a console page.
+    category: "$LARGE_GROUP|$MEDIUM_GROUP|$SMALL_GROUP"
+
+    # A builder's short name is a string up to three characters
+    # long that lets someone uniquely identify it among builders
+    # in the same category.
+    short_name: "$ID"
+  }
+}
+```
+
+#### Scheduler (CI / waterfall builders only)
+
+The scheduler is responsible for triggering CI / waterfall builders.
+
+Scheduler's configuration schema is [here][11].
+Chromium's scheduler configuration is [here][12].
+
+A typical chromium builder will need a job configuration. A chromium
+builder that's triggering on new commits or on a regular schedule
+(as opposed to being triggered by a parent builder) will also need
+a trigger entry.
+
+``` sh
+trigger {
+  id: "master-gitiles-trigger"
+
+  ...
+
+  # Adding your builder to the master-gitiles-trigger
+  # will cause your builder to be triggered on new commits
+  # to chromium's master branch.
+  triggers: "your-new-ci-builder"
+}
+
+job {
+  id: "your-new-ci-builder"
+
+  # acl_sets should either be
+  #  - "default" for builders that are triggered by the scheduler
+  #     (i.e. anything triggering on new commits or on a cron)
+  #  - "triggered-by-parent-builders" for builders that are
+  #    triggered by other builders
+  acl_sets: "default"
+
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "your-new-ci-builder"
+  }
+}
+```
+
+### Recipe-specific configurations
+
+#### chromium & chromium\_trybot
+
+The build and test configurations used by the main `chromium` and
+`chromium_trybot` recipes are stored src-side:
+
+* **Build configuration**: the gn configuration used by chromium
+recipe builders is handled by [MB][13]. MB's configuration is documented
+[here][14]. You only need to modify it if your new builder will be
+compiling.
+
+* **Test configuration**: the test configuration used by chromium
+recipe builders is in a group of `.pyl` and derived `.json` files
+in `//testing/buildbot`. The format is described [here][15].
+
+## Questions? Feedback?
+
+If you're in need of further assistance, if you're not sure about
+one or more steps, or if you found this documentation lacking, please
+reach out to infra-dev@chromium.org or [file a bug][19]!
+
+[1]: http://go/infrasys-bug
+[2]: http://go/cci-capacity-bug
+[3]: https://bit.ly/chromium-build-naming
+[4]: https://luci-config.appspot.com/#/services/chromium-swarm
+[5]: https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipe_modules/chromium_tests
+[6]: /infra/config/global
+[7]: https://luci-config.appspot.com/schemas/projects:cr-buildbucket.cfg
+[8]: /infra/config/global/cr-buildbucket.cfg
+[9]: http://luci-config.appspot.com/schemas/projects:luci-milo.cfg
+[10]: /infra/config/global/luci-milo.cfg
+[11]: https://chromium.googlesource.com/infra/luci/luci-go/+/master/scheduler/appengine/messages/config.proto
+[12]: /infra/config/global/luci-scheduler.cfg
+[13]: /tools/mb/README.md
+[14]: /tools/mb/docs/user_guide.md#the-mb_config_pyl-config-file
+[15]: /testing/buildbot/README.md
+[16]: https://chrome-internal.googlesource.com/infradata/config
+[17]: https://chromium.googlesource.com/chromium/tools/build
+[18]: /
+[19]: https://g.co/bugatrooper
+[20]: https://chromium.googlesource.com/infra/luci/luci-py/+/master/appengine/swarming/proto/bots.proto
+[21]: https://chromium.googlesource.com/chromium/tools/build/+/master/scripts/slave/recipe_modules/chromium_tests/trybots.py
diff --git a/docs/testing/json_test_results_format.md b/docs/testing/json_test_results_format.md
index d00b5a1..4eae9be 100644
--- a/docs/testing/json_test_results_format.md
+++ b/docs/testing/json_test_results_format.md
@@ -142,7 +142,6 @@
 |  `IMAGE+TEXT` | **Layout test specific, deprecated.** The test produces image and text output, both of which fail to match what we expect. Normally you will see `FAIL` instead. |
 |  `LEAK` | **Layout test specific, deprecated.** Memory leaks were detected during the test execution. |
 |  `MISSING` | **Layout test specific, deprecated.** The test completed but we could not find an expected baseline to compare against. |
-|  `NEEDSMANUALREBASELINE`  | **Layout test specific, deprecated.** The expected test result is out of date and will be ignored (as above). This result may be checked in to the TestExpectations file, but the auto-rebasline-bot will ignore these entries. This should never show up as an `actual` result. |
 |  `NEEDSREBASELINE` | **Layout test specific, deprecated.** The expected test result is out of date and will be ignored (as above); the auto-rebaseline-bot will look for tests of this type and automatically update them. This should never show up as an `actual` result. |
 |  `REBASELINE`  | **Layout test specific, deprecated.** The expected test result is out of date and will be ignored (any result other than a crash or timeout will be considered as passing). This test result should only ever show up on local test runs, not on bots (it is forbidden to check in a TestExpectations file with this expectation). This should never show up as an "actual" result. |
 |  `SLOW` | **Layout test specific, deprecated.** The test is expected to take longer than normal to run. This should never appear as an `actual` result, but may (incorrectly) appear in the expected fields. |
diff --git a/docs/testing/layout_test_expectations.md b/docs/testing/layout_test_expectations.md
index 5fbc462..9371275 100644
--- a/docs/testing/layout_test_expectations.md
+++ b/docs/testing/layout_test_expectations.md
@@ -131,15 +131,6 @@
   assuming that there are no platform-specific results for those platforms,
   you can add the flag `--fill-missing`.
 
-### Rebaselining manually
-
-1. If the tests is already listed in TestExpectations as flaky, mark the test
-   `NeedsManualRebaseline` and comment out the flaky line so that your patch can
-   land without turning the tree red. If the test is not in TestExpectations,
-   you can add a `[ Rebaseline ]` line to TestExpectations.
-2. Run `third_party/blink/tools/blink_tool.py rebaseline-expectations`
-3. Post the patch created in step 2 for review.
-
 ## Kinds of expectations files
 
 * [TestExpectations](../../third_party/WebKit/LayoutTests/TestExpectations): The
@@ -212,7 +203,7 @@
   [third_party/blink/tools/blinkpy/web_tests/port/base.py](../../third_party/blink/tools/blinkpy/web_tests/port/base.py)
   for the meta keywords and which modifiers they represent.
 * Expectations can be one or more of `Crash`, `Failure`, `Pass`, `Rebaseline`,
-  `Slow`, `Skip`, `Timeout`, `WontFix`, `Missing`, `NeedsManualRebaseline`.
+  `Slow`, `Skip`, `Timeout`, `WontFix`, `Missing`.
   If multiple expectations are listed, the test is considered "flaky" and any
   of those results will be considered as expected.
 
diff --git a/gpu/command_buffer/service/raster_cmd_decoder_autogen.h b/gpu/command_buffer/service/raster_cmd_decoder_autogen.h
deleted file mode 100644
index d1ba94c..0000000
--- a/gpu/command_buffer/service/raster_cmd_decoder_autogen.h
+++ /dev/null
@@ -1,535 +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.
-
-// This file is auto-generated from
-// gpu/command_buffer/build_raster_cmd_buffer.py
-// It's formatted by clang-format using chromium coding style:
-//    clang-format -i -style=chromium filename
-// DO NOT EDIT!
-
-// It is included by raster_cmd_decoder.cc
-#ifndef GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_DECODER_AUTOGEN_H_
-#define GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_DECODER_AUTOGEN_H_
-
-error::Error RasterDecoderImpl::HandleActiveTexture(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::ActiveTexture& c =
-      *static_cast<const volatile raster::cmds::ActiveTexture*>(cmd_data);
-  GLenum texture = static_cast<GLenum>(c.texture);
-  DoActiveTexture(texture);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleBindTexture(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::BindTexture& c =
-      *static_cast<const volatile raster::cmds::BindTexture*>(cmd_data);
-  GLenum target = static_cast<GLenum>(c.target);
-  GLuint texture = c.texture;
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glBindTexture", target, "target");
-    return error::kNoError;
-  }
-  DoBindTexture(target, texture);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleDeleteTexturesImmediate(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::DeleteTexturesImmediate& c =
-      *static_cast<const volatile raster::cmds::DeleteTexturesImmediate*>(
-          cmd_data);
-  GLsizei n = static_cast<GLsizei>(c.n);
-  uint32_t data_size;
-  if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) {
-    return error::kOutOfBounds;
-  }
-  volatile const GLuint* textures = GetImmediateDataAs<volatile const GLuint*>(
-      c, data_size, immediate_data_size);
-  if (textures == NULL) {
-    return error::kOutOfBounds;
-  }
-  DeleteTexturesHelper(n, textures);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleFinish(uint32_t immediate_data_size,
-                                             const volatile void* cmd_data) {
-  DoFinish();
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleFlush(uint32_t immediate_data_size,
-                                            const volatile void* cmd_data) {
-  DoFlush();
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleGenTexturesImmediate(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::GenTexturesImmediate& c =
-      *static_cast<const volatile raster::cmds::GenTexturesImmediate*>(
-          cmd_data);
-  GLsizei n = static_cast<GLsizei>(c.n);
-  uint32_t data_size;
-  if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) {
-    return error::kOutOfBounds;
-  }
-  volatile GLuint* textures =
-      GetImmediateDataAs<volatile GLuint*>(c, data_size, immediate_data_size);
-  if (textures == NULL) {
-    return error::kOutOfBounds;
-  }
-  auto textures_copy = std::make_unique<GLuint[]>(n);
-  GLuint* textures_safe = textures_copy.get();
-  std::copy(textures, textures + n, textures_safe);
-  if (!CheckUniqueAndNonNullIds(n, textures_safe) ||
-      !GenTexturesHelper(n, textures_safe)) {
-    return error::kInvalidArguments;
-  }
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleGetError(uint32_t immediate_data_size,
-                                               const volatile void* cmd_data) {
-  const volatile raster::cmds::GetError& c =
-      *static_cast<const volatile raster::cmds::GetError*>(cmd_data);
-  typedef cmds::GetError::Result Result;
-  Result* result_dst = GetSharedMemoryAs<Result*>(
-      c.result_shm_id, c.result_shm_offset, sizeof(*result_dst));
-  if (!result_dst) {
-    return error::kOutOfBounds;
-  }
-  *result_dst = GetErrorState()->GetGLError();
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleGetIntegerv(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::GetIntegerv& c =
-      *static_cast<const volatile raster::cmds::GetIntegerv*>(cmd_data);
-  GLenum pname = static_cast<GLenum>(c.pname);
-  typedef cmds::GetIntegerv::Result Result;
-  GLsizei num_values = 0;
-  if (!GetNumValuesReturnedForGLGet(pname, &num_values)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM(":GetIntegerv", pname, "pname");
-    return error::kNoError;
-  }
-  Result* result = GetSharedMemoryAs<Result*>(
-      c.params_shm_id, c.params_shm_offset, Result::ComputeSize(num_values));
-  GLint* params = result ? result->GetData() : NULL;
-  if (!validators_->g_l_state.IsValid(pname)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glGetIntegerv", pname, "pname");
-    return error::kNoError;
-  }
-  if (params == NULL) {
-    return error::kOutOfBounds;
-  }
-  LOCAL_COPY_REAL_GL_ERRORS_TO_WRAPPER("GetIntegerv");
-  // Check that the client initialized the result.
-  if (result->size != 0) {
-    return error::kInvalidArguments;
-  }
-  DoGetIntegerv(pname, params, num_values);
-  GLenum error = LOCAL_PEEK_GL_ERROR("GetIntegerv");
-  if (error == GL_NO_ERROR) {
-    result->SetNumResults(num_values);
-  }
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleTexParameteri(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::TexParameteri& c =
-      *static_cast<const volatile raster::cmds::TexParameteri*>(cmd_data);
-  GLenum target = static_cast<GLenum>(c.target);
-  GLenum pname = static_cast<GLenum>(c.pname);
-  GLint param = static_cast<GLint>(c.param);
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexParameteri", target, "target");
-    return error::kNoError;
-  }
-  if (!validators_->texture_parameter.IsValid(pname)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexParameteri", pname, "pname");
-    return error::kNoError;
-  }
-  DoTexParameteri(target, pname, param);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleTexStorage2DEXT(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::TexStorage2DEXT& c =
-      *static_cast<const volatile raster::cmds::TexStorage2DEXT*>(cmd_data);
-  if (!features().ext_texture_storage) {
-    return error::kUnknownCommand;
-  }
-
-  GLenum target = static_cast<GLenum>(c.target);
-  GLsizei levels = static_cast<GLsizei>(c.levels);
-  GLenum internalFormat = static_cast<GLenum>(c.internalFormat);
-  GLsizei width = static_cast<GLsizei>(c.width);
-  GLsizei height = static_cast<GLsizei>(c.height);
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage2DEXT", target, "target");
-    return error::kNoError;
-  }
-  if (levels < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DEXT", "levels < 0");
-    return error::kNoError;
-  }
-  if (!validators_->texture_internal_format_storage.IsValid(internalFormat)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage2DEXT", internalFormat,
-                                    "internalFormat");
-    return error::kNoError;
-  }
-  if (width < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DEXT", "width < 0");
-    return error::kNoError;
-  }
-  if (height < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DEXT", "height < 0");
-    return error::kNoError;
-  }
-  DoTexStorage2DEXT(target, levels, internalFormat, width, height);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleGenQueriesEXTImmediate(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::GenQueriesEXTImmediate& c =
-      *static_cast<const volatile raster::cmds::GenQueriesEXTImmediate*>(
-          cmd_data);
-  GLsizei n = static_cast<GLsizei>(c.n);
-  uint32_t data_size;
-  if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) {
-    return error::kOutOfBounds;
-  }
-  volatile GLuint* queries =
-      GetImmediateDataAs<volatile GLuint*>(c, data_size, immediate_data_size);
-  if (queries == NULL) {
-    return error::kOutOfBounds;
-  }
-  auto queries_copy = std::make_unique<GLuint[]>(n);
-  GLuint* queries_safe = queries_copy.get();
-  std::copy(queries, queries + n, queries_safe);
-  if (!CheckUniqueAndNonNullIds(n, queries_safe) ||
-      !GenQueriesEXTHelper(n, queries_safe)) {
-    return error::kInvalidArguments;
-  }
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleDeleteQueriesEXTImmediate(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::DeleteQueriesEXTImmediate& c =
-      *static_cast<const volatile raster::cmds::DeleteQueriesEXTImmediate*>(
-          cmd_data);
-  GLsizei n = static_cast<GLsizei>(c.n);
-  uint32_t data_size;
-  if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) {
-    return error::kOutOfBounds;
-  }
-  volatile const GLuint* queries = GetImmediateDataAs<volatile const GLuint*>(
-      c, data_size, immediate_data_size);
-  if (queries == NULL) {
-    return error::kOutOfBounds;
-  }
-  DeleteQueriesEXTHelper(n, queries);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleCopySubTextureCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::CopySubTextureCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::CopySubTextureCHROMIUM*>(
-          cmd_data);
-  GLuint source_id = static_cast<GLuint>(c.source_id);
-  GLint source_level = static_cast<GLint>(c.source_level);
-  GLenum dest_target = static_cast<GLenum>(c.dest_target);
-  GLuint dest_id = static_cast<GLuint>(c.dest_id);
-  GLint dest_level = static_cast<GLint>(c.dest_level);
-  GLint xoffset = static_cast<GLint>(c.xoffset);
-  GLint yoffset = static_cast<GLint>(c.yoffset);
-  GLint x = static_cast<GLint>(c.x);
-  GLint y = static_cast<GLint>(c.y);
-  GLsizei width = static_cast<GLsizei>(c.width);
-  GLsizei height = static_cast<GLsizei>(c.height);
-  GLboolean unpack_flip_y = static_cast<GLboolean>(c.unpack_flip_y);
-  GLboolean unpack_premultiply_alpha =
-      static_cast<GLboolean>(c.unpack_premultiply_alpha);
-  GLboolean unpack_unmultiply_alpha =
-      static_cast<GLboolean>(c.unpack_unmultiply_alpha);
-  if (!validators_->texture_target.IsValid(dest_target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glCopySubTextureCHROMIUM", dest_target,
-                                    "dest_target");
-    return error::kNoError;
-  }
-  if (width < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureCHROMIUM",
-                       "width < 0");
-    return error::kNoError;
-  }
-  if (height < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTextureCHROMIUM",
-                       "height < 0");
-    return error::kNoError;
-  }
-  DoCopySubTextureCHROMIUM(source_id, source_level, dest_target, dest_id,
-                           dest_level, xoffset, yoffset, x, y, width, height,
-                           unpack_flip_y, unpack_premultiply_alpha,
-                           unpack_unmultiply_alpha);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleCompressedCopyTextureCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::CompressedCopyTextureCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::CompressedCopyTextureCHROMIUM*>(
-          cmd_data);
-  GLuint source_id = static_cast<GLuint>(c.source_id);
-  GLuint dest_id = static_cast<GLuint>(c.dest_id);
-  DoCompressedCopyTextureCHROMIUM(source_id, dest_id);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleProduceTextureDirectCHROMIUMImmediate(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::ProduceTextureDirectCHROMIUMImmediate& c =
-      *static_cast<
-          const volatile raster::cmds::ProduceTextureDirectCHROMIUMImmediate*>(
-          cmd_data);
-  GLuint texture = c.texture;
-  uint32_t data_size;
-  if (!GLES2Util::ComputeDataSize<GLbyte, 16>(1, &data_size)) {
-    return error::kOutOfBounds;
-  }
-  if (data_size > immediate_data_size) {
-    return error::kOutOfBounds;
-  }
-  volatile const GLbyte* mailbox = GetImmediateDataAs<volatile const GLbyte*>(
-      c, data_size, immediate_data_size);
-  if (mailbox == NULL) {
-    return error::kOutOfBounds;
-  }
-  DoProduceTextureDirectCHROMIUM(texture, mailbox);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleBindTexImage2DCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::BindTexImage2DCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::BindTexImage2DCHROMIUM*>(
-          cmd_data);
-  GLenum target = static_cast<GLenum>(c.target);
-  GLint imageId = static_cast<GLint>(c.imageId);
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glBindTexImage2DCHROMIUM", target,
-                                    "target");
-    return error::kNoError;
-  }
-  DoBindTexImage2DCHROMIUM(target, imageId);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleReleaseTexImage2DCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::ReleaseTexImage2DCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::ReleaseTexImage2DCHROMIUM*>(
-          cmd_data);
-  GLenum target = static_cast<GLenum>(c.target);
-  GLint imageId = static_cast<GLint>(c.imageId);
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glReleaseTexImage2DCHROMIUM", target,
-                                    "target");
-    return error::kNoError;
-  }
-  DoReleaseTexImage2DCHROMIUM(target, imageId);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleTraceEndCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  DoTraceEndCHROMIUM();
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleLoseContextCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::LoseContextCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::LoseContextCHROMIUM*>(cmd_data);
-  GLenum current = static_cast<GLenum>(c.current);
-  GLenum other = static_cast<GLenum>(c.other);
-  if (!validators_->reset_status.IsValid(current)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glLoseContextCHROMIUM", current,
-                                    "current");
-    return error::kNoError;
-  }
-  if (!validators_->reset_status.IsValid(other)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glLoseContextCHROMIUM", other, "other");
-    return error::kNoError;
-  }
-  DoLoseContextCHROMIUM(current, other);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleBeginRasterCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::BeginRasterCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::BeginRasterCHROMIUM*>(cmd_data);
-  if (!features().chromium_raster_transport) {
-    return error::kUnknownCommand;
-  }
-
-  GLuint texture_id = static_cast<GLuint>(c.texture_id);
-  GLuint sk_color = static_cast<GLuint>(c.sk_color);
-  GLuint msaa_sample_count = static_cast<GLuint>(c.msaa_sample_count);
-  GLboolean can_use_lcd_text = static_cast<GLboolean>(c.can_use_lcd_text);
-  GLboolean use_distance_field_text =
-      static_cast<GLboolean>(c.use_distance_field_text);
-  GLint color_type = static_cast<GLint>(c.color_type);
-  DoBeginRasterCHROMIUM(texture_id, sk_color, msaa_sample_count,
-                        can_use_lcd_text, use_distance_field_text, color_type);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleRasterCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::RasterCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::RasterCHROMIUM*>(cmd_data);
-  if (!features().chromium_raster_transport) {
-    return error::kUnknownCommand;
-  }
-
-  GLsizeiptr size = static_cast<GLsizeiptr>(c.size);
-  uint32_t data_size = size;
-  const void* list = GetSharedMemoryAs<const void*>(
-      c.list_shm_id, c.list_shm_offset, data_size);
-  if (size < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glRasterCHROMIUM", "size < 0");
-    return error::kNoError;
-  }
-  if (list == NULL) {
-    return error::kOutOfBounds;
-  }
-  DoRasterCHROMIUM(size, list);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleEndRasterCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  if (!features().chromium_raster_transport) {
-    return error::kUnknownCommand;
-  }
-
-  DoEndRasterCHROMIUM();
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleCreateTransferCacheEntryINTERNAL(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::CreateTransferCacheEntryINTERNAL& c =
-      *static_cast<
-          const volatile raster::cmds::CreateTransferCacheEntryINTERNAL*>(
-          cmd_data);
-  GLuint entry_type = static_cast<GLuint>(c.entry_type);
-  GLuint entry_id = static_cast<GLuint>(c.entry_id);
-  GLuint handle_shm_id = static_cast<GLuint>(c.handle_shm_id);
-  GLuint handle_shm_offset = static_cast<GLuint>(c.handle_shm_offset);
-  GLuint data_shm_id = static_cast<GLuint>(c.data_shm_id);
-  GLuint data_shm_offset = static_cast<GLuint>(c.data_shm_offset);
-  GLuint data_size = static_cast<GLuint>(c.data_size);
-  DoCreateTransferCacheEntryINTERNAL(entry_type, entry_id, handle_shm_id,
-                                     handle_shm_offset, data_shm_id,
-                                     data_shm_offset, data_size);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleDeleteTransferCacheEntryINTERNAL(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::DeleteTransferCacheEntryINTERNAL& c =
-      *static_cast<
-          const volatile raster::cmds::DeleteTransferCacheEntryINTERNAL*>(
-          cmd_data);
-  GLuint entry_type = static_cast<GLuint>(c.entry_type);
-  GLuint entry_id = static_cast<GLuint>(c.entry_id);
-  DoDeleteTransferCacheEntryINTERNAL(entry_type, entry_id);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleUnlockTransferCacheEntryINTERNAL(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::UnlockTransferCacheEntryINTERNAL& c =
-      *static_cast<
-          const volatile raster::cmds::UnlockTransferCacheEntryINTERNAL*>(
-          cmd_data);
-  GLuint entry_type = static_cast<GLuint>(c.entry_type);
-  GLuint entry_id = static_cast<GLuint>(c.entry_id);
-  DoUnlockTransferCacheEntryINTERNAL(entry_type, entry_id);
-  return error::kNoError;
-}
-
-error::Error RasterDecoderImpl::HandleTexStorage2DImageCHROMIUM(
-    uint32_t immediate_data_size,
-    const volatile void* cmd_data) {
-  const volatile raster::cmds::TexStorage2DImageCHROMIUM& c =
-      *static_cast<const volatile raster::cmds::TexStorage2DImageCHROMIUM*>(
-          cmd_data);
-  if (!features().chromium_texture_storage_image) {
-    return error::kUnknownCommand;
-  }
-
-  GLenum target = static_cast<GLenum>(c.target);
-  GLenum internalFormat = static_cast<GLenum>(c.internalFormat);
-  GLenum bufferUsage = static_cast<GLenum>(c.bufferUsage);
-  GLsizei width = static_cast<GLsizei>(c.width);
-  GLsizei height = static_cast<GLsizei>(c.height);
-  if (!validators_->texture_bind_target.IsValid(target)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage2DImageCHROMIUM", target,
-                                    "target");
-    return error::kNoError;
-  }
-  if (!validators_->texture_internal_format_storage.IsValid(internalFormat)) {
-    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage2DImageCHROMIUM",
-                                    internalFormat, "internalFormat");
-    return error::kNoError;
-  }
-  if (width < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM",
-                       "width < 0");
-    return error::kNoError;
-  }
-  if (height < 0) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glTexStorage2DImageCHROMIUM",
-                       "height < 0");
-    return error::kNoError;
-  }
-  DoTexStorage2DImageCHROMIUM(target, internalFormat, bufferUsage, width,
-                              height);
-  return error::kNoError;
-}
-
-#endif  // GPU_COMMAND_BUFFER_SERVICE_RASTER_CMD_DECODER_AUTOGEN_H_
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 91b16ea6..9a0e333 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -2425,11 +2425,18 @@
     return false;
   }
 
+  unsigned int internal_format =
+      viz::TextureStorageFormat(texture_metadata.format());
+  if (!feature_info_->validators()->texture_internal_format_storage.IsValid(
+          internal_format)) {
+    LOCAL_SET_GL_ERROR_INVALID_ENUM("glTexStorage2D", internal_format,
+                                    "internal_format");
+    return error::kNoError;
+  }
+
   ScopedTextureBinder binder(&state_, texture_manager(), texture_ref,
                              texture_metadata.target(), gr_context());
 
-  unsigned int internal_format =
-      viz::TextureStorageFormat(texture_metadata.format());
   GLenum format =
       gles2::TextureManager::ExtractFormatFromStorageFormat(internal_format);
   GLenum type =
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 73ef774..859d19d4a 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -486,6 +486,29 @@
   EXPECT_EQ(GL_INVALID_OPERATION, GetGLError());
 }
 
+TEST_P(RasterDecoderManualInitTest, TexStorage2DValidateColorFormat) {
+  // GL_EXT_texture_norm16 disabled by default, so glTexStorage2DEXT with
+  // viz::ResourceFormat::R16_EXT fails validation.
+  InitState init;
+  init.extensions.push_back("GL_EXT_texture_storage");
+  InitDecoder(init);
+
+  GLuint texture_id = kNewClientId;
+  EXPECT_CALL(*gl_, GenTextures(1, _))
+      .WillOnce(SetArgPointee<1>(kNewServiceId))
+      .RetiresOnSaturation();
+  cmds::CreateTexture create_cmd;
+  create_cmd.Init(false /* use_buffer */, gfx::BufferUsage::GPU_READ,
+                  viz::ResourceFormat::R16_EXT, texture_id);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(create_cmd));
+
+  cmds::TexStorage2D storage_cmd;
+  storage_cmd.Init(texture_id, /*width=*/2, /*height=*/2);
+  EXPECT_EQ(error::kNoError, ExecuteCmd(storage_cmd));
+
+  EXPECT_EQ(GL_INVALID_ENUM, GetGLError());
+}
+
 TEST_P(RasterDecoderTest, GLImageAttachedAfterClearLevel) {
   scoped_refptr<gl::GLImage> image(new gl::GLImageStub);
   GetImageManagerForTest()->AddImage(image.get(), kImageId);
diff --git a/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json b/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json
index 859dae1a..a1707f2 100644
--- a/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json
+++ b/ios/build/bots/chromium.fyi/ios12-sdk-xcode-clang.json
@@ -1,7 +1,8 @@
 {
   "comments": [
-    "Tests for super-fat iOS 12.0.",
-    "Build is performed with gn+ninja."
+    "Tests for iOS 12.0 beta xcode clang. Goma can be enabled for beta 4 and",
+    "the final GM, but disabled for the other betas to reduce swirl for the",
+    "goma team. Build is performed with gn+ninja."
   ],
   "xcode build version": "10l213o",
   "use xcode build version": true,
@@ -13,7 +14,8 @@
     "symbol_level=1",
     "target_cpu=\"x64\"",
     "target_os=\"ios\"",
-    "use_goma=false",
+    "use_goma=true",
+    "goma_dir=\"$(goma_dir)\"",
     "use_xcode_clang=true"
   ],
   "env": {
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index 8346971b..3aa1219 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -367,6 +367,10 @@
     {"copy-image", flag_descriptions::kCopyImageName,
      flag_descriptions::kCopyImageDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kCopyImage)},
+    {"new-password-form-parsing",
+     flag_descriptions::kNewPasswordFormParsingName,
+     flag_descriptions::kNewPasswordFormParsingDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(password_manager::features::kNewPasswordFormParsing)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
index 656c144..58a3ad4 100644
--- a/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
+++ b/ios/chrome/browser/browser_state/browser_state_keyed_service_factories.mm
@@ -50,6 +50,7 @@
 #include "ios/chrome/browser/suggestions/suggestions_service_factory.h"
 #include "ios/chrome/browser/sync/consent_auditor_factory.h"
 #include "ios/chrome/browser/sync/ios_user_event_service_factory.h"
+#include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/translate/translate_accept_languages_factory.h"
@@ -115,6 +116,7 @@
   IOSChromeContentSuggestionsServiceFactory::GetInstance();
   IOSChromePasswordStoreFactory::GetInstance();
   IOSChromeProfileInvalidationProviderFactory::GetInstance();
+  ModelTypeStoreServiceFactory::GetInstance();
   ProfileSyncServiceFactory::GetInstance();
   IOSUserEventServiceFactory::GetInstance();
   GoogleLogoServiceFactory::GetInstance();
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
index 9a3017ce..f87488b 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_io_data.mm
@@ -82,8 +82,6 @@
         ChromeBrowserStateIOData::IOSChromeURLRequestContextGetterVector>
         getters) {
   DCHECK_CURRENTLY_ON(web::WebThread::IO);
-  ChromeBrowserStateIOData::IOSChromeURLRequestContextGetterVector::iterator
-      iter;
   for (auto& chrome_context_getter : *getters)
     chrome_context_getter->NotifyContextShuttingDown();
 }
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 9b9c30c..f25b3bd 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -180,6 +180,12 @@
     "prototype site instead of triggering the native Tab Switcher. The native "
     "TabSwitcher is accessible by long pressing the button";
 
+const char kNewPasswordFormParsingName[] = "New password form parsing";
+const char kNewPasswordFormParsingDescription[] =
+    "Replaces existing form parsing in password manager with a new version, "
+    "currently under development. WARNING: when enabled Password Manager might "
+    "stop working";
+
 const char kOmniboxUIElideSuggestionUrlAfterHostName[] =
     "Hide the path, query, and ref of omnibox suggestions";
 const char kOmniboxUIElideSuggestionUrlAfterHostDescription[] =
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index ba2e51fd..a9d4713 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -148,6 +148,10 @@
 extern const char kMemexTabSwitcherName[];
 extern const char kMemexTabSwitcherDescription[];
 
+// Title and description for the flag to enable new password form parsing.
+extern const char kNewPasswordFormParsingName[];
+extern const char kNewPasswordFormParsingDescription[];
+
 // Title and description for the flag to enable elision of the URL path, query,
 // and ref in omnibox URL suggestions.
 extern const char kOmniboxUIElideSuggestionUrlAfterHostName[];
diff --git a/ios/chrome/browser/metrics/ukm_egtest.mm b/ios/chrome/browser/metrics/ukm_egtest.mm
index d713178..50674449 100644
--- a/ios/chrome/browser/metrics/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/ukm_egtest.mm
@@ -40,11 +40,9 @@
 using chrome_test_util::AccountsSyncButton;
 using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::ClearBrowsingDataCollectionView;
-using chrome_test_util::ClearBrowsingHistoryButton;
 using chrome_test_util::GetIncognitoTabCount;
 using chrome_test_util::IsIncognitoMode;
 using chrome_test_util::IsSyncInitialized;
-using chrome_test_util::SecondarySignInButton;
 using chrome_test_util::SettingsAccountButton;
 using chrome_test_util::SettingsDoneButton;
 using chrome_test_util::SettingsMenuPrivacyButton;
diff --git a/ios/chrome/browser/reading_list/reading_list_model_factory.cc b/ios/chrome/browser/reading_list/reading_list_model_factory.cc
index 8520574..d8ee59a0 100644
--- a/ios/chrome/browser/reading_list/reading_list_model_factory.cc
+++ b/ios/chrome/browser/reading_list/reading_list_model_factory.cc
@@ -9,17 +9,18 @@
 #include "base/files/file_path.h"
 #include "base/memory/singleton.h"
 #include "base/time/default_clock.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/reading_list/core/reading_list_model_impl.h"
 #include "components/reading_list/core/reading_list_pref_names.h"
 #include "components/reading_list/core/reading_list_store.h"
 #include "components/sync/base/report_unrecoverable_error.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/experimental_flags.h"
+#include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/web_thread.h"
 
@@ -45,7 +46,9 @@
 ReadingListModelFactory::ReadingListModelFactory()
     : BrowserStateKeyedServiceFactory(
           "ReadingListModel",
-          BrowserStateDependencyManager::GetInstance()) {}
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
+}
 
 ReadingListModelFactory::~ReadingListModelFactory() {}
 
@@ -62,8 +65,8 @@
       ios::ChromeBrowserState::FromBrowserState(context);
 
   syncer::OnceModelTypeStoreFactory store_factory =
-      browser_sync::ProfileSyncService::GetModelTypeStoreFactory(
-          chrome_browser_state->GetStatePath());
+      ModelTypeStoreServiceFactory::GetForBrowserState(chrome_browser_state)
+          ->GetStoreFactory();
   auto change_processor =
       std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
           syncer::READING_LIST,
diff --git a/ios/chrome/browser/sync/BUILD.gn b/ios/chrome/browser/sync/BUILD.gn
index 3f46b6fb..11c64128a 100644
--- a/ios/chrome/browser/sync/BUILD.gn
+++ b/ios/chrome/browser/sync/BUILD.gn
@@ -17,6 +17,8 @@
     "ios_chrome_synced_tab_delegate.mm",
     "ios_user_event_service_factory.cc",
     "ios_user_event_service_factory.h",
+    "model_type_store_service_factory.cc",
+    "model_type_store_service_factory.h",
     "profile_sync_service_factory.cc",
     "profile_sync_service_factory.h",
     "sync_observer_bridge.h",
diff --git a/ios/chrome/browser/sync/consent_auditor_factory.cc b/ios/chrome/browser/sync/consent_auditor_factory.cc
index e023a0a..98ab4073 100644
--- a/ios/chrome/browser/sync/consent_auditor_factory.cc
+++ b/ios/chrome/browser/sync/consent_auditor_factory.cc
@@ -9,11 +9,13 @@
 
 #include "ios/chrome/browser/sync/consent_auditor_factory.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/bind_helpers.h"
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/singleton.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/consent_auditor/consent_auditor_impl.h"
 #include "components/consent_auditor/consent_sync_bridge.h"
 #include "components/consent_auditor/consent_sync_bridge_impl.h"
@@ -21,11 +23,13 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 #include "components/version_info/version_info.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/sync/ios_user_event_service_factory.h"
+#include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/browser_state.h"
 
@@ -53,6 +57,7 @@
     : BrowserStateKeyedServiceFactory(
           "ConsentAuditor",
           BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
   DependsOn(IOSUserEventServiceFactory::GetInstance());
 }
 
@@ -67,8 +72,8 @@
   syncer::UserEventService* user_event_service = nullptr;
   if (base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType)) {
     syncer::OnceModelTypeStoreFactory store_factory =
-        browser_sync::ProfileSyncService::GetModelTypeStoreFactory(
-            browser_state->GetStatePath());
+        ModelTypeStoreServiceFactory::GetForBrowserState(ios_browser_state)
+            ->GetStoreFactory();
     auto change_processor =
         std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
             syncer::USER_CONSENTS,
diff --git a/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc b/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc
index 45cd2fd..6c0e3727 100644
--- a/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc
+++ b/ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.cc
@@ -37,7 +37,6 @@
       sync_client ? std::move(sync_client)
                   : std::make_unique<IOSChromeSyncClient>(browser_state);
   init_params.network_time_update_callback = base::DoNothing();
-  init_params.base_directory = browser_state->GetStatePath();
   init_params.url_request_context = browser_state->GetRequestContext();
   init_params.url_loader_factory = browser_state->GetSharedURLLoaderFactory();
   init_params.debug_identifier = browser_state->GetDebugName();
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.h b/ios/chrome/browser/sync/ios_chrome_sync_client.h
index 216e975..9ad66714 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.h
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.h
@@ -42,6 +42,7 @@
   syncer::SyncService* GetSyncService() override;
   PrefService* GetPrefService() override;
   base::FilePath GetLocalSyncBackendFolder() override;
+  syncer::ModelTypeStoreService* GetModelTypeStoreService() override;
   bookmarks::BookmarkModel* GetBookmarkModel() override;
   favicon::FaviconService* GetFaviconService() override;
   history::HistoryService* GetHistoryService() override;
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index 6d513071..233563c9 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -59,6 +59,7 @@
 #include "ios/chrome/browser/sync/consent_auditor_factory.h"
 #include "ios/chrome/browser/sync/glue/sync_start_util.h"
 #include "ios/chrome/browser/sync/ios_user_event_service_factory.h"
+#include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #include "ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.h"
 #include "ios/chrome/browser/tabs/tab_model_synced_window_delegate_getter.h"
@@ -188,6 +189,11 @@
   return base::FilePath();
 }
 
+syncer::ModelTypeStoreService* IOSChromeSyncClient::GetModelTypeStoreService() {
+  DCHECK_CURRENTLY_ON(web::WebThread::UI);
+  return ModelTypeStoreServiceFactory::GetForBrowserState(browser_state_);
+}
+
 bookmarks::BookmarkModel* IOSChromeSyncClient::GetBookmarkModel() {
   DCHECK_CURRENTLY_ON(web::WebThread::UI);
   return ios::BookmarkModelFactory::GetForBrowserState(browser_state_);
diff --git a/ios/chrome/browser/sync/ios_user_event_service_factory.cc b/ios/chrome/browser/sync/ios_user_event_service_factory.cc
index d809709a..3469d25 100644
--- a/ios/chrome/browser/sync/ios_user_event_service_factory.cc
+++ b/ios/chrome/browser/sync/ios_user_event_service_factory.cc
@@ -12,12 +12,14 @@
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/report_unrecoverable_error.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 #include "components/sync/user_events/no_op_user_event_service.h"
 #include "components/sync/user_events/user_event_service_impl.h"
 #include "components/sync/user_events/user_event_sync_bridge.h"
 #include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
 #include "ios/web/public/browser_state.h"
@@ -37,24 +39,28 @@
 IOSUserEventServiceFactory::IOSUserEventServiceFactory()
     : BrowserStateKeyedServiceFactory(
           "UserEventService",
-          BrowserStateDependencyManager::GetInstance()) {}
+          BrowserStateDependencyManager::GetInstance()) {
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
+}
 
 IOSUserEventServiceFactory::~IOSUserEventServiceFactory() {}
 
 std::unique_ptr<KeyedService>
 IOSUserEventServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* browser_state) const {
+    web::BrowserState* context) const {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
+
   syncer::SyncService* sync_service =
-      ProfileSyncServiceFactory::GetForBrowserState(
-          ios::ChromeBrowserState::FromBrowserState(browser_state));
+      ProfileSyncServiceFactory::GetForBrowserState(browser_state);
   if (!syncer::UserEventServiceImpl::MightRecordEvents(
           browser_state->IsOffTheRecord(), sync_service)) {
     return std::make_unique<syncer::NoOpUserEventService>();
   }
 
   syncer::OnceModelTypeStoreFactory store_factory =
-      browser_sync::ProfileSyncService::GetModelTypeStoreFactory(
-          browser_state->GetStatePath());
+      ModelTypeStoreServiceFactory::GetForBrowserState(browser_state)
+          ->GetStoreFactory();
   auto bridge = std::make_unique<syncer::UserEventSyncBridge>(
       std::move(store_factory),
       std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
diff --git a/ios/chrome/browser/sync/ios_user_event_service_factory.h b/ios/chrome/browser/sync/ios_user_event_service_factory.h
index 31071e1b..418fceee 100644
--- a/ios/chrome/browser/sync/ios_user_event_service_factory.h
+++ b/ios/chrome/browser/sync/ios_user_event_service_factory.h
@@ -27,7 +27,7 @@
 class IOSUserEventServiceFactory : public BrowserStateKeyedServiceFactory {
  public:
   static syncer::UserEventService* GetForBrowserState(
-      ios::ChromeBrowserState* browser_state);
+      ios::ChromeBrowserState* context);
 
   static IOSUserEventServiceFactory* GetInstance();
 
diff --git a/ios/chrome/browser/sync/model_type_store_service_factory.cc b/ios/chrome/browser/sync/model_type_store_service_factory.cc
new file mode 100644
index 0000000..877334ea
--- /dev/null
+++ b/ios/chrome/browser/sync/model_type_store_service_factory.cc
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/chrome/browser/sync/model_type_store_service_factory.h"
+
+#include <utility>
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/ios/browser_state_dependency_manager.h"
+#include "components/sync/model_impl/model_type_store_service_impl.h"
+#include "ios/chrome/browser/browser_state/browser_state_otr_helper.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+
+// static
+ModelTypeStoreServiceFactory* ModelTypeStoreServiceFactory::GetInstance() {
+  return base::Singleton<ModelTypeStoreServiceFactory>::get();
+}
+
+// static
+syncer::ModelTypeStoreService* ModelTypeStoreServiceFactory::GetForBrowserState(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<syncer::ModelTypeStoreService*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, true));
+}
+
+ModelTypeStoreServiceFactory::ModelTypeStoreServiceFactory()
+    : BrowserStateKeyedServiceFactory(
+          "ModelTypeStoreService",
+          BrowserStateDependencyManager::GetInstance()) {}
+
+ModelTypeStoreServiceFactory::~ModelTypeStoreServiceFactory() {}
+
+std::unique_ptr<KeyedService>
+ModelTypeStoreServiceFactory::BuildServiceInstanceFor(
+    web::BrowserState* context) const {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
+  return std::make_unique<syncer::ModelTypeStoreServiceImpl>(
+      browser_state->GetStatePath());
+}
+
+web::BrowserState* ModelTypeStoreServiceFactory::GetBrowserStateToUse(
+    web::BrowserState* context) const {
+  return GetBrowserStateRedirectedInIncognito(context);
+}
diff --git a/ios/chrome/browser/sync/model_type_store_service_factory.h b/ios/chrome/browser/sync/model_type_store_service_factory.h
new file mode 100644
index 0000000..783aee0
--- /dev/null
+++ b/ios/chrome/browser/sync/model_type_store_service_factory.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_SYNC_MODEL_TYPE_STORE_SERVICE_FACTORY_H_
+#define IOS_CHROME_BROWSER_SYNC_MODEL_TYPE_STORE_SERVICE_FACTORY_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace syncer {
+class ModelTypeStoreService;
+}  // namespace syncer
+
+namespace ios {
+class ChromeBrowserState;
+}  // namespace ios
+
+// Singleton that owns all ModelTypeStoreService and associates them with
+// ios::ChromeBrowserState.
+class ModelTypeStoreServiceFactory : public BrowserStateKeyedServiceFactory {
+ public:
+  static syncer::ModelTypeStoreService* GetForBrowserState(
+      ios::ChromeBrowserState* browser_state);
+
+  static ModelTypeStoreServiceFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<ModelTypeStoreServiceFactory>;
+
+  ModelTypeStoreServiceFactory();
+  ~ModelTypeStoreServiceFactory() override;
+
+  // BrowserStateKeyedServiceFactory implementation.
+  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
+      web::BrowserState* context) const override;
+  web::BrowserState* GetBrowserStateToUse(
+      web::BrowserState* context) const override;
+};
+
+#endif  // IOS_CHROME_BROWSER_SYNC_MODEL_TYPE_STORE_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory.cc b/ios/chrome/browser/sync/profile_sync_service_factory.cc
index d3a2a46..5e483061 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory.cc
@@ -34,6 +34,7 @@
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "ios/chrome/browser/sync/consent_auditor_factory.h"
 #include "ios/chrome/browser/sync/ios_chrome_sync_client.h"
+#include "ios/chrome/browser/sync/model_type_store_service_factory.h"
 #include "ios/chrome/browser/undo/bookmark_undo_service_factory.h"
 #include "ios/chrome/browser/web_data_service_factory.h"
 #include "ios/chrome/common/channel_info.h"
@@ -111,6 +112,7 @@
   DependsOn(IOSChromeGCMProfileServiceFactory::GetInstance());
   DependsOn(IOSChromePasswordStoreFactory::GetInstance());
   DependsOn(IOSChromeProfileInvalidationProviderFactory::GetInstance());
+  DependsOn(ModelTypeStoreServiceFactory::GetInstance());
   DependsOn(ReadingListModelFactory::GetInstance());
   DependsOn(SigninClientFactory::GetInstance());
 }
@@ -143,7 +145,6 @@
   init_params.sync_client =
       std::make_unique<IOSChromeSyncClient>(browser_state);
   init_params.network_time_update_callback = base::Bind(&UpdateNetworkTime);
-  init_params.base_directory = browser_state->GetStatePath();
   init_params.url_request_context = browser_state->GetRequestContext();
   init_params.url_loader_factory = browser_state->GetSharedURLLoaderFactory();
   init_params.debug_identifier = browser_state->GetDebugName();
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index 789c079..66e73e6d 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -177,8 +177,10 @@
     "unified_consent",
     "//components/signin/core/browser",
     "//components/signin/core/browser:account_info",
+    "//components/unified_consent",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/ui/authentication/unified_consent:unified_consent_ui",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/public/provider/chrome/browser/signin",
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
index d2d6f671..d233beae 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
@@ -6,8 +6,11 @@
 
 #import <EarlGrey/EarlGrey.h>
 
+#include "components/unified_consent/feature.h"
 #include "ios/chrome/browser/ui/authentication/signin_confirmation_view_controller.h"
 #import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
+#import "ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.h"
+#import "ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
@@ -33,7 +36,18 @@
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI
       tapSettingsMenuButton:chrome_test_util::SecondarySignInButton()];
-  [self selectIdentityWithEmail:identity.userEmail];
+  if (base::FeatureList::IsEnabled(unified_consent::kUnifiedConsent)) {
+    [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                            kIdentityPickerViewIdentifier)]
+        performAction:grey_tap()];
+    [[EarlGrey
+        selectElementWithMatcher:grey_allOf(grey_accessibilityLabel(
+                                                identity.userEmail),
+                                            grey_sufficientlyVisible(), nil)]
+        performAction:grey_tap()];
+  } else {
+    [self selectIdentityWithEmail:identity.userEmail];
+  }
   [self confirmSigninConfirmationDialog];
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -58,9 +72,15 @@
   // Wait until the sync confirmation is displayed.
   id<GREYMatcher> signinUICollectionViewMatcher = nil;
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
-  signinUICollectionViewMatcher = grey_allOf(
-      grey_ancestor(grey_accessibilityID(kSigninConfirmationCollectionViewId)),
-      grey_kindOfClass([UICollectionView class]), nil);
+  if (base::FeatureList::IsEnabled(unified_consent::kUnifiedConsent)) {
+    signinUICollectionViewMatcher =
+        grey_accessibilityID(kUnifiedConsentScrollViewIdentifier);
+  } else {
+    signinUICollectionViewMatcher = grey_allOf(
+        grey_ancestor(
+            grey_accessibilityID(kSigninConfirmationCollectionViewId)),
+        grey_kindOfClass([UICollectionView class]), nil);
+  }
   [[EarlGrey selectElementWithMatcher:signinUICollectionViewMatcher]
       performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
 
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_item.mm b/ios/chrome/browser/ui/authentication/signin_promo_item.mm
index 16c74a68..41f8bb8 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_item.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_item.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_configurator.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -14,6 +15,11 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// Padding for SignInPromoView.
+const CGFloat kSignInPromoViewPadding = 10;
+}  // namespace
+
 @implementation SigninPromoItem
 
 @synthesize configurator = _configurator;
@@ -50,10 +56,28 @@
     UIView* contentView = self.contentView;
     _signinPromoView =
         [[SigninPromoView alloc] initWithFrame:self.bounds
-                                         style:SigninPromoViewUILegacy];
+                                         style:SigninPromoViewUIRefresh];
     _signinPromoView.translatesAutoresizingMaskIntoConstraints = NO;
     [contentView addSubview:_signinPromoView];
-    AddSameConstraints(_signinPromoView, contentView);
+
+    if (IsUIRefreshPhase1Enabled()) {
+      [NSLayoutConstraint activateConstraints:@[
+        [_signinPromoView.leadingAnchor
+            constraintEqualToAnchor:contentView.leadingAnchor
+                           constant:kSignInPromoViewPadding],
+        [_signinPromoView.trailingAnchor
+            constraintEqualToAnchor:contentView.trailingAnchor
+                           constant:-kSignInPromoViewPadding],
+        [_signinPromoView.topAnchor
+            constraintEqualToAnchor:contentView.topAnchor
+                           constant:kSignInPromoViewPadding],
+        [_signinPromoView.bottomAnchor
+            constraintEqualToAnchor:contentView.bottomAnchor
+                           constant:-kSignInPromoViewPadding],
+      ]];
+    } else {
+      AddSameConstraints(_signinPromoView, contentView);
+    }
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.h b/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.h
index 9dda7ef..4ad661a 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.h
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.h
@@ -7,6 +7,9 @@
 
 #import <UIKit/UIKit.h>
 
+// Accessibility identifier.
+extern NSString* const kIdentityPickerViewIdentifier;
+
 // Displays the name, email and avatar of a chrome identity, as a control.
 // An down arrow is also displayed on the right of the control, to invite the
 // user to tap and select another chrome identity. To get the tap event, see:
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm b/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm
index 106f2465..169d253 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm
@@ -14,6 +14,9 @@
 #error "This file requires ARC support."
 #endif
 
+NSString* const kIdentityPickerViewIdentifier =
+    @"kIdentityPickerViewIdentifier";
+
 namespace {
 
 const CGFloat kIdentityPickerViewRadius = 8.;
@@ -43,6 +46,7 @@
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
+    self.accessibilityIdentifier = kIdentityPickerViewIdentifier;
     self.layer.cornerRadius = kIdentityPickerViewRadius;
     self.backgroundColor = UIColorFromRGB(kHeaderBackgroundColor);
     // Adding view elements inside.
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.h b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.h
index d52e957..a6bee264 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.h
+++ b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.h
@@ -9,6 +9,9 @@
 
 #include <vector>
 
+// Accessibility identifier for |-UnifiedConsentViewController.view|.
+extern NSString* const kUnifiedConsentScrollViewIdentifier;
+
 @class UnifiedConsentViewController;
 
 // Delegate protocol for UnifiedConsentViewController.
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm
index f6eb4361..cf0c232 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm
@@ -22,6 +22,9 @@
 #error "This file requires ARC support."
 #endif
 
+NSString* const kUnifiedConsentScrollViewIdentifier =
+    @"kUnifiedConsentScrollViewIdentifier";
+
 namespace {
 
 // Sizes.
@@ -167,6 +170,7 @@
   // Main scroll view.
   self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
   self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
+  self.scrollView.accessibilityIdentifier = kUnifiedConsentScrollViewIdentifier;
   if (@available(iOS 11, *)) {
     // The observed behavior was buggy. When the view appears on the screen,
     // the scrollvie was not scrolled all the way to the top. Adjusting the
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm
index 13ab046..c801ce8b 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_legacy_view.mm
@@ -7,6 +7,7 @@
 #import "ios/chrome/browser/ui/animation_util.h"
 #import "ios/chrome/browser/ui/omnibox/clipping_textfield_container.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
+#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
@@ -315,8 +316,10 @@
 
 #pragma mark - OmniboxLeftImageConsumer
 
-- (void)setLeftImageId:(int)imageId {
-  [self setPlaceholderImage:imageId];
+- (void)setLeftImageForAutocompleteType:(AutocompleteMatchType::Type)type {
+  int image_id = GetIconForAutocompleteMatchType(type, /* is_starred */ false,
+                                                 /* is_incognito */ false);
+  [self setPlaceholderImage:image_id];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_left_image_consumer.h b/ios/chrome/browser/ui/omnibox/omnibox_left_image_consumer.h
index 04165ed..bdda045 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_left_image_consumer.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_left_image_consumer.h
@@ -7,13 +7,15 @@
 
 #import <UIKit/UIKit.h>
 
+#include "components/omnibox/browser/autocomplete_match_type.h"
+
 // Describes an object that accepts a left image for the omnibox. The left image
 // is used for showing the current selected suggestion icon, when the
 // suggestions popup is visible.
 @protocol OmniboxLeftImageConsumer
 
 // The |imageId| is a resource id.
-- (void)setLeftImageId:(int)imageId;
+- (void)setLeftImageForAutocompleteType:(AutocompleteMatchType::Type)type;
 
 @end
 
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
index 4b29c55..2f11b42 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_mediator.mm
@@ -4,7 +4,9 @@
 
 #import "ios/chrome/browser/ui/omnibox/omnibox_mediator.h"
 
+#include "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_consumer.h"
+#include "ios/chrome/browser/ui/omnibox/omnibox_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -16,8 +18,10 @@
 
 #pragma mark - OmniboxLeftImageConsumer
 
-- (void)setLeftImageId:(int)imageId {
-  UIImage* image = [NativeImage(imageId)
+- (void)setLeftImageForAutocompleteType:(AutocompleteMatchType::Type)type {
+  std::string imageName =
+      GetResourceNameForAutocompleteMatchType(type, /* is_starred */ false);
+  UIImage* image = [[UIImage imageNamed:base::SysUTF8ToNSString(imageName)]
       imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
   [self.consumer updateAutocompleteIcon:image];
 }
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
index 09a3435..f7ca13a7 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.h
@@ -104,7 +104,8 @@
 
   // OmniboxPopupViewSuggestionsDelegate methods
 
-  void OnTopmostSuggestionImageChanged(int imageId) override;
+  void OnTopmostSuggestionImageChanged(
+      AutocompleteMatchType::Type type) override;
   void OnResultsChanged(const AutocompleteResult& result) override;
   void OnPopupDidScroll() override;
   void OnSelectedMatchForAppending(const base::string16& str) override;
@@ -122,9 +123,6 @@
   // Clears the text from the omnibox.
   void ClearText();
 
-  // Set first result image.
-  void SetLeftImage(const int imageId);
-
   // Hide keyboard and call OnDidEndEditing.  This dismisses the keyboard and
   // also finalizes the editing state of the omnibox.
   void HideKeyboardAndEndEditing();
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
index 529d7c5..1790caf 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_ios.mm
@@ -897,10 +897,6 @@
   return [[field_ text] rangeOfString:objectReplacementChar].length > 0;
 }
 
-void OmniboxViewIOS::SetLeftImage(int imageId) {
-  [left_image_consumer_ setLeftImageId:imageId];
-}
-
 void OmniboxViewIOS::HideKeyboardAndEndEditing() {
   [field_ resignFirstResponder];
 
@@ -959,8 +955,9 @@
 
 #pragma mark - OmniboxPopupViewSuggestionsDelegate
 
-void OmniboxViewIOS::OnTopmostSuggestionImageChanged(int imageId) {
-  this->SetLeftImage(imageId);
+void OmniboxViewIOS::OnTopmostSuggestionImageChanged(
+    AutocompleteMatchType::Type type) {
+  [left_image_consumer_ setLeftImageForAutocompleteType:type];
 }
 
 void OmniboxViewIOS::OnResultsChanged(const AutocompleteResult& result) {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm
index 42959d5a..ae2ace4 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_ios.mm
@@ -52,9 +52,7 @@
 void OmniboxPopupViewIOS::UpdateEditViewIcon() {
   const AutocompleteResult& result = model_->result();
   const AutocompleteMatch& match = result.match_at(model_->selected_line());
-  int image_id = GetIconForAutocompleteMatchType(
-      match.type, /* is_starred */ false, /* is_incognito */ false);
-  delegate_->OnTopmostSuggestionImageChanged(image_id);
+  delegate_->OnTopmostSuggestionImageChanged(match.type);
 }
 
 void OmniboxPopupViewIOS::UpdatePopupAppearance() {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_suggestions_delegate.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_suggestions_delegate.h
index c1324dc..f96186e3d6 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_suggestions_delegate.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_suggestions_delegate.h
@@ -13,7 +13,8 @@
 class OmniboxPopupViewSuggestionsDelegate {
  public:
   // Called whenever the topmost suggestion image has changed.
-  virtual void OnTopmostSuggestionImageChanged(int imageId) = 0;
+  virtual void OnTopmostSuggestionImageChanged(
+      AutocompleteMatchType::Type type) = 0;
   // Called when results are updated.
   virtual void OnResultsChanged(const AutocompleteResult& result) = 0;
   // Called whenever the popup is scrolled.
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller_unittest.mm
index 8b710810..fafdc58c 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_collection_view_controller_unittest.mm
@@ -112,8 +112,8 @@
 // Tests ClearBrowsingDataCollectionViewControllerTest is set up with all
 // appropriate items and sections.
 TEST_F(ClearBrowsingDataCollectionViewControllerTest, TestModel) {
-  EXPECT_CALL(*mock_sync_service_, IsSyncActive())
-      .WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_sync_service_, GetDisableReasons())
+      .WillRepeatedly(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE));
   CreateController();
   CheckController();
 
@@ -146,8 +146,8 @@
 
 TEST_F(ClearBrowsingDataCollectionViewControllerTest,
        TestItemsSignedInSyncOff) {
-  EXPECT_CALL(*mock_sync_service_, IsSyncActive())
-      .WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_sync_service_, GetDisableReasons())
+      .WillRepeatedly(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE));
   signin_manager_->SetAuthenticatedAccountInfo("12345", "syncuser@example.com");
   CreateController();
   CheckController();
@@ -174,7 +174,10 @@
 
 TEST_F(ClearBrowsingDataCollectionViewControllerTest,
        TestItemsSignedInSyncActiveHistoryOff) {
-  EXPECT_CALL(*mock_sync_service_, IsSyncActive()).WillRepeatedly(Return(true));
+  EXPECT_CALL(*mock_sync_service_, GetDisableReasons())
+      .WillRepeatedly(Return(syncer::SyncService::DISABLE_REASON_NONE));
+  EXPECT_CALL(*mock_sync_service_, GetState())
+      .WillRepeatedly(Return(syncer::SyncService::State::ACTIVE));
   EXPECT_CALL(*mock_sync_service_, GetActiveDataTypes())
       .WillRepeatedly(Return(syncer::ModelTypeSet()));
   EXPECT_CALL(*mock_sync_service_, IsUsingSecondaryPassphrase())
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data_manager_unittest.mm b/ios/chrome/browser/ui/settings/clear_browsing_data_manager_unittest.mm
index 09196c093..57e2fc5 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data_manager_unittest.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data_manager_unittest.mm
@@ -103,8 +103,8 @@
 // but sync is off.
 TEST_F(ClearBrowsingDataManagerTest, TestModelSignedInSyncOff) {
   // Ensure that sync is not running.
-  EXPECT_CALL(*mock_sync_service_, IsSyncActive())
-      .WillRepeatedly(Return(false));
+  EXPECT_CALL(*mock_sync_service_, GetDisableReasons())
+      .WillRepeatedly(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE));
 
   signin_manager_->SetAuthenticatedAccountInfo("12345", "syncuser@example.com");
 
diff --git a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
index eb8d5c4..d32534bf 100644
--- a/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
+++ b/ios/chrome/browser/ui/tab_grid/grid/grid_cell.mm
@@ -202,6 +202,14 @@
 
 - (void)setIcon:(UIImage*)icon {
   self.iconView.image = icon;
+  // if |icon| is nil (that is, the cell should have no icon), set the icon
+  // background to be clear; otherwise set it to be the icon background.
+  if (icon) {
+    self.iconView.backgroundColor =
+        UIColorFromRGB(kGridCellIconBackgroundColor);
+  } else {
+    self.iconView.backgroundColor = UIColor.clearColor;
+  }
   _icon = icon;
 }
 
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
index b116cd5..0245cd1f 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -11,6 +11,7 @@
 #include "components/favicon/ios/web_favicon_driver.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/chrome_url_util.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache_factory.h"
@@ -36,7 +37,10 @@
 GridItem* CreateItem(web::WebState* web_state) {
   TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state);
   GridItem* item = [[GridItem alloc] initWithIdentifier:tab_helper->tab_id()];
-  item.title = base::SysUTF16ToNSString(web_state->GetTitle());
+  // chrome://newtab (NTP) tabs have no title.
+  if (!IsURLNtp(web_state->GetVisibleURL())) {
+    item.title = base::SysUTF16ToNSString(web_state->GetTitle());
+  }
   return item;
 }
 
@@ -319,6 +323,10 @@
   if (!webState) {
     return;
   }
+  // NTP tabs get no favicon.
+  if (IsURLNtp(webState->GetVisibleURL())) {
+    return;
+  }
   UIImage* defaultFavicon;
   if (experimental_flags::IsCollectionsUIRebootEnabled()) {
     defaultFavicon = [UIImage imageNamed:@"default_world_favicon"];
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
index b997181..9ec9f9b 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_view_controller.mm
@@ -734,7 +734,7 @@
   }
   // Fade the toolbars in for the last 60% of the transition.
   auto keyframe = ^{
-    [UIView addKeyframeWithRelativeStartTime:0.4
+    [UIView addKeyframeWithRelativeStartTime:0.2
                             relativeDuration:0.6
                                   animations:^{
                                     [self showToolbars];
@@ -772,7 +772,7 @@
   // Fade the toolbars out in the first 66% of the transition.
   auto keyframe = ^{
     [UIView addKeyframeWithRelativeStartTime:0
-                            relativeDuration:0.66
+                            relativeDuration:0.40
                                   animations:^{
                                     [self hideToolbars];
                                   }];
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn b/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn
index 008fdc13f..5a31d65 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/transitions/BUILD.gn
@@ -24,6 +24,7 @@
 
   deps = [
     "//base",
+    "//ios/chrome/browser/ui:ui_util",
     "//ios/chrome/browser/ui/util",
   ]
 }
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm
index 00b3d68..339c4c5d 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm
+++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_to_visible_tab_animator.mm
@@ -39,7 +39,7 @@
 
 - (NSTimeInterval)transitionDuration:
     (id<UIViewControllerContextTransitioning>)transitionContext {
-  return 0.3;
+  return 0.5;
 }
 
 - (void)animateTransition:
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
index 92f5a505..b6b655c 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
+++ b/ios/chrome/browser/ui/tab_grid/transitions/grid_transition_animation.mm
@@ -7,6 +7,7 @@
 #import "base/logging.h"
 #import "ios/chrome/browser/ui/tab_grid/transitions/grid_to_tab_transition_view.h"
 #import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h"
+#include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/util/property_animator_group.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -17,6 +18,10 @@
 
 // Scale factor for inactive items when a tab is expanded.
 const CGFloat kInactiveItemScale = 0.95;
+
+CGFloat DeviceCornerRadius() {
+  return IsIPhoneX() ? 40.0 : 0.0;
+}
 }
 
 @interface GridTransitionAnimation ()
@@ -95,16 +100,18 @@
   // fractions of the overall animation duration.
   CGFloat partialDuration = 0.6;
   CGFloat briefDuration = partialDuration * 0.5;
-  CGFloat delay = 0.4;
   CGFloat shortDelay = 0.2;
 
+  // Damping ratio for the resize animation.
+  CGFloat resizeDamping = 0.8;
+
   // If there's only one cell, the animation has two parts.
   //   (A) Zooming the active cell into position.
   //   (B) Crossfading from the tab to cell top view.
   //   (C) Rounding the corners of the active cell.
   //
-  //  {0%}------------------[A]----------{60%}
-  //                                {50%}---[B]---{80%}
+  //  {0%}----------------------[A]-------------------{100%}
+  //                            {50%}----[B]----{80%}
   //  {0%}---[C]---{30%}
 
   // If there's more than once cell, the animation adds two more parts:
@@ -112,16 +119,12 @@
   //   (E) Fading the inactive cells to 100% opacity.
   // The overall timing is as follows:
   //
-  //  {0%}------------------[A]----------{60%}
-  //                                {50%}---[B]---{80%}
+  //  {0%}----------------------[A]-------------------{100%}
+  //                            {50%}----[B]----{80%}
   //  {0%}---[C]---{30%}
   //           {20%}--[D]-----------------------------{100%}
-  //           {20%}--[E]-------------------------{80%}
+  //           {20%}--[E]-----------------------{80%}
   //
-  // All animations are timed ease-in (so more motion happens later), except
-  // for B and C. B is a crossfade timed ease in/out, and C is relatively small
-  // in space and short in duration; it has linear timing so it doesn't seem
-  // instantaneous.
   // (Changing the timing constants above will change the timing % values)
 
   UIView<GridToTabTransitionView>* activeCell = self.layout.activeItem.cell;
@@ -135,14 +138,11 @@
   auto zoomActiveCellAnimation = ^{
     [self positionAndScaleActiveItemInGrid];
   };
-  auto zoomActiveCellKeyframeAnimation =
-      [self keyframeAnimationWithRelativeStart:0
-                              relativeDuration:partialDuration
-                                    animations:zoomActiveCellAnimation];
-  UIViewPropertyAnimator* zoomActiveCell = [[UIViewPropertyAnimator alloc]
-      initWithDuration:self.duration
-                 curve:UIViewAnimationCurveEaseIn
-            animations:zoomActiveCellKeyframeAnimation];
+
+  UIViewPropertyAnimator* zoomActiveCell =
+      [[UIViewPropertyAnimator alloc] initWithDuration:self.duration
+                                          dampingRatio:resizeDamping
+                                            animations:zoomActiveCellAnimation];
   [self.animations addAnimator:zoomActiveCell];
 
   // B: Fade in the active cell top cell view, fade out the active cell's
@@ -161,7 +161,7 @@
 
   // C: Round the corners of the active cell.
   UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
-  cell.cornerRadius = 0.0;
+  cell.cornerRadius = DeviceCornerRadius();
   auto roundCornersAnimation = ^{
     cell.cornerRadius = self.finalActiveCellCornerRadius;
   };
@@ -193,7 +193,7 @@
                                     animations:scaleUpCellsAnimation];
   UIViewPropertyAnimator* scaleUpCells = [[UIViewPropertyAnimator alloc]
       initWithDuration:self.duration
-                 curve:UIViewAnimationCurveEaseIn
+                 curve:UIViewAnimationCurveEaseOut
             animations:scaleUpCellsKeyframeAnimation];
   [self.animations addAnimator:scaleUpCells];
 
@@ -204,12 +204,12 @@
     }
   };
   auto fadeInCellsKeyframeAnimation =
-      [self keyframeAnimationWithRelativeStart:delay
+      [self keyframeAnimationWithRelativeStart:shortDelay
                               relativeDuration:partialDuration
                                     animations:fadeInCellsAnimation];
   UIViewPropertyAnimator* fadeInCells = [[UIViewPropertyAnimator alloc]
       initWithDuration:self.duration
-                 curve:UIViewAnimationCurveEaseIn
+                 curve:UIViewAnimationCurveEaseOut
             animations:fadeInCellsKeyframeAnimation];
   [self.animations addAnimator:fadeInCells];
 }
@@ -223,6 +223,9 @@
   CGFloat veryBriefDuration = 0.2;
   CGFloat delay = (1.0 - veryBriefDuration) / 2.0;
 
+  // Damping ratio for the resize animation.
+  CGFloat resizeDamping = 0.7;
+
   // If there's only one cell, the animation has three parts:
   //   (A) Zooming the active cell out into the expanded position.
   //   (B) Crossfading the active cell's top views.
@@ -232,7 +235,7 @@
   //
   //  {0%}--[A]-----------------------------------{100%}
   //  {0%}--[B]---{30%}
-  //                               {70%}----[C]---{100%}
+  //  {0%}--[C]---{30%}
   //                   {40%}-[D]-{60%}
 
   // If there's more than once cell, the animation adds:
@@ -242,7 +245,7 @@
   //
   //  {0%}--[A]-----------------------------------{100%}
   //  {0%}--[B]---{30%}
-  //                               {70%}----[C]---{100%}
+  //  {0%}--[C]---{30%}
   //                   {40%}-[D]-{60%}
   //  {0%}--[E]-----------------------------------{100%}
   //  {0%}--[F]-------------------{66%}
@@ -264,12 +267,12 @@
   activeCell.topTabView.alpha = 0.0;
 
   // A: Zoom the active cell into position.
-  UIViewPropertyAnimator* zoomActiveCell = [[UIViewPropertyAnimator alloc]
-      initWithDuration:self.duration
-                 curve:UIViewAnimationCurveEaseOut
-            animations:^{
-              [self positionExpandedActiveItem];
-            }];
+  UIViewPropertyAnimator* zoomActiveCell =
+      [[UIViewPropertyAnimator alloc] initWithDuration:self.duration
+                                          dampingRatio:resizeDamping
+                                            animations:^{
+                                              [self positionExpandedActiveItem];
+                                            }];
   [self.animations addAnimator:zoomActiveCell];
 
   // B: Crossfade the top views.
@@ -287,10 +290,10 @@
   // C: Square the active cell's corners.
   UIView<GridToTabTransitionView>* cell = self.layout.activeItem.cell;
   auto squareCornersAnimation = ^{
-    cell.cornerRadius = 0.0;
+    cell.cornerRadius = DeviceCornerRadius();
   };
   auto squareCornersKeyframeAnimation =
-      [self keyframeAnimationWithRelativeStart:1.0 - briefDuration
+      [self keyframeAnimationWithRelativeStart:0.0
                               relativeDuration:briefDuration
                                     animations:squareCornersAnimation];
   UIViewPropertyAnimator* squareCorners = [[UIViewPropertyAnimator alloc]
@@ -380,7 +383,7 @@
 - (void)prepareInactiveItemsForAppearance {
   for (GridTransitionItem* item in self.layout.inactiveItems) {
     [self positionItemInGrid:item];
-    item.cell.alpha = 0.0;
+    item.cell.alpha = 0.2;
     item.cell.transform = CGAffineTransformScale(
         item.cell.transform, kInactiveItemScale, kInactiveItemScale);
   }
diff --git a/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm b/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm
index f27be999..f1e42f6 100644
--- a/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm
+++ b/ios/chrome/browser/ui/tab_grid/transitions/tab_to_grid_animator.mm
@@ -39,7 +39,7 @@
 
 - (NSTimeInterval)transitionDuration:
     (id<UIViewControllerContextTransitioning>)transitionContext {
-  return 0.5;
+  return 0.3;
 }
 
 - (void)animateTransition:
diff --git a/ios/chrome/content_widget_extension/content_widget_view.h b/ios/chrome/content_widget_extension/content_widget_view.h
index bea0a3fd..60ac908 100644
--- a/ios/chrome/content_widget_extension/content_widget_view.h
+++ b/ios/chrome/content_widget_extension/content_widget_view.h
@@ -48,6 +48,9 @@
 // positions starting at 0.
 - (void)updateSites:(NSDictionary<NSURL*, NTPTile*>*)sites;
 
+// Returns whether all the sites can be displayed on a single row.
+- (BOOL)sitesFitSingleRow;
+
 @end
 
 #endif  // IOS_CHROME_CONTENT_WIDGET_EXTENSION_CONTENT_WIDGET_VIEW_H_
diff --git a/ios/chrome/content_widget_extension/content_widget_view.mm b/ios/chrome/content_widget_extension/content_widget_view.mm
index 1788a2b..fa44a94f 100644
--- a/ios/chrome/content_widget_extension/content_widget_view.mm
+++ b/ios/chrome/content_widget_extension/content_widget_view.mm
@@ -252,6 +252,10 @@
   return self.shouldShowSecondRow ? kTileHeight + kTileSpacing : 0;
 }
 
+- (BOOL)sitesFitSingleRow {
+  return self.iconsPerRow >= self.siteCount;
+}
+
 #pragma mark - ContentWidgetView
 
 - (void)showMode:(BOOL)compact {
diff --git a/ios/chrome/content_widget_extension/content_widget_view_controller.mm b/ios/chrome/content_widget_extension/content_widget_view_controller.mm
index ffc2280..09b89ad 100644
--- a/ios/chrome/content_widget_extension/content_widget_view_controller.mm
+++ b/ios/chrome/content_widget_extension/content_widget_view_controller.mm
@@ -80,7 +80,7 @@
   [self.view addSubview:self.widgetView];
 
   self.extensionContext.widgetLargestAvailableDisplayMode =
-      NCWidgetDisplayModeExpanded;
+      NCWidgetDisplayModeCompact;
 
   self.widgetView.translatesAutoresizingMaskIntoConstraints = NO;
   AddSameConstraints(self.widgetView, self.view);
@@ -149,6 +149,9 @@
   }
   self.sites = newSites;
   [self.widgetView updateSites:self.sites];
+  self.extensionContext.widgetLargestAvailableDisplayMode =
+      [self.widgetView sitesFitSingleRow] ? NCWidgetDisplayModeCompact
+                                          : NCWidgetDisplayModeExpanded;
   return YES;
 }
 
diff --git a/ios/chrome/test/DEPS b/ios/chrome/test/DEPS
index 09b75044..06b6c777 100644
--- a/ios/chrome/test/DEPS
+++ b/ios/chrome/test/DEPS
@@ -11,6 +11,7 @@
   "+components/prefs",
   "+components/signin/core/browser",
   "+components/sync",
+  "+components/unified_consent",
   "+components/variations",
   "+components/version_info",
   "+google_apis",
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index d7c3542..6cb36fd 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -235,6 +235,7 @@
     "//base/test:test_support",
     "//components/signin/core/browser",
     "//components/strings",
+    "//components/unified_consent",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/authentication:authentication",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
index a8ff4c3..584b72f 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.h
@@ -71,10 +71,6 @@
 // GREYAssert is induced.
 + (void)waitForToolbarVisible:(BOOL)isVisible;
 
-// Confirms the sign in confirmation page, scrolls first to make the OK button
-// visible on short devices (e.g. iPhone 5s).
-+ (void)confirmSigninConfirmationDialog;
-
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_UI_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
index f7814a9..6d80ee7 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_ui.mm
@@ -6,7 +6,6 @@
 
 #import "base/test/ios/wait_util.h"
 #include "components/strings/grit/components_strings.h"
-#include "ios/chrome/browser/ui/authentication/signin_confirmation_view_controller.h"
 #import "ios/chrome/browser/ui/settings/accounts_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/privacy_collection_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_collection_view_controller.h"
@@ -24,7 +23,6 @@
 #error "This file requires ARC support."
 #endif
 
-using chrome_test_util::AccountConsistencyConfirmationOkButton;
 using chrome_test_util::ClearBrowsingDataCollectionView;
 using chrome_test_util::SettingsMenuButton;
 using chrome_test_util::ToolsMenuView;
@@ -168,23 +166,4 @@
              errorMessage);
 }
 
-+ (void)confirmSigninConfirmationDialog {
-  // Confirm sign in. "More" button is shown on short devices (e.g. iPhone 5s,
-  // iPhone SE), so needs to scroll first to dismiss the "More" button before
-  // taping on "OK".
-  // Cannot directly scroll on |kSignInConfirmationCollectionViewId| because it
-  // is a MDC collection view, not a UICollectionView, so itself is not
-  // scrollable.
-  // Wait until the sync confirmation is displayed.
-  [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
-  id<GREYMatcher> signinUICollectionViewMatcher = grey_allOf(
-      grey_ancestor(grey_accessibilityID(kSigninConfirmationCollectionViewId)),
-      grey_kindOfClass([UICollectionView class]), nil);
-  [[EarlGrey selectElementWithMatcher:signinUICollectionViewMatcher]
-      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
-
-  [[EarlGrey selectElementWithMatcher:AccountConsistencyConfirmationOkButton()]
-      performAction:grey_tap()];
-}
-
 @end
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index 69739cb..06b72cb2 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -11,6 +11,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/unified_consent/feature.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_ui_constants.h"
 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_switch_item.h"
@@ -292,8 +293,10 @@
 }
 
 id<GREYMatcher> AccountConsistencyConfirmationOkButton() {
-  return ButtonWithAccessibilityLabelId(
-      IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OK_BUTTON);
+  int labelID = base::FeatureList::IsEnabled(unified_consent::kUnifiedConsent)
+                    ? IDS_IOS_ACCOUNT_UNIFIED_CONSENT_OK_BUTTON
+                    : IDS_IOS_ACCOUNT_CONSISTENCY_CONFIRMATION_OK_BUTTON;
+  return ButtonWithAccessibilityLabelId(labelID);
 }
 
 id<GREYMatcher> AddAccountButton() {
diff --git a/ios/web_view/test/BUILD.gn b/ios/web_view/test/BUILD.gn
index 10551a5..2cb7211 100644
--- a/ios/web_view/test/BUILD.gn
+++ b/ios/web_view/test/BUILD.gn
@@ -9,6 +9,7 @@
 source_set("inttests") {
   testonly = true
   sources = [
+    "navigation_delegate_inttest.mm",
     "scroll_view_kvo_inttest.mm",
     "web_view_autofill_inttest.mm",
     "web_view_inttest.mm",
diff --git a/ios/web_view/test/navigation_delegate_inttest.mm b/ios/web_view/test/navigation_delegate_inttest.mm
new file mode 100644
index 0000000..46669f2
--- /dev/null
+++ b/ios/web_view/test/navigation_delegate_inttest.mm
@@ -0,0 +1,112 @@
+// 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 <ChromeWebView/ChromeWebView.h>
+#import <Foundation/Foundation.h>
+
+#import "ios/web_view/test/web_view_inttest_base.h"
+#import "ios/web_view/test/web_view_test_util.h"
+#import "net/base/mac/url_conversions.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest_mac.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+// Tests CWVNavigationDelegate.
+class NavigationDelegateTest : public ios_web_view::WebViewInttestBase {
+ public:
+  void SetUp() override {
+    ASSERT_TRUE(test_server_->Start());
+    url_with_content_ =
+        net::NSURLWithGURL(GetUrlForPageWithTitleAndBody("Title", "Body"));
+    url_with_error_ = net::NSURLWithGURL(test_server_->GetURL("/close-socket"));
+
+    mock_delegate_ = OCMStrictProtocolMock(@protocol(CWVNavigationDelegate));
+    [(id)mock_delegate_ setExpectationOrderMatters:YES];
+    web_view_.navigationDelegate = mock_delegate_;
+  }
+
+  id ArgWithURL(NSURL* url) {
+    return [OCMArg checkWithBlock:^(id object) {
+      return [[object URL] isEqual:url];
+    }];
+  }
+
+  NSURL* url_with_content_;
+  NSURL* url_with_error_;
+  id<CWVNavigationDelegate> mock_delegate_;
+};
+
+// Tests that expected delegate methods are called for a successful request.
+TEST_F(NavigationDelegateTest, RequestSucceeds) {
+  // A request made with -loadRequest: has type CWVNavigationTypeClientRedirect.
+  OCMExpect([mock_delegate_ webView:web_view_
+                shouldStartLoadWithRequest:ArgWithURL(url_with_content_)
+                            navigationType:CWVNavigationTypeClientRedirect])
+      .andReturn(YES);
+  OCMExpect([mock_delegate_ webViewDidStartProvisionalNavigation:web_view_]);
+  OCMExpect([mock_delegate_ webView:web_view_
+                shouldContinueLoadWithResponse:ArgWithURL(url_with_content_)
+                                  forMainFrame:YES])
+      .andReturn(YES);
+  OCMExpect([mock_delegate_ webViewDidCommitNavigation:web_view_]);
+  OCMExpect([mock_delegate_ webViewDidFinishNavigation:web_view_]);
+
+  ASSERT_TRUE(test::LoadUrl(web_view_, url_with_content_));
+  [(id)mock_delegate_ verify];
+}
+
+// Tests that expected delegate methods are called for a failed request.
+TEST_F(NavigationDelegateTest, RequestFails) {
+  OCMExpect([mock_delegate_ webView:web_view_
+                shouldStartLoadWithRequest:ArgWithURL(url_with_error_)
+                            navigationType:CWVNavigationTypeClientRedirect])
+      .andReturn(YES);
+  OCMExpect([mock_delegate_ webViewDidStartProvisionalNavigation:web_view_]);
+  OCMExpect([mock_delegate_ webViewDidCommitNavigation:web_view_]);
+  OCMExpect([mock_delegate_ webView:web_view_
+         didFailNavigationWithError:[OCMArg any]]);
+  // -webViewDidCommitNavigation: is called one more time for failures.
+  OCMExpect([mock_delegate_ webViewDidCommitNavigation:web_view_]);
+
+  ASSERT_TRUE(test::LoadUrl(web_view_, url_with_error_));
+  [(id)mock_delegate_ verify];
+}
+
+// Tests that a request is canceled and no further delegate methods are called
+// when -shouldStartLoadWithRequest:navigationType: returns NO.
+TEST_F(NavigationDelegateTest, CancelRequest) {
+  OCMExpect([mock_delegate_ webView:web_view_
+                shouldStartLoadWithRequest:ArgWithURL(url_with_content_)
+                            navigationType:CWVNavigationTypeClientRedirect])
+      .andReturn(NO);
+
+  ASSERT_TRUE(test::LoadUrl(web_view_, url_with_content_));
+  [(id)mock_delegate_ verify];
+}
+
+// Tests that a response is canceled and no further delegate methods are called
+// when -shouldContinueLoadWithResponse:forMainFrame: returns NO.
+TEST_F(NavigationDelegateTest, CancelResponse) {
+  OCMExpect([mock_delegate_ webView:web_view_
+                shouldStartLoadWithRequest:ArgWithURL(url_with_content_)
+                            navigationType:CWVNavigationTypeClientRedirect])
+      .andReturn(YES);
+  OCMExpect([mock_delegate_ webViewDidStartProvisionalNavigation:web_view_]);
+  OCMExpect([mock_delegate_ webView:web_view_
+                shouldContinueLoadWithResponse:ArgWithURL(url_with_content_)
+                                  forMainFrame:YES])
+      .andReturn(NO);
+
+  ASSERT_TRUE(test::LoadUrl(web_view_, url_with_content_));
+  [(id)mock_delegate_ verify];
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/test/web_view_inttest_base.mm b/ios/web_view/test/web_view_inttest_base.mm
index c2a5178e..2cc08dd 100644
--- a/ios/web_view/test/web_view_inttest_base.mm
+++ b/ios/web_view/test/web_view_inttest_base.mm
@@ -100,6 +100,7 @@
     : web_view_(test::CreateWebView()),
       test_server_(std::make_unique<net::EmbeddedTestServer>(
           net::test_server::EmbeddedTestServer::TYPE_HTTP)) {
+  test_server_->AddDefaultHandlers(FILE_PATH_LITERAL(base::FilePath()));
   test_server_->RegisterRequestHandler(
       base::BindRepeating(&TestRequestHandler));
 }
diff --git a/media/audio/cras/audio_manager_cras.cc b/media/audio/cras/audio_manager_cras.cc
index be5235270..e84164d 100644
--- a/media/audio/cras/audio_manager_cras.cc
+++ b/media/audio/cras/audio_manager_cras.cc
@@ -186,6 +186,10 @@
   if (HasKeyboardMic(devices))
     params.set_effects(AudioParameters::KEYBOARD_MIC);
 
+  if (GetSystemAecSupportedPerBoard())
+    params.set_effects(params.effects() |
+                       AudioParameters::EXPERIMENTAL_ECHO_CANCELLER);
+
   return params;
 }
 
@@ -310,6 +314,27 @@
   return static_cast<int>(buffer_size);
 }
 
+bool AudioManagerCras::GetSystemAecSupportedPerBoard() {
+  DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  bool system_aec_supported = false;
+  base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
+                            base::WaitableEvent::InitialState::NOT_SIGNALED);
+  if (main_task_runner_->BelongsToCurrentThread()) {
+    // Unittest may use the same thread for audio thread.
+    GetSystemAecSupportedOnMainThread(&system_aec_supported, &event);
+  } else {
+    // Using base::Unretained is safe here because we wait for callback be
+    // executed in main thread before local variables are destructed.
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&AudioManagerCras::GetSystemAecSupportedOnMainThread,
+                       weak_this_, base::Unretained(&system_aec_supported),
+                       base::Unretained(&event)));
+  }
+  WaitEventOrShutdown(&event);
+  return system_aec_supported;
+}
+
 AudioParameters AudioManagerCras::GetPreferredOutputStreamParameters(
     const std::string& output_device_id,
     const AudioParameters& input_params) {
@@ -466,6 +491,17 @@
   event->Signal();
 }
 
+void AudioManagerCras::GetSystemAecSupportedOnMainThread(
+    bool* system_aec_supported,
+    base::WaitableEvent* event) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+  if (chromeos::CrasAudioHandler::IsInitialized()) {
+    *system_aec_supported =
+        chromeos::CrasAudioHandler::Get()->system_aec_supported();
+  }
+  event->Signal();
+}
+
 void AudioManagerCras::WaitEventOrShutdown(base::WaitableEvent* event) {
   base::WaitableEvent* waitables[] = {event, &on_shutdown_};
   base::WaitableEvent::WaitMany(waitables, arraysize(waitables));
diff --git a/media/audio/cras/audio_manager_cras.h b/media/audio/cras/audio_manager_cras.h
index 2bce9b0..e994735e 100644
--- a/media/audio/cras/audio_manager_cras.h
+++ b/media/audio/cras/audio_manager_cras.h
@@ -79,6 +79,9 @@
   // Get default output buffer size for this board.
   int GetDefaultOutputBufferSizePerBoard();
 
+  // Get if system AEC is supported or not for this board.
+  bool GetSystemAecSupportedPerBoard();
+
   void GetAudioDeviceNamesImpl(bool is_input, AudioDeviceNames* device_names);
 
   std::string GetHardwareDeviceFromDeviceId(
@@ -97,6 +100,8 @@
                                               base::WaitableEvent* event);
   void GetDefaultOutputBufferSizeOnMainThread(int32_t* buffer_size,
                                               base::WaitableEvent* event);
+  void GetSystemAecSupportedOnMainThread(bool* system_aec_supported,
+                                         base::WaitableEvent* event);
 
   void WaitEventOrShutdown(base::WaitableEvent* event);
 
diff --git a/media/audio/cras/cras_input.cc b/media/audio/cras/cras_input.cc
index 435c910..1088581e 100644
--- a/media/audio/cras/cras_input.cc
+++ b/media/audio/cras/cras_input.cc
@@ -121,6 +121,10 @@
   audio_manager_->ReleaseInputStream(this);
 }
 
+inline bool CrasInputStream::UseCrasAec() const {
+  return params_.effects() & AudioParameters::ECHO_CANCELLER;
+}
+
 void CrasInputStream::Start(AudioInputCallback* callback) {
   DCHECK(client_);
   DCHECK(callback);
@@ -204,6 +208,9 @@
     return;
   }
 
+  if (UseCrasAec())
+    cras_client_stream_params_enable_aec(stream_params);
+
   // Before starting the stream, save the number of bytes in a frame for use in
   // the callback.
   bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
diff --git a/media/audio/cras/cras_input.h b/media/audio/cras/cras_input.h
index e7917e5..fe37f85 100644
--- a/media/audio/cras/cras_input.h
+++ b/media/audio/cras/cras_input.h
@@ -78,6 +78,9 @@
   // Convert from a volume ratio to dB.
   double GetDecibelsFromVolumeRatio(double volume_ratio) const;
 
+  // Return true to use AEC in CRAS for this input stream.
+  inline bool UseCrasAec() const;
+
   // Non-refcounted pointer back to the audio manager.
   // The AudioManager indirectly holds on to stream objects, so we don't
   // want circular references.  Additionally, stream objects live on the audio
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 42b3e0b9..f9e2ccbb 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -93,27 +93,8 @@
 component("capture_lib") {
   defines = [ "CAPTURE_IMPLEMENTATION" ]
   sources = [
-    "video/linux/camera_config_chromeos.cc",
-    "video/linux/camera_config_chromeos.h",
-    "video/linux/v4l2_capture_delegate.cc",
-    "video/linux/v4l2_capture_delegate.h",
-    "video/linux/v4l2_capture_device.h",
-    "video/linux/v4l2_capture_device_impl.cc",
-    "video/linux/v4l2_capture_device_impl.h",
-    "video/linux/video_capture_device_chromeos.cc",
-    "video/linux/video_capture_device_chromeos.h",
-    "video/linux/video_capture_device_factory_linux.cc",
-    "video/linux/video_capture_device_factory_linux.h",
-    "video/linux/video_capture_device_linux.cc",
-    "video/linux/video_capture_device_linux.h",
-    "video/mac/video_capture_device_avfoundation_mac.h",
-    "video/mac/video_capture_device_avfoundation_mac.mm",
-    "video/mac/video_capture_device_decklink_mac.h",
-    "video/mac/video_capture_device_decklink_mac.mm",
-    "video/mac/video_capture_device_factory_mac.h",
-    "video/mac/video_capture_device_factory_mac.mm",
-    "video/mac/video_capture_device_mac.h",
-    "video/mac/video_capture_device_mac.mm",
+    "video/create_video_capture_device_factory.cc",
+    "video/create_video_capture_device_factory.h",
     "video/scoped_buffer_pool_reservation.h",
     "video/shared_memory_buffer_tracker.cc",
     "video/shared_memory_buffer_tracker.h",
@@ -137,27 +118,6 @@
     "video/video_frame_receiver.h",
     "video/video_frame_receiver_on_task_runner.cc",
     "video/video_frame_receiver_on_task_runner.h",
-    "video/win/capability_list_win.cc",
-    "video/win/capability_list_win.h",
-    "video/win/filter_base_win.cc",
-    "video/win/filter_base_win.h",
-    "video/win/metrics.cc",
-    "video/win/metrics.h",
-    "video/win/pin_base_win.cc",
-    "video/win/pin_base_win.h",
-    "video/win/sink_filter_observer_win.h",
-    "video/win/sink_filter_win.cc",
-    "video/win/sink_filter_win.h",
-    "video/win/sink_input_pin_win.cc",
-    "video/win/sink_input_pin_win.h",
-    "video/win/video_capture_device_factory_win.cc",
-    "video/win/video_capture_device_factory_win.h",
-    "video/win/video_capture_device_mf_win.cc",
-    "video/win/video_capture_device_mf_win.h",
-    "video/win/video_capture_device_utils_win.cc",
-    "video/win/video_capture_device_utils_win.h",
-    "video/win/video_capture_device_win.cc",
-    "video/win/video_capture_device_win.h",
     "video_capturer_source.cc",
     "video_capturer_source.h",
   ]
@@ -194,6 +154,16 @@
   }
 
   if (is_mac) {
+    sources += [
+      "video/mac/video_capture_device_avfoundation_mac.h",
+      "video/mac/video_capture_device_avfoundation_mac.mm",
+      "video/mac/video_capture_device_decklink_mac.h",
+      "video/mac/video_capture_device_decklink_mac.mm",
+      "video/mac/video_capture_device_factory_mac.h",
+      "video/mac/video_capture_device_factory_mac.mm",
+      "video/mac/video_capture_device_mac.h",
+      "video/mac/video_capture_device_mac.mm",
+    ]
     deps += [ "//third_party/decklink" ]
     libs = [
       "AVFoundation.framework",
@@ -206,6 +176,29 @@
   }
 
   if (is_win) {
+    sources += [
+      "video/win/capability_list_win.cc",
+      "video/win/capability_list_win.h",
+      "video/win/filter_base_win.cc",
+      "video/win/filter_base_win.h",
+      "video/win/metrics.cc",
+      "video/win/metrics.h",
+      "video/win/pin_base_win.cc",
+      "video/win/pin_base_win.h",
+      "video/win/sink_filter_observer_win.h",
+      "video/win/sink_filter_win.cc",
+      "video/win/sink_filter_win.h",
+      "video/win/sink_input_pin_win.cc",
+      "video/win/sink_input_pin_win.h",
+      "video/win/video_capture_device_factory_win.cc",
+      "video/win/video_capture_device_factory_win.h",
+      "video/win/video_capture_device_mf_win.cc",
+      "video/win/video_capture_device_mf_win.h",
+      "video/win/video_capture_device_utils_win.cc",
+      "video/win/video_capture_device_utils_win.h",
+      "video/win/video_capture_device_win.cc",
+      "video/win/video_capture_device_win.h",
+    ]
     deps += [ "//media/base/win" ]
     libs = [
       "mf.lib",
@@ -223,6 +216,25 @@
     configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
   }
 
+  # This includes the case of ChromeOS
+  if (is_linux) {
+    sources += [
+      "video/linux/camera_config_chromeos.cc",
+      "video/linux/camera_config_chromeos.h",
+      "video/linux/v4l2_capture_delegate.cc",
+      "video/linux/v4l2_capture_delegate.h",
+      "video/linux/v4l2_capture_device.h",
+      "video/linux/v4l2_capture_device_impl.cc",
+      "video/linux/v4l2_capture_device_impl.h",
+      "video/linux/video_capture_device_chromeos.cc",
+      "video/linux/video_capture_device_chromeos.h",
+      "video/linux/video_capture_device_factory_linux.cc",
+      "video/linux/video_capture_device_factory_linux.h",
+      "video/linux/video_capture_device_linux.cc",
+      "video/linux/video_capture_device_linux.h",
+    ]
+  }
+
   if (is_chromeos) {
     sources += [
       "video/chromeos/camera_3a_controller.cc",
diff --git a/media/capture/video/android/video_capture_device_factory_android.cc b/media/capture/video/android/video_capture_device_factory_android.cc
index d1279b3..ae4e875 100644
--- a/media/capture/video/android/video_capture_device_factory_android.cc
+++ b/media/capture/video/android/video_capture_device_factory_android.cc
@@ -154,11 +154,4 @@
       AttachCurrentThread(), id));
 }
 
-// static
-VideoCaptureDeviceFactory*
-VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-  return new VideoCaptureDeviceFactoryAndroid();
-}
-
 }  // namespace media
diff --git a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
index adf2adf..6fb9ccac 100644
--- a/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
+++ b/media/capture/video/chromeos/video_capture_device_factory_chromeos.cc
@@ -6,8 +6,6 @@
 
 #include "base/memory/ptr_util.h"
 #include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
-#include "media/capture/video/chromeos/public/cros_features.h"
-#include "media/capture/video/linux/video_capture_device_factory_linux.h"
 
 namespace media {
 
@@ -85,28 +83,4 @@
   return true;
 }
 
-#if defined(OS_CHROMEOS)
-// static
-VideoCaptureDeviceFactory*
-VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
-    scoped_refptr<base::SingleThreadTaskRunner>
-        task_runner_for_screen_observer) {
-  // On Chrome OS we have to support two use cases:
-  //
-  // 1. For devices that have the camera HAL v3 service running on Chrome OS,
-  //    we use the HAL v3 capture device which VideoCaptureDeviceFactoryChromeOS
-  //    provides.
-  // 2. Existing devices that use UVC cameras need to use the V4L2 capture
-  //    device which VideoCaptureDeviceFacotoryLinux provides; there are also
-  //    some special devices that may never be able to implement a camera HAL
-  //    v3.
-  if (ShouldUseCrosCameraService()) {
-    return new VideoCaptureDeviceFactoryChromeOS(
-        task_runner_for_screen_observer);
-  } else {
-    return new VideoCaptureDeviceFactoryLinux(task_runner_for_screen_observer);
-  }
-}
-#endif
-
 }  // namespace media
diff --git a/media/capture/video/create_video_capture_device_factory.cc b/media/capture/video/create_video_capture_device_factory.cc
new file mode 100644
index 0000000..9ca6f8c3
--- /dev/null
+++ b/media/capture/video/create_video_capture_device_factory.cc
@@ -0,0 +1,91 @@
+// 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/create_video_capture_device_factory.h"
+
+#include "base/command_line.h"
+#include "build/build_config.h"
+#include "media/base/media_switches.h"
+#include "media/capture/video/fake_video_capture_device_factory.h"
+#include "media/capture/video/file_video_capture_device_factory.h"
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+#include "media/capture/video/linux/video_capture_device_factory_linux.h"
+#elif defined(OS_CHROMEOS)
+#include "media/capture/video/chromeos/public/cros_features.h"
+#include "media/capture/video/chromeos/video_capture_device_factory_chromeos.h"
+#include "media/capture/video/linux/video_capture_device_factory_linux.h"
+#elif defined(OS_WIN)
+#include "media/capture/video/win/video_capture_device_factory_win.h"
+#elif defined(OS_MACOSX)
+#include "media/capture/video/mac/video_capture_device_factory_mac.h"
+#elif defined(OS_ANDROID)
+#include "media/capture/video/android/video_capture_device_factory_android.h"
+#endif
+
+namespace media {
+
+namespace {
+
+std::unique_ptr<VideoCaptureDeviceFactory>
+CreatePlatformSpecificVideoCaptureDeviceFactory(
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  return std::make_unique<VideoCaptureDeviceFactoryLinux>(ui_task_runner);
+#elif defined(OS_CHROMEOS)
+  // On Chrome OS we have to support two use cases:
+  //
+  // 1. For devices that have the camera HAL v3 service running on Chrome OS,
+  //    we use the HAL v3 capture device which VideoCaptureDeviceFactoryChromeOS
+  //    provides.
+  // 2. Existing devices that use UVC cameras need to use the V4L2 capture
+  //    device which VideoCaptureDeviceFacotoryLinux provides; there are also
+  //    some special devices that may never be able to implement a camera HAL
+  //    v3.
+  if (ShouldUseCrosCameraService()) {
+    return std::make_unique<VideoCaptureDeviceFactoryChromeOS>(ui_task_runner);
+  } else {
+    return std::make_unique<VideoCaptureDeviceFactoryLinux>(ui_task_runner);
+  }
+#elif defined(OS_WIN)
+  return std::make_unique<VideoCaptureDeviceFactoryWin>();
+#elif defined(OS_MACOSX)
+  return std::make_unique<VideoCaptureDeviceFactoryMac>();
+#elif defined(OS_ANDROID)
+  return std::make_unique<VideoCaptureDeviceFactoryAndroid>();
+#else
+  NOTIMPLEMENTED();
+  return nullptr;
+#endif
+}
+
+}  // anonymous namespace
+
+std::unique_ptr<VideoCaptureDeviceFactory> CreateVideoCaptureDeviceFactory(
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  // Use a Fake or File Video Device Factory if the command line flags are
+  // present, otherwise use the normal, platform-dependent, device factory.
+  if (command_line->HasSwitch(switches::kUseFakeDeviceForMediaStream)) {
+    if (command_line->HasSwitch(switches::kUseFileForFakeVideoCapture)) {
+      return std::make_unique<FileVideoCaptureDeviceFactory>();
+    } else {
+      std::vector<FakeVideoCaptureDeviceSettings> config;
+      FakeVideoCaptureDeviceFactory::ParseFakeDevicesConfigFromOptionsString(
+          command_line->GetSwitchValueASCII(
+              switches::kUseFakeDeviceForMediaStream),
+          &config);
+      auto result = std::make_unique<FakeVideoCaptureDeviceFactory>();
+      result->SetToCustomDevicesConfig(config);
+      return std::move(result);
+    }
+  } else {
+    // |ui_task_runner| is needed for the Linux ChromeOS factory to retrieve
+    // screen rotations.
+    return CreatePlatformSpecificVideoCaptureDeviceFactory(ui_task_runner);
+  }
+}
+
+}  // namespace media
diff --git a/media/capture/video/create_video_capture_device_factory.h b/media/capture/video/create_video_capture_device_factory.h
new file mode 100644
index 0000000..7199bf8
--- /dev/null
+++ b/media/capture/video/create_video_capture_device_factory.h
@@ -0,0 +1,22 @@
+// 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_CAPTURE_VIDEO_CREATE_VIDEO_CAPTURE_DEVICE_FACTORY_H_
+#define MEDIA_CAPTURE_VIDEO_CREATE_VIDEO_CAPTURE_DEVICE_FACTORY_H_
+
+#include <memory>
+
+#include "base/single_thread_task_runner.h"
+#include "media/capture/capture_export.h"
+#include "media/capture/video/video_capture_device_factory.h"
+
+namespace media {
+
+std::unique_ptr<VideoCaptureDeviceFactory> CAPTURE_EXPORT
+CreateVideoCaptureDeviceFactory(
+    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_CREATE_VIDEO_CAPTURE_DEVICE_FACTORY_H_
diff --git a/media/capture/video/linux/video_capture_device_factory_linux.cc b/media/capture/video/linux/video_capture_device_factory_linux.cc
index 29f1562..99c9912 100644
--- a/media/capture/video/linux/video_capture_device_factory_linux.cc
+++ b/media/capture/video/linux/video_capture_device_factory_linux.cc
@@ -344,13 +344,4 @@
   }
 }
 
-#if !defined(OS_CHROMEOS)
-// static
-VideoCaptureDeviceFactory*
-VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-  return new VideoCaptureDeviceFactoryLinux(ui_task_runner);
-}
-#endif
-
 }  // namespace media
diff --git a/media/capture/video/mac/video_capture_device_factory_mac.mm b/media/capture/video/mac/video_capture_device_factory_mac.mm
index 27deb1e..48d3552 100644
--- a/media/capture/video/mac/video_capture_device_factory_mac.mm
+++ b/media/capture/video/mac/video_capture_device_factory_mac.mm
@@ -120,11 +120,4 @@
   }
 }
 
-// static
-VideoCaptureDeviceFactory*
-VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-  return new VideoCaptureDeviceFactoryMac();
-}
-
 }  // namespace media
diff --git a/media/capture/video/video_capture_device_factory.cc b/media/capture/video/video_capture_device_factory.cc
index fa247bc..4d903b6a 100644
--- a/media/capture/video/video_capture_device_factory.cc
+++ b/media/capture/video/video_capture_device_factory.cc
@@ -14,53 +14,12 @@
 
 namespace media {
 
-// static
-std::unique_ptr<VideoCaptureDeviceFactory>
-VideoCaptureDeviceFactory::CreateFactory(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-  const base::CommandLine* command_line =
-      base::CommandLine::ForCurrentProcess();
-  // Use a Fake or File Video Device Factory if the command line flags are
-  // present, otherwise use the normal, platform-dependent, device factory.
-  if (command_line->HasSwitch(switches::kUseFakeDeviceForMediaStream)) {
-    if (command_line->HasSwitch(switches::kUseFileForFakeVideoCapture)) {
-      return std::unique_ptr<VideoCaptureDeviceFactory>(
-          new FileVideoCaptureDeviceFactory());
-    } else {
-      std::vector<FakeVideoCaptureDeviceSettings> config;
-      FakeVideoCaptureDeviceFactory::ParseFakeDevicesConfigFromOptionsString(
-          command_line->GetSwitchValueASCII(
-              switches::kUseFakeDeviceForMediaStream),
-          &config);
-      auto result = std::make_unique<FakeVideoCaptureDeviceFactory>();
-      result->SetToCustomDevicesConfig(config);
-      return std::move(result);
-    }
-  } else {
-    // |ui_task_runner| is needed for the Linux ChromeOS factory to retrieve
-    // screen rotations.
-    return std::unique_ptr<VideoCaptureDeviceFactory>(
-        CreateVideoCaptureDeviceFactory(ui_task_runner));
-  }
-}
-
 VideoCaptureDeviceFactory::VideoCaptureDeviceFactory() {
   thread_checker_.DetachFromThread();
 }
 
 VideoCaptureDeviceFactory::~VideoCaptureDeviceFactory() = default;
 
-#if !defined(OS_MACOSX) && !defined(OS_LINUX) && !defined(OS_ANDROID) && \
-    !defined(OS_WIN)
-// static
-VideoCaptureDeviceFactory*
-VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-  NOTIMPLEMENTED();
-  return NULL;
-}
-#endif
-
 void VideoCaptureDeviceFactory::GetCameraLocationsAsync(
     std::unique_ptr<VideoCaptureDeviceDescriptors> device_descriptors,
     DeviceDescriptorsCallback result_callback) {
diff --git a/media/capture/video/video_capture_device_factory.h b/media/capture/video/video_capture_device_factory.h
index a160a497..9ec27426 100644
--- a/media/capture/video/video_capture_device_factory.h
+++ b/media/capture/video/video_capture_device_factory.h
@@ -34,9 +34,6 @@
 // crbug.com/665065
 class CAPTURE_EXPORT VideoCaptureDeviceFactory {
  public:
-  static std::unique_ptr<VideoCaptureDeviceFactory> CreateFactory(
-      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
-
   VideoCaptureDeviceFactory();
   virtual ~VideoCaptureDeviceFactory();
 
@@ -73,9 +70,6 @@
   base::ThreadChecker thread_checker_;
 
  private:
-  static VideoCaptureDeviceFactory* CreateVideoCaptureDeviceFactory(
-      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
-
   DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceFactory);
 };
 
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc
index 5bf87dc..768ca5d 100644
--- a/media/capture/video/video_capture_device_unittest.cc
+++ b/media/capture/video/video_capture_device_unittest.cc
@@ -21,7 +21,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "media/base/bind_to_current_loop.h"
-#include "media/capture/video/video_capture_device_factory.h"
+#include "media/capture/video/create_video_capture_device_factory.h"
 #include "media/capture/video_capture_types.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -300,8 +300,8 @@
         base::DoNothing::Repeatedly<
             media::mojom::JpegEncodeAcceleratorRequest>());
 #endif
-    video_capture_device_factory_ = VideoCaptureDeviceFactory::CreateFactory(
-        base::ThreadTaskRunnerHandle::Get());
+    video_capture_device_factory_ =
+        CreateVideoCaptureDeviceFactory(base::ThreadTaskRunnerHandle::Get());
   }
 
   void SetUp() override {
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index 42934e5b..275f7560 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -753,11 +753,4 @@
   GetApiSpecificSupportedFormats(device, formats);
 }
 
-// static
-VideoCaptureDeviceFactory*
-VideoCaptureDeviceFactory::CreateVideoCaptureDeviceFactory(
-    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
-  return new VideoCaptureDeviceFactoryWin();
-}
-
 }  // namespace media
diff --git a/net/base/cache_type.h b/net/base/cache_type.h
index 47bbf0b6..2399594 100644
--- a/net/base/cache_type.h
+++ b/net/base/cache_type.h
@@ -9,12 +9,14 @@
 
 // The types of caches that can be created.
 enum CacheType {
-  DISK_CACHE,  // Disk is used as the backing storage.
-  MEMORY_CACHE,  // Data is stored only in memory.
-  MEDIA_CACHE,  // Optimized to handle media files.
-  APP_CACHE,  // Backing store for an AppCache.
-  SHADER_CACHE, // Backing store for the GL shader cache.
-  PNACL_CACHE, // Backing store the PNaCl translation cache
+  DISK_CACHE,            // Disk is used as the backing storage.
+  MEMORY_CACHE,          // Data is stored only in memory.
+  MEDIA_CACHE,           // Optimized to handle media files.
+  APP_CACHE,             // Backing store for an AppCache.
+  SHADER_CACHE,          // Backing store for the GL shader cache.
+  PNACL_CACHE,           // Backing store the PNaCl translation cache
+  GENERATED_CODE_CACHE,  // Backing store for data (like code for JavaScript)
+                         // generated by renderer.
 };
 
 // The types of disk cache backend, only used at backend instantiation.
diff --git a/net/disk_cache/blockfile/histogram_macros.h b/net/disk_cache/blockfile/histogram_macros.h
index e222e83..6bef6d0 100644
--- a/net/disk_cache/blockfile/histogram_macros.h
+++ b/net/disk_cache/blockfile/histogram_macros.h
@@ -102,6 +102,8 @@
       case net::PNACL_CACHE:                                         \
         CACHE_HISTOGRAM_##type(my_name.data(), sample);              \
         break;                                                       \
+      case net::GENERATED_CODE_CACHE:                                \
+        break;                                                       \
     }                                                                \
   }
 
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc
index 028f2edf..9df80b7 100644
--- a/net/disk_cache/simple/simple_backend_impl.cc
+++ b/net/disk_cache/simple/simple_backend_impl.cc
@@ -240,7 +240,8 @@
           {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
       orig_max_size_(max_bytes),
-      entry_operations_mode_(cache_type == net::DISK_CACHE
+      entry_operations_mode_((cache_type == net::DISK_CACHE ||
+                              cache_type == net::GENERATED_CODE_CACHE)
                                  ? SimpleEntryImpl::OPTIMISTIC_OPERATIONS
                                  : SimpleEntryImpl::NON_OPTIMISTIC_OPERATIONS),
       net_log_(net_log) {
diff --git a/net/disk_cache/simple/simple_histogram_macros.h b/net/disk_cache/simple/simple_histogram_macros.h
index fa302c3..d530d52 100644
--- a/net/disk_cache/simple/simple_histogram_macros.h
+++ b/net/disk_cache/simple/simple_histogram_macros.h
@@ -18,27 +18,30 @@
 // TODO(pasko): add histograms for shader cache as soon as it becomes possible
 // for a user to get shader cache with the |SimpleBackendImpl| without altering
 // any flags.
-#define SIMPLE_CACHE_UMA(uma_type, uma_name, cache_type, ...)          \
-  do {                                                                 \
-    switch (cache_type) {                                              \
-      case net::DISK_CACHE:                                            \
-        SIMPLE_CACHE_THUNK(                                            \
-            uma_type, ("SimpleCache.Http." uma_name, ##__VA_ARGS__));  \
-        break;                                                         \
-      case net::APP_CACHE:                                             \
-        SIMPLE_CACHE_THUNK(                                            \
-            uma_type, ("SimpleCache.App." uma_name, ##__VA_ARGS__));   \
-        break;                                                         \
-      case net::MEDIA_CACHE:                                           \
-        SIMPLE_CACHE_THUNK(                                            \
-            uma_type, ("SimpleCache.Media." uma_name, ##__VA_ARGS__)); \
-        break;                                                         \
-      case net::SHADER_CACHE:                                          \
-        break;                                                         \
-      default:                                                         \
-        NOTREACHED();                                                  \
-        break;                                                         \
-    }                                                                  \
+// TODO(mythria): add histograms for generated_code_cache when we actually start
+// using the generated_code_cache.
+#define SIMPLE_CACHE_UMA(uma_type, uma_name, cache_type, ...)               \
+  do {                                                                      \
+    switch (cache_type) {                                                   \
+      case net::DISK_CACHE:                                                 \
+        SIMPLE_CACHE_THUNK(uma_type,                                        \
+                           ("SimpleCache.Http." uma_name, ##__VA_ARGS__));  \
+        break;                                                              \
+      case net::APP_CACHE:                                                  \
+        SIMPLE_CACHE_THUNK(uma_type,                                        \
+                           ("SimpleCache.App." uma_name, ##__VA_ARGS__));   \
+        break;                                                              \
+      case net::MEDIA_CACHE:                                                \
+        SIMPLE_CACHE_THUNK(uma_type,                                        \
+                           ("SimpleCache.Media." uma_name, ##__VA_ARGS__)); \
+        break;                                                              \
+      case net::GENERATED_CODE_CACHE:                                       \
+      case net::SHADER_CACHE:                                               \
+        break;                                                              \
+      default:                                                              \
+        NOTREACHED();                                                       \
+        break;                                                              \
+    }                                                                       \
   } while (0)
 
 #endif  // NET_DISK_CACHE_SIMPLE_SIMPLE_HISTOGRAM_MACROS_H_
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index cdd54e6..48d83083 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -1297,7 +1297,6 @@
   // Clears and cancels all non-completed attempts. If |leave_attempt| is not
   // null, it is not cleared even if complete.
   void ClearAttempts(const DnsAttempt* leave_attempt) {
-    std::unique_ptr<DnsAttempt> completed_result;
     for (auto it = attempts_.begin(); it != attempts_.end();) {
       if (!(*it)->is_completed() && it->get() != leave_attempt) {
         it = attempts_.erase(it);
diff --git a/net/websockets/websocket_stream.cc b/net/websockets/websocket_stream.cc
index 62625f9b..4334032 100644
--- a/net/websockets/websocket_stream.cc
+++ b/net/websockets/websocket_stream.cc
@@ -120,7 +120,6 @@
                                             kTrafficAnnotation)),
         connect_delegate_(std::move(connect_delegate)),
         handshake_stream_(nullptr),
-        on_handshake_stream_created_has_been_called_(false),
         perform_upgrade_has_been_called_(false),
         api_delegate_(std::move(api_delegate)) {
     create_helper->set_stream_request(this);
@@ -187,15 +186,20 @@
   void PerformUpgrade() {
     DCHECK(timer_);
     CHECK(!perform_upgrade_has_been_called_);
-    CHECK(on_handshake_stream_created_has_been_called_);
     // TODO(bnc): Change to DCHECK after https://crbug.com/850183 is fixed.
-    CHECK(handshake_stream_);
     CHECK(connect_delegate_);
 
     perform_upgrade_has_been_called_ = true;
 
     timer_->Stop();
 
+    if (!handshake_stream_) {
+      // TODO(https://crbug.com/850183):
+      // Find out why this can happen and make it stop.
+      ReportFailureWithMessage("No handshake stream has been created.");
+      return;
+    }
+
     std::unique_ptr<URLRequest> url_request = std::move(url_request_);
     WebSocketHandshakeStreamBase* handshake_stream = handshake_stream_;
     handshake_stream_ = nullptr;
@@ -267,8 +271,6 @@
     // TODO(bnc): Change to DCHECK after https://crbug.com/850183 is fixed.
     CHECK(handshake_stream);
 
-    on_handshake_stream_created_has_been_called_ = true;
-
     handshake_stream_ = handshake_stream;
   }
 
@@ -291,7 +293,6 @@
   WebSocketHandshakeStreamBase* handshake_stream_;
 
   // TODO(bnc): Remove after https://crbug.com/850183 is fixed.
-  bool on_handshake_stream_created_has_been_called_;
   bool perform_upgrade_has_been_called_;
 
   // The failure message supplied by WebSocketBasicHandshakeStream, if any.
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index 98949f1..5a66d8c 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -21,9 +21,10 @@
       request.fetch_request_mode == mojom::FetchRequestMode::kNoCORS) {
     return false;
   }
+  // CORS needs a proper origin (including a unique opaque origin). If the
+  // request doesn't have one, CORS should not work.
+  DCHECK(request.request_initiator);
   url::Origin url_origin = url::Origin::Create(request.url);
-  if (!request.request_initiator.has_value())
-    return true;
   url::Origin security_origin(request.request_initiator.value());
   return !security_origin.IsSameOriginWith(url_origin);
 }
@@ -284,7 +285,7 @@
   // |request|’s current url’s origin and |request|’s origin is not same origin
   // with |request|’s current url’s origin, then set |request|’s tainted origin
   // flag.
-  if (!request_.request_initiator ||
+  if (request_.request_initiator &&
       (!url::Origin::Create(redirect_info.new_url)
             .IsSameOriginWith(url::Origin::Create(request_.url)) &&
        !request_.request_initiator->IsSameOriginWith(
@@ -353,17 +354,16 @@
   // |httpRequest|’s header list.
   if (fetch_cors_flag_ ||
       (request_.method != "GET" && request_.method != "HEAD")) {
-    url::Origin empty_origin;
-    const url::Origin& origin = tainted_ || !request_.request_initiator
-                                    ? empty_origin
-                                    : *request_.request_initiator;
-    request_.headers.SetHeader(net::HttpRequestHeaders::kOrigin,
-                               origin.Serialize());
+    if (request_.request_initiator) {
+      request_.headers.SetHeader(
+          net::HttpRequestHeaders::kOrigin,
+          (tainted_ ? url::Origin() : *request_.request_initiator).Serialize());
+    }
   }
 
   if (request_.fetch_request_mode == mojom::FetchRequestMode::kSameOrigin) {
-    if (!request_.request_initiator ||
-        !request_.request_initiator->IsSameOriginWith(
+    DCHECK(request_.request_initiator);
+    if (!request_.request_initiator->IsSameOriginWith(
             url::Origin::Create(request_.url))) {
       HandleComplete(URLLoaderCompletionStatus(
           CORSErrorStatus(mojom::CORSError::kDisallowedByMode)));
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index e0b71b9..b9f7561 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -4,9 +4,11 @@
 
 #include "services/network/cors/cors_url_loader_factory.h"
 
+#include "base/logging.h"
 #include "net/base/load_flags.h"
 #include "services/network/cors/cors_url_loader.h"
 #include "services/network/network_context.h"
+#include "services/network/public/cpp/cors/cors.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
@@ -66,6 +68,11 @@
     const ResourceRequest& resource_request,
     mojom::URLLoaderClientPtr client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
+  if (!IsSane(resource_request)) {
+    client->OnComplete(URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT));
+    return;
+  }
+
   if (base::FeatureList::IsEnabled(features::kOutOfBlinkCORS) &&
       !disable_web_security_) {
     auto loader = std::make_unique<CORSURLLoader>(
@@ -96,6 +103,33 @@
     context_->DestroyURLLoaderFactory(this);
 }
 
+bool CORSURLLoaderFactory::IsSane(const ResourceRequest& request) {
+  // CORS needs a proper origin (including a unique opaque origin). If the
+  // request doesn't have one, CORS cannot work.
+  if (!request.request_initiator &&
+      request.fetch_request_mode != mojom::FetchRequestMode::kNavigate &&
+      request.fetch_request_mode != mojom::FetchRequestMode::kNoCORS) {
+    LOG(WARNING) << "|fetch_request_mode| is " << request.fetch_request_mode
+                 << ", but |request_initiator| is not set.";
+    return false;
+  }
+
+  const auto load_flags_pattern = net::LOAD_DO_NOT_SAVE_COOKIES |
+                                  net::LOAD_DO_NOT_SEND_COOKIES |
+                                  net::LOAD_DO_NOT_SEND_AUTH_DATA;
+  // The credentials mode and load_flags should match.
+  if (request.fetch_credentials_mode == mojom::FetchCredentialsMode::kOmit &&
+      (request.load_flags & load_flags_pattern) != load_flags_pattern) {
+    LOG(WARNING) << "|fetch_credentials_mode| and |load_flags| contradict each "
+                    "other.";
+    return false;
+  }
+
+  // TODO(yhirano): If the request mode is "no-cors", the redirect mode should
+  // be "follow".
+  return true;
+}
+
 }  // namespace cors
 
 }  // namespace network
diff --git a/services/network/cors/cors_url_loader_factory.h b/services/network/cors/cors_url_loader_factory.h
index cd25335..6697570 100644
--- a/services/network/cors/cors_url_loader_factory.h
+++ b/services/network/cors/cors_url_loader_factory.h
@@ -62,6 +62,8 @@
 
   void DeleteIfNeeded();
 
+  static bool IsSane(const ResourceRequest& request);
+
   mojo::BindingSet<mojom::URLLoaderFactory> bindings_;
 
   // Used when constructed by NetworkContext.
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc
index 2c146041..16ef0cab 100644
--- a/services/network/cors/cors_url_loader_unittest.cc
+++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -251,6 +251,135 @@
   DISALLOW_COPY_AND_ASSIGN(CORSURLLoaderTest);
 };
 
+TEST_F(CORSURLLoaderTest, SameOriginWithoutInitiator) {
+  ResourceRequest request;
+  request.fetch_request_mode = mojom::FetchRequestMode::kSameOrigin;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kInclude;
+  request.url = GURL("http://example.com/");
+  request.request_initiator = base::nullopt;
+
+  CreateLoaderAndStart(request);
+  RunUntilComplete();
+
+  EXPECT_FALSE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client().completion_status().error_code);
+}
+
+TEST_F(CORSURLLoaderTest, NoCORSWithoutInitiator) {
+  ResourceRequest request;
+  request.fetch_request_mode = mojom::FetchRequestMode::kNoCORS;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kInclude;
+  request.url = GURL("http://example.com/");
+  request.request_initiator = base::nullopt;
+
+  CreateLoaderAndStart(request);
+  NotifyLoaderClientOnReceiveResponse();
+  NotifyLoaderClientOnComplete(net::OK);
+  RunUntilComplete();
+
+  EXPECT_TRUE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_TRUE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::OK, client().completion_status().error_code);
+}
+
+TEST_F(CORSURLLoaderTest, CORSWithoutInitiator) {
+  ResourceRequest request;
+  request.fetch_request_mode = mojom::FetchRequestMode::kCORS;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kInclude;
+  request.url = GURL("http://example.com/");
+  request.request_initiator = base::nullopt;
+
+  CreateLoaderAndStart(request);
+  RunUntilComplete();
+
+  EXPECT_FALSE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client().completion_status().error_code);
+}
+
+TEST_F(CORSURLLoaderTest, NavigateWithoutInitiator) {
+  ResourceRequest request;
+  request.fetch_request_mode = mojom::FetchRequestMode::kNavigate;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kInclude;
+  request.url = GURL("http://example.com/");
+  request.request_initiator = base::nullopt;
+
+  CreateLoaderAndStart(request);
+  NotifyLoaderClientOnReceiveResponse();
+  NotifyLoaderClientOnComplete(net::OK);
+  RunUntilComplete();
+
+  EXPECT_TRUE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_TRUE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::OK, client().completion_status().error_code);
+}
+
+TEST_F(CORSURLLoaderTest, CredentialsModeAndLoadFlagsContradictEachOther1) {
+  ResourceRequest request;
+  request.fetch_request_mode = mojom::FetchRequestMode::kNavigate;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kOmit;
+  request.load_flags =
+      net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES;
+  request.url = GURL("http://example.com/");
+  request.request_initiator = base::nullopt;
+
+  CreateLoaderAndStart(request);
+  RunUntilComplete();
+
+  EXPECT_FALSE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client().completion_status().error_code);
+}
+
+TEST_F(CORSURLLoaderTest, CredentialsModeAndLoadFlagsContradictEachOther2) {
+  ResourceRequest request;
+  request.fetch_request_mode = mojom::FetchRequestMode::kNavigate;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kOmit;
+  request.load_flags =
+      net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA;
+  request.url = GURL("http://example.com/");
+  request.request_initiator = base::nullopt;
+
+  CreateLoaderAndStart(request);
+  RunUntilComplete();
+
+  EXPECT_FALSE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client().completion_status().error_code);
+}
+
+TEST_F(CORSURLLoaderTest, CredentialsModeAndLoadFlagsContradictEachOther3) {
+  ResourceRequest request;
+  request.fetch_request_mode = mojom::FetchRequestMode::kNavigate;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kOmit;
+  request.load_flags =
+      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA;
+  request.url = GURL("http://example.com/");
+  request.request_initiator = base::nullopt;
+
+  CreateLoaderAndStart(request);
+  RunUntilComplete();
+
+  EXPECT_FALSE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::ERR_INVALID_ARGUMENT, client().completion_status().error_code);
+}
+
 TEST_F(CORSURLLoaderTest, SameOriginRequest) {
   const GURL url("http://example.com/foo.png");
   CreateLoaderAndStart(url.GetOrigin(), url,
@@ -292,7 +421,7 @@
   const GURL url("http://other.com/foo.png");
   ResourceRequest request;
   request.fetch_request_mode = mojom::FetchRequestMode::kNoCORS;
-  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kOmit;
+  request.fetch_credentials_mode = mojom::FetchCredentialsMode::kInclude;
   request.method = "PATCH";
   request.url = url;
   request.request_initiator = url::Origin::Create(origin);
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h
index 8f4ae5a8b..13aead1 100644
--- a/services/network/public/cpp/resource_request.h
+++ b/services/network/public/cpp/resource_request.h
@@ -126,6 +126,9 @@
 
   // https://fetch.spec.whatwg.org/#concept-request-mode
   // Used mainly by CORS handling (out-of-blink CORS), CORB, Service Worker.
+  // CORS handling needs a proper origin (including a unique opaque origin).
+  // Hence a request with kSameOrigin, kCORS, or kCORSWithForcedPreflight should
+  // have a non-null request_initiator.
   mojom::FetchRequestMode fetch_request_mode = mojom::FetchRequestMode::kNoCORS;
 
   // https://fetch.spec.whatwg.org/#concept-request-credentials-mode
diff --git a/services/video_capture/device_factory_provider_impl.cc b/services/video_capture/device_factory_provider_impl.cc
index 3738162..787d39b 100644
--- a/services/video_capture/device_factory_provider_impl.cc
+++ b/services/video_capture/device_factory_provider_impl.cc
@@ -6,6 +6,7 @@
 
 #include "base/task_scheduler/post_task.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
+#include "media/capture/video/create_video_capture_device_factory.h"
 #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"
@@ -110,7 +111,7 @@
   // happen on a "UI thread equivalent", e.g. obtaining screen rotation on
   // Chrome OS.
   std::unique_ptr<media::VideoCaptureDeviceFactory> media_device_factory =
-      media::VideoCaptureDeviceFactory::CreateFactory(
+      media::CreateVideoCaptureDeviceFactory(
           base::ThreadTaskRunnerHandle::Get());
   auto video_capture_system = std::make_unique<media::VideoCaptureSystemImpl>(
       std::move(media_device_factory));
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index b0ffe92..8698b6e 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -236,8 +236,9 @@
       {
         "args": [
           "-v",
-          "--browser=android-chromium",
+          "--browser=android-webview",
           "--upload-results",
+          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk",
           "--run-ref-build",
           "--test-shard-map-filename=android_nexus5x_webview_16_shard_map.json",
           "--assert-gpu-compositing"
@@ -287,8 +288,9 @@
       {
         "args": [
           "-v",
-          "--browser=android-chromium",
+          "--browser=android-webview",
           "--upload-results",
+          "--webview-embedder-apk=../../out/Release/apks/SystemWebViewShell.apk",
           "--run-ref-build",
           "--test-shard-map-filename=android_nexus6_webview_shard_map.json",
           "--assert-gpu-compositing"
diff --git a/testing/scripts/common.py b/testing/scripts/common.py
index 28758fe..2217d07b 100644
--- a/testing/scripts/common.py
+++ b/testing/scripts/common.py
@@ -138,8 +138,7 @@
   # TODO(dpranke): crbug.com/357866 - we should simplify the handling of
   # both the return code and parsing the actual results, below.
 
-  passing_statuses = ('PASS', 'SLOW', 'NEEDSREBASELINE',
-                        'NEEDSMANUALREBASELINE')
+  passing_statuses = ('PASS', 'SLOW', 'NEEDSREBASELINE')
 
   for test, result in convert_trie_to_flat_paths(
       json_results['tests']).iteritems():
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=HeapIncrementalMarkingStress b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=HeapIncrementalMarkingStress
index bafc3da..b57fc18 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=HeapIncrementalMarkingStress
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=HeapIncrementalMarkingStress
@@ -1 +1,2 @@
 crbug.com/831541 inspector-protocol/css/css-collect-class-names.js [ Pass Failure ]
+crbug.com/865348 http/tests/navigation/navigation-interrupted-by-fragment.html [ Pass Timeout ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 0b12b84..1aea2d8 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -40,8 +40,8 @@
 crbug.com/591099 svg/wicd/rightsizing-grid.html [ Failure Pass ]
 
 # Features that do not have active plans to support or turn on.
-crbug.com/591099 fast/css3-text/css3-text-justify/text-justify-distribute.html [ Skip ]
 crbug.com/591099 fast/css3-text/css3-text-indent/text-indent-out-of-flow-each-line-hanging.html [ Skip ]
+crbug.com/591099 fast/css3-text/css3-text-justify/text-justify-distribute.html [ Skip ]
 
 # New passes
 crbug.com/774229 editing/pasteboard/copy-paste-white-space.html [ Pass ]
@@ -417,7 +417,7 @@
 crbug.com/591099 external/wpt/performance-timeline/po-observe.html [ Timeout ]
 crbug.com/591099 external/wpt/pointerevents/pointerevent_click_during_capture-manual.html [ Crash Timeout ]
 crbug.com/591099 external/wpt/quirks/line-height-calculation.html [ Failure ]
-crbug.com/591099 external/wpt/requestidlecallback/callback-iframe.html [ Pass ]
+crbug.com/591099 external/wpt/requestidlecallback/callback-iframe.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/requestidlecallback/callback-timeout-when-busy.html [ Timeout ]
 crbug.com/591099 external/wpt/requestidlecallback/callback-timeout.html [ Timeout ]
 crbug.com/591099 external/wpt/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html [ Failure ]
@@ -425,12 +425,12 @@
 crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-001.svg [ Failure ]
 crbug.com/591099 external/wpt/svg/painting/reftests/paint-context-002.svg [ Failure ]
 crbug.com/591099 external/wpt/svg/path/bearing/zero.svg [ Failure ]
-crbug.com/591099 external/wpt/trusted-types/HTMLImageElement-src.tentative.html [ Failure ]
-crbug.com/591099 external/wpt/trusted-types/HTMLMediaElement-src.tentative.html [ Failure ]
-crbug.com/591099 external/wpt/trusted-types/HTMLSourceElement-src.tentative.html [ Failure ]
-crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html [ Failure ]
-crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html [ Failure ]
-crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html [ Failure ]
+crbug.com/591099 external/wpt/trusted-types/HTMLImageElement-src.tentative.html [ Failure Pass ]
+crbug.com/591099 external/wpt/trusted-types/HTMLMediaElement-src.tentative.html [ Failure Pass ]
+crbug.com/591099 external/wpt/trusted-types/HTMLSourceElement-src.tentative.html [ Failure Pass ]
+crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLImageElement-src.tentative.html [ Failure Pass ]
+crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLMediaElement-src.tentative.html [ Failure Pass ]
+crbug.com/591099 external/wpt/trusted-types/block-string-assignment-to-HTMLSourceElement-src.tentative.html [ Failure Pass ]
 crbug.com/591099 external/wpt/user-timing/invoke_with_timing_attributes.worker.html [ Failure ]
 crbug.com/591099 external/wpt/webrtc/interfaces.html [ Pass Timeout ]
 crbug.com/591099 external/wpt/websockets/Create-Secure-extensions-empty.any.worker.html [ Timeout ]
@@ -544,14 +544,14 @@
 crbug.com/591099 fast/css3-text/css3-text-indent/negative-text-indent-leading-out-of-flow.html [ Failure ]
 crbug.com/591099 fast/css3-text/css3-text-indent/text-indent-leading-out-of-flow.html [ Failure ]
 crbug.com/591099 fast/dom/HTMLAreaElement/area-download.html [ Failure ]
-crbug.com/755750 fast/dom/Range/get-bounding-client-rect-empty-and-non-empty.html [ Failure ]
+crbug.com/755750 fast/dom/Range/get-bounding-client-rect-empty-and-non-empty.html [ Failure Pass ]
 crbug.com/755750 fast/dom/Range/getBoundingClientRect-linebreak-character.html [ Failure ]
 crbug.com/591099 fast/dom/nodesFromRect/nodesFromRect-basic.html [ Failure ]
 crbug.com/591099 fast/dynamic/first-letter-after-list-marker.html [ Failure ]
 crbug.com/591099 fast/dynamic/text-combine.html [ Failure ]
 crbug.com/591099 fast/encoding/utf-16-big-endian.html [ Failure ]
 crbug.com/591099 fast/encoding/utf-16-little-endian.html [ Failure ]
-crbug.com/591099 fast/events/background-tab-on-submit-ctrl-click.html [ Failure ]
+crbug.com/591099 fast/events/background-tab-on-submit-ctrl-click.html [ Failure Timeout ]
 crbug.com/591099 fast/events/background-tab-on-submit-synthesized-ctrl-click.html [ Failure ]
 crbug.com/591099 fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
 crbug.com/591099 fast/events/wheel/mainthread-touchpad-fling-latching.html [ Pass ]
@@ -572,7 +572,6 @@
 crbug.com/591099 fast/inline/nested-text-descendants.html [ Failure ]
 crbug.com/591099 fast/inline/outline-continuations.html [ Failure ]
 crbug.com/714962 fast/inline/outline-offset.html [ Failure ]
-crbug.com/591099 fast/lists/004.html [ Failure ]
 crbug.com/860415 fast/multicol/out-of-flow/abspos-auto-position-on-line-rtl.html [ Failure ]
 crbug.com/591099 fast/overflow/overflow-update-transform.html [ Failure ]
 crbug.com/591099 fast/overflow/recompute-overflow-of-layout-root-container.html [ Failure ]
@@ -663,10 +662,10 @@
 crbug.com/591099 http/tests/preload/dynamic_remove_preload_href.html [ Failure Pass ]
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-appcache.https.html [ Crash Failure ]
 crbug.com/591099 http/tests/security/cors-rfc1918/addressspace-document-csp-appcache.https.html [ Crash Failure Pass ]
-crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ]
+crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash Pass ]
 crbug.com/591099 http/tests/security/xssAuditor/block-does-not-leak-location.html [ Failure ]
 crbug.com/591099 http/tests/websocket/invalid-subprotocol-characters.html [ Pass Timeout ]
-crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass ]
+crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Crash Pass Timeout ]
 crbug.com/714962 images/color-profile-background-clip-text.html [ Failure ]
 crbug.com/591099 images/color-profile-image-filter-all.html [ Failure ]
 crbug.com/714962 inspector-protocol/css/css-get-platform-fonts.js [ Failure ]
@@ -820,7 +819,7 @@
 crbug.com/591099 storage/indexeddb/mozilla/indexes.html [ Timeout ]
 crbug.com/591099 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Timeout ]
 crbug.com/591099 storage/indexeddb/objectstore-cursor.html [ Pass Timeout ]
-crbug.com/591099 storage/indexeddb/objectstore-keycursor.html [ Timeout ]
+crbug.com/591099 storage/indexeddb/objectstore-keycursor.html [ Pass Timeout ]
 crbug.com/591099 svg/custom/object-sizing-no-width-height.xhtml [ Failure ]
 crbug.com/591099 svg/filters/feTurbulence-bad-seeds.html [ Failure ]
 crbug.com/591099 svg/in-html/sizing/svg-inline.html [ Failure ]
@@ -854,7 +853,7 @@
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/background-tab-on-submit-synthesized-ctrl-click.html [ Failure ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/wheel/mainthread-touchpad-fling-latching.html [ Pass ]
-crbug.com/591099 virtual/mouseevent_fractional/fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Pass ]
+crbug.com/591099 virtual/mouseevent_fractional/fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Failure Pass ]
 crbug.com/591099 virtual/new-remote-playback-pipeline/ [ Skip ]
 crbug.com/591099 virtual/off-main-thread-websocket/http/tests/websocket/invalid-subprotocol-characters.html [ Pass Timeout ]
 crbug.com/591099 virtual/outofblink-cors/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 0e4c464..158ae6a 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1716,6 +1716,10 @@
 crbug.com/736308 virtual/outofblink-cors-ns/http/tests/xmlhttprequest/origin-whitelisting-ip-addresses.html [ Failure ]
 crbug.com/736308 virtual/outofblink-cors-ns/http/tests/xmlhttprequest/workers/cross-origin-unsupported-url.html [ Failure ]
 
+# The tests time out on some builders.
+crbug.com/862184 [ Linux ] virtual/outofblink-cors-ns/http/tests/fetch/chromium/call-extra-crash-tee.html [ Timeout Pass ]
+crbug.com/862184 [ Linux ] virtual/outofblink-cors-ns/http/tests/fetch/chromium/release-handle-crash.html [ Timeout Pass ]
+
 # ====== Out of Blink CORS related tests END ======
 
 crbug.com/771118 virtual/service-worker-servicification/external/wpt/service-workers/service-worker/mime-sniffing.https.html [ Failure ]
@@ -4422,9 +4426,6 @@
 crbug.com/826936 external/wpt/webauthn/getcredential-timeout.https.html [ Pass Timeout ]
 crbug.com/826936 external/wpt/webauthn/createcredential-pubkeycredparams.https.html [ Pass Timeout ]
 
-# WebRTC with Unified Plan
-crbug.com/828866 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html [ Timeout ]
-
 # WebRTC codec tests - software H.264 not present on webkit bot family
 crbug.com/840659 external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ]
 crbug.com/840659 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ]
@@ -4669,3 +4670,6 @@
 # Sheriff 2018-07-18
 crbug.com/863067 [ Win10 ] fast/dom/Window/window-focus-self.html [ Failure Pass ]
 crbug.com/864994 [ Mac ] external/wpt/encoding/legacy-mb-korean/euc-kr/euckr-decode-ksc_5601.html [ Timeout Failure Pass ]
+
+crbug.com/865371 [ Mac ]  external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping.html [ Failure Pass ]
+crbug.com/865432 [ Linux ] external/wpt/workers/modules/dedicated-worker-import-blob-url.any.worker.html [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
index 82042fe..438ce0ff 100644
--- a/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
+++ b/third_party/WebKit/LayoutTests/external/WPT_BASE_MANIFEST.json
@@ -104210,6 +104210,16 @@
      {}
     ]
    ],
+   "console/idlharness.any-expected.txt": [
+    [
+     {}
+    ]
+   ],
+   "console/idlharness.any.worker-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "content-security-policy/META.yml": [
     [
      {}
@@ -135565,6 +135575,11 @@
      {}
     ]
    ],
+   "css/cssom/cssstyledeclaration-setter-logical-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/cssom/cssstyledeclaration-setter-order-expected.txt": [
     [
      {}
@@ -171045,11 +171060,6 @@
      {}
     ]
    ],
-   "webrtc/RTCPeerConnection-getTransceivers-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "webrtc/RTCPeerConnection-helper.js": [
     [
      {}
@@ -185601,6 +185611,16 @@
      {}
     ]
    ],
+   "console/idlharness.any.js": [
+    [
+     "/console/idlharness.any.html",
+     {}
+    ],
+    [
+     "/console/idlharness.any.worker.html",
+     {}
+    ]
+   ],
    "content-security-policy/base-uri/base-uri-allow.sub.html": [
     [
      "/content-security-policy/base-uri/base-uri-allow.sub.html",
@@ -198789,9 +198809,15 @@
      {}
     ]
    ],
-   "css/cssom/cssstyledeclaration-setter-order.html": [
+   "css/cssom/cssstyledeclaration-setter-declarations.html": [
     [
-     "/css/cssom/cssstyledeclaration-setter-order.html",
+     "/css/cssom/cssstyledeclaration-setter-declarations.html",
+     {}
+    ]
+   ],
+   "css/cssom/cssstyledeclaration-setter-logical.html": [
+    [
+     "/css/cssom/cssstyledeclaration-setter-logical.html",
      {}
     ]
    ],
@@ -252263,6 +252289,12 @@
      {}
     ]
    ],
+   "trusted-types/block-string-assignment-to-window-open.tentative.html": [
+    [
+     "/trusted-types/block-string-assignment-to-window-open.tentative.html",
+     {}
+    ]
+   ],
    "trusted-types/createContextualFragment.tentative.html": [
     [
      "/trusted-types/createContextualFragment.tentative.html",
@@ -252335,6 +252367,12 @@
      {}
     ]
    ],
+   "trusted-types/window-open.tentative.html": [
+    [
+     "/trusted-types/window-open.tentative.html",
+     {}
+    ]
+   ],
    "uievents/constructors/inputevent-constructor.html": [
     [
      "/uievents/constructors/inputevent-constructor.html",
@@ -276770,6 +276808,18 @@
    "8b2d2e8d6675efacf99f49fec95c8be52f38407e",
    "manual"
   ],
+  "console/idlharness.any-expected.txt": [
+   "6aa91b8fcfd8f7956e518e0c90018051ad49bbfa",
+   "support"
+  ],
+  "console/idlharness.any.js": [
+   "bb3f2248963773fb214a050d7f64c06c631ad8d4",
+   "testharness"
+  ],
+  "console/idlharness.any.worker-expected.txt": [
+   "6aa91b8fcfd8f7956e518e0c90018051ad49bbfa",
+   "support"
+  ],
   "content-security-policy/META.yml": [
    "8b13100d5ecf0c606f74c249a18bf031ba77094d",
    "support"
@@ -342587,7 +342637,7 @@
    "testharness"
   ],
   "css/cssom/cssstyledeclaration-mutationrecord-001.html": [
-   "5d455757e4c80b4781ea4263fa78bced1d6b8632",
+   "0ed8cb2c41f371fdb509731f2ad1cf11e047d46f",
    "testharness"
   ],
   "css/cssom/cssstyledeclaration-mutationrecord-002.html": [
@@ -342602,14 +342652,22 @@
    "958b71b8f1c58a809590459e6f085f3e1217e9c7",
    "testharness"
   ],
+  "css/cssom/cssstyledeclaration-setter-declarations.html": [
+   "e530f6b573bfb9774dd732f7289156117fc4bd94",
+   "testharness"
+  ],
+  "css/cssom/cssstyledeclaration-setter-logical-expected.txt": [
+   "51cae6a9e4342e6fb6fc76529a1073c0cbf01c8c",
+   "support"
+  ],
+  "css/cssom/cssstyledeclaration-setter-logical.html": [
+   "c454a34b964e2aa05790831cc2de20e169162dd5",
+   "testharness"
+  ],
   "css/cssom/cssstyledeclaration-setter-order-expected.txt": [
    "d9f80374aad1bb903b7a72cc3c706e491b9b28b6",
    "support"
   ],
-  "css/cssom/cssstyledeclaration-setter-order.html": [
-   "3e0e768c466011bb3d91b3f0eff55e029a2aec0f",
-   "testharness"
-  ],
   "css/cssom/escape.html": [
    "c9ed57c7ef7a035c25feff4ea60547a57d727f31",
    "testharness"
@@ -377855,7 +377913,7 @@
    "support"
   ],
   "interfaces/console.idl": [
-   "43ced34008dc73d05c79140d8dc33c60e2d9df3a",
+   "7f3bdda49f7c4f0b4ae5a87e4a05463da05f0554",
    "support"
   ],
   "interfaces/cookie-store.idl": [
@@ -405286,6 +405344,10 @@
    "da38712c6e43d1e6fe5892a5339a45c4bf438c7e",
    "testharness"
   ],
+  "trusted-types/block-string-assignment-to-window-open.tentative.html": [
+   "210a8b4968f4976dca5316876228debbc3d9ddfa",
+   "testharness"
+  ],
   "trusted-types/createContextualFragment.tentative.html": [
    "e98f5e7fa6feeb5000a6310377ea82041c87e27d",
    "testharness"
@@ -405338,6 +405400,10 @@
    "6162bad41b15d0ae0be727b5d960bb538d430fe2",
    "support"
   ],
+  "trusted-types/window-open.tentative.html": [
+   "bbdc214490d471285f9b086cd0b98eb8a765691a",
+   "testharness"
+  ],
   "uievents/META.yml": [
    "a6706289064c1bbabcfab6540831084fc39fb94a",
    "support"
@@ -408943,7 +409009,7 @@
    "support"
   ],
   "webrtc/OWNERS": [
-   "029972b55b8ece773bd13d288f590b34b2974b20",
+   "f22508b5bacfde530030399516c1cf005cbd448f",
    "support"
   ],
   "webrtc/RTCCertificate-expected.txt": [
@@ -409015,7 +409081,7 @@
    "testharness"
   ],
   "webrtc/RTCDTMFSender-ontonechange.https-expected.txt": [
-   "caa6ccc6a286883d4ecea875544462c1abaef503",
+   "5f5dd477a4ce9b1cd4e0cf83eb3938443554d807",
    "support"
   ],
   "webrtc/RTCDTMFSender-ontonechange.https.html": [
@@ -409083,7 +409149,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-addTrack.https-expected.txt": [
-   "db28694a7d0af70e49d1e61e30765064dd401bd5",
+   "525e696aea18da56f97605eb6d555a36f2c114d9",
    "support"
   ],
   "webrtc/RTCPeerConnection-addTrack.https.html": [
@@ -409143,7 +409209,7 @@
    "support"
   ],
   "webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt": [
-   "ae109983a8f1761931ab848288e22dc991672032",
+   "2435b4d6afc9ccb9c793c67d10486926fc8ed1f5",
    "support"
   ],
   "webrtc/RTCPeerConnection-createOffer-offerToReceive.html": [
@@ -409190,10 +409256,6 @@
    "fbb26c647a8759d1b9da637f7167b1a805f647c4",
    "testharness"
   ],
-  "webrtc/RTCPeerConnection-getTransceivers-expected.txt": [
-   "3e82583213727df78f6ad7842628faac97a30ac7",
-   "support"
-  ],
   "webrtc/RTCPeerConnection-getTransceivers.html": [
    "b4c97af4f907a3d02fe1ebd24f00ab110b387575",
    "testharness"
@@ -409235,7 +409297,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnection-ontrack.https-expected.txt": [
-   "2223a5f8f478047654446f6c6950a2db83528541",
+   "df68d12770ca3d1a359b1b8172ffbd31b49f3e79",
    "support"
   ],
   "webrtc/RTCPeerConnection-ontrack.https.html": [
diff --git a/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any-expected.txt
new file mode 100644
index 0000000..b38fd27
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS console interfaces
+FAIL console namespace: operation assert(boolean, any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation clear() assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation debug(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation error(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation info(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation log(any...) assert_equals: operation has wrong .length expected 0 but got 1
+PASS console namespace: operation table(any, [object Object])
+FAIL console namespace: operation trace(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation warn(any...) assert_equals: operation has wrong .length expected 0 but got 1
+PASS console namespace: operation dir(any, object)
+FAIL console namespace: operation dirxml(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation count(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation countReset(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation group(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation groupCollapsed(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation groupEnd() assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation time(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation timeLog(DOMString, any...) assert_own_property: namespace object missing operation "timeLog" expected property "timeLog" missing
+FAIL console namespace: operation timeEnd(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any.js b/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any.js
new file mode 100644
index 0000000..114564f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any.js
@@ -0,0 +1,14 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+// https://console.spec.whatwg.org/
+
+promise_test(async () => {
+  const srcs = ['console'];
+  const [idl] = await Promise.all(
+      srcs.map(i => fetch(`/interfaces/${i}.idl`).then(r => r.text())));
+
+  const idl_array = new IdlArray();
+  idl_array.add_idls(idl);
+  idl_array.test();
+}, 'console interfaces');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any.worker-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any.worker-expected.txt
new file mode 100644
index 0000000..b38fd27
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/console/idlharness.any.worker-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS console interfaces
+FAIL console namespace: operation assert(boolean, any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation clear() assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation debug(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation error(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation info(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation log(any...) assert_equals: operation has wrong .length expected 0 but got 1
+PASS console namespace: operation table(any, [object Object])
+FAIL console namespace: operation trace(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation warn(any...) assert_equals: operation has wrong .length expected 0 but got 1
+PASS console namespace: operation dir(any, object)
+FAIL console namespace: operation dirxml(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation count(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation countReset(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation group(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation groupCollapsed(any...) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation groupEnd() assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation time(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+FAIL console namespace: operation timeLog(DOMString, any...) assert_own_property: namespace object missing operation "timeLog" expected property "timeLog" missing
+FAIL console namespace: operation timeEnd(DOMString) assert_equals: operation has wrong .length expected 0 but got 1
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-mutationrecord-001.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-mutationrecord-001.html
index 5bd8456..ba4d926b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-mutationrecord-001.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-mutationrecord-001.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>CSSOM: CSSStyleDeclaration.setPropertyValue queues a mutation record when not actually mutated</title>
+<title>CSSOM: CSSStyleDeclaration.setPropertyValue queues a mutation record when changed</title>
 <link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setpropertyvalue">
 <link rel="help" href="https://drafts.csswg.org/cssom/#update-style-attribute-for">
 <link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
@@ -9,12 +9,12 @@
 <script>
   document.documentElement.style.top = "0px";
 
-  let test = async_test("CSSStyleDeclaration.setPropertyValue queues a mutation record, even if not mutated");
+  let test = async_test("CSSStyleDeclaration.setPropertyValue queues a mutation record when serialization is changed");
   let m = new MutationObserver(function(r) {
     assert_equals(r.length, 1);
     test.done();
   });
 
   m.observe(document.documentElement,  { attributes: true });
-  document.documentElement.style.top = "0px";
+  document.documentElement.style.top = "1px";
 </script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-declarations.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-declarations.html
new file mode 100644
index 0000000..e66466e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-declarations.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<title>CSSOM test: declaration block after setting via CSSOM</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#set-a-css-declaration-value">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+  function generateCSSDeclBlock(props) {
+    let elem = document.createElement("div");
+    let cssText = props.map(({name, value, priority}) => {
+      let longhand = `${name}: ${value}`;
+      if (priority) {
+        longhand += "!" + priority;
+      }
+      return longhand + ";";
+    }).join(" ");
+    elem.setAttribute("style", cssText);
+    return elem.style;
+  }
+  function compareByName(a, b) {
+    if (a.name < b.name) return -1;
+    if (a.name > b.name) return 1;
+    return 0;
+  }
+  function checkDeclarationsAnyOrder(block, props, msg) {
+    let actual = [];
+    for (let name of block) {
+      let value = block.getPropertyValue(name);
+      let priority = block.getPropertyPriority(name);
+      actual.push({name, value, priority});
+    }
+    actual.sort(compareByName);
+    let expected = Array.from(props);
+    expected.sort(compareByName);
+    assert_object_equals(actual, expected, "Declaration block content should match " + msg);
+  }
+  function longhand(name, value, priority="") {
+    return {name, value, priority};
+  }
+  function* shorthand(name, value, priority="") {
+    for (let subprop of SUBPROPS[name]) {
+      yield longhand(subprop, value, priority);
+    }
+  }
+
+  const SUBPROPS = {
+    "margin": ["margin-top", "margin-right", "margin-bottom", "margin-left"],
+    "padding": ["padding-top", "padding-right", "padding-bottom", "padding-left"],
+  };
+
+  test(function() {
+    let expectedDecls = [
+      longhand("top", "1px"),
+      longhand("bottom", "2px"),
+      longhand("left", "3px", "important"),
+      longhand("right", "4px"),
+    ];
+    let block = generateCSSDeclBlock(expectedDecls);
+    checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+    block.setProperty("top", "5px", "important");
+    expectedDecls[0] = longhand("top", "5px", "important");
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property");
+
+    block.setProperty("bottom", "2px");
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with identical value");
+
+    block.setProperty("left", "3px");
+    expectedDecls[2].priority = "";
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with different priority");
+
+    block.setProperty("float", "none");
+    expectedDecls.push(longhand("float", "none"));
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting non-existing property");
+  }, "setProperty with longhand should update only the declaration being set");
+
+  test(function() {
+    let expectedDecls = [
+      longhand("top", "1px"),
+      longhand("bottom", "2px"),
+      longhand("left", "3px", "important"),
+      longhand("right", "4px"),
+    ];
+    let block = generateCSSDeclBlock(expectedDecls);
+    checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+    block.top = "5px";
+    expectedDecls[0] = longhand("top", "5px");
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property");
+
+    block.bottom = "2px";
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with identical value");
+
+    block.left = "3px";
+    expectedDecls[2].priority = "";
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting existing property with different priority");
+
+    block.float = "none";
+    expectedDecls.push(longhand("float", "none"));
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting non-existing property");
+  }, "property setter should update only the declaration being set");
+
+  test(function() {
+    let expectedDecls = [
+      ...shorthand("margin", "1px"),
+      longhand("top", "2px"),
+      ...shorthand("padding", "3px", "important"),
+    ];
+    let block = generateCSSDeclBlock(expectedDecls);
+    checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+    block.setProperty("margin", "4px");
+    for (let i = 0; i < 4; i++) {
+      expectedDecls[i].value = "4px";
+    }
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand");
+
+    block.setProperty("margin", "4px");
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with identical value");
+
+    block.setProperty("padding", "3px");
+    for (let i = 5; i < 9; i++) {
+      expectedDecls[i].priority = "";
+    }
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with different priority");
+
+    block.setProperty("margin-bottom", "5px", "important");
+    expectedDecls[2] = longhand("margin-bottom", "5px", "important");
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting a longhand in an existing shorthand");
+  }, "setProperty with shorthand should update only the declarations being set");
+
+  test(function() {
+    let expectedDecls = [
+      ...shorthand("margin", "1px"),
+      longhand("top", "2px"),
+      ...shorthand("padding", "3px", "important"),
+    ];
+    let block = generateCSSDeclBlock(expectedDecls);
+    checkDeclarationsAnyOrder(block, expectedDecls, "in initial block");
+
+    block.margin = "4px";
+    for (let i = 0; i < 4; i++) {
+      expectedDecls[i].value = "4px";
+    }
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand");
+
+    block.margin = "4px";
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with identical value");
+
+    block.padding = "3px";
+    for (let i = 5; i < 9; i++) {
+      expectedDecls[i].priority = "";
+    }
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting an existing shorthand with different priority");
+
+    block.marginBottom = "5px";
+    expectedDecls[2] = longhand("margin-bottom", "5px");
+    checkDeclarationsAnyOrder(block, expectedDecls, "after setting a longhand in an existing shorthand");
+  }, "longhand property setter should update only the decoarations being set");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-logical-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-logical-expected.txt
new file mode 100644
index 0000000..34394c9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-logical-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL newly set declaration should be after all declarations in the same logical property group but have different logical kind assert_less_than: padding-top should be after padding-block-start after setting padding-top expected a number less than 0 but got 4
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-logical.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-logical.html
new file mode 100644
index 0000000..13d68e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-logical.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<title>CSSOM test: declaration block after setting via CSSOM</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#set-a-css-declaration-value">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="test"></div>
+<script>
+  test(function() {
+    const PHYSICAL_PROPS = ["padding-top", "padding-right",
+                            "padding-bottom", "padding-left"];
+    const LOGICAL_PROPS = ["padding-block-start", "padding-block-end",
+                           "padding-inline-start", "padding-inline-end"];
+    let elem = document.getElementById("test");
+    let decls = new Map;
+
+    {
+      let css = []
+      let i = 0;
+      for (let name of [...PHYSICAL_PROPS, ...LOGICAL_PROPS]) {
+        let value = `${i++}px`;
+        css.push(`${name}: ${value};`);
+        decls.set(name, value);
+      }
+      elem.setAttribute("style", css.join(" "));
+    }
+
+    let style = elem.style;
+    function indexOfProperty(prop) {
+      return Array.prototype.indexOf.apply(style, [prop]);
+    }
+    function assertPropAfterProps(prop, props, msg) {
+      let index = indexOfProperty(prop);
+      for (let p of props) {
+        assert_less_than(indexOfProperty(p), index, `${prop} should be after ${p} ${msg}`);
+      }
+    }
+    // We are not changing any value in this test, just order.
+    function assertValueUnchanged() {
+      for (let [name, value] of decls.entries()) {
+        assert_equals(style.getPropertyValue(name), value,
+                      `value of ${name} shouldn't be changed`);
+      }
+    }
+
+    assertValueUnchanged();
+    // Check that logical properties are all after physical properties
+    // at the beginning.
+    for (let p of LOGICAL_PROPS) {
+      assertPropAfterProps(p, PHYSICAL_PROPS, "initially");
+    }
+
+    // Try setting a longhand.
+    style.setProperty("padding-top", "0px");
+    assertValueUnchanged();
+    // Check that padding-top is after logical properties, but other
+    // physical properties are still before logical properties.
+    assertPropAfterProps("padding-top", LOGICAL_PROPS, "after setting padding-top");
+    for (let p of LOGICAL_PROPS) {
+      assertPropAfterProps(p, PHYSICAL_PROPS.filter(prop => prop != "padding-top"),
+                           "after setting padding-top");
+    }
+
+    // Try setting a shorthand.
+    style.setProperty("padding", "0px 1px 2px 3px");
+    assertValueUnchanged();
+    // Check that all physical properties are now after logical properties.
+    for (let p of PHYSICAL_PROPS) {
+      assertPropAfterProps(p, LOGICAL_PROPS, "after setting padding shorthand");
+    }
+  }, "newly set declaration should be after all declarations " +
+  "in the same logical property group but have different logical kind");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-order.html b/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-order.html
deleted file mode 100644
index 702b38f..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/cssom/cssstyledeclaration-setter-order.html
+++ /dev/null
@@ -1,108 +0,0 @@
-<!DOCTYPE html>
-<title>CSSOM test: order of declarations after setting via CSSOM</title>
-<link rel="help" href="https://drafts.csswg.org/cssom/#set-a-css-declaration-value">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<script>
-  function generateCSSDeclBlock(props) {
-    let elem = document.createElement("div");
-    let cssText = props.map(([prop, value]) => `${prop}: ${value};`).join(" ");
-    elem.setAttribute("style", cssText);
-    return elem.style;
-  }
-  function checkOrder(block, props, msg) {
-    assert_array_equals(Array.from(block), props, `Property order should match ${msg}`);
-  }
-  function arrayWithItemsAtEnd(array, items) {
-    let result = array.filter(item => !items.includes(item));
-    return result.concat(items);
-  }
-
-  const SUBPROPS = {
-    "margin": ["margin-top", "margin-right", "margin-bottom", "margin-left"],
-    "padding": ["padding-top", "padding-right", "padding-bottom", "padding-left"],
-  };
-
-  test(function() {
-    let block = generateCSSDeclBlock([
-      ["top", "1px"],
-      ["bottom", "2px"],
-      ["left", "3px"],
-      ["right", "4px"],
-    ]);
-    let expectedOrder = ["top", "bottom", "left", "right"];
-    checkOrder(block, expectedOrder, "in initial block");
-
-    block.setProperty("top", "5px");
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, ["top"]);
-    checkOrder(block, expectedOrder, "after setting existing property");
-
-    block.setProperty("bottom", "2px");
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, ["bottom"]);
-    checkOrder(block, expectedOrder, "after setting existing property with identical value");
-  }, "setProperty with existing longhand should change order");
-
-  test(function() {
-    let block = generateCSSDeclBlock([
-      ["top", "1px"],
-      ["bottom", "2px"],
-      ["left", "3px"],
-      ["right", "4px"],
-    ]);
-    let expectedOrder = ["top", "bottom", "left", "right"];
-    checkOrder(block, expectedOrder, "in initial block");
-
-    block.top = "5px";
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, ["top"]);
-    checkOrder(block, expectedOrder, "after setting existing property");
-
-    block.bottom = "2px";
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, ["bottom"]);
-    checkOrder(block, expectedOrder, "after setting existing property with identical value");
-  }, "invoke property setter with existing longhand should change order");
-
-  test(function() {
-    let block = generateCSSDeclBlock([
-      ["margin", "1px"],
-      ["top", "2px"],
-      ["padding", "3px"],
-    ]);
-    let expectedOrder = SUBPROPS["margin"].concat(["top"]).concat(SUBPROPS["padding"]);
-    checkOrder(block, expectedOrder, "in initial block");
-
-    block.setProperty("margin", "4px");
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, SUBPROPS["margin"]);
-    checkOrder(block, expectedOrder, "after setting an existing shorthand");
-
-    block.setProperty("padding", "3px");
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, SUBPROPS["padding"]);
-    checkOrder(block, expectedOrder, "after setting an existing shorthand with identical value");
-
-    block.setProperty("margin-bottom", "5px");
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, ["margin-bottom"]);
-    checkOrder(block, expectedOrder, "after setting a longhand in an existing shorthand");
-  }, "setProperty with existing shorthand should change order");
-
-  test(function() {
-    let block = generateCSSDeclBlock([
-      ["margin", "1px"],
-      ["top", "2px"],
-      ["padding", "3px"],
-    ]);
-    let expectedOrder = SUBPROPS["margin"].concat(["top"]).concat(SUBPROPS["padding"]);
-    checkOrder(block, expectedOrder, "in initial block");
-
-    block.margin = "4px";
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, SUBPROPS["margin"]);
-    checkOrder(block, expectedOrder, "after setting an existing shorthand");
-
-    block.padding = "3px";
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, SUBPROPS["padding"]);
-    checkOrder(block, expectedOrder, "after setting an existing shorthand with identical value");
-
-    block.marginBottom = "5px";
-    expectedOrder = arrayWithItemsAtEnd(expectedOrder, ["margin-bottom"]);
-    checkOrder(block, expectedOrder, "after setting a longhand in an existing shorthand");
-  }, "invoke property setter with existing shorthand should change order");
-</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/interfaces/console.idl b/third_party/WebKit/LayoutTests/external/wpt/interfaces/console.idl
index 7351bfc..32729ec 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/interfaces/console.idl
+++ b/third_party/WebKit/LayoutTests/external/wpt/interfaces/console.idl
@@ -1,9 +1,13 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content of this file was automatically extracted from the
+// "Console Standard" spec.
+// See: https://console.spec.whatwg.org/
+
 [Exposed=(Window,Worker,Worklet)]
 namespace console { // but see namespace object requirements below
   // Logging
   void assert(optional boolean condition = false, any... data);
   void clear();
-  void count(optional DOMString label = "default");
   void debug(any... data);
   void error(any... data);
   void info(any... data);
@@ -14,6 +18,10 @@
   void dir(any item, optional object? options);
   void dirxml(any... data);
 
+  // Counting
+  void count(optional DOMString label = "default");
+  void countReset(optional DOMString label = "default");
+
   // Grouping
   void group(any... data);
   void groupCollapsed(any... data);
@@ -21,5 +29,6 @@
 
   // Timing
   void time(optional DOMString label = "default");
+  void timeLog(optional DOMString label = "default", any... data);
   void timeEnd(optional DOMString label = "default");
 };
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLIFrameElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLIFrameElement-src.tentative.html
new file mode 100644
index 0000000..6e89a0f3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLIFrameElement-src.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<body>
+<script>
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('iframe');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('iframe');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLLinkElement-href.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLLinkElement-href.tentative.html
new file mode 100644
index 0000000..7f6e7e23
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLLinkElement-href.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/helper.sub.js"></script>
+<body>
+<script>
+  //helper function for the tests
+  function testHref(str, url) {
+    var link = document.createElement('link');
+    link.href = url;
+    assert_equals(link.href, str);
+  }
+
+  test(t => {
+    testHref(URLS.safe, TrustedURL.create(URLS.safe));
+  }, "link.href = URLS.safe, TrustedURL.create");
+
+  test(t => {
+    testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
+  }, "link.href = URLS.safe, TrustedURL.unsafelyCreate");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLObjectElement.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLObjectElement.tentative.html
new file mode 100644
index 0000000..a9dcc94a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/HTMLObjectElement.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/helper.sub.js"></script>
+<body>
+<script>
+  //helper function for the tests
+  function testData(str, url) {
+    var objectElement = document.createElement('object');
+    objectElement.data = url;
+    objectElement.codeBase = url;
+    assert_equals(objectElement.data, str);
+    assert_equals(objectElement.codeBase,str);
+  }
+
+  test(t => {
+    testData(URLS.safe, TrustedURL.create(URLS.safe));
+  }, "Basic processing: safe URL, safe construction.");
+
+  test(t => {
+    testData(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
+  }, "Basic processing: safe URL, unsafe construction.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLIFrameElement-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLIFrameElement-src.tentative.html
new file mode 100644
index 0000000..8db0d60
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLIFrameElement-src.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
+<body>
+<script>
+  //URL assignments don't throw
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('iframe');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('iframe');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+
+  //String assignment throws
+  test(t => {
+    var d = document.createElement('iframe');
+    assert_throws(new TypeError(), _ => {
+      d.src = "Fail.";
+    });
+  }, "'src = string' throws.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLLinkElement-href.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLLinkElement-href.tentative.html
new file mode 100644
index 0000000..11950da
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLLinkElement-href.tentative.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="support/helper.sub.js"></script>
+
+  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
+</head>
+<body>
+<script>
+  //helper function for the tests
+  function testHref(str, url) {
+    var link = document.createElement('link');
+    link.href = url;
+    assert_equals(link.href, str);
+  }
+
+  //URL assignments do not throw.
+  test(t => {
+    testHref(URLS.safe, TrustedURL.create(URLS.safe));
+  }, "link.href = URLS.safe, TrustedURL.create");
+
+  test(t => {
+    testHref(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
+  }, "link.href = URLS.safe, TrustedURL.unsafelyCreate");
+
+  // String assignments throw.
+  test(t => {
+    var link = document.createElement('link');
+    assert_throws(new TypeError(), _ => {
+      link.href = "A string";
+    });
+  }, "`link.href = string` throws");
+
+  //Null assignment throws.
+  test(t => {
+    var link = document.createElement('link');
+    assert_throws(new TypeError(), _ => {
+      link.href = null;
+    });
+  }, "`link.href = null` throws");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLObjectElement.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLObjectElement.tentative.html
new file mode 100644
index 0000000..87e1f46
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-HTMLObjectElement.tentative.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="support/helper.sub.js"></script>
+
+  <meta http-equiv="Content-Security-Policy" content="require-trusted-types">
+</head>
+<body>
+<script>
+  //helper function for the tests
+  function testData(str, url) {
+    var objectElement = document.createElement('object');
+    objectElement.data = url;
+    objectElement.codeBase = url;
+    assert_equals(objectElement.data, str);
+    assert_equals(objectElement.codeBase, str);
+  }
+
+  //URL assignments do not throw
+  test(t => {
+    testData(URLS.safe, TrustedURL.create(URLS.safe));
+  }, "Basic processing: safe URL, safe construction.");
+
+  test(t => {
+    testData(URLS.safe, TrustedURL.unsafelyCreate(URLS.safe));
+  }, "Basic processing: safe URL, unsafe construction.");
+
+  //String assignments throw
+  test(t => {
+    var objectElement = document.createElement('object');
+    assert_throws(new TypeError(), _ => {
+      objectElement.data = "A string";
+    });
+  }, "`objectElement.data = string` throws");
+
+  test(t => {
+    var objectElement = document.createElement('object');
+    assert_throws(new TypeError(), _ => {
+      objectElement.codeBase = "A string";
+    });
+  }, "`objectElement.codeBase = string` throws");
+
+  //Null assignment throws.
+  test(t => {
+    var objectElement = document.createElement('object');
+    assert_throws(new TypeError(), _ => {
+      objectElement.data = null;
+    });
+  }, "`objectElement.data = null` throws");
+
+  //Null assignment throws.
+  test(t => {
+    var objectElement = document.createElement('object');
+    assert_throws(new TypeError(), _ => {
+      objectElement.codeBase = null;
+    });
+  }, "`objectElement.codeBase = null` throws");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-frame-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-frame-src.tentative.html
new file mode 100644
index 0000000..c915e43
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-frame-src.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
+<body>
+<script>
+  //URL assignments don't throw
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('frame');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('frame');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+
+  //String assignment throws
+  test(t => {
+    var d = document.createElement('frame');
+    assert_throws(new TypeError(), _ => {
+      d.src = "Fail.";
+    });
+  }, "'src = string' throws.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-input-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-input-src.tentative.html
new file mode 100644
index 0000000..732ebe8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-input-src.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
+<body>
+<script>
+  //URL assignments don't throw
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('input');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('input');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+
+  //String assignment throws
+  test(t => {
+    var d = document.createElement('input');
+    assert_throws(new TypeError(), _ => {
+      d.src = "Fail.";
+    });
+  }, "'src = string' throws.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-track-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-track-src.tentative.html
new file mode 100644
index 0000000..b6f81e9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/block-string-assignment-to-track-src.tentative.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<meta http-equiv="Content-Security-Policy" content="require-trusted-types">
+<body>
+<script>
+  //URL assignments don't throw
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('track');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('track');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+
+  //String assignment throws
+  test(t => {
+    var d = document.createElement('track');
+    assert_throws(new TypeError(), _ => {
+      d.src = "Fail.";
+    });
+  }, "'src = string' throws.");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/frame-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/frame-src.tentative.html
new file mode 100644
index 0000000..31a1ce91
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/frame-src.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<body>
+<script>
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('frame');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('frame');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/input-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/input-src.tentative.html
new file mode 100644
index 0000000..8cbd0ad4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/input-src.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<body>
+<script>
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('input');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('input');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/trusted-types/track-src.tentative.html b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/track-src.tentative.html
new file mode 100644
index 0000000..cfe5374e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/trusted-types/track-src.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./support/helper.sub.js"></script>
+
+<body>
+<script>
+  test(t => {
+    var url = TrustedURL.create(URLS.safe);
+
+    var d = document.createElement('track');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.create().");
+
+  test(t => {
+    var url = TrustedURL.unsafelyCreate(URLS.safe);
+
+    var d = document.createElement('track');
+    d.src = url;
+    assert_equals("" + d.src, URLS.safe);
+  }, "src = TrustedURL.unsafelyCreate().");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/OWNERS b/third_party/WebKit/LayoutTests/external/wpt/webrtc/OWNERS
index ba485b1..ea7994a 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/OWNERS
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/OWNERS
@@ -1,3 +1,4 @@
 # COMPONENT: Blink>WebRTC
 # WPT-NOTIFY: true
+hbos@chromium.org
 hta@chromium.org
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
index ab7e6cd..6543f47 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
@@ -5,7 +5,7 @@
 PASS insertDTMF() with duration less than 40 should be clamped to 40
 PASS insertDTMF() with interToneGap less than 30 should be clamped to 30
 PASS insertDTMF with comma should delay next tonechange event for a constant 2000ms
-FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_unreached: Unexpected promise rejection: TypeError: pc.getTransceivers is not a function Reached unreachable code
+FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_unreached: Unexpected promise rejection: Error: assert_equals: Expect there to be only one tranceiver in pc expected 1 but got 0 Reached unreachable code
 PASS Calling insertDTMF() in the middle of tonechange events should cause future tonechanges to be updated to new tones
 PASS Calling insertDTMF() multiple times in the middle of tonechange events should cause future tonechanges to be updated the last provided tones
 PASS Calling insertDTMF('') in the middle of tonechange events should stop future tonechange events from firing
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
index 02c650c..e6a5158 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS addTrack when pc is closed should throw InvalidStateError
-FAIL addTrack with single track argument and no stream should succeed promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
+FAIL addTrack with single track argument and no stream should succeed assert_equals: Expect only one transceiver with sender added expected 1 but got 0
 PASS addTrack with single track argument and single stream should succeed
 FAIL addTrack with single track argument and multiple streams should succeed promise_test: Unhandled rejection with value: object "NotSupportedError: Failed to execute 'addTrack' on 'RTCPeerConnection': Adding a track to multiple streams is not supported."
 PASS Adding the same track multiple times should throw InvalidAccessError
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
index a4f81da..1904ff4 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
@@ -1,18 +1,18 @@
 This is a testharness.js-based test.
-FAIL createOffer() with offerToReceiveAudio set to false should not create a transceiver promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
+PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
 FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" pc.addTransceiver is not a function
-FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL createOffer() with offerToReceiveVideo set to false should not create a transceiver promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
+FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
 FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" pc.addTransceiver is not a function
-FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
-FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
+FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-getTransceivers-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-getTransceivers-expected.txt
deleted file mode 100644
index bc1f216..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-getTransceivers-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL Initial peer connection should have list of zero senders, receivers and transceivers assert_idl_attribute: property "getTransceivers" not found in prototype chain
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
index 0fa3558..7c8c39c 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
-FAIL setRemoteDescription should trigger ontrack event when the MSID of the stream is is parsed. RTCRtpTransceiver is not defined
+FAIL setRemoteDescription should trigger ontrack event when the MSID of the stream is is parsed. assert_true: Expect trackEvent.transceiver to be defined and is instance of RTCRtpTransceiver expected true got false
 PASS setRemoteDescription() with m= line of recvonly direction should not trigger track event
-FAIL addTrack() should cause remote connection to fire ontrack when setRemoteDescription() RTCRtpTransceiver is not defined
+FAIL addTrack() should cause remote connection to fire ontrack when setRemoteDescription() assert_true: Expect trackEvent.transceiver to be defined and is instance of RTCRtpTransceiver expected true got false
 FAIL addTransceiver('video') should cause remote connection to fire ontrack when setRemoteDescription() pc1.addTransceiver is not a function
 FAIL addTransceiver() with inactive direction should not cause remote connection to fire ontrack when setRemoteDescription() pc1.addTransceiver is not a function
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open-expected.txt b/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open-expected.txt
index fd293e75..35ba814 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open-expected.txt
@@ -1,5 +1,5 @@
 PASS popupWindow.innerWidth is 100
-PASS popupWindow.innerWidth is 150
+FAIL Popup wasn't resized.
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open.html b/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open.html
index bdc5fdac..29f4e3a 100644
--- a/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open.html
+++ b/third_party/WebKit/LayoutTests/fast/forms/select-popup/popup-menu-resize-after-open.html
@@ -24,6 +24,10 @@
 openPicker(menu, function() {
     shouldBe('popupWindow.innerWidth', '100');
     popupWindow.addEventListener("resize", checkPopupWidth, false);
+    setTimeout(function() {
+        testFailed('Popup wasn\'t resized.');
+        finishJSTest();
+    }, 3000);
     menu.style.width = "150px";
     menu.offsetTop;
 }, openPickerErrorCallback);
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-fixed-scrolling-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-fixed-scrolling-expected.png
index aee31e9..0dd049d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-fixed-scrolling-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-fixed-scrolling-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-opacity-overlay-expected.png b/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-opacity-overlay-expected.png
index afe0a12f..7a93399 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-opacity-overlay-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/compositing/geometry/video-opacity-overlay-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select-popup/popup-menu-resize-after-open-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select-popup/popup-menu-resize-after-open-expected.txt
new file mode 100644
index 0000000..fd293e75
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/forms/select-popup/popup-menu-resize-after-open-expected.txt
@@ -0,0 +1,6 @@
+PASS popupWindow.innerWidth is 100
+PASS popupWindow.innerWidth is 150
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.12/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping-expected.txt
new file mode 100644
index 0000000..8dc474c8
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.12/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping-expected.txt
@@ -0,0 +1,49 @@
+This is a testharness.js-based test.
+PASS # AUDIT TASK RUNNER STARTED.
+PASS > [ref-distance-error] 
+FAIL X new PannerNode(c, {refDistance: -1}) threw "RangeError" instead of RangeError. assert_true: expected true got false
+PASS   new PannerNode(c, {refDistance: 0}) did not throw an exception.
+PASS   new PannerNode(c, {refDistance: 5e-324}) did not throw an exception.
+FAIL X panner.refDistance = -1 threw "RangeError" instead of RangeError. assert_true: expected true got false
+PASS   panner.refDistance = 0 did not throw an exception.
+PASS   panner.refDistance = 5e-324 did not throw an exception.
+FAIL < [ref-distance-error] 2 out of 6 assertions were failed. assert_true: expected true got false
+PASS > [max-distance-error] 
+FAIL X new PannerNode(c, {maxDistance: -1}) threw "RangeError" instead of RangeError. assert_true: expected true got false
+FAIL X new PannerNode(c, {maxDistance: 0}) threw "RangeError" instead of RangeError. assert_true: expected true got false
+PASS   new PannerNode(c, {maxDistance: 5e-324}) did not throw an exception.
+FAIL X panner.maxDistance = -1 threw "RangeError" instead of RangeError. assert_true: expected true got false
+FAIL X panner.maxDistance = 0 threw "RangeError" instead of RangeError. assert_true: expected true got false
+PASS   panner.maxDistance = 5e-324 did not throw an exception.
+FAIL < [max-distance-error] 4 out of 6 assertions were failed. assert_true: expected true got false
+PASS > [min-distance] 
+PASS   Model: linear: Distance (0.01) is outside the range [1, 10000] is equal to true.
+PASS   Test panner output {"distance":0.01,"distanceModel":"linear"} is identical to the array [0,0.20702192187309265,0.4738078713417053,-0.23029834032058716,-0.4195944368839264,-0.025587772950530052,0.042879875749349594,0.45131322741508484,0.15709976851940155,-0.49066805839538574,-0.1654014140367508,0.00021875571110285819,0.25102242827415466,0.4455359876155853,-0.29729729890823364,-0.3819781541824341...].
+PASS   Model: exponential: Distance (0.01) is outside the range [1, 10000] is equal to true.
+PASS   Test panner output {"distance":0.01,"distanceModel":"exponential"} is identical to the array [0,0.20702192187309265,0.4738078713417053,-0.23029834032058716,-0.4195944368839264,-0.025587772950530052,0.042879875749349594,0.45131322741508484,0.15709976851940155,-0.49066805839538574,-0.1654014140367508,0.00021875571110285819,0.25102242827415466,0.4455359876155853,-0.29729729890823364,-0.3819781541824341...].
+PASS   Model: inverse: Distance (0.01) is outside the range [1, 10000] is equal to true.
+PASS   Test panner output {"distance":0.01,"distanceModel":"inverse"} is identical to the array [0,0.20702192187309265,0.4738078713417053,-0.23029834032058716,-0.4195944368839264,-0.025587772950530052,0.042879875749349594,0.45131322741508484,0.15709976851940155,-0.49066805839538574,-0.1654014140367508,0.00021875571110285819,0.25102242827415466,0.4455359876155853,-0.29729729890823364,-0.3819781541824341...].
+PASS   Model: linear: Distance (2) is outside the range [10, 1000] is equal to true.
+PASS   Test panner output {"distance":2,"distanceModel":"linear","maxDistance":1000,"refDistance":10} is identical to the array [0,0.20702192187309265,0.4738078713417053,-0.23029834032058716,-0.4195944368839264,-0.025587772950530052,0.042879875749349594,0.45131322741508484,0.15709976851940155,-0.49066805839538574,-0.1654014140367508,0.00021875571110285819,0.25102242827415466,0.4455359876155853,-0.29729729890823364,-0.3819781541824341...].
+PASS   Model: exponential: Distance (2) is outside the range [10, 1000] is equal to true.
+PASS   Test panner output {"distance":2,"distanceModel":"exponential","maxDistance":1000,"refDistance":10} is identical to the array [0,0.20702192187309265,0.4738078713417053,-0.23029834032058716,-0.4195944368839264,-0.025587772950530052,0.042879875749349594,0.45131322741508484,0.15709976851940155,-0.49066805839538574,-0.1654014140367508,0.00021875571110285819,0.25102242827415466,0.4455359876155853,-0.29729729890823364,-0.3819781541824341...].
+PASS   Model: inverse: Distance (2) is outside the range [10, 1000] is equal to true.
+PASS   Test panner output {"distance":2,"distanceModel":"inverse","maxDistance":1000,"refDistance":10} is identical to the array [0,0.20702192187309265,0.4738078713417053,-0.23029834032058716,-0.4195944368839264,-0.025587772950530052,0.042879875749349594,0.45131322741508484,0.15709976851940155,-0.49066805839538574,-0.1654014140367508,0.00021875571110285819,0.25102242827415466,0.4455359876155853,-0.29729729890823364,-0.3819781541824341...].
+PASS < [min-distance] All assertions passed. (total 12 assertions)
+PASS > [max-distance] 
+PASS   Model: linear: Distance (20000) is outside the range [1, 10000] is equal to true.
+PASS   Test panner output {"distance":20000,"distanceModel":"linear"} is identical to the array [0,0.10351096093654633,0.23690393567085266,-0.11514917016029358,-0.2097972184419632,-0.012793886475265026,0.021439937874674797,0.22565661370754242,0.07854988425970078,-0.24533402919769287,-0.0827007070183754,0.00010937785555142909,0.12551121413707733,0.22276799380779266,-0.14864864945411682,-0.19098907709121704...].
+PASS   Model: exponential: Distance (21000) is outside the range [1, 10000] is equal to true.
+PASS   Test panner output {"distance":21000,"distanceModel":"exponential"} is identical to the array [0,0.001428587012924254,0.0032695848494768143,-0.0015892095398157835,-0.0028954767622053623,-0.00017657240096013993,0.00029589925543405116,0.0031143571250140667,0.0010840913746505976,-0.0033859312534332275,-0.001141378190368414,0.000001509557819190377,0.0017322193598374724,0.003074490465223789,-0.002051546238362789,-0.0026358996983617544...].
+PASS   Model: inverse: Distance (23000) is outside the range [1, 10000] is equal to true.
+PASS   Test panner output {"distance":23000,"distanceModel":"inverse"} is identical to the array [0,0.000018001124772126786,0.00004119889490539208,-0.00002002507244469598,-0.0000364848856406752,-0.0000022249271296459483,0.0000037285228700056905,0.0000392429246858228,0.00001366025571769569,-0.00004266493488103151,-0.000014382106201082934,1.902140844833866e-8,0.00002182708885811735,0.00003874057802022435,-0.00002585081529105082,-0.00003321404801681638...].
+PASS   Model: linear: Distance (5000) is outside the range [10, 1000] is equal to true.
+PASS   Test panner output {"distance":5000,"distanceModel":"linear","maxDistance":1000,"refDistance":10} is identical to the array [0,0.10351096093654633,0.23690393567085266,-0.11514917016029358,-0.2097972184419632,-0.012793886475265026,0.021439937874674797,0.22565661370754242,0.07854988425970078,-0.24533402919769287,-0.0827007070183754,0.00010937785555142909,0.12551121413707733,0.22276799380779266,-0.14864864945411682,-0.19098907709121704...].
+PASS   Model: inverse: Distance (5000) is outside the range [10, 1000] is equal to true.
+PASS   Test panner output {"distance":5000,"distanceModel":"inverse","maxDistance":1000,"refDistance":10} is identical to the array [0,0.0008264348143711686,0.0018914486281573772,-0.0009193546720780432,-0.0016750276554375887,-0.00010214679787168279,0.00017117714742198586,0.0018016495741903782,0.0006271447637118399,-0.001958754612132907,-0.0006602850626222789,8.732762921681569e-7,0.001002085511572659,0.0017785867676138878,-0.0011868155561387539,-0.0015248628333210945...].
+PASS   Model: exponential: Distance (5000) is outside the range [10, 1000] is equal to true.
+PASS   Test panner output {"distance":5000,"distanceModel":"exponential","maxDistance":1000,"refDistance":10} is identical to the array [0,0.009258301928639412,0.021189333871006966,-0.010299255140125751,-0.018764834851026535,-0.0011443200055509806,0.0019176463829353452,0.020183341577649117,0.007025715429335833,-0.021943343803286552,-0.007396976463496685,0.000009783053428691346,0.011226064525544643,0.01992497593164444,-0.013295539654791355,-0.017082583159208298...].
+PASS < [max-distance] All assertions passed. (total 12 assertions)
+FAIL # AUDIT TASK RUNNER FINISHED: 2 out of 4 tasks were failed. assert_true: expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/geometry/video-fixed-scrolling-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/geometry/video-fixed-scrolling-expected.png
deleted file mode 100644
index ddbbda9..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/geometry/video-fixed-scrolling-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/geometry/video-opacity-overlay-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/geometry/video-opacity-overlay-expected.png
deleted file mode 100644
index 1e784e4..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/geometry/video-opacity-overlay-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/visibility/visibility-simple-video-layer-expected.png
deleted file mode 100644
index a5e147f..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/compositing/visibility/visibility-simple-video-layer-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping-expected.txt b/third_party/WebKit/LayoutTests/platform/mac-retina/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping-expected.txt
deleted file mode 100644
index b4d9e25f..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/external/wpt/webaudio/the-audio-api/the-pannernode-interface/panner-distance-clamping-expected.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-This is a testharness.js-based test.
-PASS # AUDIT TASK RUNNER STARTED.
-PASS > [ref-distance-error] 
-FAIL X new PannerNode(c, {refDistance: -1}) threw "RangeError" instead of RangeError. assert_true: expected true got false
-PASS   new PannerNode(c, {refDistance: 0}) did not throw an exception.
-PASS   new PannerNode(c, {refDistance: 5e-324}) did not throw an exception.
-FAIL X panner.refDistance = -1 threw "RangeError" instead of RangeError. assert_true: expected true got false
-PASS   panner.refDistance = 0 did not throw an exception.
-PASS   panner.refDistance = 5e-324 did not throw an exception.
-FAIL < [ref-distance-error] 2 out of 6 assertions were failed. assert_true: expected true got false
-PASS > [max-distance-error] 
-FAIL X new PannerNode(c, {maxDistance: -1}) threw "RangeError" instead of RangeError. assert_true: expected true got false
-FAIL X new PannerNode(c, {maxDistance: 0}) threw "RangeError" instead of RangeError. assert_true: expected true got false
-PASS   new PannerNode(c, {maxDistance: 5e-324}) did not throw an exception.
-FAIL X panner.maxDistance = -1 threw "RangeError" instead of RangeError. assert_true: expected true got false
-FAIL X panner.maxDistance = 0 threw "RangeError" instead of RangeError. assert_true: expected true got false
-PASS   panner.maxDistance = 5e-324 did not throw an exception.
-FAIL < [max-distance-error] 4 out of 6 assertions were failed. assert_true: expected true got false
-PASS > [min-distance] 
-PASS   Model: linear: Distance (0.01) is outside the range [1, 10000] is equal to true.
-PASS   Test panner output {"distance":0.01,"distanceModel":"linear"} is identical to the array [0,0.20702196657657623,0.4738079607486725,-0.23029832541942596,-0.41959449648857117,-0.025587784126400948,0.042879894375801086,0.4513133466243744,0.15709976851940155,-0.4906681776046753,-0.16540144383907318,0.0002187670033890754,0.25102245807647705,0.4455360770225525,-0.29729732871055603,-0.38197818398475647...].
-PASS   Model: exponential: Distance (0.01) is outside the range [1, 10000] is equal to true.
-PASS   Test panner output {"distance":0.01,"distanceModel":"exponential"} is identical to the array [0,0.20702196657657623,0.4738079607486725,-0.23029832541942596,-0.41959449648857117,-0.025587784126400948,0.042879894375801086,0.4513133466243744,0.15709976851940155,-0.4906681776046753,-0.16540144383907318,0.0002187670033890754,0.25102245807647705,0.4455360770225525,-0.29729732871055603,-0.38197818398475647...].
-PASS   Model: inverse: Distance (0.01) is outside the range [1, 10000] is equal to true.
-PASS   Test panner output {"distance":0.01,"distanceModel":"inverse"} is identical to the array [0,0.20702196657657623,0.4738079607486725,-0.23029832541942596,-0.41959449648857117,-0.025587784126400948,0.042879894375801086,0.4513133466243744,0.15709976851940155,-0.4906681776046753,-0.16540144383907318,0.0002187670033890754,0.25102245807647705,0.4455360770225525,-0.29729732871055603,-0.38197818398475647...].
-PASS   Model: linear: Distance (2) is outside the range [10, 1000] is equal to true.
-PASS   Test panner output {"distance":2,"distanceModel":"linear","maxDistance":1000,"refDistance":10} is identical to the array [0,0.20702196657657623,0.4738079607486725,-0.23029832541942596,-0.41959449648857117,-0.025587784126400948,0.042879894375801086,0.4513133466243744,0.15709976851940155,-0.4906681776046753,-0.16540144383907318,0.0002187670033890754,0.25102245807647705,0.4455360770225525,-0.29729732871055603,-0.38197818398475647...].
-PASS   Model: exponential: Distance (2) is outside the range [10, 1000] is equal to true.
-PASS   Test panner output {"distance":2,"distanceModel":"exponential","maxDistance":1000,"refDistance":10} is identical to the array [0,0.20702196657657623,0.4738079607486725,-0.23029832541942596,-0.41959449648857117,-0.025587784126400948,0.042879894375801086,0.4513133466243744,0.15709976851940155,-0.4906681776046753,-0.16540144383907318,0.0002187670033890754,0.25102245807647705,0.4455360770225525,-0.29729732871055603,-0.38197818398475647...].
-PASS   Model: inverse: Distance (2) is outside the range [10, 1000] is equal to true.
-PASS   Test panner output {"distance":2,"distanceModel":"inverse","maxDistance":1000,"refDistance":10} is identical to the array [0,0.20702196657657623,0.4738079607486725,-0.23029832541942596,-0.41959449648857117,-0.025587784126400948,0.042879894375801086,0.4513133466243744,0.15709976851940155,-0.4906681776046753,-0.16540144383907318,0.0002187670033890754,0.25102245807647705,0.4455360770225525,-0.29729732871055603,-0.38197818398475647...].
-PASS < [min-distance] All assertions passed. (total 12 assertions)
-PASS > [max-distance] 
-PASS   Model: linear: Distance (20000) is outside the range [1, 10000] is equal to true.
-PASS   Test panner output {"distance":20000,"distanceModel":"linear"} is identical to the array [0,0.10351098328828812,0.23690398037433624,-0.11514916270971298,-0.20979724824428558,-0.012793892063200474,0.021439947187900543,0.2256566733121872,0.07854988425970078,-0.24533408880233765,-0.08270072191953659,0.0001093835016945377,0.12551122903823853,0.22276803851127625,-0.14864866435527802,-0.19098909199237823...].
-PASS   Model: exponential: Distance (21000) is outside the range [1, 10000] is equal to true.
-PASS   Test panner output {"distance":21000,"distanceModel":"exponential"} is identical to the array [0,0.0014285872457548976,0.0032695855479687452,-0.0015892094234004617,-0.002895476995036006,-0.0001765724882716313,0.000295899371849373,0.0031143580563366413,0.0010840913746505976,-0.003385932184755802,-0.0011413784231990576,0.0000015096356946742162,0.0017322194762527943,0.0030744909308850765,-0.002051546471193433,-0.002635899931192398...].
-PASS   Model: inverse: Distance (23000) is outside the range [1, 10000] is equal to true.
-PASS   Test panner output {"distance":23000,"distanceModel":"inverse"} is identical to the array [0,0.000018001128410105594,0.000041198902181349695,-0.000020025070625706576,-0.000036484892916632816,-0.00000222492803914065,0.0000037285244616214186,0.000039242931961780414,0.00001366025571769569,-0.00004266494215698913,-0.000014382108929567039,1.902239077367085e-8,0.000021827090677106753,0.00003874058529618196,-0.00002585081892902963,-0.000033214051654795185...].
-PASS   Model: linear: Distance (5000) is outside the range [10, 1000] is equal to true.
-PASS   Test panner output {"distance":5000,"distanceModel":"linear","maxDistance":1000,"refDistance":10} is identical to the array [0,0.10351098328828812,0.23690398037433624,-0.11514916270971298,-0.20979724824428558,-0.012793892063200474,0.021439947187900543,0.2256566733121872,0.07854988425970078,-0.24533408880233765,-0.08270072191953659,0.0001093835016945377,0.12551122903823853,0.22276803851127625,-0.14864866435527802,-0.19098909199237823...].
-PASS   Model: exponential: Distance (5000) is outside the range [10, 1000] is equal to true.
-PASS   Test panner output {"distance":5000,"distanceModel":"exponential","maxDistance":1000,"refDistance":10} is identical to the array [0,0.009258303791284561,0.021189337596297264,-0.010299254208803177,-0.018764836713671684,-0.0011443205876275897,0.001917647197842598,0.020183347165584564,0.007025715429335833,-0.021943349391222,-0.007396977860480547,0.00000978355819825083,0.011226066388189793,0.019924979656934738,-0.013295541517436504,-0.017082585021853447...].
-PASS   Model: inverse: Distance (5000) is outside the range [10, 1000] is equal to true.
-PASS   Test panner output {"distance":5000,"distanceModel":"inverse","maxDistance":1000,"refDistance":10} is identical to the array [0,0.0008264349889941514,0.0018914489774033427,-0.0009193546138703823,-0.0016750278882682323,-0.00010214684152742848,0.000171177220181562,0.0018016500398516655,0.0006271447637118399,-0.001958755310624838,-0.0006602851790376008,8.733213689993136e-7,0.0010020856279879808,0.0017785871168598533,-0.0011868156725540757,-0.0015248629497364163...].
-PASS < [max-distance] All assertions passed. (total 12 assertions)
-FAIL # AUDIT TASK RUNNER FINISHED: 2 out of 4 tasks were failed. assert_true: expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/media/color-profile-video-seek-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/media/color-profile-video-seek-object-fit-expected.png
deleted file mode 100644
index ed5a172f..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/media/color-profile-video-seek-object-fit-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png
deleted file mode 100644
index a5e147f..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/threaded/compositing/visibility/visibility-simple-video-layer-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/video-surface-layer/media/color-profile-video-seek-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/video-surface-layer/media/color-profile-video-seek-object-fit-expected.png
deleted file mode 100644
index ed5a172f..0000000
--- a/third_party/WebKit/LayoutTests/platform/mac-retina/virtual/video-surface-layer/media/color-profile-video-seek-object-fit-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-fixed-scrolling-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-fixed-scrolling-expected.png
index 25267c40..ddbbda9 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-fixed-scrolling-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-fixed-scrolling-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-opacity-overlay-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-opacity-overlay-expected.png
index 8f9f1ff..1e784e4 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-opacity-overlay-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/geometry/video-opacity-overlay-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/WebKit/LayoutTests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png
index 9f5fb0a..a5e147f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/compositing/visibility/visibility-simple-video-layer-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-object-fit-expected.png
index 38cad4d..ed5a172f 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-object-fit-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/media/color-profile-video-seek-object-fit-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-fixed-scrolling-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-fixed-scrolling-expected.png
index 8706c32..c96560a 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-fixed-scrolling-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-fixed-scrolling-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-opacity-overlay-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-opacity-overlay-expected.png
index 2c183da6..d25d7f6 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-opacity-overlay-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/geometry/video-opacity-overlay-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png b/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png
index f4a325b..38543cc 100644
--- a/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/compositing/visibility/visibility-simple-video-layer-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-object-fit-expected.png b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-object-fit-expected.png
index 406f546..dffb76a 100644
--- a/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-object-fit-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/media/color-profile-video-seek-object-fit-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 969d845..368b54163 100644
--- a/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4808,6 +4808,7 @@
     method getRemoteStreams
     method getSenders
     method getStats
+    method getTransceivers
     method removeStream
     method removeTrack
     method setConfiguration
@@ -4846,6 +4847,16 @@
     method getStats
     method replaceTrack
     method setParameters
+interface RTCRtpTransceiver
+    attribute @@toStringTag
+    getter currentDirection
+    getter direction
+    getter mid
+    getter receiver
+    getter sender
+    getter stopped
+    method constructor
+    setter direction
 interface RTCSessionDescription
     attribute @@toStringTag
     getter sdp
@@ -4870,6 +4881,7 @@
     getter receiver
     getter streams
     getter track
+    getter transceiver
     method constructor
 interface RadioNodeList : NodeList
     attribute @@toStringTag
@@ -9166,6 +9178,7 @@
     method getRemoteStreams
     method getSenders
     method getStats
+    method getTransceivers
     method removeStream
     method removeTrack
     method setConfiguration
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
new file mode 100644
index 0000000..4deba74
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+Harness Error. harness_status.status = 1 , harness_status.message = Uncaught TypeError: transceiver.stop is not a function
+PASS insertDTMF() with default duration and intertoneGap should fire tonechange events at the expected time
+PASS insertDTMF() with explicit duration and intertoneGap should fire tonechange events at the expected time
+FAIL insertDTMF('') should not fire any tonechange event, including for '' tone assert_unreached: Expect no tonechange event to be fired Reached unreachable code
+PASS insertDTMF() with duration less than 40 should be clamped to 40
+PASS insertDTMF() with interToneGap less than 30 should be clamped to 30
+PASS insertDTMF with comma should delay next tonechange event for a constant 2000ms
+FAIL insertDTMF() with transceiver stopped in the middle should stop future tonechange events from firing assert_greater_than: More tonechange event is fired than expected expected a number greater than 0 but got 0
+PASS Calling insertDTMF() in the middle of tonechange events should cause future tonechanges to be updated to new tones
+PASS Calling insertDTMF() multiple times in the middle of tonechange events should cause future tonechanges to be updated the last provided tones
+PASS Calling insertDTMF('') in the middle of tonechange events should stop future tonechange events from firing
+FAIL Setting transceiver.currentDirection to recvonly in the middle of tonechange events should stop future tonechange events from firing pc.addTransceiver is not a function
+PASS Tone change event constructor works
+PASS Tone change event with unexpected name should not crash
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
index 5c1d3925..9ae3282 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-addTrack.https-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS addTrack when pc is closed should throw InvalidStateError
-FAIL addTrack with single track argument and no stream should succeed promise_test: Unhandled rejection with value: object "TypeError: pc.getTransceivers is not a function"
+PASS addTrack with single track argument and no stream should succeed
 PASS addTrack with single track argument and single stream should succeed
 PASS addTrack with single track argument and multiple streams should succeed
 PASS Adding the same track multiple times should throw InvalidAccessError
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
new file mode 100644
index 0000000..561ec28
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-createOffer-offerToReceive-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+PASS createOffer() with offerToReceiveAudio set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveAudio should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveAudio option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS offerToReceiveAudio option should be ignored if a non-stopped "sendrecv" transceiver exists
+FAIL offerToReceiveAudio set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveAudio set to false with a "recvonly" transceiver should change the direction to "inactive" pc.addTransceiver is not a function
+FAIL subsequent offerToReceiveAudio set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+PASS createOffer() with offerToReceiveVideo set to false should not create a transceiver
+FAIL createOffer() with offerToReceiveVideo should create a "recvonly" transceiver assert_equals: Expect pc to have one transceiver expected 1 but got 0
+FAIL offerToReceiveVideo option should be ignored if a non-stopped "recvonly" transceiver exists assert_equals: Expect pc to have one transceiver expected 1 but got 0
+PASS offerToReceiveVideo option should be ignored if a non-stopped "sendrecv" transceiver exists
+FAIL offerToReceiveVideo set to false with a track should create a "sendonly" transceiver assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveVideo set to false with a "recvonly" transceiver should change the direction to "inactive" pc.addTransceiver is not a function
+FAIL subsequent offerToReceiveVideo set to false with a track should change the direction to "sendonly" assert_equals: Expect transceiver to have "sendonly" direction expected "sendonly" but got "sendrecv"
+FAIL offerToReceiveAudio and Video should create two "recvonly" transceivers assert_equals: Expect pc to have two transceivers expected 2 but got 0
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-getTransceivers-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-getTransceivers-expected.txt
new file mode 100644
index 0000000..1928976
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-getTransceivers-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+PASS Initial peer connection should have list of zero senders, receivers and transceivers
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
index 866ef108..518b5f6 100644
--- a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-ontrack.https-expected.txt
@@ -1,7 +1,7 @@
 This is a testharness.js-based test.
-FAIL setRemoteDescription should trigger ontrack event when the MSID of the stream is is parsed. RTCRtpTransceiver is not defined
-FAIL setRemoteDescription() with m= line of recvonly direction should not trigger track event assert_unreached: ontrack event should not fire for track with recvonly direction Reached unreachable code
-FAIL addTrack() should cause remote connection to fire ontrack when setRemoteDescription() RTCRtpTransceiver is not defined
+PASS setRemoteDescription should trigger ontrack event when the MSID of the stream is is parsed.
+PASS setRemoteDescription() with m= line of recvonly direction should not trigger track event
+PASS addTrack() should cause remote connection to fire ontrack when setRemoteDescription()
 FAIL addTransceiver('video') should cause remote connection to fire ontrack when setRemoteDescription() pc1.addTransceiver is not a function
 FAIL addTransceiver() with inactive direction should not cause remote connection to fire ontrack when setRemoteDescription() pc1.addTransceiver is not a function
 Harness: the test ran to completion.
diff --git a/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
new file mode 100644
index 0000000..0505a96f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+PASS addTrack() with a track and no stream makes ontrack fire with a track and no stream.
+PASS addTrack() with a track and a stream makes ontrack fire with a track and a stream.
+PASS ontrack fires before setRemoteDescription resolves.
+PASS addTrack() with two tracks and one stream makes ontrack fire twice with the tracks and shared stream.
+PASS addTrack() for an existing stream makes stream.onaddtrack fire.
+PASS stream.onaddtrack fires before setRemoteDescription resolves.
+PASS addTrack() with a track and two streams makes ontrack fire with a track and two streams.
+PASS ontrack's receiver matches getReceivers().
+PASS removeTrack() does not remove the receiver.
+PASS removeTrack() makes stream.onremovetrack fire and the track to be removed from the stream.
+PASS stream.onremovetrack fires before setRemoteDescription resolves.
+PASS removeTrack() makes track.onmute fire and the track to be muted.
+PASS track.onmute fires before setRemoteDescription resolves.
+PASS removeTrack() twice is safe.
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
index bfa34c8..6efa627 100644
--- a/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/global-interface-listing-expected.txt
@@ -5450,6 +5450,7 @@
     method getRemoteStreams
     method getSenders
     method getStats
+    method getTransceivers
     method removeStream
     method removeTrack
     method setConfiguration
@@ -5488,6 +5489,16 @@
     method getStats
     method replaceTrack
     method setParameters
+interface RTCRtpTransceiver
+    attribute @@toStringTag
+    getter currentDirection
+    getter direction
+    getter mid
+    getter receiver
+    getter sender
+    getter stopped
+    method constructor
+    setter direction
 interface RTCSessionDescription
     attribute @@toStringTag
     getter sdp
@@ -5512,6 +5523,7 @@
     getter receiver
     getter streams
     getter track
+    getter transceiver
     method constructor
 interface RadioNodeList : NodeList
     attribute @@toStringTag
@@ -10148,6 +10160,7 @@
     method getRemoteStreams
     method getSenders
     method getStats
+    method getTransceivers
     method removeStream
     method removeTrack
     method setConfiguration
diff --git a/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h b/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h
index 7626f89d..0153e960 100644
--- a/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h
+++ b/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h
@@ -45,10 +45,9 @@
 
 class WebURLRequest;
 
-// This interface is implemented by the client and is only called on the main
-// thread. HasControllerServiceWorker() and ControllerServiceWorkerID() are to
-// be implemented only by Frame and SharedWorker's provider as they are needed
-// only for controllee contexts (but not in controller context).
+// This interface is implemented by the embedder and is only called on the main
+// thread. Currently the embedder has implementations for service worker clients
+// (frames and shared workers), and service workers themselves.
 //
 // An instance of this class is owned by the associated loading context, e.g.
 // DocumentLoader.
@@ -56,21 +55,22 @@
  public:
   virtual ~WebServiceWorkerNetworkProvider() = default;
 
-  // A request is about to be sent out, and the client may modify it. Request
-  // is writable, and changes to the URL, for example, will change the request
-  // made.
+  // A request is about to be sent out, and the embedder may modify it. The
+  // request is writable, and changes to the URL, for example, will change the
+  // request made.
   virtual void WillSendRequest(WebURLRequest&) {}
 
   // Returns an identifier of this provider.
   virtual int ProviderID() const { return -1; }
 
+  // For service worker clients.
   virtual blink::mojom::ControllerServiceWorkerMode
   IsControlledByServiceWorker() {
     return blink::mojom::ControllerServiceWorkerMode::kNoController;
   }
 
-  // Returns an identifier of the controller service worker
-  // associated with the WebDocumentLoader.
+  // For service worker clients. Returns an identifier of the controller service
+  // worker associated with the loading context.
   virtual int64_t ControllerServiceWorkerID() { return -1; }
 
   // S13nServiceWorker:
diff --git a/third_party/blink/public/platform/scheduler/test/fake_renderer_scheduler.h b/third_party/blink/public/platform/scheduler/test/fake_renderer_scheduler.h
index 63c5c79b..1c0d566 100644
--- a/third_party/blink/public/platform/scheduler/test/fake_renderer_scheduler.h
+++ b/third_party/blink/public/platform/scheduler/test/fake_renderer_scheduler.h
@@ -50,7 +50,7 @@
   void Shutdown() override;
   void SetTopLevelBlameContext(
       base::trace_event::BlameContext* blame_context) override;
-  void SetRAILModeObserver(RAILModeObserver* observer) override;
+  void AddRAILModeObserver(RAILModeObserver* observer) override;
   void SetRendererProcessType(RendererProcessType type) override;
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
       const char* name,
diff --git a/third_party/blink/public/platform/scheduler/test/mock_renderer_scheduler.h b/third_party/blink/public/platform/scheduler/test/mock_renderer_scheduler.h
index 6e32cb337..46ffd66 100644
--- a/third_party/blink/public/platform/scheduler/test/mock_renderer_scheduler.h
+++ b/third_party/blink/public/platform/scheduler/test/mock_renderer_scheduler.h
@@ -60,7 +60,7 @@
   MOCK_METHOD0(VirtualTimePaused, void());
   MOCK_METHOD0(VirtualTimeResumed, void());
   MOCK_METHOD1(SetTopLevelBlameContext, void(base::trace_event::BlameContext*));
-  MOCK_METHOD1(SetRAILModeObserver, void(RAILModeObserver*));
+  MOCK_METHOD1(AddRAILModeObserver, void(RAILModeObserver*));
   MOCK_METHOD1(SetRendererProcessType, void(RendererProcessType));
   MOCK_METHOD2(CreateWebScopedVirtualTimePauser,
                WebScopedVirtualTimePauser(
diff --git a/third_party/blink/public/platform/scheduler/web_thread_scheduler.h b/third_party/blink/public/platform/scheduler/web_thread_scheduler.h
index 6652f38..aa6b2f06 100644
--- a/third_party/blink/public/platform/scheduler/web_thread_scheduler.h
+++ b/third_party/blink/public/platform/scheduler/web_thread_scheduler.h
@@ -211,7 +211,7 @@
   // called on the main thread and must outlive this class.
   // [1]
   // https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail
-  virtual void SetRAILModeObserver(RAILModeObserver* observer);
+  virtual void AddRAILModeObserver(RAILModeObserver* observer);
 
   // Sets the kind of renderer process. Should be called on the main thread
   // once.
diff --git a/third_party/blink/public/platform/web_media_constraints.h b/third_party/blink/public/platform/web_media_constraints.h
index 432b927..d227066 100644
--- a/third_party/blink/public/platform/web_media_constraints.h
+++ b/third_party/blink/public/platform/web_media_constraints.h
@@ -43,6 +43,7 @@
 
 // Possible values of the echo canceller type constraint.
 BLINK_PLATFORM_EXPORT extern const char kEchoCancellationTypeBrowser[];
+BLINK_PLATFORM_EXPORT extern const char kEchoCancellationTypeAec3[];
 BLINK_PLATFORM_EXPORT extern const char kEchoCancellationTypeSystem[];
 
 class WebMediaConstraintsPrivate;
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 fe73a35..6c199fe2 100644
--- a/third_party/blink/public/platform/web_media_stream_source.h
+++ b/third_party/blink/public/platform/web_media_stream_source.h
@@ -74,7 +74,7 @@
     kReadyStateEnded = 2
   };
 
-  enum class EchoCancellationMode { kDisabled, kSoftware, kHardware };
+  enum class EchoCancellationMode { kDisabled, kBrowser, kAec3, kSystem };
 
   struct Capabilities {
     // WebVector is used to store an optional range for the below numeric
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 4f58990..895bd6cb 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -72,9 +72,11 @@
 
   BLINK_PLATFORM_EXPORT static void EnableAccelerated2dCanvas(bool);
   BLINK_PLATFORM_EXPORT static void EnableAccessibilityObjectModel(bool);
+  BLINK_PLATFORM_EXPORT static void EnableAdTagging(bool);
   BLINK_PLATFORM_EXPORT static void EnableAllowActivationDelegationAttr(bool);
   BLINK_PLATFORM_EXPORT static void EnableAudioOutputDevices(bool);
   BLINK_PLATFORM_EXPORT static void EnableBlinkHeapIncrementalMarking(bool);
+  BLINK_PLATFORM_EXPORT static void EnableBloatedRendererDetection(bool);
   BLINK_PLATFORM_EXPORT static void EnableCacheInlineScriptCode(bool);
   BLINK_PLATFORM_EXPORT static void EnableCanvas2dImageChromium(bool);
   BLINK_PLATFORM_EXPORT static void EnableCSSHexAlphaColor(bool);
diff --git a/third_party/blink/renderer/core/css/threaded/font_object_threaded_test.cc b/third_party/blink/renderer/core/css/threaded/font_object_threaded_test.cc
index 3b0c6b27..c335197 100644
--- a/third_party/blink/renderer/core/css/threaded/font_object_threaded_test.cc
+++ b/third_party/blink/renderer/core/css/threaded/font_object_threaded_test.cc
@@ -15,7 +15,7 @@
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
 #include "third_party/blink/renderer/platform/language.h"
 #include "third_party/blink/renderer/platform/testing/font_test_helpers.h"
diff --git a/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc b/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc
index d911f47..93c338284 100644
--- a/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc
+++ b/third_party/blink/renderer/core/css/threaded/text_renderer_threaded_test.cc
@@ -11,7 +11,7 @@
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_selector.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
 #include "third_party/blink/renderer/platform/graphics/test/mock_paint_canvas.h"
 #include "third_party/blink/renderer/platform/language.h"
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 f2834a6..1759da1 100644
--- a/third_party/blink/renderer/core/dom/idle_deadline_test.cc
+++ b/third_party/blink/renderer/core/dom/idle_deadline_test.cc
@@ -51,6 +51,9 @@
   void RemoveTaskObserver(
       base::MessageLoop::TaskObserver* task_observer) override {}
 
+  void AddRAILModeObserver(
+      scheduler::WebThreadScheduler::RAILModeObserver*) override {}
+
   scheduler::NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
     return nullptr;
   }
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 7030478..4e1d93c 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
@@ -57,6 +57,9 @@
   void RemoveTaskObserver(
       base::MessageLoop::TaskObserver* task_observer) override {}
 
+  void AddRAILModeObserver(
+      scheduler::WebThreadScheduler::RAILModeObserver*) override {}
+
   scheduler::NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
     return nullptr;
   }
diff --git a/third_party/blink/renderer/core/frame/ad_tracker_test.cc b/third_party/blink/renderer/core/frame/ad_tracker_test.cc
index 6facd2c..68ca203 100644
--- a/third_party/blink/renderer/core/frame/ad_tracker_test.cc
+++ b/third_party/blink/renderer/core/frame/ad_tracker_test.cc
@@ -288,4 +288,34 @@
   EXPECT_TRUE(local_subframe->IsAdSubframe());
 }
 
+class AdTrackerDisabledSimTest : public SimTest {
+ protected:
+  void SetUp() override {
+    RuntimeEnabledFeatures::SetAdTaggingEnabled(false);
+
+    SimTest::SetUp();
+    main_resource_ = std::make_unique<SimRequest>(
+        "https://example.com/test.html", "text/html");
+
+    LoadURL("https://example.com/test.html");
+    main_resource_->Start();
+  }
+
+  void TearDown() override { SimTest::TearDown(); }
+
+  std::unique_ptr<SimRequest> main_resource_;
+};
+
+TEST_F(AdTrackerDisabledSimTest, ResourceLoadedWhenAdTaggingDisabled) {
+  SimRequest iframe_resource("https://example.com/iframe.html", "text/html");
+
+  main_resource_->Complete(R"HTML(
+    <iframe src=https://example.com/iframe.html></iframe>
+    )HTML");
+
+  iframe_resource.Complete("<body></body>");
+
+  EXPECT_FALSE(GetDocument().GetFrame()->IsAdSubframe());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 9cd22922..464a41e 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -353,7 +353,8 @@
 
   if (IsLocalRoot()) {
     performance_monitor_->Shutdown();
-    ad_tracker_->Shutdown();
+    if (ad_tracker_)
+      ad_tracker_->Shutdown();
   }
   idleness_detector_->Shutdown();
   if (inspector_trace_events_)
@@ -941,10 +942,12 @@
       interface_registry_(interface_registry) {
   if (IsLocalRoot()) {
     probe_sink_ = new CoreProbeSink();
-    ad_tracker_ = new AdTracker(this);
     performance_monitor_ = new PerformanceMonitor(this);
     inspector_trace_events_ = new InspectorTraceEvents();
     probe_sink_->addInspectorTraceEvents(inspector_trace_events_);
+    if (RuntimeEnabledFeatures::AdTaggingEnabled()) {
+      ad_tracker_ = new AdTracker(this);
+    }
   } else {
     // Inertness only needs to be updated if this frame might inherit the
     // inert state from a higher-level frame. If this is an OOPIF local root,
@@ -958,7 +961,11 @@
   idleness_detector_ = new IdlenessDetector(this);
   inspector_task_runner_->InitIsolate(V8PerIsolateData::MainThreadIsolate());
 
-  SetIsAdSubframeIfNecessary();
+  if (ad_tracker_) {
+    SetIsAdSubframeIfNecessary();
+  }
+  DCHECK(ad_tracker_ ? RuntimeEnabledFeatures::AdTaggingEnabled()
+                     : !RuntimeEnabledFeatures::AdTaggingEnabled());
 }
 
 FrameScheduler* LocalFrame::GetFrameScheduler() {
@@ -1262,7 +1269,8 @@
 }
 
 void LocalFrame::SetAdTrackerForTesting(AdTracker* ad_tracker) {
-  ad_tracker_->Shutdown();
+  if (ad_tracker_)
+    ad_tracker_->Shutdown();
   ad_tracker_ = ad_tracker;
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.idl b/third_party/blink/renderer/core/html/forms/html_input_element.idl
index 099fed4b..880bb6a5 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.idl
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.idl
@@ -21,6 +21,9 @@
 
 // https://html.spec.whatwg.org/#the-input-element
 
+// The `URLString` reference below is from Trusted Types:
+// https://github.com/WICG/trusted-types/, which is still WIP.
+// https://crbug.com/739170.
 enum SelectionMode { "select", "start", "end", "preserve" };
 
 [
@@ -58,7 +61,7 @@
     [CEReactions, Reflect] attribute boolean readOnly;
     [CEReactions, Reflect] attribute boolean required;
     [CEReactions, RaisesException=Setter, CustomElementCallbacks] attribute unsigned long size;
-    [CEReactions, Reflect, URL] attribute DOMString src;
+    [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString src;
     [CEReactions, Reflect] attribute DOMString step;
     [CEReactions, CustomElementCallbacks] attribute DOMString type;
     [CEReactions, Reflect=value, CustomElementCallbacks] attribute DOMString defaultValue;
diff --git a/third_party/blink/renderer/core/html/forms/resources/listPicker.js b/third_party/blink/renderer/core/html/forms/resources/listPicker.js
index 6ce6d12..6ed61189 100644
--- a/third_party/blink/renderer/core/html/forms/resources/listPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/listPicker.js
@@ -83,8 +83,12 @@
         this._config.anchorRectInScreen.y !== window.updateData.anchorRectInScreen.y ||
         this._config.anchorRectInScreen.width !== window.updateData.anchorRectInScreen.width ||
         this._config.anchorRectInScreen.height !== window.updateData.anchorRectInScreen.height) {
-      this._config.anchorRectInScreen = window.updateData.anchorRectInScreen;
-      this._fixWindowSize();
+      // TODO(tkent): Don't fix window size here due to a bug of Aura or
+      // compositor. crbug.com/863770
+      if (!navigator.platform.startsWith('Win')) {
+        this._config.anchorRectInScreen = window.updateData.anchorRectInScreen;
+        this._fixWindowSize();
+      }
     }
   }
   delete window.updateData;
diff --git a/third_party/blink/renderer/core/html/html_frame_element.idl b/third_party/blink/renderer/core/html/html_frame_element.idl
index 6b1773a..0950629 100644
--- a/third_party/blink/renderer/core/html/html_frame_element.idl
+++ b/third_party/blink/renderer/core/html/html_frame_element.idl
@@ -19,11 +19,15 @@
  */
 
 // https://html.spec.whatwg.org/#htmlframeelement
+
+// The `URLString` reference below is from Trusted Types:
+// https://github.com/WICG/trusted-types/, which is still WIP.
+// https://crbug.com/739170.
 [HTMLConstructor]
 interface HTMLFrameElement : HTMLElement {
     [CEReactions, Reflect] attribute DOMString name;
     [CEReactions, Reflect] attribute DOMString scrolling;
-    [CEReactions, Reflect, URL] attribute USVString src;
+    [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString src;
     [CEReactions, Reflect] attribute DOMString frameBorder;
     [CEReactions, Reflect, URL] attribute USVString longDesc;
     [CEReactions, Reflect] attribute boolean noResize;
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.idl b/third_party/blink/renderer/core/html/html_iframe_element.idl
index 998559d..c1ff20c 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.idl
+++ b/third_party/blink/renderer/core/html/html_iframe_element.idl
@@ -19,9 +19,13 @@
  */
 
 // https://html.spec.whatwg.org/#the-iframe-element
+
+// The `HTMLString` and `URLString` references below are from Trusted Types:
+// https://github.com/WICG/trusted-types/, which is still WIP.
+// https://crbug.com/739170.
 [HTMLConstructor]
 interface HTMLIFrameElement : HTMLElement {
-    [CEReactions, Reflect, URL] attribute USVString src;
+    [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString src;
     [CEReactions, Reflect, RaisesException=Setter] attribute HTMLString srcdoc;
     [CEReactions, Reflect] attribute DOMString name;
     [PutForwards=value] readonly attribute DOMTokenList sandbox;
diff --git a/third_party/blink/renderer/core/html/html_li_element.cc b/third_party/blink/renderer/core/html/html_li_element.cc
index 56853bd..30f2e50b 100644
--- a/third_party/blink/renderer/core/html/html_li_element.cc
+++ b/third_party/blink/renderer/core/html/html_li_element.cc
@@ -111,7 +111,7 @@
     // inside.  We don't want to change our style to say "inside" since that
     // would affect nested nodes.
     if (!list_node)
-      ordinal->SetNotInList(true);
+      ordinal->SetNotInList(true, *this);
 
     ParseValue(FastGetAttribute(valueAttr), ordinal);
   }
diff --git a/third_party/blink/renderer/core/html/html_link_element.idl b/third_party/blink/renderer/core/html/html_link_element.idl
index 7a6b3b9..0a34d5da 100644
--- a/third_party/blink/renderer/core/html/html_link_element.idl
+++ b/third_party/blink/renderer/core/html/html_link_element.idl
@@ -25,7 +25,7 @@
     // FIXME: The disabled attribute has been removed from the spec:
     // https://www.w3.org/Bugs/Public/show_bug.cgi?id=14703
     [Reflect, Measure] attribute boolean disabled;
-    [Reflect, URL] attribute USVString href;
+    [Reflect, URL, RaisesException=Setter] attribute URLString href;
     [CEReactions, Reflect, ReflectOnly=("anonymous","use-credentials"), ReflectEmpty="anonymous", ReflectInvalid="anonymous"] attribute DOMString? crossOrigin;
     [CEReactions, Reflect] attribute DOMString rel;
     [SameObject, PutForwards=value] readonly attribute DOMTokenList relList;
diff --git a/third_party/blink/renderer/core/html/html_object_element.idl b/third_party/blink/renderer/core/html/html_object_element.idl
index aad36a2..bd6d6db 100644
--- a/third_party/blink/renderer/core/html/html_object_element.idl
+++ b/third_party/blink/renderer/core/html/html_object_element.idl
@@ -26,7 +26,7 @@
     ActiveScriptWrappable,
     HTMLConstructor
 ] interface HTMLObjectElement : HTMLElement {
-    [CEReactions, Reflect, URL] attribute DOMString data;
+    [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString data;
     [CEReactions, Reflect] attribute DOMString type;
     // TODO(foolip): attribute boolean typeMustMatch;
     [CEReactions, Reflect] attribute DOMString name;
@@ -55,7 +55,7 @@
     [CEReactions, Reflect] attribute unsigned long hspace;
     [CEReactions, Reflect] attribute DOMString standby;
     [CEReactions, Reflect] attribute unsigned long vspace;
-    [CEReactions, Reflect, URL] attribute DOMString codeBase;
+    [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString codeBase;
     [CEReactions, Reflect] attribute DOMString codeType;
 
     [CEReactions, Reflect] attribute [TreatNullAs=EmptyString] DOMString border;
diff --git a/third_party/blink/renderer/core/html/list_item_ordinal.cc b/third_party/blink/renderer/core/html/list_item_ordinal.cc
index 4b051a6a..1949d8b3 100644
--- a/third_party/blink/renderer/core/html/list_item_ordinal.cc
+++ b/third_party/blink/renderer/core/html/list_item_ordinal.cc
@@ -34,7 +34,8 @@
 
 namespace blink {
 
-ListItemOrdinal::ListItemOrdinal() : type_(kNeedsUpdate), not_in_list_(false) {}
+ListItemOrdinal::ListItemOrdinal()
+    : type_(kNeedsUpdate), not_in_list_(false), not_in_list_changed_(false) {}
 
 bool ListItemOrdinal::IsList(const Node& node) {
   return IsHTMLUListElement(node) || IsHTMLOListElement(node);
@@ -240,8 +241,19 @@
   InvalidateAfter(EnclosingList(&item_node), &item_node);
 }
 
-void ListItemOrdinal::SetNotInList(bool not_in_list) {
+void ListItemOrdinal::SetNotInList(bool not_in_list, const Node& item_node) {
+  if (not_in_list_ == not_in_list)
+    return;
+
   not_in_list_ = not_in_list;
+  SetNotInListChanged(true);
+  LayoutObject* layout_object = item_node.GetLayoutObject();
+  if (layout_object->IsLayoutNGListItem())
+    layout_object->NotifyOfSubtreeChange();
+}
+
+void ListItemOrdinal::SetNotInListChanged(bool changed) {
+  not_in_list_changed_ = changed;
 }
 
 unsigned ListItemOrdinal::ItemCountForOrderedList(
diff --git a/third_party/blink/renderer/core/html/list_item_ordinal.h b/third_party/blink/renderer/core/html/list_item_ordinal.h
index 6e16e75..299c837 100644
--- a/third_party/blink/renderer/core/html/list_item_ordinal.h
+++ b/third_party/blink/renderer/core/html/list_item_ordinal.h
@@ -63,7 +63,9 @@
 
   // Get/set whether this item is in a list or not.
   bool NotInList() const { return not_in_list_; }
-  void SetNotInList(bool);
+  void SetNotInList(bool, const Node&);
+  bool NotInListChanged() const { return not_in_list_changed_; }
+  void SetNotInListChanged(bool);
 
   static bool IsList(const Node&);
   static bool IsListItem(const Node&);
@@ -110,6 +112,7 @@
   mutable int value_ = 0;
   mutable unsigned type_ : 2;  // ValueType
   unsigned not_in_list_ : 1;
+  unsigned not_in_list_changed_ : 1;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/track/html_track_element.idl b/third_party/blink/renderer/core/html/track/html_track_element.idl
index 92638564..bcafa67 100644
--- a/third_party/blink/renderer/core/html/track/html_track_element.idl
+++ b/third_party/blink/renderer/core/html/track/html_track_element.idl
@@ -24,10 +24,14 @@
  */
 
 // https://html.spec.whatwg.org/#the-track-element
+
+// The `URLString` reference below is from Trusted Types:
+// https://github.com/WICG/trusted-types/, which is still WIP.
+// https://crbug.com/739170.
 [HTMLConstructor]
 interface HTMLTrackElement : HTMLElement {
     [CEReactions] attribute DOMString kind;
-    [CEReactions, Reflect, URL] attribute DOMString src;
+    [CEReactions, Reflect, URL, RaisesException=Setter] attribute URLString src;
     [CEReactions, Reflect] attribute DOMString srclang;
     [CEReactions, Reflect] attribute DOMString label;
     [CEReactions, Reflect] attribute boolean default;
diff --git a/third_party/blink/renderer/core/layout/layout_embedded_content.cc b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
index 9b1a6882..b5991e0 100644
--- a/third_party/blink/renderer/core/layout/layout_embedded_content.cc
+++ b/third_party/blink/renderer/core/layout/layout_embedded_content.cc
@@ -292,16 +292,19 @@
 }
 
 LayoutRect LayoutEmbeddedContent::ReplacedContentRect() const {
-  LayoutRect content_rect = ContentBoxRect();
-  // IFrames set as the root scroller should get their size from their parent.
-  if (ChildFrameView() && View() && RootScrollerUtil::IsEffective(*this))
-    content_rect = LayoutRect(LayoutPoint(), View()->ViewRect().Size());
-
   // We don't propagate sub-pixel into sub-frame layout, in other words, the
   // rect is snapped at the document boundary, and sub-pixel movement could
   // cause the sub-frame to layout due to the 1px snap difference. In order to
   // avoid that, the size of sub-frame is rounded in advance.
-  return PreSnappedRectForPersistentSizing(content_rect);
+  LayoutRect size_rounded_rect = ContentBoxRect();
+
+  // IFrames set as the root scroller should get their size from their parent.
+  if (ChildFrameView() && View() && RootScrollerUtil::IsEffective(*this))
+    size_rounded_rect = LayoutRect(LayoutPoint(), View()->ViewRect().Size());
+
+  size_rounded_rect.SetSize(
+      LayoutSize(RoundedIntSize(size_rounded_rect.Size())));
+  return size_rounded_rect;
 }
 
 void LayoutEmbeddedContent::UpdateOnEmbeddedContentViewChange() {
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.cc b/third_party/blink/renderer/core/layout/layout_replaced.cc
index 34f4b1e..f61b8984 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.cc
+++ b/third_party/blink/renderer/core/layout/layout_replaced.cc
@@ -636,11 +636,6 @@
   return ComputeObjectFit();
 }
 
-LayoutRect LayoutReplaced::PreSnappedRectForPersistentSizing(LayoutRect rect) {
-  rect.SetSize(LayoutSize(RoundedIntSize(rect.Size())));
-  return rect;
-}
-
 void LayoutReplaced::ComputeIntrinsicSizingInfo(
     IntrinsicSizingInfo& intrinsic_sizing_info) const {
   if (ShouldApplySizeContainment()) {
diff --git a/third_party/blink/renderer/core/layout/layout_replaced.h b/third_party/blink/renderer/core/layout/layout_replaced.h
index 5074401..6d403787 100644
--- a/third_party/blink/renderer/core/layout/layout_replaced.h
+++ b/third_party/blink/renderer/core/layout/layout_replaced.h
@@ -63,12 +63,6 @@
   // This function returns the local rect of the replaced content.
   virtual LayoutRect ReplacedContentRect() const;
 
-  // This is used by a few special elements, e.g. <video>, <iframe> to ensure
-  // a persistent sizing under different subpixel offset, because these
-  // elements have a high cost to resize. The drawback is that we may overflow
-  // or underflow the final content box by 1px.
-  static LayoutRect PreSnappedRectForPersistentSizing(LayoutRect);
-
   bool NeedsPreferredWidthsRecalculation() const override;
 
   // These values are specified to be 300 and 150 pixels in the CSS 2.1 spec.
diff --git a/third_party/blink/renderer/core/layout/layout_video.cc b/third_party/blink/renderer/core/layout/layout_video.cc
index 964735e..dbb9797 100644
--- a/third_party/blink/renderer/core/layout/layout_video.cc
+++ b/third_party/blink/renderer/core/layout/layout_video.cc
@@ -171,7 +171,10 @@
   if (ShouldDisplayVideo()) {
     // Video codecs may need to restart from an I-frame when the output is
     // resized. Round size in advance to avoid 1px snap difference.
-    return PreSnappedRectForPersistentSizing(ComputeObjectFit());
+    // TODO(trchen): The way of rounding is different from LayoutEmbeddedContent
+    // just to match existing behavior. This is probably a bug and We should
+    // unify it with LayoutEmbeddedContent.
+    return LayoutRect(PixelSnappedIntRect(ComputeObjectFit()));
   }
   // If we are displaying the poster image no pre-rounding is needed, but the
   // size of the image should be used for fitting instead.
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 10757f3..9cecafec 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
@@ -28,7 +28,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_positioned_float.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_unpositioned_float.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
index 20c5366..5f52e8c 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker.h
@@ -10,7 +10,7 @@
 #include "third_party/blink/renderer/core/layout/ng/exclusions/ng_line_layout_opportunity.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
 #include "third_party/blink/renderer/platform/text/text_break_iterator.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
index 2c5ae85..919cb68 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_truncator.cc
@@ -7,7 +7,7 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_text_fragment_builder.h"
 #include "third_party/blink/renderer/platform/fonts/font_baseline.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
index d0e848c0..ae9625d9 100644
--- a/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
+++ b/third_party/blink/renderer/core/layout/ng/list/layout_ng_list_item.cc
@@ -66,6 +66,12 @@
   if (!marker_)
     return;
 
+  if (ordinal_.NotInListChanged()) {
+    UpdateMarker();
+    ordinal_.SetNotInListChanged(false);
+    return;
+  }
+
   // Make sure outside marker is the direct child of ListItem.
   if (!IsInside() && marker_->Parent() != this) {
     marker_->Remove();
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index 17ee4cd..bc0a9f1 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -226,6 +226,7 @@
           "peerconnection/rtc_rtp_contributing_source.idl",
           "peerconnection/rtc_rtp_receiver.idl",
           "peerconnection/rtc_rtp_sender.idl",
+          "peerconnection/rtc_rtp_transceiver.idl",
           "peerconnection/rtc_session_description.idl",
           "peerconnection/rtc_stats_report.idl",
           "peerconnection/rtc_stats_response.idl",
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 990592d..924ebf1e 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -32,6 +32,8 @@
     "rtc_rtp_receiver.h",
     "rtc_rtp_sender.cc",
     "rtc_rtp_sender.h",
+    "rtc_rtp_transceiver.cc",
+    "rtc_rtp_transceiver.h",
     "rtc_session_description.cc",
     "rtc_session_description.h",
     "rtc_session_description_request_impl.cc",
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 195d7fa2..6de5e60 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -90,6 +90,7 @@
 #include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_ice_event.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_session_description.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_session_description_init.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_session_description_request_impl.h"
@@ -1288,10 +1289,21 @@
 
 MediaStreamVector RTCPeerConnection::getLocalStreams() const {
   MediaStreamVector local_streams;
-  for (const auto& rtp_sender : getSenders()) {
-    for (const auto& stream : rtp_sender->streams()) {
-      if (!local_streams.Contains(stream))
-        local_streams.push_back(stream);
+  if (sdp_semantics_ == WebRTCSdpSemantics::kPlanB) {
+    for (const auto& sender : rtp_senders_) {
+      for (const auto& stream : sender->streams()) {
+        if (!local_streams.Contains(stream))
+          local_streams.push_back(stream);
+      }
+    }
+  } else {
+    for (const auto& transceiver : transceivers_) {
+      if (!transceiver->DirectionHasSend())
+        continue;
+      for (const auto& stream : transceiver->sender()->streams()) {
+        if (!local_streams.Contains(stream))
+          local_streams.push_back(stream);
+      }
     }
   }
   return local_streams;
@@ -1299,10 +1311,21 @@
 
 MediaStreamVector RTCPeerConnection::getRemoteStreams() const {
   MediaStreamVector remote_streams;
-  for (const auto& rtp_receiver : rtp_receivers_) {
-    for (const auto& stream : rtp_receiver->streams()) {
-      if (!remote_streams.Contains(stream))
-        remote_streams.push_back(stream);
+  if (sdp_semantics_ == WebRTCSdpSemantics::kPlanB) {
+    for (const auto& receiver : rtp_receivers_) {
+      for (const auto& stream : receiver->streams()) {
+        if (!remote_streams.Contains(stream))
+          remote_streams.push_back(stream);
+      }
+    }
+  } else {
+    for (const auto& transceiver : transceivers_) {
+      if (!transceiver->DirectionHasRecv())
+        continue;
+      for (const auto& stream : transceiver->receiver()->streams()) {
+        if (!remote_streams.Contains(stream))
+          remote_streams.push_back(stream);
+      }
     }
   }
   return remote_streams;
@@ -1457,6 +1480,11 @@
   return track_receiver->getStats(script_state);
 }
 
+const HeapVector<Member<RTCRtpTransceiver>>&
+RTCPeerConnection::getTransceivers() const {
+  return transceivers_;
+}
+
 const HeapVector<Member<RTCRtpSender>>& RTCPeerConnection::getSenders() const {
   return rtp_senders_;
 }
@@ -1503,20 +1531,37 @@
   }
 
   auto web_transceiver = error_or_transceiver.MoveValue();
-  // TODO(hbos): Currently the result of this operation is always to surface
-  // senders only - which is Plan B behavior - even if Unified Plan SDP
-  // semantics is used. Implement and surface transceivers under Unified Plan.
-  // https://crbug.com/777617
-  DCHECK_EQ(web_transceiver->ImplementationType(),
-            WebRTCRtpTransceiverImplementationType::kPlanBSenderOnly);
-  std::unique_ptr<WebRTCRtpSender> web_rtp_sender = web_transceiver->Sender();
 
-  DCHECK(FindSender(*web_rtp_sender) == rtp_senders_.end());
-  RTCRtpSender* rtp_sender =
-      new RTCRtpSender(this, std::move(web_rtp_sender), track, streams);
-  tracks_.insert(track->Component(), track);
-  rtp_senders_.push_back(rtp_sender);
-  return rtp_sender;
+  // The track must be known to the peer connection when performing
+  // CreateOrUpdateSender() below.
+  RegisterTrack(track);
+
+  auto stream_ids = web_transceiver->Sender()->StreamIds();
+  RTCRtpSender* sender;
+  if (sdp_semantics_ == WebRTCSdpSemantics::kPlanB) {
+    DCHECK_EQ(web_transceiver->ImplementationType(),
+              WebRTCRtpTransceiverImplementationType::kPlanBSenderOnly);
+    sender = CreateOrUpdateSender(web_transceiver->Sender(), track->kind());
+  } else {
+    DCHECK_EQ(sdp_semantics_, WebRTCSdpSemantics::kUnifiedPlan);
+    DCHECK_EQ(web_transceiver->ImplementationType(),
+              WebRTCRtpTransceiverImplementationType::kFullTransceiver);
+    RTCRtpTransceiver* transceiver =
+        CreateOrUpdateTransceiver(std::move(web_transceiver));
+    sender = transceiver->sender();
+  }
+  // Newly created senders have no streams set, we have to set it ourselves.
+  sender->set_streams(streams);
+
+  // The stream IDs should match between layers, with one exception;
+  // in Plan B if no stream was supplied, the lower layer still generates a
+  // stream which has no blink layer correspondence.
+  DCHECK(sdp_semantics_ != WebRTCSdpSemantics::kPlanB ||
+         (streams.size() == 0u && stream_ids.size() == 1u) ||
+         stream_ids.size() == streams.size());
+  DCHECK(sdp_semantics_ != WebRTCSdpSemantics::kUnifiedPlan ||
+         stream_ids.size() == streams.size());
+  return sender;
 }
 
 void RTCPeerConnection::removeTrack(RTCRtpSender* sender,
@@ -1533,17 +1578,28 @@
   }
 
   auto error_or_transceiver = peer_handler_->RemoveTrack(sender->web_sender());
-  if (!error_or_transceiver.ok()) {
-    // Operation aborted. This indicates that the sender is no longer used by
-    // the peer connection, i.e. that it was removed due to setting a remote
-    // description of type "rollback".
-    return;
+  if (sdp_semantics_ == WebRTCSdpSemantics::kPlanB) {
+    // Plan B: Was the sender removed?
+    if (!error_or_transceiver.ok()) {
+      // Operation aborted. This indicates that the sender is no longer used by
+      // the peer connection, i.e. that it was removed due to setting a remote
+      // description of type "rollback".
+      return;
+    }
+    // Successfully removing the track results in the sender's track property
+    // being nulled.
+    DCHECK(!sender->web_sender()->Track());
+    sender->SetTrack(nullptr);
+    rtp_senders_.erase(it);
+  } else {
+    // Unified Plan: Was the transceiver updated?
+    DCHECK_EQ(sdp_semantics_, WebRTCSdpSemantics::kUnifiedPlan);
+    if (!error_or_transceiver.ok()) {
+      ThrowExceptionFromRTCError(error_or_transceiver.error(), exception_state);
+      return;
+    }
+    CreateOrUpdateTransceiver(error_or_transceiver.MoveValue());
   }
-  // Successfully removing the track results in the sender's track property
-  // being nulled.
-  DCHECK(!sender->web_sender()->Track());
-  sender->SetTrack(nullptr);
-  rtp_senders_.erase(it);
 }
 
 RTCDataChannel* RTCPeerConnection::createDataChannel(
@@ -1632,6 +1688,104 @@
   return rtp_receivers_.end();
 }
 
+HeapVector<Member<RTCRtpTransceiver>>::iterator
+RTCPeerConnection::FindTransceiver(
+    const WebRTCRtpTransceiver& web_transceiver) {
+  for (auto* it = transceivers_.begin(); it != transceivers_.end(); ++it) {
+    if ((*it)->web_transceiver()->Id() == web_transceiver.Id())
+      return it;
+  }
+  return transceivers_.end();
+}
+
+RTCRtpSender* RTCPeerConnection::CreateOrUpdateSender(
+    std::unique_ptr<WebRTCRtpSender> web_sender,
+    String kind) {
+  // The track corresponding to |web_track| must already be known to us by being
+  // in |tracks_|, as is a prerequisite of CreateOrUpdateSender().
+  WebMediaStreamTrack web_track = web_sender->Track();
+  MediaStreamTrack* track;
+  if (web_track.IsNull()) {
+    track = nullptr;
+  } else {
+    track = tracks_.at(web_track);
+    DCHECK(track);
+  }
+
+  // Create or update sender. If the web sender has stream IDs the sender's
+  // streams need to be set separately outside of this method.
+  auto* sender_it = FindSender(*web_sender);
+  RTCRtpSender* sender;
+  if (sender_it == rtp_senders_.end()) {
+    // Create new sender (with empty stream set).
+    sender = new RTCRtpSender(this, std::move(web_sender), kind, track, {});
+    rtp_senders_.push_back(sender);
+  } else {
+    // Update existing sender (not touching the stream set).
+    sender = *sender_it;
+    DCHECK_EQ(sender->web_sender()->Id(), web_sender->Id());
+    sender->SetTrack(track);
+  }
+  return sender;
+}
+
+RTCRtpReceiver* RTCPeerConnection::CreateOrUpdateReceiver(
+    std::unique_ptr<WebRTCRtpReceiver> web_receiver) {
+  auto* receiver_it = FindReceiver(*web_receiver);
+  // Create track.
+  MediaStreamTrack* track;
+  if (receiver_it == rtp_receivers_.end()) {
+    track =
+        MediaStreamTrack::Create(GetExecutionContext(), web_receiver->Track());
+    RegisterTrack(track);
+  } else {
+    track = (*receiver_it)->track();
+  }
+
+  // Create or update receiver. If the web receiver has stream IDs the
+  // receiver's streams need to be set separately outside of this method.
+  RTCRtpReceiver* receiver;
+  if (receiver_it == rtp_receivers_.end()) {
+    // Create new receiver.
+    receiver = new RTCRtpReceiver(std::move(web_receiver), track, {});
+    rtp_receivers_.push_back(receiver);
+  } else {
+    // Update existing receiver is a no-op.
+    receiver = *receiver_it;
+    DCHECK_EQ(receiver->web_receiver().Id(), web_receiver->Id());
+    DCHECK_EQ(receiver->track(), track);  // Its track should never change.
+  }
+  return receiver;
+}
+
+RTCRtpTransceiver* RTCPeerConnection::CreateOrUpdateTransceiver(
+    std::unique_ptr<WebRTCRtpTransceiver> web_transceiver) {
+  String kind = (web_transceiver->Receiver()->Track().Source().GetType() ==
+                 WebMediaStreamSource::kTypeAudio)
+                    ? "audio"
+                    : "video";
+  RTCRtpSender* sender = CreateOrUpdateSender(web_transceiver->Sender(), kind);
+  RTCRtpReceiver* receiver =
+      CreateOrUpdateReceiver(web_transceiver->Receiver());
+
+  RTCRtpTransceiver* transceiver;
+  auto* transceiver_it = FindTransceiver(*web_transceiver);
+  if (transceiver_it == transceivers_.end()) {
+    // Create new tranceiver.
+    transceiver = new RTCRtpTransceiver(this, std::move(web_transceiver),
+                                        sender, receiver);
+    transceivers_.push_back(transceiver);
+  } else {
+    // Update existing transceiver.
+    transceiver = *transceiver_it;
+    // The sender and receiver have already been updated above.
+    DCHECK_EQ(transceiver->sender(), sender);
+    DCHECK_EQ(transceiver->receiver(), receiver);
+    transceiver->UpdateMembers();
+  }
+  return transceiver;
+}
+
 RTCDTMFSender* RTCPeerConnection::createDTMFSender(
     MediaStreamTrack* track,
     ExceptionState& exception_state) {
@@ -1672,6 +1826,11 @@
   CloseInternal();
 }
 
+void RTCPeerConnection::RegisterTrack(MediaStreamTrack* track) {
+  DCHECK(track);
+  tracks_.insert(track->Component(), track);
+}
+
 void RTCPeerConnection::NoteSdpCreated(const RTCSessionDescription& desc) {
   if (desc.type() == "offer") {
     last_offer_ = desc.sdp();
@@ -1758,6 +1917,7 @@
     std::unique_ptr<WebRTCRtpReceiver> web_receiver) {
   DCHECK(!closed_);
   DCHECK(GetExecutionContext()->IsContextThread());
+  DCHECK_EQ(sdp_semantics_, WebRTCSdpSemantics::kPlanB);
   if (signaling_state_ ==
       webrtc::PeerConnectionInterface::SignalingState::kClosed)
     return;
@@ -1806,13 +1966,14 @@
       new RTCRtpReceiver(std::move(web_receiver), track, streams);
   rtp_receivers_.push_back(rtp_receiver);
   ScheduleDispatchEvent(
-      new RTCTrackEvent(rtp_receiver, rtp_receiver->track(), streams));
+      new RTCTrackEvent(rtp_receiver, rtp_receiver->track(), streams, nullptr));
 }
 
 void RTCPeerConnection::DidRemoveReceiverPlanB(
     std::unique_ptr<WebRTCRtpReceiver> web_receiver) {
   DCHECK(!closed_);
   DCHECK(GetExecutionContext()->IsContextThread());
+  DCHECK_EQ(sdp_semantics_, WebRTCSdpSemantics::kPlanB);
 
   auto* it = FindReceiver(*web_receiver);
   DCHECK(it != rtp_receivers_.end());
@@ -1845,11 +2006,122 @@
 }
 
 void RTCPeerConnection::DidModifyTransceivers(
-    std::vector<std::unique_ptr<WebRTCRtpTransceiver>> web_transceiver,
+    std::vector<std::unique_ptr<WebRTCRtpTransceiver>> web_transceivers,
     bool is_remote_description) {
-  DCHECK_EQ(sdp_semantics_, WebRTCSdpSemantics::kUnifiedPlan);
-  // TODO(hbos): Exercise this codepath. https://crbug.com/777617
-  NOTREACHED();
+  HeapVector<Member<MediaStreamTrack>> mute_tracks;
+  HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>
+      remove_list;
+  HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>> add_list;
+  HeapVector<Member<RTCRtpTransceiver>> track_events;
+  for (auto& web_transceiver : web_transceivers) {
+    auto* it = FindTransceiver(*web_transceiver);
+    bool previously_had_recv =
+        (it != transceivers_.end()) ? (*it)->FiredDirectionHasRecv() : false;
+    RTCRtpTransceiver* transceiver =
+        CreateOrUpdateTransceiver(std::move(web_transceiver));
+
+    // The transceiver is now up-to-date. Compare before/after values of
+    // FiredDirectionHasRecv() and process the remote track if it changed.
+    if (is_remote_description && !previously_had_recv &&
+        transceiver->FiredDirectionHasRecv()) {
+      ProcessAdditionOfRemoteTrack(
+          transceiver, transceiver->web_transceiver()->Receiver()->StreamIds(),
+          &add_list, &track_events);
+    }
+    if (previously_had_recv && !transceiver->FiredDirectionHasRecv()) {
+      ProcessRemovalOfRemoteTrack(transceiver, &remove_list, &mute_tracks);
+    }
+  }
+
+  for (auto& track : mute_tracks) {
+    track->Component()->Source()->SetReadyState(
+        MediaStreamSource::kReadyStateMuted);
+  }
+  for (auto& pair : remove_list) {
+    auto& stream = pair.first;
+    auto& track = pair.second;
+    if (stream->getTracks().Contains(track)) {
+      stream->RemoveTrackAndFireEvents(track);
+    }
+  }
+  for (auto& pair : add_list) {
+    auto& stream = pair.first;
+    auto& track = pair.second;
+    if (!stream->getTracks().Contains(track)) {
+      stream->AddTrackAndFireEvents(track);
+    }
+  }
+
+  for (auto& transceiver : track_events) {
+    ScheduleDispatchEvent(new RTCTrackEvent(
+        transceiver->receiver(), transceiver->receiver()->track(),
+        transceiver->receiver()->streams(), transceiver));
+  }
+}
+
+void RTCPeerConnection::ProcessAdditionOfRemoteTrack(
+    RTCRtpTransceiver* transceiver,
+    const WebVector<WebString>& stream_ids,
+    HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+        add_list,
+    HeapVector<Member<RTCRtpTransceiver>>* track_events) {
+  SetAssociatedMediaStreams(transceiver->receiver(), stream_ids, nullptr,
+                            add_list);
+  track_events->push_back(transceiver);
+}
+
+void RTCPeerConnection::ProcessRemovalOfRemoteTrack(
+    RTCRtpTransceiver* transceiver,
+    HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+        remove_list,
+    HeapVector<Member<MediaStreamTrack>>* mute_tracks) {
+  WebVector<WebString> stream_ids = {};
+  SetAssociatedMediaStreams(transceiver->receiver(), stream_ids, remove_list,
+                            nullptr);
+  if (!transceiver->receiver()->track()->muted())
+    mute_tracks->push_back(transceiver->receiver()->track());
+}
+
+void RTCPeerConnection::SetAssociatedMediaStreams(
+    RTCRtpReceiver* receiver,
+    const WebVector<WebString>& stream_ids,
+    HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+        remove_list,
+    HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+        add_list) {
+  MediaStreamVector known_streams = getRemoteStreams();
+
+  MediaStreamVector streams;
+  for (const auto& stream_id : stream_ids) {
+    MediaStream* curr_stream = nullptr;
+    for (const auto& known_stream : known_streams) {
+      if (static_cast<WebString>(known_stream->id()) == stream_id) {
+        curr_stream = known_stream;
+        break;
+      }
+    }
+    if (!curr_stream) {
+      curr_stream = MediaStream::Create(
+          GetExecutionContext(), MediaStreamDescriptor::Create(
+                                     static_cast<String>(stream_id), {}, {}));
+    }
+    streams.push_back(curr_stream);
+  }
+
+  const MediaStreamVector& prev_streams = receiver->streams();
+  if (remove_list) {
+    for (const auto& stream : prev_streams) {
+      if (!streams.Contains(stream))
+        remove_list->push_back(std::make_pair(stream, receiver->track()));
+    }
+  }
+  if (add_list) {
+    for (const auto& stream : streams) {
+      if (!prev_streams.Contains(stream))
+        add_list->push_back(std::make_pair(stream, receiver->track()));
+    }
+  }
+  receiver->set_streams(std::move(streams));
 }
 
 void RTCPeerConnection::DidAddRemoteDataChannel(
@@ -1982,6 +2254,10 @@
   ChangeIceConnectionState(kICEConnectionStateClosed);
   ChangeSignalingState(webrtc::PeerConnectionInterface::SignalingState::kClosed,
                        false);
+  for (auto& transceiver : transceivers_) {
+    transceiver->OnPeerConnectionClosed();
+  }
+
   Document* document = ToDocument(GetExecutionContext());
   HostsUsingFeatures::CountAnyWorld(
       *document, HostsUsingFeatures::Feature::kRTCPeerConnectionUsed);
@@ -2044,6 +2320,7 @@
   visitor->Trace(tracks_);
   visitor->Trace(rtp_senders_);
   visitor->Trace(rtp_receivers_);
+  visitor->Trace(transceivers_);
   visitor->Trace(dispatch_scheduled_event_runner_);
   visitor->Trace(scheduled_events_);
   EventTargetWithInlineData::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
index 1d5be7a..4cb1f0f 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/modules/event_target_modules.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_ice_candidate.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h"
 #include "third_party/blink/renderer/platform/async_method_runner.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
@@ -174,6 +175,7 @@
       MediaStreamTrack* selector);
   ScriptPromise PromiseBasedGetStats(ScriptState*, MediaStreamTrack* selector);
 
+  const HeapVector<Member<RTCRtpTransceiver>>& getTransceivers() const;
   const HeapVector<Member<RTCRtpSender>>& getSenders() const;
   const HeapVector<Member<RTCRtpReceiver>>& getReceivers() const;
   RTCRtpSender* addTrack(MediaStreamTrack*, MediaStreamVector, ExceptionState&);
@@ -291,6 +293,60 @@
       const WebRTCRtpSender& web_sender);
   HeapVector<Member<RTCRtpReceiver>>::iterator FindReceiver(
       const WebRTCRtpReceiver& web_receiver);
+  HeapVector<Member<RTCRtpTransceiver>>::iterator FindTransceiver(
+      const WebRTCRtpTransceiver& web_transceiver);
+
+  // Creates or updates the sender such that it is up-to-date with the
+  // WebRTCRtpSender in all regards *except for streams*. The web sender only
+  // knows of stream IDs; updating the stream objects requires additional logic
+  // which is different depending on context, e.g:
+  // - If created/updated with addTrack(), the streams were supplied as
+  //   arguments.
+  // The web sender's web track must already have a correspondent blink track in
+  // |tracks_|. The caller is responsible for ensuring this with
+  // RegisterTrack(), e.g:
+  // - On addTrack(), the track is supplied as an argument.
+  RTCRtpSender* CreateOrUpdateSender(std::unique_ptr<WebRTCRtpSender>,
+                                     String kind);
+  // Creates or updates the receiver such that it is up-to-date with the
+  // WebRTCRtpReceiver in all regards *except for streams*. The web receiver
+  // only knows of stream IDs; updating the stream objects requires additional
+  // logic which is different depending on context, e.g:
+  // - If created/updated with setRemoteDescription(), there is an algorithm for
+  //   processing the addition/removal of remote tracks which includes how to
+  //   create and update the associated streams set.
+  RTCRtpReceiver* CreateOrUpdateReceiver(std::unique_ptr<WebRTCRtpReceiver>);
+  // Creates or updates the transceiver such that it, including its sender and
+  // receiver, are up-to-date with the WebRTCRtpTransceiver in all regerds
+  // *except for sender and receiver streams*. The web sender and web receiver
+  // only knows of stream IDs; updating the stream objects require additional
+  // logic which is different depending on context. See above.
+  RTCRtpTransceiver* CreateOrUpdateTransceiver(
+      std::unique_ptr<WebRTCRtpTransceiver>);
+
+  // https://w3c.github.io/webrtc-pc/#process-remote-track-addition
+  void ProcessAdditionOfRemoteTrack(
+      RTCRtpTransceiver* transceiver,
+      const WebVector<WebString>& stream_ids,
+      HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+          add_list,
+      HeapVector<Member<RTCRtpTransceiver>>* track_events);
+  // https://w3c.github.io/webrtc-pc/#process-remote-track-removal
+  void ProcessRemovalOfRemoteTrack(
+      RTCRtpTransceiver* transceiver,
+      HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+          remove_list,
+      HeapVector<Member<MediaStreamTrack>>* mute_tracks);
+  // Update the |receiver->streams()| to the streams indicated by |stream_ids|,
+  // adding to |remove_list| and |add_list| accordingly.
+  // https://w3c.github.io/webrtc-pc/#set-associated-remote-streams
+  void SetAssociatedMediaStreams(
+      RTCRtpReceiver* receiver,
+      const WebVector<WebString>& stream_ids,
+      HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+          remove_list,
+      HeapVector<std::pair<Member<MediaStream>, Member<MediaStreamTrack>>>*
+          add_list);
 
   // Sets the signaling state synchronously, and dispatches a
   // signalingstatechange event synchronously or asynchronously depending on
@@ -340,8 +396,15 @@
   // includes tracks of |rtp_senders_| and |rtp_receivers_|.
   HeapHashMap<WeakMember<MediaStreamComponent>, WeakMember<MediaStreamTrack>>
       tracks_;
+  // In Plan B, senders and receivers exist independently of one another.
+  // In Unified Plan, all senders and receivers are the sender-receiver pairs of
+  // transceivers.
+  // TODO(hbos): When Plan B is removed, remove |rtp_senders_| and
+  // |rtp_receivers_| since these are part of |transceivers_|.
+  // https://crbug.com/857004
   HeapVector<Member<RTCRtpSender>> rtp_senders_;
   HeapVector<Member<RTCRtpReceiver>> rtp_receivers_;
+  HeapVector<Member<RTCRtpTransceiver>> transceivers_;
 
   std::unique_ptr<WebRTCPeerConnectionHandler> peer_handler_;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
index 323bf996..39ac5949 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
@@ -132,6 +132,8 @@
     [CallWith=ScriptState] Promise<any> getStats(optional any callbackOrSelector);
 
     // RTP Media API
+    // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-gettransceivers
+    sequence<RTCRtpTranceiver> getTransceivers();
     // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-getsenders
     [Measure] sequence<RTCRtpSender> getSenders();
     // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-getreceivers
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
index dadd9fb..5e2cf66 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.cc
@@ -21,11 +21,6 @@
       streams_(std::move(streams)) {
   DCHECK(receiver_);
   DCHECK(track_);
-  // Some bots require #if around the DCHECK to avoid compile error about
-  // |StateMatchesWebReceiver| (which is behind #if) not being defined.
-#if DCHECK_IS_ON()
-  DCHECK(StateMatchesWebReceiver());
-#endif  // DCHECK_IS_ON()
 }
 
 MediaStreamTrack* RTCRtpReceiver::track() const {
@@ -53,24 +48,10 @@
   return streams_;
 }
 
-#if DCHECK_IS_ON()
-
-bool RTCRtpReceiver::StateMatchesWebReceiver() const {
-  if (track_->Component() !=
-      static_cast<MediaStreamComponent*>(receiver_->Track())) {
-    return false;
-  }
-  WebVector<WebString> stream_ids = receiver_->StreamIds();
-  if (streams_.size() != stream_ids.size())
-    return false;
-  for (size_t i = 0; i < streams_.size(); ++i)
-    if (static_cast<WebString>(streams_[i]->id()) != stream_ids[i])
-      return false;
-  return true;
+void RTCRtpReceiver::set_streams(MediaStreamVector streams) {
+  streams_ = std::move(streams);
 }
 
-#endif  // DCHECK_IS_ON()
-
 void RTCRtpReceiver::UpdateSourcesIfNeeded() {
   if (!contributing_sources_needs_updating_)
     return;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
index 840ff7a..33dc326 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h
@@ -34,14 +34,12 @@
 
   const WebRTCRtpReceiver& web_receiver() const;
   MediaStreamVector streams() const;
+  void set_streams(MediaStreamVector streams);
   void UpdateSourcesIfNeeded();
 
   void Trace(blink::Visitor*) override;
 
  private:
-#if DCHECK_IS_ON()
-  bool StateMatchesWebReceiver() const;
-#endif  // DCHECK_IS_ON()
   void SetContributingSourcesNeedsUpdating();
 
   std::unique_ptr<WebRTCRtpReceiver> receiver_;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index 8ae4c566..5ac0337f 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -241,16 +241,17 @@
 
 RTCRtpSender::RTCRtpSender(RTCPeerConnection* pc,
                            std::unique_ptr<WebRTCRtpSender> sender,
+                           String kind,
                            MediaStreamTrack* track,
                            MediaStreamVector streams)
     : pc_(pc),
       sender_(std::move(sender)),
+      kind_(std::move(kind)),
       track_(track),
       streams_(std::move(streams)) {
   DCHECK(pc_);
   DCHECK(sender_);
-  DCHECK(track_);
-  kind_ = track->kind();
+  DCHECK(!track || kind_ == track->kind());
 }
 
 MediaStreamTrack* RTCRtpSender::track() {
@@ -267,8 +268,10 @@
     return promise;
   }
   WebMediaStreamTrack web_track;
-  if (with_track)
+  if (with_track) {
+    pc_->RegisterTrack(with_track);
     web_track = with_track->Component();
+  }
   ReplaceTrackRequest* request =
       new ReplaceTrackRequest(this, with_track, resolver);
   sender_->ReplaceTrack(web_track, request);
@@ -409,6 +412,10 @@
   return streams_;
 }
 
+void RTCRtpSender::set_streams(MediaStreamVector streams) {
+  streams_ = std::move(streams);
+}
+
 RTCDTMFSender* RTCRtpSender::dtmf() {
   // Lazy initialization of dtmf_ to avoid overhead when not used.
   if (!dtmf_ && kind_ == "audio") {
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
index 699dcc526..fed78091 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h
@@ -30,6 +30,7 @@
   // https://github.com/w3c/webrtc-pc/issues/1712
   RTCRtpSender(RTCPeerConnection*,
                std::unique_ptr<WebRTCRtpSender>,
+               String kind,
                MediaStreamTrack*,
                MediaStreamVector streams);
 
@@ -46,16 +47,17 @@
   void SetTrack(MediaStreamTrack*);
   void ClearLastReturnedParameters();
   MediaStreamVector streams() const;
+  void set_streams(MediaStreamVector streams);
 
   void Trace(blink::Visitor*) override;
 
  private:
   Member<RTCPeerConnection> pc_;
   std::unique_ptr<WebRTCRtpSender> sender_;
-  Member<MediaStreamTrack> track_;
-  // The spec says that "kind" should be looked up in transceiver, but
-  // keeping it in sender at least until transceiver is implemented.
+  // The spec says that "kind" should be looked up in transceiver, but keeping
+  // a copy here as long as we support Plan B.
   String kind_;
+  Member<MediaStreamTrack> track_;
   Member<RTCDTMFSender> dtmf_;
   MediaStreamVector streams_;
   base::Optional<RTCRtpSendParameters> last_returned_parameters_;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
new file mode 100644
index 0000000..44f52f7d
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.cc
@@ -0,0 +1,179 @@
+// 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/modules/peerconnection/rtc_rtp_transceiver.h"
+
+#include "third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/assertions.h"
+
+namespace blink {
+
+namespace {
+
+String TransceiverDirectionToString(
+    const webrtc::RtpTransceiverDirection& direction) {
+  switch (direction) {
+    case webrtc::RtpTransceiverDirection::kSendRecv:
+      return "sendrecv";
+    case webrtc::RtpTransceiverDirection::kSendOnly:
+      return "sendonly";
+    case webrtc::RtpTransceiverDirection::kRecvOnly:
+      return "recvonly";
+    case webrtc::RtpTransceiverDirection::kInactive:
+      return "inactive";
+  }
+}
+
+String OptionalTransceiverDirectionToString(
+    const base::Optional<webrtc::RtpTransceiverDirection>& direction) {
+  return direction ? TransceiverDirectionToString(*direction)
+                   : String();  // null
+}
+
+bool TransceiverDirectionFromString(
+    const String& direction_string,
+    base::Optional<webrtc::RtpTransceiverDirection>* direction_out) {
+  if (!direction_string) {
+    *direction_out = base::nullopt;
+    return true;
+  }
+  if (direction_string == "sendrecv") {
+    *direction_out = webrtc::RtpTransceiverDirection::kSendRecv;
+    return true;
+  }
+  if (direction_string == "sendonly") {
+    *direction_out = webrtc::RtpTransceiverDirection::kSendOnly;
+    return true;
+  }
+  if (direction_string == "recvonly") {
+    *direction_out = webrtc::RtpTransceiverDirection::kRecvOnly;
+    return true;
+  }
+  if (direction_string == "inactive") {
+    *direction_out = webrtc::RtpTransceiverDirection::kInactive;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
+RTCRtpTransceiver::RTCRtpTransceiver(
+    RTCPeerConnection* pc,
+    std::unique_ptr<WebRTCRtpTransceiver> web_transceiver,
+    RTCRtpSender* sender,
+    RTCRtpReceiver* receiver)
+    : pc_(pc),
+      web_transceiver_(std::move(web_transceiver)),
+      sender_(sender),
+      receiver_(receiver),
+      fired_direction_(base::nullopt) {
+  DCHECK(pc_);
+  DCHECK(web_transceiver_);
+  DCHECK(sender_);
+  DCHECK(receiver_);
+  UpdateMembers();
+}
+
+String RTCRtpTransceiver::mid() const {
+  return web_transceiver_->Mid();
+}
+
+RTCRtpSender* RTCRtpTransceiver::sender() const {
+  return sender_;
+}
+
+RTCRtpReceiver* RTCRtpTransceiver::receiver() const {
+  return receiver_;
+}
+
+bool RTCRtpTransceiver::stopped() const {
+  return stopped_;
+}
+
+String RTCRtpTransceiver::direction() const {
+  return direction_;
+}
+
+void RTCRtpTransceiver::setDirection(String direction,
+                                     ExceptionState& exception_state) {
+  base::Optional<webrtc::RtpTransceiverDirection> webrtc_direction;
+  if (!TransceiverDirectionFromString(direction, &webrtc_direction) ||
+      !webrtc_direction) {
+    exception_state.ThrowTypeError("Invalid RTCRtpTransceiverDirection.");
+    return;
+  }
+  if (pc_->IsClosed()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "The peer connection is closed.");
+    return;
+  }
+  if (stopped_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "The transceiver is stopped.");
+    return;
+  }
+  web_transceiver_->SetDirection(*webrtc_direction);
+  UpdateMembers();
+}
+
+String RTCRtpTransceiver::currentDirection() const {
+  return current_direction_;
+}
+
+void RTCRtpTransceiver::UpdateMembers() {
+  stopped_ = web_transceiver_->Stopped();
+  direction_ = TransceiverDirectionToString(web_transceiver_->Direction());
+  current_direction_ = OptionalTransceiverDirectionToString(
+      web_transceiver_->CurrentDirection());
+  fired_direction_ = web_transceiver_->FiredDirection();
+}
+
+void RTCRtpTransceiver::OnPeerConnectionClosed() {
+  receiver_->track()->Component()->Source()->SetReadyState(
+      MediaStreamSource::kReadyStateMuted);
+  stopped_ = true;
+}
+
+WebRTCRtpTransceiver* RTCRtpTransceiver::web_transceiver() const {
+  return web_transceiver_.get();
+}
+
+base::Optional<webrtc::RtpTransceiverDirection>
+RTCRtpTransceiver::fired_direction() const {
+  return fired_direction_;
+}
+
+bool RTCRtpTransceiver::DirectionHasSend() const {
+  auto direction = web_transceiver_->Direction();
+  return direction == webrtc::RtpTransceiverDirection::kSendRecv ||
+         direction == webrtc::RtpTransceiverDirection::kSendOnly;
+}
+
+bool RTCRtpTransceiver::DirectionHasRecv() const {
+  auto direction = web_transceiver_->Direction();
+  return direction == webrtc::RtpTransceiverDirection::kSendRecv ||
+         direction == webrtc::RtpTransceiverDirection::kRecvOnly;
+}
+
+bool RTCRtpTransceiver::FiredDirectionHasRecv() const {
+  return fired_direction_ &&
+         (*fired_direction_ == webrtc::RtpTransceiverDirection::kSendRecv ||
+          *fired_direction_ == webrtc::RtpTransceiverDirection::kRecvOnly);
+}
+
+void RTCRtpTransceiver::Trace(Visitor* visitor) {
+  visitor->Trace(pc_);
+  visitor->Trace(sender_);
+  visitor->Trace(receiver_);
+  ScriptWrappable::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h
new file mode 100644
index 0000000..d321baf4
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h
@@ -0,0 +1,75 @@
+// 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_MODULES_PEERCONNECTION_RTC_RTP_TRANSCEIVER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_RTP_TRANSCEIVER_H_
+
+#include "base/optional.h"
+#include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/webrtc/api/rtptransceiverinterface.h"
+
+namespace blink {
+
+class RTCPeerConnection;
+class RTCRtpReceiver;
+class RTCRtpSender;
+
+class RTCRtpTransceiver final : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  RTCRtpTransceiver(RTCPeerConnection* pc,
+                    std::unique_ptr<WebRTCRtpTransceiver>,
+                    RTCRtpSender*,
+                    RTCRtpReceiver*);
+
+  // rtc_rtp_transciever.idl
+  String mid() const;
+  RTCRtpSender* sender() const;
+  RTCRtpReceiver* receiver() const;
+  bool stopped() const;
+  // Enum type RTCRtpTransceiverDirection
+  String direction() const;
+  void setDirection(String direction, ExceptionState&);
+  String currentDirection() const;
+
+  // Updates the transceiver attributes by fetching values from
+  // |web_transceiver_|. This is made an explicit operation (rather than
+  // fetching the values from the |web_transceivers_| directly every time an
+  // attribute is read) to make it possible to look at before/after snapshots of
+  // the attributes when a session description has been applied. This is
+  // necessary in order for blink to know when to process the addition/removal
+  // of remote tracks:
+  // https://w3c.github.io/webrtc-pc/#set-the-rtcsessiondescription.
+  void UpdateMembers();
+  void OnPeerConnectionClosed();
+
+  WebRTCRtpTransceiver* web_transceiver() const;
+  base::Optional<webrtc::RtpTransceiverDirection> fired_direction() const;
+  bool DirectionHasSend() const;
+  bool DirectionHasRecv() const;
+  bool FiredDirectionHasRecv() const;
+
+  void Trace(Visitor*) override;
+
+ private:
+  Member<RTCPeerConnection> pc_;
+  std::unique_ptr<WebRTCRtpTransceiver> web_transceiver_;
+  Member<RTCRtpSender> sender_;
+  Member<RTCRtpReceiver> receiver_;
+  bool stopped_;
+  String direction_;
+  String current_direction_;
+  base::Optional<webrtc::RtpTransceiverDirection> fired_direction_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_RTP_TRANSCEIVER_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.idl b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.idl
new file mode 100644
index 0000000..6cd3809
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.idl
@@ -0,0 +1,22 @@
+// 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.
+
+// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverdirection
+enum RTCRtpTransceiverDirection {
+    "sendrecv",
+    "sendonly",
+    "recvonly",
+    "inactive"
+};
+
+// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiver
+[Exposed=Window]
+interface RTCRtpTransceiver {
+    readonly attribute DOMString? mid;
+    [SameObject] readonly attribute RTCRtpSender sender;
+    [SameObject] readonly attribute RTCRtpReceiver receiver;
+    readonly attribute boolean stopped;
+    [RaisesException=Setter] attribute RTCRtpTransceiverDirection direction;
+    readonly attribute RTCRtpTransceiverDirection? currentDirection;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_track_event.cc b/third_party/blink/renderer/modules/peerconnection/rtc_track_event.cc
index 2451c08..075c1008 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_track_event.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_track_event.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_receiver.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_rtp_transceiver.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_track_event_init.h"
 
 namespace blink {
@@ -21,18 +22,21 @@
     : Event(type, eventInitDict),
       receiver_(eventInitDict.receiver()),
       track_(eventInitDict.track()),
-      streams_(eventInitDict.streams()) {
+      streams_(eventInitDict.streams()),
+      transceiver_(eventInitDict.transceiver()) {
   DCHECK(receiver_);
   DCHECK(track_);
 }
 
 RTCTrackEvent::RTCTrackEvent(RTCRtpReceiver* receiver,
                              MediaStreamTrack* track,
-                             const HeapVector<Member<MediaStream>>& streams)
+                             const HeapVector<Member<MediaStream>>& streams,
+                             RTCRtpTransceiver* transceiver)
     : Event(EventTypeNames::track, Bubbles::kNo, Cancelable::kNo),
       receiver_(receiver),
       track_(track),
-      streams_(streams) {
+      streams_(streams),
+      transceiver_(transceiver) {
   DCHECK(receiver_);
   DCHECK(track_);
 }
@@ -49,10 +53,15 @@
   return streams_;
 }
 
+RTCRtpTransceiver* RTCTrackEvent::transceiver() const {
+  return transceiver_;
+}
+
 void RTCTrackEvent::Trace(blink::Visitor* visitor) {
   visitor->Trace(receiver_);
   visitor->Trace(track_);
   visitor->Trace(streams_);
+  visitor->Trace(transceiver_);
   Event::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_track_event.h b/third_party/blink/renderer/modules/peerconnection/rtc_track_event.h
index 575666f9..cbeaf6b 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_track_event.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_track_event.h
@@ -15,6 +15,7 @@
 class MediaStream;
 class MediaStreamTrack;
 class RTCRtpReceiver;
+class RTCRtpTransceiver;
 class RTCTrackEventInit;
 
 // https://w3c.github.io/webrtc-pc/#rtctrackevent
@@ -26,11 +27,13 @@
                                const RTCTrackEventInit& eventInitDict);
   RTCTrackEvent(RTCRtpReceiver*,
                 MediaStreamTrack*,
-                const HeapVector<Member<MediaStream>>&);
+                const HeapVector<Member<MediaStream>>&,
+                RTCRtpTransceiver*);
 
   RTCRtpReceiver* receiver() const;
   MediaStreamTrack* track() const;
   HeapVector<Member<MediaStream>> streams() const;
+  RTCRtpTransceiver* transceiver() const;
 
   void Trace(blink::Visitor*) override;
 
@@ -41,6 +44,7 @@
   Member<RTCRtpReceiver> receiver_;
   Member<MediaStreamTrack> track_;
   HeapVector<Member<MediaStream>> streams_;
+  Member<RTCRtpTransceiver> transceiver_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_track_event.idl b/third_party/blink/renderer/modules/peerconnection/rtc_track_event.idl
index c103db6..5c55b025 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_track_event.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_track_event.idl
@@ -11,6 +11,5 @@
     readonly attribute MediaStreamTrack         track;
     [SameObject]
     readonly attribute FrozenArray<MediaStream> streams;
-    // TODO(hbos): When RTCRtpTransceiver is supported, add:
-    // readonly attribute RTCRtpTransceiver        transceiver;
+    readonly attribute RTCRtpTransceiver        transceiver;
 };
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_track_event_init.idl b/third_party/blink/renderer/modules/peerconnection/rtc_track_event_init.idl
index 74d6c438fa..f12d204 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_track_event_init.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_track_event_init.idl
@@ -7,6 +7,5 @@
     required RTCRtpReceiver        receiver;
     required MediaStreamTrack      track;
              sequence<MediaStream> streams = [];
-    // TODO(hbos): When RTCRtpTransceiver is supported, add:
-    // required RTCRtpTransceiver     transceiver;
+    required RTCRtpTransceiver     transceiver;
 };
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 01b8383..d5d8e483 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -689,13 +689,13 @@
     "fonts/shaping/caching_word_shape_iterator.h",
     "fonts/shaping/caching_word_shaper.cc",
     "fonts/shaping/caching_word_shaper.h",
-    "fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc",
-    "fonts/shaping/case_mapping_harf_buzz_buffer_filler.h",
-    "fonts/shaping/harf_buzz_face.cc",
-    "fonts/shaping/harf_buzz_face.h",
-    "fonts/shaping/harf_buzz_font_cache.h",
-    "fonts/shaping/harf_buzz_shaper.cc",
-    "fonts/shaping/harf_buzz_shaper.h",
+    "fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc",
+    "fonts/shaping/case_mapping_harfbuzz_buffer_filler.h",
+    "fonts/shaping/harfbuzz_face.cc",
+    "fonts/shaping/harfbuzz_face.h",
+    "fonts/shaping/harfbuzz_font_cache.h",
+    "fonts/shaping/harfbuzz_shaper.cc",
+    "fonts/shaping/harfbuzz_shaper.h",
     "fonts/shaping/run_segmenter.cc",
     "fonts/shaping/run_segmenter.h",
     "fonts/shaping/shape_cache.cc",
@@ -1742,7 +1742,7 @@
     "fonts/orientation_iterator_test.cc",
     "fonts/script_run_iterator_test.cc",
     "fonts/shaping/caching_word_shaper_test.cc",
-    "fonts/shaping/harf_buzz_shaper_test.cc",
+    "fonts/shaping/harfbuzz_shaper_test.cc",
     "fonts/shaping/run_segmenter_test.cc",
     "fonts/shaping/shape_result_bloberizer_test.cc",
     "fonts/shaping/shaping_line_breaker_test.cc",
@@ -2154,7 +2154,7 @@
 
 fuzzer_test("blink_harfbuzz_shaper_fuzzer") {
   sources = [
-    "fonts/shaping/harf_buzz_shaper_fuzzer.cc",
+    "fonts/shaping/harfbuzz_shaper_fuzzer.cc",
   ]
   deps = [
     ":blink_fuzzer_test_support",
diff --git a/third_party/blink/renderer/platform/exported/web_media_constraints.cc b/third_party/blink/renderer/platform/exported/web_media_constraints.cc
index e29bc1f0..c379548 100644
--- a/third_party/blink/renderer/platform/exported/web_media_constraints.cc
+++ b/third_party/blink/renderer/platform/exported/web_media_constraints.cc
@@ -73,6 +73,7 @@
 }  // namespace
 
 const char kEchoCancellationTypeBrowser[] = "browser";
+const char kEchoCancellationTypeAec3[] = "aec3";
 const char kEchoCancellationTypeSystem[] = "system";
 
 class WebMediaConstraintsPrivate final
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 45d8507..e3383f4 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -39,6 +39,10 @@
   RuntimeEnabledFeatures::SetHeapIncrementalMarkingEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableBloatedRendererDetection(bool enable) {
+  RuntimeEnabledFeatures::SetBloatedRendererDetectionEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableExperimentalFeatures(bool enable) {
   RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(enable);
 }
@@ -88,6 +92,10 @@
   RuntimeEnabledFeatures::SetAccessibilityObjectModelEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableAdTagging(bool enable) {
+  RuntimeEnabledFeatures::SetAdTaggingEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableAllowActivationDelegationAttr(bool enable) {
   RuntimeEnabledFeatures::SetAllowActivationDelegationAttrEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/fonts/README.md b/third_party/blink/renderer/platform/fonts/README.md
index 7891fbad..c926090 100644
--- a/third_party/blink/renderer/platform/fonts/README.md
+++ b/third_party/blink/renderer/platform/fonts/README.md
@@ -227,8 +227,8 @@
 
 The text shaping implementation is
 in
-[shaping/harf_buzz_shaper.h](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h) and
-[shaping/harf_buzz_shaper.cc](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc)
+[shaping/harfbuzz_shaper.h](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h) and
+[shaping/harfbuzz_shaper.cc](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc)
 
 Shaping text runs is split into several
 stages: [Run segmentation](#Run-Segmentation), shaping the initial segment
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.cc b/third_party/blink/renderer/platform/fonts/font_cache.cc
index 761c2a5b..ac44cca 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.cc
+++ b/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -286,7 +286,14 @@
     UChar32 lookup_char,
     const SimpleFontData* font_data_to_substitute,
     FontFallbackPriority fallback_priority) {
-  if (Character::IsPrivateUse(lookup_char))
+  // In addition to PUA, do not perform fallback for non-characters either. Some
+  // of these are sentinel characters to detect encodings and do appear on
+  // websites. More details on
+  // http://www.unicode.org/faq/private_use.html#nonchar1 - See also
+  // crbug.com/862352 where performing fallback for U+FFFE causes a memory
+  // regression.
+  if (Character::IsPrivateUse(lookup_char) ||
+      Character::IsNonCharacter(lookup_char))
     return nullptr;
   return PlatformFallbackFontForCharacter(
       description, lookup_char, font_data_to_substitute, fallback_priority);
diff --git a/third_party/blink/renderer/platform/fonts/font_global_context.h b/third_party/blink/renderer/platform/fonts/font_global_context.h
index d9e8c01..928df28 100644
--- a/third_party/blink/renderer/platform/fonts/font_global_context.h
+++ b/third_party/blink/renderer/platform/fonts/font_global_context.h
@@ -6,7 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_FONT_GLOBAL_CONTEXT_H_
 
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/text/layout_locale.h"
 
@@ -29,7 +29,7 @@
   static inline FontCache& GetFontCache() { return Get()->font_cache_; }
 
   static inline HarfBuzzFontCache& GetHarfBuzzFontCache() {
-    return Get()->harf_buzz_font_cache_;
+    return Get()->harfbuzz_font_cache_;
   }
 
   static hb_font_funcs_t* GetHarfBuzzFontFuncs() {
@@ -52,7 +52,7 @@
   ~FontGlobalContext() = default;
 
   FontCache font_cache_;
-  HarfBuzzFontCache harf_buzz_font_cache_;
+  HarfBuzzFontCache harfbuzz_font_cache_;
   hb_font_funcs_t* harfbuzz_font_funcs_;
 };
 
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data.cc b/third_party/blink/renderer/platform/fonts/font_platform_data.cc
index 9591396..51a6adf 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.cc
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.cc
@@ -27,7 +27,7 @@
 #include "third_party/blink/public/platform/linux/web_sandbox_support.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/layout_test_support.h"
 #include "third_party/blink/renderer/platform/text/character.h"
@@ -100,7 +100,7 @@
 #if !defined(OS_WIN) && !defined(OS_MACOSX)
       style_(source.style_),
 #endif
-      harf_buzz_face_(nullptr),
+      harfbuzz_face_(nullptr),
       is_hash_table_deleted_value_(false)
 #if defined(OS_WIN)
       ,
@@ -193,7 +193,7 @@
   synthetic_bold_ = other.synthetic_bold_;
   synthetic_italic_ = other.synthetic_italic_;
   avoid_embedded_bitmaps_ = other.avoid_embedded_bitmaps_;
-  harf_buzz_face_ = nullptr;
+  harfbuzz_face_ = nullptr;
   orientation_ = other.orientation_;
 #if !defined(OS_WIN) && !defined(OS_MACOSX)
   style_ = other.style_;
@@ -247,11 +247,11 @@
 }
 
 HarfBuzzFace* FontPlatformData::GetHarfBuzzFace() const {
-  if (!harf_buzz_face_)
-    harf_buzz_face_ =
+  if (!harfbuzz_face_)
+    harfbuzz_face_ =
         HarfBuzzFace::Create(const_cast<FontPlatformData*>(this), UniqueID());
 
-  return harf_buzz_face_.get();
+  return harfbuzz_face_.get();
 }
 
 bool FontPlatformData::HasSpaceInLigaturesOrKerning(
diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data.h b/third_party/blink/renderer/platform/fonts/font_platform_data.h
index dd5762f82..df741b2 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.h
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.h
@@ -192,7 +192,7 @@
   WebFontRenderStyle style_;
 #endif
 
-  mutable scoped_refptr<HarfBuzzFace> harf_buzz_face_;
+  mutable scoped_refptr<HarfBuzzFace> harfbuzz_face_;
   bool is_hash_table_deleted_value_;
 #if defined(OS_WIN)
   // TODO(https://crbug.com/808221): Replace |paint_text_flags_| with |style_|.
diff --git a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
index 129804a9..11368b4b 100644
--- a/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
+++ b/third_party/blink/renderer/platform/fonts/mac/font_platform_data_mac.mm
@@ -29,7 +29,7 @@
 #import "third_party/blink/public/platform/platform.h"
 #import "third_party/blink/renderer/platform/fonts/font.h"
 #import "third_party/blink/renderer/platform/fonts/opentype/font_settings.h"
-#import "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#import "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
 #import "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #import "third_party/blink/renderer/platform/layout_test_support.h"
 #import "third_party/blink/renderer/platform/wtf/retain_ptr.h"
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc
index 5b5ec7de..3365749c 100644
--- a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.cc
@@ -7,16 +7,16 @@
 namespace blink {
 
 OpenTypeCapsSupport::OpenTypeCapsSupport()
-    : harf_buzz_face_(nullptr),
+    : harfbuzz_face_(nullptr),
       requested_caps_(FontDescription::kCapsNormal),
       font_support_(FontSupport::kFull),
       caps_synthesis_(CapsSynthesis::kNone) {}
 
 OpenTypeCapsSupport::OpenTypeCapsSupport(
-    const HarfBuzzFace* harf_buzz_face,
+    const HarfBuzzFace* harfbuzz_face,
     FontDescription::FontVariantCaps requested_caps,
     hb_script_t script)
-    : harf_buzz_face_(harf_buzz_face),
+    : harfbuzz_face_(harfbuzz_face),
       requested_caps_(requested_caps),
       font_support_(FontSupport::kFull),
       caps_synthesis_(CapsSynthesis::kNone) {
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h
index 16dd277..841e465 100644
--- a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h
@@ -7,8 +7,8 @@
 
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
 #include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
 
 #include <hb.h>
@@ -32,7 +32,7 @@
   void DetermineFontSupport(hb_script_t);
   bool SupportsOpenTypeFeature(hb_script_t, uint32_t tag) const;
 
-  const HarfBuzzFace* harf_buzz_face_;
+  const HarfBuzzFace* harfbuzz_face_;
   FontDescription::FontVariantCaps requested_caps_;
 
   enum class FontSupport {
diff --git a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc
index 41feeb4d..0e0a808 100644
--- a/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc
+++ b/third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support_mpl.cc
@@ -15,7 +15,7 @@
 bool OpenTypeCapsSupport::SupportsOpenTypeFeature(hb_script_t script,
                                                   uint32_t tag) const {
   hb_face_t* face = hb_font_get_face(
-      harf_buzz_face_->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout));
+      harfbuzz_face_->GetScaledFont(nullptr, HarfBuzzFace::NoVerticalLayout));
   DCHECK(face);
 
   DCHECK(
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc
index cb276f78..939164e 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
 
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
index 41f5fa9c..aab219b 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/caching_word_shaper.cc
@@ -27,7 +27,7 @@
 
 #include "third_party/blink/renderer/platform/fonts/character_range.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/caching_word_shape_iterator.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_cache.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
diff --git a/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc b/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc
similarity index 85%
rename from third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc
rename to third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.cc
index d23b4e5..2ab8d8c 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.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/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.h"
 
 namespace blink {
 
@@ -18,17 +18,17 @@
 CaseMappingHarfBuzzBufferFiller::CaseMappingHarfBuzzBufferFiller(
     CaseMapIntend case_map_intend,
     AtomicString locale,
-    hb_buffer_t* harf_buzz_buffer,
+    hb_buffer_t* harfbuzz_buffer,
     const String& text,
     unsigned start_index,
     unsigned num_characters)
-    : harf_buzz_buffer_(harf_buzz_buffer) {
+    : harfbuzz_buffer_(harfbuzz_buffer) {
   if (case_map_intend == CaseMapIntend::kKeepSameCase) {
     if (text.Is8Bit()) {
-      hb_buffer_add_latin1(harf_buzz_buffer_, text.Characters8(), text.length(),
+      hb_buffer_add_latin1(harfbuzz_buffer_, text.Characters8(), text.length(),
                            start_index, num_characters);
     } else {
-      hb_buffer_add_utf16(harf_buzz_buffer_, ToUint16(text.Characters16()),
+      hb_buffer_add_utf16(harfbuzz_buffer_, ToUint16(text.Characters16()),
                           text.length(), start_index, num_characters);
     }
   } else {
@@ -45,7 +45,7 @@
 
     DCHECK_EQ(case_mapped_text.length(), text.length());
     DCHECK(!case_mapped_text.Is8Bit());
-    hb_buffer_add_utf16(harf_buzz_buffer_,
+    hb_buffer_add_utf16(harfbuzz_buffer_,
                         ToUint16(case_mapped_text.Characters16()),
                         text.length(), start_index, num_characters);
   }
@@ -61,7 +61,7 @@
     unsigned start_index,
     unsigned num_characters) {
   // Record pre-context.
-  hb_buffer_add_utf16(harf_buzz_buffer_, ToUint16(buffer), buffer_length,
+  hb_buffer_add_utf16(harfbuzz_buffer_, ToUint16(buffer), buffer_length,
                       start_index, 0);
 
   for (unsigned char_index = start_index;
@@ -81,13 +81,13 @@
                codepoint);
       // Add all characters of the case mapping result at the same cluster
       // position.
-      hb_buffer_add(harf_buzz_buffer_, codepoint, char_index);
+      hb_buffer_add(harfbuzz_buffer_, codepoint, char_index);
     }
     char_index = new_char_index;
   }
 
   // Record post-context
-  hb_buffer_add_utf16(harf_buzz_buffer_, ToUint16(buffer), buffer_length,
+  hb_buffer_add_utf16(harfbuzz_buffer_, ToUint16(buffer), buffer_length,
                       start_index + num_characters, 0);
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h b/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.h
similarity index 88%
rename from third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h
rename to third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.h
index 3ac7c30..e67c770 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.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_FONTS_SHAPING_CASE_MAPPING_HARF_BUZZ_BUFFER_FILLER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CASE_MAPPING_HARF_BUZZ_BUFFER_FILLER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CASE_MAPPING_HARFBUZZ_BUFFER_FILLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_CASE_MAPPING_HARFBUZZ_BUFFER_FILLER_H_
 
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -22,7 +22,7 @@
  public:
   CaseMappingHarfBuzzBufferFiller(CaseMapIntend,
                                   AtomicString locale,
-                                  hb_buffer_t* harf_buzz_buffer,
+                                  hb_buffer_t* harfbuzz_buffer,
                                   const String& text,
                                   unsigned start_index,
                                   unsigned num_characters);
@@ -34,7 +34,7 @@
                     unsigned buffer_length,
                     unsigned start_index,
                     unsigned num_characters);
-  hb_buffer_t* harf_buzz_buffer_;
+  hb_buffer_t* harfbuzz_buffer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
similarity index 96%
rename from third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.cc
rename to third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
index 74aa474..264a9439 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.cc
@@ -28,7 +28,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
 
 #include <memory>
 
@@ -36,8 +36,8 @@
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/fonts/font_global_context.h"
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
 #include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
 #include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
@@ -92,7 +92,7 @@
   }
   result.stored_value->value->AddRef();
   unscaled_font_ = result.stored_value->value->HbFont();
-  harf_buzz_font_data_ = result.stored_value->value->HbFontData();
+  harfbuzz_font_data_ = result.stored_value->value->HbFontData();
 }
 
 HarfBuzzFace::~HarfBuzzFace() {
@@ -244,14 +244,14 @@
 
   // Check whether computing is needed and compute for gpos/gsub.
   if (features & kKerning &&
-      harf_buzz_font_data_->space_in_gpos_ ==
+      harfbuzz_font_data_->space_in_gpos_ ==
           HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Unknown) {
     if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
       return false;
     // Compute for gpos.
     hb_face_t* face = hb_font_get_face(unscaled_font_);
     DCHECK(face);
-    harf_buzz_font_data_->space_in_gpos_ =
+    harfbuzz_font_data_->space_in_gpos_ =
         hb_ot_layout_has_positioning(face) &&
                 TableHasSpace(face, glyphs.get(), HB_OT_TAG_GPOS, space)
             ? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present
@@ -261,14 +261,14 @@
   hb_set_clear(glyphs.get());
 
   if (features & kLigatures &&
-      harf_buzz_font_data_->space_in_gsub_ ==
+      harfbuzz_font_data_->space_in_gsub_ ==
           HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Unknown) {
     if (space == kInvalidCodepoint && !GetSpaceGlyph(unscaled_font_, space))
       return false;
     // Compute for gpos.
     hb_face_t* face = hb_font_get_face(unscaled_font_);
     DCHECK(face);
-    harf_buzz_font_data_->space_in_gsub_ =
+    harfbuzz_font_data_->space_in_gsub_ =
         hb_ot_layout_has_substitution(face) &&
                 TableHasSpace(face, glyphs.get(), HB_OT_TAG_GSUB, space)
             ? HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present
@@ -276,10 +276,10 @@
   }
 
   return (features & kKerning &&
-          harf_buzz_font_data_->space_in_gpos_ ==
+          harfbuzz_font_data_->space_in_gpos_ ==
               HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present) ||
          (features & kLigatures &&
-          harf_buzz_font_data_->space_in_gsub_ ==
+          harfbuzz_font_data_->space_in_gsub_ ==
               HarfBuzzFontData::SpaceGlyphInOpenTypeTables::Present);
 }
 
@@ -289,7 +289,7 @@
 }
 
 bool HarfBuzzFace::ShouldSubpixelPosition() {
-  return harf_buzz_font_data_->paint_.isSubpixelText();
+  return harfbuzz_font_data_->paint_.isSubpixelText();
 }
 
 static hb_font_funcs_t* HarfBuzzSkiaGetFontFuncs() {
@@ -420,8 +420,8 @@
   PaintFont paint_font;
   platform_data_->SetupPaintFont(&paint_font);
   paint_font.SetTextEncoding(SkPaint::kGlyphID_TextEncoding);
-  harf_buzz_font_data_->range_set_ = std::move(range_set);
-  harf_buzz_font_data_->UpdateFallbackMetricsAndScale(
+  harfbuzz_font_data_->range_set_ = std::move(range_set);
+  harfbuzz_font_data_->UpdateFallbackMetricsAndScale(
       *platform_data_, paint_font.ToSkPaint(), vertical_layout);
 
   int scale =
@@ -429,7 +429,7 @@
   hb_font_set_scale(unscaled_font_, scale, scale);
   hb_font_set_ptem(unscaled_font_, platform_data_->size() / kCssPixelsPerPoint);
 
-  SkTypeface* typeface = harf_buzz_font_data_->paint_.getTypeface();
+  SkTypeface* typeface = harfbuzz_font_data_->paint_.getTypeface();
   int axis_count = typeface->getVariationDesignPosition(nullptr, 0);
   if (axis_count > 0) {
     Vector<SkFontArguments::VariationPosition::Coordinate> axis_values;
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h
similarity index 92%
rename from third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h
rename to third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h
index 35d81fe..a234385 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h
@@ -28,8 +28,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FACE_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FACE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FACE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FACE_H_
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/fonts/typesetting_features.h"
@@ -52,7 +52,7 @@
 
  public:
   static scoped_refptr<HarfBuzzFace> Create(FontPlatformData* platform_data,
-                                     uint64_t unique_id) {
+                                            uint64_t unique_id) {
     return base::AdoptRef(new HarfBuzzFace(platform_data, unique_id));
   }
   ~HarfBuzzFace();
@@ -79,9 +79,9 @@
   FontPlatformData* platform_data_;
   uint64_t unique_id_;
   hb_font_t* unscaled_font_;
-  HarfBuzzFontData* harf_buzz_font_data_;
+  HarfBuzzFontData* harfbuzz_font_data_;
 };
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FACE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FACE_H_
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h
similarity index 96%
rename from third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h
rename to third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h
index a25f253..83865f78 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_font_cache.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_cache.h
@@ -2,13 +2,13 @@
 // 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_FONTS_SHAPING_HARF_BUZZ_FONT_CACHE_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_FONT_CACHE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FONT_CACHE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_FONT_CACHE_H_
 
 #include <memory>
 #include "third_party/blink/renderer/platform/fonts/font_metrics.h"
 #include "third_party/blink/renderer/platform/fonts/opentype/open_type_vertical_data.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
 #include "third_party/blink/renderer/platform/fonts/unicode_range_set.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
similarity index 99%
rename from third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc
rename to third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
index f9e4c5c..1666578 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
@@ -29,7 +29,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 
 #include <hb.h>
 #include <unicode/uchar.h>
@@ -43,8 +43,8 @@
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_fallback_iterator.h"
 #include "third_party/blink/renderer/platform/fonts/opentype/open_type_caps_support.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harf_buzz_buffer_filler.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_face.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/case_mapping_harfbuzz_buffer_filler.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
 #include "third_party/blink/renderer/platform/fonts/small_caps_iterator.h"
 #include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
@@ -953,7 +953,8 @@
          (start >= pre_segmented->start && end <= pre_segmented->end));
 
   unsigned length = end - start;
-  scoped_refptr<ShapeResult> result = ShapeResult::Create(font, length, direction);
+  scoped_refptr<ShapeResult> result =
+      ShapeResult::Create(font, length, direction);
   HarfBuzzScopedPtr<hb_buffer_t> buffer(hb_buffer_create(), hb_buffer_destroy);
 
   RangeData range_data;
@@ -1007,8 +1008,9 @@
   return result;
 }
 
-scoped_refptr<ShapeResult> HarfBuzzShaper::Shape(const Font* font,
-                                          TextDirection direction) const {
+scoped_refptr<ShapeResult> HarfBuzzShaper::Shape(
+    const Font* font,
+    TextDirection direction) const {
   return Shape(font, direction, 0, text_.length());
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h
similarity index 96%
rename from third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h
rename to third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h
index 70a5c1d..a65259f 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h
@@ -28,8 +28,8 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_SHAPER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_SHAPER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_SHAPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_SHAPER_H_
 
 #include "third_party/blink/renderer/platform/fonts/shaping/run_segmenter.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
@@ -77,7 +77,6 @@
   ~HarfBuzzShaper() = default;
 
  private:
-
   // Shapes a single seqment, as identified by the RunSegmenterRange parameter,
   // one or more times taking font fallback into account. The start and end
   // parameters are for the entire text run, not the segment, and are used to
@@ -111,4 +110,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARF_BUZZ_SHAPER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HARFBUZZ_SHAPER_H_
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_fuzzer.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc
similarity index 99%
rename from third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_fuzzer.cc
rename to third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc
index 40c22552..186cb35 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_fuzzer.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_fuzzer.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/platform/fonts/font.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/testing/blink_fuzzer_test_support.h"
 
 #include <stddef.h>
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
similarity index 98%
rename from third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc
rename to third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
index 33f63fd..8c96a9b 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.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/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 
 #include <unicode/uscript.h>
 
@@ -124,7 +124,8 @@
                         testing::Values(TextDirection::kLtr,
                                         TextDirection::kRtl));
 
-static inline ShapeResultTestInfo* TestInfo(scoped_refptr<ShapeResult>& result) {
+static inline ShapeResultTestInfo* TestInfo(
+    scoped_refptr<ShapeResult>& result) {
   return static_cast<ShapeResultTestInfo*>(result.get());
 }
 
@@ -185,7 +186,8 @@
   };
   for (auto& test : testlist) {
     HarfBuzzShaper shaper(test.string);
-    scoped_refptr<ShapeResult> result = shaper.Shape(&font, TextDirection::kLtr);
+    scoped_refptr<ShapeResult> result =
+        shaper.Shape(&font, TextDirection::kLtr);
 
     EXPECT_EQ(1u, TestInfo(result)->NumberOfRunsForTesting()) << test.name;
     ASSERT_TRUE(
@@ -371,10 +373,13 @@
   UChar arabic_string[] = {0x647, 0x64A};
   HarfBuzzShaper shaper(String(arabic_string, 2));
 
-  scoped_refptr<ShapeResult> combined = shaper.Shape(&font, TextDirection::kRtl);
+  scoped_refptr<ShapeResult> combined =
+      shaper.Shape(&font, TextDirection::kRtl);
 
-  scoped_refptr<ShapeResult> first = shaper.Shape(&font, TextDirection::kRtl, 0, 1);
-  scoped_refptr<ShapeResult> second = shaper.Shape(&font, TextDirection::kRtl, 1, 2);
+  scoped_refptr<ShapeResult> first =
+      shaper.Shape(&font, TextDirection::kRtl, 0, 1);
+  scoped_refptr<ShapeResult> second =
+      shaper.Shape(&font, TextDirection::kRtl, 1, 2);
 
   // Combined width should be the same when shaping the two characters
   // separately as when shaping them combined.
@@ -703,8 +708,10 @@
 
   HarfBuzzShaper shaper(string);
   scoped_refptr<ShapeResult> result = shaper.Shape(&font, direction);
-  scoped_refptr<ShapeResult> first = shaper.Shape(&font, direction, 0, 5);    // Hello
-  scoped_refptr<ShapeResult> second = shaper.Shape(&font, direction, 6, 11);  // World
+  scoped_refptr<ShapeResult> first =
+      shaper.Shape(&font, direction, 0, 5);  // Hello
+  scoped_refptr<ShapeResult> second =
+      shaper.Shape(&font, direction, 6, 11);  // World
 
   EXPECT_EQ(0.0f, result->PositionForOffset(0));
   ASSERT_NEAR(first->Width(), result->PositionForOffset(5), 1);
@@ -1146,7 +1153,8 @@
 
   String string = To16Bit("ffi ff", 6);
   HarfBuzzShaper shaper(string);
-  scoped_refptr<ShapeResult> result = shaper.Shape(&testFont, TextDirection::kLtr);
+  scoped_refptr<ShapeResult> result =
+      shaper.Shape(&testFont, TextDirection::kLtr);
 
   EXPECT_EQ(0u, result->NextSafeToBreakOffset(0));  // At start of string.
   EXPECT_EQ(3u, result->NextSafeToBreakOffset(1));  // At end of "ffi" ligature.
@@ -1185,7 +1193,8 @@
 
   String string = To16Bit("ffi ff", 6);
   HarfBuzzShaper shaper(string);
-  scoped_refptr<ShapeResult> result = shaper.Shape(&testFont, TextDirection::kLtr);
+  scoped_refptr<ShapeResult> result =
+      shaper.Shape(&testFont, TextDirection::kLtr);
 
   EXPECT_EQ(6u, result->PreviousSafeToBreakOffset(6));  // At end of "ff" liga.
   EXPECT_EQ(4u, result->PreviousSafeToBreakOffset(5));  // At end of "ff" liga.
@@ -1226,7 +1235,8 @@
   // RA and CA form ligatures, most glyph pairs have kerning.
   String string(u"ABRACADABRA");
   HarfBuzzShaper shaper(string);
-  scoped_refptr<ShapeResult> result = shaper.Shape(&testFont, TextDirection::kLtr);
+  scoped_refptr<ShapeResult> result =
+      shaper.Shape(&testFont, TextDirection::kLtr);
   EXPECT_EQ(6u, result->NextSafeToBreakOffset(1));    // After CA ligature.
   EXPECT_EQ(6u, result->NextSafeToBreakOffset(6));    // After CA ligature.
   EXPECT_EQ(11u, result->NextSafeToBreakOffset(7));   // At end of string.
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index 88e9c01..5d23754 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -815,15 +815,15 @@
 void ShapeResult::ComputeGlyphPositions(ShapeResult::RunInfo* run,
                                         unsigned start_glyph,
                                         unsigned num_glyphs,
-                                        hb_buffer_t* harf_buzz_buffer) {
+                                        hb_buffer_t* harfbuzz_buffer) {
   DCHECK_EQ(is_horizontal_run, run->IsHorizontal());
   const SimpleFontData& current_font_data = *run->font_data_;
   const hb_glyph_info_t* glyph_infos =
-      hb_buffer_get_glyph_infos(harf_buzz_buffer, nullptr);
+      hb_buffer_get_glyph_infos(harfbuzz_buffer, nullptr);
   const hb_glyph_position_t* glyph_positions =
-      hb_buffer_get_glyph_positions(harf_buzz_buffer, nullptr);
+      hb_buffer_get_glyph_positions(harfbuzz_buffer, nullptr);
   const unsigned start_cluster =
-      HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harf_buzz_buffer))
+      HB_DIRECTION_IS_FORWARD(hb_buffer_get_direction(harfbuzz_buffer))
           ? glyph_infos[start_glyph].cluster
           : glyph_infos[start_glyph + num_glyphs - 1].cluster;
 
@@ -881,7 +881,7 @@
 void ShapeResult::InsertRun(std::unique_ptr<ShapeResult::RunInfo> run_to_insert,
                             unsigned start_glyph,
                             unsigned num_glyphs,
-                            hb_buffer_t* harf_buzz_buffer) {
+                            hb_buffer_t* harfbuzz_buffer) {
   DCHECK_GT(num_glyphs, 0u);
   std::unique_ptr<ShapeResult::RunInfo> run(std::move(run_to_insert));
   DCHECK_EQ(num_glyphs, run->glyph_data_.size());
@@ -891,11 +891,11 @@
     // cases, no adjustments are needed because |glyph_bounding_box_| is in
     // logical coordinates and uses alphabetic baseline.
     ComputeGlyphPositions<true>(run.get(), start_glyph, num_glyphs,
-                                harf_buzz_buffer);
+                                harfbuzz_buffer);
   } else {
     // Inserting a vertical run to a vertical result.
     ComputeGlyphPositions<false>(run.get(), start_glyph, num_glyphs,
-                                 harf_buzz_buffer);
+                                 harfbuzz_buffer);
   }
   width_ += run->width_;
   num_glyphs_ += run->NumGlyphs();
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h
index 676cbff..f659681 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_test_info.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_TEST_INFO_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_TEST_INFO_H_
 
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h"
 
 #include <hb.h>
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
index 5b03f82..a7cd5af 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.h"
 
 #include "third_party/blink/renderer/platform/fonts/font.h"
-#include "third_party/blink/renderer/platform/fonts/shaping/harf_buzz_shaper.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_inline_headers.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_spacing.h"
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index bf4210c3..94c2171 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -57,7 +57,6 @@
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 #include "third_party/blink/renderer/platform/wtf/stack_util.h"
 #include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
@@ -179,6 +178,7 @@
       gc_state_(kNoGCScheduled),
       gc_phase_(GCPhase::kNone),
       reason_for_scheduled_gc_(BlinkGC::GCReason::kMaxValue),
+      should_optimize_for_load_time_(false),
       isolate_(nullptr),
       trace_dom_wrappers_(nullptr),
       invalidate_dead_objects_in_wrappers_marking_deque_(nullptr),
@@ -211,6 +211,16 @@
 void ThreadState::AttachMainThread() {
   thread_specific_ = new WTF::ThreadSpecific<ThreadState*>();
   new (main_thread_state_storage_) ThreadState();
+
+  // PpapiThread doesn't set the current thread.
+  WebThread* current_thread = Platform::Current()->CurrentThread();
+  if (current_thread) {
+    ThreadScheduler* scheduler = current_thread->Scheduler();
+    // Some binaries do not have a scheduler (e.g.
+    // v8_context_snapshot_generator)
+    if (scheduler)
+      scheduler->AddRAILModeObserver(MainThreadState());
+  }
 }
 
 void ThreadState::AttachCurrentThread() {
@@ -528,7 +538,10 @@
 
   if ((gc_type == BlinkGC::kV8MajorGC && ShouldForceMemoryPressureGC()) ||
       ShouldScheduleV8FollowupGC()) {
-    if (RuntimeEnabledFeatures::HeapIncrementalMarkingEnabled()) {
+    // When we want to optimize for load time, we should prioritize throughput
+    // over latency and not do incremental marking.
+    if (RuntimeEnabledFeatures::HeapIncrementalMarkingEnabled() &&
+        !should_optimize_for_load_time_) {
       VLOG(2) << "[state:" << this << "] "
               << "ScheduleV8FollowupGCIfNeeded: Scheduled incremental v8 "
                  "followup GC";
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 063439b2..0060c09 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -39,6 +39,7 @@
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
 #include "third_party/blink/renderer/platform/heap/threading_traits.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/address_sanitizer.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
@@ -137,7 +138,8 @@
   ThreadState* thread_state_;
 };
 
-class PLATFORM_EXPORT ThreadState {
+class PLATFORM_EXPORT ThreadState final
+    : scheduler::WebThreadScheduler::RAILModeObserver {
   USING_FAST_MALLOC(ThreadState);
 
  public:
@@ -567,6 +569,11 @@
 
   MarkingVisitor* CurrentVisitor() { return current_gc_data_.visitor.get(); }
 
+  // Implementation for RAILModeObserver
+  void OnRAILModeChanged(v8::RAILMode new_mode) override {
+    should_optimize_for_load_time_ = new_mode == v8::RAILMode::PERFORMANCE_LOAD;
+  }
+
  private:
   // Needs to set up visitor for testing purposes.
   friend class incremental_marking_test::IncrementalMarkingScope;
@@ -585,7 +592,7 @@
   static base::subtle::AtomicWord wrapper_tracing_counter_;
 
   ThreadState();
-  ~ThreadState();
+  ~ThreadState() override;
 
   // The version is needed to be able to start incremental marking.
   void MarkPhasePrologue(BlinkGC::StackState,
@@ -713,6 +720,8 @@
   GCPhase gc_phase_;
   BlinkGC::GCReason reason_for_scheduled_gc_;
 
+  bool should_optimize_for_load_time_;
+
   using PreFinalizerCallback = bool (*)(void*);
   using PreFinalizer = std::pair<void*, PreFinalizerCallback>;
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
index bdb109d..d78dc19 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h
@@ -56,10 +56,14 @@
 // enforces a bunch of security checks and rules for resource revalidation. Its
 // lifetime is roughly per-DocumentLoader, in that it is generally created in
 // the DocumentLoader constructor and loses its ability to generate network
-// requests when the DocumentLoader is destroyed. Documents also hold a pointer
-// to ResourceFetcher for their lifetime (and will create one if they are
-// initialized without a LocalFrame), so a Document can keep a ResourceFetcher
-// alive past detach if scripts still reference the Document.
+// requests when the DocumentLoader is destroyed.
+//
+// It is also created for workers and worklets.
+//
+// Documents also hold a pointer to ResourceFetcher for their lifetime (and will
+// create one if they are initialized without a LocalFrame), so a Document can
+// keep a ResourceFetcher alive past detach if scripts still reference the
+// Document.
 class PLATFORM_EXPORT ResourceFetcher
     : public GarbageCollectedFinalized<ResourceFetcher> {
   WTF_MAKE_NONCOPYABLE(ResourceFetcher);
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_source.cc b/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
index 86453cd..9f6364b1 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_source.cc
@@ -132,12 +132,17 @@
         settings.echo_cancellation = false;
         settings.echo_cancellation_type.Reset();
         break;
-      case EchoCancellationMode::kSoftware:
+      case EchoCancellationMode::kBrowser:
         settings.echo_cancellation = true;
         settings.echo_cancellation_type =
             WebString::FromASCII(blink::kEchoCancellationTypeBrowser);
         break;
-      case EchoCancellationMode::kHardware:
+      case EchoCancellationMode::kAec3:
+        settings.echo_cancellation = true;
+        settings.echo_cancellation_type =
+            WebString::FromASCII(blink::kEchoCancellationTypeAec3);
+        break;
+      case EchoCancellationMode::kSystem:
         settings.echo_cancellation = true;
         settings.echo_cancellation_type =
             WebString::FromASCII(blink::kEchoCancellationTypeSystem);
@@ -183,9 +188,11 @@
                    MediaStreamSource::kReadyStateEnded);
 STATIC_ASSERT_ENUM(WebMediaStreamSource::EchoCancellationMode::kDisabled,
                    MediaStreamSource::EchoCancellationMode::kDisabled);
-STATIC_ASSERT_ENUM(WebMediaStreamSource::EchoCancellationMode::kSoftware,
-                   MediaStreamSource::EchoCancellationMode::kSoftware);
-STATIC_ASSERT_ENUM(WebMediaStreamSource::EchoCancellationMode::kHardware,
-                   MediaStreamSource::EchoCancellationMode::kHardware);
+STATIC_ASSERT_ENUM(WebMediaStreamSource::EchoCancellationMode::kBrowser,
+                   MediaStreamSource::EchoCancellationMode::kBrowser);
+STATIC_ASSERT_ENUM(WebMediaStreamSource::EchoCancellationMode::kAec3,
+                   MediaStreamSource::EchoCancellationMode::kAec3);
+STATIC_ASSERT_ENUM(WebMediaStreamSource::EchoCancellationMode::kSystem,
+                   MediaStreamSource::EchoCancellationMode::kSystem);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_source.h b/third_party/blink/renderer/platform/mediastream/media_stream_source.h
index 9d9d40f..dfc765b 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_source.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_source.h
@@ -72,7 +72,7 @@
     kReadyStateEnded = 2
   };
 
-  enum class EchoCancellationMode { kDisabled, kSoftware, kHardware };
+  enum class EchoCancellationMode { kDisabled, kBrowser, kAec3, kSystem };
 
   static MediaStreamSource* Create(const String& id,
                                    StreamType,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 63f47614..e500a21 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -76,6 +76,10 @@
       status: "experimental",
     },
     {
+      name: "AdTagging",
+      status: "test",
+    },
+    {
       name: "AllowActivationDelegationAttr",
     },
     {
diff --git a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
index 1cf6d98..2d93ae8 100644
--- a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
@@ -153,7 +153,7 @@
   NOTREACHED();
 }
 
-void WebThreadScheduler::SetRAILModeObserver(RAILModeObserver* observer) {
+void WebThreadScheduler::AddRAILModeObserver(RAILModeObserver* observer) {
   NOTREACHED();
 }
 
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 f71f5f7..b69d7c6d 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
@@ -580,7 +580,6 @@
                                &main_thread_scheduler_impl->tracing_controller_,
                                YesNoStateToString),
       background_status_changed_at(now),
-      rail_mode_observer(nullptr),
       wake_up_budget_pool(nullptr),
       metrics_helper(
           main_thread_scheduler_impl,
@@ -738,7 +737,7 @@
   task_queue_throttler_.reset();
   idle_helper_.Shutdown();
   helper_.Shutdown();
-  main_thread_only().rail_mode_observer = nullptr;
+  main_thread_only().rail_mode_observers.Clear();
   was_shutdown_ = true;
 }
 
@@ -1698,10 +1697,10 @@
   }
 
   main_thread_only().rail_mode_for_tracing = new_policy.rail_mode();
-  if (main_thread_only().rail_mode_observer &&
-      new_policy.rail_mode() != main_thread_only().current_policy.rail_mode()) {
-    main_thread_only().rail_mode_observer->OnRAILModeChanged(
-        new_policy.rail_mode());
+  if (new_policy.rail_mode() != main_thread_only().current_policy.rail_mode()) {
+    for (auto& observer : main_thread_only().rail_mode_observers) {
+      observer.OnRAILModeChanged(new_policy.rail_mode());
+    }
   }
 
   if (new_policy.should_disable_throttling() !=
@@ -2447,8 +2446,9 @@
   ipc_task_queue_->SetBlameContext(blame_context);
 }
 
-void MainThreadSchedulerImpl::SetRAILModeObserver(RAILModeObserver* observer) {
-  main_thread_only().rail_mode_observer = observer;
+void MainThreadSchedulerImpl::AddRAILModeObserver(RAILModeObserver* observer) {
+  main_thread_only().rail_mode_observers.AddObserver(observer);
+  observer->OnRAILModeChanged(main_thread_only().current_policy.rail_mode());
 }
 
 void MainThreadSchedulerImpl::SetRendererProcessType(RendererProcessType type) {
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 1ac0745..d15386e 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
@@ -175,7 +175,7 @@
   void Shutdown() override;
   void SetTopLevelBlameContext(
       base::trace_event::BlameContext* blame_context) override;
-  void SetRAILModeObserver(RAILModeObserver* observer) override;
+  void AddRAILModeObserver(RAILModeObserver* observer) override;
   void SetRendererProcessType(RendererProcessType type) override;
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
       const char* name,
@@ -843,7 +843,7 @@
     base::TimeDelta max_queueing_time;
     base::TimeTicks background_status_changed_at;
     std::set<PageSchedulerImpl*> page_schedulers;  // Not owned.
-    RAILModeObserver* rail_mode_observer;          // Not owned.
+    base::ObserverList<RAILModeObserver> rail_mode_observers;  // Not owned.
     WakeUpBudgetPool* wake_up_budget_pool;         // Not owned.
     MainThreadMetricsHelper metrics_helper;
     TraceableState<RendererProcessType, kTracingCategoryNameTopLevel>
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 d852b12e..8e37aae 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
@@ -276,6 +276,10 @@
     return any_thread().begin_main_frame_on_critical_path;
   }
 
+  void RemoveRAILModeObserver(RAILModeObserver const* observer) {
+    main_thread_only().rail_mode_observers.RemoveObserver(observer);
+  }
+
   bool waiting_for_meaningful_paint() const {
     base::AutoLock lock(any_thread_lock_);
     return any_thread().waiting_for_meaningful_paint;
@@ -3361,30 +3365,30 @@
 
 TEST_F(MainThreadSchedulerImplTest, TestResponseRAILMode) {
   MockRAILModeObserver observer;
-  scheduler_->SetRAILModeObserver(&observer);
+  scheduler_->AddRAILModeObserver(&observer);
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_RESPONSE));
 
   scheduler_->SetHaveSeenABlockingGestureForTesting(true);
   ForceBlockingInputToBeExpectedSoon();
   EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
   EXPECT_EQ(v8::PERFORMANCE_RESPONSE, GetRAILMode());
-  scheduler_->SetRAILModeObserver(nullptr);
+  scheduler_->RemoveRAILModeObserver(&observer);
 }
 
 TEST_F(MainThreadSchedulerImplTest, TestAnimateRAILMode) {
   MockRAILModeObserver observer;
-  scheduler_->SetRAILModeObserver(&observer);
+  scheduler_->AddRAILModeObserver(&observer);
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION)).Times(0);
 
   EXPECT_FALSE(BeginFrameNotExpectedSoon());
   EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
   EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
-  scheduler_->SetRAILModeObserver(nullptr);
+  scheduler_->RemoveRAILModeObserver(&observer);
 }
 
 TEST_F(MainThreadSchedulerImplTest, TestIdleRAILMode) {
   MockRAILModeObserver observer;
-  scheduler_->SetRAILModeObserver(&observer);
+  scheduler_->AddRAILModeObserver(&observer);
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION));
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_IDLE));
 
@@ -3394,12 +3398,12 @@
   scheduler_->SetAllRenderWidgetsHidden(false);
   EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
   EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
-  scheduler_->SetRAILModeObserver(nullptr);
+  scheduler_->RemoveRAILModeObserver(&observer);
 }
 
 TEST_F(MainThreadSchedulerImplTest, TestLoadRAILMode) {
   MockRAILModeObserver observer;
-  scheduler_->SetRAILModeObserver(&observer);
+  scheduler_->AddRAILModeObserver(&observer);
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION));
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_LOAD));
 
@@ -3409,12 +3413,12 @@
   scheduler_->OnFirstMeaningfulPaint();
   EXPECT_EQ(UseCase::kNone, ForceUpdatePolicyAndGetCurrentUseCase());
   EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
-  scheduler_->SetRAILModeObserver(nullptr);
+  scheduler_->RemoveRAILModeObserver(&observer);
 }
 
 TEST_F(MainThreadSchedulerImplTest, InputTerminatesLoadRAILMode) {
   MockRAILModeObserver observer;
-  scheduler_->SetRAILModeObserver(&observer);
+  scheduler_->AddRAILModeObserver(&observer);
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_ANIMATION));
   EXPECT_CALL(observer, OnRAILModeChanged(v8::PERFORMANCE_LOAD));
 
@@ -3430,7 +3434,7 @@
   EXPECT_EQ(UseCase::kCompositorGesture,
             ForceUpdatePolicyAndGetCurrentUseCase());
   EXPECT_EQ(v8::PERFORMANCE_ANIMATION, GetRAILMode());
-  scheduler_->SetRAILModeObserver(nullptr);
+  scheduler_->RemoveRAILModeObserver(&observer);
 }
 
 TEST_F(MainThreadSchedulerImplTest, UnthrottledTaskRunner) {
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 ad3f656..8381a36 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -68,6 +68,9 @@
   virtual void PostNonNestableIdleTask(const base::Location&,
                                        WebThread::IdleTask) = 0;
 
+  virtual void AddRAILModeObserver(
+      scheduler::WebThreadScheduler::RAILModeObserver* observer) = 0;
+
   // Returns a task runner for kV8 tasks. Can be called from any thread.
   virtual scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() = 0;
 
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_renderer_scheduler.cc b/third_party/blink/renderer/platform/scheduler/test/fake_renderer_scheduler.cc
index 1fae4d2..03bb979 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_renderer_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_renderer_scheduler.cc
@@ -96,7 +96,7 @@
 void FakeRendererScheduler::SetTopLevelBlameContext(
     base::trace_event::BlameContext* blame_context) {}
 
-void FakeRendererScheduler::SetRAILModeObserver(RAILModeObserver* observer) {}
+void FakeRendererScheduler::AddRAILModeObserver(RAILModeObserver* observer) {}
 
 void FakeRendererScheduler::SetRendererProcessType(RendererProcessType type) {}
 
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 c79308c..08f0dcec 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
@@ -46,6 +46,7 @@
   void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
   void RemoveTaskObserver(
       base::MessageLoop::TaskObserver* task_observer) override;
+  void AddRAILModeObserver(RAILModeObserver*) override {}
   void Shutdown() override;
 
   // SingleThreadIdleTaskRunner::Delegate:
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 d40d4206..19e9ac4 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
@@ -53,6 +53,7 @@
   void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
   void RemoveTaskObserver(
       base::MessageLoop::TaskObserver* task_observer) override;
+  void AddRAILModeObserver(RAILModeObserver*) override {}
   void Shutdown() override;
 
   // NonMainThreadSchedulerImpl implementation:
diff --git a/third_party/blink/renderer/platform/text/character.cc b/third_party/blink/renderer/platform/text/character.cc
index 8aade37..6f64a2a 100644
--- a/third_party/blink/renderer/platform/text/character.cc
+++ b/third_party/blink/renderer/platform/text/character.cc
@@ -299,4 +299,8 @@
   return WTF::Unicode::Category(character) & WTF::Unicode::kOther_PrivateUse;
 }
 
+bool Character::IsNonCharacter(UChar32 character) {
+  return U_IS_UNICODE_NONCHAR(character);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/character.h b/third_party/blink/renderer/platform/text/character.h
index 9e76873f..c436616e 100644
--- a/third_party/blink/renderer/platform/text/character.h
+++ b/third_party/blink/renderer/platform/text/character.h
@@ -184,6 +184,7 @@
 
   static bool IsCommonOrInheritedScript(UChar32);
   static bool IsPrivateUse(UChar32);
+  static bool IsNonCharacter(UChar32);
 
  private:
   static bool IsCJKIdeographOrSymbolSlow(UChar32);
diff --git a/third_party/blink/renderer/platform/text/character_test.cc b/third_party/blink/renderer/platform/text/character_test.cc
index 0599a02..504e37a 100644
--- a/third_party/blink/renderer/platform/text/character_test.cc
+++ b/third_party/blink/renderer/platform/text/character_test.cc
@@ -356,4 +356,30 @@
   EXPECT_FALSE(Character::IsBidiControl(0x05D0));
 }
 
+TEST(CharacterTest, IsNonCharacter) {
+  // See http://www.unicode.org/faq/private_use.html#nonchar4
+  EXPECT_FALSE(Character::IsNonCharacter(0xFDD0 - 1));
+  for (UChar32 bmp_noncharacter = 0xFDD0; bmp_noncharacter < 0xFDEF;
+       ++bmp_noncharacter) {
+    EXPECT_TRUE(Character::IsNonCharacter(bmp_noncharacter));
+  }
+  EXPECT_FALSE(Character::IsNonCharacter(0xFDEF + 1));
+
+  EXPECT_FALSE(Character::IsNonCharacter(0xFFFE - 1));
+  EXPECT_TRUE(Character::IsNonCharacter(0xFFFE));
+  EXPECT_TRUE(Character::IsNonCharacter(0xFFFF));
+  EXPECT_FALSE(Character::IsNonCharacter(0xFFFF + 1));
+
+  for (uint32_t supplementary_plane_prefix = 0x10000;
+       supplementary_plane_prefix < 0x100000;
+       supplementary_plane_prefix += 0x10000) {
+    EXPECT_FALSE(
+        Character::IsNonCharacter(supplementary_plane_prefix + 0xFFFE - 1));
+    EXPECT_TRUE(Character::IsNonCharacter(supplementary_plane_prefix + 0xFFFE));
+    EXPECT_TRUE(Character::IsNonCharacter(supplementary_plane_prefix + 0xFFFF));
+    EXPECT_FALSE(
+        Character::IsNonCharacter(supplementary_plane_prefix + 0xFFFF + 1));
+  }
+}
+
 }  // namespace blink
diff --git a/tools/binary_size/libsupersize/html_report.py b/tools/binary_size/libsupersize/html_report.py
index 7673173b..38b9f169 100644
--- a/tools/binary_size/libsupersize/html_report.py
+++ b/tools/binary_size/libsupersize/html_report.py
@@ -56,6 +56,8 @@
   'tree-worker.js',
 ]
 
+_DEFAULT_SYMBOL_COUNT = 100000
+
 
 def _GetSymbolType(symbol):
   symbol_type = symbol.section
@@ -63,9 +65,25 @@
     symbol_type = _SYMBOL_TYPE_VTABLE
   elif symbol.name.endswith(']'):
     symbol_type = _SYMBOL_TYPE_GENERATED
+  if symbol_type not in _SMALL_SYMBOL_DESCRIPTIONS:
+    symbol_type = _SYMBOL_TYPE_OTHER
   return symbol_type
 
 
+def _GetOrAddFileNode(symbol, file_nodes, components):
+  path = symbol.source_path or symbol.object_path
+  file_node = file_nodes.get(path)
+  if file_node is None:
+    component_index = components.GetOrAdd(symbol.component)
+    file_node = {
+      _COMPACT_FILE_PATH_KEY: path,
+      _COMPACT_FILE_COMPONENT_INDEX_KEY: component_index,
+      _COMPACT_FILE_SYMBOLS_KEY: [],
+    }
+    file_nodes[path] = file_node
+  return file_node
+
+
 class IndexedSet(object):
   """Set-like object where values are unique and indexed.
 
@@ -86,7 +104,7 @@
     return index
 
 
-def _MakeTreeViewList(symbols, min_symbol_size):
+def _MakeTreeViewList(symbols, include_all_symbols):
   """Builds JSON data of the symbols for the tree view HTML report.
 
   As the tree is built on the client-side, this function creates a flat list
@@ -94,23 +112,30 @@
 
   Args:
     symbols: A SymbolGroup containing all symbols.
-    min_symbol_size: The minimum byte size needed for a symbol to be included.
+    include_all_symbols: If true, include all symbols in the data file.
   """
   file_nodes = {}
   components = IndexedSet()
-  total_size = 0
 
   # Build a container for symbols smaller than min_symbol_size
   small_symbols = collections.defaultdict(dict)
 
+  # Dex methods (type "m") are whitelisted for the method_count mode on the
+  # UI. It's important to see details on all the methods.
+  dex_symbols = symbols.WhereIsDex()
+  ordered_symbols = dex_symbols.Inverted().Sorted()
+  if include_all_symbols:
+    symbol_count = len(ordered_symbols)
+  else:
+    symbol_count = _DEFAULT_SYMBOL_COUNT - len(dex_symbols)
+
+  main_symbols = dex_symbols + ordered_symbols[:symbol_count]
+  extra_symbols = ordered_symbols[symbol_count:]
+
   # Bundle symbols by the file they belong to,
   # and add all the file buckets into file_nodes
-  for symbol in symbols:
+  for symbol in main_symbols:
     symbol_type = _GetSymbolType(symbol)
-    if symbol_type not in _SMALL_SYMBOL_DESCRIPTIONS:
-      symbol_type = _SYMBOL_TYPE_OTHER
-
-    total_size += symbol.pss
     symbol_size = round(symbol.pss, 2)
     if symbol_size.is_integer():
       symbol_size = int(symbol_size)
@@ -118,51 +143,44 @@
     if symbol.IsDelta() and symbol.diff_status == models.DIFF_STATUS_REMOVED:
       symbol_count = -1
 
-    path = symbol.source_path or symbol.object_path
-    file_node = file_nodes.get(path)
-    if file_node is None:
-      component_index = components.GetOrAdd(symbol.component)
-      file_node = {
-        _COMPACT_FILE_PATH_KEY: path,
-        _COMPACT_FILE_COMPONENT_INDEX_KEY: component_index,
-        _COMPACT_FILE_SYMBOLS_KEY: [],
-      }
-      file_nodes[path] = file_node
+    file_node = _GetOrAddFileNode(symbol, file_nodes, components)
 
-    # Dex methods (type "m") are whitelisted for the method_count mode on the
-    # UI. It's important to see details on all the methods, and most fall below
-    # the default byte size.
     is_dex_method = symbol_type == _SYMBOL_TYPE_DEX_METHOD
-    if is_dex_method or abs(symbol_size) >= min_symbol_size:
-      symbol_entry = {
-        _COMPACT_SYMBOL_NAME_KEY: symbol.template_name,
-        _COMPACT_SYMBOL_TYPE_KEY: symbol_type,
-        _COMPACT_SYMBOL_BYTE_SIZE_KEY: symbol_size,
-      }
-      # We use symbol count for the method count mode in the diff mode report.
-      # Negative values are used to indicate a symbol was removed, so it should
-      # count as -1 rather than the default, 1.
-      # We don't care about accurate counts for other symbol types currently,
-      # so this data is only included for methods.
-      if is_dex_method and symbol_count != 1:
-        symbol_entry[_COMPACT_SYMBOL_COUNT_KEY] = symbol_count
-      file_node[_COMPACT_FILE_SYMBOLS_KEY].append(symbol_entry)
-    else:
-      small_type_symbol = small_symbols[path].get(symbol_type)
-      if small_type_symbol is None:
-        small_type_symbol = {
-          _COMPACT_SYMBOL_NAME_KEY: _SMALL_SYMBOL_DESCRIPTIONS[symbol_type],
-          _COMPACT_SYMBOL_TYPE_KEY: symbol_type,
-          _COMPACT_SYMBOL_BYTE_SIZE_KEY: 0,
-        }
-        small_symbols[path][symbol_type] = small_type_symbol
-        file_node[_COMPACT_FILE_SYMBOLS_KEY].append(small_type_symbol)
+    symbol_entry = {
+      _COMPACT_SYMBOL_NAME_KEY: symbol.template_name,
+      _COMPACT_SYMBOL_TYPE_KEY: symbol_type,
+      _COMPACT_SYMBOL_BYTE_SIZE_KEY: symbol_size,
+    }
+    # We use symbol count for the method count mode in the diff mode report.
+    # Negative values are used to indicate a symbol was removed, so it should
+    # count as -1 rather than the default, 1.
+    # We don't care about accurate counts for other symbol types currently,
+    # so this data is only included for methods.
+    if is_dex_method and symbol_count != 1:
+      symbol_entry[_COMPACT_SYMBOL_COUNT_KEY] = symbol_count
+    file_node[_COMPACT_FILE_SYMBOLS_KEY].append(symbol_entry)
 
-      small_type_symbol[_COMPACT_SYMBOL_BYTE_SIZE_KEY] += symbol_size
+  for symbol in extra_symbols:
+    symbol_type = _GetSymbolType(symbol)
+
+    file_node = _GetOrAddFileNode(symbol, file_nodes, components)
+    path = file_node[_COMPACT_FILE_PATH_KEY]
+
+    small_type_symbol = small_symbols[path].get(symbol_type)
+    if small_type_symbol is None:
+      small_type_symbol = {
+        _COMPACT_SYMBOL_NAME_KEY: _SMALL_SYMBOL_DESCRIPTIONS[symbol_type],
+        _COMPACT_SYMBOL_TYPE_KEY: symbol_type,
+        _COMPACT_SYMBOL_BYTE_SIZE_KEY: 0,
+      }
+      small_symbols[path][symbol_type] = small_type_symbol
+      file_node[_COMPACT_FILE_SYMBOLS_KEY].append(small_type_symbol)
+
+    small_type_symbol[_COMPACT_SYMBOL_BYTE_SIZE_KEY] += symbol.pss
 
   meta = {
     'components': components.value_list,
-    'total': total_size,
+    'total': symbols.pss,
   }
   return meta, file_nodes.values()
 
@@ -203,9 +221,9 @@
   parser.add_argument('--report-dir', metavar='PATH', required=True,
                       help='Write output to the specified directory. An HTML '
                             'report is generated here.')
-  parser.add_argument('--min-symbol-size', type=float, default=1024,
-                      help='Minimum size (PSS) for a symbol to be included as '
-                           'an independent node.')
+  parser.add_argument('--all-symbols', action='store_true',
+                      help='Include all symbols. Will cause the data file to '
+                           'take longer to load.')
   parser.add_argument('--diff-with',
                       help='Diffs the input_file against an older .size file')
 
@@ -222,12 +240,15 @@
     before_size_info = archive.LoadAndPostProcessSizeInfo(args.diff_with)
     after_size_info = size_info
     size_info = diff.Diff(before_size_info, after_size_info)
-  symbols = size_info.raw_symbols
+    symbols = size_info.raw_symbols
+    symbols = symbols.WhereDiffStatusIs(models.DIFF_STATUS_UNCHANGED).Inverted()
+  else:
+    symbols = size_info.raw_symbols
 
   template_src = os.path.join(os.path.dirname(__file__), 'template_tree_view')
   _CopyTreeViewTemplateFiles(template_src, args.report_dir)
   logging.info('Creating JSON objects')
-  meta, tree_nodes = _MakeTreeViewList(symbols, args.min_symbol_size)
+  meta, tree_nodes = _MakeTreeViewList(symbols, args.all_symbols)
   meta.update({
     'diff_mode': bool(args.diff_with),
     'section_sizes': size_info.section_sizes,
diff --git a/tools/binary_size/libsupersize/template_tree_view/index.html b/tools/binary_size/libsupersize/template_tree_view/index.html
index 5803733..4ce8b99 100644
--- a/tools/binary_size/libsupersize/template_tree_view/index.html
+++ b/tools/binary_size/libsupersize/template_tree_view/index.html
@@ -67,7 +67,6 @@
         }
 
         .options {
-            display: none;
             grid-area: options;
         }
 
diff --git a/tools/binary_size/libsupersize/template_tree_view/shared.js b/tools/binary_size/libsupersize/template_tree_view/shared.js
index 03644a02..6892a57 100644
--- a/tools/binary_size/libsupersize/template_tree_view/shared.js
+++ b/tools/binary_size/libsupersize/template_tree_view/shared.js
@@ -84,7 +84,7 @@
 const _CONTAINER_TYPE_SET = new Set(Object.values(_CONTAINER_TYPES));
 
 /** Type for a dex method symbol */
-const _DEX_METHOD_SYMBOL_TYPE = 'm'
+const _DEX_METHOD_SYMBOL_TYPE = 'm';
 /** Type for an 'other' symbol */
 const _OTHER_SYMBOL_TYPE = 'o';
 
@@ -122,3 +122,20 @@
     }
   }
 }
+
+/**
+ * Limit how frequently `func` is called.
+ * @template {T}
+ * @param {T & Function} func
+ * @param {number} wait Time to wait before func can be called again (ms).
+ * @returns {T}
+ */
+function debounce(func, wait) {
+  /** @type {number} */
+  let timeoutId;
+  function debounced (...args) {
+    clearTimeout(timeoutId);
+    timeoutId = setTimeout(() => func(...args), wait);
+  };
+  return /** @type {any} */ (debounced);
+}
diff --git a/tools/binary_size/libsupersize/template_tree_view/tree-ui.js b/tools/binary_size/libsupersize/template_tree_view/tree-ui.js
index 70113d2..22bf84d 100644
--- a/tools/binary_size/libsupersize/template_tree_view/tree-ui.js
+++ b/tools/binary_size/libsupersize/template_tree_view/tree-ui.js
@@ -322,16 +322,36 @@
 }
 
 {
+  class ProgressBar {
+    /** @param {string} id */
+    constructor(id) {
+      /** @type {HTMLProgressElement} */
+      this._element = document.getElementById(id);
+      this.lastValue = this._element.value;
+    }
+
+    setValue(val) {
+      if (val === 0 || val >= this.lastValue) {
+        this._element.value = val;
+        this.lastValue = val;
+      } else {
+        // Reset to 0 so the progress bar doesn't animate backwards.
+        this.setValue(0);
+        requestAnimationFrame(() => this.setValue(val));
+      }
+    }
+  }
+
   /** @type {HTMLUListElement} */
   const _symbolTree = document.getElementById('symboltree');
-  /** @type {HTMLProgressElement} */
-  const _progress = document.getElementById('progress');
+  const _progress = new ProgressBar('progress');
 
   /**
    * Displays the given data as a tree view
-   * @param {TreeProgress} param0
+   * @param {TreeProgress} message
    */
-  function displayTree({root, percent, diffMode, error}) {
+  function displayTree(message) {
+    const {root, percent, diffMode, error} = message;
     /** @type {DocumentFragment | null} */
     let rootElement = null;
     if (root) {
@@ -344,21 +364,25 @@
     }
     state.set('diff_mode', diffMode ? 'on' : null);
 
-    requestAnimationFrame(() => {
-      _progress.value = percent;
-      if (error) {
-        document.body.classList.add('error');
-      } else {
-        document.body.classList.remove('error');
-      }
-      if (diffMode) {
-        document.body.classList.add('diff');
-      } else {
-        document.body.classList.remove('diff');
-      }
+    // Double requestAnimationFrame ensures that the code inside executes in a
+    // different frame than the above tree element creation.
+    requestAnimationFrame(() =>
+      requestAnimationFrame(() => {
+        _progress.setValue(percent);
+        if (error) {
+          document.body.classList.add('error');
+        } else {
+          document.body.classList.remove('error');
+        }
+        if (diffMode) {
+          document.body.classList.add('diff');
+        } else {
+          document.body.classList.remove('diff');
+        }
 
-      dom.replace(_symbolTree, rootElement);
-    });
+        dom.replace(_symbolTree, rootElement);
+      })
+    );
   }
 
   treeReady.then(displayTree);
@@ -366,13 +390,13 @@
 
   form.addEventListener('change', event => {
     if (event.target.dataset.dynamic == null) {
-      _progress.value = 0;
+      _progress.setValue(0);
       worker.loadTree().then(displayTree);
     }
   });
   form.addEventListener('submit', event => {
     event.preventDefault();
-    _progress.value = 0;
+    _progress.setValue(0);
     worker.loadTree().then(displayTree);
   });
 }
diff --git a/tools/binary_size/libsupersize/template_tree_view/tree-worker.js b/tools/binary_size/libsupersize/template_tree_view/tree-worker.js
index bd736181..00d7954 100644
--- a/tools/binary_size/libsupersize/template_tree_view/tree-worker.js
+++ b/tools/binary_size/libsupersize/template_tree_view/tree-worker.js
@@ -37,29 +37,32 @@
 
 const _PATH_SEP = '/';
 
-/**
- * Return the basename of the pathname 'path'. In a file path, this is the name
- * of the file and its extension. In a folder path, this is the name of the
- * folder.
- * @param {string} path Path to find basename of.
- * @param {string} sep Path seperator, such as '/'.
- */
-function basename(path, sep) {
-  const sepIndex = path.lastIndexOf(sep);
-  const pathIndex = path.lastIndexOf(_PATH_SEP);
-  return path.substring(Math.max(sepIndex, pathIndex) + 1);
+/** @param {FileEntry} fileEntry */
+function getSourcePath(fileEntry) {
+  return fileEntry[_KEYS.SOURCE_PATH];
 }
 
 /**
- * Return the basename of the pathname 'path'. In a file path, this is the
+ * Find the last index of either '/' or `sep` in the given path.
+ * @param {string} path
+ * @param {string} sep
+ */
+function lastIndexOf(path, sep) {
+  if (sep === _PATH_SEP) {
+    return path.lastIndexOf(_PATH_SEP);
+  } else {
+    return Math.max(path.lastIndexOf(sep), path.lastIndexOf(_PATH_SEP));
+  }
+}
+
+/**
+ * Return the dirname of the pathname 'path'. In a file path, this is the
  * full path of its folder.
  * @param {string} path Path to find dirname of.
  * @param {string} sep Path seperator, such as '/'.
  */
 function dirname(path, sep) {
-  const sepIndex = path.lastIndexOf(sep);
-  const pathIndex = path.lastIndexOf(_PATH_SEP);
-  return path.substring(0, Math.max(sepIndex, pathIndex));
+  return path.substring(0, lastIndexOf(path, sep));
 }
 
 /**
@@ -73,19 +76,19 @@
 
 /**
  * Make a node with some default arguments
- * @param {Partial<TreeNode> & {shortName:string}} options
+ * @param {Partial<TreeNode>} options
  * Values to use for the node. If a value is
  * omitted, a default will be used instead.
  * @returns {TreeNode}
  */
 function createNode(options) {
-  const {idPath, type, shortName, size = 0, childStats = {}} = options;
+  const {idPath, type, shortNameIndex, size = 0, childStats = {}} = options;
   return {
     children: [],
     parent: null,
     childStats,
     idPath,
-    shortNameIndex: idPath.lastIndexOf(shortName),
+    shortNameIndex,
     size,
     type,
   };
@@ -114,7 +117,7 @@
 
     this.rootNode = createNode({
       idPath: this._sep,
-      shortName: this._sep,
+      shortNameIndex: 0,
       type: this._containerType(this._sep),
     });
     /** @type {Map<string, TreeNode>} Cache for directory nodes */
@@ -131,37 +134,38 @@
    * Link a node to a new parent. Will go up the tree to update parent sizes to
    * include the new child.
    * @param {TreeNode} node Child node.
-   * @param {TreeNode} parent New parent node.
+   * @param {TreeNode} directParent New parent node.
    */
-  static _attachToParent(node, parent) {
+  static _attachToParent(node, directParent) {
     // Link the nodes together
-    parent.children.push(node);
-    node.parent = parent;
+    directParent.children.push(node);
+    node.parent = directParent;
 
     const additionalSize = node.size;
     const additionalStats = Object.entries(node.childStats);
 
     // Update the size and childStats of all ancestors
-    while (node != null && node.parent != null) {
-      const [containerType, lastBiggestType] = node.parent.type;
+    while (node.parent != null) {
+      const {parent} = node;
+      const [containerType, lastBiggestType] = parent.type;
       let {size: lastBiggestSize = 0} =
-        node.parent.childStats[lastBiggestType] || {};
+        parent.childStats[lastBiggestType] || {};
       for (const [type, stat] of additionalStats) {
-        const parentStat = node.parent.childStats[type] || {size: 0, count: 0};
+        const parentStat = parent.childStats[type] || {size: 0, count: 0};
 
         parentStat.size += stat.size;
         parentStat.count += stat.count;
-        node.parent.childStats[type] = parentStat;
+        parent.childStats[type] = parentStat;
 
         const absSize = Math.abs(parentStat.size);
         if (absSize > lastBiggestSize) {
-          node.parent.type = `${containerType}${type}`;
+          parent.type = `${containerType}${type}`;
           lastBiggestSize = absSize;
         }
       }
 
-      node.parent.size += additionalSize;
-      node = node.parent;
+      parent.size += additionalSize;
+      node = parent;
     }
   }
 
@@ -197,7 +201,7 @@
           if (classNode == null) {
             classNode = createNode({
               idPath: classIdPath,
-              shortName: classIdPath.slice(childNode.shortNameIndex),
+              shortNameIndex: childNode.shortNameIndex,
               type: _CONTAINER_TYPES.JAVA_CLASS,
             });
             javaClassContainers.set(classIdPath, classNode);
@@ -303,7 +307,7 @@
       if (parentNode == null) {
         parentNode = createNode({
           idPath: parentPath,
-          shortName: basename(parentPath, this._sep),
+          shortNameIndex: lastIndexOf(parentPath, this._sep) + 1,
           type: this._containerType(childNode.idPath),
         });
         this._parents.set(parentPath, parentNode);
@@ -323,15 +327,11 @@
    * @param {FileEntry} fileEntry File entry from data file
    */
   addFileEntry(fileEntry) {
-    // make path for this
-    const filePath = fileEntry[_KEYS.SOURCE_PATH];
-    // insert zero-width spaces after certain characters to indicate to the
-    // browser it could add a line break there on small screen sizes.
     const idPath = this._getPath(fileEntry);
     // make node for this
     const fileNode = createNode({
       idPath,
-      shortName: basename(filePath, this._sep),
+      shortNameIndex: lastIndexOf(idPath, this._sep) + 1,
       type: _CONTAINER_TYPES.FILE,
     });
     // build child nodes for this file's symbols and attach to self
@@ -342,7 +342,7 @@
       const symbolNode = createNode({
         // Join file path to symbol name with a ":"
         idPath: `${idPath}:${symbol[_KEYS.SYMBOL_NAME]}`,
-        shortName: symbol[_KEYS.SYMBOL_NAME],
+        shortNameIndex: idPath.length + 1,
         size,
         type: symbol[_KEYS.TYPE],
         childStats: {[type]: {size, count}},
@@ -366,6 +366,9 @@
    * Finalize the creation of the tree and return the root node.
    */
   build() {
+    this._getPath = () => '';
+    this._filterTest = () => false;
+    this._parents.clear();
     return this.rootNode;
   }
 
@@ -431,6 +434,8 @@
   constructor(url) {
     this._controller = new AbortController();
     this._url = url;
+    /** @type {Uint8Array | null} */
+    this._cache = null;
   }
 
   /**
@@ -446,41 +451,76 @@
   }
 
   /**
-   * Transforms a binary stream into a newline delimited JSON (.ndjson) stream.
-   * Each yielded value corresponds to one line in the stream.
-   * @returns {AsyncIterable<Meta | FileEntry>}
+   * Yields binary chunks as Uint8Arrays. After a complete run, the bytes are
+   * cached and future calls will yield the cached Uint8Array instead.
    */
-  async *newlineDelimtedJsonStream() {
+  async *arrayBufferStream() {
+    if (this._cache) {
+      yield this._cache;
+      return;
+    }
+
     const response = await this.fetch();
-    // Are streams supported?
+    let result;
+    // Use streams, if supported, so that we can show in-progress data instead
+    // of waiting for the entire data file to download. The file can be >100 MB,
+    // so on streams ensure slow connections still see some data.
     if (response.body) {
-      const decoder = new TextDecoder();
-      const decodeOptions = {stream: true};
       const reader = response.body.getReader();
 
-      let buffer = '';
+      /** @type {Uint8Array[]} Store received bytes to merge later */
+      let buffer = [];
+      /** Total size of received bytes */
+      let byteSize = 0;
       while (true) {
         // Read values from the stream
         const {done, value} = await reader.read();
         if (done) break;
 
-        // Convert binary values to text chunks.
-        const chunk = decoder.decode(value, decodeOptions);
-        buffer += chunk;
-        // Split the chunk base on newlines,
-        // and turn each complete line into JSON
-        const lines = buffer.split('\n');
-        [buffer] = lines.splice(lines.length - 1, 1);
+        const chunk = new Uint8Array(value, 0, value.length);
+        yield chunk;
+        buffer.push(chunk);
+        byteSize += chunk.length;
+      }
 
-        for (const line of lines) {
-          yield JSON.parse(line);
-        }
+      // We just cache a single typed array to save some memory and make future
+      // runs consistent with the no streams mode.
+      result = new Uint8Array(byteSize);
+      let i = 0;
+      for (const chunk of buffer) {
+        result.set(chunk, i);
+        i += chunk.length;
       }
     } else {
       // In-memory version for browsers without stream support
-      const text = await response.text();
-      for (const line of text.split('\n')) {
-        if (line) yield JSON.parse(line);
+      result = new Uint8Array(await response.arrayBuffer());
+      yield result;
+    }
+
+    this._cache = result;
+  }
+
+  /**
+   * Transforms a binary stream into a newline delimited JSON (.ndjson) stream.
+   * Each yielded value corresponds to one line in the stream.
+   * @returns {AsyncIterable<Meta | FileEntry>}
+   */
+  async *newlineDelimtedJsonStream() {
+    const decoder = new TextDecoder();
+    const decoderArgs = {stream: true};
+    let textBuffer = '';
+
+    for await (const bytes of this.arrayBufferStream()) {
+      if (this._controller.signal.aborted) {
+        throw new DOMException('Request was aborted', 'AbortError');
+      }
+
+      textBuffer += decoder.decode(bytes, decoderArgs);
+      const lines = textBuffer.split('\n');
+      [textBuffer] = lines.splice(lines.length - 1, 1);
+
+      for (const line of lines) {
+        yield JSON.parse(line);
       }
     }
   }
@@ -517,11 +557,18 @@
     }
   }
 
+  /** @type {Array<(symbolNode: TreeNode) => boolean>} */
+  const filters = [];
+
   /** Ensure symbol size is past the minimum */
-  const checkSize = s => Math.abs(s.size) >= minSymbolSize;
+  if (minSymbolSize > 0) {
+    filters.push(s => Math.abs(s.size) >= minSymbolSize);
+  }
+
   /** Ensure the symbol size wasn't filtered out */
-  const checkType = s => typeFilter.has(s.type);
-  const filters = [checkSize, checkType];
+  if (typeFilter.size < _SYMBOL_TYPE_SET.size) {
+    filters.push(s => typeFilter.has(s.type));
+  }
 
   if (includeRegex) {
     const regex = new RegExp(includeRegex);
@@ -551,6 +598,7 @@
  * Assemble a tree when this worker receives a message.
  * @param {string} options Query string containing options for the builder.
  * @param {(msg: TreeProgress) => void} onProgress
+ * @returns {Promise<TreeProgress>}
  */
 async function buildTree(options, onProgress) {
   const {groupBy, filterTest} = parseOptions(options);
@@ -562,19 +610,14 @@
   const getPathMap = {
     component(fileEntry) {
       const component = meta.components[fileEntry[_KEYS.COMPONENT_INDEX]];
-      const path = getPathMap.source_path(fileEntry);
-      return (component || '(No component)') + '>' + path;
+      const path = getSourcePath(fileEntry);
+      return `${component || '(No component)'}>${path}`;
     },
-    source_path(fileEntry) {
-      return fileEntry[_KEYS.SOURCE_PATH];
-    },
-  };
-  const sepMap = {
-    component: '>',
+    source_path: getSourcePath,
   };
 
   builder = new TreeBuilder({
-    sep: sepMap[groupBy],
+    sep: groupBy === 'component' ? '>' : _PATH_SEP,
     getPath: getPathMap[groupBy],
     filterTest,
   });
@@ -616,11 +659,9 @@
     onProgress(message);
   }
 
-  /** @type {number} ID from setInterval */
-  let interval = null;
   try {
     // Post partial state every second
-    interval = setInterval(postToUi, 1000);
+    let lastBatchSent = Date.now();
     for await (const dataObj of fetcher.newlineDelimtedJsonStream()) {
       if (meta == null) {
         // First line of data is used to store meta information.
@@ -628,22 +669,26 @@
         postToUi();
       } else {
         builder.addFileEntry(/** @type {FileEntry} */ (dataObj));
+        const currentTime = Date.now();
+        if (currentTime - lastBatchSent > 500) {
+          postToUi();
+          await Promise.resolve();  // Pause loop to check for worker messages
+          lastBatchSent = currentTime;
+        }
       }
     }
-    clearInterval(interval);
 
     return createProgressMessage({
       root: builder.build(),
       percent: 1,
     });
   } catch (error) {
-    if (interval != null) clearInterval(interval);
     if (error.name === 'AbortError') {
       console.info(error.message);
     } else {
       console.error(error);
-      return createProgressMessage({error});
     }
+    return createProgressMessage({error});
   }
 }
 
@@ -665,10 +710,11 @@
 /**
  * Call the requested action function with the given data. If an error is thrown
  * or rejected, post the error message to the UI thread.
- * @param {MessageEvent} event Event for when this worker receives a message.
+ * @param {number} id Unique message ID.
+ * @param {string} action Action type, corresponding to a key in `actions.`
+ * @param {any} data Data to supply to the action function.
  */
-self.onmessage = async event => {
-  const {id, action, data} = event.data;
+async function runAction(id, action, data) {
   try {
     const result = await actions[action](data);
     // @ts-ignore
@@ -678,4 +724,22 @@
     self.postMessage({id, error: err.message});
     throw err;
   }
+}
+
+const runActionDebounced = debounce(runAction, 0);
+
+/**
+ * @param {MessageEvent} event Event for when this worker receives a message.
+ */
+self.onmessage = async event => {
+  const {id, action, data} = event.data;
+  if (action === 'load') {
+    // Loading large files will block the worker thread until complete or when
+    // an await statement is reached. During this time, multiple load messages
+    // can pile up due to filters being adjusted. We debounce the load call
+    // so that only the last message is read (the current set of filters).
+    runActionDebounced(id, action, data);
+  } else {
+    runAction(id, action, data);
+  }
 };
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 2dfc541..b3d91a1 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -38,14 +38,12 @@
       'Android Cronet Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon',
       'Android Cronet Builder (dbg)': 'android_cronet_debug_static_bot_arm_no_neon',
       'Android Cronet Builder Asan': 'android_cronet_release_bot_minimal_symbols_arm_no_neon_clang_asan',
-      'Android Cronet Data Reduction Proxy Builder': 'android_cronet_data_reduction_proxy_release_bot_minimal_symbols_arm_no_neon',
       'Android Cronet KitKat Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon',
       'Android Cronet Lollipop Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon',
       'Android Cronet Marshmallow 64bit Builder': 'android_cronet_release_bot_minimal_symbols_arm64',
       'Android Cronet Marshmallow 64bit Perf': 'android_cronet_release_bot_minimal_symbols_arm64',
       'Android Cronet x86 Builder': 'android_cronet_release_bot_minimal_symbols_x86',
       'Android Cronet x86 Builder (dbg)': 'android_cronet_debug_static_bot_x86',
-      'Android N5X Swarm Builder': 'android_release_bot_minimal_symbols_arm64',
       'Android arm Builder (dbg)': 'android_debug_static_bot',
       'Android arm64 Builder (dbg)': 'android_debug_static_bot_arm64',
       'Android x64 Builder (dbg)': 'android_debug_static_bot_x64',
@@ -60,11 +58,8 @@
     'chromium.android.fyi': {
       'Android Cronet Builder (dbg)': 'android_cronet_debug_static_bot_arm_no_neon',
       'Android Cronet Builder Asan': 'android_cronet_release_bot_minimal_symbols_arm_no_neon_clang_asan',
-      'Android Cronet Data Reduction Proxy Builder': 'android_cronet_data_reduction_proxy_release_bot_minimal_symbols_arm_no_neon',
       'Android Cronet KitKat Builder': 'android_cronet_release_bot_minimal_symbols_arm_no_neon',
       'Memory Infra Tester': 'android_release_thumb_bot',
-      'NDK Next MIPS Builder':
-        'android_ndk_next_release_bot_minimal_symbols_mipsel',
       'NDK Next arm Builder':
         'android_ndk_next_release_bot_minimal_symbols',
       'NDK Next arm64 Builder':
@@ -93,9 +88,6 @@
     },
 
     'chromium.chromiumos': {
-      'ChromiumOS amd64-generic Compile': 'cros_chrome_sdk',
-      'ChromiumOS daisy Compile': 'cros_chrome_sdk',
-      'Linux ChromiumOS Builder (dbg)': 'chromeos_with_codecs_debug_bot',
       'Linux ChromiumOS Full': 'chromeos_with_codecs_release_bot',
 
       'chromeos-amd64-generic-rel': 'cros_chrome_sdk',
@@ -185,11 +177,9 @@
       'Win Goma Canary LocalOutputCache': 'release_bot_x86_minimal_symbols',
       'WinMSVC64 Goma Canary': 'win_msvc_release_bot',
 
-      'EarlGreyiOS': 'ios',
       'fuchsia-fyi-arm64-rel': 'release_bot_fuchsia_arm64',
       'fuchsia-fyi-x64-dbg': 'debug_bot_fuchsia',
       'fuchsia-fyi-x64-rel': 'release_bot_fuchsia',
-      'Fuchsia': 'release_bot_fuchsia',
 
       'ios-device-goma-canary-clobber': 'ios',
 
@@ -198,19 +188,12 @@
       'Jumbo Linux x64': 'jumbo_large_chunks_release_bot_minimal_symbols',
       'Jumbo Mac': 'jumbo_release_bot_minimal_symbols',
       'Jumbo Win x64': 'jumbo_release_bot_minimal_symbols',
-      'MD Top Chrome ChromeOS material-hybrid': 'chromeos_with_codecs_debug_bot',
-      'MD Top Chrome ChromeOS non-material': 'chromeos_with_codecs_debug_bot',
-      'MD Top Chrome Win material': 'debug_bot_minimal_symbols',
-      'MD Top Chrome Linux material': 'debug_bot',
       'Libfuzzer Upload Linux ASan': 'libfuzzer_asan_release_bot',
       'Libfuzzer Upload Linux ASan Debug': 'libfuzzer_asan_debug_bot',
       'Libfuzzer Upload Linux MSan': 'libfuzzer_msan_release_bot',
       'Libfuzzer Upload Linux UBSan': 'libfuzzer_ubsan_release_bot',
       'Libfuzzer Upload Mac ASan': 'libfuzzer_mac_asan_release_bot',
       'Linux ARM': 'release_bot_arm',
-      'Linux ARM (dbg)': 'debug_bot_arm',
-      'Linux ARM64': 'release_bot_arm64',
-      'Linux ARM64 (dbg)': 'debug_bot_arm64',
       'Linux Clang Analyzer': 'linux_chromium_analysis',
       'Linux remote_run Builder': 'release_bot',
       'Linux remote_run Tester': 'release_bot',
@@ -230,7 +213,6 @@
       'Win 10 Fast Ring': 'release_trybot',
       'Windows deterministic': 'release_bot_x86_minimal_symbols',
       'Windows Clang deterministic': 'clang_release_bot_minimal_symbols_x86',
-      'Windows Clang Analyzer': 'windows_chromium_analysis',
       'win-annotator-rel': 'release_bot',
     },
 
@@ -540,8 +522,6 @@
       'android_mojo': 'android_release_trybot_arm64',
       'android_n5x_swarming_dbg': 'android_debug_trybot_arm64',
       'android_optional_gpu_tests_rel': 'gpu_tests_android_release_trybot_arm64',
-      'android_unswarmed_n5_rel': 'android_release_trybot',
-      'android_unswarmed_n5x_rel': 'android_release_trybot_arm64',
       'android_unswarmed_pixel_aosp': 'android_debug_trybot_arm64',
       'cast_shell_android': 'android_cast_debug_static_bot_compile_only',
       'gpu-manual-try-android-p-pixel-2-32': 'gpu_tests_android_vulkan_release_trybot',
@@ -770,11 +750,6 @@
       'dcheck_always_on',
     ],
 
-    'android_cronet_data_reduction_proxy_release_bot_minimal_symbols_arm_no_neon': [
-      'android', 'cronet', 'release_bot', 'minimal_symbols', 'arm_no_neon',
-      'strip_debug_info',
-    ],
-
     'android_cronet_debug_static_bot_arm64': [
       'android', 'cronet', 'debug_static_bot', 'arm64',
     ],
@@ -787,11 +762,6 @@
       'android', 'cronet', 'debug_static_bot', 'x86',
     ],
 
-    'android_cronet_data_reduction_proxy_release_bot_minimal_symbols_arm_no_neon': [
-      'android', 'cronet', 'release_bot', 'minimal_symbols', 'arm_no_neon',
-      'strip_debug_info',
-    ],
-
     'android_cronet_debug_static_bot_arm64': [
       'android', 'cronet', 'debug_static_bot', 'arm64',
     ],
@@ -888,10 +858,6 @@
       'android', 'ndk_next', 'release_bot', 'minimal_symbols', 'arm64', 'strip_debug_info',
     ],
 
-    'android_ndk_next_release_bot_minimal_symbols_mipsel': [
-      'android', 'ndk_next', 'release_bot', 'minimal_symbols', 'mipsel', 'strip_debug_info',
-    ],
-
     'android_ndk_next_release_bot_minimal_symbols_x64': [
       'android', 'ndk_next', 'release_bot', 'minimal_symbols', 'x64', 'strip_debug_info',
     ],
@@ -1203,14 +1169,6 @@
       'debug_bot',
     ],
 
-    'debug_bot_arm': [
-      'debug_bot', 'arm',
-    ],
-
-    'debug_bot_arm64': [
-      'debug_bot', 'arm64',
-    ],
-
     'debug_bot_enable_blink_heap_incremental_marking': [
       'debug_bot', 'enable_blink_heap_incremental_marking',
     ],
@@ -1404,10 +1362,6 @@
       'analysis'
     ],
 
-    'windows_chromium_analysis': [
-      'analysis'
-    ],
-
     'mac_views_browser_release_bot': [
       'mac_views_browser', 'release_bot',
     ],
@@ -1481,10 +1435,6 @@
       'release_bot', 'arm',
     ],
 
-    'release_bot_arm64': [
-      'release_bot', 'arm64',
-    ],
-
     'release_bot_chrome_with_codecs': [
       'release_bot', 'chrome_with_codecs',
     ],
@@ -1938,10 +1888,6 @@
       'gn_args': 'symbol_level=1',
     },
 
-    'mipsel': {
-      'gn_args': 'target_cpu="mipsel"',
-    },
-
     'msan': {
       'gn_args': 'is_msan=true msan_track_origins=2 use_prebuilt_instrumented_libraries=true',
     },
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 242743f..27b1964 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -15263,6 +15263,84 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="ProfileChooser_AddressesClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User clicked 'Addresses' in the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_CloseAllClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User clicked 'Close All Windows' in the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_GuestClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User opened a guest session from the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_ManageClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User clicked 'Manage people' in the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_PasswordsClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User clicked 'Passwords' in the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_PaymentsClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User clicked 'Payments' in the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_ProfileClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User clicked a profile to switch in the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_Show">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>The user opened the profile chooser menu.</description>
+</action>
+
+<action name="ProfileChooser_SignInAgainClicked">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    User clicked 'Sign in again' in the profile chooser menu.
+  </description>
+</action>
+
+<action name="ProfileChooser_SignInAgainDisplayed">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <description>
+    The profile chooser opened with 'Sign in again' button.
+  </description>
+</action>
+
 <action name="Redo">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 88e9494..251155f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -28970,6 +28970,7 @@
   <int value="1115476442" label="PolicyTool:disabled"/>
   <int value="1115635149" label="EnableUnifiedMultiDeviceSetup:enabled"/>
   <int value="1116593018" label="CaptureThumbnailOnLoadFinished:disabled"/>
+  <int value="1117795262" label="BloatedRendererDetection:disabled"/>
   <int value="1118109174" label="enable-launcher-search-provider-api"/>
   <int value="1126061778" label="CaptureThumbnailOnLoadFinished:enabled"/>
   <int value="1127183523" label="PassiveEventListenersDueToFling:enabled"/>
@@ -29428,6 +29429,7 @@
   <int value="2067735898" label="WebVrAutopresentFromIntent:enabled"/>
   <int value="2069999572"
       label="AllowSignedHTTPExchangeCertsWithoutExtension:disabled"/>
+  <int value="2071229145" label="BloatedRendererDetection:enabled"/>
   <int value="2071340353" label="progress-bar-completion"/>
   <int value="2071461362" label="disable-credit-card-scan"/>
   <int value="2075207488" label="AutomaticPasswordGeneration:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index a877f51..0a2d2b6 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -78322,6 +78322,15 @@
   <summary>Size of the web data database.</summary>
 </histogram>
 
+<histogram name="ProfileChooser.HasProfilesShown" enum="BooleanShown">
+  <owner>vasilii@chromium.org</owner>
+  <owner>ewald@chromium.org</owner>
+  <summary>
+    Whether any profile was shown in the list when the profile chooser was
+    opened.
+  </summary>
+</histogram>
+
 <histogram name="ProfileReset.ResetRequestOrigin"
     enum="ProfileResetRequestOriginEnum">
   <owner>alito@chromium.org</owner>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 153cc4b..089a047 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -320,7 +320,7 @@
           ],
         }
       ],
-      'platform': 'android',
+      'platform': 'android-webview',
       'dimension': {
         'pool': 'chrome.tests.perf-webview',
         'os': 'Android',
@@ -341,7 +341,7 @@
           ],
         }
       ],
-      'platform': 'android',
+      'platform': 'android-webview',
       'dimension': {
         'pool': 'chrome.tests.perf-webview',
         'os': 'Android',
diff --git a/tools/perf/core/perf_json_config_validator.py b/tools/perf/core/perf_json_config_validator.py
index 89327e6f..2c3dbbf 100644
--- a/tools/perf/core/perf_json_config_validator.py
+++ b/tools/perf/core/perf_json_config_validator.py
@@ -39,6 +39,14 @@
   return options.shard_file
 
 
+def _ParseBrowserFlags(args):
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--browser')
+  parser.add_argument('--webview-embedder-apk')
+  options, _ = parser.parse_known_args(args)
+  return options
+
+
 _SHARD_MAP_DIR = os.path.join(os.path.dirname(__file__), 'shard_maps')
 
 
@@ -68,14 +76,38 @@
             repr(shard_file_name), num_shards, repr(builder_name)))
 
 
+def _ValidateBrowserType(builder_name, test_config):
+  browser_options = _ParseBrowserFlags(test_config['args'])
+  if 'WebView' in builder_name or 'webview' in builder_name:
+    if browser_options.browser != 'android-webview':
+      raise ValueError("%s must use 'android-webview' browser type" %
+                       builder_name)
+    if not browser_options.webview_embedder_apk:
+      raise ValueError('%s must set --webview-embedder-apk flag' % builder_name)
+  elif 'Android' in builder_name or 'android' in builder_name:
+    if browser_options.browser != 'android-chromium':
+      raise ValueError("%s must use 'android-chromium' browser" %
+                       builder_name)
+  elif builder_name in ('win-10-perf', 'Win 7 Nvidia GPU Perf'):
+    if browser_options.browser != 'release_x64':
+      raise ValueError("%s must use 'release_x64' browser type" %
+                       builder_name)
+  else:  # The rest must be desktop/laptop builders
+    if browser_options.browser != 'release':
+      raise ValueError("%s must use 'release' browser type" %
+                       builder_name)
+
+
 def ValidateTestingBuilder(builder_name, builder_data):
   isolated_scripts = builder_data['isolated_scripts']
   for test_config in isolated_scripts:
     _ValidateSwarmingDimension(
         builder_name,
         swarming_dimensions=test_config['swarming'].get('dimension_sets', {}))
-    if test_config['isolate_name'] == 'performance_test_suite':
+    if (test_config['isolate_name'] in
+        ('performance_test_suite', 'performance_webview_test_suite')):
       _ValidateShardingData(builder_name, test_config)
+      _ValidateBrowserType(builder_name, test_config)
 
 
 def _IsBuilderName(name):
diff --git a/tools/perf/page_sets/data/key_mobile_sites_016.wprgo.sha1 b/tools/perf/page_sets/data/key_mobile_sites_016.wprgo.sha1
new file mode 100644
index 0000000..6428f690
--- /dev/null
+++ b/tools/perf/page_sets/data/key_mobile_sites_016.wprgo.sha1
@@ -0,0 +1 @@
+426a18415a403b36311e5611626af487a1017642
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/key_mobile_sites_017.wprgo.sha1 b/tools/perf/page_sets/data/key_mobile_sites_017.wprgo.sha1
new file mode 100644
index 0000000..4f2799d8
--- /dev/null
+++ b/tools/perf/page_sets/data/key_mobile_sites_017.wprgo.sha1
@@ -0,0 +1 @@
+58491427488fac00fc00b426980d9ce830fb7850
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/key_mobile_sites_018.wprgo.sha1 b/tools/perf/page_sets/data/key_mobile_sites_018.wprgo.sha1
new file mode 100644
index 0000000..d1afb75f
--- /dev/null
+++ b/tools/perf/page_sets/data/key_mobile_sites_018.wprgo.sha1
@@ -0,0 +1 @@
+6f54d2b7b9035366354c133e1b226082f53af263
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/key_mobile_sites_019.wprgo.sha1 b/tools/perf/page_sets/data/key_mobile_sites_019.wprgo.sha1
new file mode 100644
index 0000000..96e681c
--- /dev/null
+++ b/tools/perf/page_sets/data/key_mobile_sites_019.wprgo.sha1
@@ -0,0 +1 @@
+b2399395a1f9245fa39aff6cf2612e2f894ef4f9
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/key_mobile_sites_020.wprgo.sha1 b/tools/perf/page_sets/data/key_mobile_sites_020.wprgo.sha1
new file mode 100644
index 0000000..00e70f3
--- /dev/null
+++ b/tools/perf/page_sets/data/key_mobile_sites_020.wprgo.sha1
@@ -0,0 +1 @@
+65a6cb2f94319afba4464d6d43c934744d439d0a
\ No newline at end of file
diff --git a/tools/perf/page_sets/data/rendering_mobile.json b/tools/perf/page_sets/data/rendering_mobile.json
index 0c6bf453..00662ef8 100644
--- a/tools/perf/page_sets/data/rendering_mobile.json
+++ b/tools/perf/page_sets/data/rendering_mobile.json
@@ -276,6 +276,261 @@
         "linkedin_mobile_sync_scroll": {
             "DEFAULT": "key_mobile_sites_002.wprgo"
         },
+	"amazon_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "amazon_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "androidpolice_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "androidpolice_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "androidpolice_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "baidu_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "baidu_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "bing_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "bing_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "blogspot_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "blogspot_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "boingboing_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "boingboing_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "booking.com_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "booking.com_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "capitolvolkswagen_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "capitolvolkswagen_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "cnn_article_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "cnn_article_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "cnn_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "cnn_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "deviantart_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "deviantart_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "digg_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "digg_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "ebay_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "ebay_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "espn_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "espn_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "facebook_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "facebook_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "forecast.io_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "forecast.io_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "google_plus_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "google_plus_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "google_web_search_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "google_web_search_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "linkedin_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "linkedin_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "mlb_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "mlb_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "nytimes_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "nytimes_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "pinterest_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "pinterest_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "reddit_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "reddit_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "sfgate_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "sfgate_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "slashdot_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "slashdot_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "techcrunch_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "techcrunch_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "theverge_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "theverge_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "twitter_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "twitter_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "usatoday_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "usatoday_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wikipedia_delayed_scroll_start_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wikipedia_delayed_scroll_start_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wikipedia_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wikipedia_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wordpress_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wordpress_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "worldjournal_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "worldjournal_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wowwiki_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wowwiki_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wsj_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "wsj_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "yahoo_answers_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "yahoo_answers_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "yahoo_news_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "yahoo_news_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "youtube_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "youtube_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_016.wprgo"
+        },
+        "google_image_search_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_020.wprgo"
+        },
+        "google_image_search_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_020.wprgo"
+        },
+        "google_news_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_018.wprgo"
+        },
+        "google_news_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_018.wprgo"
+        },
+        "gsp.ro_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_019.wprgo"
+        },
+        "gsp.ro_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_019.wprgo"
+        },
+        "theverge_article_mobile_2018": {
+            "DEFAULT": "key_mobile_sites_017.wprgo"
+        },
+        "theverge_article_mobile_sync_scroll_2018": {
+            "DEFAULT": "key_mobile_sites_017.wprgo"
+        },
 	"google_docs": {
             "DEFAULT": "top_25_009.wprgo"
         },
diff --git a/tools/perf/page_sets/login_helpers/linkedin_login.py b/tools/perf/page_sets/login_helpers/linkedin_login.py
index 5843e14..f65ebd25 100644
--- a/tools/perf/page_sets/login_helpers/linkedin_login.py
+++ b/tools/perf/page_sets/login_helpers/linkedin_login.py
@@ -39,3 +39,26 @@
   search_bar_function = (
       'document.getElementsByClassName("nav-search-bar")[0]')
   action_runner.WaitForElement(element_function=search_bar_function)
+
+
+def LoginMobileAccount(action_runner, credential,
+                 credentials_path=login_utils.DEFAULT_CREDENTIAL_PATH):
+  """Mobile equivalent of above"""
+
+  account_name, password = login_utils.GetAccountNameAndPassword(
+      credential, credentials_path=credentials_path)
+
+  action_runner.Navigate('https://www.linkedin.com/uas/login')
+  action_runner.Wait(1) # Error page happens if this wait is not here.
+  login_utils.InputWithSelector(
+      action_runner, '%s@gmail.com' % account_name, 'input[type=email]')
+  login_utils.InputWithSelector(
+      action_runner, password, 'input[type=password]')
+
+  login_button_function = (
+      'document.getElementById("signin-submit")')
+  action_runner.WaitForElement(element_function=login_button_function)
+  action_runner.ClickElement(element_function=login_button_function)
+
+  action_runner.Wait(2)
+  action_runner.ReloadPage()
diff --git a/tools/perf/page_sets/rendering/rendering_story.py b/tools/perf/page_sets/rendering/rendering_story.py
index 374e139..54df6ea 100644
--- a/tools/perf/page_sets/rendering/rendering_story.py
+++ b/tools/perf/page_sets/rendering/rendering_story.py
@@ -32,6 +32,7 @@
   SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
   TAGS = None
   PLATFORM_SPECIFIC = False
+  YEAR = None
 
   def __init__(self,
                page_set,
@@ -45,9 +46,12 @@
       for t in self.TAGS:
         assert t in story_tags.ALL_TAGS
         tags.append(t.name)
+    name = self.BASE_NAME + name_suffix
+    if self.YEAR:
+      name += ('_' + self.YEAR)
     super(RenderingStory, self).__init__(
         page_set=page_set,
-        name=self.BASE_NAME + name_suffix,
+        name=name,
         url=self.URL,
         tags=tags,
         platform_specific=self.PLATFORM_SPECIFIC,
diff --git a/tools/perf/page_sets/rendering/top_real_world_mobile.py b/tools/perf/page_sets/rendering/top_real_world_mobile.py
index 5f6ecc7..52f675b 100644
--- a/tools/perf/page_sets/rendering/top_real_world_mobile.py
+++ b/tools/perf/page_sets/rendering/top_real_world_mobile.py
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 from telemetry.page import shared_page_state
 
+from page_sets.login_helpers import linkedin_login
 from page_sets.rendering import rendering_story
 from page_sets.rendering import story_tags
 from page_sets.system_health import platforms
@@ -53,6 +54,24 @@
         'document.body.scrollHeight > 2560')
 
 
+class CapitolVolkswagenMobile2018Page(TopRealWorldMobilePage):
+  """ Why: Typical mobile business site """
+  BASE_NAME = 'capitolvolkswagen_mobile'
+  YEAR = '2018'
+  URL = 'https://www.capitolvolkswagen.com/'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(CapitolVolkswagenMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+
 class TheVergeArticleMobilePage(TopRealWorldMobilePage):
   """ Why: Top tech blog """
   BASE_NAME = 'theverge_article_mobile'
@@ -80,6 +99,25 @@
         ' window.Chorus.Comments.Json.load_comments())')
 
 
+class TheVergeArticleMobile2018Page(TopRealWorldMobilePage):
+  """ Why: Top tech blog """
+  BASE_NAME = 'theverge_article_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'https://www.theverge.com/2018/7/18/17582836/chrome-os-tablet-acer-chromebook-tab-10-android-ipad'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(TheVergeArticleMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+
 class CnnArticleMobilePage(TopRealWorldMobilePage):
   """ Why: Top news site """
   BASE_NAME = 'cnn_article_mobile'
@@ -108,6 +146,35 @@
       action_runner.ScrollPage(top_start_ratio=0.01)
 
 
+class CnnArticleMobile2018Page(TopRealWorldMobilePage):
+  """ Why: Top news site """
+  BASE_NAME = 'cnn_article_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'https://www.cnn.com/travel/article/airbus-a330-900-neo-tours-us-airports/index.html'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(CnnArticleMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+  def RunNavigateSteps(self, action_runner):
+    super(CnnArticleMobile2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForElement(selector='.Article__entitlement')
+
+  def RunPageInteractions(self, action_runner):
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      # With default top_start_ratio=0.5 the corresponding element in this page
+      # will not be in the root scroller.
+      action_runner.ScrollPage(top_start_ratio=0.01)
+
+
 class FacebookMobilePage(TopRealWorldMobilePage):
   """ Why: #1 (Alexa global) """
   BASE_NAME = 'facebook_mobile'
@@ -131,6 +198,30 @@
         'document.body.scrollHeight > window.innerHeight')
 
 
+class FacebookMobile2018Page(TopRealWorldMobilePage):
+  """ Why: #1 (Alexa global) """
+  BASE_NAME = 'facebook_mobile'
+  YEAR = '2018'
+  URL = 'https://facebook.com/barackobama'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(FacebookMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+  def RunNavigateSteps(self, action_runner):
+    super(FacebookMobile2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForJavaScriptCondition(
+        'document.getElementById("u_0_c") !== null &&'
+        'document.body.scrollHeight > window.innerHeight')
+
+
 class YoutubeMobilePage(TopRealWorldMobilePage):
   """ Why: #3 (Alexa global) """
   BASE_NAME = 'youtube_mobile'
@@ -153,6 +244,29 @@
         'document.getElementById("paginatortarget") !== null')
 
 
+class YoutubeMobile2018Page(TopRealWorldMobilePage):
+  """ Why: #3 (Alexa global) """
+  BASE_NAME = 'youtube_mobile'
+  YEAR = '2018'
+  URL = 'http://m.youtube.com/watch?v=9hBpF_Zj4OA'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(YoutubeMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+  def RunNavigateSteps(self, action_runner):
+    super(YoutubeMobile2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForJavaScriptCondition(
+        'document.getElementById("player") !== null')
+
+
 class LinkedInMobilePage(TopRealWorldMobilePage):
   """ Why: #12 (Alexa global),Public profile """
   BASE_NAME = 'linkedin_mobile'
@@ -183,6 +297,38 @@
         'document.getElementById("profile-view-scroller") !== null')
 
 
+class LinkedInMobile2018Page(TopRealWorldMobilePage):
+  """ Why: #12 (Alexa global),Public profile """
+  BASE_NAME = 'linkedin_mobile'
+  YEAR = '2018'
+  URL = 'https://www.linkedin.com/in/linustorvalds'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(LinkedInMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+  # Linkedin has expensive shader compilation so it can benefit from shader
+  # cache from reload.
+  def RunNavigateSteps(self, action_runner):
+    linkedin_login.LoginMobileAccount(action_runner, 'linkedin')
+    super(LinkedInMobile2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForJavaScriptCondition(
+        'document.getElementById("profile-wrapper") !== null')
+
+    action_runner.ScrollPage()
+
+    super(LinkedInMobile2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.WaitForJavaScriptCondition(
+        'document.getElementById("profile-wrapper") !== null')
+
+
 class YahooAnswersMobilePage(TopRealWorldMobilePage):
   """ Why: #1 Alexa reference """
   BASE_NAME = 'yahoo_answers_mobile'
@@ -206,6 +352,29 @@
     action_runner.ClickElement(text='Other Answers (1 - 20 of 149)')
 
 
+class YahooAnswersMobile2018Page(TopRealWorldMobilePage):
+  """ Why: #1 Alexa reference """
+  BASE_NAME = 'yahoo_answers_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'https://ca.answers.yahoo.com/'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(YahooAnswersMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+  def RunNavigateSteps(self, action_runner):
+   super(YahooAnswersMobile2018Page, self).RunNavigateSteps(action_runner)
+   action_runner.ScrollElement(selector='#page_scrollable')
+
+
 class GoogleNewsMobilePage(TopRealWorldMobilePage):
   """ Why: Google News: accelerated scrolling version """
   BASE_NAME = 'google_news_mobile'
@@ -229,6 +398,42 @@
         'NEWS_telemetryReady == true')
 
 
+class GoogleNewsMobile2018Page(TopRealWorldMobilePage):
+  """ Why: Google News: accelerated scrolling version """
+  BASE_NAME = 'google_news_mobile'
+  YEAR = '2018'
+  URL = 'https://news.google.com/'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(GoogleNewsMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+
+class GoogleImageSearchMobile2018Page(TopRealWorldMobilePage):
+  """ Why: tough image case; top google properties """
+  BASE_NAME = 'google_image_search_mobile'
+  YEAR = '2018'
+  URL = 'https://www.google.com/search?q=cats&tbm=isch'
+
+  def __init__(self,
+               page_set,
+               shared_page_state_class=shared_page_state.SharedPageState,
+               name_suffix='',
+               extra_browser_args=None):
+    super(GoogleImageSearchMobile2018Page, self).__init__(
+        page_set=page_set,
+        shared_page_state_class=shared_page_state_class,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args)
+
+
 class AmazonNicolasCageMobilePage(TopRealWorldMobilePage):
   """
   Why: #1 world commerce website by visits; #3 commerce in the US by time spent
@@ -254,6 +459,26 @@
           distance_expr='document.body.scrollHeight - window.innerHeight')
 
 
+class AmazonNicolasCageMobile2018Page(TopRealWorldMobilePage):
+  """
+  Why: #1 world commerce website by visits; #3 commerce in the US by time spent
+  """
+  BASE_NAME = 'amazon_mobile'
+  YEAR = '2018'
+  URL = 'http://www.amazon.com/gp/aw/s/ref=is_box_?k=nicolas+cage'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(AmazonNicolasCageMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+
 class WowwikiMobilePage(TopRealWorldMobilePage):
   """Why: Mobile wiki."""
   BASE_NAME = 'wowwiki_mobile'
@@ -278,6 +503,31 @@
     super(WowwikiMobilePage, self).RunNavigateSteps(action_runner)
 
 
+class WowwikiMobile2018Page(TopRealWorldMobilePage):
+  """Why: Mobile wiki."""
+  BASE_NAME = 'wowwiki_mobile'
+  YEAR = '2018'
+  URL = 'http://www.wowwiki.com/World_of_Warcraft:_Mists_of_Pandaria'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(WowwikiMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+  # Wowwiki has expensive shader compilation so it can benefit from shader
+  # cache from reload.
+  def RunNavigateSteps(self, action_runner):
+    super(WowwikiMobile2018Page, self).RunNavigateSteps(action_runner)
+    action_runner.ScrollPage()
+    super(WowwikiMobile2018Page, self).RunNavigateSteps(action_runner)
+
+
 class WikipediaDelayedScrollMobilePage(TopRealWorldMobilePage):
   """Why: Wikipedia page with a delayed scroll start"""
   BASE_NAME = 'wikipedia_delayed_scroll_start'
@@ -301,12 +551,43 @@
       action_runner.ScrollPage()
 
 
+class WikipediaDelayedScrollMobile2018Page(TopRealWorldMobilePage):
+  """Why: Wikipedia page with a delayed scroll start"""
+  BASE_NAME = 'wikipedia_delayed_scroll_start'
+  YEAR = '2018'
+  URL = 'http://en.wikipedia.org/wiki/Wikipedia'
+
+  def __init__(self,
+               page_set,
+               name_suffix='',
+               extra_browser_args=None,
+               shared_page_state_class=shared_page_state.SharedMobilePageState):
+    super(WikipediaDelayedScrollMobile2018Page, self).__init__(
+        page_set=page_set,
+        name_suffix=name_suffix,
+        extra_browser_args=extra_browser_args,
+        shared_page_state_class=shared_page_state_class)
+
+  def RunPageInteractions(self, action_runner):
+    action_runner.WaitForJavaScriptCondition(
+      'document.readyState == "complete"', timeout=30)
+    with action_runner.CreateGestureInteraction('ScrollAction'):
+      action_runner.ScrollPage()
+
+
 class BlogspotMobilePage(TopRealWorldMobilePage):
   """Why: #11 (Alexa global), google property"""
   BASE_NAME = 'blogspot_mobile'
   URL = 'http://googlewebmastercentral.blogspot.com/'
 
 
+class BlogspotMobile2018Page(TopRealWorldMobilePage):
+  """Why: #11 (Alexa global), google property"""
+  BASE_NAME = 'blogspot_mobile'
+  YEAR = '2018'
+  URL = 'http://googlewebmastercentral.blogspot.com/'
+
+
 class WordpressMobilePage(TopRealWorldMobilePage):
   """Why: #18 (Alexa global), Picked an interesting post"""
   BASE_NAME = 'wordpress_mobile'
@@ -314,36 +595,79 @@
   URL = 'http://en.blog.wordpress.com/2012/09/04/freshly-pressed-editors-picks-for-august-2012/'
 
 
+class WordpressMobile2018Page(TopRealWorldMobilePage):
+  """Why: #18 (Alexa global), Picked an interesting post"""
+  BASE_NAME = 'wordpress_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'http://en.blog.wordpress.com/2012/09/04/freshly-pressed-editors-picks-for-august-2012/'
+
+
 class WikipediaMobilePage(TopRealWorldMobilePage):
   """Why: #6 (Alexa) most visited worldwide, picked an interesting page"""
   BASE_NAME = 'wikipedia_mobile'
   URL = 'http://en.wikipedia.org/wiki/Wikipedia'
 
 
+class WikipediaMobile2018Page(TopRealWorldMobilePage):
+  """Why: #6 (Alexa) most visited worldwide, picked an interesting page"""
+  BASE_NAME = 'wikipedia_mobile'
+  YEAR = '2018'
+  URL = 'http://en.wikipedia.org/wiki/Wikipedia'
+
+
 class TwitterMobilePage(TopRealWorldMobilePage):
   """Why: #8 (Alexa global), picked an interesting page"""
   BASE_NAME = 'twitter_mobile'
   URL = 'http://twitter.com/katyperry'
 
 
+class TwitterMobile2018Page(TopRealWorldMobilePage):
+  """Why: #8 (Alexa global), picked an interesting page"""
+  BASE_NAME = 'twitter_mobile'
+  YEAR = '2018'
+  URL = 'http://twitter.com/katyperry'
+
+
 class PinterestMobilePage(TopRealWorldMobilePage):
   """Why: #37 (Alexa global)."""
   BASE_NAME = 'pinterest_mobile'
   URL = 'http://pinterest.com'
 
 
+class PinterestMobile2018Page(TopRealWorldMobilePage):
+  """Why: #37 (Alexa global)."""
+  BASE_NAME = 'pinterest_mobile'
+  YEAR = '2018'
+  URL = 'https://www.pinterest.com/search/pins/?q=flowers&rs=typed'
+
+
 class ESPNMobilePage(TopRealWorldMobilePage):
   """Why: #1 sports."""
   BASE_NAME = 'espn_mobile'
   URL = 'http://espn.go.com'
 
 
+class ESPNMobile2018Page(TopRealWorldMobilePage):
+  """Why: #1 sports."""
+  BASE_NAME = 'espn_mobile'
+  YEAR = '2018'
+  URL = 'http://www.espn.com/'
+
+
 class ForecastIOMobilePage(TopRealWorldMobilePage):
   """Why: crbug.com/231413"""
   BASE_NAME = 'forecast.io_mobile'
   URL = 'http://forecast.io'
 
 
+class ForecastIOMobile2018Page(TopRealWorldMobilePage):
+  """Why: crbug.com/231413"""
+  BASE_NAME = 'forecast.io_mobile'
+  YEAR = '2018'
+  URL = 'http://forecast.io'
+
+
 class GooglePlusMobilePage(TopRealWorldMobilePage):
   """Why: Social; top Google property; Public profile; infinite scrolls."""
   BASE_NAME = 'google_plus_mobile'
@@ -351,6 +675,14 @@
   URL = 'https://plus.google.com/app/basic/110031535020051778989/posts?source=apppromo'
 
 
+class GooglePlusMobile2018Page(TopRealWorldMobilePage):
+  """Why: Social; top Google property; Public profile; infinite scrolls."""
+  BASE_NAME = 'google_plus_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'https://plus.google.com/app/basic/110031535020051778989/posts?source=apppromo'
+
+
 class AndroidPoliceMobilePage(TopRealWorldMobilePage):
   """Why: crbug.com/242544"""
   BASE_NAME = 'androidpolice_mobile'
@@ -358,48 +690,105 @@
   URL = 'http://www.androidpolice.com/2012/10/03/rumor-evidence-mounts-that-an-lg-optimus-g-nexus-is-coming-along-with-a-nexus-phone-certification-program/'
 
 
+class AndroidPoliceMobile2018Page(TopRealWorldMobilePage):
+  """Why: crbug.com/242544"""
+  BASE_NAME = 'androidpolice_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'http://www.androidpolice.com/2012/10/03/rumor-evidence-mounts-that-an-lg-optimus-g-nexus-is-coming-along-with-a-nexus-phone-certification-program/'
+
+
 class GSPMobilePage(TopRealWorldMobilePage):
   """Why: crbug.com/149958"""
   BASE_NAME = 'gsp.ro_mobile'
   URL = 'http://gsp.ro'
 
 
+class GSPMobile2018Page(TopRealWorldMobilePage):
+  """Why: crbug.com/149958"""
+  BASE_NAME = 'gsp.ro_mobile'
+  YEAR = '2018'
+  URL = 'http://gsp.ro'
+
+
 class TheVergeMobilePage(TopRealWorldMobilePage):
   """Why: Top tech blog"""
   BASE_NAME = 'theverge_mobile'
   URL = 'http://theverge.com'
 
 
+class TheVergeMobile2018Page(TopRealWorldMobilePage):
+  """Why: Top tech blog"""
+  BASE_NAME = 'theverge_mobile'
+  YEAR = '2018'
+  URL = 'http://theverge.com'
+
+
 class DiggMobilePage(TopRealWorldMobilePage):
   """Why: Top tech site"""
   BASE_NAME = 'digg_mobile'
   URL = 'http://digg.com'
 
 
+class DiggMobile2018Page(TopRealWorldMobilePage):
+  """Why: Top tech site"""
+  BASE_NAME = 'digg_mobile'
+  YEAR = '2018'
+  URL = 'http://digg.com/channel/digg-feature'
+
+
 class GoogleSearchMobilePage(TopRealWorldMobilePage):
   """Why: Top Google property; a Google tab is often open"""
   BASE_NAME = 'google_web_search_mobile'
   URL = 'https://www.google.co.uk/search?hl=en&q=barack+obama&cad=h'
 
 
+class GoogleSearchMobile2018Page(TopRealWorldMobilePage):
+  """Why: Top Google property; a Google tab is often open"""
+  BASE_NAME = 'google_web_search_mobile'
+  YEAR = '2018'
+  URL = 'https://www.google.co.uk/search?hl=en&q=barack+obama&cad=h'
+
+
 class YahooNewsMobilePage(TopRealWorldMobilePage):
   """Why: #1 news worldwide (Alexa global)"""
   BASE_NAME = 'yahoo_news_mobile'
   URL = 'http://news.yahoo.com'
 
 
+class YahooNewsMobile2018Page(TopRealWorldMobilePage):
+  """Why: #1 news worldwide (Alexa global)"""
+  BASE_NAME = 'yahoo_news_mobile'
+  YEAR = '2018'
+  URL = 'http://news.yahoo.com'
+
+
 class CnnNewsMobilePage(TopRealWorldMobilePage):
   """# Why: #2 news worldwide"""
   BASE_NAME = 'cnn_mobile'
   URL = 'http://www.cnn.com'
 
 
+class CnnNewsMobile2018Page(TopRealWorldMobilePage):
+  """# Why: #2 news worldwide"""
+  BASE_NAME = 'cnn_mobile'
+  YEAR = '2018'
+  URL = 'http://www.cnn.com'
+
+
 class EbayMobilePage(TopRealWorldMobilePage):
   """Why: #1 commerce website by time spent by users in US"""
   BASE_NAME = 'ebay_mobile'
   URL = 'http://shop.mobileweb.ebay.com/searchresults?kw=viking+helmet'
 
 
+class EbayMobile2018Page(TopRealWorldMobilePage):
+  """Why: #1 commerce website by time spent by users in US"""
+  BASE_NAME = 'ebay_mobile'
+  YEAR = '2018'
+  URL = 'https://m.ebay.com/'
+
+
 class BookingMobilePage(TopRealWorldMobilePage):
   """Why: #1 Alexa recreation"""
   BASE_NAME = 'booking.com_mobile'
@@ -407,42 +796,92 @@
   URL = 'http://www.booking.com/searchresults.html?src=searchresults&latitude=65.0500&longitude=25.4667'
 
 
+class BookingMobile2018Page(TopRealWorldMobilePage):
+  """Why: #1 Alexa recreation"""
+  BASE_NAME = 'booking.com_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'https://www.booking.com'
+
+
 class TechCrunchMobilePage(TopRealWorldMobilePage):
   """Why: Top tech blog"""
   BASE_NAME = 'techcrunch_mobile'
   URL = 'http://techcrunch.com'
 
 
+class TechCrunchMobile2018Page(TopRealWorldMobilePage):
+  """Why: Top tech blog"""
+  BASE_NAME = 'techcrunch_mobile'
+  YEAR = '2018'
+  URL = 'http://techcrunch.com'
+
+
 class MLBMobilePage(TopRealWorldMobilePage):
   """Why: #6 Alexa sports"""
   BASE_NAME = 'mlb_mobile'
   URL = 'http://mlb.com/'
 
 
+class MLBMobile2018Page(TopRealWorldMobilePage):
+  """Why: #6 Alexa sports"""
+  BASE_NAME = 'mlb_mobile'
+  YEAR = '2018'
+  URL = 'http://mlb.com/'
+
+
 class SFGateMobilePage(TopRealWorldMobilePage):
   """Why: #14 Alexa California"""
   BASE_NAME = 'sfgate_mobile'
   URL = 'http://www.sfgate.com/'
 
 
+class SFGateMobile2018Page(TopRealWorldMobilePage):
+  """Why: #14 Alexa California"""
+  BASE_NAME = 'sfgate_mobile'
+  YEAR = '2018'
+  URL = 'http://www.sfgate.com/'
+
+
 class WorldJournalMobilePage(TopRealWorldMobilePage):
   """Why: Non-latin character set"""
   BASE_NAME = 'worldjournal_mobile'
   URL = 'http://worldjournal.com/'
 
 
+class WorldJournalMobile2018Page(TopRealWorldMobilePage):
+  """Why: Non-latin character set"""
+  BASE_NAME = 'worldjournal_mobile'
+  YEAR = '2018'
+  URL = 'http://worldjournal.com/'
+
+
 class WSJMobilePage(TopRealWorldMobilePage):
   """Why: #15 Alexa news"""
   BASE_NAME = 'wsj_mobile'
   URL = 'http://online.wsj.com/home-page'
 
 
+class WSJMobile2018Page(TopRealWorldMobilePage):
+  """Why: #15 Alexa news"""
+  BASE_NAME = 'wsj_mobile'
+  YEAR = '2018'
+  URL = 'http://online.wsj.com/home-page'
+
+
 class DeviantArtMobilePage(TopRealWorldMobilePage):
   """Why: Image-heavy mobile site"""
   BASE_NAME = 'deviantart_mobile'
   URL = 'http://www.deviantart.com/'
 
 
+class DeviantArtMobile2018Page(TopRealWorldMobilePage):
+  """Why: Image-heavy mobile site"""
+  BASE_NAME = 'deviantart_mobile'
+  YEAR = '2018'
+  URL = 'http://www.deviantart.com/'
+
+
 class BaiduMobilePage(TopRealWorldMobilePage):
   """Why: Top search engine"""
   BASE_NAME = 'baidu_mobile'
@@ -450,21 +889,43 @@
   URL = 'http://www.baidu.com/s?wd=barack+obama&rsv_bp=0&rsv_spt=3&rsv_sug3=9&rsv_sug=0&rsv_sug4=3824&rsv_sug1=3&inputT=4920'
 
 
+class BaiduMobile2018Page(TopRealWorldMobilePage):
+  """Why: Top search engine"""
+  BASE_NAME = 'baidu_mobile'
+  YEAR = '2018'
+  # pylint: disable=line-too-long
+  URL = 'http://www.baidu.com/s?wd=barack+obama&rsv_bp=0&rsv_spt=3&rsv_sug3=9&rsv_sug=0&rsv_sug4=3824&rsv_sug1=3&inputT=4920'
+
+
 class BingMobilePage(TopRealWorldMobilePage):
   """Why: Top search engine"""
   BASE_NAME = 'bing_mobile'
   URL = 'http://www.bing.com/search?q=sloths'
 
 
+class BingMobile2018Page(TopRealWorldMobilePage):
+  """Why: Top search engine"""
+  BASE_NAME = 'bing_mobile'
+  YEAR = '2018'
+  URL = 'http://www.bing.com/search?q=sloths'
+
+
 class USATodayMobilePage(TopRealWorldMobilePage):
   """Why: Good example of poor initial scrolling"""
   BASE_NAME = 'usatoday_mobile'
   URL = 'http://ftw.usatoday.com/2014/05/spelling-bee-rules-shenanigans'
 
 
+class USATodayMobile2018Page(TopRealWorldMobilePage):
+  """Why: Good example of poor initial scrolling"""
+  BASE_NAME = 'usatoday_mobile'
+  YEAR = '2018'
+  URL = 'http://ftw.usatoday.com/'
+
+
 class FastPathSmoothMobilePage(TopRealWorldMobilePage):
   ABSTRACT_STORY = True
-  TAGS = [story_tags.FASTPATH]
+  TAGS = [story_tags.FASTPATH, story_tags.TOP_REAL_WORLD_MOBILE]
 
   def __init__(self,
                page_set,
@@ -484,6 +945,13 @@
   URL = 'http://nytimes.com/'
 
 
+class NYTimesMobile2018Page(FastPathSmoothMobilePage):
+  """Why: Top news site."""
+  BASE_NAME = 'nytimes_mobile'
+  YEAR = '2018'
+  URL = 'http://nytimes.com/'
+
+
 class CuteOverloadMobilePage(FastPathSmoothMobilePage):
   """Why: Image-heavy site."""
   BASE_NAME = 'cuteoverload_mobile'
@@ -496,13 +964,34 @@
   URL = 'http://www.reddit.com/r/programming/comments/1g96ve'
 
 
+class RedditMobile2018Page(FastPathSmoothMobilePage):
+  """Why: #5 Alexa news."""
+  BASE_NAME = 'reddit_mobile'
+  YEAR = '2018'
+  URL = 'http://www.reddit.com/r/programming/comments/1g96ve'
+
+
 class BoingBoingMobilePage(FastPathSmoothMobilePage):
   """Why: Problematic use of fixed position elements."""
   BASE_NAME = 'boingboing_mobile'
   URL = 'http://www.boingboing.net'
 
 
+class BoingBoingMobile2018Page(FastPathSmoothMobilePage):
+  """Why: Problematic use of fixed position elements."""
+  BASE_NAME = 'boingboing_mobile'
+  YEAR = '2018'
+  URL = 'http://www.boingboing.net'
+
+
 class SlashDotMobilePage(FastPathSmoothMobilePage):
   """Why: crbug.com/169827"""
   BASE_NAME = 'slashdot_mobile'
   URL = 'http://slashdot.org'
+
+
+class SlashDotMobile2018Page(FastPathSmoothMobilePage):
+  """Why: crbug.com/169827"""
+  BASE_NAME = 'slashdot_mobile'
+  YEAR = '2018'
+  URL = 'http://slashdot.org'
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm b/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm
index 9bc48ec..fb4e271 100644
--- a/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm
+++ b/ui/accelerated_widget_mac/ca_layer_tree_coordinator.mm
@@ -48,7 +48,7 @@
   if (pending_ca_renderer_layer_tree_) {
     pending_ca_renderer_layer_tree_->CommitScheduledCALayers(
         root_ca_layer_.get(), std::move(current_ca_renderer_layer_tree_),
-        scale_factor_);
+        pixel_size_, scale_factor_);
     current_ca_renderer_layer_tree_.swap(pending_ca_renderer_layer_tree_);
   } else {
     TRACE_EVENT0("gpu", "Blank frame: No overlays or CALayers");
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
index 7c90bae6..a4e7fe3 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
@@ -51,6 +51,7 @@
   // not re-used by |this| will be removed from the CALayer hierarchy.
   void CommitScheduledCALayers(CALayer* superlayer,
                                std::unique_ptr<CARendererLayerTree> old_tree,
+                               const gfx::Size& pixel_size,
                                float scale_factor);
 
   // Returns the contents used for a given solid color.
@@ -86,15 +87,12 @@
     // to nil, so that its destructor will not remove an active CALayer.
     void CommitToCA(CALayer* superlayer,
                     RootLayer* old_layer,
+                    const gfx::Size& pixel_size,
                     float scale_factor);
 
-    // Check to see if the CALayer tree is just a video layer on a black
-    // background. If so, return true and set background_rect to the
-    // background's bounding rect, otherwise return false. CommitToCA() calls
-    // this function and, based on its return value, either gives the root
-    // layer this frame and a black background color or clears them.
-    bool WantsFullcreenLowPowerBackdrop(float scale_factor,
-                                        gfx::RectF* background_rect);
+    // Return true if the CALayer tree is just a video layer on a black or
+    // transparent background, false otherwise.
+    bool WantsFullcreenLowPowerBackdrop();
 
     std::vector<ClipAndSortingLayer> clip_and_sorting_layers;
     base::scoped_nsobject<CALayer> ca_layer;
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index 9b64866..1c5988fe 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -220,6 +220,7 @@
 void CARendererLayerTree::CommitScheduledCALayers(
     CALayer* superlayer,
     std::unique_ptr<CARendererLayerTree> old_tree,
+    const gfx::Size& pixel_size,
     float scale_factor) {
   TRACE_EVENT0("gpu", "CARendererLayerTree::CommitScheduledCALayers");
   RootLayer* old_root_layer = nullptr;
@@ -229,7 +230,7 @@
       old_root_layer = &old_tree->root_layer_;
   }
 
-  root_layer_.CommitToCA(superlayer, old_root_layer, scale_factor);
+  root_layer_.CommitToCA(superlayer, old_root_layer, pixel_size, scale_factor);
   // If there are any extra CALayers in |old_tree| that were not stolen by this
   // tree, they will be removed from the CALayer tree in this deallocation.
   old_tree.reset();
@@ -237,9 +238,7 @@
   scale_factor_ = scale_factor;
 }
 
-bool CARendererLayerTree::RootLayer::WantsFullcreenLowPowerBackdrop(
-    float scale_factor,
-    gfx::RectF* background_rect) {
+bool CARendererLayerTree::RootLayer::WantsFullcreenLowPowerBackdrop() {
   bool found_video_layer = false;
   for (auto& clip_layer : clip_and_sorting_layers) {
     for (auto& transform_layer : clip_layer.transform_layers) {
@@ -250,7 +249,6 @@
 
         // See if this is the video layer.
         if (content_layer.use_av_layer) {
-          background_rect->Union(gfx::RectF(content_layer.rect));
           found_video_layer = true;
           if (!transform_layer.transform.IsPositiveScaleOrTranslation())
             return false;
@@ -263,15 +261,13 @@
         // solid black or transparent
         if (content_layer.io_surface)
           return false;
-        if (content_layer.background_color == SK_ColorBLACK) {
-          background_rect->Union(gfx::RectF(content_layer.rect));
-        } else if (content_layer.background_color != SK_ColorTRANSPARENT) {
+        if (content_layer.background_color != SK_ColorBLACK &&
+            content_layer.background_color != SK_ColorTRANSPARENT) {
           return false;
         }
       }
     }
   }
-  background_rect->Scale(1 / scale_factor);
   return found_video_layer;
 }
 
@@ -554,6 +550,7 @@
 
 void CARendererLayerTree::RootLayer::CommitToCA(CALayer* superlayer,
                                                 RootLayer* old_layer,
+                                                const gfx::Size& pixel_size,
                                                 float scale_factor) {
   if (old_layer) {
     DCHECK(old_layer->ca_layer);
@@ -569,8 +566,9 @@
     DLOG(ERROR) << "CARendererLayerTree root layer not attached to tree.";
   }
 
-  gfx::RectF bg_rect;
-  if (WantsFullcreenLowPowerBackdrop(scale_factor, &bg_rect)) {
+  if (WantsFullcreenLowPowerBackdrop()) {
+    const gfx::RectF bg_rect(
+        ScaleSize(gfx::SizeF(pixel_size), 1 / scale_factor));
     if (gfx::RectF([ca_layer frame]) != bg_rect)
       [ca_layer setFrame:bg_rect.ToCGRect()];
     if (![ca_layer backgroundColor])
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 303af44a..dae92dc1 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -408,12 +408,15 @@
 }
 
 - (id)accessibilityHitTest:(NSPoint)point {
-  for (AXPlatformNodeCocoa* child in [self AXChildren]) {
-    if (![child accessibilityIsIgnored] &&
-        NSPointInRect(point, child.boundsInScreen)) {
-      return [child accessibilityHitTest:point];
-    }
+  if (!NSPointInRect(point, [self boundsInScreen]))
+    return nil;
+
+  for (id child in [[self AXChildren] reverseObjectEnumerator]) {
+    if (id foundChild = [child accessibilityHitTest:point])
+      return foundChild;
   }
+
+  // Hit self, but not any child.
   return NSAccessibilityUnignoredAncestor(self);
 }
 
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 801d52c5..cb3833a0 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -87,6 +87,7 @@
     "clipboard/custom_data_helper_mac.mm",
     "cocoa/a11y_util.h",
     "cocoa/a11y_util.mm",
+    "cocoa/accessibility_hostable.h",
     "cocoa/animation_utils.h",
     "cocoa/appkit_utils.h",
     "cocoa/appkit_utils.mm",
diff --git a/ui/base/cocoa/accessibility_hostable.h b/ui/base/cocoa/accessibility_hostable.h
new file mode 100644
index 0000000..63a4a24
--- /dev/null
+++ b/ui/base/cocoa/accessibility_hostable.h
@@ -0,0 +1,25 @@
+// 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 UI_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_
+#define UI_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_
+
+#import <objc/objc.h>
+
+// An object that can be hosted in another accessibility hierarchy.
+// This allows for stitching together heterogenous accessibility
+// hierarchies, for example the AXPlatformNodeCocoa-based views
+// toolkit hierarchy and the BrowserAccessibilityCocoa-based
+// web content hierarchy.
+@protocol AccessibilityHostable
+
+// Sets |accessibilityParent| as the object returned when the
+// receiver is queried for its accessibility parent.
+// TODO(lgrey/ellyjones): Remove this in favor of setAccessibilityParent:
+// when we switch to the new accessibility API.
+- (void)setAccessibilityParentElement:(id)accessibilityParent;
+
+@end
+
+#endif  // UI_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_
diff --git a/ui/events/blink/web_input_event.cc b/ui/events/blink/web_input_event.cc
index 1f9378b..7acf014 100644
--- a/ui/events/blink/web_input_event.cc
+++ b/ui/events/blink/web_input_event.cc
@@ -392,11 +392,17 @@
   return gesture_event;
 }
 
-blink::WebGestureEvent MakeWebGestureEventFlingCancel() {
+blink::WebGestureEvent MakeWebGestureEventFlingCancel(
+    const blink::WebMouseWheelEvent& wheel_event) {
   blink::WebGestureEvent gesture_event(
       blink::WebInputEvent::kGestureFlingCancel,
-      blink::WebInputEvent::kNoModifiers, EventTimeForNow(),
+      blink::WebInputEvent::kNoModifiers, wheel_event.TimeStamp(),
       blink::kWebGestureDeviceTouchpad);
+  // Coordinates need to be transferred to the fling cancel gesture only
+  // for Surface-targeting to ensure that it is targeted to the correct
+  // RenderWidgetHost.
+  gesture_event.SetPositionInWidget(wheel_event.PositionInWidget());
+  gesture_event.SetPositionInScreen(wheel_event.PositionInScreen());
   // All other fields are ignored on a GestureFlingCancel event.
   return gesture_event;
 }
diff --git a/ui/events/blink/web_input_event.h b/ui/events/blink/web_input_event.h
index 58d02452..17d90025a 100644
--- a/ui/events/blink/web_input_event.h
+++ b/ui/events/blink/web_input_event.h
@@ -51,7 +51,8 @@
     const ScrollEvent& event,
     const base::Callback<gfx::PointF(const ui::LocatedEvent& event)>&
         screen_location_callback);
-blink::WebGestureEvent MakeWebGestureEventFlingCancel();
+blink::WebGestureEvent MakeWebGestureEventFlingCancel(
+    const blink::WebMouseWheelEvent& wheel_event);
 
 }  // namespace ui
 
diff --git a/ui/file_manager/file_manager/foreground/js/app_state_controller.js b/ui/file_manager/file_manager/foreground/js/app_state_controller.js
index 35f4acb1..c3a9b4e 100644
--- a/ui/file_manager/file_manager/foreground/js/app_state_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/app_state_controller.js
@@ -42,7 +42,7 @@
    * @private {string}
    */
   this.fileListSortDirection_ = AppStateController.DEFAULT_SORT_DIRECTION;
-};
+}
 
 /**
  * Default sort field of the file list.
@@ -109,6 +109,8 @@
       'column-resize-end', this.saveViewOptions.bind(this));
   directoryModel.getFileList().addEventListener(
       'sorted', this.onFileListSorted_.bind(this));
+  directoryModel.getFileFilter().addEventListener(
+      'changed', this.onFileFilterChanged_.bind(this));
   directoryModel.addEventListener(
       'directory-changed', this.onDirectoryChanged_.bind(this));
 
@@ -121,6 +123,8 @@
     this.fileListSortDirection_ = this.viewOptions_.sortDirection;
   this.directoryModel_.getFileList().sort(
       this.fileListSortField_, this.fileListSortDirection_);
+  if (this.viewOptions_.isAllAndroidFoldersVisible)
+    this.directoryModel_.getFileFilter().setAllAndroidFoldersVisible(true);
   if (this.viewOptions_.columnConfig) {
     this.ui_.listContainer.table.columnModel.restoreColumnConfig(
         this.viewOptions_.columnConfig);
@@ -136,6 +140,8 @@
     sortDirection: this.fileListSortDirection_,
     columnConfig: {},
     listType: this.ui_.listContainer.currentListType,
+    isAllAndroidFoldersVisible:
+        this.directoryModel_.getFileFilter().isAllAndroidFoldersVisible()
   };
   var cm = this.ui_.listContainer.table.columnModel;
   prefs.columnConfig = cm.exportColumnConfig();
@@ -155,6 +161,9 @@
   }
 };
 
+/**
+ * @private
+ */
 AppStateController.prototype.onFileListSorted_ = function() {
   var currentDirectory = this.directoryModel_.getCurrentDirEntry();
   if (!currentDirectory)
@@ -171,6 +180,19 @@
 };
 
 /**
+ * @private
+ */
+AppStateController.prototype.onFileFilterChanged_ = function() {
+  const isAllAndroidFoldersVisible =
+      this.directoryModel_.getFileFilter().isAllAndroidFoldersVisible();
+  if (this.viewOptions_.isAllAndroidFoldersVisible !==
+      isAllAndroidFoldersVisible) {
+    this.viewOptions_.isAllAndroidFoldersVisible = isAllAndroidFoldersVisible;
+    this.saveViewOptions();
+  }
+};
+
+/**
  * @param {Event} event
  * @private
  */
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 49e98d1..d9bd0298 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
@@ -833,6 +833,8 @@
             !!fileManager.volumeManager.getCurrentProfileVolumeInfo(
                 VolumeManagerCommon.VolumeType.ANDROID_FILES);
         event.command.setHidden(!event.canExecute);
+        event.command.checked =
+            fileManager.fileFilter.isAllAndroidFoldersVisible();
       }
     });
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index edf2667b..957e1a5b 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -239,6 +239,7 @@
     "widget/root_view.h",
     "widget/root_view_targeter.h",
     "widget/tooltip_manager.h",
+    "widget/util_mac.h",
     "widget/widget.h",
     "widget/widget_aura_utils.h",
     "widget/widget_delegate.h",
diff --git a/ui/views/cocoa/native_widget_mac_nswindow.h b/ui/views/cocoa/native_widget_mac_nswindow.h
index 97b37b8..8a0f51b7 100644
--- a/ui/views/cocoa/native_widget_mac_nswindow.h
+++ b/ui/views/cocoa/native_widget_mac_nswindow.h
@@ -9,6 +9,7 @@
 
 #import "ui/base/cocoa/command_dispatcher.h"
 #include "ui/views/views_export.h"
+#include "ui/views/widget/util_mac.h"
 
 @protocol WindowTouchBarDelegate;
 
@@ -18,9 +19,7 @@
 @interface NSNextStepFrame : NSView
 @end
 
-WEAK_IMPORT_ATTRIBUTE
-@interface NSThemeFrame : NSView
-@end
+@class NSThemeFrame;
 
 VIEWS_EXPORT
 @interface NativeWidgetMacNSWindowBorderlessFrame : NSNextStepFrame
diff --git a/ui/views/controls/native/native_view_host_mac.mm b/ui/views/controls/native/native_view_host_mac.mm
index 16e16b3..debbe51f 100644
--- a/ui/views/controls/native/native_view_host_mac.mm
+++ b/ui/views/controls/native/native_view_host_mac.mm
@@ -7,6 +7,8 @@
 #import <Cocoa/Cocoa.h>
 
 #include "base/mac/foundation_util.h"
+#import "ui/accessibility/platform/ax_platform_node_mac.h"
+#import "ui/base/cocoa/accessibility_hostable.h"
 #import "ui/views/cocoa/bridged_native_widget.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/widget/native_widget_mac.h"
@@ -36,6 +38,17 @@
   }
 }
 
+AXPlatformNodeCocoa* ClosestPlatformAncestorNode(views::View* view) {
+  do {
+    gfx::NativeViewAccessible accessible = view->GetNativeViewAccessible();
+    if ([accessible isKindOfClass:[AXPlatformNodeCocoa class]]) {
+      return NSAccessibilityUnignoredAncestor(accessible);
+    }
+    view = view->parent();
+  } while (view);
+  return nil;
+}
+
 }  // namespace
 
 NativeViewHostMac::NativeViewHostMac(NativeViewHost* host) : host_(host) {
@@ -56,6 +69,28 @@
 
   if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
     [native_view_ cr_setParentUiLayer:host_->layer()];
+  if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) {
+    // Find the closest ancestor view that participates in the views toolkit
+    // accessibility hierarchy and set its element as the native view's parent.
+    // This is necessary because a closer ancestor might already be attaching
+    // to the NSView/content hierarchy.
+    // For example, web content is currently embedded into the views hierarchy
+    // roughly like this:
+    // BrowserView (views)
+    // |_  WebView (views)
+    //   |_  NativeViewHost (views)
+    //     |_  WebContentView (Cocoa, is |native_view_| in this scenario,
+    //         |               accessibility ignored).
+    //         |_ RenderWidgetHostView (Cocoa)
+    // WebView specifies either the RenderWidgetHostView or the native view as
+    // its accessibility element. That means that if we were to set it as
+    // |native_view_|'s parent, the RenderWidgetHostView would be its own
+    // accessibility parent! Instead, we want to find the browser view and
+    // attach to its node.
+    id hostable = native_view_;
+    [hostable setAccessibilityParentElement:ClosestPlatformAncestorNode(
+                                                host_->parent())];
+  }
 
   EnsureNativeViewHasNoChildWidgets(native_view_);
   BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow(
@@ -85,6 +120,10 @@
 
   if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)])
     [native_view_ cr_setParentUiLayer:nullptr];
+  if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) {
+    id hostable = native_view_;
+    [hostable setAccessibilityParentElement:nil];
+  }
 
   EnsureNativeViewHasNoChildWidgets(host_->native_view());
   BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow(
@@ -164,7 +203,7 @@
 }
 
 gfx::NativeViewAccessible NativeViewHostMac::GetNativeViewAccessible() {
-  return NULL;
+  return nullptr;
 }
 
 gfx::NativeCursor NativeViewHostMac::GetCursor(int x, int y) {
diff --git a/ui/views/controls/native/native_view_host_mac_unittest.mm b/ui/views/controls/native/native_view_host_mac_unittest.mm
index bb2493f..4a4fdca 100644
--- a/ui/views/controls/native/native_view_host_mac_unittest.mm
+++ b/ui/views/controls/native/native_view_host_mac_unittest.mm
@@ -12,11 +12,19 @@
 #import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #import "testing/gtest_mac.h"
+#import "ui/base/cocoa/accessibility_hostable.h"
 #include "ui/views/controls/native/native_view_host.h"
 #include "ui/views/controls/native/native_view_host_test_base.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
+@interface TestAccessibilityHostableView : NSView<AccessibilityHostable>
+@property(nonatomic, assign) id accessibilityParentElement;
+@end
+@implementation TestAccessibilityHostableView
+@synthesize accessibilityParentElement = accessibilityParentElement_;
+@end
+
 namespace views {
 
 class NativeViewHostMacTest : public test::NativeViewHostTestBase {
@@ -100,6 +108,24 @@
   DestroyHost();
 }
 
+// Ensure the native view is integrated into the views accessibility
+// hierarchy if the native view conforms to the AccessibilityParent
+// protocol.
+TEST_F(NativeViewHostMacTest, AccessibilityParent) {
+  CreateHost();
+  host()->Detach();
+
+  base::scoped_nsobject<TestAccessibilityHostableView> view(
+      [[TestAccessibilityHostableView alloc] init]);
+  host()->Attach(view);
+  EXPECT_NSEQ([view accessibilityParentElement],
+              toplevel()->GetRootView()->GetNativeViewAccessible());
+
+  host()->Detach();
+  DestroyHost();
+  EXPECT_FALSE([view accessibilityParentElement]);
+}
+
 // Test that the content windows' bounds are set to the correct values while the
 // native size is equal or not equal to the View size.
 TEST_F(NativeViewHostMacTest, ContentViewPositionAndSize) {
diff --git a/ui/views/widget/util_mac.h b/ui/views/widget/util_mac.h
new file mode 100644
index 0000000..604c693
--- /dev/null
+++ b/ui/views/widget/util_mac.h
@@ -0,0 +1,17 @@
+// 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 UI_VIEWS_WIDGET_UTIL_MAC_H_
+#define UI_VIEWS_WIDGET_UTIL_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/foundation_util.h"
+
+// Weak lets Chrome launch even if a future macOS doesn't have NSThemeFrame.
+WEAK_IMPORT_ATTRIBUTE
+@interface NSThemeFrame : NSView
+@end
+
+#endif  // UI_VIEWS_WIDGET_UTIL_MAC_H_