diff --git a/DEPS b/DEPS
index 91700a7..985206c 100644
--- a/DEPS
+++ b/DEPS
@@ -116,11 +116,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': '51916083e75254f7bb46ad0e638530259026813a',
+  'skia_revision': '8b35379ae1f1b223385a1c566628eb9a33a5683c',
   # 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': 'fad38646ccf24d3c02b1c706097cec72ec456ff3',
+  'v8_revision': 'fcb0f91c52a8f4ba50037f9b5bb660ab51e7dafe',
   # 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.
@@ -516,7 +516,7 @@
       'packages': [
           {
        'package': 'chromium/third_party/android_tools_bundletool',
-       'version': 'version:0.6.0-cr0',
+       'version': 'version:0.6.2-cr0',
    },
       ],
       'condition': 'checkout_android',
@@ -655,7 +655,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '6386fdec9f499e98c799a4aa30609e22541c867a',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3d2886849216faf07789db8590c707c24c4e5a9a',
       'condition': 'checkout_linux',
   },
 
@@ -1009,7 +1009,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c7000ce0e554e3a9387dbb1e359cebb433e9c42f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '688679325c646668984dc4a3be284ee3178ac02c',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1161,7 +1161,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '0d55c887e92b645f6effe753528323ab2ffd94c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '9f878f6e97fad6f554e473979f419230db0f8958',
+    Var('webrtc_git') + '/src.git' + '@' + 'aed3070ad40c02d2298e6603a6cce8bb91f320c7',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1192,7 +1192,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c26971222e50e9e97f8dc58167ef174e6efecfb4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@5f64f594fd65406a8de971dc4d7891b22b48adfd',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index ed41ae5..45694742 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1880,7 +1880,8 @@
     'blink_bindings': ['blink-reviews-bindings@chromium.org'],
     'blink_bindings_serialization': ['jbroman+watch@chromium.org'],
     'blink_bluetooth': ['mattreynolds+watch@chromium.org',
-                        'ortuno+watch@chromium.org'],
+                        'ortuno+watch@chromium.org',
+                        'odejesush+watch@chromium.org'],
     'blink_canvas': [ 'dongseong.hwang@intel.com',
                       'fserb@chromium.org'],
     'blink_client_hints': ['yoav@yoav.ws'],
@@ -2139,7 +2140,8 @@
     'device_bluetooth': ['mattreynolds+watch@chromium.org',
                          'ortuno+watch@chromium.org'],
     'device_chooser': ['juncai+watch@chromium.org',
-                       'mattreynolds+watch@chromium.org'],
+                       'mattreynolds+watch@chromium.org',
+                       'odejesush+watch@chromium.org'],
     'devtools': ['devtools-reviews@chromium.org',
                  'pfeldman@chromium.org'],
     'dial': ['mfoltz+watch@chromium.org',
@@ -2450,7 +2452,8 @@
     'ui_display_win': ['robliao+watch@chromium.org'],
     'ui_resources': ['oshima+watch@chromium.org'],
     'ui_strings': ['srahim+watch@chromium.org'],
-    'usb': ['mattreynolds+watch@chromium.org'],
+    'usb': ['mattreynolds+watch@chromium.org',
+            'odejesush+watch@chromium.org'],
     'vaapi': ['vaapi-reviews@chromium.org'],
     'version_assembly': ['caitkp+watch@chromium.org',
                          'gab+watch@chromium.org'],
diff --git a/ash/keyboard/arc/arc_input_method_surface_manager_unittest.cc b/ash/keyboard/arc/arc_input_method_surface_manager_unittest.cc
index 03d4e1b..643d1d5 100644
--- a/ash/keyboard/arc/arc_input_method_surface_manager_unittest.cc
+++ b/ash/keyboard/arc/arc_input_method_surface_manager_unittest.cc
@@ -10,6 +10,7 @@
 #include "components/exo/input_method_surface.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "ui/aura/env.h"
 
 namespace ash {
@@ -42,7 +43,8 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-    wm_helper_ = std::make_unique<exo::WMHelper>(Shell::Get()->aura_env());
+    wm_helper_ =
+        std::make_unique<exo::WMHelperChromeOS>(Shell::Get()->aura_env());
     exo::WMHelper::SetInstance(wm_helper_.get());
   }
 
diff --git a/ash/public/cpp/menu_struct_mojom_traits.h b/ash/public/cpp/menu_struct_mojom_traits.h
index f168c5e..3abe6627 100644
--- a/ash/public/cpp/menu_struct_mojom_traits.h
+++ b/ash/public/cpp/menu_struct_mojom_traits.h
@@ -31,6 +31,9 @@
         return ash::mojom::MenuItemType::SUBMENU;
       case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
         return ash::mojom::MenuItemType::ACTIONABLE_SUBMENU;
+      case ui::MenuModel::TYPE_HIGHLIGHTED:
+        NOTREACHED() << "TYPE_HIGHLIGHTED is not yet supported";
+        return ash::mojom::MenuItemType::COMMAND;
     }
     NOTREACHED();
     return ash::mojom::MenuItemType::COMMAND;
diff --git a/ash/public/cpp/menu_utils.cc b/ash/public/cpp/menu_utils.cc
index ebce149..e06cc16 100644
--- a/ash/public/cpp/menu_utils.cc
+++ b/ash/public/cpp/menu_utils.cc
@@ -77,6 +77,9 @@
           submenus->push_back(std::move(submenu));
         }
         break;
+      case ui::MenuModel::TYPE_HIGHLIGHTED:
+        NOTREACHED() << "TYPE_HIGHLIGHTED is not yet supported.";
+        break;
     }
     if (!item->image.isNull()) {
       model->SetIcon(model->GetIndexOfCommandId(item->command_id),
diff --git a/ash/system/message_center/arc/arc_notification_content_view_unittest.cc b/ash/system/message_center/arc/arc_notification_content_view_unittest.cc
index 753ce94..6c07e84a 100644
--- a/ash/system/message_center/arc/arc_notification_content_view_unittest.cc
+++ b/ash/system/message_center/arc/arc_notification_content_view_unittest.cc
@@ -33,6 +33,7 @@
 #include "components/exo/surface.h"
 #include "components/exo/test/exo_test_helper.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/aura/env.h"
 #include "ui/aura/test/test_window_delegate.h"
@@ -112,8 +113,8 @@
 
   void SetUp() override {
     AshTestBase::SetUp();
-
-    wm_helper_ = std::make_unique<exo::WMHelper>(ash::Shell::Get()->aura_env());
+    wm_helper_ =
+        std::make_unique<exo::WMHelperChromeOS>(ash::Shell::Get()->aura_env());
     exo::WMHelper::SetInstance(wm_helper_.get());
     DCHECK(exo::WMHelper::HasInstance());
 
diff --git a/ash/wayland/wayland_server_controller.cc b/ash/wayland/wayland_server_controller.cc
index ee8dcce..11788d6 100644
--- a/ash/wayland/wayland_server_controller.cc
+++ b/ash/wayland/wayland_server_controller.cc
@@ -16,6 +16,7 @@
 #include "components/exo/file_helper.h"
 #include "components/exo/wayland/server.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "ui/aura/env.h"
 
 namespace ash {
@@ -74,7 +75,7 @@
       std::make_unique<ArcNotificationSurfaceManagerImpl>();
   arc_input_method_surface_manager_ =
       std::make_unique<ArcInputMethodSurfaceManager>();
-  wm_helper_ = std::make_unique<exo::WMHelper>(env);
+  wm_helper_ = std::make_unique<exo::WMHelperChromeOS>(env);
   exo::WMHelper::SetInstance(wm_helper_.get());
   display_ = std::make_unique<exo::Display>(
       arc_notification_surface_manager_.get(),
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 44a141755..c2dae03b 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -779,6 +779,8 @@
     "task/single_thread_task_runner_thread_mode.h",
     "task/task_executor.cc",
     "task/task_executor.h",
+    "task/task_features.cc",
+    "task/task_features.h",
     "task/task_observer.h",
     "task/task_scheduler/can_schedule_sequence_observer.h",
     "task/task_scheduler/delayed_task_manager.cc",
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h
index 3d91c26b..a09a66ca6 100644
--- a/base/task/sequence_manager/sequence_manager.h
+++ b/base/task/sequence_manager/sequence_manager.h
@@ -64,6 +64,11 @@
   // SequenceManager.
   virtual void BindToMessageLoop(MessageLoop* message_loop) = 0;
 
+  // Finishes the initialization for a SequenceManager created via
+  // CreateUnboundSequenceManagerWithPump(). Must not be called in any other
+  // circumstances. The ownership of the pump is transferred to SequenceManager.
+  virtual void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) = 0;
+
   // Initializes the SequenceManager on the bound thread. Should only be called
   // once and only after the ThreadController's dependencies were initialized.
   // Note that CreateSequenceManagerOnCurrentThread() performs this
@@ -133,6 +138,27 @@
   // Returns the metric recording configuration for the current SequenceManager.
   virtual const MetricRecordingSettings& GetMetricRecordingSettings() const = 0;
 
+  // Delete all tasks inside of all queues in this Sequence Manager.
+  // Note that at the moment SequenceManager still can have tasks inside it
+  // after this call due to tasks posting tasks from destructor.
+  virtual void DeletePendingTasks() = 0;
+
+  // Explicitly allow execution in the nested loops.
+  // TODO(altimin,crbug.com/901362): This method exists in the current form
+  // to facilitate MessageLoop deprecation. Rethink this after MessageLoopImpl
+  // is gone.
+  virtual void SetTaskExecutionAllowed(bool allowed) = 0;
+
+  // Whether task execution is allowed (it is disallowed in the nested loops
+  // unless allowed explicitly by SetTaskExecutionAllowed(true).
+  // TODO(altimin): This method exists in the current form to facilitate
+  // MessageLoop deprecation. Rethink this after MessageLoopImpl is gone.
+  virtual bool IsTaskExecutionAllowed() const = 0;
+
+  // Whether at least one queue associated with this SequenceManager has
+  // at least one task, either delayed or immediate.
+  virtual bool HasTasks() = 0;
+
   // Creates a task queue with the given type, |spec| and args.
   // Must be called on the main thread.
   // TODO(scheduler-dev): SequenceManager should not create TaskQueues.
@@ -143,6 +169,11 @@
                                             std::forward<Args>(args)...));
   }
 
+  // Returns true iff this SequenceManager has no immediate work to do
+  // (tasks with unexpired delay are fine, tasks with zero delay and
+  // expired delay are not).
+  virtual bool IsIdleForTesting() const = 0;
+
  protected:
   virtual std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl(
       const TaskQueue::Spec& spec) = 0;
@@ -166,6 +197,12 @@
 BASE_EXPORT std::unique_ptr<SequenceManager> CreateUnboundSequenceManager(
     MessageLoop* message_loop);
 
+// Create a SequenceManager for a future use which would bound directly
+// to a message pump. BindToMessagePump() call is expected before this
+// SequenceManager can be used.
+BASE_EXPORT std::unique_ptr<SequenceManager>
+CreateUnboundSequenceManagerWithPump();
+
 }  // namespace sequence_manager
 }  // namespace base
 
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index ac4e30f4..e8046eb 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -17,6 +17,7 @@
 #include "base/task/sequence_manager/real_time_domain.h"
 #include "base/task/sequence_manager/task_time_observer.h"
 #include "base/task/sequence_manager/thread_controller_impl.h"
+#include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
 #include "base/task/sequence_manager/work_queue.h"
 #include "base/task/sequence_manager/work_queue_sets.h"
 #include "base/time/default_tick_clock.h"
@@ -36,6 +37,10 @@
   return internal::SequenceManagerImpl::CreateUnbound(message_loop);
 }
 
+std::unique_ptr<SequenceManager> CreateUnboundSequenceManagerWithPump() {
+  return internal::SequenceManagerImpl::CreateUnboundWithPump();
+}
+
 namespace internal {
 
 namespace {
@@ -143,18 +148,32 @@
 // static
 std::unique_ptr<SequenceManagerImpl> SequenceManagerImpl::CreateUnbound(
     MessageLoop* message_loop) {
-  return WrapUnique(
-      new SequenceManagerImpl(internal::ThreadControllerImpl::Create(
-          message_loop, DefaultTickClock::GetInstance())));
+  return WrapUnique(new SequenceManagerImpl(ThreadControllerImpl::Create(
+      message_loop, DefaultTickClock::GetInstance())));
+}
+
+// static
+std::unique_ptr<SequenceManagerImpl>
+SequenceManagerImpl::CreateUnboundWithPump() {
+  return WrapUnique(new SequenceManagerImpl(
+      ThreadControllerWithMessagePumpImpl::CreateUnbound(
+          DefaultTickClock::GetInstance())));
 }
 
 void SequenceManagerImpl::BindToMessageLoop(MessageLoop* message_loop) {
-  controller_->SetMessageLoop(message_loop);
+  controller_->BindToCurrentThread(message_loop);
+  CompleteInitializationOnBoundThread();
+}
+
+void SequenceManagerImpl::BindToMessagePump(std::unique_ptr<MessagePump> pump) {
+  controller_->BindToCurrentThread(std::move(pump));
   CompleteInitializationOnBoundThread();
 }
 
 void SequenceManagerImpl::BindToCurrentThread() {
-  associated_thread_->BindToCurrentThread();
+  // Associated thread is bound early for thread controller with message pump.
+  if (associated_thread_->thread_id == kInvalidThreadId)
+    associated_thread_->BindToCurrentThread();
 }
 
 void SequenceManagerImpl::CompleteInitializationOnBoundThread() {
@@ -261,13 +280,32 @@
 }
 
 void SequenceManagerImpl::ReloadEmptyWorkQueues() {
+  DCHECK(main_thread_only().queues_to_reload.empty());
+
+  {
+    AutoLock lock(any_thread_lock_);
+
+    for (internal::IncomingImmediateWorkList* iter =
+             any_thread().incoming_immediate_work_list;
+         iter; iter = iter->next) {
+      main_thread_only().queues_to_reload.push_back(iter->queue);
+      iter->queue = nullptr;
+    }
+
+    any_thread().incoming_immediate_work_list = nullptr;
+  }
+
   // There are two cases where a queue needs reloading.  First, it might be
   // completely empty and we've just posted a task (this method handles that
   // case). Secondly if the work queue becomes empty in when calling
   // WorkQueue::TakeTaskFromWorkQueue (handled there).
   for (internal::TaskQueueImpl* queue : main_thread_only().queues_to_reload) {
+    // It's important we call ReloadImmediateWorkQueueIfEmpty out side of
+    // |any_thread_lock_| avoid lock order inversion.
     queue->ReloadImmediateWorkQueueIfEmpty();
   }
+
+  main_thread_only().queues_to_reload.clear();
 }
 
 void SequenceManagerImpl::WakeUpReadyDelayedQueues(LazyNow* lazy_now) {
@@ -312,8 +350,8 @@
 void SequenceManagerImpl::OnQueueHasIncomingImmediateWork(
     internal::TaskQueueImpl* queue,
     internal::EnqueueOrder enqueue_order,
-    bool queue_is_blocked) {
-  if (AddToIncomingImmediateWorkList(queue, enqueue_order) && !queue_is_blocked)
+    bool schedule_work) {
+  if (AddToIncomingImmediateWorkList(queue, enqueue_order) && schedule_work)
     controller_->ScheduleWork();
 }
 
@@ -351,22 +389,6 @@
   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
   TRACE_EVENT0("sequence_manager", "SequenceManagerImpl::TakeTask");
 
-  {
-    AutoLock lock(any_thread_lock_);
-    main_thread_only().queues_to_reload.clear();
-
-    for (internal::IncomingImmediateWorkList* iter =
-             any_thread().incoming_immediate_work_list;
-         iter; iter = iter->next) {
-      main_thread_only().queues_to_reload.push_back(iter->queue);
-      iter->queue = nullptr;
-    }
-
-    any_thread().incoming_immediate_work_list = nullptr;
-  }
-
-  // It's important we call ReloadEmptyWorkQueues out side of the lock to
-  // avoid a lock order inversion.
   ReloadEmptyWorkQueues();
   LazyNow lazy_now(controller_->GetClock());
   WakeUpReadyDelayedQueues(&lazy_now);
@@ -427,7 +449,7 @@
     CleanUpQueues();
 }
 
-TimeDelta SequenceManagerImpl::DelayTillNextTask(LazyNow* lazy_now) {
+TimeDelta SequenceManagerImpl::DelayTillNextTask(LazyNow* lazy_now) const {
   DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
 
   // If the selector has non-empty queues we trivially know there is immediate
@@ -751,6 +773,50 @@
   return metric_recording_settings_;
 }
 
+// TODO(altimin): Ensure that this removes all pending tasks.
+void SequenceManagerImpl::DeletePendingTasks() {
+  DCHECK(main_thread_only().task_execution_stack.empty())
+      << "Tasks should be deleted outside RunLoop";
+
+  for (TaskQueueImpl* task_queue : main_thread_only().active_queues)
+    task_queue->DeletePendingTasks();
+  for (const auto& it : main_thread_only().queues_to_gracefully_shutdown)
+    it.first->DeletePendingTasks();
+  for (const auto& it : main_thread_only().queues_to_delete)
+    it.first->DeletePendingTasks();
+}
+
+bool SequenceManagerImpl::HasTasks() {
+  DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
+
+  for (TaskQueueImpl* task_queue : main_thread_only().active_queues) {
+    if (task_queue->HasTasks())
+      return true;
+  }
+  for (const auto& it : main_thread_only().queues_to_gracefully_shutdown) {
+    if (it.first->HasTasks())
+      return true;
+  }
+  for (const auto& it : main_thread_only().queues_to_delete) {
+    if (it.first->HasTasks())
+      return true;
+  }
+  return false;
+}
+
+void SequenceManagerImpl::SetTaskExecutionAllowed(bool allowed) {
+  controller_->SetTaskExecutionAllowed(allowed);
+}
+
+bool SequenceManagerImpl::IsTaskExecutionAllowed() const {
+  return controller_->IsTaskExecutionAllowed();
+}
+
+bool SequenceManagerImpl::IsIdleForTesting() const {
+  LazyNow lazy_now(controller_->GetClock());
+  return DelayTillNextTask(&lazy_now) != TimeDelta();
+}
+
 NOINLINE bool SequenceManagerImpl::Validate() {
   return memory_corruption_sentinel_ == kMemoryCorruptionSentinelValue;
 }
diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h
index d6614866..62682a6e 100644
--- a/base/task/sequence_manager/sequence_manager_impl.h
+++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -96,9 +96,12 @@
   static std::unique_ptr<SequenceManagerImpl> CreateUnbound(
       MessageLoop* message_loop);
 
+  static std::unique_ptr<SequenceManagerImpl> CreateUnboundWithPump();
+
   // SequenceManager implementation:
   void BindToCurrentThread() override;
   void BindToMessageLoop(MessageLoop* message_loop) override;
+  void BindToMessagePump(std::unique_ptr<MessagePump> message_loop) override;
   void CompleteInitializationOnBoundThread() override;
   void SetObserver(Observer* observer) override;
   void AddTaskObserver(MessageLoop::TaskObserver* task_observer) override;
@@ -119,11 +122,16 @@
   void EnableCrashKeys(const char* file_name_crash_key,
                        const char* function_name_crash_key) override;
   const MetricRecordingSettings& GetMetricRecordingSettings() const override;
+  void DeletePendingTasks() override;
+  bool HasTasks() override;
+  void SetTaskExecutionAllowed(bool allowed) override;
+  bool IsTaskExecutionAllowed() const override;
+  bool IsIdleForTesting() const override;
 
   // Implementation of SequencedTaskSource:
   Optional<PendingTask> TakeTask() override;
   void DidRunTask() override;
-  TimeDelta DelayTillNextTask(LazyNow* lazy_now) override;
+  TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override;
   bool HasPendingHighResolutionTasks() override;
 
   // Requests that a task to process work is posted on the main task runner.
@@ -255,6 +263,8 @@
     bool task_was_run_on_quiescence_monitored_queue = false;
     bool nesting_observer_registered_ = false;
 
+    bool task_execution_allowed_ = true;
+
     // Due to nested runloops more than one task can be executing concurrently.
     std::list<ExecutingTask> task_execution_stack;
 
@@ -287,11 +297,11 @@
                             internal::WorkQueue* selected_work_queue) const;
 
   // Adds |queue| to |any_thread().has_incoming_immediate_work_| and if
-  // |queue_is_blocked| is false it makes sure a DoWork is posted.
+  // |schedule_work| is true it makes sure a DoWork is posted.
   // Can be called from any thread.
   void OnQueueHasIncomingImmediateWork(internal::TaskQueueImpl* queue,
                                        internal::EnqueueOrder enqueue_order,
-                                       bool queue_is_blocked);
+                                       bool schedule_work);
 
   // Returns true if |task_queue| was added to the list, or false if it was
   // already in the list.  If |task_queue| was inserted, the |order| is set
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index a4965ac..f3ab617 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/sequence_manager/real_time_domain.h"
 #include "base/task/sequence_manager/task_queue_impl.h"
@@ -43,6 +44,7 @@
 using testing::ElementsAreArray;
 using testing::Mock;
 using testing::Not;
+using testing::UnorderedElementsAre;
 using testing::_;
 using base::sequence_manager::internal::EnqueueOrder;
 
@@ -1889,17 +1891,22 @@
   // We should get a notification when a task is posted on an empty queue.
   EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
   runners_[0]->PostTask(FROM_HERE, BindOnce(&NopTask));
+  manager_->ReloadEmptyWorkQueues();
   Mock::VerifyAndClearExpectations(&observer);
 
   // But not subsequently.
   EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_, _)).Times(0);
   runners_[0]->PostTask(FROM_HERE, BindOnce(&NopTask));
+  manager_->ReloadEmptyWorkQueues();
   Mock::VerifyAndClearExpectations(&observer);
 
   // Unless the immediate work queue is emptied.
-  runners_[0]->GetTaskQueueImpl()->ReloadImmediateWorkQueueIfEmpty();
+  manager_->TakeTask();
+  manager_->TakeTask();
   EXPECT_CALL(observer, OnQueueNextWakeUpChanged(runners_[0].get(), _));
   runners_[0]->PostTask(FROM_HERE, BindOnce(&NopTask));
+  manager_->ReloadEmptyWorkQueues();
+  Mock::VerifyAndClearExpectations(&observer);
 
   // Tidy up.
   runners_[0]->ShutdownTaskQueue();
@@ -3347,12 +3354,15 @@
 
   // Create without a sequence manager.
   std::unique_ptr<TimeDomain> time_domain =
-      std::make_unique<internal::RealTimeDomain>();
+      std::make_unique<MockTimeDomain>(TimeTicks());
   std::unique_ptr<TaskQueueImpl> queue2 = std::make_unique<TaskQueueImpl>(
       nullptr, time_domain.get(), TaskQueue::Spec("stub"));
   scoped_refptr<SingleThreadTaskRunner> task_runner2 =
       queue2->CreateTaskRunner(0);
   EXPECT_FALSE(task_runner2->PostTask(FROM_HERE, BindOnce(&NopTask)));
+
+  // Tidy up.
+  queue2->UnregisterTaskQueue();
 }
 
 TEST_P(SequenceManagerTest, DestructorPostChainDuringShutdown) {
@@ -3506,6 +3516,162 @@
   EXPECT_FALSE(manager_->HasPendingHighResolutionTasks());
 }
 
+namespace {
+
+class PostTaskWhenDeleted;
+void CallbackWithDestructor(std::unique_ptr<PostTaskWhenDeleted>);
+
+class PostTaskWhenDeleted {
+ public:
+  PostTaskWhenDeleted(std::string name,
+                      scoped_refptr<SingleThreadTaskRunner> task_runner,
+                      size_t depth,
+                      std::set<std::string>* tasks_alive,
+                      std::vector<std::string>* tasks_deleted)
+      : name_(name),
+        task_runner_(std::move(task_runner)),
+        depth_(depth),
+        tasks_alive_(tasks_alive),
+        tasks_deleted_(tasks_deleted) {
+    tasks_alive_->insert(full_name());
+  }
+
+  ~PostTaskWhenDeleted() {
+    DCHECK(tasks_alive_->find(full_name()) != tasks_alive_->end());
+    tasks_alive_->erase(full_name());
+    tasks_deleted_->push_back(full_name());
+
+    if (depth_ > 0) {
+      task_runner_->PostTask(
+          FROM_HERE, base::BindOnce(&CallbackWithDestructor,
+                                    std::make_unique<PostTaskWhenDeleted>(
+                                        name_, task_runner_, depth_ - 1,
+                                        tasks_alive_, tasks_deleted_)));
+    }
+  }
+
+ private:
+  std::string full_name() { return name_ + " " + IntToString(depth_); }
+
+  std::string name_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  int depth_;
+  std::set<std::string>* tasks_alive_;
+  std::vector<std::string>* tasks_deleted_;
+};
+
+void CallbackWithDestructor(std::unique_ptr<PostTaskWhenDeleted> object) {}
+
+}  // namespace
+
+TEST_P(SequenceManagerTest, DeletePendingTasks_Simple) {
+  CreateTaskQueues(1u);
+
+  std::set<std::string> tasks_alive;
+  std::vector<std::string> tasks_deleted;
+
+  runners_[0]->PostTask(
+      FROM_HERE,
+      BindOnce(&CallbackWithDestructor, std::make_unique<PostTaskWhenDeleted>(
+                                            "task", runners_[0]->task_runner(),
+                                            0, &tasks_alive, &tasks_deleted)));
+
+  EXPECT_THAT(tasks_alive, ElementsAre("task 0"));
+  EXPECT_TRUE(manager_->HasTasks());
+
+  manager_->DeletePendingTasks();
+
+  EXPECT_THAT(tasks_alive, ElementsAre());
+  EXPECT_THAT(tasks_deleted, ElementsAre("task 0"));
+  EXPECT_FALSE(manager_->HasTasks());
+
+  // Ensure that |tasks_alive| and |tasks_deleted| outlive |manager_|
+  // and we get a test failure instead of a test crash.
+  manager_.reset();
+}
+
+TEST_P(SequenceManagerTest, DeletePendingTasks_Complex) {
+  CreateTaskQueues(4u);
+
+  std::set<std::string> tasks_alive;
+  std::vector<std::string> tasks_deleted;
+
+  // Post immediate and delayed to the same task queue.
+  runners_[0]->PostTask(
+      FROM_HERE,
+      BindOnce(&CallbackWithDestructor, std::make_unique<PostTaskWhenDeleted>(
+                                            "Q1 I1", runners_[0]->task_runner(),
+                                            1, &tasks_alive, &tasks_deleted)));
+  runners_[0]->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(&CallbackWithDestructor, std::make_unique<PostTaskWhenDeleted>(
+                                            "Q1 D1", runners_[0]->task_runner(),
+                                            0, &tasks_alive, &tasks_deleted)),
+      base::TimeDelta::FromSeconds(1));
+
+  // Post one delayed task to the second queue.
+  runners_[1]->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(&CallbackWithDestructor, std::make_unique<PostTaskWhenDeleted>(
+                                            "Q2 D1", runners_[1]->task_runner(),
+                                            1, &tasks_alive, &tasks_deleted)),
+      base::TimeDelta::FromSeconds(1));
+
+  // Post two immediate tasks and force a queue reload between them.
+  runners_[2]->PostTask(
+      FROM_HERE,
+      BindOnce(&CallbackWithDestructor, std::make_unique<PostTaskWhenDeleted>(
+                                            "Q3 I1", runners_[2]->task_runner(),
+                                            0, &tasks_alive, &tasks_deleted)));
+  runners_[2]->GetTaskQueueImpl()->ReloadImmediateWorkQueueIfEmpty();
+  runners_[2]->PostTask(
+      FROM_HERE,
+      BindOnce(&CallbackWithDestructor, std::make_unique<PostTaskWhenDeleted>(
+                                            "Q3 I2", runners_[2]->task_runner(),
+                                            1, &tasks_alive, &tasks_deleted)));
+
+  // Post a delayed task and force a delay to expire.
+  runners_[3]->PostDelayedTask(
+      FROM_HERE,
+      BindOnce(&CallbackWithDestructor, std::make_unique<PostTaskWhenDeleted>(
+                                            "Q4 D1", runners_[1]->task_runner(),
+                                            0, &tasks_alive, &tasks_deleted)),
+      TimeDelta::FromMilliseconds(10));
+  test_task_runner_->AdvanceMockTickClock(TimeDelta::FromMilliseconds(100));
+  LazyNow lazy_now(test_task_runner_->GetMockTickClock());
+  manager_->WakeUpReadyDelayedQueues(&lazy_now);
+
+  EXPECT_THAT(tasks_alive,
+              UnorderedElementsAre("Q1 I1 1", "Q1 D1 0", "Q2 D1 1", "Q3 I1 0",
+                                   "Q3 I2 1", "Q4 D1 0"));
+  EXPECT_TRUE(manager_->HasTasks());
+
+  manager_->DeletePendingTasks();
+
+  // Note that the tasks reposting themselves are still alive.
+  EXPECT_THAT(tasks_alive,
+              UnorderedElementsAre("Q1 I1 0", "Q2 D1 0", "Q3 I2 0"));
+  EXPECT_THAT(tasks_deleted,
+              UnorderedElementsAre("Q1 I1 1", "Q1 D1 0", "Q2 D1 1", "Q3 I1 0",
+                                   "Q3 I2 1", "Q4 D1 0"));
+  EXPECT_TRUE(manager_->HasTasks());
+  tasks_deleted.clear();
+
+  // Second call should remove the rest.
+  manager_->DeletePendingTasks();
+  EXPECT_THAT(tasks_alive, UnorderedElementsAre());
+  EXPECT_THAT(tasks_deleted,
+              UnorderedElementsAre("Q1 I1 0", "Q2 D1 0", "Q3 I2 0"));
+  EXPECT_FALSE(manager_->HasTasks());
+
+  // Ensure that |tasks_alive| and |tasks_deleted| outlive |manager_|
+  // and we get a test failure instead of a test crash.
+  manager_.reset();
+}
+
+// TODO(altimin): Add a test that posts an infinite number of other tasks
+// from its destructor.
+
 }  // namespace sequence_manager_impl_unittest
 }  // namespace internal
 }  // namespace sequence_manager
diff --git a/base/task/sequence_manager/sequenced_task_source.h b/base/task/sequence_manager/sequenced_task_source.h
index 612a2b4..38d79ee7 100644
--- a/base/task/sequence_manager/sequenced_task_source.h
+++ b/base/task/sequence_manager/sequenced_task_source.h
@@ -29,7 +29,7 @@
 
   // Returns the delay till the next task or TimeDelta::Max()
   // if there are no tasks left.
-  virtual TimeDelta DelayTillNextTask(LazyNow* lazy_now) = 0;
+  virtual TimeDelta DelayTillNextTask(LazyNow* lazy_now) const = 0;
 
   // Return true if there are any pending tasks in the task source which require
   // high resolution timing.
diff --git a/base/task/sequence_manager/task_queue.h b/base/task/sequence_manager/task_queue.h
index 33bf4f2..f6e5d83 100644
--- a/base/task/sequence_manager/task_queue.h
+++ b/base/task/sequence_manager/task_queue.h
@@ -315,6 +315,7 @@
  protected:
   TaskQueue(std::unique_ptr<internal::TaskQueueImpl> impl,
             const TaskQueue::Spec& spec);
+
   virtual ~TaskQueue();
 
   internal::TaskQueueImpl* GetTaskQueueImpl() const { return impl_.get(); }
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index ff27999..c344c4f 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -47,11 +47,12 @@
                              TimeDomain* time_domain,
                              const TaskQueue::Spec& spec)
     : name_(spec.name),
+      sequence_manager_(sequence_manager),
       associated_thread_(sequence_manager
                              ? sequence_manager->associated_thread()
                              : AssociatedThreadId::CreateBound()),
-      any_thread_(sequence_manager, time_domain),
-      main_thread_only_(sequence_manager, this, time_domain),
+      any_thread_(time_domain),
+      main_thread_only_(this, time_domain),
       proxy_(MakeRefCounted<TaskQueueProxy>(this, associated_thread_)),
       should_monitor_quiescence_(spec.should_monitor_quiescence),
       should_notify_observers_(spec.should_notify_observers),
@@ -70,23 +71,19 @@
   // contains a strong reference to this TaskQueueImpl and the
   // SequenceManagerImpl destructor calls UnregisterTaskQueue on all task
   // queues.
-  DCHECK(!any_thread().sequence_manager)
+  DCHECK(any_thread().unregistered)
       << "UnregisterTaskQueue must be called first!";
 #endif
 }
 
-TaskQueueImpl::AnyThread::AnyThread(SequenceManagerImpl* sequence_manager,
-                                    TimeDomain* time_domain)
-    : sequence_manager(sequence_manager), time_domain(time_domain) {}
+TaskQueueImpl::AnyThread::AnyThread(TimeDomain* time_domain)
+    : time_domain(time_domain) {}
 
 TaskQueueImpl::AnyThread::~AnyThread() = default;
 
-TaskQueueImpl::MainThreadOnly::MainThreadOnly(
-    SequenceManagerImpl* sequence_manager,
-    TaskQueueImpl* task_queue,
-    TimeDomain* time_domain)
-    : sequence_manager(sequence_manager),
-      time_domain(time_domain),
+TaskQueueImpl::MainThreadOnly::MainThreadOnly(TaskQueueImpl* task_queue,
+                                              TimeDomain* time_domain)
+    : time_domain(time_domain),
       delayed_work_queue(
           new WorkQueue(task_queue, "delayed", WorkQueue::QueueType::kDelayed)),
       immediate_work_queue(new WorkQueue(task_queue,
@@ -119,17 +116,12 @@
     if (main_thread_only().time_domain)
       main_thread_only().time_domain->UnregisterQueue(this);
 
-    if (!any_thread().sequence_manager)
-      return;
+    any_thread().unregistered = true;
 
     main_thread_only().on_task_completed_handler = OnTaskCompletedHandler();
     any_thread().time_domain = nullptr;
     main_thread_only().time_domain = nullptr;
 
-    any_thread().sequence_manager = nullptr;
-    main_thread_only().sequence_manager = nullptr;
-    any_thread().on_next_wake_up_changed_callback =
-        OnNextWakeUpChangedCallback();
     main_thread_only().on_next_wake_up_changed_callback =
         OnNextWakeUpChangedCallback();
     immediate_incoming_queue.swap(immediate_incoming_queue_);
@@ -171,26 +163,62 @@
   // i.e. has a sequence manager and not being unregistered. This is enforced
   // by |proxy_| which is detached if this condition not met.
   if (task.delay.is_zero()) {
-    PostImmediateTaskImpl(std::move(task));
+    PostImmediateTaskImpl(std::move(task), current_thread);
   } else {
     PostDelayedTaskImpl(std::move(task), current_thread);
   }
 }
 
-void TaskQueueImpl::PostImmediateTaskImpl(PostedTask task) {
+void TaskQueueImpl::PostImmediateTaskImpl(PostedTask task,
+                                          CurrentThread current_thread) {
   // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
   // for details.
   CHECK(task.callback);
-  AutoLock lock(any_thread_lock_);
-  DCHECK(any_thread().sequence_manager);
 
-  EnqueueOrder sequence_number =
-      any_thread().sequence_manager->GetNextSequenceNumber();
+  TimeTicks now;
+  if (delayed_fence_allowed_) {
+    if (current_thread == CurrentThread::kMainThread) {
+      now = main_thread_only().time_domain->Now();
+    } else {
+      AutoLock lock(any_thread_lock_);
+      now = any_thread().time_domain->Now();
+    }
+  }
 
-  PushOntoImmediateIncomingQueueLocked(Task(
-      std::move(task),
-      delayed_fence_allowed_ ? any_thread().time_domain->Now() : TimeTicks(),
-      sequence_number, sequence_number));
+  // If the |immediate_incoming_queue| is empty we need a DoWork posted to make
+  // it run.
+  bool was_immediate_incoming_queue_empty;
+  EnqueueOrder sequence_number;
+  {
+    // TODO(alexclarke): Maybe add a main thread only immediate_incoming_queue
+    // See https://crbug.com/901800
+    AutoLock lock(immediate_incoming_queue_lock_);
+    // The sequence number must be incremented atomically with pushing onto the
+    // incoming queue. Otherwise if there are several threads posting task we
+    // risk breaking the assumption that sequence numbers increase monotonically
+    // within a queue.
+    sequence_number = sequence_manager_->GetNextSequenceNumber();
+    was_immediate_incoming_queue_empty = immediate_incoming_queue().empty();
+    immediate_incoming_queue().push_back(
+        Task(std::move(task), now, sequence_number, sequence_number));
+    sequence_manager_->WillQueueTask(&immediate_incoming_queue().back());
+  }
+
+  if (was_immediate_incoming_queue_empty) {
+    bool schedule_work = true;
+    // In general we need to ScheduleWork unless the queue is blocked or
+    // disabled, however we can only detect that on the main thread. If there's
+    // a wake up observer we have to ScheduleWork or we can't guarantee the
+    // observer will fire.
+    if (current_thread == CurrentThread::kMainThread &&
+        main_thread_only().on_next_wake_up_changed_callback.is_null()) {
+      schedule_work = IsQueueEnabled() && !main_thread_only().current_fence;
+    }
+    sequence_manager_->OnQueueHasIncomingImmediateWork(this, sequence_number,
+                                                       schedule_work);
+  }
+
+  TraceQueueSize();
 }
 
 void TaskQueueImpl::PostDelayedTaskImpl(PostedTask task,
@@ -212,10 +240,7 @@
 
   if (current_thread == CurrentThread::kMainThread) {
     // Lock-free fast path for delayed tasks posted from the main thread.
-    DCHECK(main_thread_only().sequence_manager);
-
-    EnqueueOrder sequence_number =
-        main_thread_only().sequence_manager->GetNextSequenceNumber();
+    EnqueueOrder sequence_number = sequence_manager_->GetNextSequenceNumber();
 
     TimeTicks time_domain_now = main_thread_only().time_domain->Now();
     TimeTicks time_domain_delayed_run_time = time_domain_now + task.delay;
@@ -229,10 +254,7 @@
     // because it causes two main thread tasks to be run.  Should this
     // assumption prove to be false in future, we may need to revisit this.
     AutoLock lock(any_thread_lock_);
-    DCHECK(any_thread().sequence_manager);
-
-    EnqueueOrder sequence_number =
-        any_thread().sequence_manager->GetNextSequenceNumber();
+    EnqueueOrder sequence_number = sequence_manager_->GetNextSequenceNumber();
 
     TimeTicks time_domain_now = any_thread().time_domain->Now();
     TimeTicks time_domain_delayed_run_time = time_domain_now + task.delay;
@@ -247,7 +269,7 @@
     TimeTicks now,
     bool notify_task_annotator) {
   if (notify_task_annotator)
-    main_thread_only().sequence_manager->WillQueueTask(&pending_task);
+    sequence_manager_->WillQueueTask(&pending_task);
   main_thread_only().delayed_incoming_queue.push(std::move(pending_task));
 
   LazyNow lazy_now(now);
@@ -257,18 +279,15 @@
 }
 
 void TaskQueueImpl::PushOntoDelayedIncomingQueueLocked(Task pending_task) {
-  any_thread().sequence_manager->WillQueueTask(&pending_task);
+  sequence_manager_->WillQueueTask(&pending_task);
 
-  EnqueueOrder thread_hop_task_sequence_number =
-      any_thread().sequence_manager->GetNextSequenceNumber();
   // TODO(altimin): Add a copy method to Task to capture metadata here.
-  PushOntoImmediateIncomingQueueLocked(
-      Task(PostedTask(BindOnce(&TaskQueueImpl::ScheduleDelayedWorkTask,
-                               Unretained(this), std::move(pending_task)),
-                      FROM_HERE, TimeDelta(), Nestable::kNonNestable,
-                      pending_task.task_type),
-           TimeTicks(), thread_hop_task_sequence_number,
-           thread_hop_task_sequence_number));
+  PostImmediateTaskImpl(
+      PostedTask(BindOnce(&TaskQueueImpl::ScheduleDelayedWorkTask,
+                          Unretained(this), std::move(pending_task)),
+                 FROM_HERE, TimeDelta(), Nestable::kNonNestable,
+                 pending_task.task_type),
+      CurrentThread::kNotMainThread);
 }
 
 void TaskQueueImpl::ScheduleDelayedWorkTask(Task pending_task) {
@@ -292,41 +311,16 @@
   TraceQueueSize();
 }
 
-void TaskQueueImpl::PushOntoImmediateIncomingQueueLocked(Task task) {
-  // If the |immediate_incoming_queue| is empty we need a DoWork posted to make
-  // it run.
-  bool was_immediate_incoming_queue_empty;
-
-  EnqueueOrder sequence_number = task.enqueue_order();
-  TimeTicks desired_run_time = task.delayed_run_time;
-
-  {
-    AutoLock lock(immediate_incoming_queue_lock_);
-    was_immediate_incoming_queue_empty = immediate_incoming_queue().empty();
-    any_thread().sequence_manager->WillQueueTask(&task);
-    immediate_incoming_queue().push_back(std::move(task));
-  }
-
-  if (was_immediate_incoming_queue_empty) {
-    // However there's no point posting a DoWork for a blocked queue. NB we can
-    // only tell if it's disabled from the main thread.
-    bool queue_is_blocked =
-        RunsTasksInCurrentSequence() &&
-        (!IsQueueEnabled() || main_thread_only().current_fence);
-    any_thread().sequence_manager->OnQueueHasIncomingImmediateWork(
-        this, sequence_number, queue_is_blocked);
-    if (!any_thread().on_next_wake_up_changed_callback.is_null())
-      any_thread().on_next_wake_up_changed_callback.Run(desired_run_time);
-  }
-
-  TraceQueueSize();
-}
-
 void TaskQueueImpl::ReloadImmediateWorkQueueIfEmpty() {
   if (!main_thread_only().immediate_work_queue->Empty())
     return;
 
   main_thread_only().immediate_work_queue->ReloadEmptyImmediateQueue();
+
+  if (!main_thread_only().on_next_wake_up_changed_callback.is_null() &&
+      IsQueueEnabled()) {
+    main_thread_only().on_next_wake_up_changed_callback.Run(TimeTicks());
+  }
 }
 
 void TaskQueueImpl::ReloadEmptyImmediateQueue(TaskDeque* queue) {
@@ -433,8 +427,7 @@
       break;
     ActivateDelayedFenceIfNeeded(task.delayed_run_time);
     DCHECK(!task.enqueue_order_set());
-    task.set_enqueue_order(
-        main_thread_only().sequence_manager->GetNextSequenceNumber());
+    task.set_enqueue_order(sequence_manager_->GetNextSequenceNumber());
     main_thread_only().delayed_work_queue->Push(std::move(task));
     main_thread_only().delayed_incoming_queue.pop();
 
@@ -443,8 +436,7 @@
     // delayed tasks). Ensure that there is a DoWork posting. No-op inside
     // existing DoWork due to DoWork deduplication.
     if (IsQueueEnabled() || !main_thread_only().current_fence) {
-      main_thread_only().sequence_manager->MaybeScheduleImmediateWork(
-          FROM_HERE);
+      sequence_manager_->MaybeScheduleImmediateWork(FROM_HERE);
     }
   }
 
@@ -472,11 +464,10 @@
 }
 
 void TaskQueueImpl::SetQueuePriority(TaskQueue::QueuePriority priority) {
-  if (!main_thread_only().sequence_manager || priority == GetQueuePriority())
+  if (priority == GetQueuePriority())
     return;
-  main_thread_only()
-      .sequence_manager->main_thread_only()
-      .selector.SetQueuePriority(this, priority);
+  sequence_manager_->main_thread_only().selector.SetQueuePriority(this,
+                                                                  priority);
 }
 
 TaskQueue::QueuePriority TaskQueueImpl::GetQueuePriority() const {
@@ -491,7 +482,7 @@
   AutoLock immediate_incoming_queue_lock(immediate_incoming_queue_lock_);
   state->BeginDictionary();
   state->SetString("name", GetName());
-  if (!main_thread_only().sequence_manager) {
+  if (any_thread().unregistered) {
     state->SetBoolean("unregistered", true);
     state->EndDictionary();
     return;
@@ -590,12 +581,8 @@
   {
     AutoLock lock(any_thread_lock_);
     DCHECK(time_domain);
-    // NOTE this is similar to checking |any_thread().sequence_manager| but
-    // the TaskQueueSelectorTests constructs TaskQueueImpl directly with a null
-    // sequence_manager.  Instead we check |any_thread().time_domain| which is
-    // another way of asserting that UnregisterTaskQueue has not been called.
-    DCHECK(any_thread().time_domain);
-    if (!any_thread().time_domain)
+    DCHECK(!any_thread().unregistered);
+    if (any_thread().unregistered)
       return;
     DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
     if (time_domain == main_thread_only().time_domain)
@@ -617,7 +604,8 @@
 }
 
 TimeDomain* TaskQueueImpl::GetTimeDomain() const {
-  if (PlatformThread::CurrentId() == associated_thread_->thread_id)
+  if (associated_thread_->thread_id == kInvalidThreadId ||
+      PlatformThread::CurrentId() == associated_thread_->thread_id)
     return main_thread_only().time_domain;
 
   AutoLock lock(any_thread_lock_);
@@ -629,17 +617,13 @@
 }
 
 void TaskQueueImpl::InsertFence(TaskQueue::InsertFencePosition position) {
-  if (!main_thread_only().sequence_manager)
-    return;
-
   // Only one fence may be present at a time.
   main_thread_only().delayed_fence = nullopt;
 
   EnqueueOrder previous_fence = main_thread_only().current_fence;
-  EnqueueOrder current_fence =
-      position == TaskQueue::InsertFencePosition::kNow
-          ? main_thread_only().sequence_manager->GetNextSequenceNumber()
-          : EnqueueOrder::blocking_fence();
+  EnqueueOrder current_fence = position == TaskQueue::InsertFencePosition::kNow
+                                   ? sequence_manager_->GetNextSequenceNumber()
+                                   : EnqueueOrder::blocking_fence();
 
   // Tasks posted after this point will have a strictly higher enqueue order
   // and will be blocked from running.
@@ -658,9 +642,8 @@
     }
   }
 
-  if (IsQueueEnabled() && task_unblocked) {
-    main_thread_only().sequence_manager->MaybeScheduleImmediateWork(FROM_HERE);
-  }
+  if (IsQueueEnabled() && task_unblocked)
+    sequence_manager_->MaybeScheduleImmediateWork(FROM_HERE);
 }
 
 void TaskQueueImpl::InsertFenceAt(TimeTicks time) {
@@ -674,9 +657,6 @@
 }
 
 void TaskQueueImpl::RemoveFence() {
-  if (!main_thread_only().sequence_manager)
-    return;
-
   EnqueueOrder previous_fence = main_thread_only().current_fence;
   main_thread_only().current_fence = EnqueueOrder::none();
   main_thread_only().delayed_fence = nullopt;
@@ -692,9 +672,8 @@
     }
   }
 
-  if (IsQueueEnabled() && task_unblocked) {
-    main_thread_only().sequence_manager->MaybeScheduleImmediateWork(FROM_HERE);
-  }
+  if (IsQueueEnabled() && task_unblocked)
+    sequence_manager_->MaybeScheduleImmediateWork(FROM_HERE);
 }
 
 bool TaskQueueImpl::BlockedByFence() const {
@@ -839,7 +818,8 @@
 }
 
 void TaskQueueImpl::EnableOrDisableWithSelector(bool enable) {
-  if (!main_thread_only().sequence_manager)
+  // |sequence_manager_| can be null in tests.
+  if (!sequence_manager_)
     return;
 
   LazyNow lazy_now = main_thread_only().time_domain->CreateLazyNow();
@@ -854,13 +834,9 @@
 
     // Note the selector calls SequenceManager::OnTaskQueueEnabled which posts
     // a DoWork if needed.
-    main_thread_only()
-        .sequence_manager->main_thread_only()
-        .selector.EnableQueue(this);
+    sequence_manager_->main_thread_only().selector.EnableQueue(this);
   } else {
-    main_thread_only()
-        .sequence_manager->main_thread_only()
-        .selector.DisableQueue(this);
+    sequence_manager_->main_thread_only().selector.DisableQueue(this);
   }
 }
 
@@ -875,8 +851,7 @@
 void TaskQueueImpl::SweepCanceledDelayedTasks(TimeTicks now) {
   if (main_thread_only().delayed_incoming_queue.empty())
     return;
-  const SequenceManagerImpl* sequence_manager =
-      main_thread_only().sequence_manager;
+  const SequenceManagerImpl* sequence_manager = sequence_manager_;
   main_thread_only().delayed_incoming_queue.SweepCancelledTasks(
       sequence_manager);
 
@@ -917,8 +892,6 @@
            "blink::scheduler::TaskQueue";
   }
 #endif
-  AutoLock lock(any_thread_lock_);
-  any_thread().on_next_wake_up_changed_callback = callback;
   main_thread_only().on_next_wake_up_changed_callback = callback;
 }
 
@@ -994,14 +967,13 @@
 
 bool TaskQueueImpl::IsUnregistered() const {
   AutoLock lock(any_thread_lock_);
-  return !any_thread().sequence_manager;
+  return any_thread().unregistered;
 }
 
 WeakPtr<SequenceManagerImpl> TaskQueueImpl::GetSequenceManagerWeakPtr() {
-  return main_thread_only().sequence_manager->GetWeakPtr();
+  return sequence_manager_->GetWeakPtr();
 }
 
-
 void TaskQueueImpl::SetQueueEnabledForTest(bool enabled) {
   main_thread_only().is_enabled_for_test = enabled;
   EnableOrDisableWithSelector(IsQueueEnabled());
@@ -1016,10 +988,38 @@
   main_thread_only().delayed_fence = nullopt;
 }
 
-void TaskQueueImpl::ClearSequenceManagerForTesting() {
-  AutoLock lock(any_thread_lock_);
-  any_thread().sequence_manager = nullptr;
-  main_thread_only().sequence_manager = nullptr;
+void TaskQueueImpl::DeletePendingTasks() {
+  main_thread_only().delayed_work_queue->DeletePendingTasks();
+  main_thread_only().immediate_work_queue->DeletePendingTasks();
+  // TODO(altimin): Add clear() method to DelayedIncomingQueue.
+  DelayedIncomingQueue queue_to_delete;
+  main_thread_only().delayed_incoming_queue.swap(queue_to_delete);
+
+  TaskDeque deque;
+  {
+    // Limit the scope of the lock to ensure that the deque is destroyed
+    // outside of the lock to allow it to post tasks.
+    base::AutoLock lock(immediate_incoming_queue_lock_);
+    deque.swap(immediate_incoming_queue());
+  }
+
+  LazyNow lazy_now = main_thread_only().time_domain->CreateLazyNow();
+  UpdateDelayedWakeUp(&lazy_now);
+}
+
+bool TaskQueueImpl::HasTasks() const {
+  if (!main_thread_only().delayed_work_queue->Empty())
+    return true;
+  if (!main_thread_only().immediate_work_queue->Empty())
+    return true;
+  if (!main_thread_only().delayed_incoming_queue.empty())
+    return true;
+
+  base::AutoLock lock(immediate_incoming_queue_lock_);
+  if (!immediate_incoming_queue().empty())
+    return true;
+
+  return false;
 }
 
 TaskQueueImpl::DelayedIncomingQueue::DelayedIncomingQueue() = default;
@@ -1054,6 +1054,11 @@
   queue_ = std::move(remaining_tasks);
 }
 
+void TaskQueueImpl::DelayedIncomingQueue::swap(DelayedIncomingQueue& other) {
+  queue_.swap(other.queue_);
+  std::swap(pending_high_res_tasks_, other.pending_high_res_tasks_);
+}
+
 void TaskQueueImpl::DelayedIncomingQueue::AsValueInto(
     TimeTicks now,
     trace_event::TracedValue* state) const {
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index eff885e5..c90d471 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -231,21 +231,24 @@
   bool RequiresTaskTiming() const;
 
   WeakPtr<SequenceManagerImpl> GetSequenceManagerWeakPtr();
-  SequenceManagerImpl* sequence_manager() {
-    return main_thread_only().sequence_manager;
-  }
+
+  SequenceManagerImpl* sequence_manager() const { return sequence_manager_; }
 
   // Returns true if this queue is unregistered or task queue manager is deleted
   // and this queue can be safely deleted on any thread.
   bool IsUnregistered() const;
 
+  // Delete all tasks within this TaskQueue.
+  void DeletePendingTasks();
+
+  // Whether this task queue owns any tasks. Task queue being disabled doesn't
+  // affect this.
+  bool HasTasks() const;
+
   // Disables queue for testing purposes, when a QueueEnabledVoter can't be
   // constructed due to not having TaskQueue.
   void SetQueueEnabledForTest(bool enabled);
 
-  // TODO(alexclarke): Remove when possible.
-  void ClearSequenceManagerForTesting();
-
  protected:
   void SetDelayedWakeUpForTesting(Optional<DelayedWakeUp> wake_up);
 
@@ -254,17 +257,15 @@
   friend class WorkQueueTest;
 
   struct AnyThread {
-    AnyThread(SequenceManagerImpl* sequence_manager, TimeDomain* time_domain);
+    explicit AnyThread(TimeDomain* time_domain);
     ~AnyThread();
 
-    // SequenceManagerImpl, TimeDomain and Observer are maintained in two
-    // copies: inside AnyThread and inside MainThreadOnly. They can be changed
-    // only from main thread, so it should be locked before accessing from other
-    // threads.
-    SequenceManagerImpl* sequence_manager;
+    // TimeDomain is maintained in two copies: inside AnyThread and inside
+    // MainThreadOnly. It can be changed only from main thread, so it should be
+    // locked before accessing from other threads.
     TimeDomain* time_domain;
-    // Callback corresponding to TaskQueue::Observer::OnQueueNextChanged.
-    OnNextWakeUpChangedCallback on_next_wake_up_changed_callback;
+
+    bool unregistered = false;
   };
 
   // A queue for holding delayed tasks before their delay has expired.
@@ -278,6 +279,7 @@
     bool empty() const { return queue_.empty(); }
     size_t size() const { return queue_.size(); }
     const Task& top() const { return queue_.top(); }
+    void swap(DelayedIncomingQueue& other);
 
     bool has_pending_high_resolution_tasks() const {
       return pending_high_res_tasks_;
@@ -296,16 +298,13 @@
   };
 
   struct MainThreadOnly {
-    MainThreadOnly(SequenceManagerImpl* sequence_manager,
-                   TaskQueueImpl* task_queue,
-                   TimeDomain* time_domain);
+    MainThreadOnly(TaskQueueImpl* task_queue, TimeDomain* time_domain);
     ~MainThreadOnly();
 
-    // Another copy of SequenceManagerImpl, TimeDomain and Observer
-    // for lock-free access from the main thread.
+    // Another copy of TimeDomain for lock-free access from the main thread.
     // See description inside struct AnyThread for details.
-    SequenceManagerImpl* sequence_manager;
     TimeDomain* time_domain;
+
     // Callback corresponding to TaskQueue::Observer::OnQueueNextChanged.
     OnNextWakeUpChangedCallback on_next_wake_up_changed_callback;
 
@@ -329,7 +328,7 @@
     bool is_enabled_for_test;
   };
 
-  void PostImmediateTaskImpl(PostedTask task);
+  void PostImmediateTaskImpl(PostedTask task, CurrentThread current_thread);
   void PostDelayedTaskImpl(PostedTask task, CurrentThread current_thread);
 
   // Push the task onto the |delayed_incoming_queue|. Lock-free main thread
@@ -346,11 +345,6 @@
 
   void MoveReadyImmediateTasksToImmediateWorkQueueLocked();
 
-  // Push the task onto the |immediate_incoming_queue| and for auto pumped
-  // queues it calls MaybePostDoWorkOnMainRunner if the Incoming queue was
-  // empty.
-  void PushOntoImmediateIncomingQueueLocked(Task task);
-
   using TaskDeque = LazilyDeallocatedDeque<Task>;
 
   // Extracts all the tasks from the immediate incoming queue and swaps it with
@@ -382,6 +376,7 @@
   void ActivateDelayedFenceIfNeeded(TimeTicks now);
 
   const char* name_;
+  SequenceManagerImpl* const sequence_manager_;
 
   scoped_refptr<AssociatedThreadId> associated_thread_;
 
diff --git a/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc b/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc
index aa60d28..25e41ca 100644
--- a/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc
+++ b/base/task/sequence_manager/test/lazy_thread_controller_for_test.cc
@@ -85,8 +85,9 @@
     pending_observer_ = false;
     return;
   }
-  // TODO(altimin): We can't use |message_loop_->IsBoundToCurrentThread()| here
-  // because |message_loop_| is probably dead by now.
+  // TODO(altimin): Refactor this to use STE::LifetimeObserver.
+  // We can't use message_loop_->IsBoundToCurrentThread as |message_loop_|
+  // might be deleted.
   if (MessageLoopCurrent::Get()->ToMessageLoopDeprecated() != message_loop_)
     return;
   RunLoop::RemoveNestingObserverOnCurrentThread(this);
diff --git a/base/task/sequence_manager/test/sequence_manager_for_test.h b/base/task/sequence_manager/test/sequence_manager_for_test.h
index 61f3be52..836c34b 100644
--- a/base/task/sequence_manager/test/sequence_manager_for_test.h
+++ b/base/task/sequence_manager/test/sequence_manager_for_test.h
@@ -40,6 +40,7 @@
   size_t QueuesToShutdownCount();
 
   using internal::SequenceManagerImpl::GetNextSequenceNumber;
+  using internal::SequenceManagerImpl::ReloadEmptyWorkQueues;
   using internal::SequenceManagerImpl::WakeUpReadyDelayedQueues;
 
  private:
diff --git a/base/task/sequence_manager/thread_controller.h b/base/task/sequence_manager/thread_controller.h
index 5827288..a4f7d1a2 100644
--- a/base/task/sequence_manager/thread_controller.h
+++ b/base/task/sequence_manager/thread_controller.h
@@ -14,6 +14,7 @@
 namespace base {
 
 class MessageLoop;
+class MessagePump;
 class TickClock;
 struct PendingTask;
 
@@ -68,9 +69,20 @@
   // Has no effect on some platforms.
   virtual void SetTimerSlack(TimerSlack timer_slack) = 0;
 
-  // Completes delayed initialization of a ThreadControllers created with a null
-  // MessageLoop. May only be called once.
-  virtual void SetMessageLoop(MessageLoop* message_loop) = 0;
+  // Completes delayed initialization of unbound ThreadControllers.
+  // BindToCurrentThread(MessageLoop*) or BindToCurrentThread(MessagePump*)
+  // may only be called once.
+  virtual void BindToCurrentThread(MessageLoop* message_loop) = 0;
+
+  // Completes delayed initialization of unbound ThreadControllers.
+  // BindToCurrentThread(MessageLoop*) or BindToCurrentThread(MessagePump*)
+  // may only be called once.
+  virtual void BindToCurrentThread(
+      std::unique_ptr<MessagePump> message_pump) = 0;
+
+  virtual void SetTaskExecutionAllowed(bool allowed) = 0;
+
+  virtual bool IsTaskExecutionAllowed() const = 0;
 
   // TODO(altimin): Get rid of the methods below.
   // These methods exist due to current integration of SequenceManager
diff --git a/base/task/sequence_manager/thread_controller_impl.cc b/base/task/sequence_manager/thread_controller_impl.cc
index bf696184..9649eeb6 100644
--- a/base/task/sequence_manager/thread_controller_impl.cc
+++ b/base/task/sequence_manager/thread_controller_impl.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_pump.h"
 #include "base/run_loop.h"
 #include "base/task/sequence_manager/lazy_now.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
@@ -151,7 +152,7 @@
   message_loop_->SetTaskRunner(message_loop_task_runner_);
 }
 
-void ThreadControllerImpl::SetMessageLoop(MessageLoop* message_loop) {
+void ThreadControllerImpl::BindToCurrentThread(MessageLoop* message_loop) {
   DCHECK(!message_loop_);
   DCHECK(message_loop);
 #if DCHECK_IS_ON()
@@ -162,6 +163,11 @@
   message_loop_task_runner_ = message_loop->task_runner();
 }
 
+void ThreadControllerImpl::BindToCurrentThread(
+    std::unique_ptr<MessagePump> message_pump) {
+  NOTREACHED();
+}
+
 void ThreadControllerImpl::WillQueueTask(PendingTask* pending_task) {
   task_annotator_.WillQueueTask("SequenceManager::PostTask", pending_task);
 }
@@ -300,6 +306,14 @@
   main_sequence_only().work_batch_size_ = work_batch_size;
 }
 
+void ThreadControllerImpl::SetTaskExecutionAllowed(bool allowed) {
+  NOTREACHED();
+}
+
+bool ThreadControllerImpl::IsTaskExecutionAllowed() const {
+  return true;
+}
+
 }  // namespace internal
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/thread_controller_impl.h b/base/task/sequence_manager/thread_controller_impl.h
index 88ed620..773a0e5 100644
--- a/base/task/sequence_manager/thread_controller_impl.h
+++ b/base/task/sequence_manager/thread_controller_impl.h
@@ -40,7 +40,8 @@
   void SetWorkBatchSize(int work_batch_size) override;
   void WillQueueTask(PendingTask* pending_task) override;
   void ScheduleWork() override;
-  void SetMessageLoop(MessageLoop* message_loop) override;
+  void BindToCurrentThread(MessageLoop* message_loop) override;
+  void BindToCurrentThread(std::unique_ptr<MessagePump> message_pump) override;
   void SetNextDelayedDoWork(LazyNow* lazy_now, TimeTicks run_time) override;
   void SetSequencedTaskSource(SequencedTaskSource* sequence) override;
   void SetTimerSlack(TimerSlack timer_slack) override;
@@ -51,6 +52,8 @@
   void AddNestingObserver(RunLoop::NestingObserver* observer) override;
   void RemoveNestingObserver(RunLoop::NestingObserver* observer) override;
   const scoped_refptr<AssociatedThreadId>& GetAssociatedThread() const override;
+  void SetTaskExecutionAllowed(bool allowed) override;
+  bool IsTaskExecutionAllowed() const override;
 
   // RunLoop::NestingObserver:
   void OnBeginNestedRunLoop() override;
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index 912ffc7..6769a40 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -5,6 +5,7 @@
 #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
 
 #include "base/auto_reset.h"
+#include "base/message_loop/message_pump.h"
 #include "base/time/tick_clock.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
@@ -27,15 +28,15 @@
 }  // namespace
 
 ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
-    std::unique_ptr<MessagePump> message_pump,
     const TickClock* time_source)
     : associated_thread_(AssociatedThreadId::CreateUnbound()),
-      pump_(std::move(message_pump)),
-      time_source_(time_source) {
-  scoped_set_sequence_local_storage_map_for_current_thread_ = std::make_unique<
-      base::internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
-      &sequence_local_storage_map_);
-  RunLoop::RegisterDelegateForCurrentThread(this);
+      time_source_(time_source) {}
+
+ThreadControllerWithMessagePumpImpl::ThreadControllerWithMessagePumpImpl(
+    std::unique_ptr<MessagePump> message_pump,
+    const TickClock* time_source)
+    : ThreadControllerWithMessagePumpImpl(time_source) {
+  BindToCurrentThread(std::move(message_pump));
 }
 
 ThreadControllerWithMessagePumpImpl::~ThreadControllerWithMessagePumpImpl() {
@@ -45,6 +46,13 @@
   // de-register the current thread as a sequence.
 }
 
+// static
+std::unique_ptr<ThreadControllerWithMessagePumpImpl>
+ThreadControllerWithMessagePumpImpl::CreateUnbound(
+    const TickClock* time_source) {
+  return base::WrapUnique(new ThreadControllerWithMessagePumpImpl(time_source));
+}
+
 ThreadControllerWithMessagePumpImpl::MainThreadOnly::MainThreadOnly() = default;
 
 ThreadControllerWithMessagePumpImpl::MainThreadOnly::~MainThreadOnly() =
@@ -57,12 +65,29 @@
   main_thread_only().task_source = task_source;
 }
 
-void ThreadControllerWithMessagePumpImpl::SetMessageLoop(
+void ThreadControllerWithMessagePumpImpl::BindToCurrentThread(
     MessageLoop* message_loop) {
   NOTREACHED()
       << "ThreadControllerWithMessagePumpImpl doesn't support MessageLoops";
 }
 
+void ThreadControllerWithMessagePumpImpl::BindToCurrentThread(
+    std::unique_ptr<MessagePump> message_pump) {
+  associated_thread_->BindToCurrentThread();
+  AutoLock lock(pump_lock_);
+  pump_ = std::move(message_pump);
+  RunLoop::RegisterDelegateForCurrentThread(this);
+  scoped_set_sequence_local_storage_map_for_current_thread_ = std::make_unique<
+      base::internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
+      &sequence_local_storage_map_);
+  if (task_runner_to_set_) {
+    SetDefaultTaskRunner(task_runner_to_set_);
+    task_runner_to_set_ = nullptr;
+  }
+  if (should_schedule_work_after_bind_)
+    ScheduleWork();
+}
+
 void ThreadControllerWithMessagePumpImpl::SetWorkBatchSize(
     int work_batch_size) {
   DCHECK_GE(work_batch_size, 1);
@@ -71,6 +96,7 @@
 
 void ThreadControllerWithMessagePumpImpl::SetTimerSlack(
     TimerSlack timer_slack) {
+  DCHECK(RunsTasksInCurrentSequence());
   pump_->SetTimerSlack(timer_slack);
 }
 
@@ -80,6 +106,13 @@
 }
 
 void ThreadControllerWithMessagePumpImpl::ScheduleWork() {
+  auto lock = AcquirePumpReadLockIfNeeded();
+
+  if (!pump_) {
+    should_schedule_work_after_bind_ = true;
+    return;
+  }
+
   // This assumes that cross thread ScheduleWork isn't frequent enough to
   // warrant ScheduleWork deduplication.
   if (RunsTasksInCurrentSequence()) {
@@ -96,7 +129,7 @@
 void ThreadControllerWithMessagePumpImpl::SetNextDelayedDoWork(
     LazyNow* lazy_now,
     TimeTicks run_time) {
-  DCHECK_LT(time_source_->NowTicks(), run_time);
+  DCHECK_LT(lazy_now->Now(), run_time);
 
   if (main_thread_only().next_delayed_do_work == run_time)
     return;
@@ -110,6 +143,9 @@
   run_time = CapAtOneDay(run_time, lazy_now);
 
   main_thread_only().next_delayed_do_work = run_time;
+  // |pump_| can't be null as all postTasks are cross-thread before binding,
+  // and delayed cross-thread postTasks do the thread hop through an immediate
+  // task.
   pump_->ScheduleDelayedWork(run_time);
 }
 
@@ -123,8 +159,16 @@
 
 void ThreadControllerWithMessagePumpImpl::SetDefaultTaskRunner(
     scoped_refptr<SingleThreadTaskRunner> task_runner) {
-  main_thread_only().thread_task_runner_handle =
-      std::make_unique<ThreadTaskRunnerHandle>(task_runner);
+  if (associated_thread_->thread_id == kInvalidThreadId) {
+    // Save task runner, it will be set in BindToCurrentThread.
+    task_runner_to_set_ = task_runner;
+  } else {
+    // Only one ThreadTaskRunnerHandle can exist at any time,
+    // so reset the old one.
+    main_thread_only().thread_task_runner_handle.reset();
+    main_thread_only().thread_task_runner_handle =
+        std::make_unique<ThreadTaskRunnerHandle>(task_runner);
+  }
 }
 
 void ThreadControllerWithMessagePumpImpl::RestoreDefaultTaskRunner() {
@@ -153,19 +197,20 @@
 }
 
 bool ThreadControllerWithMessagePumpImpl::DoWork() {
-  base::TimeTicks next_run_time;
   main_thread_only().immediate_do_work_posted = false;
-  return DoWorkImpl(&next_run_time);
+  return DoWorkImpl(nullptr);
 }
 
 bool ThreadControllerWithMessagePumpImpl::DoDelayedWork(
     TimeTicks* next_run_time) {
-  main_thread_only().next_delayed_do_work = TimeTicks::Max();
   return DoWorkImpl(next_run_time);
 }
 
 bool ThreadControllerWithMessagePumpImpl::DoWorkImpl(
     base::TimeTicks* next_run_time) {
+  if (!main_thread_only().task_execution_allowed)
+    return false;
+
   DCHECK(main_thread_only().task_source);
   bool task_ran = false;
 
@@ -176,12 +221,21 @@
     if (!task)
       break;
 
+    // Execute the task and assume the worst: it is probably not reentrant.
+    main_thread_only().task_execution_allowed = false;
+
     TRACE_TASK_EXECUTION("ThreadController::Task", *task);
     task_annotator_.RunTask("ThreadController::Task", &*task);
     task_ran = true;
 
+    main_thread_only().task_execution_allowed = true;
+
     main_thread_only().task_source->DidRunTask();
 
+    // If we have executed a delayed task, reset the next delayed do work.
+    if (next_run_time)
+      main_thread_only().next_delayed_do_work = TimeTicks();
+
     // When Quit() is called we must stop running the batch because the caller
     // expects per-task granularity.
     if (main_thread_only().quit_do_work)
@@ -206,15 +260,19 @@
   if (do_work_delay.is_zero()) {
     // Need to run new work immediately, but due to the contract of DoWork we
     // only need to return true to ensure that happens.
-    *next_run_time = lazy_now.Now();
+    if (next_run_time)
+      *next_run_time = main_thread_only().next_delayed_do_work;
     main_thread_only().immediate_do_work_posted = true;
     return true;
   } else if (do_work_delay != TimeDelta::Max()) {
-    *next_run_time = CapAtOneDay(lazy_now.Now() + do_work_delay, &lazy_now);
     // Cancels any previously scheduled delayed wake-ups.
-    pump_->ScheduleDelayedWork(*next_run_time);
-  } else {
-    *next_run_time = base::TimeTicks::Max();
+    // TODO(altimin): Avoid calling ScheduleDelayedWork from DoDelayedWork.
+    SetNextDelayedDoWork(
+        &lazy_now, CapAtOneDay(lazy_now.Now() + do_work_delay, &lazy_now));
+    if (next_run_time)
+      *next_run_time = main_thread_only().next_delayed_do_work;
+  } else if (next_run_time) {
+    *next_run_time = base::TimeTicks();
   }
 
   return task_ran;
@@ -245,12 +303,16 @@
 }
 
 void ThreadControllerWithMessagePumpImpl::Run(bool application_tasks_allowed) {
-  // No system messages are being processed by this class.
-  DCHECK(application_tasks_allowed);
-
-  // MessagePump::Run() blocks until Quit() called, but previously started
-  // Run() calls continue to block.
-  pump_->Run(this);
+  DCHECK(RunsTasksInCurrentSequence());
+  if (application_tasks_allowed && !main_thread_only().task_execution_allowed) {
+    // Allow nested task execution as explicitly requested.
+    DCHECK(RunLoop::IsNestedOnCurrentThread());
+    main_thread_only().task_execution_allowed = true;
+    pump_->Run(this);
+    main_thread_only().task_execution_allowed = false;
+  } else {
+    pump_->Run(this);
+  }
 }
 
 void ThreadControllerWithMessagePumpImpl::OnBeginNestedRunLoop() {
@@ -267,6 +329,7 @@
 }
 
 void ThreadControllerWithMessagePumpImpl::Quit() {
+  DCHECK(RunsTasksInCurrentSequence());
   // Interrupt a batch of work.
   if (InTopLevelDoWork())
     main_thread_only().quit_do_work = true;
@@ -279,6 +342,22 @@
   ScheduleWork();
 }
 
+void ThreadControllerWithMessagePumpImpl::SetTaskExecutionAllowed(
+    bool allowed) {
+  main_thread_only().task_execution_allowed = allowed;
+}
+
+bool ThreadControllerWithMessagePumpImpl::IsTaskExecutionAllowed() const {
+  return main_thread_only().task_execution_allowed;
+}
+
+Optional<MoveableAutoLock>
+ThreadControllerWithMessagePumpImpl::AcquirePumpReadLockIfNeeded() {
+  if (RunsTasksInCurrentSequence())
+    return nullopt;
+  return MoveableAutoLock(pump_lock_);
+}
+
 }  // namespace internal
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
index 412a409e..11379d3 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
@@ -9,7 +9,9 @@
 
 #include "base/debug/task_annotator.h"
 #include "base/message_loop/message_pump.h"
+#include "base/optional.h"
 #include "base/task/sequence_manager/associated_thread_id.h"
+#include "base/task/sequence_manager/moveable_auto_lock.h"
 #include "base/task/sequence_manager/sequenced_task_source.h"
 #include "base/task/sequence_manager/thread_controller.h"
 #include "base/threading/platform_thread.h"
@@ -33,9 +35,13 @@
                                       const TickClock* time_source);
   ~ThreadControllerWithMessagePumpImpl() override;
 
+  static std::unique_ptr<ThreadControllerWithMessagePumpImpl> CreateUnbound(
+      const TickClock* time_source);
+
   // ThreadController implementation:
   void SetSequencedTaskSource(SequencedTaskSource* task_source) override;
-  void SetMessageLoop(MessageLoop* message_loop) override;
+  void BindToCurrentThread(MessageLoop* message_loop) override;
+  void BindToCurrentThread(std::unique_ptr<MessagePump> message_pump) override;
   void SetWorkBatchSize(int work_batch_size) override;
   void WillQueueTask(PendingTask* pending_task) override;
   void ScheduleWork() override;
@@ -49,12 +55,16 @@
   void AddNestingObserver(RunLoop::NestingObserver* observer) override;
   void RemoveNestingObserver(RunLoop::NestingObserver* observer) override;
   const scoped_refptr<AssociatedThreadId>& GetAssociatedThread() const override;
+  void SetTaskExecutionAllowed(bool allowed) override;
+  bool IsTaskExecutionAllowed() const override;
 
   // RunLoop::NestingObserver:
   void OnBeginNestedRunLoop() override;
   void OnExitNestedRunLoop() override;
 
  protected:
+  explicit ThreadControllerWithMessagePumpImpl(const TickClock* time_source);
+
   // MessagePump::Delegate implementation.
   bool DoWork() override;
   bool DoDelayedWork(TimeTicks* next_run_time) override;
@@ -101,6 +111,8 @@
 
     // When the next scheduled delayed work should run, if any.
     TimeTicks next_delayed_do_work = TimeTicks::Max();
+
+    bool task_execution_allowed = true;
   };
 
   MainThreadOnly& main_thread_only() {
@@ -113,11 +125,26 @@
     return main_thread_only_;
   }
 
+  // Acquires a |pump_lock_| if necessary when we want to read |pump_|.
+  // Non-main thread read access should be guarded by a lock,
+  // while main-thread access can be lock-free. Write access is possible
+  // only from the main thread and needs a lock.
+  Optional<MoveableAutoLock> AcquirePumpReadLockIfNeeded();
+
+  // TODO(altimin): Merge with the one in SequenceManager.
   scoped_refptr<AssociatedThreadId> associated_thread_;
   MainThreadOnly main_thread_only_;
+
+  // Protects |pump_| and |should_schedule_work_after_bind_| as work can be
+  // scheduled from another thread before we create the pump.
+  // TODO(altimin, crbug.com/901345): Remove this lock.
+  base::Lock pump_lock_;
   std::unique_ptr<MessagePump> pump_;
+  bool should_schedule_work_after_bind_ = false;
+
   debug::TaskAnnotator task_annotator_;
   const TickClock* time_source_;  // Not owned.
+  scoped_refptr<SingleThreadTaskRunner> task_runner_to_set_;
 
   // Required to register the current thread as a sequence.
   base::internal::SequenceLocalStorageMap sequence_local_storage_map_;
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
index cfd4b77f..6c9f32f 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
@@ -4,13 +4,22 @@
 
 #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h"
 
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
 #include "base/test/mock_callback.h"
 #include "base/test/simple_test_tick_clock.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#include "base/debug/stack_trace.h"
+
 #include <queue>
 
+using testing::_;
+using testing::Invoke;
+using testing::ElementsAre;
+
 namespace base {
 namespace sequence_manager {
 
@@ -40,6 +49,27 @@
   MOCK_METHOD1(SetTimerSlack, void(TimerSlack));
 };
 
+// TODO(crbug.com/901373): Deduplicate FakeTaskRunners.
+class FakeTaskRunner : public SingleThreadTaskRunner {
+ public:
+  bool PostDelayedTask(const Location& from_here,
+                       OnceClosure task,
+                       TimeDelta delay) override {
+    return true;
+  }
+
+  bool PostNonNestableDelayedTask(const Location& from_here,
+                                  OnceClosure task,
+                                  TimeDelta delay) override {
+    return true;
+  }
+
+  bool RunsTasksInCurrentSequence() const override { return true; }
+
+ protected:
+  ~FakeTaskRunner() override = default;
+};
+
 class FakeSequencedTaskSource : public internal::SequencedTaskSource {
  public:
   explicit FakeSequencedTaskSource(TickClock* clock) : clock_(clock) {}
@@ -57,7 +87,7 @@
 
   void DidRunTask() override {}
 
-  TimeDelta DelayTillNextTask(LazyNow* lazy_now) override {
+  TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override {
     if (tasks_.empty())
       return TimeDelta::Max();
     if (tasks_.front().delayed_run_time.is_null())
@@ -90,112 +120,239 @@
 
 }  // namespace
 
-TEST(ThreadControllerWithMessagePumpTest, ScheduleDelayedWork) {
-  MockMessagePump* message_pump;
-  std::unique_ptr<MockMessagePump> pump =
-      std::make_unique<testing::StrictMock<MockMessagePump>>();
-  message_pump = pump.get();
+class ThreadControllerWithMessagePumpTest : public testing::Test {
+ public:
+  ThreadControllerWithMessagePumpTest()
+      : message_pump_(new testing::StrictMock<MockMessagePump>()),
+        thread_controller_(std::unique_ptr<MessagePump>(message_pump_),
+                           &clock_),
+        task_source_(&clock_) {
+    thread_controller_.SetWorkBatchSize(1);
+    thread_controller_.SetSequencedTaskSource(&task_source_);
+  }
 
-  SimpleTestTickClock clock;
-  ThreadControllerForTest thread_controller(std::move(pump), &clock);
-  thread_controller.SetWorkBatchSize(1);
+ protected:
+  MockMessagePump* message_pump_;
+  SimpleTestTickClock clock_;
+  ThreadControllerForTest thread_controller_;
+  FakeSequencedTaskSource task_source_;
+};
 
-  FakeSequencedTaskSource task_source(&clock);
-  thread_controller.SetSequencedTaskSource(&task_source);
-
+TEST_F(ThreadControllerWithMessagePumpTest, ScheduleDelayedWork) {
   TimeTicks next_run_time;
 
   MockCallback<OnceClosure> task1;
-  task_source.AddTask(PendingTask(FROM_HERE, task1.Get(), Seconds(10)));
+  task_source_.AddTask(PendingTask(FROM_HERE, task1.Get(), Seconds(10)));
   MockCallback<OnceClosure> task2;
-  task_source.AddTask(PendingTask(FROM_HERE, task2.Get(), TimeTicks()));
+  task_source_.AddTask(PendingTask(FROM_HERE, task2.Get(), TimeTicks()));
   MockCallback<OnceClosure> task3;
-  task_source.AddTask(PendingTask(FROM_HERE, task3.Get(), Seconds(20)));
+  task_source_.AddTask(PendingTask(FROM_HERE, task3.Get(), Seconds(20)));
 
   // Call a no-op DoWork. Expect that it doesn't do any work, but
   // schedules a delayed wake-up appropriately.
-  clock.SetNowTicks(Seconds(5));
-  EXPECT_CALL(*message_pump, ScheduleDelayedWork(Seconds(10)));
-  EXPECT_FALSE(thread_controller.DoWork());
-  testing::Mock::VerifyAndClearExpectations(message_pump);
+  clock_.SetNowTicks(Seconds(5));
+  EXPECT_CALL(*message_pump_, ScheduleDelayedWork(Seconds(10)));
+  EXPECT_FALSE(thread_controller_.DoWork());
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
 
   // Call DoDelayedWork after the expiration of the delay.
-  // Expect that a task will run and the next delay will equal to |now|
-  // as we have immediate work to do.
-  clock.SetNowTicks(Seconds(11));
+  // Expect that a task will run and the next delay will equal to
+  // TimeTicks() as we have immediate work to do.
+  clock_.SetNowTicks(Seconds(11));
   EXPECT_CALL(task1, Run()).Times(1);
-  EXPECT_TRUE(thread_controller.DoDelayedWork(&next_run_time));
-  EXPECT_EQ(next_run_time, Seconds(11));
-  testing::Mock::VerifyAndClearExpectations(message_pump);
+  EXPECT_TRUE(thread_controller_.DoDelayedWork(&next_run_time));
+  EXPECT_EQ(next_run_time, TimeTicks());
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
   testing::Mock::VerifyAndClearExpectations(&task1);
 
   // Call DoWork immediately after the previous call. Expect a new task
   // to be run.
   EXPECT_CALL(task2, Run()).Times(1);
-  EXPECT_CALL(*message_pump, ScheduleDelayedWork(Seconds(20)));
-  EXPECT_TRUE(thread_controller.DoWork());
-  testing::Mock::VerifyAndClearExpectations(message_pump);
+  EXPECT_CALL(*message_pump_, ScheduleDelayedWork(Seconds(20)));
+  EXPECT_TRUE(thread_controller_.DoWork());
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
   testing::Mock::VerifyAndClearExpectations(&task2);
 
   // Call DoDelayedWork for the last task and expect to be told
-  // about the lack of further delayed work
-  // (delay being base::TimeDelta::Max()).
-  clock.SetNowTicks(Seconds(21));
+  // about the lack of further delayed work (next run time being TimeTicks()).
+  clock_.SetNowTicks(Seconds(21));
   EXPECT_CALL(task3, Run()).Times(1);
-  EXPECT_TRUE(thread_controller.DoDelayedWork(&next_run_time));
-  EXPECT_EQ(next_run_time, TimeTicks::Max());
-  testing::Mock::VerifyAndClearExpectations(message_pump);
+  EXPECT_TRUE(thread_controller_.DoDelayedWork(&next_run_time));
+  EXPECT_EQ(next_run_time, TimeTicks());
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
   testing::Mock::VerifyAndClearExpectations(&task3);
 }
 
-TEST(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork) {
-  MockMessagePump* message_pump;
-  std::unique_ptr<MockMessagePump> pump =
-      std::make_unique<testing::StrictMock<MockMessagePump>>();
-  message_pump = pump.get();
+TEST_F(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork) {
+  EXPECT_CALL(*message_pump_, ScheduleDelayedWork(Seconds(123)));
 
-  SimpleTestTickClock clock;
-  ThreadControllerForTest thread_controller(std::move(pump), &clock);
-
-  EXPECT_CALL(*message_pump, ScheduleDelayedWork(Seconds(123)));
-
-  LazyNow lazy_now(&clock);
-  thread_controller.SetNextDelayedDoWork(&lazy_now, Seconds(123));
+  LazyNow lazy_now(&clock_);
+  thread_controller_.SetNextDelayedDoWork(&lazy_now, Seconds(123));
 }
 
-TEST(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork_CapAtOneDay) {
-  MockMessagePump* message_pump;
-  std::unique_ptr<MockMessagePump> pump =
-      std::make_unique<testing::StrictMock<MockMessagePump>>();
-  message_pump = pump.get();
+TEST_F(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork_CapAtOneDay) {
+  EXPECT_CALL(*message_pump_, ScheduleDelayedWork(Days(1)));
 
-  SimpleTestTickClock clock;
-  ThreadControllerForTest thread_controller(std::move(pump), &clock);
-
-  EXPECT_CALL(*message_pump, ScheduleDelayedWork(Days(1)));
-
-  LazyNow lazy_now(&clock);
-  thread_controller.SetNextDelayedDoWork(&lazy_now, Days(2));
+  LazyNow lazy_now(&clock_);
+  thread_controller_.SetNextDelayedDoWork(&lazy_now, Days(2));
 }
 
-TEST(ThreadControllerWithMessagePumpTest, DelayedWork_CapAtOneDay) {
-  MockMessagePump* message_pump;
-  std::unique_ptr<MockMessagePump> pump =
-      std::make_unique<testing::StrictMock<MockMessagePump>>();
-  message_pump = pump.get();
-
-  SimpleTestTickClock clock;
-  ThreadControllerForTest thread_controller(std::move(pump), &clock);
-  thread_controller.SetWorkBatchSize(1);
-
-  FakeSequencedTaskSource task_source(&clock);
-  thread_controller.SetSequencedTaskSource(&task_source);
-
+TEST_F(ThreadControllerWithMessagePumpTest, DelayedWork_CapAtOneDay) {
   MockCallback<OnceClosure> task1;
-  task_source.AddTask(PendingTask(FROM_HERE, task1.Get(), Days(10)));
+  task_source_.AddTask(PendingTask(FROM_HERE, task1.Get(), Days(10)));
 
-  EXPECT_CALL(*message_pump, ScheduleDelayedWork(Days(1)));
-  EXPECT_FALSE(thread_controller.DoWork());
+  EXPECT_CALL(*message_pump_, ScheduleDelayedWork(Days(1)));
+  EXPECT_FALSE(thread_controller_.DoWork());
+}
+
+TEST_F(ThreadControllerWithMessagePumpTest, NestedExecution) {
+  // This test posts three immediate tasks. The first creates a nested RunLoop
+  // and the test expects that the second and third tasks are run outside of
+  // the nested loop.
+  std::vector<std::string> log;
+
+  ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>());
+
+  EXPECT_CALL(*message_pump_, Run(_))
+      .WillOnce(Invoke([&log, this](MessagePump::Delegate* delegate) {
+        log.push_back("entering top-level runloop");
+        EXPECT_EQ(delegate, &thread_controller_);
+        EXPECT_TRUE(delegate->DoWork());
+        EXPECT_TRUE(delegate->DoWork());
+        EXPECT_TRUE(delegate->DoWork());
+        EXPECT_FALSE(delegate->DoWork());
+        log.push_back("exiting top-level runloop");
+      }))
+      .WillOnce(Invoke([&log, this](MessagePump::Delegate* delegate) {
+        log.push_back("entering nested runloop");
+        EXPECT_EQ(delegate, &thread_controller_);
+        EXPECT_FALSE(thread_controller_.IsTaskExecutionAllowed());
+        EXPECT_FALSE(delegate->DoWork());
+        log.push_back("exiting nested runloop");
+      }));
+
+  task_source_.AddTask(
+      PendingTask(FROM_HERE,
+                  base::BindOnce(
+                      [](std::vector<std::string>* log,
+                         ThreadControllerForTest* controller) {
+                        EXPECT_FALSE(controller->IsTaskExecutionAllowed());
+                        log->push_back("task1");
+                        RunLoop().Run();
+                      },
+                      &log, &thread_controller_),
+                  TimeTicks()));
+  task_source_.AddTask(
+      PendingTask(FROM_HERE,
+                  base::BindOnce(
+                      [](std::vector<std::string>* log,
+                         ThreadControllerForTest* controller) {
+                        EXPECT_FALSE(controller->IsTaskExecutionAllowed());
+                        log->push_back("task2");
+                      },
+                      &log, &thread_controller_),
+                  TimeTicks()));
+  task_source_.AddTask(
+      PendingTask(FROM_HERE,
+                  base::BindOnce(
+                      [](std::vector<std::string>* log,
+                         ThreadControllerForTest* controller) {
+                        EXPECT_FALSE(controller->IsTaskExecutionAllowed());
+                        log->push_back("task3");
+                      },
+                      &log, &thread_controller_),
+                  TimeTicks()));
+
+  EXPECT_TRUE(thread_controller_.IsTaskExecutionAllowed());
+  RunLoop().Run();
+
+  EXPECT_THAT(log,
+              ElementsAre("entering top-level runloop", "task1",
+                          "entering nested runloop", "exiting nested runloop",
+                          "task2", "task3", "exiting top-level runloop"));
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
+}
+
+TEST_F(ThreadControllerWithMessagePumpTest,
+       NestedExecutionWithApplicationTasks) {
+  // THis test is similar to the previous one, but execution is explicitly
+  // allowed (by specifying appropriate RunLoop type), and tasks are run inside
+  // nested runloop.
+  std::vector<std::string> log;
+
+  ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>());
+
+  EXPECT_CALL(*message_pump_, Run(_))
+      .WillOnce(Invoke([&log, this](MessagePump::Delegate* delegate) {
+        log.push_back("entering top-level runloop");
+        EXPECT_EQ(delegate, &thread_controller_);
+        EXPECT_TRUE(delegate->DoWork());
+        EXPECT_FALSE(delegate->DoWork());
+        log.push_back("exiting top-level runloop");
+      }))
+      .WillOnce(Invoke([&log, this](MessagePump::Delegate* delegate) {
+        log.push_back("entering nested runloop");
+        EXPECT_EQ(delegate, &thread_controller_);
+        EXPECT_TRUE(thread_controller_.IsTaskExecutionAllowed());
+        EXPECT_TRUE(delegate->DoWork());
+        EXPECT_TRUE(delegate->DoWork());
+        EXPECT_FALSE(delegate->DoWork());
+        log.push_back("exiting nested runloop");
+      }));
+
+  task_source_.AddTask(
+      PendingTask(FROM_HERE,
+                  base::BindOnce(
+                      [](std::vector<std::string>* log,
+                         ThreadControllerForTest* controller) {
+                        EXPECT_FALSE(controller->IsTaskExecutionAllowed());
+                        log->push_back("task1");
+                        RunLoop(RunLoop::Type::kNestableTasksAllowed).Run();
+                      },
+                      &log, &thread_controller_),
+                  TimeTicks()));
+  task_source_.AddTask(
+      PendingTask(FROM_HERE,
+                  base::BindOnce(
+                      [](std::vector<std::string>* log,
+                         ThreadControllerForTest* controller) {
+                        EXPECT_FALSE(controller->IsTaskExecutionAllowed());
+                        log->push_back("task2");
+                      },
+                      &log, &thread_controller_),
+                  TimeTicks()));
+  task_source_.AddTask(
+      PendingTask(FROM_HERE,
+                  base::BindOnce(
+                      [](std::vector<std::string>* log,
+                         ThreadControllerForTest* controller) {
+                        EXPECT_FALSE(controller->IsTaskExecutionAllowed());
+                        log->push_back("task3");
+                      },
+                      &log, &thread_controller_),
+                  TimeTicks()));
+
+  EXPECT_TRUE(thread_controller_.IsTaskExecutionAllowed());
+  RunLoop().Run();
+
+  EXPECT_THAT(
+      log, ElementsAre("entering top-level runloop", "task1",
+                       "entering nested runloop", "task2", "task3",
+                       "exiting nested runloop", "exiting top-level runloop"));
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
+}
+
+TEST_F(ThreadControllerWithMessagePumpTest, SetDefaultTaskRunner) {
+  scoped_refptr<SingleThreadTaskRunner> task_runner1 =
+      MakeRefCounted<FakeTaskRunner>();
+  thread_controller_.SetDefaultTaskRunner(task_runner1);
+  EXPECT_EQ(task_runner1, ThreadTaskRunnerHandle::Get());
+
+  // Check that we are correctly supporting overriding.
+  scoped_refptr<SingleThreadTaskRunner> task_runner2 =
+      MakeRefCounted<FakeTaskRunner>();
+  thread_controller_.SetDefaultTaskRunner(task_runner2);
+  EXPECT_EQ(task_runner2, ThreadTaskRunnerHandle::Get());
 }
 
 }  // namespace sequence_manager
diff --git a/base/task/sequence_manager/time_domain_unittest.cc b/base/task/sequence_manager/time_domain_unittest.cc
index 0e343ea1..4b1ec2e 100644
--- a/base/task/sequence_manager/time_domain_unittest.cc
+++ b/base/task/sequence_manager/time_domain_unittest.cc
@@ -186,7 +186,7 @@
 }
 
 TEST_F(TimeDomainTest, UnregisterQueue) {
-  std::unique_ptr<TaskQueueImplForTest> task_queue2_ =
+  std::unique_ptr<TaskQueueImplForTest> task_queue2 =
       std::make_unique<TaskQueueImplForTest>(nullptr, time_domain_.get(),
                                              TaskQueue::Spec("test"));
 
@@ -196,8 +196,7 @@
   EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, wake_up1)).Times(1);
   task_queue_->SetDelayedWakeUpForTesting(internal::DelayedWakeUp{wake_up1, 0});
   TimeTicks wake_up2 = now + TimeDelta::FromMilliseconds(100);
-  task_queue2_->SetDelayedWakeUpForTesting(
-      internal::DelayedWakeUp{wake_up2, 0});
+  task_queue2->SetDelayedWakeUpForTesting(internal::DelayedWakeUp{wake_up2, 0});
 
   EXPECT_EQ(task_queue_.get(), time_domain_->NextScheduledTaskQueue());
 
@@ -206,16 +205,21 @@
   EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, wake_up2)).Times(1);
 
   time_domain_->UnregisterQueue(task_queue_.get());
-  task_queue_ = std::unique_ptr<TaskQueueImplForTest>();
-  EXPECT_EQ(task_queue2_.get(), time_domain_->NextScheduledTaskQueue());
+  EXPECT_EQ(task_queue2.get(), time_domain_->NextScheduledTaskQueue());
+
+  task_queue_->UnregisterTaskQueue();
+  task_queue_ = nullptr;
 
   testing::Mock::VerifyAndClearExpectations(time_domain_.get());
 
   EXPECT_CALL(*time_domain_.get(), SetNextDelayedDoWork(_, TimeTicks::Max()))
       .Times(1);
 
-  time_domain_->UnregisterQueue(task_queue2_.get());
+  time_domain_->UnregisterQueue(task_queue2.get());
   EXPECT_FALSE(time_domain_->NextScheduledTaskQueue());
+
+  task_queue2->UnregisterTaskQueue();
+  task_queue2 = nullptr;
 }
 
 TEST_F(TimeDomainTest, WakeUpReadyDelayedQueues) {
@@ -369,6 +373,10 @@
   time_domain_->SetNextWakeUpForQueue(
       &q1, nullopt, internal::WakeUpResolution::kLow, &lazy_now);
   EXPECT_FALSE(time_domain_->HasPendingHighResolutionTasks());
+
+  // Tidy up.
+  q1.UnregisterTaskQueue();
+  q2.UnregisterTaskQueue();
 }
 
 }  // namespace sequence_manager
diff --git a/base/task/sequence_manager/work_queue.cc b/base/task/sequence_manager/work_queue.cc
index 48102f3c..5dfa1be 100644
--- a/base/task/sequence_manager/work_queue.cc
+++ b/base/task/sequence_manager/work_queue.cc
@@ -239,16 +239,23 @@
   return enqueue_order < other_enqueue_order;
 }
 
+void WorkQueue::MaybeShrinkQueue() {
+  tasks_.MaybeShrinkQueue();
+}
+
+void WorkQueue::DeletePendingTasks() {
+  tasks_.clear();
+
+  if (work_queue_sets_ && heap_handle().IsValid())
+    work_queue_sets_->OnPopQueue(this);
+}
+
 void WorkQueue::PopTaskForTesting() {
   if (tasks_.empty())
     return;
   tasks_.pop_front();
 }
 
-void WorkQueue::MaybeShrinkQueue() {
-  tasks_.MaybeShrinkQueue();
-}
-
 }  // namespace internal
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/work_queue.h b/base/task/sequence_manager/work_queue.h
index d727f82..4f2ddd0 100644
--- a/base/task/sequence_manager/work_queue.h
+++ b/base/task/sequence_manager/work_queue.h
@@ -131,12 +131,15 @@
   // Otherwise returns false.
   bool BlockedByFence() const;
 
-  // Test support function. This should not be used in production code.
-  void PopTaskForTesting();
-
   // Shrinks |tasks_| if it's wasting memory.
   void MaybeShrinkQueue();
 
+  // Delete all tasks within this WorkQueue.
+  void DeletePendingTasks();
+
+  // Test support function. This should not be used in production code.
+  void PopTaskForTesting();
+
  private:
   bool InsertFenceImpl(EnqueueOrder fence);
 
diff --git a/base/task/sequence_manager/work_queue_unittest.cc b/base/task/sequence_manager/work_queue_unittest.cc
index 4ada0fa4..3ed93d2 100644
--- a/base/task/sequence_manager/work_queue_unittest.cc
+++ b/base/task/sequence_manager/work_queue_unittest.cc
@@ -36,7 +36,11 @@
  public:
   void SetUp() override {
     dummy_sequence_manager_ = SequenceManagerImpl::CreateUnbound(nullptr);
+    scoped_refptr<AssociatedThreadId> thread_checker =
+        dummy_sequence_manager_->associated_thread();
+    thread_checker->BindToCurrentThread();
     time_domain_.reset(new RealTimeDomain());
+    dummy_sequence_manager_->RegisterTimeDomain(time_domain_.get());
     task_queue_ = std::make_unique<TaskQueueImpl>(dummy_sequence_manager_.get(),
                                                   time_domain_.get(),
                                                   TaskQueue::Spec("test"));
@@ -49,8 +53,8 @@
 
   void TearDown() override {
     work_queue_sets_->RemoveQueue(work_queue_.get());
-
-    task_queue_->ClearSequenceManagerForTesting();
+    task_queue_->UnregisterTaskQueue();
+    dummy_sequence_manager_->UnregisterTimeDomain(time_domain_.get());
   }
 
  protected:
diff --git a/base/task/task_features.cc b/base/task/task_features.cc
new file mode 100644
index 0000000..8086807
--- /dev/null
+++ b/base/task/task_features.cc
@@ -0,0 +1,14 @@
+// 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 "base/task/task_features.h"
+
+#include "base/feature_list.h"
+
+namespace base {
+
+const base::Feature kMergeBlockingNonBlockingPools = {
+    "MergeBlockingNonBlockingPools", base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace base
\ No newline at end of file
diff --git a/base/task/task_features.h b/base/task/task_features.h
new file mode 100644
index 0000000..628f369
--- /dev/null
+++ b/base/task/task_features.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 BASE_TASK_TASK_FEATURES_H_
+#define BASE_TASK_TASK_FEATURES_H_
+
+#include "base/base_export.h"
+
+namespace base {
+
+struct Feature;
+extern const BASE_EXPORT base::Feature kMergeBlockingNonBlockingPools;
+
+}  // namespace base
+
+#endif  // BASE_TASK_TASK_FEATURES_H_
diff --git a/base/task/task_scheduler/task_scheduler_impl.cc b/base/task/task_scheduler/task_scheduler_impl.cc
index 8fc31df..0ca7250 100644
--- a/base/task/task_scheduler/task_scheduler_impl.cc
+++ b/base/task/task_scheduler/task_scheduler_impl.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
+#include "base/task/task_features.h"
 #include "base/task/task_scheduler/scheduler_parallel_task_runner.h"
 #include "base/task/task_scheduler/scheduler_sequenced_task_runner.h"
 #include "base/task/task_scheduler/scheduler_worker_pool_params.h"
@@ -46,9 +47,6 @@
 
 }  // namespace
 
-const base::Feature kMergeBlockingNonBlockingPools = {
-    "MergeBlockingNonBlockingPools", base::FEATURE_DISABLED_BY_DEFAULT};
-
 TaskSchedulerImpl::TaskSchedulerImpl(StringPiece histogram_label)
     : TaskSchedulerImpl(histogram_label,
                         std::make_unique<TaskTrackerImpl>(histogram_label)) {}
diff --git a/base/task/task_scheduler/task_scheduler_impl.h b/base/task/task_scheduler/task_scheduler_impl.h
index 5281114..b929ea404e 100644
--- a/base/task/task_scheduler/task_scheduler_impl.h
+++ b/base/task/task_scheduler/task_scheduler_impl.h
@@ -39,12 +39,9 @@
 
 class HistogramBase;
 class Thread;
-struct Feature;
 
 namespace internal {
 
-extern const BASE_EXPORT base::Feature kMergeBlockingNonBlockingPools;
-
 // Default TaskScheduler implementation. This class is thread-safe.
 class BASE_EXPORT TaskSchedulerImpl : public TaskScheduler,
                                       public SchedulerWorkerPool::Delegate,
diff --git a/base/task/task_scheduler/task_scheduler_impl_unittest.cc b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
index 2acdf27b..0b616c2 100644
--- a/base/task/task_scheduler/task_scheduler_impl_unittest.cc
+++ b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/task/task_features.h"
 #include "base/task/task_scheduler/environment_config.h"
 #include "base/task/task_scheduler/scheduler_worker_observer.h"
 #include "base/task/task_scheduler/scheduler_worker_pool_params.h"
diff --git a/build/android/gyp/bundletool.py b/build/android/gyp/bundletool.py
index cd803c1..9256f99 100755
--- a/build/android/gyp/bundletool.py
+++ b/build/android/gyp/bundletool.py
@@ -17,7 +17,7 @@
     __file__, '..', '..', '..', '..', 'third_party', 'android_build_tools',
     'bundletool'))
 
-BUNDLETOOL_VERSION = '0.6.0'
+BUNDLETOOL_VERSION = '0.6.2'
 
 BUNDLETOOL_JAR_PATH = os.path.join(
     BUNDLETOOL_DIR, 'bundletool-all-%s.jar' % BUNDLETOOL_VERSION)
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 6186f451..5e7636ae 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1418,7 +1418,7 @@
     _enable_bytecode_rewriter =
         _enable_assert || _enable_custom_resources || _enable_thread_annotations
     _is_prebuilt = defined(invoker.is_prebuilt) && invoker.is_prebuilt
-    _enable_bytecode_checks = defined(invoker.enable_bytecode_checks) &&
+    _enable_bytecode_checks = !defined(invoker.enable_bytecode_checks) ||
                               invoker.enable_bytecode_checks
 
     # Release builds don't have asserts enabled, so they often will not run the
diff --git a/build/config/jumbo.gni b/build/config/jumbo.gni
index 77c131a..d6c6c22 100644
--- a/build/config/jumbo.gni
+++ b/build/config/jumbo.gni
@@ -86,7 +86,7 @@
 
   gen_target_dir = invoker.target_gen_dir
 
-  assert(gen_target_dir != "")  # Prevent "unused variable".
+  not_needed([ "gen_target_dir" ])  # Prevent "unused variable".
 
   if (use_jumbo_build_for_target) {
     jumbo_files = []
@@ -173,16 +173,14 @@
     # If the list subtraction triggers a gn error,
     # jumbo_excluded_sources lists a file that is not in sources.
     sources_after_exclusion = invoker_sources - excluded_sources
-    assert(sources_after_exclusion != [] || true)  # Prevent "unused variable".
+    not_needed([ "sources_after_exclusion" ])
   }
 
   target_type = invoker.target_type
   if (use_jumbo_build_for_target && target_type == "split_static_library") {
     # Meaningless and also impossible if split_count > len(jumbo_files)
     target_type = "static_library"
-
-    # Prevent "unused variable" warning.
-    assert(!defined(invoker.split_count) || invoker.split_count > 0)
+    not_needed(invoker, [ "split_count" ])
   }
 
   # Perform the actual operation, either on the original sources or
diff --git a/build/write_build_date_header.py b/build/write_build_date_header.py
index e856eaf..7738828 100755
--- a/build/write_build_date_header.py
+++ b/build/write_build_date_header.py
@@ -13,10 +13,10 @@
 def main():
   argument_parser = argparse.ArgumentParser()
   argument_parser.add_argument('output_file', help='The file to write to')
-  argument_parser.add_argument('timestamp', type=int)
+  argument_parser.add_argument('timestamp')
   args = argument_parser.parse_args()
 
-  date = datetime.datetime.utcfromtimestamp(args.timestamp)
+  date = datetime.datetime.utcfromtimestamp(int(args.timestamp))
   output = ('// Generated by //build/write_build_date_header.py\n'
            '#ifndef BUILD_DATE\n'
            '#define BUILD_DATE "{:%b %d %Y %H:%M:%S}"\n'
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc
index 56811dbed..96b02540 100644
--- a/cc/paint/oop_pixeltest.cc
+++ b/cc/paint/oop_pixeltest.cc
@@ -1397,21 +1397,20 @@
     typeface = SkTypeface::MakeFromName("monospace", SkFontStyle());
   }
 
-  SkPaint paint;
-  paint.setTypeface(typeface);
-  paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-  paint.setHinting(SkPaint::kNormal_Hinting);
-  paint.setTextSize(1u);
+  SkFont font;
+  font.setTypeface(typeface);
+  font.setHinting(SkFont::kNormal_Hinting);
+  font.setSize(1u);
   if (use_lcd_text) {
-    paint.setAntiAlias(true);
-    paint.setSubpixelText(true);
-    paint.setLCDRenderText(true);
+    font.DEPRECATED_setAntiAlias(true);
+    font.setSubpixel(true);
+    font.DEPRECATED_setLCDRender(true);
   }
 
   SkTextBlobBuilder builder;
   SkRect bounds = SkRect::MakeWH(100, 100);
   const int glyphCount = 10;
-  const auto& runBuffer = builder.allocRunPosH(paint, glyphCount, 0, &bounds);
+  const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
   for (int i = 0; i < glyphCount; i++) {
     runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
     runBuffer.pos[i] = SkIntToScalar(i);
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 1e53114..02032f2 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -1162,8 +1162,7 @@
 
 std::vector<sk_sp<SkTextBlob>> test_paint_blobs = {
     [] {
-      SkPaint font;
-      font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+      SkFont font;
       font.setTypeface(test_typefaces[0][0]);
 
       SkTextBlobBuilder builder;
@@ -1175,8 +1174,7 @@
       return builder.make();
     }(),
     [] {
-      SkPaint font;
-      font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+      SkFont font;
       font.setTypeface(test_typefaces[1][0]);
 
       SkTextBlobBuilder builder;
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
index 8755e8ae..f69d7e5 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedProcessScopeFactory.java
@@ -16,6 +16,7 @@
 import com.google.android.libraries.feed.hostimpl.logging.LoggingApiImpl;
 
 import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefChangeRegistrar;
@@ -26,6 +27,8 @@
 
 /** Holds singleton {@link FeedProcessScope} and some of the scope's host implementations. */
 public class FeedProcessScopeFactory {
+    private static final String TAG = "FeedProcessScopeFtry";
+
     /** Flag that tracks whether we've ever been disabled via enterprise policy. Should only be
      * accessed through isFeedProcessScopeEnabled(). */
     private static boolean sEverDisabledForPolicy = false;
@@ -194,6 +197,8 @@
         // Should only be subscribed while it was enabled. A change should mean articles are now
         // disabled.
         assert !PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_SECTION_ENABLED);
+        // Log this event warning while investigating https://crbug.com/901414.
+        Log.w(TAG, "Disabling Feed because of policy.");
         sEverDisabledForPolicy = true;
         destroy();
     }
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml
index 0b91a9ab..23da396 100644
--- a/chrome/android/java/AndroidManifest.xml
+++ b/chrome/android/java/AndroidManifest.xml
@@ -1165,6 +1165,8 @@
              controllers with its own list. -->
         <meta-data android:name="org.chromium.content.browser.REMOTE_MEDIA_PLAYERS"
             android:value="org.chromium.chrome.browser.media.remote.DefaultMediaRouteController"/>
+        <meta-data android:name="org.chromium.content.browser.REMOTE_PLAYBACK_APPS"
+            android:value="org.chromium.chrome.browser.media.router.cast.remoting.DefaultRemotingApp"/>
 
         {% endblock %}
     </application>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index a1dffff..3ed8ed1b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -465,7 +465,9 @@
                         getTabModelSelector(), getControlContainerHeightResource());
             }
 
-            ((BottomContainer) findViewById(R.id.bottom_container)).initialize(mFullscreenManager);
+            ((BottomContainer) findViewById(R.id.bottom_container))
+                    .initialize(mFullscreenManager,
+                            mManualFillingController.getKeyboardExtensionSizeManager());
 
             mModalDialogManager = createModalDialogManager();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
index 3b84999..ca285ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
@@ -325,6 +325,7 @@
         mKeyboardAccessory.closeActiveTab();
         mKeyboardAccessory.setBottomOffset(0);
         mAccessorySheet.hide();
+        mActivity.getCompositorViewHolder().requestLayout(); // Request checks for keyboard changes.
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java
index fde69c0..242dc208 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java
@@ -7,9 +7,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ArgbEvaluator;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
-import android.util.Property;
 import android.view.View;
 
 import org.chromium.chrome.browser.compositor.layouts.ChromeAnimation;
@@ -23,37 +21,6 @@
  * pulsing.
  */
 public class AnimatedProgressBar {
-    // TODO(806868): Move these properties into MaterialProgressBar.java.
-    private static final Property<MaterialProgressBar, Integer> PROGRESS_PROPERTY =
-            new Property<MaterialProgressBar, Integer>(Integer.class, "progress") {
-                @Override
-                public Integer get(MaterialProgressBar progressBar) {
-                    // TODO(806868): Implement get once this property is moved into
-                    // MaterialProgressBar.java.
-                    throw new UnsupportedOperationException();
-                }
-
-                @Override
-                public void set(MaterialProgressBar progressBar, Integer progress) {
-                    progressBar.setProgress(progress);
-                }
-            };
-
-    private static final Property<MaterialProgressBar, Integer> COLOR_PROPERTY =
-            new Property<MaterialProgressBar, Integer>(Integer.class, "progressColor") {
-                @Override
-                public Integer get(MaterialProgressBar progressBar) {
-                    // TODO(806868): Implement get once this property is moved into
-                    // MaterialProgressBar.java.
-                    throw new UnsupportedOperationException();
-                }
-
-                @Override
-                public void set(MaterialProgressBar progressBar, Integer progressColor) {
-                    progressBar.setProgressColor(progressColor);
-                }
-            };
-
     // The number of ms the progress bar would take to go from 0 to 100%.
     private static final int PROGRESS_BAR_SPEED_MS = 3_000;
     private static final int PROGRESS_BAR_PULSING_DURATION_MS = 1_000;
@@ -64,8 +31,8 @@
 
     private boolean mIsRunningProgressAnimation = false;
     private int mLastProgress = 0;
-    private Queue<ObjectAnimator> mPendingIncreaseAnimations = new ArrayDeque<>();
-    private ObjectAnimator mPulseAnimation = null;
+    private Queue<ValueAnimator> mPendingIncreaseAnimations = new ArrayDeque<>();
+    private ValueAnimator mPulseAnimation = null;
 
     public AnimatedProgressBar(MaterialProgressBar progressBar, int normalColor, int pulsedColor) {
         mProgressBar = progressBar;
@@ -87,8 +54,7 @@
      */
     public void maybeIncreaseProgress(int progress) {
         if (progress > mLastProgress) {
-            ObjectAnimator progressAnimation =
-                    ObjectAnimator.ofInt(mProgressBar, PROGRESS_PROPERTY, mLastProgress, progress);
+            ValueAnimator progressAnimation = ValueAnimator.ofInt(mLastProgress, progress);
             progressAnimation.setDuration(PROGRESS_BAR_SPEED_MS * (progress - mLastProgress) / 100);
             progressAnimation.setInterpolator(ChromeAnimation.getAccelerateInterpolator());
             progressAnimation.addListener(new AnimatorListenerAdapter() {
@@ -102,6 +68,8 @@
                     }
                 }
             });
+            progressAnimation.addUpdateListener(
+                    animation -> mProgressBar.setProgress((int) animation.getAnimatedValue()));
             mLastProgress = progress;
 
             if (mIsRunningProgressAnimation) {
@@ -115,8 +83,7 @@
 
     public void enablePulsing() {
         if (mPulseAnimation == null) {
-            mPulseAnimation =
-                    ObjectAnimator.ofInt(mProgressBar, COLOR_PROPERTY, mNormalColor, mPulsedColor);
+            mPulseAnimation = ValueAnimator.ofInt(mNormalColor, mPulsedColor);
             mPulseAnimation.setDuration(PROGRESS_BAR_PULSING_DURATION_MS);
             mPulseAnimation.setEvaluator(new ArgbEvaluator());
             mPulseAnimation.setRepeatCount(ValueAnimator.INFINITE);
@@ -128,6 +95,8 @@
                     mProgressBar.setProgressColor(mNormalColor);
                 }
             });
+            mPulseAnimation.addUpdateListener(
+                    animation -> mProgressBar.setProgressColor((int) animation.getAnimatedValue()));
             mPulseAnimation.start();
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java
index d5a8608..be200da0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java
@@ -175,11 +175,12 @@
         if (mState == State.API_CONNECTION_SUSPENDED) return;
 
         try {
-            launchApplication(mApiClient, mSource.getApplicationId(), true)
-                    .setResultCallback(this);
+            launchApplication(mApiClient, mSource.getApplicationId(), true).setResultCallback(this);
             mState = State.LAUNCHING_APPLICATION;
         } catch (Exception e) {
-            Log.e(TAG, "Launch application failed: %s", mSource.getApplicationId(), e);
+            // Do not log appId, as it can contain appIds overriden in downstream code that must not
+            // be leaked
+            Log.e(TAG, "Launch application failed", e);
             reportError();
         }
     }
@@ -209,8 +210,10 @@
 
         Status status = result.getStatus();
         if (!status.isSuccess()) {
-            Log.e(TAG, "Launch application failed with status: %s, %d, %s",
-                    mSource.getApplicationId(), status.getStatusCode(), status.getStatusMessage());
+            // Do not log appId, as it can contain appIds overriden in downstream code that must not
+            // be leaked
+            Log.e(TAG, "Launch application failed with status: %d, %s", status.getStatusCode(),
+                    status.getStatusMessage());
             reportError();
             return;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/DefaultRemotingApp.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/DefaultRemotingApp.java
new file mode 100644
index 0000000..f93e606
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/DefaultRemotingApp.java
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.media.router.cast.remoting;
+
+import com.google.android.gms.cast.CastMediaControlIntent;
+import com.google.android.gms.common.api.GoogleApiClient;
+
+import org.chromium.base.annotations.UsedByReflection;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.media.router.MediaSource;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * App corresponding to the default media receiver application.
+ */
+@UsedByReflection("RemotingAppLauncher.java")
+public class DefaultRemotingApp implements RemotingAppLauncher.RemotingApp {
+    private static final String TAG = "DefaultRmtApp";
+
+    public DefaultRemotingApp() {}
+
+    // RemotingApp implementation
+    @Override
+    public boolean canPlayMedia(RemotingMediaSource source) {
+        try {
+            String scheme = new URI(source.getMediaUrl()).getScheme();
+            if (scheme == null) return false;
+
+            return scheme.equals(UrlConstants.HTTP_SCHEME)
+                    || scheme.equals(UrlConstants.HTTPS_SCHEME);
+        } catch (URISyntaxException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public String getApplicationId() {
+        // Can be overriden downstream.
+        return CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
+    }
+
+    @Override
+    public boolean hasCustomLoad() {
+        // Can be overriden downstream.
+        return false;
+    }
+
+    @Override
+    public void load(GoogleApiClient client, long startTime, MediaSource source) {
+        // Can be overriden downstream.
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingAppLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingAppLauncher.java
new file mode 100644
index 0000000..9f70acb
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingAppLauncher.java
@@ -0,0 +1,129 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.media.router.cast.remoting;
+
+import android.app.Activity;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.Log;
+import org.chromium.chrome.browser.media.router.MediaSource;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class that manages the instantiation of RemotingApps.
+ *
+ * A RemotingApp is essentially a wrapper around an application ID. We use them to protect certain
+ * application IDs in downstream code, and to override some app specific logic. The IDs correspond
+ * to Cast receiver apps.
+ *
+ * The types of RemotingApps are discovered by reflection. We find the class names via the
+ * REMOTE_PLAYBACK_APPS_KEY in AndroidManifest.xml. This allows us to specify different apps in the
+ * default chromium manifest, versus the official Chrome on Android manifest.
+ */
+public class RemotingAppLauncher {
+    private static final String TAG = "RemoteAppLnchr";
+
+    // This is a key for meta-data in the package manifest.
+    private static final String REMOTE_PLAYBACK_APPS_KEY =
+            "org.chromium.content.browser.REMOTE_PLAYBACK_APPS";
+
+    private static RemotingAppLauncher sInstance;
+
+    /**
+     * Interface that represents a Cast receiver app, that is compatible with remote playback.
+     */
+    public interface RemotingApp {
+        /**
+         * Returns true if the given app supports the source
+         */
+        public boolean canPlayMedia(RemotingMediaSource source);
+
+        /**
+         * Returns the application ID of the receiver app.
+         * NOTE: This string should not be added to logs, since this can be overriden
+         * by downstream and we would leak internal app IDs.
+         */
+        public String getApplicationId();
+
+        /**
+         * Returns true if RemoteMediaPlayer.load() should be avoided, and RemotingApp.load() should
+         * be called instead.
+         */
+        public boolean hasCustomLoad();
+
+        /**
+         * Loads the given source, at the given start time, for the already connected client.
+         */
+        public void load(GoogleApiClient client, long startTime, MediaSource source);
+    }
+
+    private List<RemotingApp> mRemotingApps = new ArrayList<RemotingApp>();
+
+    /**
+     * The private constructor to make sure the object is only created by the instance() method.
+     */
+    private RemotingAppLauncher() {}
+
+    private void createRemotingApps() {
+        // We only need to do this once
+        if (!mRemotingApps.isEmpty()) return;
+        try {
+            Activity currentActivity = ApplicationStatus.getLastTrackedFocusedActivity();
+            ApplicationInfo ai = currentActivity.getPackageManager().getApplicationInfo(
+                    currentActivity.getPackageName(), PackageManager.GET_META_DATA);
+            Bundle bundle = ai.metaData;
+            String classNameString = bundle.getString(REMOTE_PLAYBACK_APPS_KEY);
+
+            if (classNameString != null) {
+                String[] classNames = classNameString.split(",");
+                for (String className : classNames) {
+                    Log.d(TAG, "Adding remoting app %s", className.trim());
+                    Class<?> remotingAppClass = Class.forName(className.trim());
+                    Object remotingApp = remotingAppClass.newInstance();
+                    mRemotingApps.add((RemotingApp) remotingApp);
+                }
+            }
+        } catch (NameNotFoundException | ClassNotFoundException | SecurityException
+                | InstantiationException | IllegalAccessException | IllegalArgumentException e) {
+            // Should never happen, implies corrupt AndroidManifest
+            Log.e(TAG, "Couldn't instatiate RemotingApps", e);
+            assert false;
+        }
+    }
+
+    /**
+     * Returns the first RemotingApp that can support the given source, or null if none are found.
+     */
+    public RemotingApp getRemotingApp(MediaSource source) {
+        if (!(source instanceof RemotingMediaSource)) return null;
+
+        for (RemotingApp app : mRemotingApps) {
+            if (app.canPlayMedia((RemotingMediaSource) source)) {
+                return app;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The singleton instance access method.
+     */
+    public static RemotingAppLauncher instance() {
+        if (sInstance == null) {
+            sInstance = new RemotingAppLauncher();
+            sInstance.createRemotingApps();
+        }
+
+        return sInstance;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java
index fead258..cc4b0fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java
@@ -33,7 +33,7 @@
     /**
      * The Cast application id.
      */
-    private final String mApplicationId;
+    private String mApplicationId = null;
 
     /**
      * The URL to fling to the Cast device.
@@ -61,9 +61,7 @@
             return null;
         }
 
-        // TODO(avayvod): check the content URL and override the app id if needed.
-        String applicationId = CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
-        return new RemotingMediaSource(sourceId, applicationId, mediaUrl);
+        return new RemotingMediaSource(sourceId, mediaUrl);
     }
 
     /**
@@ -75,7 +73,7 @@
     @Override
     public MediaRouteSelector buildRouteSelector() {
         return new MediaRouteSelector.Builder()
-                .addControlCategory(CastMediaControlIntent.categoryForCast(mApplicationId))
+                .addControlCategory(CastMediaControlIntent.categoryForCast(getApplicationId()))
                 .build();
     }
 
@@ -84,6 +82,12 @@
      */
     @Override
     public String getApplicationId() {
+        if (mApplicationId == null) {
+            RemotingAppLauncher.RemotingApp app =
+                    RemotingAppLauncher.instance().getRemotingApp(this);
+
+            mApplicationId = (app != null) ? app.getApplicationId() : "";
+        }
         return mApplicationId;
     }
 
@@ -102,9 +106,8 @@
         return mMediaUrl;
     }
 
-    private RemotingMediaSource(String sourceId, String applicationId, String mediaUrl) {
+    private RemotingMediaSource(String sourceId, String mediaUrl) {
         mSourceId = sourceId;
-        mApplicationId = applicationId;
         mMediaUrl = mediaUrl;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelDefinitions.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelDefinitions.java
index 9bd7d5f..429d104 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelDefinitions.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelDefinitions.java
@@ -177,6 +177,13 @@
     }
 
     /**
+     * @return A set of all known channel ids that can be used for {@link #getChannelFromId}.
+     */
+    static Set<String> getAllChannelIds() {
+        return PredefinedChannels.MAP.keySet();
+    }
+
+    /**
      * @return A set of channel ids of channels that should be initialized on startup.
      */
     static Set<String> getStartupChannelIds() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializer.java
index ff617ec2..bc4105a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializer.java
@@ -53,6 +53,8 @@
         for (NotificationChannel channel : mNotificationManager.getNotificationChannels()) {
             channelIds.add(channel.getId());
         }
+        // only re-initialize known channel ids, as we only want to update known & existing channels
+        channelIds.retainAll(ChannelDefinitions.getAllChannelIds());
         ensureInitialized(channelIds);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/BottomContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/BottomContainer.java
index ea74125c..625a7b4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/BottomContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/BottomContainer.java
@@ -8,6 +8,7 @@
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 
+import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardExtensionSizeManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
 import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
@@ -17,10 +18,14 @@
  * The container that holds both infobars and snackbars. It will be translated up and down when the
  * bottom controls' offset changes.
  */
-public class BottomContainer extends FrameLayout implements FullscreenListener {
+public class BottomContainer
+        extends FrameLayout implements FullscreenListener, KeyboardExtensionSizeManager.Observer {
     /** The {@link ChromeFullscreenManager} to listen for controls offset changes. */
     private ChromeFullscreenManager mFullscreenManager;
 
+    /** A {@link KeyboardExtensionSizeManager} to listen to for keyboard extension size changes. */
+    private KeyboardExtensionSizeManager mKeyboardExtensionSizeManager;
+
     /** The desired Y offset if unaffected by other UI. */
     private float mBaseYOffset;
 
@@ -37,9 +42,12 @@
     /**
      * Initializes this container.
      */
-    public void initialize(ChromeFullscreenManager fullscreenManager) {
+    public void initialize(ChromeFullscreenManager fullscreenManager,
+            KeyboardExtensionSizeManager keyboardExtensionSizeManager) {
         mFullscreenManager = fullscreenManager;
         mFullscreenManager.addListener(this);
+        mKeyboardExtensionSizeManager = keyboardExtensionSizeManager;
+        mKeyboardExtensionSizeManager.addObserver(this);
         setTranslationY(mBaseYOffset);
     }
 
@@ -62,6 +70,12 @@
         });
     }
 
+    // KeyboardExtensionSizeManager methods
+    @Override
+    public void onKeyboardExtensionHeightChanged(int keyboardHeight) {
+        setTranslationY(mBaseYOffset);
+    }
+
     // FullscreenListener methods
     @Override
     public void onControlsOffsetChanged(float topOffset, float bottomOffset, boolean needsAnimate) {
@@ -74,6 +88,7 @@
 
         float offsetFromControls = mFullscreenManager.getBottomControlOffset()
                 - mFullscreenManager.getBottomControlsHeight();
+        offsetFromControls -= mKeyboardExtensionSizeManager.getKeyboardExtensionHeight();
 
         // Sit on top of either the bottom sheet or the bottom toolbar depending on which is larger
         // (offsets are negative).
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
index 3a2a702f..4082552 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java
@@ -1985,7 +1985,6 @@
      * background on low-memory devices).
      * @return true iff the Tab handled the request.
      */
-    @CalledByNative
     public boolean loadIfNeeded() {
         if (getActivity() == null) {
             Log.e(TAG, "Tab couldn't be loaded because Context was null.");
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 485d9a4..70b924c 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -867,6 +867,8 @@
   "java/src/org/chromium/chrome/browser/media/router/cast/CastSessionInfo.java",
   "java/src/org/chromium/chrome/browser/media/router/cast/ChromeCastSessionManager.java",
   "java/src/org/chromium/chrome/browser/media/router/cast/CreateRouteRequest.java",
+  "java/src/org/chromium/chrome/browser/media/router/cast/remoting/DefaultRemotingApp.java",
+  "java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingAppLauncher.java",
   "java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingCastSession.java",
   "java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaRouteProvider.java",
   "java/src/org/chromium/chrome/browser/media/router/cast/remoting/RemotingMediaSource.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java
index 71aa8316..addd2c9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java
@@ -163,11 +163,6 @@
         }
 
         @Override
-        public String getOriginalUrlForVisibleNavigationEntry() {
-            return null;
-        }
-
-        @Override
         public void clearSslPreferences() {
         }
 
@@ -211,23 +206,6 @@
         }
 
         @Override
-        public boolean canCopyStateOver() {
-            return false;
-        }
-
-        @Override
-        public boolean canPruneAllButLastCommitted() {
-            return false;
-        }
-
-        @Override
-        public void copyStateFrom(NavigationController source, boolean needsReload) {}
-
-        @Override
-        public void copyStateFromAndPrune(NavigationController source, boolean replaceEntry) {
-        }
-
-        @Override
         public String getEntryExtraData(int index, String key) {
             return null;
         }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
index d3b6678b..178f356 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingIntegrationTest.java
@@ -8,6 +8,7 @@
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
@@ -40,6 +41,8 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
 import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder;
+import org.chromium.chrome.browser.snackbar.Snackbar;
+import org.chromium.chrome.browser.snackbar.SnackbarManager;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.InfoBarTestAnimationListener;
@@ -428,4 +431,40 @@
 
         whenDisplayed(withText(kInfoBarText));
     }
+
+    @Test
+    @SmallTest
+    public void testMovesUpSnackbar() throws InterruptedException, TimeoutException {
+        final String kSnackbarText = "snackbar";
+
+        mHelper.loadTestPage(false);
+        mHelper.createTestTab();
+
+        // Create a simple, persistent snackbar and verify it's displayed.
+        SnackbarManager manager = mActivityTestRule.getActivity().getSnackbarManager();
+        ThreadUtils.runOnUiThread(
+                ()
+                        -> manager.showSnackbar(Snackbar.make(kSnackbarText,
+                                new SnackbarManager.SnackbarController() {},
+                                Snackbar.TYPE_PERSISTENT, Snackbar.UMA_TEST_SNACKBAR)));
+        CriteriaHelper.pollUiThread(manager::isShowing);
+        CriteriaHelper.pollUiThread(
+                mActivityTestRule.getActivity().getWindowAndroid()::haveAnimationsEnded);
+
+        // Click in a field to open keyboard and accessory -- this shouldn't hide the snackbar.
+        mHelper.clickPasswordField();
+        mHelper.waitForKeyboard();
+        whenDisplayed(withId(R.id.keyboard_accessory));
+        onView(withText(kSnackbarText)).check(matches(isCompletelyDisplayed()));
+
+        // Open a keyboard accessory sheet -- this also shouldn't hide the snackbar.
+        whenDisplayed(withId(R.id.tabs)).perform(selectTabAtPosition(0));
+        whenDisplayed(withId(R.id.keyboard_accessory_sheet));
+        onView(withText(kSnackbarText)).check(matches(isCompletelyDisplayed()));
+
+        // Click into the email field to dismiss the keyboard accessory.
+        mHelper.clickEmailField(false);
+        mHelper.waitToBeHidden(withId(R.id.keyboard_accessory));
+        onView(withText(kSnackbarText)).check(matches(isCompletelyDisplayed()));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java
index 502fe79..1122423 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java
@@ -139,6 +139,24 @@
     @MinAndroidSdkLevel(Build.VERSION_CODES.O)
     @TargetApi(Build.VERSION_CODES.O)
     @Feature({"Browser", "Notifications"})
+    public void testUpdateLocale_otherChannelsDoNotThrowException() throws Exception {
+        NotificationChannelGroup group =
+                ChannelDefinitions.getChannelGroup(ChannelDefinitions.ChannelGroupId.GENERAL)
+                        .toNotificationChannelGroup(mContext.getResources());
+        NotificationChannel channel =
+                new NotificationChannel("ACCOUNT", "Account", NotificationManager.IMPORTANCE_LOW);
+        channel.setGroup(ChannelDefinitions.ChannelGroupId.GENERAL);
+        mNotificationManagerProxy.createNotificationChannelGroup(group);
+        mNotificationManagerProxy.createNotificationChannel(channel);
+        mContext = InstrumentationRegistry.getTargetContext();
+        mChannelsInitializer.updateLocale(mContext.getResources());
+    }
+
+    @Test
+    @SmallTest
+    @MinAndroidSdkLevel(Build.VERSION_CODES.O)
+    @TargetApi(Build.VERSION_CODES.O)
+    @Feature({"Browser", "Notifications"})
     public void testEnsureInitialized_browserChannel() throws Exception {
         mChannelsInitializer.ensureInitialized(ChannelDefinitions.ChannelId.BROWSER);
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/contextual_suggestions/GoogleSearchRestrictionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/contextual_suggestions/GoogleSearchRestrictionTest.java
index f7f10b3..6db276c3e 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/contextual_suggestions/GoogleSearchRestrictionTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/contextual_suggestions/GoogleSearchRestrictionTest.java
@@ -347,11 +347,6 @@
         }
 
         @Override
-        public String getOriginalUrlForVisibleNavigationEntry() {
-            return null;
-        }
-
-        @Override
         public void clearSslPreferences() {}
 
         @Override
@@ -373,22 +368,6 @@
         }
 
         @Override
-        public boolean canCopyStateOver() {
-            return false;
-        }
-
-        @Override
-        public boolean canPruneAllButLastCommitted() {
-            return false;
-        }
-
-        @Override
-        public void copyStateFrom(NavigationController source, boolean needsReload) {}
-
-        @Override
-        public void copyStateFromAndPrune(NavigationController source, boolean replaceEntry) {}
-
-        @Override
         public String getEntryExtraData(int index, String key) {
             return null;
         }
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index cd5c881..bcc5328 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -176,6 +176,7 @@
 #define IDC_SHOW_BETA_FORUM             40252
 #define IDC_TOGGLE_JAVASCRIPT_APPLE_EVENTS 40253
 #define IDC_INSTALL_PWA                 40254
+#define IDC_MANAGED_UI_HELP             40255
 
 // Spell-check
 // Insert any additional suggestions before _LAST; these have to be consecutive.
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 9fa21c2..7218b29 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -3499,12 +3499,6 @@
   <message name="IDS_EASY_UNLOCK_SETUP_NOTIFICATION_MESSAGE" desc="The body text for the notification inviting the user to use the Easy Unlock feature.">
     Try Smart Lock to unlock your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> without a password when your phone is nearby.
   </message>
-  <message name="IDS_EASY_UNLOCK_SETUP_NOTIFICATION_TITLE_ALTERNATE" desc="Title for the notification inviting the user to use the Easy Unlock feature.">
-    Screen lock required
-  </message>
-  <message name="IDS_EASY_UNLOCK_SETUP_NOTIFICATION_MESSAGE_ALTERNATE" desc="The body text for the notification inviting the user to use the Easy Unlock feature.">
-    Make your phone more secure before connecting your Chromebook. <ph name="LINK_BEGIN">&lt;a href="$1<ex>www.google.com</ex>"&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
-  </message>
   <message name="IDS_EASY_UNLOCK_SETUP_NOTIFICATION_BUTTON_TITLE" desc="The text to show on the button in the notification inviting the user to use the Easy Unlock feature.">
     Start setup
   </message>
@@ -3538,13 +3532,6 @@
   <message name="IDS_EASY_UNLOCK_PAIRING_CHANGE_APPLIED_NOTIFICATION_MESSAGE" desc="Message for notification shown when the pairing change is applied.">
     Your <ph name="PHONE_NAME">$1<ex>Moto X</ex></ph> can now unlock this <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph> too.
   </message>
-  <!-- Other -->
-  <message name="IDS_EASY_UNLOCK_DESCRIPTION" desc="Description of feature that automatically unlocks the user's Chromebook when their phone is nearby.">
-    Use your phone to unlock your <ph name="DEVICE_TYPE">$2<ex>Chromebook</ex></ph>
-  </message>
-  <message name="IDS_EASY_UNLOCK_ON" desc="Tells the user they now have turned on the Smart Lock feature.">
-    Smart Lock is on
-  </message>
 
   <!-- Strings for the Easy Unlock setup dialog -->
   <!-- Step 1: Intro -->
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3e968048..3aa9c7f 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1097,6 +1097,9 @@
         <message name="IDS_EXIT" desc="The text label of the Exit menu item">
           E&amp;xit
         </message>
+        <message name="IDS_MANAGED_BY_ORG" desc="The text label of the 'Managed by' UI in the app menu, for enterprise users">
+          Managed by your organization
+        </message>
       </if>
       <if expr="use_titlecase">
         <message name="IDS_IMPORT_SETTINGS_MENU_LABEL" desc="In Title Case: The app menu label to import bookmarks and settings.">
@@ -1135,6 +1138,9 @@
         <message name="IDS_EXIT" desc="In Title Case: The text label of the Exit menu item">
           E&amp;xit
         </message>
+        <message name="IDS_MANAGED_BY_ORG" desc="The text label of the 'Managed by' UI in the app menu, for enterprise users">
+          Managed by your Organization
+        </message>
       </if>
 
       <!-- Keywords -->
diff --git a/chrome/app/generated_resources_grd/IDS_MANAGED_BY_ORG.png.sha1 b/chrome/app/generated_resources_grd/IDS_MANAGED_BY_ORG.png.sha1
new file mode 100644
index 0000000..52336ea
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_MANAGED_BY_ORG.png.sha1
@@ -0,0 +1 @@
+5da71cdadaae89ea1d464fff75b65a60e3040b74
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 29d2f6d..dc2c8eb 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -902,8 +902,6 @@
     "notifications/persistent_notification_handler.h",
     "notifications/platform_notification_service_impl.cc",
     "notifications/platform_notification_service_impl.h",
-    "notifications/popups_only_ui_controller.cc",
-    "notifications/popups_only_ui_controller.h",
     "notifications/system_notification_helper.cc",
     "notifications/system_notification_helper.h",
     "ntp_snippets/bookmark_last_visit_updater.cc",
@@ -3492,6 +3490,8 @@
       "notifications/fullscreen_notification_blocker.h",
       "notifications/notification_ui_manager_impl.cc",
       "notifications/notification_ui_manager_impl.h",
+      "notifications/popups_only_ui_controller.cc",
+      "notifications/popups_only_ui_controller.h",
       "notifications/screen_lock_notification_blocker.cc",
       "notifications/screen_lock_notification_blocker.h",
       "platform_util.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 5082a05..54080f84 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3204,10 +3204,6 @@
     {"new-usb-backend", flag_descriptions::kNewUsbBackendName,
      flag_descriptions::kNewUsbBackendDescription, kOsWin,
      FEATURE_VALUE_TYPE(device::kNewUsbBackend)},
-    {"enable-desktop-ios-promotions",
-     flag_descriptions::kEnableDesktopIosPromotionsName,
-     flag_descriptions::kEnableDesktopIosPromotionsDescription, kOsWin,
-     FEATURE_VALUE_TYPE(features::kDesktopIOSPromotion)},
 #endif  // defined(OS_WIN)
 
     {"enable-zero-suggest-redirect-to-chrome",
@@ -3565,6 +3561,11 @@
      flag_descriptions::kDirectManipulationStylusDescription,
      kOsWin | kOsMac | kOsLinux,
      FEATURE_VALUE_TYPE(features::kDirectManipulationStylus)},
+
+    {"show-managed-ui", flag_descriptions::kShowManagedUiName,
+     flag_descriptions::kShowManagedUiDescription,
+     kOsWin | kOsMac | kOsLinux | kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kShowManagedUi)},
 #endif  // defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX)
 
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 2e74acc6..66655e1f 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -65,9 +65,9 @@
     JNIEnv* env,
     jobject jcaller,
     const JavaParamRef<jobject>& webContents,
-    const base::android::JavaParamRef<jobjectArray>& parameterNames,
-    const base::android::JavaParamRef<jobjectArray>& parameterValues,
-    const base::android::JavaParamRef<jstring>& initialUrlString)
+    const JavaParamRef<jobjectArray>& parameterNames,
+    const JavaParamRef<jobjectArray>& parameterValues,
+    const JavaParamRef<jstring>& initialUrlString)
     : ui_delegate_(nullptr) {
   java_autofill_assistant_ui_controller_.Reset(env, jcaller);
 
@@ -135,7 +135,7 @@
 
 void UiControllerAndroid::OnScriptSelected(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jobject>& jcaller,
     const JavaParamRef<jstring>& jscript_path) {
   std::string script_path;
   base::android::ConvertJavaStringToUTF8(env, jscript_path, &script_path);
@@ -144,8 +144,8 @@
 
 void UiControllerAndroid::OnAddressSelected(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    const base::android::JavaParamRef<jstring>& jaddress_guid) {
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jstring>& jaddress_guid) {
   if (!address_or_card_callback_)  // possibly duplicate call
     return;
 
@@ -156,8 +156,8 @@
 
 void UiControllerAndroid::OnCardSelected(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    const base::android::JavaParamRef<jstring>& jcard_guid) {
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jstring>& jcard_guid) {
   if (!address_or_card_callback_)  // possibly duplicate call
     return;
 
@@ -168,13 +168,13 @@
 
 void UiControllerAndroid::OnGetPaymentInformation(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jobject>& jcaller,
     jboolean jsucceed,
-    const base::android::JavaParamRef<jobject>& jcard,
-    const base::android::JavaParamRef<jobject>& jaddress,
-    const base::android::JavaParamRef<jstring>& jpayer_name,
-    const base::android::JavaParamRef<jstring>& jpayer_phone,
-    const base::android::JavaParamRef<jstring>& jpayer_email) {
+    const JavaParamRef<jobject>& jcard,
+    const JavaParamRef<jobject>& jaddress,
+    const JavaParamRef<jstring>& jpayer_name,
+    const JavaParamRef<jstring>& jpayer_phone,
+    const JavaParamRef<jstring>& jpayer_email) {
   DCHECK(get_payment_information_callback_);
 
   std::unique_ptr<PaymentInformation> payment_info =
@@ -209,7 +209,7 @@
 
 void UiControllerAndroid::OnAccessToken(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jobject>& jcaller,
     jboolean success,
     const JavaParamRef<jstring>& access_token) {
   if (fetch_access_token_callback_) {
@@ -221,7 +221,7 @@
 base::android::ScopedJavaLocalRef<jstring>
 UiControllerAndroid::GetPrimaryAccountName(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller) {
+    const JavaParamRef<jobject>& jcaller) {
   AccountInfo account_info = IdentityManagerFactory::GetForProfile(
                                  Profile::FromBrowserContext(browser_context_))
                                  ->GetPrimaryAccountInfo();
@@ -358,11 +358,11 @@
 
 static jlong JNI_AutofillAssistantUiController_Init(
     JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    const base::android::JavaParamRef<jobject>& webContents,
-    const base::android::JavaParamRef<jobjectArray>& parameterNames,
-    const base::android::JavaParamRef<jobjectArray>& parameterValues,
-    const base::android::JavaParamRef<jstring>& initialUrlString) {
+    const JavaParamRef<jobject>& jcaller,
+    const JavaParamRef<jobject>& webContents,
+    const JavaParamRef<jobjectArray>& parameterNames,
+    const JavaParamRef<jobjectArray>& parameterValues,
+    const JavaParamRef<jstring>& initialUrlString) {
   auto* ui_controller_android = new autofill_assistant::UiControllerAndroid(
       env, jcaller, webContents, parameterNames, parameterValues,
       initialUrlString);
diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index a16d9fe..ffc0be2a 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -253,11 +253,6 @@
   return Java_Tab_isUserInteractable(env, weak_java_tab_.get(env));
 }
 
-bool TabAndroid::LoadIfNeeded() {
-  JNIEnv* env = base::android::AttachCurrentThread();
-  return Java_Tab_loadIfNeeded(env, weak_java_tab_.get(env));
-}
-
 Profile* TabAndroid::GetProfile() const {
   if (!web_contents())
     return NULL;
diff --git a/chrome/browser/android/tab_android.h b/chrome/browser/android/tab_android.h
index 42c4c06..8d536ac 100644
--- a/chrome/browser/android/tab_android.h
+++ b/chrome/browser/android/tab_android.h
@@ -107,9 +107,6 @@
   // it.
   bool IsUserInteractable() const;
 
-  // Load the tab if it was unloaded from memory.
-  bool LoadIfNeeded();
-
   // Helper methods to make it easier to access objects from the associated
   // WebContents.  Can return NULL.
   Profile* GetProfile() const;
diff --git a/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_browsertest.cc b/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_browsertest.cc
index 8076d041..7ce48d1 100644
--- a/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/sync_file_system/sync_file_system_service.h"
 #include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
 #include "components/drive/service/fake_drive_service.h"
+#include "components/signin/core/browser/account_info.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/result_catcher.h"
 #include "services/identity/public/cpp/identity_manager.h"
@@ -28,6 +29,9 @@
 
 namespace {
 
+const char kGaiaId[] = "gaia_id";
+const char kEmail[] = "email@example.com";
+
 class FakeDriveServiceFactory
     : public drive_backend::SyncEngine::DriveServiceFactory {
  public:
@@ -117,8 +121,9 @@
   }
 
   void SignIn() {
-    fake_signin_manager_->SetAuthenticatedAccountInfo("12345", "tester");
-    sync_engine()->GoogleSigninSucceeded("12345", "tester");
+    fake_signin_manager_->SetAuthenticatedAccountInfo(kGaiaId, kEmail);
+    sync_engine()->OnPrimaryAccountSet(
+        fake_signin_manager_->GetAuthenticatedAccountInfo());
   }
 
   void SetSyncEnabled(bool enabled) {
@@ -169,7 +174,10 @@
   // service.  Wait for the completion and resume the app.
   WaitUntilIdle();
 
-  sync_engine()->GoogleSignedOut("test_account", std::string());
+  AccountInfo info;
+  info.account_id = kGaiaId;
+  info.account_id = kEmail;
+  sync_engine()->OnPrimaryAccountCleared(info);
   foo_created.Reply("resume");
 
   ASSERT_TRUE(bar_created.WaitUntilSatisfied());
@@ -182,7 +190,7 @@
   EXPECT_EQ(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
             sync_engine()->GetCurrentState());
 
-  sync_engine()->GoogleSigninSucceeded("test_account", "tester");
+  sync_engine()->OnPrimaryAccountSet(info);
   WaitUntilIdle();
 
   bar_created.Reply("resume");
diff --git a/chrome/browser/autofill/autofill_provider_browsertest.cc b/chrome/browser/autofill/autofill_provider_browsertest.cc
index ee2dbf8c..79e927a 100644
--- a/chrome/browser/autofill/autofill_provider_browsertest.cc
+++ b/chrome/browser/autofill/autofill_provider_browsertest.cc
@@ -97,6 +97,7 @@
   }
 
   void SetUpOnMainThread() override {
+    autofill_client_ = std::make_unique<TestAutofillClient>();
     autofill_provider_ = std::make_unique<MockAutofillProvider>();
     embedded_test_server()->AddDefaultHandlers(base::FilePath(kDocRoot));
     // Serve both a.com and b.com (and any other domain).
@@ -114,7 +115,7 @@
 
     // Replace the ContentAutofillDriverFactory for sub frame.
     ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
-        web_contents, &autofill_client_, "en-US",
+        web_contents, autofill_client_.get(), "en-US",
         AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
         autofill_provider_.get());
   }
@@ -216,7 +217,7 @@
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
-  TestAutofillClient autofill_client_;
+  std::unique_ptr<TestAutofillClient> autofill_client_;
 };
 
 IN_PROC_BROWSER_TEST_F(AutofillProviderBrowserTest,
diff --git a/chrome/browser/autofill/content_autofill_driver_browsertest.cc b/chrome/browser/autofill/content_autofill_driver_browsertest.cc
index 3d6f0d5..3800335c 100644
--- a/chrome/browser/autofill/content_autofill_driver_browsertest.cc
+++ b/chrome/browser/autofill/content_autofill_driver_browsertest.cc
@@ -89,17 +89,19 @@
   ~ContentAutofillDriverBrowserTest() override {}
 
   void SetUpOnMainThread() override {
+    autofill_client_ =
+        std::make_unique<testing::NiceMock<MockAutofillClient>>();
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
     ASSERT_TRUE(web_contents != NULL);
     Observe(web_contents);
-    prefs::RegisterProfilePrefs(autofill_client_.GetPrefRegistry());
+    prefs::RegisterProfilePrefs(autofill_client().GetPrefRegistry());
 
     web_contents->RemoveUserData(
         ContentAutofillDriverFactory::
             kContentAutofillDriverFactoryWebContentsUserDataKey);
     ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
-        web_contents, &autofill_client_, "en-US",
+        web_contents, &autofill_client(), "en-US",
         AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
 
     embedded_test_server()->AddDefaultHandlers(base::FilePath(kDocRoot));
@@ -110,8 +112,8 @@
 
   void TearDownOnMainThread() override {
     // Verify the expectations here, because closing the browser may incur
-    // other calls in |autofill_client_| e.g., HideAutofillPopup.
-    testing::Mock::VerifyAndClearExpectations(&autofill_client_);
+    // other calls in |autofill_client()| e.g., HideAutofillPopup.
+    testing::Mock::VerifyAndClearExpectations(&autofill_client());
   }
 
   void OnVisibilityChanged(content::Visibility visibility) override {
@@ -169,18 +171,22 @@
     }
   }
 
+  testing::NiceMock<MockAutofillClient>& autofill_client() {
+    return *autofill_client_.get();
+  }
+
  protected:
   base::Closure web_contents_hidden_callback_;
   base::Closure nav_entry_committed_callback_;
   base::Closure same_document_navigation_callback_;
   base::Closure subframe_navigation_callback_;
 
-  testing::NiceMock<MockAutofillClient> autofill_client_;
+  std::unique_ptr<testing::NiceMock<MockAutofillClient>> autofill_client_;
 };
 
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverBrowserTest,
                        SwitchTabAndHideAutofillPopup) {
-  EXPECT_CALL(autofill_client_, HideAutofillPopup()).Times(1);
+  EXPECT_CALL(autofill_client(), HideAutofillPopup()).Times(1);
 
   scoped_refptr<content::MessageLoopRunner> runner =
       new content::MessageLoopRunner;
@@ -201,7 +207,8 @@
   // The Autofill popup should be hidden for same document navigations. It may
   // called twice because the zoom changed event may also fire for same-page
   // navigations.
-  EXPECT_CALL(autofill_client_, HideAutofillPopup()).Times(testing::AtLeast(1));
+  EXPECT_CALL(autofill_client(), HideAutofillPopup())
+      .Times(testing::AtLeast(1));
 
   scoped_refptr<content::MessageLoopRunner> runner =
       new content::MessageLoopRunner;
@@ -222,7 +229,7 @@
   ui_test_utils::NavigateToURL(browser(), url);
 
   // The Autofill popup should NOT be hidden for subframe navigations.
-  EXPECT_CALL(autofill_client_, HideAutofillPopup()).Times(0);
+  EXPECT_CALL(autofill_client(), HideAutofillPopup()).Times(0);
 
   scoped_refptr<content::MessageLoopRunner> runner =
       new content::MessageLoopRunner;
@@ -240,7 +247,7 @@
 IN_PROC_BROWSER_TEST_F(ContentAutofillDriverBrowserTest,
                        TestPageNavigationHidingAutofillPopup) {
   // HideAutofillPopup is called once for each navigation.
-  EXPECT_CALL(autofill_client_, HideAutofillPopup()).Times(2);
+  EXPECT_CALL(autofill_client(), HideAutofillPopup()).Times(2);
 
   scoped_refptr<content::MessageLoopRunner> runner =
       new content::MessageLoopRunner;
diff --git a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
index 2a084ec..e7b043e 100644
--- a/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
+++ b/chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
@@ -19,6 +19,7 @@
 #include "components/exo/shell_surface.h"
 #include "components/exo/test/exo_test_helper.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "components/viz/common/features.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/env.h"
@@ -45,7 +46,8 @@
     chromeos::AccessibilityManager::Get()->SetProfileForTest(
         browser()->profile());
 
-    wm_helper_ = std::make_unique<exo::WMHelper>(ash::Shell::Get()->aura_env());
+    wm_helper_ =
+        std::make_unique<exo::WMHelperChromeOS>(ash::Shell::Get()->aura_env());
     exo::WMHelper::SetInstance(wm_helper_.get());
   }
 
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 992b02e..2910368 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2856,6 +2856,11 @@
     "Enables proactive tab freezing and discarding. This requires "
     "#enable-page-almost-idle.";
 
+const char kShowManagedUiName[] = "Show managed UI for managed users";
+const char kShowManagedUiDescription[] =
+    "Enabled/disable showing enterprise users a 'Managed by your organization' "
+    "message in the app menu and on some chrome:// pages.";
+
 const char kSiteCharacteristicsDatabaseName[] = "Site Characteristics database";
 const char kSiteCharacteristicsDatabaseDescription[] =
     "Records usage of some features in a database while a tab is in background "
@@ -2904,11 +2909,6 @@
     "Enables the use of an AppContainer on sandboxed processes to improve "
     "security.";
 
-const char kEnableDesktopIosPromotionsName[] = "Desktop to iOS promotions.";
-const char kEnableDesktopIosPromotionsDescription[] =
-    "Enable Desktop to iOS promotions, and allow users to see them if they are "
-    "eligible.";
-
 const char kEnableGpuAppcontainerName[] = "Enable GPU AppContainer Lockdown.";
 const char kEnableGpuAppcontainerDescription[] =
     "Enables the use of an AppContainer for the GPU sandboxed processes to "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index ff171832..1969b80b6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1711,6 +1711,9 @@
 extern const char kProactiveTabFreezeAndDiscardName[];
 extern const char kProactiveTabFreezeAndDiscardDescription[];
 
+extern const char kShowManagedUiName[];
+extern const char kShowManagedUiDescription[];
+
 extern const char kSiteCharacteristicsDatabaseName[];
 extern const char kSiteCharacteristicsDatabaseDescription[];
 
@@ -1742,9 +1745,6 @@
 extern const char kEnableAppcontainerName[];
 extern const char kEnableAppcontainerDescription[];
 
-extern const char kEnableDesktopIosPromotionsName[];
-extern const char kEnableDesktopIosPromotionsDescription[];
-
 extern const char kEnableGpuAppcontainerName[];
 extern const char kEnableGpuAppcontainerDescription[];
 
diff --git a/chrome/browser/notifications/notification_interactive_uitest.cc b/chrome/browser/notifications/notification_interactive_uitest.cc
index 13af769..952f260 100644
--- a/chrome/browser/notifications/notification_interactive_uitest.cc
+++ b/chrome/browser/notifications/notification_interactive_uitest.cc
@@ -39,7 +39,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/message_center/message_center.h"
-#include "ui/message_center/message_center_observer.h"
 #include "ui/message_center/notification_blocker.h"
 #include "ui/message_center/public/cpp/message_center_constants.h"
 #include "ui/message_center/public/cpp/notification.h"
@@ -131,20 +130,36 @@
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, NotificationBlockerTest) {
   ToggledNotificationBlocker blocker;
+  TestMessageCenterObserver observer;
 
   ASSERT_TRUE(embedded_test_server()->Start());
+  message_center::MessageCenter::Get()->AddObserver(&observer);
 
   // Creates a simple notification.
   AllowAllOrigins();
   ui_test_utils::NavigateToURL(browser(), GetTestPageURL());
 
+  EXPECT_EQ(0, GetNotificationPopupCount());
+  blocker.SetNotificationsEnabled(false);
+
   std::string result = CreateSimpleNotification(browser(), true);
   EXPECT_NE("-1", result);
+  EXPECT_EQ(0, GetNotificationPopupCount());
+  EXPECT_EQ("", observer.last_displayed_id());
+
+  blocker.SetNotificationsEnabled(true);
+  EXPECT_EQ(1, GetNotificationPopupCount());
+  EXPECT_NE("", observer.last_displayed_id());
+
+  result = CreateSimpleNotification(browser(), true);
+  EXPECT_NE("-1", result);
   result = CreateSimpleNotification(browser(), true);
   EXPECT_NE("-1", result);
 
   blocker.SetNotificationsEnabled(false);
   EXPECT_EQ(0, GetNotificationPopupCount());
+
+  message_center::MessageCenter::Get()->RemoveObserver(&observer);
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationsTest, TestCloseNotification) {
diff --git a/chrome/browser/notifications/notification_interactive_uitest_support.cc b/chrome/browser/notifications/notification_interactive_uitest_support.cc
index 1df4dca..9c836a51 100644
--- a/chrome/browser/notifications/notification_interactive_uitest_support.cc
+++ b/chrome/browser/notifications/notification_interactive_uitest_support.cc
@@ -117,7 +117,15 @@
   return impl_->Wait();
 }
 
-// -----------------------------------------------------------------------------
+void TestMessageCenterObserver::OnNotificationDisplayed(
+    const std::string& notification_id,
+    const message_center::DisplaySource source) {
+  last_displayed_id_ = notification_id;
+}
+
+const std::string& TestMessageCenterObserver::last_displayed_id() const {
+  return last_displayed_id_;
+}
 
 void NotificationsTest::SetUpDefaultCommandLine(
     base::CommandLine* command_line) {
diff --git a/chrome/browser/notifications/notification_interactive_uitest_support.h b/chrome/browser/notifications/notification_interactive_uitest_support.h
index a71e8e7..b604d90 100644
--- a/chrome/browser/notifications/notification_interactive_uitest_support.h
+++ b/chrome/browser/notifications/notification_interactive_uitest_support.h
@@ -10,6 +10,7 @@
 #include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/content_settings/core/common/content_settings.h"
+#include "ui/message_center/message_center_observer.h"
 
 class MessageCenterChangeObserver {
  public:
@@ -25,6 +26,23 @@
   DISALLOW_COPY_AND_ASSIGN(MessageCenterChangeObserver);
 };
 
+class TestMessageCenterObserver : public message_center::MessageCenterObserver {
+ public:
+  TestMessageCenterObserver() = default;
+
+  // MessageCenterObserver:
+  void OnNotificationDisplayed(
+      const std::string& notification_id,
+      const message_center::DisplaySource source) override;
+
+  const std::string& last_displayed_id() const;
+
+ private:
+  std::string last_displayed_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestMessageCenterObserver);
+};
+
 class NotificationsTest : public InProcessBrowserTest {
  public:
   NotificationsTest() {}
diff --git a/chrome/browser/notifications/notification_ui_manager_browsertest.cc b/chrome/browser/notifications/notification_ui_manager_browsertest.cc
index 1e8c1b51..6302f11b 100644
--- a/chrome/browser/notifications/notification_ui_manager_browsertest.cc
+++ b/chrome/browser/notifications/notification_ui_manager_browsertest.cc
@@ -15,6 +15,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/notifications/notification_interactive_uitest_support.h"
 #include "chrome/browser/notifications/notification_ui_manager_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -126,11 +127,15 @@
   // mode..." or something which may change the expectation.
   // TODO(mukai): move this to SetUpOnMainThread() after fixing the side-effect
   // of canceling animation which prevents some Displayed() event.
+  TestMessageCenterObserver observer;
+  message_center()->AddObserver(&observer);
   manager()->CancelAll();
   manager()->Add(CreateTestNotification("hey"), profile());
   EXPECT_EQ(1u, message_center()->NotificationCount());
+  EXPECT_NE("", observer.last_displayed_id());
   manager()->CancelById("hey", NotificationUIManager::GetProfileID(profile()));
   EXPECT_EQ(0u, message_center()->NotificationCount());
+  message_center()->RemoveObserver(&observer);
 }
 
 IN_PROC_BROWSER_TEST_F(NotificationUIManagerBrowserTest, BasicDelegate) {
diff --git a/chrome/browser/notifications/notification_ui_manager_impl.cc b/chrome/browser/notifications/notification_ui_manager_impl.cc
index 9108c98..a3a085e 100644
--- a/chrome/browser/notifications/notification_ui_manager_impl.cc
+++ b/chrome/browser/notifications/notification_ui_manager_impl.cc
@@ -41,8 +41,7 @@
 
 NotificationUIManagerImpl::NotificationUIManagerImpl()
     : system_observer_(this),
-      popups_only_ui_controller_(std::make_unique<PopupsOnlyUiController>(
-          PopupsOnlyUiController::CreateDelegate())) {
+      popups_only_ui_controller_(std::make_unique<PopupsOnlyUiController>()) {
   auto* message_center = MessageCenter::Get();
   message_center->AddObserver(this);
 
diff --git a/chrome/browser/notifications/popups_only_ui_controller.cc b/chrome/browser/notifications/popups_only_ui_controller.cc
index 2d0a90aa..d9fcdaf5 100644
--- a/chrome/browser/notifications/popups_only_ui_controller.cc
+++ b/chrome/browser/notifications/popups_only_ui_controller.cc
@@ -7,12 +7,15 @@
 #include "ui/display/screen.h"
 #include "ui/message_center/message_center.h"
 
-PopupsOnlyUiController::PopupsOnlyUiController(
-    std::unique_ptr<Delegate> delegate)
-    : message_center_(message_center::MessageCenter::Get()),
-      delegate_(std::move(delegate)) {
+PopupsOnlyUiController::PopupsOnlyUiController()
+    : message_center_(message_center::MessageCenter::Get()) {
   message_center_->AddObserver(this);
   message_center_->SetHasMessageCenterView(false);
+
+  // Initialize delegate after calling message_center_->AddObserver to ensure
+  // the correct order of observers. (PopupsOnlyUiController has to be called
+  // before MessagePopupCollection, see crbug.com/901350)
+  delegate_ = CreateDelegate();
 }
 
 PopupsOnlyUiController::~PopupsOnlyUiController() {
@@ -43,6 +46,11 @@
     ShowOrHidePopupBubbles();
 }
 
+void PopupsOnlyUiController::OnBlockingStateChanged(
+    message_center::NotificationBlocker* blocker) {
+  ShowOrHidePopupBubbles();
+}
+
 void PopupsOnlyUiController::ShowOrHidePopupBubbles() {
   if (popups_visible_ && !message_center_->HasPopupNotifications()) {
     if (delegate_)
diff --git a/chrome/browser/notifications/popups_only_ui_controller.h b/chrome/browser/notifications/popups_only_ui_controller.h
index 44fde08a..eef747d 100644
--- a/chrome/browser/notifications/popups_only_ui_controller.h
+++ b/chrome/browser/notifications/popups_only_ui_controller.h
@@ -27,10 +27,10 @@
   // Implementations are platform specific.
   static std::unique_ptr<Delegate> CreateDelegate();
 
-  explicit PopupsOnlyUiController(std::unique_ptr<Delegate> delegate);
+  PopupsOnlyUiController();
   ~PopupsOnlyUiController() override;
 
-  // UiDelegate implementation.
+  // MessageCenterObserver:
   void OnNotificationAdded(const std::string& notification_id) override;
   void OnNotificationRemoved(const std::string& notification_id,
                              bool b_user) override;
@@ -39,13 +39,15 @@
       const std::string& notification_id,
       const base::Optional<int>& button_index,
       const base::Optional<base::string16>& reply) override;
+  void OnBlockingStateChanged(
+      message_center::NotificationBlocker* blocker) override;
 
   Delegate* delegate() { return delegate_.get(); }
   bool popups_visible() const { return popups_visible_; }
 
  private:
   message_center::MessageCenter* const message_center_;
-  const std::unique_ptr<Delegate> delegate_;
+  std::unique_ptr<Delegate> delegate_;
 
   // Update the visibility of the popup bubbles. Shows or hides them if
   // necessary.
diff --git a/chrome/browser/notifications/win/notification_image_retainer.h b/chrome/browser/notifications/win/notification_image_retainer.h
index c1c8f17..04d91ec 100644
--- a/chrome/browser/notifications/win/notification_image_retainer.h
+++ b/chrome/browser/notifications/win/notification_image_retainer.h
@@ -48,9 +48,8 @@
 
   // Stores an |image| on disk in a temporary (short-lived) file. Returns the
   // path to the file created, which will be valid for a few seconds only. It
-  // will be deleted either after a short timeout or after a restart of Chrome
-  // (the next time this function is called). The function returns an empty
-  // FilePath if file creation fails.
+  // will be deleted either after a short timeout or after a restart of Chrome.
+  // The function returns an empty FilePath if file creation fails.
   virtual base::FilePath RegisterTemporaryImage(const gfx::Image& image);
 
   // Returns a closure that, when run, performs cleanup operations. This closure
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 8c86eb18..c6be13b 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -309,7 +309,6 @@
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #include "chrome/browser/safe_browsing/chrome_cleaner/settings_resetter_win.h"
 #include "chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_prefs_manager.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
 #endif
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
@@ -529,7 +528,6 @@
 
 #if defined(OS_WIN)
   component_updater::RegisterPrefsForSwReporter(registry);
-  desktop_ios_promotion::RegisterLocalPrefs(registry);
 #if defined(GOOGLE_CHROME_BUILD)
   IncompatibleApplicationsUpdater::RegisterLocalStatePrefs(registry);
   ModuleDatabase::RegisterLocalStatePrefs(registry);
@@ -739,7 +737,6 @@
 
 #if defined(OS_WIN)
   component_updater::RegisterProfilePrefsForSwReporter(registry);
-  desktop_ios_promotion::RegisterProfilePrefs(registry);
   NetworkProfileBubble::RegisterProfilePrefs(registry);
   safe_browsing::SettingsResetPromptPrefsManager::RegisterProfilePrefs(
       registry);
diff --git a/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc b/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
index 91bef59..731e715 100644
--- a/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
+++ b/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
@@ -134,7 +134,8 @@
     cmd->AppendSwitch("enable-spdy-proxy-auth");
   }
 
-  void SetResourceLoadingHints(const std::vector<std::string>& hints_sites) {
+  void SetDefaultOnlyResourceLoadingHints(
+      const std::vector<std::string>& hints_sites) {
     std::vector<std::string> resource_patterns;
     resource_patterns.push_back("foo.jpg");
     resource_patterns.push_back("png");
@@ -152,6 +153,51 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  // Sets the resource loading hints in optimization guide service. The hints
+  // are set as experimental.
+  void SetExperimentOnlyResourceLoadingHints(
+      const std::vector<std::string>& hints_sites) {
+    std::vector<std::string> resource_patterns;
+    resource_patterns.push_back("foo.jpg");
+    resource_patterns.push_back("png");
+    resource_patterns.push_back("woff2");
+
+    const optimization_guide::ComponentInfo& component_info =
+        test_component_creator_.CreateComponentInfoWithExperimentalPageHints(
+            optimization_guide::proto::RESOURCE_LOADING, hints_sites,
+            resource_patterns);
+
+    g_browser_process->optimization_guide_service()->ProcessHints(
+        component_info);
+
+    // Wait for hints to be processed by PreviewsOptimizationGuide.
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Sets the resource loading hints in optimization guide service. Some hints
+  // are set as experimental, while others are set as default.
+  void SetMixResourceLoadingHints(const std::vector<std::string>& hints_sites) {
+    std::vector<std::string> experimental_resource_patterns;
+    experimental_resource_patterns.push_back("foo.jpg");
+    experimental_resource_patterns.push_back("png");
+    experimental_resource_patterns.push_back("woff2");
+
+    std::vector<std::string> default_resource_patterns;
+    default_resource_patterns.push_back("bar.jpg");
+    default_resource_patterns.push_back("woff2");
+
+    const optimization_guide::ComponentInfo& component_info =
+        test_component_creator_.CreateComponentInfoWithMixPageHints(
+            optimization_guide::proto::RESOURCE_LOADING, hints_sites,
+            experimental_resource_patterns, default_resource_patterns);
+
+    g_browser_process->optimization_guide_service()->ProcessHints(
+        component_info);
+
+    // Wait for hints to be processed by PreviewsOptimizationGuide.
+    base::RunLoop().RunUntilIdle();
+  }
+
   void AddTestOptimizationGuideServiceObserver(
       TestOptimizationGuideServiceObserver* observer) {
     g_browser_process->optimization_guide_service()->AddObserver(observer);
@@ -282,34 +328,15 @@
 // Previews InfoBar (which these tests triggers) does not work on Mac.
 // See https://crbug.com/782322 for details. Also occasional flakes on win7
 // (https://crbug.com/789542).
-// Additionally, ResourceLoadingHintsHttpsWhitelistedRedirectToHttps is disabled
-// on all OS types until hints are made to work with redirects.
-// TODO(jegray): Re-enable ResourceLoadingHintsHttpsWhitelistedRedirectToHttps
-// when support for redirects is added: https://crbug.com/891752
-#if !defined(OS_MACOSX) && !defined(OS_WIN)
-#define MAYBE_ResourceLoadingHintsHttpsWhitelisted \
-  ResourceLoadingHintsHttpsWhitelisted
-#define MAYBE_ResourceLoadingHintsHttpsWhitelistedRedirectToHttps \
-  DISABLED_ResourceLoadingHintsHttpsWhitelistedRedirectToHttps
-#define MAYBE_ResourceLoadingHintsHttpsNoWhitelisted \
-  ResourceLoadingHintsHttpsNoWhitelisted
-#define MAYBE_ResourceLoadingHintsHttp ResourceLoadingHintsHttp
-#define MAYBE_ResourceLoadingHintsHttpsWhitelistedNoTransform \
-  ResourceLoadingHintsHttpsWhitelistedNoTransform
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#define DISABLE_ON_WIN_MAC(x) DISABLED_##x
 #else
-#define MAYBE_ResourceLoadingHintsHttpsWhitelisted \
-  DISABLED_ResourceLoadingHintsHttpsWhitelisted
-#define MAYBE_ResourceLoadingHintsHttpsWhitelistedRedirectToHttps \
-  DISABLED_ResourceLoadingHintsHttpsWhitelistedRedirectToHttps
-#define MAYBE_ResourceLoadingHintsHttpsNoWhitelisted \
-  DISABLED_ResourceLoadingHintsHttpsNoWhitelisted
-#define MAYBE_ResourceLoadingHintsHttp DISABLED_ResourceLoadingHintsHttp
-#define MAYBE_ResourceLoadingHintsHttpsWhitelistedNoTransform \
-  DISABLED_ResourceLoadingHintsHttpsWhitelistedNoTransform
+#define DISABLE_ON_WIN_MAC(x) x
 #endif
 
-IN_PROC_BROWSER_TEST_F(ResourceLoadingHintsBrowserTest,
-                       MAYBE_ResourceLoadingHintsHttpsWhitelisted) {
+IN_PROC_BROWSER_TEST_F(
+    ResourceLoadingHintsBrowserTest,
+    DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttpsWhitelisted)) {
   SetExpectedFooJpgRequest(false);
   SetExpectedBarJpgRequest(true);
 
@@ -318,7 +345,7 @@
   base::RunLoop().RunUntilIdle();
 
   // Whitelist test URL for resource loading hints.
-  SetResourceLoadingHints({https_url().host()});
+  SetDefaultOnlyResourceLoadingHints({https_url().host()});
   observer.WaitForNotification();
 
   base::HistogramTester histogram_tester;
@@ -335,7 +362,7 @@
       static_cast<int>(previews::PreviewsEligibilityReason::ALLOWED), 1);
   histogram_tester.ExpectBucketCount(
       "Previews.InfoBarAction.ResourceLoadingHints", 0, 1);
-  // SetResourceLoadingHints sets 3 resource loading hints patterns.
+  // SetDefaultOnlyResourceLoadingHints sets 3 resource loading hints patterns.
   histogram_tester.ExpectBucketCount(
       "ResourceLoadingHints.CountBlockedSubresourcePatterns", 3, 1);
   EXPECT_TRUE(resource_loading_hint_intervention_header_seen());
@@ -358,15 +385,53 @@
       static_cast<int>(previews::PreviewsEligibilityReason::ALLOWED), 2);
   histogram_tester.ExpectBucketCount(
       "Previews.InfoBarAction.ResourceLoadingHints", 0, 2);
-  // SetResourceLoadingHints sets 3 resource loading hints patterns.
+  // SetDefaultOnlyResourceLoadingHints sets 3 resource loading hints patterns.
   histogram_tester.ExpectBucketCount(
       "ResourceLoadingHints.CountBlockedSubresourcePatterns", 3, 2);
   EXPECT_TRUE(resource_loading_hint_intervention_header_seen());
 }
 
+// Sets only the experimental hints, but does not enable the matching
+// experiment. Verifies that the hints are not used, and the resource loading is
+// not blocked.
 IN_PROC_BROWSER_TEST_F(
     ResourceLoadingHintsBrowserTest,
-    MAYBE_ResourceLoadingHintsHttpsWhitelistedRedirectToHttps) {
+    DISABLE_ON_WIN_MAC(ExperimentalHints_ExperimentIsNotEnabled)) {
+  SetExpectedFooJpgRequest(true);
+  SetExpectedBarJpgRequest(true);
+
+  TestOptimizationGuideServiceObserver observer;
+  AddTestOptimizationGuideServiceObserver(&observer);
+  base::RunLoop().RunUntilIdle();
+
+  // Whitelist test URL for resource loading hints.
+  SetExperimentOnlyResourceLoadingHints({https_url().host()});
+  observer.WaitForNotification();
+
+  base::HistogramTester histogram_tester;
+
+  ui_test_utils::NavigateToURL(browser(), https_url());
+
+  histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason.ResourceLoadingHints",
+      static_cast<int>(previews::PreviewsEligibilityReason::ALLOWED), 1);
+  histogram_tester.ExpectTotalCount(
+      "Previews.InfoBarAction.ResourceLoadingHints", 0);
+  histogram_tester.ExpectTotalCount(
+      "ResourceLoadingHints.CountBlockedSubresourcePatterns", 0);
+  EXPECT_FALSE(resource_loading_hint_intervention_header_seen());
+}
+
+// Sets only the experimental hints, and enables the matching experiment.
+// Verifies that the hints are used, and the resource loading is blocked.
+IN_PROC_BROWSER_TEST_F(
+    ResourceLoadingHintsBrowserTest,
+    DISABLE_ON_WIN_MAC(ExperimentalHints_ExperimentIsEnabled)) {
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeatureWithParameters(
+      previews::features::kOptimizationHintsExperiments,
+      {{previews::features::kOptimizationHintsExperimentNameParam,
+        optimization_guide::testing::kFooExperimentName}});
   SetExpectedFooJpgRequest(false);
   SetExpectedBarJpgRequest(true);
 
@@ -374,7 +439,126 @@
   AddTestOptimizationGuideServiceObserver(&observer);
   base::RunLoop().RunUntilIdle();
 
-  SetResourceLoadingHints({https_url().host()});
+  // Whitelist test URL for resource loading hints.
+  SetExperimentOnlyResourceLoadingHints({https_url().host()});
+  observer.WaitForNotification();
+
+  base::HistogramTester histogram_tester;
+
+  ui_test_utils::NavigateToURL(browser(), https_url());
+
+  RetryForHistogramUntilCountReached(
+      &histogram_tester, "ResourceLoadingHints.CountBlockedSubresourcePatterns",
+      1);
+  histogram_tester.ExpectUniqueSample(
+      "ResourceLoadingHints.ResourcePatternsAvailableAtCommit", 1, 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason.ResourceLoadingHints",
+      static_cast<int>(previews::PreviewsEligibilityReason::ALLOWED), 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.InfoBarAction.ResourceLoadingHints", 0, 1);
+  // SetDefaultOnlyResourceLoadingHints sets 3 resource loading hints patterns.
+  histogram_tester.ExpectBucketCount(
+      "ResourceLoadingHints.CountBlockedSubresourcePatterns", 3, 1);
+  EXPECT_TRUE(resource_loading_hint_intervention_header_seen());
+}
+
+// Sets both the experimental and default hints, and enables the matching
+// experiment. Verifies that the hints are used, and the resource loading is
+// blocked.
+IN_PROC_BROWSER_TEST_F(
+    ResourceLoadingHintsBrowserTest,
+    DISABLE_ON_WIN_MAC(MixExperimentalHints_ExperimentIsEnabled)) {
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeatureWithParameters(
+      previews::features::kOptimizationHintsExperiments,
+      {{previews::features::kOptimizationHintsExperimentNameParam,
+        optimization_guide::testing::kFooExperimentName}});
+  SetExpectedFooJpgRequest(false);
+  SetExpectedBarJpgRequest(true);
+
+  TestOptimizationGuideServiceObserver observer;
+  AddTestOptimizationGuideServiceObserver(&observer);
+  base::RunLoop().RunUntilIdle();
+
+  // Whitelist test URL for resource loading hints. Set both experimental and
+  // non-experimental hints.
+  SetMixResourceLoadingHints({https_url().host()});
+  observer.WaitForNotification();
+
+  base::HistogramTester histogram_tester;
+
+  ui_test_utils::NavigateToURL(browser(), https_url());
+
+  RetryForHistogramUntilCountReached(
+      &histogram_tester, "ResourceLoadingHints.CountBlockedSubresourcePatterns",
+      1);
+  histogram_tester.ExpectUniqueSample(
+      "ResourceLoadingHints.ResourcePatternsAvailableAtCommit", 1, 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason.ResourceLoadingHints",
+      static_cast<int>(previews::PreviewsEligibilityReason::ALLOWED), 1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.InfoBarAction.ResourceLoadingHints", 0, 1);
+  // SetDefaultOnlyResourceLoadingHints sets 3 resource loading hints patterns.
+  histogram_tester.ExpectBucketCount(
+      "ResourceLoadingHints.CountBlockedSubresourcePatterns", 3, 1);
+  EXPECT_TRUE(resource_loading_hint_intervention_header_seen());
+}
+
+// Sets both the experimental and default hints, but does not enable the
+// matching experiment. Verifies that the hints from the experiment are not
+// used.
+IN_PROC_BROWSER_TEST_F(
+    ResourceLoadingHintsBrowserTest,
+    DISABLE_ON_WIN_MAC(MixExperimentalHints_ExperimentIsNotEnabled)) {
+  base::test::ScopedFeatureList scoped_list;
+  scoped_list.InitAndEnableFeatureWithParameters(
+      previews::features::kOptimizationHintsExperiments,
+      {{previews::features::kOptimizationHintsExperimentNameParam,
+        "some_other_experiment"}});
+  SetExpectedFooJpgRequest(true);
+  SetExpectedBarJpgRequest(false);
+
+  TestOptimizationGuideServiceObserver observer;
+  AddTestOptimizationGuideServiceObserver(&observer);
+  base::RunLoop().RunUntilIdle();
+
+  // Whitelist test URL for resource loading hints.
+  SetMixResourceLoadingHints({https_url().host()});
+  observer.WaitForNotification();
+
+  base::HistogramTester histogram_tester;
+
+  ui_test_utils::NavigateToURL(browser(), https_url());
+
+  RetryForHistogramUntilCountReached(
+      &histogram_tester, "ResourceLoadingHints.CountBlockedSubresourcePatterns",
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.EligibilityReason.ResourceLoadingHints",
+      static_cast<int>(previews::PreviewsEligibilityReason::ALLOWED), 1);
+  // Infobar would still be shown since there were at least one resource
+  // loading hints available, even though none of them matched.
+  histogram_tester.ExpectTotalCount(
+      "Previews.InfoBarAction.ResourceLoadingHints", 1);
+  EXPECT_TRUE(resource_loading_hint_intervention_header_seen());
+}
+
+// Disabled on all OS types until hints are made to work with redirects.
+// TODO(jegray): https://crbug.com/891752. Re-enable when support for redirects
+// is added.
+IN_PROC_BROWSER_TEST_F(
+    ResourceLoadingHintsBrowserTest,
+    DISABLED_ResourceLoadingHintsHttpsWhitelistedRedirectToHttps) {
+  SetExpectedFooJpgRequest(false);
+  SetExpectedBarJpgRequest(true);
+
+  TestOptimizationGuideServiceObserver observer;
+  AddTestOptimizationGuideServiceObserver(&observer);
+  base::RunLoop().RunUntilIdle();
+
+  SetDefaultOnlyResourceLoadingHints({https_url().host()});
   observer.WaitForNotification();
 
   base::HistogramTester histogram_tester;
@@ -389,14 +573,15 @@
       static_cast<int>(previews::PreviewsEligibilityReason::ALLOWED), 1);
   histogram_tester.ExpectTotalCount(
       "Previews.InfoBarAction.ResourceLoadingHints", 1);
-  // SetResourceLoadingHints sets 3 resource loading hints patterns.
+  // SetDefaultOnlyResourceLoadingHints sets 3 resource loading hints patterns.
   histogram_tester.ExpectBucketCount(
       "ResourceLoadingHints.CountBlockedSubresourcePatterns", 3, 1);
   EXPECT_TRUE(resource_loading_hint_intervention_header_seen());
 }
 
-IN_PROC_BROWSER_TEST_F(ResourceLoadingHintsBrowserTest,
-                       MAYBE_ResourceLoadingHintsHttpsNoWhitelisted) {
+IN_PROC_BROWSER_TEST_F(
+    ResourceLoadingHintsBrowserTest,
+    DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttpsNoWhitelisted)) {
   SetExpectedFooJpgRequest(true);
   SetExpectedBarJpgRequest(true);
 
@@ -404,7 +589,7 @@
   AddTestOptimizationGuideServiceObserver(&observer);
   base::RunLoop().RunUntilIdle();
 
-  SetResourceLoadingHints({});
+  SetDefaultOnlyResourceLoadingHints({});
   observer.WaitForNotification();
 
   base::HistogramTester histogram_tester;
@@ -426,7 +611,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ResourceLoadingHintsBrowserTest,
-                       MAYBE_ResourceLoadingHintsHttp) {
+                       DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttp)) {
   SetExpectedFooJpgRequest(true);
   SetExpectedBarJpgRequest(true);
 
@@ -435,7 +620,7 @@
   base::RunLoop().RunUntilIdle();
 
   // Whitelist test HTTP URL for resource loading hints.
-  SetResourceLoadingHints({https_url().host()});
+  SetDefaultOnlyResourceLoadingHints({https_url().host()});
   observer.WaitForNotification();
 
   base::HistogramTester histogram_tester;
@@ -453,8 +638,9 @@
   EXPECT_FALSE(resource_loading_hint_intervention_header_seen());
 }
 
-IN_PROC_BROWSER_TEST_F(ResourceLoadingHintsBrowserTest,
-                       MAYBE_ResourceLoadingHintsHttpsWhitelistedNoTransform) {
+IN_PROC_BROWSER_TEST_F(
+    ResourceLoadingHintsBrowserTest,
+    DISABLE_ON_WIN_MAC(ResourceLoadingHintsHttpsWhitelistedNoTransform)) {
   SetExpectedFooJpgRequest(true);
   SetExpectedBarJpgRequest(true);
 
@@ -463,7 +649,7 @@
   base::RunLoop().RunUntilIdle();
 
   // Whitelist test URL for resource loading hints.
-  SetResourceLoadingHints({https_url().host()});
+  SetDefaultOnlyResourceLoadingHints({https_url().host()});
   observer.WaitForNotification();
 
   base::HistogramTester histogram_tester;
diff --git a/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc b/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc
index b1fd35c..10fce0f 100644
--- a/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc
+++ b/chrome/browser/printing/cloud_print/gcd_api_flow_unittest.cc
@@ -82,8 +82,8 @@
   MockDelegate* mock_delegate_;
 
  private:
-  identity::IdentityTestEnvironment identity_test_environment_;
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
+  identity::IdentityTestEnvironment identity_test_environment_;
   scoped_refptr<network::WeakWrapperSharedURLLoaderFactory>
       test_shared_url_loader_factory_;
 };
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 b1626549..21ccdb1 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -126,7 +126,6 @@
 
 #if defined(OS_WIN)
 #include "chrome/browser/profile_resetter/triggered_profile_resetter_factory.h"
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h"
 #endif
 
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
@@ -244,9 +243,6 @@
   CookieSettingsFactory::GetInstance();
   NotifierStateTrackerFactory::GetInstance();
   data_use_measurement::ChromeDataUseAscriberServiceFactory::GetInstance();
-#if defined(OS_WIN)
-  SMSServiceFactory::GetInstance();
-#endif
   dom_distiller::DomDistillerServiceFactory::GetInstance();
   domain_reliability::DomainReliabilityServiceFactory::GetInstance();
   DownloadCoreServiceFactory::GetInstance();
diff --git a/chrome/browser/safe_browsing/threat_details_unittest.cc b/chrome/browser/safe_browsing/threat_details_unittest.cc
index 9859032..dbbaada 100644
--- a/chrome/browser/safe_browsing/threat_details_unittest.cc
+++ b/chrome/browser/safe_browsing/threat_details_unittest.cc
@@ -997,12 +997,17 @@
 // This test uses the following structure.
 // kDOMParentURL
 //  \- <div id=outer>  # Trimmed
+//  \- <script id=shared-resource src=kFirstRedirectURL>  # Trimmed
 //  \- <script id=outer-sibling src=kReferrerURL>  # Reported (parent of ad ID)
 //   \- <script id=sibling src=kFirstRedirectURL>  # Reported (sibling of ad ID)
 //   \- <div data-google-query-id=ad-tag>  # Reported (ad ID)
 //     \- <iframe src=kDOMChildURL foo=bar>  # Reported (child of ad ID)
 //       \- <div id=inner bar=baz/>  # Reported (child of ad ID)
 //       \- <script src=kDOMChildURL2>  # Reported (child of ad ID)
+//
+// *Note: the best way to match the inputs and expectations in the body of the
+// test with the structure above, is to use URLs for resources, and the ID
+// attributes for DOM elements.
 TEST_F(ThreatDetailsTest, ThreatDOMDetails_TrimToAdTags) {
   // Create a child renderer inside the main frame to house the inner iframe.
   // Perform the navigation first in order to manipulate the frame tree.
@@ -1022,12 +1027,22 @@
       mojom::AttributeNameValue::New("id", "outer"));
   outer_params.push_back(std::move(outer_div));
 
+  mojom::ThreatDOMDetailsNodePtr shared_resource_script =
+      mojom::ThreatDOMDetailsNode::New();
+  shared_resource_script->node_id = 2;
+  shared_resource_script->tag_name = "script";
+  shared_resource_script->url = GURL(kFirstRedirectURL);
+  shared_resource_script->parent = GURL(kDOMParentURL);
+  shared_resource_script->attributes.push_back(
+      mojom::AttributeNameValue::New("id", "shared-resource"));
+  outer_params.push_back(std::move(shared_resource_script));
+
   mojom::ThreatDOMDetailsNodePtr outer_sibling_script =
       mojom::ThreatDOMDetailsNode::New();
-  outer_sibling_script->node_id = 2;
+  outer_sibling_script->node_id = 3;
   outer_sibling_script->url = GURL(kReferrerURL);
-  outer_sibling_script->child_node_ids.push_back(3);
   outer_sibling_script->child_node_ids.push_back(4);
+  outer_sibling_script->child_node_ids.push_back(5);
   outer_sibling_script->tag_name = "script";
   outer_sibling_script->parent = GURL(kDOMParentURL);
   outer_sibling_script->attributes.push_back(
@@ -1038,11 +1053,11 @@
 
   mojom::ThreatDOMDetailsNodePtr sibling_script =
       mojom::ThreatDOMDetailsNode::New();
-  sibling_script->node_id = 3;
+  sibling_script->node_id = 4;
   sibling_script->url = GURL(kFirstRedirectURL);
   sibling_script->tag_name = "script";
   sibling_script->parent = GURL(kDOMParentURL);
-  sibling_script->parent_node_id = 2;
+  sibling_script->parent_node_id = 3;
   sibling_script->attributes.push_back(
       mojom::AttributeNameValue::New("src", kFirstRedirectURL));
   sibling_script->attributes.push_back(
@@ -1051,9 +1066,9 @@
 
   mojom::ThreatDOMDetailsNodePtr outer_ad_tag_div =
       mojom::ThreatDOMDetailsNode::New();
-  outer_ad_tag_div->node_id = 4;
-  outer_ad_tag_div->parent_node_id = 2;
-  outer_ad_tag_div->child_node_ids.push_back(5);
+  outer_ad_tag_div->node_id = 5;
+  outer_ad_tag_div->parent_node_id = 3;
+  outer_ad_tag_div->child_node_ids.push_back(6);
   outer_ad_tag_div->tag_name = "div";
   outer_ad_tag_div->parent = GURL(kDOMParentURL);
   outer_ad_tag_div->attributes.push_back(
@@ -1062,8 +1077,8 @@
 
   mojom::ThreatDOMDetailsNodePtr outer_child_iframe =
       mojom::ThreatDOMDetailsNode::New();
-  outer_child_iframe->node_id = 5;
-  outer_child_iframe->parent_node_id = 4;
+  outer_child_iframe->node_id = 6;
+  outer_child_iframe->parent_node_id = 5;
   outer_child_iframe->url = GURL(kDOMChildURL);
   outer_child_iframe->tag_name = "iframe";
   outer_child_iframe->parent = GURL(kDOMParentURL);
@@ -1148,14 +1163,14 @@
 
   ClientSafeBrowsingReportRequest::Resource* res_ad_parent =
       expected.add_resources();
-  res_ad_parent->set_id(4);
+  res_ad_parent->set_id(6);
   res_ad_parent->set_url(kReferrerURL);
   res_ad_parent->set_parent_id(5);
   res_ad_parent->set_tag_name("script");
 
   ClientSafeBrowsingReportRequest::Resource* res_sibling =
       expected.add_resources();
-  res_sibling->set_id(6);
+  res_sibling->set_id(4);
   res_sibling->set_url(kFirstRedirectURL);
   res_sibling->set_parent_id(5);
   res_sibling->set_tag_name("script");
@@ -1165,22 +1180,22 @@
   res_dom_parent->set_id(5);
   res_dom_parent->set_url(kDOMParentURL);
   res_dom_parent->add_child_ids(3);
-  res_dom_parent->add_child_ids(4);
   res_dom_parent->add_child_ids(6);
+  res_dom_parent->add_child_ids(4);
 
   HTMLElement* elem_dom_parent_script = expected.add_dom();
-  elem_dom_parent_script->set_id(3);
+  elem_dom_parent_script->set_id(4);
   elem_dom_parent_script->set_tag("SCRIPT");
   elem_dom_parent_script->set_resource_id(res_ad_parent->id());
   elem_dom_parent_script->add_attribute()->set_name("src");
   elem_dom_parent_script->mutable_attribute(0)->set_value(kReferrerURL);
   elem_dom_parent_script->add_attribute()->set_name("id");
   elem_dom_parent_script->mutable_attribute(1)->set_value("outer-sibling");
-  elem_dom_parent_script->add_child_ids(4);
   elem_dom_parent_script->add_child_ids(5);
+  elem_dom_parent_script->add_child_ids(6);
 
   HTMLElement* elem_dom_sibling_script = expected.add_dom();
-  elem_dom_sibling_script->set_id(4);
+  elem_dom_sibling_script->set_id(5);
   elem_dom_sibling_script->set_tag("SCRIPT");
   elem_dom_sibling_script->set_resource_id(res_sibling->id());
   elem_dom_sibling_script->add_attribute()->set_name("src");
@@ -1189,14 +1204,14 @@
   elem_dom_sibling_script->mutable_attribute(1)->set_value("sibling");
 
   HTMLElement* elem_dom_ad_tag_div = expected.add_dom();
-  elem_dom_ad_tag_div->set_id(5);
+  elem_dom_ad_tag_div->set_id(6);
   elem_dom_ad_tag_div->set_tag("DIV");
   elem_dom_ad_tag_div->add_attribute()->set_name("data-google-query-id");
   elem_dom_ad_tag_div->mutable_attribute(0)->set_value("ad-tag");
-  elem_dom_ad_tag_div->add_child_ids(6);
+  elem_dom_ad_tag_div->add_child_ids(7);
 
   HTMLElement* elem_dom_outer_iframe = expected.add_dom();
-  elem_dom_outer_iframe->set_id(6);
+  elem_dom_outer_iframe->set_id(7);
   elem_dom_outer_iframe->set_tag("IFRAME");
   elem_dom_outer_iframe->set_resource_id(res_dom_child->id());
   elem_dom_outer_iframe->add_attribute()->set_name("src");
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine.cc b/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
index dd77cf7..eaf02c8 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
@@ -244,8 +244,8 @@
   Reset();
 
   content::GetNetworkConnectionTracker()->RemoveNetworkConnectionObserver(this);
-  if (signin_manager_)
-    signin_manager_->RemoveObserver(this);
+  if (identity_manager_)
+    identity_manager_->RemoveObserver(this);
   if (notification_manager_)
     notification_manager_->RemoveObserver(this);
 }
@@ -706,19 +706,19 @@
   }
 }
 
-void SyncEngine::GoogleSigninFailed(const GoogleServiceAuthError& error) {
+void SyncEngine::OnPrimaryAccountSigninFailed(
+    const GoogleServiceAuthError& error) {
   Reset();
   UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
                      "Failed to sign in.");
 }
 
-void SyncEngine::GoogleSigninSucceeded(const std::string& account_id,
-                                       const std::string& username) {
+void SyncEngine::OnPrimaryAccountSet(const AccountInfo& primary_account_info) {
   Initialize();
 }
 
-void SyncEngine::GoogleSignedOut(const std::string& account_id,
-                                 const std::string& username) {
+void SyncEngine::OnPrimaryAccountCleared(
+    const AccountInfo& previous_primary_account_info) {
   Reset();
   UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
                      "User signed out.");
@@ -758,8 +758,8 @@
   DCHECK(sync_file_system_dir_.IsAbsolute());
   if (notification_manager_)
     notification_manager_->AddObserver(this);
-  if (signin_manager_)
-    signin_manager_->AddObserver(this);
+  if (identity_manager_)
+    identity_manager_->AddObserver(this);
   content::GetNetworkConnectionTracker()->AddNetworkConnectionObserver(this);
 }
 
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine.h b/chrome/browser/sync_file_system/drive_backend/sync_engine.h
index a7a4357..4632a2a 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine.h
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine.h
@@ -20,7 +20,9 @@
 #include "chrome/browser/sync_file_system/sync_direction.h"
 #include "components/drive/drive_notification_observer.h"
 #include "components/drive/service/drive_service_interface.h"
+#include "components/signin/core/browser/account_info.h"
 #include "components/signin/core/browser/signin_manager_base.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
@@ -38,10 +40,6 @@
 class ExtensionServiceInterface;
 }
 
-namespace identity {
-class IdentityManager;
-}
-
 namespace leveldb {
 class Env;
 }
@@ -68,8 +66,8 @@
       public LocalChangeProcessor,
       public drive::DriveNotificationObserver,
       public drive::DriveServiceObserver,
-      public network::NetworkConnectionTracker::NetworkConnectionObserver,
-      public SigninManagerBase::Observer {
+      public identity::IdentityManager::Observer,
+      public network::NetworkConnectionTracker::NetworkConnectionObserver {
  public:
   typedef RemoteFileSyncService::Observer SyncServiceObserver;
 
@@ -149,12 +147,12 @@
   // network::NetworkConnectionTracker::NetworkConnectionObserver overrides.
   void OnConnectionChanged(network::mojom::ConnectionType type) override;
 
-  // SigninManagerBase::Observer overrides.
-  void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
-  void GoogleSigninSucceeded(const std::string& account_id,
-                             const std::string& username) override;
-  void GoogleSignedOut(const std::string& account_id,
-                       const std::string& username) override;
+  // IdentityManager::Observer overrides.
+  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountCleared(
+      const AccountInfo& previous_primary_account_info) override;
+  void OnPrimaryAccountSigninFailed(
+      const GoogleServiceAuthError& error) override;
 
  private:
   class WorkerObserver;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 6d70ed0..b3e9f0a 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -869,6 +869,8 @@
       "layout_constants.h",
       "location_bar/location_bar.cc",
       "location_bar/location_bar.h",
+      "managed_ui.cc",
+      "managed_ui.h",
       "media_router/cast_dialog_controller.h",
       "media_router/cast_dialog_model.cc",
       "media_router/cast_dialog_model.h",
@@ -2103,17 +2105,6 @@
   if (is_win) {
     assert(toolkit_views)
     sources += [
-      "desktop_ios_promotion/desktop_ios_promotion_bubble_controller.cc",
-      "desktop_ios_promotion/desktop_ios_promotion_bubble_controller.h",
-      "desktop_ios_promotion/desktop_ios_promotion_controller.cc",
-      "desktop_ios_promotion/desktop_ios_promotion_controller.h",
-      "desktop_ios_promotion/desktop_ios_promotion_util.cc",
-      "desktop_ios_promotion/desktop_ios_promotion_util.h",
-      "desktop_ios_promotion/desktop_ios_promotion_view.h",
-      "desktop_ios_promotion/sms_service.cc",
-      "desktop_ios_promotion/sms_service.h",
-      "desktop_ios_promotion/sms_service_factory.cc",
-      "desktop_ios_promotion/sms_service_factory.h",
       "network_profile_bubble.cc",
       "network_profile_bubble.h",
       "startup/default_browser_prompt_win.cc",
@@ -2127,10 +2118,6 @@
       "views/color_chooser_win.cc",
       "views/critical_notification_bubble_view.cc",
       "views/critical_notification_bubble_view.h",
-      "views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc",
-      "views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h",
-      "views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.cc",
-      "views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.h",
       "views/frame/browser_desktop_window_tree_host.h",
       "views/frame/browser_desktop_window_tree_host_win.cc",
       "views/frame/browser_desktop_window_tree_host_win.h",
@@ -2313,7 +2300,6 @@
       "autofill/save_card_bubble_view.h",
       "autofill/save_card_ui.h",
       "bubble_anchor_util.h",
-      "desktop_ios_promotion/desktop_ios_promotion_footnote_delegate.h",
 
       # This test header is included because it contains forward declarations
       # needed for "friend" statements for use in tests.
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc
index 71ec6ec..af4af93 100644
--- a/chrome/browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc
@@ -7,47 +7,22 @@
 #include "base/memory/ptr_util.h"
 #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
-#include "chrome/browser/signin/account_tracker_service_factory.h"
-#include "chrome/browser/signin/fake_signin_manager_builder.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/account_id/account_id.h"
-#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/account_info.h"
 #include "components/user_manager/scoped_user_manager.h"
 #include "components/user_manager/user.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
 
 namespace ash {
 
 namespace {
 
-const char kTestGaiaId[] = "gaia_id";
-const char kTestAccountId[] = "test@test.com";
-
-class MultiUserTestingProfile : public TestingProfile {
- public:
-  explicit MultiUserTestingProfile(TestingProfile* profile)
-      : profile_(profile) {}
-  ~MultiUserTestingProfile() override {}
-
-  Profile* GetOriginalProfile() override { return this; }
-
-  std::string GetProfileUserName() const override {
-    const SigninManagerBase* signin_manager =
-        SigninManagerFactory::GetForProfileIfExists(profile_.get());
-    if (signin_manager)
-      return signin_manager->GetAuthenticatedAccountInfo().email;
-
-    return std::string();
-  }
-
-  TestingProfile* profile() { return profile_.get(); }
-
- private:
-  std::unique_ptr<TestingProfile> profile_;
-
-  DISALLOW_COPY_AND_ASSIGN(MultiUserTestingProfile);
-};
+const char kTestAccountEmail[] = "test@test.com";
 
 }  // namespace
 
@@ -63,50 +38,49 @@
     user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
         base::WrapUnique(fake_user_manager_));
 
-    TestingProfile::Builder builder;
-    builder.AddTestingFactory(
-        SigninManagerFactory::GetInstance(),
-        base::BindRepeating(&BuildFakeSigninManagerForTesting));
-    TestingProfile* profile = builder.Build().release();
-    profile_.reset(new MultiUserTestingProfile(profile));
+    profile_.reset(IdentityTestEnvironmentProfileAdaptor::
+                       CreateProfileForIdentityTestEnvironment()
+                           .release());
+
+    identity_test_env_adaptor_.reset(
+        new IdentityTestEnvironmentProfileAdaptor(profile_.get()));
   }
 
   void TearDown() override {
+    identity_test_env_adaptor_.reset();
     profile_.reset();
     AshTestBase::TearDown();
   }
 
-  // Add a user to account tracker service with given gaia_id and email.
-  std::string AddUserToAccountTracker(const std::string& gaia_id,
-                                      const std::string& email) {
-    AccountTrackerService* account_tracker_service =
-        AccountTrackerServiceFactory::GetForProfile(profile_->profile());
-    FakeSigninManagerBase* signin_manager = static_cast<FakeSigninManagerBase*>(
-        SigninManagerFactory::GetForProfile(profile_->profile()));
-    account_tracker_service->SeedAccountInfo(gaia_id, email);
-    const std::string id =
-        account_tracker_service->PickAccountIdForAccount(gaia_id, email);
-    signin_manager->SignIn(id);
-
-    fake_user_manager_->AddUser(multi_user_util::GetAccountIdFromEmail(id));
+  // Add a user to the identity manager with given gaia_id and email.
+  std::string AddUserAndSignIn(const std::string& email) {
+    AccountInfo account_info =
+        identity_test_env()->MakePrimaryAccountAvailable(email);
+    fake_user_manager_->AddUser(
+        multi_user_util::GetAccountIdFromEmail(account_info.account_id));
     fake_user_manager_->UserLoggedIn(
-        multi_user_util::GetAccountIdFromEmail(id),
-        chromeos::ProfileHelper::GetUserIdHashByUserIdForTesting(id),
+        multi_user_util::GetAccountIdFromEmail(account_info.account_id),
+        chromeos::ProfileHelper::GetUserIdHashByUserIdForTesting(
+            account_info.account_id),
         false /* browser_restart */, false /* is_child */);
 
-    return id;
+    return account_info.account_id;
   }
 
   void SimulateTokenRevoked(const std::string& account_id) {
-    AccountTrackerService* account_tracker_service =
-        AccountTrackerServiceFactory::GetForProfile(profile_->profile());
-    account_tracker_service->RemoveAccount(account_id);
+    identity_test_env()->RemoveRefreshTokenForAccount(account_id);
   }
 
-  MultiUserTestingProfile* profile() { return profile_.get(); }
+  TestingProfile* profile() { return profile_.get(); }
+
+  identity::IdentityTestEnvironment* identity_test_env() {
+    return identity_test_env_adaptor_->identity_test_env();
+  };
 
  private:
-  std::unique_ptr<MultiUserTestingProfile> profile_;
+  std::unique_ptr<TestingProfile> profile_;
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_adaptor_;
   // |fake_user_manager_| is owned by |user_manager_enabler_|.
   chromeos::FakeChromeUserManager* fake_user_manager_;
   std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
@@ -118,14 +92,18 @@
 // valid profile is provided, even if this profile's refresh token has been
 // revoked. (On Chrome OS we don't force to end the session in this case.)
 TEST_F(MultiUserUtilTest, ReturnValidAccountIdIfTokenRevoked) {
-  std::string id = AddUserToAccountTracker(kTestGaiaId, kTestAccountId);
-  EXPECT_EQ(kTestAccountId, profile()->GetProfileUserName());
-  EXPECT_EQ(kTestAccountId,
+  std::string account_id = AddUserAndSignIn(kTestAccountEmail);
+  identity::IdentityManager* identity_manager =
+      identity_test_env()->identity_manager();
+
+  EXPECT_TRUE(identity_manager->HasAccountWithRefreshToken(account_id));
+  EXPECT_EQ(kTestAccountEmail,
             multi_user_util::GetAccountIdFromProfile(profile()).GetUserEmail());
 
-  SimulateTokenRevoked(id);
-  EXPECT_EQ(std::string(), profile()->GetProfileUserName());
-  EXPECT_EQ(kTestAccountId,
+  SimulateTokenRevoked(account_id);
+
+  EXPECT_FALSE(identity_manager->HasAccountWithRefreshToken(account_id));
+  EXPECT_EQ(kTestAccountEmail,
             multi_user_util::GetAccountIdFromProfile(profile()).GetUserEmail());
 }
 
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 21a375e..8ac63042 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -39,11 +39,13 @@
 #include "chrome/browser/ui/extensions/application_launch.h"
 #include "chrome/browser/ui/extensions/hosted_app_browser_controller.h"
 #include "chrome/browser/ui/page_info/page_info_dialog.h"
+#include "chrome/browser/ui/singleton_tabs.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/webui/inspect_ui.h"
 #include "chrome/common/content_restriction.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/profiling.h"
+#include "chrome/common/url_constants.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/dom_distiller/core/dom_distiller_switches.h"
 #include "components/feature_engagement/buildflags.h"
@@ -677,6 +679,9 @@
     case IDC_WINDOW_PIN_TAB:
       PinTab(browser_);
       break;
+    case IDC_MANAGED_UI_HELP:
+      ShowSingletonTab(browser_, GURL(kManagedUiLearnMoreUrl));
+      break;
 
     // Hosted App commands
     case IDC_COPY_URL:
@@ -1194,6 +1199,7 @@
   command_updater_.UpdateCommandEnabled(IDC_VIEW_PASSWORDS, show_main_ui);
   command_updater_.UpdateCommandEnabled(IDC_ABOUT, show_main_ui);
   command_updater_.UpdateCommandEnabled(IDC_SHOW_APP_MENU, show_main_ui);
+  command_updater_.UpdateCommandEnabled(IDC_MANAGED_UI_HELP, true);
 
   if (base::debug::IsProfilingSupported())
     command_updater_.UpdateCommandEnabled(IDC_PROFILING_ENABLED, show_main_ui);
diff --git a/chrome/browser/ui/desktop_ios_promotion/DEPS b/chrome/browser/ui/desktop_ios_promotion/DEPS
deleted file mode 100644
index e8948bf..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/DEPS
+++ /dev/null
@@ -1,3 +0,0 @@
-include_rules = [
-  '+third_party/libphonenumber/phonenumber_api.h',
-]
diff --git a/chrome/browser/ui/desktop_ios_promotion/OWNERS b/chrome/browser/ui/desktop_ios_promotion/OWNERS
deleted file mode 100644
index e5a1912..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-pkl@chromium.org
-justincohen@chromium.org
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.cc
deleted file mode 100644
index f8cf39ec..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.h"
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_view.h"
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h"
-#include "components/prefs/pref_service.h"
-
-DesktopIOSPromotionBubbleController::DesktopIOSPromotionBubbleController(
-    Profile* profile,
-    DesktopIOSPromotionView* promotion_view,
-    desktop_ios_promotion::PromotionEntryPoint entry_point)
-    : DesktopIOSPromotionController(profile, entry_point),
-      sms_service_(SMSServiceFactory::GetForProfile(profile)),
-      promotion_view_(promotion_view),
-      weak_ptr_factory_(this) {
-  sms_service_->QueryPhoneNumber(
-      base::Bind(&DesktopIOSPromotionBubbleController::OnGotPhoneNumber,
-                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-DesktopIOSPromotionBubbleController::~DesktopIOSPromotionBubbleController() =
-    default;
-
-void DesktopIOSPromotionBubbleController::OnSendSMSClicked() {
-  sms_service_->SendSMS(
-      desktop_ios_promotion::GetSMSID(),
-      base::Bind(&DesktopIOSPromotionBubbleController::OnSendSMS,
-                 weak_ptr_factory_.GetWeakPtr()));
-
-  // Update Profile prefs.
-  profile_prefs()->SetInteger(prefs::kIOSPromotionSMSEntryPoint,
-                              static_cast<int>(entry_point()));
-
-  // Update dismissal reason.
-  SetDismissalReason(desktop_ios_promotion::PromotionDismissalReason::SEND_SMS);
-}
-
-void DesktopIOSPromotionBubbleController::OnNoThanksClicked() {
-  if (entry_point() !=
-      desktop_ios_promotion::PromotionEntryPoint::FOOTNOTE_FOLLOWUP_BUBBLE) {
-    PrefService* local_state = g_browser_process->local_state();
-    local_state->SetBoolean(
-        desktop_ios_promotion::kEntryPointLocalPrefs
-            [static_cast<int>(entry_point())][static_cast<int>(
-                desktop_ios_promotion::EntryPointLocalPrefType::DISMISSED)],
-        true);
-  }
-  SetDismissalReason(
-      desktop_ios_promotion::PromotionDismissalReason::NO_THANKS);
-}
-
-std::string DesktopIOSPromotionBubbleController::GetUsersRecoveryPhoneNumber() {
-  return recovery_number_;
-}
-
-void DesktopIOSPromotionBubbleController::OnGotPhoneNumber(
-    SMSService::Request* request,
-    bool success,
-    const std::string& number) {
-  DCHECK(promotion_view_);
-  if (success) {
-    recovery_number_ = desktop_ios_promotion::FormatPhoneNumber(number);
-    promotion_view_->UpdateRecoveryPhoneLabel();
-  }
-  UMA_HISTOGRAM_BOOLEAN("DesktopIOSPromotion.QueryPhoneNumberSucceeded",
-                        success);
-}
-
-void DesktopIOSPromotionBubbleController::OnSendSMS(
-    SMSService::Request* request,
-    bool success,
-    const std::string& number) {
-  UMA_HISTOGRAM_BOOLEAN("DesktopIOSPromotion.SendSMSSucceeded", success);
-}
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.h b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.h
deleted file mode 100644
index f43fd88..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_BUBBLE_CONTROLLER_H_
-#define CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_BUBBLE_CONTROLLER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.h"
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service.h"
-
-namespace desktop_ios_promotion {
-enum class PromotionEntryPoint;
-}
-
-class DesktopIOSPromotionView;
-
-// This class provides data to the Desktop to mobile promotion and control the
-// promotion actions.
-class DesktopIOSPromotionBubbleController
-    : public DesktopIOSPromotionController {
- public:
-  // Must be instantiated on the UI thread.
-  DesktopIOSPromotionBubbleController(
-      Profile* profile,
-      DesktopIOSPromotionView* promotion_view,
-      desktop_ios_promotion::PromotionEntryPoint entry_point);
-  ~DesktopIOSPromotionBubbleController();
-
-  // Called by the view code when "Send SMS" button is clicked by the user.
-  void OnSendSMSClicked();
-
-  // Called by the view code when "No Thanks" button is clicked by the user.
-  void OnNoThanksClicked();
-
-  // Returns the Recovery phone number, returns empy string if the number is not
-  // set.
-  std::string GetUsersRecoveryPhoneNumber();
-
- private:
-  // Updates the user's recovery phone number once the sms_service phone query
-  // returns a response.
-  void OnGotPhoneNumber(SMSService::Request* request,
-                        bool success,
-                        const std::string& number);
-
-  // Callback that logs the result when sms_service send sms returns a response.
-  void OnSendSMS(SMSService::Request* request,
-                 bool success,
-                 const std::string& number);
-
-  // Service used to send SMS to the user recovery phone number.
-  SMSService* sms_service_;
-  // User's recovery phone number, this is updated by the sms_service.
-  std::string recovery_number_;
-  // A Weak pointer to the promotion view.
-  DesktopIOSPromotionView* promotion_view_;
-
-  base::WeakPtrFactory<DesktopIOSPromotionBubbleController> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopIOSPromotionBubbleController);
-};
-
-#endif  // CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc
deleted file mode 100644
index 3e0b147..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.h"
-
-#include "base/bind.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/test_timeouts.h"
-#include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service.h"
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_notifier_impl.h"
-#include "components/prefs/pref_service.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/sync_preferences/pref_service_mock_factory.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-
-namespace {
-
-class FakeSMSService : public SMSService {
- public:
-  FakeSMSService() : SMSService(nullptr, nullptr) {}
-  ~FakeSMSService() override {}
-  MOCK_METHOD1(QueryPhoneNumber, void(const PhoneNumberCallback&));
-  MOCK_METHOD2(SendSMS,
-               void(const std::string&,
-                    const SMSService::PhoneNumberCallback&));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(FakeSMSService);
-};
-
-std::unique_ptr<KeyedService> BuildFakeSMSService(
-    content::BrowserContext* profile) {
-  return std::make_unique<FakeSMSService>();
-}
-
-}  // namespace
-
-class DesktopIOSPromotionBubbleControllerTest : public testing::Test {
- public:
-  DesktopIOSPromotionBubbleControllerTest() {}
-  ~DesktopIOSPromotionBubbleControllerTest() override {}
-
-  void SetUp() override {
-    pref_service_ =
-        std::make_unique<sync_preferences::TestingPrefServiceSyncable>(
-            new TestingPrefStore(), new TestingPrefStore(),
-            new TestingPrefStore(), new TestingPrefStore(),
-            new user_prefs::PrefRegistrySyncable(), new PrefNotifierImpl());
-    RegisterUserProfilePrefs(pref_service_->registry());
-    TestingProfile::Builder builder;
-    builder.SetPrefService(std::move(pref_service_));
-    builder.AddTestingFactory(SMSServiceFactory::GetInstance(),
-                              base::BindRepeating(&BuildFakeSMSService));
-    profile_ = builder.Build();
-    local_state_ = std::make_unique<TestingPrefServiceSimple>();
-    TestingBrowserProcess::GetGlobal()->SetLocalState(local_state_.get());
-    desktop_ios_promotion::RegisterLocalPrefs(local_state_->registry());
-    sms_service_ = static_cast<FakeSMSService*>(
-        SMSServiceFactory::GetForProfile(profile_.get()));
-  }
-
-  void TearDown() override {
-    TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
-    local_state_.reset();
-    controller_.reset();
-    profile_.reset();
-  }
-
-  void InitController(desktop_ios_promotion::PromotionEntryPoint entry_point) {
-    ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(sms_service_));
-    EXPECT_CALL(*sms_service_, QueryPhoneNumber(_));
-    controller_ = std::make_unique<DesktopIOSPromotionBubbleController>(
-        profile_.get(), nullptr, entry_point);
-  }
-
-  PrefService* prefs() { return profile_->GetPrefs(); }
-
- protected:
-  FakeSMSService* sms_service_ = nullptr;
-  content::TestBrowserThreadBundle thread_bundle_;
-  std::unique_ptr<TestingPrefServiceSimple> local_state_;
-  std::unique_ptr<DesktopIOSPromotionBubbleController> controller_;
-  std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> pref_service_;
-  std::unique_ptr<TestingProfile> profile_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DesktopIOSPromotionBubbleControllerTest);
-};
-
-TEST_F(DesktopIOSPromotionBubbleControllerTest, ClickSendSMS) {
-  InitController(
-      desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE);
-  EXPECT_CALL(*sms_service_, SendSMS(_, _));
-  controller_->OnSendSMSClicked();
-  EXPECT_EQ(desktop_ios_promotion::PromotionDismissalReason::SEND_SMS,
-            controller_->dismissal_reason());
-  EXPECT_EQ(1, prefs()->GetInteger(prefs::kIOSPromotionSMSEntryPoint));
-}
-
-TEST_F(DesktopIOSPromotionBubbleControllerTest, PromotionShown) {
-  const char kHistogram[] = "DesktopIOSPromotion.ImpressionFromEntryPoint";
-  base::HistogramTester histograms;
-  desktop_ios_promotion::PromotionEntryPoint entry_point =
-      desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE;
-  InitController(entry_point);
-
-  EXPECT_EQ(0, local_state_->GetInteger(
-                   prefs::kNumberSavePasswordsBubbleIOSPromoShown));
-  EXPECT_EQ(0, prefs()->GetInteger(prefs::kIOSPromotionShownEntryPoints));
-  controller_->OnPromotionShown();
-  // Impressions increase.
-  EXPECT_EQ(1, local_state_->GetInteger(
-                   prefs::kNumberSavePasswordsBubbleIOSPromoShown));
-  double lst_impr = prefs()->GetDouble(prefs::kIOSPromotionLastImpression);
-  // last impression time updated correctly.
-  EXPECT_LT(base::Time::Now() - base::Time::FromDoubleT(lst_impr),
-            TestTimeouts::action_timeout());
-  // We reset the SMS entry point as the user is is still eligible and haven't
-  // seen the promotion for 7 days.
-  EXPECT_EQ(0, prefs()->GetInteger(prefs::kIOSPromotionSMSEntryPoint));
-  // Check if the impression is logged to histograms.
-  histograms.ExpectUniqueSample(kHistogram, static_cast<int>(entry_point), 1);
-  // Check if the bit for this entry point was set in profile prefs.
-  EXPECT_EQ(1 << static_cast<int>(entry_point),
-            prefs()->GetInteger(prefs::kIOSPromotionShownEntryPoints));
-  int shown_promotions =
-      prefs()->GetInteger(prefs::kIOSPromotionShownEntryPoints);
-
-  controller_->OnPromotionShown();
-  EXPECT_EQ(2, local_state_->GetInteger(
-                   prefs::kNumberSavePasswordsBubbleIOSPromoShown));
-  histograms.ExpectUniqueSample(kHistogram, static_cast<int>(entry_point), 2);
-
-  // Check different entry point.
-  entry_point = desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_BUBBLE;
-  InitController(entry_point);
-  controller_->OnPromotionShown();
-  histograms.ExpectBucketCount(kHistogram, static_cast<int>(entry_point), 1);
-  histograms.ExpectTotalCount(kHistogram, 3);
-
-  // Check if the bit for this entry point was set in profile prefs.
-  EXPECT_EQ(shown_promotions | (1 << static_cast<int>(entry_point)),
-            prefs()->GetInteger(prefs::kIOSPromotionShownEntryPoints));
-}
-
-TEST_F(DesktopIOSPromotionBubbleControllerTest, ClickNoThanks) {
-  InitController(
-      desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE);
-  controller_->OnNoThanksClicked();
-  EXPECT_EQ(desktop_ios_promotion::PromotionDismissalReason::NO_THANKS,
-            controller_->dismissal_reason());
-  EXPECT_TRUE(
-      local_state_->GetBoolean(prefs::kSavePasswordsBubbleIOSPromoDismissed));
-}
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.cc
deleted file mode 100644
index 58a6d73..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.h"
-
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/time/time.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-#include "chrome/common/chrome_features.h"
-#include "components/prefs/pref_service.h"
-
-DesktopIOSPromotionController::DesktopIOSPromotionController(
-    Profile* profile,
-    desktop_ios_promotion::PromotionEntryPoint entry_point)
-    : profile_prefs_(profile->GetPrefs()),
-      entry_point_(entry_point),
-      dismissal_reason_(
-          desktop_ios_promotion::PromotionDismissalReason::FOCUS_LOST) {}
-
-DesktopIOSPromotionController::~DesktopIOSPromotionController() {
-  desktop_ios_promotion::LogDismissalReason(dismissal_reason_, entry_point_);
-}
-
-void DesktopIOSPromotionController::OnPromotionShown() {
-  UMA_HISTOGRAM_ENUMERATION(
-      "DesktopIOSPromotion.ImpressionFromEntryPoint",
-      static_cast<int>(entry_point_),
-      static_cast<int>(
-          desktop_ios_promotion::PromotionEntryPoint::ENTRY_POINT_MAX_VALUE));
-
-  if (entry_point_ ==
-      desktop_ios_promotion::PromotionEntryPoint::FOOTNOTE_FOLLOWUP_BUBBLE) {
-    // We don't want to update sync with the impression of this entrypoint.
-    return;
-  }
-  // update the impressions count.
-  PrefService* local_state = g_browser_process->local_state();
-  int impressions = local_state->GetInteger(
-      desktop_ios_promotion::kEntryPointLocalPrefs
-          [static_cast<int>(entry_point_)][static_cast<int>(
-              desktop_ios_promotion::EntryPointLocalPrefType::IMPRESSIONS)]);
-  impressions++;
-  local_state->SetInteger(
-      desktop_ios_promotion::kEntryPointLocalPrefs
-          [static_cast<int>(entry_point_)][static_cast<int>(
-              desktop_ios_promotion::EntryPointLocalPrefType::IMPRESSIONS)],
-      impressions);
-
-  // Update synced profile prefs.
-  int shown_entrypoints =
-      profile_prefs_->GetInteger(prefs::kIOSPromotionShownEntryPoints);
-  shown_entrypoints |= 1 << static_cast<int>(entry_point_);
-  profile_prefs_->SetInteger(prefs::kIOSPromotionShownEntryPoints,
-                             shown_entrypoints);
-
-  // If the promo is seen then it means the SMS was not sent on the last 7 days,
-  // reset the pref.
-  profile_prefs_->SetInteger(prefs::kIOSPromotionSMSEntryPoint, 0);
-
-  double last_impression = base::Time::NowFromSystemTime().ToDoubleT();
-  profile_prefs_->SetDouble(prefs::kIOSPromotionLastImpression,
-                            last_impression);
-
-  // If the variation id paramater is set on the finch experiement, set this
-  // variation id to chrome sync pref to be accessed from iOS side.
-  int variation_id = base::GetFieldTrialParamByFeatureAsInt(
-      features::kDesktopIOSPromotion, "promo_variation_id", 0);
-  if (variation_id)
-    profile_prefs_->SetInteger(prefs::kIOSPromotionVariationId, variation_id);
-}
-
-void DesktopIOSPromotionController::OnLearnMoreLinkClicked() {
-  dismissal_reason_ =
-      desktop_ios_promotion::PromotionDismissalReason::LEARN_MORE;
-}
-
-void DesktopIOSPromotionController::SetDismissalReason(
-    desktop_ios_promotion::PromotionDismissalReason reason) {
-  dismissal_reason_ = reason;
-}
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.h b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.h
deleted file mode 100644
index a63697b0..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_CONTROLLER_H_
-#define CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_CONTROLLER_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-
-namespace desktop_ios_promotion {
-enum class PromotionEntryPoint;
-enum class PromotionDismissalReason;
-}
-
-class Profile;
-class PrefService;
-
-// This class provides data to the Desktop to mobile promotion and control the
-// promotion actions.
-class DesktopIOSPromotionController {
- public:
-  // Must be instantiated on the UI thread.
-  DesktopIOSPromotionController(
-      Profile* profile,
-      desktop_ios_promotion::PromotionEntryPoint entry_point);
-  ~DesktopIOSPromotionController();
-
-  // Returns the current promotion entry point.
-  desktop_ios_promotion::PromotionEntryPoint entry_point() const {
-    return entry_point_;
-  }
-
-  desktop_ios_promotion::PromotionDismissalReason dismissal_reason() const {
-    return dismissal_reason_;
-  }
-
-  // Called by the view code when the promotion is ready to show.
-  void OnPromotionShown();
-
-  // Called by the view when link to detailed promo is clicked by the user.
-  void OnLearnMoreLinkClicked();
-
- protected:
-  void SetDismissalReason(
-      desktop_ios_promotion::PromotionDismissalReason reason);
-
-  PrefService* profile_prefs() { return profile_prefs_; }
-
- private:
-  PrefService* profile_prefs_;
-  const desktop_ios_promotion::PromotionEntryPoint entry_point_;
-
-  // Track the action that is responsible for the promotion Dismissal.
-  desktop_ios_promotion::PromotionDismissalReason dismissal_reason_;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopIOSPromotionController);
-};
-
-#endif  // CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_CONTROLLER_H_
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_footnote_delegate.h b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_footnote_delegate.h
deleted file mode 100644
index 8b10e81..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_footnote_delegate.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_FOOTNOTE_DELEGATE_H_
-#define CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_FOOTNOTE_DELEGATE_H_
-
-class DesktopIOSPromotionFootnoteDelegate {
- public:
-  virtual ~DesktopIOSPromotionFootnoteDelegate() {}
-  virtual void OnIOSPromotionFootnoteLinkClicked() = 0;
-};
-
-#endif  // CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_FOOTNOTE_DELEGATE_H_
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.cc
deleted file mode 100644
index 63e7925..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.cc
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-
-#include "base/command_line.h"
-#include "base/i18n/rtl.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/app/vector_icons/vector_icons.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profiles_state.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/common/chrome_switches.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_error_controller.h"
-#include "third_party/libphonenumber/phonenumber_api.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/color_utils.h"
-#include "ui/gfx/paint_vector_icon.h"
-#include "ui/native_theme/native_theme.h"
-
-using i18n::phonenumbers::PhoneNumber;
-using i18n::phonenumbers::PhoneNumberUtil;
-
-namespace desktop_ios_promotion {
-
-// Default Impression cap. for each entry point.
-const int kEntryPointImpressionCap[] = {0, 2, 2, 5, 10};
-
-const char* kEntrypointHistogramPrefix[] = {
-    "",  // Padding as PromotionEntryPoints values starts from 1.
-    "SavePasswordsNewBubble",
-    "BookmarksNewBubble",
-    "BookmarksFootNote",
-    "HistoryPage",
-};
-
-// Entry point string names, used to check which entry point is targeted from
-// finch parameters.
-const char* kPromotionEntryPointNames[] = {
-    "", "SavePasswordsBubblePromotion", "BookmarksBubblePromotion",
-    "BookmarksFootnotePromotion", "HistoryPagePromotion"};
-
-// Text used on the promotion bubble body when the phone number is present,
-// this array is indexed by the text version specified by body_text_id Finch
-// parameter.
-const int kBubbleBodyTextNoPhoneNumberId[2] = {
-    IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TEXT,
-    IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TEXT_V2};
-
-// Text used on the promotion bubble body when the phone number is not present,
-// this array is indexed by the text version specified by body_text_id Finch
-// parameter.
-const int kBubbleBodyTextWithPhoneNumberId[2] = {
-    IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TEXT_WITH_PHONE_NUMBER,
-    IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TEXT_WITH_PHONE_NUMBER_V2};
-
-// Text used on the promotion bubble title, The first dimension is the entry
-// point, and the second is the text version specified by title_text_id Finch
-// parameter.
-const int kBubbleTitleTextId[3][3] = {
-    {0, 0, 0},  // Padding as PromotionEntryPoints values starts from 1.
-    {IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TITLE,
-     IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TITLE_V2,
-     IDS_PASSWORD_MANAGER_DESKTOP_TO_IOS_PROMO_TITLE_V3},
-    {IDS_BOOKMARK_BUBBLE_DESKTOP_TO_IOS_PROMO_TITLE,
-     IDS_BOOKMARK_BUBBLE_DESKTOP_TO_IOS_PROMO_TITLE_V2,
-     IDS_BOOKMARK_BUBBLE_DESKTOP_TO_IOS_PROMO_TITLE_V3}};
-
-bool IsEligibleForIOSPromotion(
-    Profile* profile,
-    desktop_ios_promotion::PromotionEntryPoint entry_point) {
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kForceDesktopIOSPromotion)) {
-    return true;
-  }
-
-  // Don't show promotion if there has been authentication error, because this
-  // will prevent the recovery phone number from showing.
-  const SigninErrorController* signin_error_controller =
-      profiles::GetSigninErrorController(profile);
-  if (signin_error_controller && signin_error_controller->HasError())
-    return false;
-
-  // Promotion should only show for english locale.
-  PrefService* local_state = g_browser_process->local_state();
-  std::string locale = base::i18n::GetConfiguredLocale();
-  if (locale != "en-US" && locale != "en-CA")
-    return false;
-  if (!base::FeatureList::IsEnabled(features::kDesktopIOSPromotion) ||
-      !profile->IsSyncAllowed()) {
-    return false;
-  }
-
-  // Check if the specific entrypoint is enabled by Finch.
-  std::string targeted_entry_point = base::GetFieldTrialParamValueByFeature(
-      features::kDesktopIOSPromotion, "entry_point");
-  if (targeted_entry_point !=
-      kPromotionEntryPointNames[static_cast<int>(entry_point)])
-    return false;
-  bool is_dismissed = local_state->GetBoolean(
-      kEntryPointLocalPrefs[static_cast<int>(entry_point)][1]);
-  int show_count = local_state->GetInteger(
-      kEntryPointLocalPrefs[static_cast<int>(entry_point)][0]);
-
-  // Get the impression cap. from Finch, if exists override the value from the
-  // kEntryPointImpressionCap array.
-  int impression_cap = base::GetFieldTrialParamByFeatureAsInt(
-      features::kDesktopIOSPromotion, "max_views",
-      kEntryPointImpressionCap[static_cast<int>(entry_point)]);
-  if (is_dismissed || show_count >= impression_cap)
-    return false;
-
-  PrefService* prefs = profile->GetPrefs();
-  // Don't show the promotion if the user have used any entry point to recieve
-  // SMS on the last 7 days.
-  double last_impression = prefs->GetDouble(prefs::kIOSPromotionLastImpression);
-  int sms_entrypoint = prefs->GetInteger(prefs::kIOSPromotionSMSEntryPoint);
-  base::TimeDelta delta =
-      base::Time::Now() - base::Time::FromDoubleT(last_impression);
-  if (delta.InDays() <= 7 && sms_entrypoint != 0)
-    return false;
-
-  bool is_user_eligible = prefs->GetBoolean(prefs::kIOSPromotionEligible);
-  bool did_promo_done_before = prefs->GetBoolean(prefs::kIOSPromotionDone);
-  return is_user_eligible && !did_promo_done_before;
-}
-
-gfx::ImageSkia GetPromoImage(SkColor color) {
-  gfx::ImageSkia icon = gfx::CreateVectorIcon(
-      kOpenInPhoneIcon, color_utils::DeriveDefaultIconColor(color));
-  return icon;
-}
-
-base::string16 GetPromoText(
-    desktop_ios_promotion::PromotionEntryPoint entry_point,
-    const std::string& phone_number) {
-  int text_id_from_finch = base::GetFieldTrialParamByFeatureAsInt(
-      features::kDesktopIOSPromotion, "body_text_id", 0);
-  int body_text_i10_id = kBubbleBodyTextWithPhoneNumberId[text_id_from_finch];
-  if (phone_number.empty()) {
-    body_text_i10_id = kBubbleBodyTextNoPhoneNumberId[text_id_from_finch];
-    return l10n_util::GetStringUTF16(body_text_i10_id)
-        .append(base::string16(13, ' '));
-  }
-  return l10n_util::GetStringFUTF16(body_text_i10_id,
-                                    base::UTF8ToUTF16(phone_number));
-}
-
-base::string16 GetPromoTitle(
-    desktop_ios_promotion::PromotionEntryPoint entry_point) {
-  int text_id_from_finch = base::GetFieldTrialParamByFeatureAsInt(
-      features::kDesktopIOSPromotion, "title_text_id", 0);
-  return l10n_util::GetStringUTF16(
-      kBubbleTitleTextId[static_cast<int>(entry_point)][text_id_from_finch]);
-}
-
-std::string GetSMSID() {
-  const std::string default_sms_message_id = "19001507";
-  // Get the SMS message id from the finch group.
-  std::string finch_sms_id = base::GetFieldTrialParamValueByFeature(
-      features::kDesktopIOSPromotion, "sms_id");
-  return finch_sms_id.empty() ? default_sms_message_id : finch_sms_id;
-}
-
-std::string FormatPhoneNumber(const std::string& number) {
-  PhoneNumberUtil* phone_util = PhoneNumberUtil::GetInstance();
-  PhoneNumber number_object;
-  if (phone_util->Parse(number, std::string(), &number_object) !=
-      PhoneNumberUtil::NO_PARSING_ERROR) {
-    return number;
-  }
-  std::string formatted_number;
-  phone_util->Format(number_object,
-                     PhoneNumberUtil::PhoneNumberFormat::NATIONAL,
-                     &formatted_number);
-  return formatted_number;
-}
-
-void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterBooleanPref(
-      prefs::kIOSPromotionEligible, false,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kIOSPromotionDone, false,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterIntegerPref(
-      prefs::kIOSPromotionSMSEntryPoint, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterIntegerPref(
-      prefs::kIOSPromotionShownEntryPoints, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterDoublePref(
-      prefs::kIOSPromotionLastImpression, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterIntegerPref(
-      prefs::kIOSPromotionVariationId, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-}
-
-void RegisterLocalPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterIntegerPref(prefs::kNumberSavePasswordsBubbleIOSPromoShown,
-                                0);
-  registry->RegisterBooleanPref(prefs::kSavePasswordsBubbleIOSPromoDismissed,
-                                false);
-  registry->RegisterIntegerPref(prefs::kNumberBookmarksBubbleIOSPromoShown, 0);
-  registry->RegisterBooleanPref(prefs::kBookmarksBubbleIOSPromoDismissed,
-                                false);
-  registry->RegisterIntegerPref(prefs::kNumberBookmarksFootNoteIOSPromoShown,
-                                0);
-  registry->RegisterBooleanPref(prefs::kBookmarksFootNoteIOSPromoDismissed,
-                                false);
-  registry->RegisterIntegerPref(prefs::kNumberHistoryPageIOSPromoShown, 0);
-  registry->RegisterBooleanPref(prefs::kHistoryPageIOSPromoDismissed, false);
-}
-
-void LogDismissalReason(PromotionDismissalReason reason,
-                        PromotionEntryPoint entry_point) {
-  constexpr int dismissal_reason_min =
-      static_cast<int>(PromotionDismissalReason::FOCUS_LOST);
-  base::Histogram::FactoryGet(
-      base::StringPrintf(
-          "DesktopIOSPromotion.%s.DismissalReason",
-          desktop_ios_promotion::kEntrypointHistogramPrefix[static_cast<int>(
-              entry_point)]),
-      dismissal_reason_min,
-      static_cast<int>(PromotionDismissalReason::DISMISSAL_REASON_MAX_VALUE),
-      static_cast<int>(PromotionDismissalReason::DISMISSAL_REASON_MAX_VALUE) +
-          1,
-      base::HistogramBase::kUmaTargetedHistogramFlag)
-      ->Add(static_cast<int>(reason));
-}
-
-}  // namespace desktop_ios_promotion
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h
deleted file mode 100644
index 7dcf7f29..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_UTIL_H_
-#define CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_UTIL_H_
-
-#include "base/macros.h"
-#include "chrome/common/pref_names.h"
-#include "ui/gfx/image/image_skia.h"
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-class PrefRegistrySimple;
-class Profile;
-
-namespace desktop_ios_promotion {
-
-// Represents the reason that the Desktop to iOS promotion was dismissed for.
-// These values are written to logs. New values can be added, but existing
-// values must never be reordered or deleted and reused.
-enum class PromotionDismissalReason {
-  // Focus lost is the default reason and represents no interaction.
-  FOCUS_LOST = 0,
-  NO_THANKS = 1,
-  CLOSE_BUTTON = 2,
-  SEND_SMS = 3,
-  LEARN_MORE = 4,
-  // Used for histograms logging.
-  DISMISSAL_REASON_MAX_VALUE = 5
-};
-
-enum class EntryPointLocalPrefType { IMPRESSIONS = 0, DISMISSED = 1 };
-
-// The place where the promotion appeared.
-// Intentionally skipped the = 0 value, as it represents the defaults on the
-// prefs (eg. User didn't recieve SMS).
-// These values are written to logs. New values can be added, but existing
-// values must never be reordered or deleted and reused.
-enum class PromotionEntryPoint {
-  SAVE_PASSWORD_BUBBLE = 1,
-  BOOKMARKS_BUBBLE = 2,
-  BOOKMARKS_FOOTNOTE = 3,
-  HISTORY_PAGE = 4,
-  FOOTNOTE_FOLLOWUP_BUBBLE = 5,
-  // Used for histograms logging.
-  ENTRY_POINT_MAX_VALUE = 6
-};
-
-// Entry points local prefs, each entry point has a preference for impressions
-// count and a preference for whether user dismissed it or not.
-// Do not change the order of this array, as it's indexed using
-// desktop_ios_promotion::PromotionEntryPoint.
-const char* const kEntryPointLocalPrefs[5][2] = {
-    // The first emtpy value is for padding as PromotionEntryPoints enum starts
-    // from 1.
-    {"", ""},
-    {prefs::kNumberSavePasswordsBubbleIOSPromoShown,
-     prefs::kSavePasswordsBubbleIOSPromoDismissed},
-    {prefs::kNumberBookmarksBubbleIOSPromoShown,
-     prefs::kBookmarksBubbleIOSPromoDismissed},
-    {prefs::kNumberBookmarksFootNoteIOSPromoShown,
-     prefs::kBookmarksFootNoteIOSPromoDismissed},
-    {prefs::kNumberHistoryPageIOSPromoShown,
-     prefs::kHistoryPageIOSPromoDismissed}};
-
-bool IsEligibleForIOSPromotion(Profile* profile,
-                               PromotionEntryPoint entry_point);
-
-// Returns the SMS ID to be used with send SMS API call.
-std::string GetSMSID();
-
-// Returns the Promotion text based on the promotion entry point, Finch
-// parameters and phone number presence.
-base::string16 GetPromoText(PromotionEntryPoint entry_point,
-                            const std::string& phone_number = std::string());
-
-// Returns the Promotion title based on the promotion entry point and finch
-// parameters.
-base::string16 GetPromoTitle(PromotionEntryPoint entry_point);
-
-gfx::ImageSkia GetPromoImage(SkColor color);
-
-std::string FormatPhoneNumber(const std::string& number);
-
-// Register all Priority Sync preferences.
-void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
-
-// Register all local only Preferences.
-void RegisterLocalPrefs(PrefRegistrySimple* registry);
-
-// Log promotion dismissal reason for entry points.
-void LogDismissalReason(PromotionDismissalReason reason,
-                        PromotionEntryPoint entry_point);
-
-}  // namespace desktop_ios_promotion
-
-#endif  // CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_UTIL_H_
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc
deleted file mode 100644
index 1ead757..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/i18n/rtl.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/prefs/browser_prefs.h"
-#include "chrome/browser/signin/signin_error_controller_factory.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "chrome/browser/sync/profile_sync_test_util.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/test/base/scoped_testing_local_state.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/browser_sync/profile_sync_service.h"
-#include "components/browser_sync/test_profile_sync_service.h"
-#include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/fake_auth_status_provider.h"
-#include "components/sync/driver/fake_sync_service.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "services/identity/public/cpp/identity_test_environment.h"
-
-namespace {
-
-class TestSyncService : public browser_sync::TestProfileSyncService {
- public:
-  explicit TestSyncService(Profile* profile)
-      : browser_sync::TestProfileSyncService(
-            CreateProfileSyncServiceParamsForTest(profile)) {}
-
-  int GetDisableReasons() const override { return disable_reasons_; }
-
-  void SetDisableReasons(int disable_reasons) {
-    disable_reasons_ = disable_reasons;
-  }
-
- private:
-  int disable_reasons_ = DISABLE_REASON_NONE;
-  DISALLOW_COPY_AND_ASSIGN(TestSyncService);
-};
-
-std::unique_ptr<KeyedService> BuildFakeSyncService(
-    content::BrowserContext* context) {
-  return std::make_unique<TestSyncService>(
-      static_cast<TestingProfile*>(context));
-}
-
-constexpr int kSMSEntrypointSavePasswordBubble =
-    1 << static_cast<int>(
-        desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE);
-
-constexpr int kSMSEntrypointBookmarksBubble =
-    1 << static_cast<int>(
-        desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_BUBBLE);
-
-}  // namespace
-
-class DesktopIOSPromotionUtilTest : public testing::Test {
- public:
-  DesktopIOSPromotionUtilTest()
-      : local_state_(TestingBrowserProcess::GetGlobal()) {}
-  ~DesktopIOSPromotionUtilTest() override {}
-
-  void SetUp() override {
-    auto pref_service =
-        std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
-    RegisterUserProfilePrefs(pref_service->registry());
-    TestingProfile::Builder profile_builder;
-    profile_builder.SetPrefService(std::move(pref_service));
-    profile_builder.AddTestingFactory(
-        ProfileSyncServiceFactory::GetInstance(),
-        base::BindRepeating(&BuildFakeSyncService));
-    profile_ = profile_builder.Build();
-    sync_service_ = static_cast<TestSyncService*>(
-        ProfileSyncServiceFactory::GetForProfile(profile_.get()));
-    identity_test_environment_.MakePrimaryAccountAvailable("test@gmail.com");
-  }
-
-  void TearDown() override {
-    profile_.reset();
-  }
-
-  PrefService* local_state() { return local_state_.Get(); }
-
-  TestSyncService* sync_service() { return sync_service_; }
-
-  PrefService* prefs() { return profile_->GetPrefs(); }
-
-  Profile* profile() { return profile_.get(); }
-
-  std::string account_id() {
-    return identity_test_environment_.identity_manager()
-        ->GetPrimaryAccountInfo()
-        .account_id;
-  }
-
-  double GetDoubleNDayOldDate(int days) {
-    base::Time time_result =
-        base::Time::NowFromSystemTime() - base::TimeDelta::FromDays(days);
-    return time_result.ToDoubleT();
-  }
-
- private:
-  TestSyncService* sync_service_ = nullptr;
-  content::TestBrowserThreadBundle thread_bundle_;
-  identity::IdentityTestEnvironment identity_test_environment_;
-  ScopedTestingLocalState local_state_;
-  std::unique_ptr<TestingProfile> profile_;
-  DISALLOW_COPY_AND_ASSIGN(DesktopIOSPromotionUtilTest);
-};
-
-TEST_F(DesktopIOSPromotionUtilTest, IsEligibleForIOSPromotionForSavePassword) {
-  desktop_ios_promotion::PromotionEntryPoint entry_point =
-      desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE;
-  // By default the promo is off.
-  EXPECT_FALSE(
-      desktop_ios_promotion::IsEligibleForIOSPromotion(profile(), entry_point));
-
-  // Enable the promotion and assign the entry_point finch parameter.
-  base::FieldTrialList field_trial_list(nullptr);
-  base::test::ScopedFeatureList scoped_feature_list;
-  std::map<std::string, std::string> params;
-  params["entry_point"] = "SavePasswordsBubblePromotion";
-  base::AssociateFieldTrialParams("DesktopIOSPromotion",
-                                  "SavePasswordBubblePromotion", params);
-  scoped_refptr<base::FieldTrial> trial(
-      base::FieldTrialList::FactoryGetFieldTrial(
-          "DesktopIOSPromotion", 100, "SavePasswordBubblePromotion",
-          base::FieldTrialList::kNoExpirationYear, 1, 1,
-          base::FieldTrial::SESSION_RANDOMIZED, nullptr));
-  std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
-  feature_list->RegisterFieldTrialOverride(
-      features::kDesktopIOSPromotion.name,
-      base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
-  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
-
-  std::string locales[] = {"en-US", "en-CA", "en-AU", "es-US"};
-  constexpr struct {
-    bool is_sync_allowed;
-    bool signin_error;
-    int locale_index;
-    bool is_dismissed;
-    int show_count;
-    int last_impression_days;
-    int sms_entrypoint;
-    bool is_user_eligible;
-    bool promo_done;
-    bool result;
-  } kTestData[] = {
-      // {sync allowed, signin error exist, locale, dismissed before, impression
-      // count, seen days
-      // ago, bitmask with entry points seen, is user eligible, flow was
-      // completed before, expected result }
-      {false, false, 0, false, 0, 1, 0, false, false, false},
-      {false, false, 1, false, 0, 3, 0, true, false, false},
-      {true, false, 3, false, 0, 4, 0, true, false, false},
-      {true, false, 2, false, 0, 10, 0, true, false, false},
-      {true, false, 0, true, 1, 3, 0, true, false, false},
-      {true, false, 0, false, 3, 1, 0, true, false, false},
-      {true, false, 0, false, 1, 3, kSMSEntrypointSavePasswordBubble, true,
-       false, false},
-      {true, false, 0, false, 0, 4, kSMSEntrypointBookmarksBubble, true, false,
-       false},
-      {true, false, 0, false, 1, 10, 0, false, false, false},
-      {true, false, 0, false, 0, 1, 0, true, true, false},
-      {true, true, 1, false, 1, 1, 0, true, false, false},
-      {true, false, 1, false, 1, 1, 0, true, false, true},
-      {true, false, 1, false, 0, 2, 0, true, false, true},
-      {true, true, 0, false, 0, 8, kSMSEntrypointSavePasswordBubble, true,
-       false, false},
-      {true, false, 0, false, 0, 8, kSMSEntrypointSavePasswordBubble, true,
-       false, true},
-  };
-  std::string locale = base::i18n::GetConfiguredLocale();
-  SigninErrorControllerFactory::GetForProfile(profile())->SetPrimaryAccountID(
-      account_id());
-
-  for (const auto& test_case : kTestData) {
-    SCOPED_TRACE(testing::Message("#test_case = ") << (&test_case - kTestData));
-    sync_service()->SetDisableReasons(
-        test_case.is_sync_allowed
-            ? syncer::SyncService::DISABLE_REASON_NONE
-            : syncer::SyncService::DISABLE_REASON_PLATFORM_OVERRIDE);
-    const GoogleServiceAuthError error(
-        test_case.signin_error
-            ? GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
-            : GoogleServiceAuthError::NONE);
-
-    FakeAuthStatusProvider auth_status_provider(
-        SigninErrorControllerFactory::GetForProfile(profile()));
-    auth_status_provider.SetAuthError(account_id(), error);
-
-    local_state()->SetBoolean(prefs::kSavePasswordsBubbleIOSPromoDismissed,
-                              test_case.is_dismissed);
-    local_state()->SetInteger(prefs::kNumberSavePasswordsBubbleIOSPromoShown,
-                              test_case.show_count);
-    base::i18n::SetICUDefaultLocale(locales[test_case.locale_index]);
-    prefs()->SetDouble(prefs::kIOSPromotionLastImpression,
-                       GetDoubleNDayOldDate(test_case.last_impression_days));
-    prefs()->SetInteger(prefs::kIOSPromotionSMSEntryPoint,
-                        test_case.sms_entrypoint);
-    prefs()->SetBoolean(prefs::kIOSPromotionEligible,
-                        test_case.is_user_eligible);
-    prefs()->SetBoolean(prefs::kIOSPromotionDone, test_case.promo_done);
-    EXPECT_EQ(test_case.result,
-              IsEligibleForIOSPromotion(profile(), entry_point));
-  }
-  base::i18n::SetICUDefaultLocale(locale);
-}
diff --git a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_view.h b/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_view.h
deleted file mode 100644
index b633d6b6..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_view.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_VIEW_H_
-#define CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_VIEW_H_
-
-// Interface for The Desktop iOS promotion view.
-class DesktopIOSPromotionView {
- public:
-  virtual void UpdateRecoveryPhoneLabel() = 0;
-
- protected:
-  virtual ~DesktopIOSPromotionView() = default;
-};
-
-#endif  // CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_VIEW_H_
diff --git a/chrome/browser/ui/desktop_ios_promotion/sms_service.cc b/chrome/browser/ui/desktop_ios_promotion/sms_service.cc
deleted file mode 100644
index 3a46817..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/sms_service.cc
+++ /dev/null
@@ -1,339 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service.h"
-
-#include "base/bind.h"
-#include "base/json/json_reader.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/optional.h"
-#include "base/strings/stringprintf.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "net/base/load_flags.h"
-#include "net/base/url_util.h"
-#include "net/http/http_status_code.h"
-#include "net/http/http_util.h"
-#include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/identity/public/cpp/identity_manager.h"
-#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-
-namespace {
-
-const char kDesktopIOSPromotionOAuthScope[] =
-    "https://www.googleapis.com/auth/mobile_user_preferences";
-
-const char kDesktopIOSPromotionQueryPhoneNumber[] =
-    "https://growth-pa.googleapis.com/v1/get_verified_phone_numbers";
-
-const char kDesktopIOSPromotionSendSMS[] =
-    "https://growth-pa.googleapis.com/v1/send_sms";
-
-const char kPostDataMimeType[] = "application/json";
-
-const char kSendSMSPromoFormat[] = "{promo_id:%s}";
-
-// The maximum number of retries for the URLFetcher requests.
-const size_t kMaxRetries = 1;
-
-class RequestImpl : public SMSService::Request {
- public:
-  ~RequestImpl() override {}
-
-  // Returns the response code received from the server, which will only be
-  // valid if the request succeeded.
-  int GetResponseCode() override { return response_code_; }
-
-  // Returns the contents of the response body received from the server.
-  const std::string& GetResponseBody() override { return response_body_; }
-
-  bool IsPending() override { return is_pending_; }
-
- private:
-  friend class ::SMSService;
-
-  RequestImpl(identity::IdentityManager* identity_manager,
-              scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-              const GURL& url,
-              const SMSService::CompletionCallback& callback)
-      : identity_manager_(identity_manager),
-        url_loader_factory_(std::move(url_loader_factory)),
-        url_(url),
-        post_data_mime_type_(kPostDataMimeType),
-        response_code_(0),
-        auth_retry_count_(0),
-        callback_(callback),
-        is_pending_(false) {
-    DCHECK(identity_manager_);
-    DCHECK(url_loader_factory_);
-  }
-
-  void Start() override {
-    OAuth2TokenService::ScopeSet oauth_scopes;
-    oauth_scopes.insert(kDesktopIOSPromotionOAuthScope);
-    token_fetcher_ =
-        std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
-            "desktop_ios_promotion", identity_manager_, oauth_scopes,
-            base::BindOnce(&RequestImpl::AccessTokenFetchComplete,
-                           base::Unretained(this)),
-            identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
-    is_pending_ = true;
-  }
-
-  void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body) {
-    response_code_ = -1;
-    if (simple_url_loader_->ResponseInfo() &&
-        simple_url_loader_->ResponseInfo()->headers) {
-      response_code_ =
-          simple_url_loader_->ResponseInfo()->headers->response_code();
-    }
-
-    UMA_HISTOGRAM_CUSTOM_ENUMERATION(
-        "DesktopIOSPromotion.OAuthTokenResponseCode",
-        net::HttpUtil::MapStatusCodeForHistogram(response_code_),
-        net::HttpUtil::GetStatusCodesForHistogram());
-
-    // If the response code indicates that the token might not be valid,
-    // invalidate the token and try again.
-    if (response_code_ == net::HTTP_UNAUTHORIZED && ++auth_retry_count_ <= 1) {
-      OAuth2TokenService::ScopeSet oauth_scopes;
-      oauth_scopes.insert(kDesktopIOSPromotionOAuthScope);
-      identity_manager_->RemoveAccessTokenFromCache(
-          identity_manager_->GetPrimaryAccountId(), oauth_scopes,
-          access_token_);
-      access_token_.clear();
-      Start();
-      return;
-    }
-    bool success = !!response_body;
-    if (success) {
-      response_body_ = std::move(*response_body);
-    } else {
-      response_body_.clear();
-    }
-    simple_url_loader_.reset();
-    is_pending_ = false;
-    callback_.Run(this, success);
-    // It is valid for the callback to delete |this|, so do not access any
-    // members below here.
-  }
-
-  void AccessTokenFetchComplete(GoogleServiceAuthError error,
-                                identity::AccessTokenInfo access_token_info) {
-    token_fetcher_.reset();
-
-    if (error.state() != GoogleServiceAuthError::NONE) {
-      is_pending_ = false;
-      UMA_HISTOGRAM_BOOLEAN("DesktopIOSPromotion.OAuthTokenCompletion", false);
-
-      callback_.Run(this, false);
-      // It is valid for the callback to delete |this|, so do not access any
-      // members below here.
-      return;
-    }
-
-    std::string access_token = access_token_info.token;
-    DCHECK(!access_token.empty());
-    access_token_ = access_token;
-
-    UMA_HISTOGRAM_BOOLEAN("DesktopIOSPromotion.OAuthTokenCompletion", true);
-
-    // Got an access token -- start the actual API request.
-    net::NetworkTrafficAnnotationTag traffic_annotation =
-        net::DefineNetworkTrafficAnnotation("desktop_ios_promotion", R"(
-        semantics {
-          sender: "Desktop iOS Promotion"
-          description:
-            "Performes either of the following two tasks: Queries a logged in "
-            "user's recovery phone number, or sends a predetermined "
-            "promotional SMS to that number. SMS text may change but it always "
-            "contains a link to download Chrome from iTunes."
-          trigger:
-            "The query without SMS is only triggered when the desktop to iOS "
-            "promotion is shown to the user. The query and send is triggered "
-            "when the user clicks on 'Send SMS' button in the desktop to iOS "
-            "promotion."
-          data:
-            "It sends an oauth token (X-Developer-Key) which lets the Google "
-            "API identify the user."
-          destination: GOOGLE_OWNED_SERVICE
-        }
-        policy {
-          cookies_allowed: NO
-          setting:
-            "The feature cannot be disabled by settings, but it can be "
-            "disabled by 'Desktop to iOS Promotions' feature flag in "
-            "about:flags."
-          policy_exception_justification:
-            "Not implemented, considered not useful as it does not upload any "
-            "data and just downloads a recovery number."
-        })");
-    auto resource_request = std::make_unique<network::ResourceRequest>();
-    resource_request->url = url_;
-    resource_request->load_flags =
-        net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
-    resource_request->method = post_data_ ? "POST" : "GET";
-    resource_request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
-                                        "Bearer " + access_token);
-    resource_request->headers.SetHeader(
-        "X-Developer-Key", GaiaUrls::GetInstance()->oauth2_chrome_client_id());
-    simple_url_loader_ = network::SimpleURLLoader::Create(
-        std::move(resource_request), traffic_annotation);
-    simple_url_loader_->SetRetryOptions(kMaxRetries,
-                                        network::SimpleURLLoader::RETRY_ON_5XX);
-    if (post_data_)
-      simple_url_loader_->AttachStringForUpload(post_data_.value(),
-                                                post_data_mime_type_);
-    simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
-        url_loader_factory_.get(),
-        base::BindOnce(&RequestImpl::OnSimpleLoaderComplete,
-                       base::Unretained(this)));
-  }
-
-  void SetPostData(const std::string& post_data) override {
-    SetPostDataAndType(post_data, kPostDataMimeType);
-  }
-
-  void SetPostDataAndType(const std::string& post_data,
-                          const std::string& mime_type) override {
-    post_data_ = post_data;
-    post_data_mime_type_ = mime_type;
-  }
-
-  identity::IdentityManager* identity_manager_;
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // The URL of the API endpoint.
-  GURL url_;
-
-  // POST data to be sent with the request (may be empty).
-  base::Optional<std::string> post_data_;
-
-  // MIME type of the post requests. Defaults to text/plain.
-  std::string post_data_mime_type_;
-
-  // The OAuth2 access token request.
-  std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher> token_fetcher_;
-
-  // The current OAuth2 access token.
-  std::string access_token_;
-
-  // Handles the actual API requests after the OAuth token is acquired.
-  std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
-
-  // Holds the response code received from the server.
-  int response_code_;
-
-  // Holds the response body received from the server.
-  std::string response_body_;
-
-  // The number of times this request has already been retried due to
-  // authorization problems.
-  int auth_retry_count_;
-
-  // The callback to execute when the query is complete.
-  SMSService::CompletionCallback callback_;
-
-  // True if the request was started and has not yet completed, otherwise false.
-  bool is_pending_;
-};
-
-}  // namespace
-
-SMSService::Request::Request() {}
-
-SMSService::Request::~Request() {}
-
-SMSService::SMSService(
-    identity::IdentityManager* identity_manager,
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : identity_manager_(identity_manager),
-      url_loader_factory_(std::move(url_loader_factory)),
-      weak_ptr_factory_(this) {}
-
-SMSService::~SMSService() {}
-
-SMSService::Request* SMSService::CreateRequest(
-    const GURL& url,
-    const CompletionCallback& callback) {
-  return new RequestImpl(identity_manager_, url_loader_factory_, url, callback);
-}
-
-void SMSService::QueryPhoneNumber(const PhoneNumberCallback& callback) {
-  CompletionCallback completion_callback =
-      base::Bind(&SMSService::QueryPhoneNumberCompletionCallback,
-                 weak_ptr_factory_.GetWeakPtr(), callback);
-
-  GURL url(kDesktopIOSPromotionQueryPhoneNumber);
-  Request* request = CreateRequest(url, completion_callback);
-  // This placeholder is required by the API.
-  request->SetPostData("{}");
-  pending_requests_[request] = base::WrapUnique(request);
-  request->Start();
-}
-
-void SMSService::SendSMS(const std::string& promo_id,
-                         const SMSService::PhoneNumberCallback& callback) {
-  CompletionCallback completion_callback = base::Bind(
-      &SMSService::SendSMSCallback, weak_ptr_factory_.GetWeakPtr(), callback);
-  GURL url(kDesktopIOSPromotionSendSMS);
-  Request* request = CreateRequest(url, completion_callback);
-  request->SetPostData(
-      base::StringPrintf(kSendSMSPromoFormat, promo_id.c_str()));
-  pending_requests_[request] = base::WrapUnique(request);
-  request->Start();
-}
-
-void SMSService::QueryPhoneNumberCompletionCallback(
-    const SMSService::PhoneNumberCallback& callback,
-    SMSService::Request* request,
-    bool success) {
-  std::unique_ptr<Request> request_ptr = std::move(pending_requests_[request]);
-  pending_requests_.erase(request);
-
-  std::string phone_number;
-  bool has_number = false;
-  std::unique_ptr<base::Value> value =
-      base::JSONReader::Read(request->GetResponseBody());
-  if (value.get() && value.get()->is_dict()) {
-    const base::DictionaryValue* dictionary;
-    if (value->GetAsDictionary(&dictionary)) {
-      const base::ListValue* number_list;
-      if (dictionary->GetList("phoneNumber", &number_list)) {
-        const base::DictionaryValue* sub_dictionary;
-        // For now only handle the first number.
-        if (number_list->GetSize() > 0 &&
-            number_list->GetDictionary(0, &sub_dictionary)) {
-          if (sub_dictionary->GetString("phoneNumber", &phone_number))
-            has_number = true;
-        }
-      }
-    }
-  }
-  callback.Run(request, success && has_number, phone_number);
-}
-
-void SMSService::SendSMSCallback(
-    const SMSService::PhoneNumberCallback& callback,
-    SMSService::Request* request,
-    bool success) {
-  std::unique_ptr<Request> request_ptr = std::move(pending_requests_[request]);
-  pending_requests_.erase(request);
-
-  std::string phone_number;
-  bool has_number = false;
-  std::unique_ptr<base::Value> value =
-      base::JSONReader::Read(request->GetResponseBody());
-  if (value.get() && value.get()->is_dict()) {
-    const base::DictionaryValue* dictionary;
-    if (value->GetAsDictionary(&dictionary)) {
-      if (dictionary->GetString("phoneNumber", &phone_number))
-        has_number = true;
-    }
-  }
-  callback.Run(request, success && has_number, phone_number);
-}
diff --git a/chrome/browser/ui/desktop_ios_promotion/sms_service.h b/chrome/browser/ui/desktop_ios_promotion/sms_service.h
deleted file mode 100644
index 9549185..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/sms_service.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_SMS_SERVICE_H_
-#define CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_SMS_SERVICE_H_
-
-#include <stddef.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "url/gurl.h"
-
-namespace identity {
-class IdentityManager;
-}
-
-namespace network {
-class SharedURLLoaderFactory;
-}
-
-// Provides an API for querying a logged in users's verified phone number,
-// and sending a predetermined promotional SMS to that number.  This class is
-// based heavily on WebHistoryService's implementation to query Google services.
-class SMSService : public KeyedService {
- public:
-  class Request {
-   public:
-    virtual ~Request();
-
-    virtual bool IsPending() = 0;
-
-    // Returns the response code received from the server, which will only be
-    // valid if the request succeeded.
-    virtual int GetResponseCode() = 0;
-
-    // Returns the contents of the response body received from the server.
-    virtual const std::string& GetResponseBody() = 0;
-
-    virtual void SetPostData(const std::string& post_data) = 0;
-
-    virtual void SetPostDataAndType(const std::string& post_data,
-                                    const std::string& mime_type) = 0;
-
-    // Tells the request to begin.
-    virtual void Start() = 0;
-
-   protected:
-    Request();
-  };
-
-  typedef base::Callback<
-      void(Request*, bool success, const std::string& number)>
-      PhoneNumberCallback;
-  typedef base::Callback<void(Request*, bool success)> CompletionCallback;
-
-  SMSService(identity::IdentityManager* identity_manager,
-             scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-  ~SMSService() override;
-
-  // Query the logged in user's verified phone number.
-  virtual void QueryPhoneNumber(const PhoneNumberCallback& callback);
-
-  // Send an SMS to the logged in user's verified phone number.  The text of
-  // the SMS is determined by |promo_id|.
-  virtual void SendSMS(const std::string& promo_id,
-                       const SMSService::PhoneNumberCallback& callback);
-
- protected:
-  void QueryPhoneNumberCompletionCallback(
-      const SMSService::PhoneNumberCallback& callback,
-      SMSService::Request* request,
-      bool success);
-
-  void SendSMSCallback(const SMSService::PhoneNumberCallback& callback,
-                       SMSService::Request* request,
-                       bool success);
-
- private:
-  virtual Request* CreateRequest(const GURL& url,
-                                 const CompletionCallback& callback);
-
-  // Stores pointer to the IdentityManager instance. It must outlive the
-  // SMSService and can be null during tests.
-  identity::IdentityManager* identity_manager_;
-
-  // Request context getter to use.
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-
-  // Pending expiration requests to be canceled if not complete by profile
-  // shutdown.
-  std::map<Request*, std::unique_ptr<Request>> pending_requests_;
-
-  base::WeakPtrFactory<SMSService> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(SMSService);
-};
-
-#endif  // CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_SMS_SERVICE_H_
diff --git a/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.cc b/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.cc
deleted file mode 100644
index 2a630a3c..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h"
-
-#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-
-// static
-SMSServiceFactory* SMSServiceFactory::GetInstance() {
-  return base::Singleton<SMSServiceFactory>::get();
-}
-
-// static
-SMSService* SMSServiceFactory::GetForProfile(Profile* profile) {
-  return static_cast<SMSService*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-KeyedService* SMSServiceFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  Profile* profile = Profile::FromBrowserContext(context);
-  return new SMSService(
-      IdentityManagerFactory::GetForProfile(profile),
-      content::BrowserContext::GetDefaultStoragePartition(profile)
-          ->GetURLLoaderFactoryForBrowserProcess());
-}
-
-SMSServiceFactory::SMSServiceFactory()
-    : BrowserContextKeyedServiceFactory(
-          "SMSServiceFactory",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(IdentityManagerFactory::GetInstance());
-}
-
-SMSServiceFactory::~SMSServiceFactory() {}
diff --git a/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h b/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h
deleted file mode 100644
index 8d7b1f7e..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/sms_service_factory.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_SMS_SERVICE_FACTORY_H_
-#define CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_SMS_SERVICE_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "chrome/browser/profiles/profile.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class SMSService;
-
-class SMSServiceFactory : public BrowserContextKeyedServiceFactory {
- public:
-  // Get the singleton instance of the factory.
-  static SMSServiceFactory* GetInstance();
-
-  // Get the SMSService for |profile|, creating one if needed.
-  static SMSService* GetForProfile(Profile* profile);
-
- protected:
-  // Overridden from BrowserContextKeyedServiceFactory.
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-
- private:
-  friend struct base::DefaultSingletonTraits<SMSServiceFactory>;
-
-  SMSServiceFactory();
-  ~SMSServiceFactory() override;
-
-  DISALLOW_COPY_AND_ASSIGN(SMSServiceFactory);
-};
-
-#endif  // CHROME_BROWSER_UI_DESKTOP_IOS_PROMOTION_SMS_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/desktop_ios_promotion/sms_service_unittest.cc b/chrome/browser/ui/desktop_ios_promotion/sms_service_unittest.cc
deleted file mode 100644
index 887b5eb..0000000
--- a/chrome/browser/ui/desktop_ios_promotion/sms_service_unittest.cc
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/desktop_ios_promotion/sms_service.h"
-
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/test_signin_client.h"
-#include "net/http/http_status_code.h"
-#include "services/identity/public/cpp/identity_test_environment.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-// A testing web history service that does extra checks and creates a
-// TestRequest instead of a normal request.
-class TestingSMSService : public SMSService {
- public:
-  TestingSMSService(
-      identity::IdentityManager* identity_manager,
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-      : SMSService(identity_manager, url_loader_factory) {}
-
-  ~TestingSMSService() override {}
-
-  SMSService::Request* CreateRequest(
-      const GURL& url,
-      const CompletionCallback& callback) override;
-
-  void SetNextRequestResponseData(int response_code,
-                                  const std::string& response_body) {
-    next_response_code_ = response_code;
-    next_response_body_ = response_body;
-  }
-
-  void QueryPhoneNumberCallbackSucceeded(SMSService::Request* request,
-                                         bool success,
-                                         const std::string& number) {
-    EXPECT_TRUE(success);
-    EXPECT_EQ(number, "1");
-  }
-
-  void QueryPhoneNumberCallbackFailed(SMSService::Request* request,
-                                      bool success,
-                                      const std::string& number) {
-    EXPECT_FALSE(success);
-    EXPECT_EQ(number, "");
-  }
-
- private:
-  int next_response_code_;
-  std::string next_response_body_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestingSMSService);
-};
-
-// A testing request class that allows expected values to be filled in.
-class TestRequest : public SMSService::Request {
- public:
-  TestRequest(const SMSService::CompletionCallback& callback,
-              int response_code,
-              const std::string& response_body)
-      : callback_(callback),
-        response_code_(response_code),
-        response_body_(response_body),
-        is_pending_(false) {}
-
-  ~TestRequest() override {}
-
-  // history::Request overrides
-  bool IsPending() override { return is_pending_; }
-  int GetResponseCode() override { return response_code_; }
-  const std::string& GetResponseBody() override { return response_body_; }
-  void SetPostData(const std::string& post_data) override {
-    post_data_ = post_data;
-  }
-  void SetPostDataAndType(const std::string& post_data,
-                          const std::string& mime_type) override {
-    SetPostData(post_data);
-  }
-  void Start() override {
-    is_pending_ = true;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(&TestRequest::MimicReturnFromFetch, base::Unretained(this)));
-  }
-
-  void MimicReturnFromFetch() {
-    callback_.Run(this, response_code_ == net::HTTP_OK);
-  }
-
- private:
-  GURL url_;
-  SMSService::CompletionCallback callback_;
-  int response_code_;
-  std::string response_body_;
-  std::string post_data_;
-  bool is_pending_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestRequest);
-};
-
-SMSService::Request* TestingSMSService::CreateRequest(
-    const GURL& url,
-    const CompletionCallback& callback) {
-  SMSService::Request* request =
-      new TestRequest(callback, next_response_code_, next_response_body_);
-  return request;
-}
-
-}  // namespace
-
-// A test class used for testing the SMSService class.
-// In order for SMSService to be valid, we must have a valid
-// ProfileSyncService. Using the ProfileSyncServiceMock class allows to
-// assign specific return values as needed to make sure the web history
-// service is available.
-class SMSServiceTest : public testing::Test {
- public:
-  SMSServiceTest()
-      : test_shared_loader_factory_(
-            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-                &test_url_loader_factory_)),
-        sms_service_(identity_test_environment_.identity_manager(),
-                     test_shared_loader_factory_) {}
-
-  ~SMSServiceTest() override {}
-
-  void TearDown() override {
-    base::RunLoop run_loop;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  run_loop.QuitClosure());
-    run_loop.Run();
-  }
-
-  TestingSMSService* sms_service() { return &sms_service_; }
-
-  void SetNextRequestResponseData(int response_code,
-                                  const std::string& response_body) {
-    sms_service_.SetNextRequestResponseData(response_code, response_body);
-  }
-
- private:
-  base::MessageLoop message_loop_;
-  identity::IdentityTestEnvironment identity_test_environment_;
-  network::TestURLLoaderFactory test_url_loader_factory_;
-  scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
-  TestingSMSService sms_service_;
-
-  DISALLOW_COPY_AND_ASSIGN(SMSServiceTest);
-};
-
-TEST_F(SMSServiceTest, VerifyJsonData) {
-  // Test that properly formatted response with good response code returns true
-  // as expected.
-  std::string query_phone_valid =
-      "{\n\"phoneNumber\": "
-      "  [\n"
-      "    {\"phoneNumber\": \"1\"},"
-      "    {\"phoneNumber\": \"2\"}"
-      "  ]\n"
-      "}";
-  sms_service()->SetNextRequestResponseData(net::HTTP_OK, query_phone_valid);
-  sms_service()->QueryPhoneNumber(
-      base::Bind(&TestingSMSService::QueryPhoneNumberCallbackSucceeded,
-                 base::Unretained(sms_service())));
-  base::RunLoop().RunUntilIdle();
-  std::string send_sms_valid = "{\"phoneNumber\": \"1\"}";
-  sms_service()->SetNextRequestResponseData(net::HTTP_OK, send_sms_valid);
-  std::string promo_id = "";
-  sms_service()->SendSMS(
-      promo_id,
-      base::Bind(&TestingSMSService::QueryPhoneNumberCallbackSucceeded,
-                 base::Unretained(sms_service())));
-  base::RunLoop().RunUntilIdle();
-  // Test that improperly formatted response returns no number.
-  std::string query_phone_invalid =
-      "{\n\"phoneNumber\": "
-      "  [\n"
-      "  ]\n"
-      "}";
-  sms_service()->SetNextRequestResponseData(net::HTTP_OK, query_phone_invalid);
-  sms_service()->QueryPhoneNumber(
-      base::Bind(&TestingSMSService::QueryPhoneNumberCallbackFailed,
-                 base::Unretained(sms_service())));
-  base::RunLoop().RunUntilIdle();
-  std::string send_sms_invalid = "{}";
-  sms_service()->SetNextRequestResponseData(net::HTTP_OK, send_sms_invalid);
-  sms_service()->SendSMS(
-      promo_id, base::Bind(&TestingSMSService::QueryPhoneNumberCallbackFailed,
-                           base::Unretained(sms_service())));
-  base::RunLoop().RunUntilIdle();
-}
diff --git a/chrome/browser/ui/libgtkui/native_theme_gtk.cc b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
index eb4fb60..e9147f2 100644
--- a/chrome/browser/ui/libgtkui/native_theme_gtk.cc
+++ b/chrome/browser/ui/libgtkui/native_theme_gtk.cc
@@ -133,6 +133,12 @@
     case ui::NativeTheme::kColorId_TouchableMenuItemLabelColor:
     case ui::NativeTheme::kColorId_ActionableSubmenuVerticalSeparatorColor:
       return gfx::kPlaceholderColor;
+    // Fallback to the same colors as Aura.
+    case ui::NativeTheme::kColorId_HighlightedMenuItemBackgroundColor:
+    case ui::NativeTheme::kColorId_HighlightedMenuItemForegroundColor:
+    case ui::NativeTheme::kColorId_FocusedHighlightedMenuItemBackgroundColor:
+      return ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+          color_id);
 
     // Label
     case ui::NativeTheme::kColorId_LabelEnabledColor:
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
new file mode 100644
index 0000000..f49ba49
--- /dev/null
+++ b/chrome/browser/ui/managed_ui.cc
@@ -0,0 +1,71 @@
+// 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/managed_ui.h"
+
+#include "base/feature_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/policy/chrome_browser_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/policy/profile_policy_connector_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_features.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "components/user_manager/user_manager.h"
+#endif
+
+namespace chrome {
+
+bool ShouldDisplayManagedUi(Profile* profile) {
+  if (!base::FeatureList::IsEnabled(features::kShowManagedUi))
+    return false;
+
+  // Most policies don't apply to incognito mode, and incognito already
+  // discloses that you may be tracked/MITM'd by your admin.
+  if (profile->IsOffTheRecord())
+    return false;
+
+  // This profile may have policies configured.
+  auto* profile_connector =
+      policy::ProfilePolicyConnectorFactory::GetForBrowserContext(profile);
+  if (profile_connector->IsManaged())
+    return true;
+
+#if defined(OS_CHROMEOS)
+  // This session's primary user may also have policies, and those policies may
+  // not have per-profile support.
+  auto* primary_user = user_manager::UserManager::Get()->GetPrimaryUser();
+  if (primary_user) {
+    auto* primary_profile =
+        chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
+    if (primary_profile) {
+      auto* primary_profile_connector =
+          policy::ProfilePolicyConnectorFactory::GetForBrowserContext(
+              primary_profile);
+      if (primary_profile_connector->IsManaged())
+        return true;
+    }
+  }
+
+  // The machine may be enrolled, via Google Cloud or Active Directory.
+  auto* browser_connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  if (browser_connector->IsEnterpriseManaged())
+    return true;
+#else
+  // There may be policies set in a platform-specific way (e.g. Windows
+  // Registry), or with machine level user cloud policies.
+  auto* browser_connector = g_browser_process->browser_policy_connector();
+  if (browser_connector->HasMachineLevelPolicies())
+    return true;
+#endif
+
+  return false;
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/ui/managed_ui.h b/chrome/browser/ui/managed_ui.h
new file mode 100644
index 0000000..2c548f26
--- /dev/null
+++ b/chrome/browser/ui/managed_ui.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_MANAGED_UI_H_
+#define CHROME_BROWSER_UI_MANAGED_UI_H_
+
+class Profile;
+
+namespace chrome {
+
+// Returns true if a 'Managed by your organization' message should appear in
+// Chrome's App Menu, and on the following chrome:// pages:
+// - chrome://bookmarks
+// - chrome://downloads
+// - chrome://extensions
+// - chrome://history
+// - chrome://settings
+//
+// N.B.: This is independent of Chrome OS's system tray message for enterprise
+// users.
+bool ShouldDisplayManagedUi(Profile* profile);
+
+}  // namespace chrome
+
+#endif  // CHROME_BROWSER_UI_MANAGED_UI_H_
diff --git a/chrome/browser/ui/managed_ui_browsertest.cc b/chrome/browser/ui/managed_ui_browsertest.cc
new file mode 100644
index 0000000..638293e
--- /dev/null
+++ b/chrome/browser/ui/managed_ui_browsertest.cc
@@ -0,0 +1,69 @@
+// 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/managed_ui.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using namespace policy;
+
+class ManagedUiTest : public InProcessBrowserTest {
+ public:
+  ManagedUiTest() = default;
+  ~ManagedUiTest() override = default;
+
+  void SetUpInProcessBrowserTestFixture() override {
+    EXPECT_CALL(provider_, IsInitializationComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    BrowserPolicyConnectorBase::SetPolicyProviderForTesting(&provider_);
+  }
+
+  MockConfigurationPolicyProvider* provider() { return &provider_; }
+
+ private:
+  MockConfigurationPolicyProvider provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(ManagedUiTest);
+};
+
+IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiFlagDisabled) {
+  PolicyMap policy_map;
+  policy_map.Set("test-policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                 POLICY_SOURCE_PLATFORM,
+                 std::make_unique<base::Value>("hello world"), nullptr);
+  provider()->UpdateChromePolicy(policy_map);
+
+  EXPECT_FALSE(chrome::ShouldDisplayManagedUi(browser()->profile()));
+}
+
+IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiNoPolicies) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitFromCommandLine("ShowManagedUi", "");
+
+  EXPECT_FALSE(chrome::ShouldDisplayManagedUi(browser()->profile()));
+}
+
+IN_PROC_BROWSER_TEST_F(ManagedUiTest, ShouldDisplayManagedUiOnDesktop) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitFromCommandLine("ShowManagedUi", "");
+
+  PolicyMap policy_map;
+  policy_map.Set("test-policy", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                 POLICY_SOURCE_PLATFORM,
+                 std::make_unique<base::Value>("hello world"), nullptr);
+  provider()->UpdateChromePolicy(policy_map);
+
+#if defined(OS_CHROMEOS)
+  EXPECT_FALSE(chrome::ShouldDisplayManagedUi(browser()->profile()));
+#else
+  EXPECT_TRUE(chrome::ShouldDisplayManagedUi(browser()->profile()));
+#endif
+}
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
index 84e68c92..0233677 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -32,10 +32,6 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if defined(OS_WIN)
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-#endif
-
 namespace metrics_util = password_manager::metrics_util;
 
 namespace {
@@ -291,7 +287,6 @@
       case password_manager::ui::CREDENTIAL_REQUEST_STATE:
       case password_manager::ui::AUTO_SIGNIN_STATE:
       case password_manager::ui::CHROME_SIGN_IN_PROMO_STATE:
-      case password_manager::ui::CHROME_DESKTOP_IOS_PROMO_STATE:
       case password_manager::ui::INACTIVE_STATE:
         NOTREACHED();
         break;
@@ -315,7 +310,6 @@
       case password_manager::ui::MANAGE_STATE:
       case password_manager::ui::CREDENTIAL_REQUEST_STATE:
       case password_manager::ui::CHROME_SIGN_IN_PROMO_STATE:
-      case password_manager::ui::CHROME_DESKTOP_IOS_PROMO_STATE:
       case password_manager::ui::INACTIVE_STATE:
         NOTREACHED();
         break;
@@ -490,18 +484,6 @@
     interaction_keeper_->set_sign_in_promo_shown_count(show_count);
     return true;
   }
-#if defined(OS_WIN)
-  // Desktop to mobile promotion only enabled on windows.
-  if (desktop_ios_promotion::IsEligibleForIOSPromotion(
-          profile,
-          desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE)) {
-    interaction_keeper_->ReportInteractions(this);
-    title_ = desktop_ios_promotion::GetPromoTitle(
-        desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE);
-    state_ = password_manager::ui::CHROME_DESKTOP_IOS_PROMO_STATE;
-    return true;
-  }
-#endif
   return false;
 }
 
diff --git a/chrome/browser/ui/toolbar/app_menu_model.cc b/chrome/browser/ui/toolbar/app_menu_model.cc
index 2408134..dccb467f 100644
--- a/chrome/browser/ui/toolbar/app_menu_model.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model.cc
@@ -35,6 +35,7 @@
 #include "chrome/browser/ui/global_error/global_error.h"
 #include "chrome/browser/ui/global_error/global_error_service.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
+#include "chrome/browser/ui/managed_ui.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/bookmark_sub_menu_model.h"
 #include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
@@ -53,6 +54,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/signin_metrics.h"
 #include "components/strings/grit/components_strings.h"
+#include "components/vector_icons/vector_icons.h"
 #include "components/zoom/zoom_controller.h"
 #include "components/zoom/zoom_event_manager.h"
 #include "content/public/browser/host_zoom_map.h"
@@ -65,8 +67,10 @@
 #include "ui/base/layout.h"
 #include "ui/base/models/button_menu_item_model.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/text_elider.h"
 
 #if defined(GOOGLE_CHROME_BUILD)
@@ -798,6 +802,17 @@
     AddSeparator(ui::NORMAL_SEPARATOR);
     AddItemWithStringId(IDC_EXIT, IDS_EXIT);
   }
+
+  if (chrome::ShouldDisplayManagedUi(browser_->profile())) {
+    AddSeparator(ui::LOWER_SEPARATOR);
+    const int kIconSize = 20;
+    const auto icon = gfx::CreateVectorIcon(gfx::IconDescription(
+        vector_icons::kBusinessIcon, kIconSize, gfx::kChromeIconGrey,
+        base::TimeDelta(), gfx::kNoneIcon));
+    AddHighlightedItemWithStringIdAndIcon(IDC_MANAGED_UI_HELP,
+                                          IDS_MANAGED_BY_ORG, icon);
+  }
+
   uma_action_recorded_ = false;
 }
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index e99debd..0f85d12f 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -35,11 +35,6 @@
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/widget/widget.h"
 
-#if defined(OS_WIN)
-#include "chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h"
-#include "chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.h"
-#endif
-
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/ui/views/sync/dice_bubble_sync_promo_view.h"
@@ -113,10 +108,6 @@
 
 base::string16 BookmarkBubbleView::GetDialogButtonLabel(
     ui::DialogButton button) const {
-#if defined(OS_WIN)
-  if (is_showing_ios_promotion_)
-    return ios_promo_view_->GetDialogButtonLabel(button);
-#endif
   return l10n_util::GetStringUTF16((button == ui::DIALOG_BUTTON_OK)
                                        ? IDS_DONE
                                        : IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK);
@@ -129,12 +120,6 @@
 }
 
 base::string16 BookmarkBubbleView::GetWindowTitle() const {
-#if defined(OS_WIN)
-  if (is_showing_ios_promotion_) {
-    return desktop_ios_promotion::GetPromoTitle(
-        desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_BUBBLE);
-  }
-#endif
   return l10n_util::GetStringUTF16(newly_bookmarked_
                                        ? IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED
                                        : IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK);
@@ -145,15 +130,11 @@
 }
 
 gfx::ImageSkia BookmarkBubbleView::GetWindowIcon() {
-#if defined(OS_WIN)
-  if (is_showing_ios_promotion_)
-    return ios_promo_view_->GetWindowIcon();
-#endif
   return gfx::ImageSkia();
 }
 
 bool BookmarkBubbleView::ShouldShowWindowIcon() const {
-  return is_showing_ios_promotion_;
+  return false;
 }
 
 void BookmarkBubbleView::WindowClosing() {
@@ -161,7 +142,6 @@
   // destroyed asynchronously and the shown state will be checked before then.
   DCHECK_EQ(bookmark_bubble_, this);
   bookmark_bubble_ = NULL;
-  is_showing_ios_promotion_ = false;
 
   if (observer_)
     observer_->OnBookmarkBubbleHidden();
@@ -183,15 +163,6 @@
 }
 
 views::View* BookmarkBubbleView::CreateFootnoteView() {
-#if defined(OS_WIN)
-  if (!is_showing_ios_promotion_ &&
-      desktop_ios_promotion::IsEligibleForIOSPromotion(
-          profile_,
-          desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_FOOTNOTE)) {
-    footnote_view_ = new DesktopIOSPromotionFootnoteView(profile_, this);
-    return footnote_view_;
-  }
-#endif
   if (!SyncPromoUI::ShouldShowSyncPromo(profile_))
     return nullptr;
 
@@ -219,10 +190,6 @@
 }
 
 bool BookmarkBubbleView::Cancel() {
-#if defined(OS_WIN)
-  if (is_showing_ios_promotion_)
-    return ios_promo_view_->Cancel();
-#endif
   base::RecordAction(UserMetricsAction("BookmarkBubble_Unstar"));
   // Set this so we remove the bookmark after the window closes.
   remove_bookmark_ = true;
@@ -231,16 +198,6 @@
 }
 
 bool BookmarkBubbleView::Accept() {
-#if defined(OS_WIN)
-  if (is_showing_ios_promotion_)
-    return ios_promo_view_->Accept();
-  using desktop_ios_promotion::PromotionEntryPoint;
-  if (desktop_ios_promotion::IsEligibleForIOSPromotion(
-          profile_, PromotionEntryPoint::BOOKMARKS_BUBBLE)) {
-    ShowIOSPromotion(PromotionEntryPoint::BOOKMARKS_BUBBLE);
-    return false;
-  }
-#endif
   return true;
 }
 
@@ -279,15 +236,6 @@
   }
 }
 
-// DesktopIOSPromotionFootnoteDelegate -----------------------------------------
-
-void BookmarkBubbleView::OnIOSPromotionFootnoteLinkClicked() {
-#if defined(OS_WIN)
-  ShowIOSPromotion(
-      desktop_ios_promotion::PromotionEntryPoint::FOOTNOTE_FOLLOWUP_BUBBLE);
-#endif
-}
-
 // views::BubbleDialogDelegateView ---------------------------------------------
 
 void BookmarkBubbleView::Init() {
@@ -386,29 +334,3 @@
     folder_model()->MaybeChangeParent(node, parent_combobox_->selected_index());
   }
 }
-
-#if defined(OS_WIN)
-void BookmarkBubbleView::ShowIOSPromotion(
-    desktop_ios_promotion::PromotionEntryPoint entry_point) {
-  DCHECK(!is_showing_ios_promotion_);
-  edit_button_->SetVisible(false);
-
-  // If edits are to be applied we must do so before removing the content.
-  if (apply_edits_)
-    ApplyEdits();
-
-  delete bookmark_contents_view_;
-  bookmark_contents_view_ = nullptr;
-  delete footnote_view_;
-  footnote_view_ = nullptr;
-  is_showing_ios_promotion_ = true;
-  ios_promo_view_ = new DesktopIOSPromotionBubbleView(profile_, entry_point);
-  AddChildView(ios_promo_view_);
-  set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
-      views::TEXT, views::TEXT));
-  GetWidget()->UpdateWindowIcon();
-  GetWidget()->UpdateWindowTitle();
-  DialogModelChanged();
-  SizeToContents();
-}
-#endif
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
index 00b781f..b85b383 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
@@ -11,7 +11,6 @@
 #include "base/strings/string16.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_footnote_delegate.h"
 #include "chrome/browser/ui/sync/bubble_sync_promo_delegate.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
 #include "ui/views/controls/button/button.h"
@@ -19,19 +18,12 @@
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "url/gurl.h"
 
-class DesktopIOSPromotionBubbleView;
 class Profile;
 
 namespace bookmarks {
 class BookmarkBubbleObserver;
 }
 
-#if defined(OS_WIN)
-namespace desktop_ios_promotion {
-enum class PromotionEntryPoint;
-}
-#endif
-
 namespace views {
 class LabelButton;
 class Textfield;
@@ -43,8 +35,7 @@
 // instead use the static Show method.
 class BookmarkBubbleView : public LocationBarBubbleDelegateView,
                            public views::ButtonListener,
-                           public views::ComboboxListener,
-                           public DesktopIOSPromotionFootnoteDelegate {
+                           public views::ComboboxListener {
  public:
   // If |anchor_view| is null, |anchor_rect| is used to anchor the bubble and
   // |parent_window| is used to ensure the bubble closes if the parent closes.
@@ -90,9 +81,6 @@
   // views::ComboboxListener:
   void OnPerformAction(views::Combobox* combobox) override;
 
-  // DesktopIOSPromotionFootnoteDelegate:
-  void OnIOSPromotionFootnoteLinkClicked() override;
-
  protected:
   // LocationBarBubbleDelegateView:
   void Init() override;
@@ -124,11 +112,6 @@
   // Sets the bookmark name and parent of the node.
   void ApplyEdits();
 
-#if defined(OS_WIN)
-  // Shows the iOS promotion.
-  void ShowIOSPromotion(desktop_ios_promotion::PromotionEntryPoint entry_point);
-#endif
-
   // The bookmark bubble, if we're showing one.
   static BookmarkBubbleView* bookmark_bubble_;
 
@@ -161,9 +144,6 @@
   // buttons. TODO(tapted): Move the buttons to the DialogClientView.
   views::View* bookmark_contents_view_ = nullptr;
 
-  // iOS promotion view.
-  DesktopIOSPromotionBubbleView* ios_promo_view_ = nullptr;
-
   // Footnote view.
   views::View* footnote_view_ = nullptr;
 
@@ -173,9 +153,6 @@
   // When the destructor is invoked should edits be applied?
   bool apply_edits_ = true;
 
-  // Whether the Windows to iOS promotion is shown to the user.
-  bool is_showing_ios_promotion_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(BookmarkBubbleView);
 };
 
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc
index b5db0b52..96b4031 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc
@@ -70,11 +70,3 @@
                        InvokeUi_bookmark_details_signed_in) {
   ShowAndVerifyUi();
 }
-
-#if defined(OS_WIN)
-IN_PROC_BROWSER_TEST_F(BookmarkBubbleViewBrowserTest, InvokeUi_ios_promotion) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kForceDesktopIOSPromotion);
-  ShowAndVerifyUi();
-}
-#endif
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc
deleted file mode 100644
index 0823bc7..0000000
--- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h"
-
-#include <memory>
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller.h"
-#include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/chrome_typography.h"
-#include "chrome/grit/generated_resources.h"
-#include "chrome/grit/locale_settings.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/views/border.h"
-#include "ui/views/layout/fill_layout.h"
-#include "ui/views/widget/widget.h"
-
-DesktopIOSPromotionBubbleView::DesktopIOSPromotionBubbleView(
-    Profile* profile,
-    desktop_ios_promotion::PromotionEntryPoint entry_point)
-    : promotion_text_label_(
-          new views::Label(desktop_ios_promotion::GetPromoText(entry_point),
-                           CONTEXT_BODY_TEXT_LARGE,
-                           STYLE_SECONDARY)),
-      promotion_controller_(
-          std::make_unique<DesktopIOSPromotionBubbleController>(profile,
-                                                                this,
-                                                                entry_point)) {
-  SetLayoutManager(std::make_unique<views::FillLayout>());
-
-  SetBorder(
-      views::CreateEmptyBorder(0,
-                               ChromeLayoutProvider::Get()
-                                       ->GetInsetsMetric(views::INSETS_DIALOG)
-                                       .left() +
-                                   GetWindowIcon().width(),
-                               0, 0));
-
-  promotion_text_label_->SetMultiLine(true);
-  promotion_text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  AddChildView(promotion_text_label_);
-
-  promotion_controller_->OnPromotionShown();
-}
-
-DesktopIOSPromotionBubbleView::~DesktopIOSPromotionBubbleView() = default;
-
-void DesktopIOSPromotionBubbleView::UpdateRecoveryPhoneLabel() {
-  std::string number = promotion_controller_->GetUsersRecoveryPhoneNumber();
-  if (!number.empty()) {
-    promotion_text_label_->SetText(desktop_ios_promotion::GetPromoText(
-        promotion_controller_->entry_point(), number));
-    Layout();
-    views::Widget* widget = GetWidget();
-    gfx::Rect old_bounds = widget->GetWindowBoundsInScreen();
-    old_bounds.set_height(
-        widget->GetRootView()->GetHeightForWidth(old_bounds.width()));
-    widget->SetBounds(old_bounds);
-  }
-}
-
-bool DesktopIOSPromotionBubbleView::Accept() {
-  promotion_controller_->OnSendSMSClicked();
-  return true;
-}
-
-bool DesktopIOSPromotionBubbleView::Cancel() {
-  promotion_controller_->OnNoThanksClicked();
-  return true;
-}
-
-base::string16 DesktopIOSPromotionBubbleView::GetDialogButtonLabel(
-    ui::DialogButton button) const {
-  return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK
-                                       ? IDS_DESKTOP_TO_IOS_PROMO_SEND_TO_PHONE
-                                       : IDS_DESKTOP_TO_IOS_PROMO_NO_THANKS);
-}
-
-gfx::ImageSkia DesktopIOSPromotionBubbleView::GetWindowIcon() {
-  return desktop_ios_promotion::GetPromoImage(GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_TextfieldDefaultColor));
-}
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h
deleted file mode 100644
index a368cfb..0000000
--- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_BUBBLE_VIEW_H_
-#define CHROME_BROWSER_UI_VIEWS_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_BUBBLE_VIEW_H_
-
-#include "base/macros.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_view.h"
-#include "ui/views/controls/button/button.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/view.h"
-
-class DesktopIOSPromotionBubbleController;
-class Profile;
-
-// The DesktopIOSPromotionBubbleView is the main view for the desktop-to-ios
-// promotion view. It proxies DialogClientView functionality to provide
-// replacement button labels, actions and icon when activated.
-class DesktopIOSPromotionBubbleView : public DesktopIOSPromotionView,
-                                      public views::View {
- public:
-  DesktopIOSPromotionBubbleView(
-      Profile* profile,
-      desktop_ios_promotion::PromotionEntryPoint entry_point);
-  ~DesktopIOSPromotionBubbleView() override;
-
-  // DesktopIOSPromotionView:
-  void UpdateRecoveryPhoneLabel() override;
-
-  bool Accept();
-  bool Cancel();
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const;
-  gfx::ImageSkia GetWindowIcon();
-
- private:
-  // The text that will appear on the promotion body.
-  views::Label* promotion_text_label_;
-  std::unique_ptr<DesktopIOSPromotionBubbleController> promotion_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopIOSPromotionBubbleView);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.cc b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.cc
deleted file mode 100644
index 0a22c520..0000000
--- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.h"
-
-#include <stddef.h>
-
-#include <memory>
-
-#include "base/strings/string16.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_controller.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_footnote_delegate.h"
-#include "chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h"
-#include "chrome/browser/ui/views/chrome_typography.h"
-#include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/generated_resources.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/gfx/font.h"
-#include "ui/views/controls/styled_label.h"
-#include "ui/views/layout/fill_layout.h"
-
-DesktopIOSPromotionFootnoteView::DesktopIOSPromotionFootnoteView(
-    Profile* profile,
-    DesktopIOSPromotionFootnoteDelegate* delegate)
-    : StyledLabel(base::string16(), this),
-      promotion_controller_(std::make_unique<DesktopIOSPromotionController>(
-          profile,
-          desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_FOOTNOTE)),
-      delegate_(delegate) {
-  size_t offset = 0;
-  base::string16 link_text =
-      l10n_util::GetStringUTF16(IDS_FOOTNOTE_DESKTOP_TO_IOS_PROMO_LINK);
-  base::string16 promo_text = l10n_util::GetStringFUTF16(
-      IDS_BOOKMARK_FOOTNOTE_DESKTOP_TO_IOS_PROMO_MESSAGE, link_text, &offset);
-  SetText(promo_text);
-
-  AddStyleRange(gfx::Range(offset, offset + link_text.length()),
-                views::StyledLabel::RangeStyleInfo::CreateForLink());
-
-  views::StyledLabel::RangeStyleInfo promo_style;
-  promo_style.text_style = STYLE_SECONDARY;
-  gfx::Range before_link_range(0, offset);
-  if (!before_link_range.is_empty())
-    AddStyleRange(before_link_range, promo_style);
-  gfx::Range after_link_range(offset + link_text.length(), promo_text.length());
-  if (!after_link_range.is_empty())
-    AddStyleRange(after_link_range, promo_style);
-  promotion_controller_->OnPromotionShown();
-}
-
-DesktopIOSPromotionFootnoteView::~DesktopIOSPromotionFootnoteView() {}
-
-void DesktopIOSPromotionFootnoteView::StyledLabelLinkClicked(
-    views::StyledLabel* label,
-    const gfx::Range& range,
-    int event_flags) {
-  promotion_controller_->OnLearnMoreLinkClicked();
-  delegate_->OnIOSPromotionFootnoteLinkClicked();
-}
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.h b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.h
deleted file mode 100644
index 54460ac..0000000
--- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_footnote_view.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_FOOTNOTE_VIEW_H_
-#define CHROME_BROWSER_UI_VIEWS_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_FOOTNOTE_VIEW_H_
-
-#include "base/macros.h"
-#include "ui/views/controls/styled_label.h"
-#include "ui/views/controls/styled_label_listener.h"
-
-class DesktopIOSPromotionController;
-class DesktopIOSPromotionFootnoteDelegate;
-class Profile;
-
-// Desktop iOS promotion Footnote displayed at the bottom of bookmark bubble.
-class DesktopIOSPromotionFootnoteView : public views::StyledLabel,
-                                        public views::StyledLabelListener {
- public:
-  DesktopIOSPromotionFootnoteView(
-      Profile* profile,
-      DesktopIOSPromotionFootnoteDelegate* delegate);
-  ~DesktopIOSPromotionFootnoteView() override;
-
- private:
-  // views::StyledLabelListener:
-  void StyledLabelLinkClicked(views::StyledLabel* label,
-                              const gfx::Range& range,
-                              int event_flags) override;
-
-  std::unique_ptr<DesktopIOSPromotionController> promotion_controller_;
-  // Delegate, to handle clicks on the sign in link.
-  DesktopIOSPromotionFootnoteDelegate* delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopIOSPromotionFootnoteView);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_DESKTOP_IOS_PROMOTION_DESKTOP_IOS_PROMOTION_FOOTNOTE_VIEW_H_
diff --git a/chrome/browser/ui/views/message_center/popups_only_ui_delegate_unittest.cc b/chrome/browser/ui/views/message_center/popups_only_ui_delegate_unittest.cc
index 54baeca..2986c5b65 100644
--- a/chrome/browser/ui/views/message_center/popups_only_ui_delegate_unittest.cc
+++ b/chrome/browser/ui/views/message_center/popups_only_ui_delegate_unittest.cc
@@ -92,8 +92,7 @@
 };
 
 TEST_F(PopupsOnlyUiDelegateTest, WebNotificationPopupBubble) {
-  auto ui_controller = std::make_unique<PopupsOnlyUiController>(
-      std::make_unique<PopupsOnlyUiDelegate>());
+  auto ui_controller = std::make_unique<PopupsOnlyUiController>();
 
   // Adding a notification should show the popup bubble.
   AddNotification("id1");
@@ -116,8 +115,7 @@
 }
 
 TEST_F(PopupsOnlyUiDelegateTest, ManyPopupNotifications) {
-  auto ui_controller = std::make_unique<PopupsOnlyUiController>(
-      std::make_unique<PopupsOnlyUiDelegate>());
+  auto ui_controller = std::make_unique<PopupsOnlyUiController>();
 
   // Add the max visible popup notifications +1, ensure the correct num visible.
   size_t notifications_to_add =
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.cc b/chrome/browser/ui/views/passwords/password_pending_view.cc
index 794dfe0..5fa8190 100644
--- a/chrome/browser/ui/views/passwords/password_pending_view.cc
+++ b/chrome/browser/ui/views/passwords/password_pending_view.cc
@@ -36,10 +36,6 @@
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/window/dialog_client_view.h"
 
-#if defined(OS_WIN)
-#include "chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h"
-#endif
-
 namespace {
 
 // TODO(pbos): Investigate expicitly obfuscating items inside ComboboxModel.
@@ -248,7 +244,6 @@
       is_update_bubble_(model()->state() ==
                         password_manager::ui::PENDING_PASSWORD_UPDATE_STATE),
       sign_in_promo_(nullptr),
-      desktop_ios_promo_(nullptr),
       username_field_(nullptr),
       password_view_button_(nullptr),
       initially_focused_view_(nullptr),
@@ -319,10 +314,6 @@
 bool PasswordPendingView::Accept() {
   if (sign_in_promo_)
     return sign_in_promo_->Accept();
-#if defined(OS_WIN)
-  if (desktop_ios_promo_)
-    return desktop_ios_promo_->Accept();
-#endif
   UpdateUsernameAndPasswordInModel();
   model()->OnSaveClicked();
   if (model()->ReplaceToShowPromotionIfNeeded()) {
@@ -335,10 +326,6 @@
 bool PasswordPendingView::Cancel() {
   if (sign_in_promo_)
     return sign_in_promo_->Cancel();
-#if defined(OS_WIN)
-  if (desktop_ios_promo_)
-    return desktop_ios_promo_->Cancel();
-#endif
   UpdateUsernameAndPasswordInModel();
   if (is_update_bubble_) {
     model()->OnNopeUpdateClicked();
@@ -370,7 +357,7 @@
 }
 
 views::View* PasswordPendingView::CreateFootnoteView() {
-  if (sign_in_promo_ || desktop_ios_promo_ || !model()->ShouldShowFooter())
+  if (sign_in_promo_ || !model()->ShouldShowFooter())
     return nullptr;
   views::Label* label = new views::Label(
       l10n_util::GetStringUTF16(IDS_SAVE_PASSWORD_FOOTER),
@@ -406,10 +393,6 @@
   // ask each different possible promo.
   if (sign_in_promo_)
     return sign_in_promo_->GetDialogButtonLabel(button);
-#if defined(OS_WIN)
-  if (desktop_ios_promo_)
-    return desktop_ios_promo_->GetDialogButtonLabel(button);
-#endif
 
   int message = 0;
   if (button == ui::DIALOG_BUTTON_OK) {
@@ -425,15 +408,11 @@
 }
 
 gfx::ImageSkia PasswordPendingView::GetWindowIcon() {
-#if defined(OS_WIN)
-  if (desktop_ios_promo_)
-    return desktop_ios_promo_->GetWindowIcon();
-#endif
   return gfx::ImageSkia();
 }
 
 bool PasswordPendingView::ShouldShowWindowIcon() const {
-  return desktop_ios_promo_ != nullptr;
+  return false;
 }
 
 bool PasswordPendingView::ShouldShowCloseButton() const {
@@ -487,14 +466,6 @@
   if (model()->state() == password_manager::ui::CHROME_SIGN_IN_PROMO_STATE) {
     sign_in_promo_ = new PasswordSignInPromoView(model());
     AddChildView(sign_in_promo_);
-#if defined(OS_WIN)
-  } else if (model()->state() ==
-             password_manager::ui::CHROME_DESKTOP_IOS_PROMO_STATE) {
-    desktop_ios_promo_ = new DesktopIOSPromotionBubbleView(
-        model()->GetProfile(),
-        desktop_ios_promotion::PromotionEntryPoint::SAVE_PASSWORD_BUBBLE);
-    AddChildView(desktop_ios_promo_);
-#endif
   } else {
     NOTREACHED();
   }
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.h b/chrome/browser/ui/views/passwords/password_pending_view.h
index fb3ed536..30b58341 100644
--- a/chrome/browser/ui/views/passwords/password_pending_view.h
+++ b/chrome/browser/ui/views/passwords/password_pending_view.h
@@ -16,7 +16,6 @@
 class ToggleImageButton;
 }  // namespace views
 
-class DesktopIOSPromotionBubbleView;
 class PasswordSignInPromoView;
 
 // A view offering the user the ability to save or update credentials (depending
@@ -70,7 +69,6 @@
   // across devices. One of these are non-null when the promotion dialog is
   // active.
   PasswordSignInPromoView* sign_in_promo_;
-  DesktopIOSPromotionBubbleView* desktop_ios_promo_;
 
   views::View* username_field_;
   views::ToggleImageButton* password_view_button_;
diff --git a/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc
index 9ef9d88b..a9a76355 100644
--- a/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc
@@ -355,6 +355,9 @@
 // should return false for them.
 IN_PROC_BROWSER_TEST_F(PaymentRequestCanMakePaymentQueryPMITest,
                        QueryQuotaForPaymentApps) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kWebPaymentsPerMethodCanMakePaymentQuota);
   NavigateTo("/payment_request_payment_method_identifier_test.html");
   CallCanMakePayment(CheckFor::ALICE_PAY);
 
@@ -411,8 +414,10 @@
                        QueryQuotaForPaymentAppsInIncognitoMode) {
   NavigateTo("/payment_request_payment_method_identifier_test.html");
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      ::features::kServiceWorkerPaymentApps);
+  scoped_feature_list.InitWithFeatures(
+      /*enable_features=*/{::features::kServiceWorkerPaymentApps,
+                           features::kWebPaymentsPerMethodCanMakePaymentQuota},
+      /*disable_features=*/{});
 
   SetIncognito();
 
@@ -439,6 +444,9 @@
 // NotSupportedError to avoid user fingerprinting.
 IN_PROC_BROWSER_TEST_F(PaymentRequestCanMakePaymentQueryPMITest,
                        NoQueryQuotaForPaymentAppsAndCardsInIncognito) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kWebPaymentsPerMethodCanMakePaymentQuota);
   NavigateTo("/payment_request_payment_method_identifier_test.html");
   SetIncognito();
 
diff --git a/chrome/browser/ui/webui/sync_internals_message_handler.cc b/chrome/browser/ui/webui/sync_internals_message_handler.cc
index bc6b01b9..6c9f817 100644
--- a/chrome/browser/ui/webui/sync_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/sync_internals_message_handler.cc
@@ -4,11 +4,10 @@
 
 #include "chrome/browser/ui/webui/sync_internals_message_handler.h"
 
-#include <stdint.h>
-
 #include <utility>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
@@ -17,9 +16,7 @@
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "chrome/common/channel_info.h"
 #include "components/browser_sync/profile_sync_service.h"
-#include "components/sync/base/enum_set.h"
 #include "components/sync/base/model_type.h"
-#include "components/sync/base/weak_handle.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service.h"
@@ -36,11 +33,7 @@
 using base::DictionaryValue;
 using base::ListValue;
 using base::Value;
-using browser_sync::ProfileSyncService;
-using syncer::JsEventDetails;
-using syncer::ModelTypeSet;
 using syncer::SyncService;
-using syncer::WeakHandle;
 
 namespace {
 
@@ -224,7 +217,7 @@
 
   DictionaryValue event_details;
   auto type_list = std::make_unique<ListValue>();
-  ModelTypeSet protocol_types = syncer::ProtocolTypes();
+  syncer::ModelTypeSet protocol_types = syncer::ProtocolTypes();
   for (syncer::ModelType type : protocol_types) {
     type_list->AppendString(ModelTypeToString(type));
   }
@@ -404,7 +397,7 @@
 
 void SyncInternalsMessageHandler::HandleJsEvent(
     const std::string& name,
-    const JsEventDetails& details) {
+    const syncer::JsEventDetails& details) {
   DVLOG(1) << "Handling event: " << name
            << " with details " << details.ToString();
   DispatchEvent(name, details.Get());
diff --git a/chrome/browser/ui/webui/sync_internals_message_handler.h b/chrome/browser/ui/webui/sync_internals_message_handler.h
index 3f94dce4..73c613c6 100644
--- a/chrome/browser/ui/webui/sync_internals_message_handler.h
+++ b/chrome/browser/ui/webui/sync_internals_message_handler.h
@@ -8,10 +8,8 @@
 #include <memory>
 #include <string>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/scoped_observer.h"
 #include "base/values.h"
 #include "components/sync/driver/sync_service_observer.h"
 #include "components/sync/engine/cycle/type_debug_info_observer.h"
@@ -21,10 +19,6 @@
 #include "components/version_info/channel.h"
 #include "content/public/browser/web_ui_message_handler.h"
 
-namespace browser_sync {
-class ProfileSyncService;
-}  // namespace browser_sync
-
 namespace syncer {
 class SyncService;
 }  //  namespace syncer
diff --git a/chrome/browser/vr/font_fallback.cc b/chrome/browser/vr/font_fallback.cc
index dc64768..09c67a2 100644
--- a/chrome/browser/vr/font_fallback.cc
+++ b/chrome/browser/vr/font_fallback.cc
@@ -47,8 +47,7 @@
     auto& supported = supported_scripts_[script];
     if (supported != UNDEFINED)
       return supported == KNOWN;
-    uint16_t glyph_id;
-    paint_.textToGlyphs(&character, sizeof(UChar32), &glyph_id);
+    uint16_t glyph_id = typeface_->unicharToGlyph(character);
     supported = glyph_id ? KNOWN : UNKNOWN;
     return supported == KNOWN;
   }
@@ -59,11 +58,10 @@
     SkString sk_name;
     skia_face->getFamilyName(&sk_name);
     name_ = std::string(sk_name.c_str(), sk_name.size());
-    paint_.setTypeface(std::move(skia_face));
-    paint_.setTextEncoding(SkPaint::kUTF32_TextEncoding);
+    typeface_ = std::move(skia_face);
   }
 
-  SkPaint paint_;
+  sk_sp<SkTypeface> typeface_;
   std::map<UScriptCode, KnownGlyph> supported_scripts_;
   std::string name_;
 
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 8a4114a..275133cb 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -183,13 +183,6 @@
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
-#if defined(OS_WIN)
-// Enables or disables desktop ios promotion, which shows a promotion to the
-// user promoting Chrome for iOS.
-const base::Feature kDesktopIOSPromotion{"DesktopIOSPromotion",
-                                         base::FEATURE_DISABLED_BY_DEFAULT};
-#endif
-
 // Enables or disables windowing related features for desktop PWAs.
 const base::Feature kDesktopPWAWindowing {
   "DesktopPWAWindowing",
@@ -496,6 +489,11 @@
 const base::Feature kSecurityKeyAttestationPrompt{
     "SecurityKeyAttestationPrompt", base::FEATURE_ENABLED_BY_DEFAULT};
 
+#if !defined(OS_ANDROID)
+const base::Feature kShowManagedUi{"ShowManagedUi",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 #if defined(OS_ANDROID)
 const base::Feature kShowTrustedPublisherURL{"ShowTrustedPublisherURL",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 5f14f3b..7daff28 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -117,11 +117,6 @@
 extern const base::Feature kUsageTimeLimitPolicy;
 #endif
 
-#if defined(OS_WIN)
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::Feature kDesktopIOSPromotion;
-#endif  // defined(OS_WIN)
-
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kDesktopPWAWindowing;
 
@@ -335,6 +330,10 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSecurityKeyAttestationPrompt;
 
+#if !defined(OS_ANDROID)
+COMPONENT_EXPORT(CHROME_FEATURES) extern const base::Feature kShowManagedUi;
+#endif
+
 #if defined(OS_ANDROID)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kShowTrustedPublisherURL;
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 4b78f5f..acf3421 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -364,10 +364,6 @@
 // the app to be installed if it hasn't been already.
 const char kForceAppMode[]                  = "force-app-mode";
 
-// Forces Desktop to iOS promotion to appear in windows whenever an entrypoint
-// is triggered.
-const char kForceDesktopIOSPromotion[] = "force-desktop-ios-promotion";
-
 // Displays the First Run experience when the browser is started, regardless of
 // whether or not it's actually the First Run (this overrides kNoFirstRun).
 const char kForceFirstRun[]                 = "force-first-run";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 9618667..beaeb052 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -115,7 +115,6 @@
 extern const char kFastStart[];
 extern const char kForceAndroidAppMode[];
 extern const char kForceAppMode[];
-extern const char kForceDesktopIOSPromotion[];
 extern const char kForceFirstRun[];
 extern const char kForceFirstRunDialog[];
 extern const char kForceLocalNtp[];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index d4374bd..7cd0512 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2386,77 +2386,6 @@
 const char kWebShareVisitedTargets[] = "profile.web_share.visited_targets";
 
 #if defined(OS_WIN)
-// True if the user is eligible to recieve "desktop to iOS" promotion. This
-// vlaue is set by a job that access the growth table to check users with iOS
-// devices and phone recovery number and update the eligibility on chrome sync.
-const char kIOSPromotionEligible[] = "ios.desktoptomobileeligible";
-
-// True if the "desktop to iOS" promotion was successful, i.e. user installed
-// the application and signed in on the iOS client after seeing the promotion
-// and receiving the SMS.
-const char kIOSPromotionDone[] = "ios.desktop_ios_promo_done";
-
-// Index of the entry point that last initiated sending the SMS to the user for
-// the "desktop to iOS" promotion (see DesktopIOSPromotion.IOSSigninReason
-// histogram for details).
-const char kIOSPromotionSMSEntryPoint[] =
-    "ios.desktop_ios_promo_sms_entrypoint";
-
-// Bit mask that represents the Indices of all the entry points shown to the
-// user for "desktop to iOS" promotion. Each entry point is represented by
-// 1<<entrypoint_value using the values from the Enum
-// desktop_ios_promotion::PromotionEntryPoint.
-const char kIOSPromotionShownEntryPoints[] =
-    "ios.desktop_ios_promo_shown_entrypoints";
-
-// Timestamp of the last "desktop to iOS" promotion last impression. If the
-// user sends SMS on that impression then we deal with this timestamp as the
-// SMS sending time because after sending the sms the user shouldn't see the
-// promotion again (Accuracy to the minutes and seconds is not important).
-const char kIOSPromotionLastImpression[] =
-    "ios.desktop_ios_promo_last_impression";
-
-// Integer that represents which variation of title and text of the
-// "desktop to iOS" promotion was presented to the user.
-const char kIOSPromotionVariationId[] = "ios.desktop_ios_promo_variation_id";
-
-// Number of times user has seen the "desktop to iOS" save passwords bubble
-// promotion.
-const char kNumberSavePasswordsBubbleIOSPromoShown[] =
-    "savepasswords_bubble_ios_promo_shown_count";
-
-// True if the user has dismissed the "desktop to iOS" save passwords bubble
-// promotion.
-const char kSavePasswordsBubbleIOSPromoDismissed[] =
-    "savepasswords_bubble_ios_promo_dismissed";
-
-// Number of times the user has seen the "desktop to iOS" bookmarks bubble
-// promotion.
-const char kNumberBookmarksBubbleIOSPromoShown[] =
-    "bookmarks_bubble_ios_promo_shown_count";
-
-// True if the user has dismissed the "desktop to iOS" bookmarks bubble
-// promotion.
-const char kBookmarksBubbleIOSPromoDismissed[] =
-    "bookmarks_bubble_ios_promo_dismissed";
-
-// Number of times user has seen the "desktop to iOS" bookmarks foot note
-// promotion.
-const char kNumberBookmarksFootNoteIOSPromoShown[] =
-    "bookmarks_footnote_ios_promo_shown_count";
-
-// True if the user has dismissed the "desktop to iOS" bookmarks foot note
-// promotion.
-const char kBookmarksFootNoteIOSPromoDismissed[] =
-    "bookmarks_footnote_ios_promo_dismissed";
-
-// Number of times user has seen the "desktop to iOS" history page promotion.
-const char kNumberHistoryPageIOSPromoShown[] =
-    "history_page_ios_promo_shown_count";
-
-// True if the user has dismissed the "desktop to iOS" history page promotion.
-const char kHistoryPageIOSPromoDismissed[] = "history_page_ios_promo_dismissed";
-
 #if defined(GOOGLE_CHROME_BUILD)
 // Acts as a cache to remember incompatible applications through restarts. Used
 // for the Incompatible Applications Warning feature.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 7a8a38e..f33408b 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -839,21 +839,6 @@
 extern const char kWebShareVisitedTargets[];
 
 #if defined(OS_WIN)
-extern const char kIOSPromotionEligible[];
-extern const char kIOSPromotionDone[];
-extern const char kIOSPromotionSMSEntryPoint[];
-extern const char kIOSPromotionShownEntryPoints[];
-extern const char kIOSPromotionLastImpression[];
-extern const char kIOSPromotionVariationId[];
-extern const char kNumberSavePasswordsBubbleIOSPromoShown[];
-extern const char kSavePasswordsBubbleIOSPromoDismissed[];
-extern const char kNumberBookmarksBubbleIOSPromoShown[];
-extern const char kBookmarksBubbleIOSPromoDismissed[];
-extern const char kNumberBookmarksFootNoteIOSPromoShown[];
-extern const char kBookmarksFootNoteIOSPromoDismissed[];
-extern const char kNumberHistoryPageIOSPromoShown[];
-extern const char kHistoryPageIOSPromoDismissed[];
-
 #if defined(GOOGLE_CHROME_BUILD)
 extern const char kIncompatibleApplications[];
 extern const char kModuleBlacklistCacheMD5Digest[];
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index e72219a..827615a 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -152,6 +152,10 @@
 const char kLegacySupervisedUserManagementURL[] =
     "https://www.chrome.com/manage";
 
+// TODO(nicolaso): Replace with a p-link once it's ready. b/117655761
+const char kManagedUiLearnMoreUrl[] =
+    "https://support.google.com/chromebook/answer/1331549";
+
 const char kMyActivityUrlInClearBrowsingData[] =
     "https://myactivity.google.com/myactivity/?utm_source=chrome_cbd";
 
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 70e18fc..ef278f31 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -142,6 +142,9 @@
 // Management URL for Chrome Supervised Users.
 extern const char kLegacySupervisedUserManagementURL[];
 
+// The URL for the Learn More page about policies and enterprise enrollment.
+extern const char kManagedUiLearnMoreUrl[];
+
 // "myactivity.google.com" URL for the history checkbox in ClearBrowsingData.
 extern const char kMyActivityUrlInClearBrowsingData[];
 
diff --git a/chrome/installer/mini_installer/BUILD.gn b/chrome/installer/mini_installer/BUILD.gn
index 7001812..931761f 100644
--- a/chrome/installer/mini_installer/BUILD.gn
+++ b/chrome/installer/mini_installer/BUILD.gn
@@ -5,7 +5,6 @@
 import("//build/config/compiler/compiler.gni")
 import("//build/config/features.gni")
 import("//build/config/ui.gni")
-import("//build/timestamp.gni")
 import("//chrome/process_version_rc_template.gni")
 import("//components/nacl/features.gni")
 import("//third_party/icu/config.gni")
@@ -147,8 +146,6 @@
       packed_files_rc_file,
     ]
     args = [
-      "--timestamp",
-      build_timestamp,
       "--build_dir",
       rebase_path(root_out_dir, root_build_dir),
       "--staging_dir",
diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
index a892428..9dd55a4 100644
--- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
@@ -86,6 +86,10 @@
             base::BindRepeating([](mojo::ScopedInterfaceEndpointHandle handle) {
               handle.reset();
             }));
+
+    // Necessary for focus changes to work correctly and dispatch blur events
+    // when a field was previously focused.
+    GetWebWidget()->SetFocus(true);
   }
 
   void TearDown() override {
@@ -1088,4 +1092,105 @@
   }
 }
 
+TEST_F(PasswordGenerationAgentTest, PasswordUnmaskedUntilCompleteDeletion) {
+  LoadHTMLWithUserGesture(kAccountCreationFormHTML);
+  SetNotBlacklistedMessage(password_generation_, kAccountCreationFormHTML);
+  SetAccountCreationFormsDetectedMessage(password_generation_,
+                                         GetMainFrame()->GetDocument(), 0, 1);
+
+  constexpr char kGenerationElementId[] = "first_password";
+
+  // Generate a new password.
+  FocusField(kGenerationElementId);
+  base::string16 password = base::ASCIIToUTF16("random_password");
+  EXPECT_CALL(fake_pw_client_,
+              PresaveGeneratedPassword(testing::Field(
+                  &autofill::PasswordForm::password_value, password)));
+  password_generation_->GeneratedPasswordAccepted(password);
+  fake_pw_client_.Flush();
+  testing::Mock::VerifyAndClearExpectations(&fake_pw_client_);
+
+  // Delete characters of the generated password until only
+  // |kMinimumLengthForEditedPassword| - 1 chars remain.
+  fake_pw_client_.reset_called_automatic_generation_status_changed_true();
+  FocusField(kGenerationElementId);
+  EXPECT_CALL(fake_pw_client_, PasswordNoLongerGenerated(testing::_));
+  size_t max_chars_to_delete =
+      password.length() -
+      PasswordGenerationAgent::kMinimumLengthForEditedPassword + 1;
+  for (size_t i = 0; i < max_chars_to_delete; ++i)
+    SimulateUserTypingASCIICharacter(ui::VKEY_BACK, false);
+  base::RunLoop().RunUntilIdle();
+  // The remaining characters no longer count as a generated password, so
+  // generation should be offered again.
+  EXPECT_TRUE(GetCalledAutomaticGenerationStatusChangedTrue());
+
+  // Check that the characters remain unmasked.
+  WebDocument document = GetMainFrame()->GetDocument();
+  blink::WebElement element =
+      document.GetElementById(blink::WebString::FromUTF8(kGenerationElementId));
+  ASSERT_FALSE(element.IsNull());
+  blink::WebInputElement input = element.To<WebInputElement>();
+  EXPECT_TRUE(input.ShouldRevealPassword());
+
+  // Delete the rest of the characters. The field should now mask new
+  // characters.
+  for (size_t i = 0;
+       i < PasswordGenerationAgent::kMinimumLengthForEditedPassword; ++i)
+    SimulateUserTypingASCIICharacter(ui::VKEY_BACK, false);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(input.ShouldRevealPassword());
+}
+
+TEST_F(PasswordGenerationAgentTest, ShortPasswordMaskedAfterChangingFocus) {
+  LoadHTMLWithUserGesture(kPasswordFormAndSpanHTML);
+  SetNotBlacklistedMessage(password_generation_, kPasswordFormAndSpanHTML);
+  SetAccountCreationFormsDetectedMessage(password_generation_,
+                                         GetMainFrame()->GetDocument(), 0, 1);
+
+  constexpr char kGenerationElementId[] = "password";
+
+  // Generate a new password.
+  FocusField(kGenerationElementId);
+  base::string16 password = base::ASCIIToUTF16("random_password");
+  EXPECT_CALL(fake_pw_client_,
+              PresaveGeneratedPassword(testing::Field(
+                  &autofill::PasswordForm::password_value, password)));
+  password_generation_->GeneratedPasswordAccepted(password);
+  fake_pw_client_.Flush();
+  testing::Mock::VerifyAndClearExpectations(&fake_pw_client_);
+
+  // Delete characters of the generated password until only
+  // |kMinimumLengthForEditedPassword| - 1 chars remain.
+  fake_pw_client_.reset_called_automatic_generation_status_changed_true();
+  FocusField(kGenerationElementId);
+  EXPECT_CALL(fake_pw_client_, PasswordNoLongerGenerated(testing::_));
+  size_t max_chars_to_delete =
+      password.length() -
+      PasswordGenerationAgent::kMinimumLengthForEditedPassword + 1;
+  for (size_t i = 0; i < max_chars_to_delete; ++i)
+    SimulateUserTypingASCIICharacter(ui::VKEY_BACK, false);
+  // The remaining characters no longer count as a generated password, so
+  // generation should be offered again.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(GetCalledAutomaticGenerationStatusChangedTrue());
+
+  // Check that the characters remain unmasked.
+  WebDocument document = GetMainFrame()->GetDocument();
+  blink::WebElement element =
+      document.GetElementById(blink::WebString::FromUTF8(kGenerationElementId));
+  ASSERT_FALSE(element.IsNull());
+  blink::WebInputElement input = element.To<WebInputElement>();
+  EXPECT_TRUE(input.ShouldRevealPassword());
+
+  // Focus another element on the page. The password should be masked.
+  ASSERT_TRUE(SimulateElementClick("span"));
+  EXPECT_FALSE(input.ShouldRevealPassword());
+
+  // Focus the password field again. As the remaining characters are not
+  // a generated password, they should remain masked.
+  FocusField(kGenerationElementId);
+  EXPECT_FALSE(input.ShouldRevealPassword());
+}
+
 }  // namespace autofill
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index f0decc2..c354063 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -787,6 +787,7 @@
       "../browser/ui/blocked_content/popup_tracker_browsertest.cc",
       "../browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc",
       "../browser/ui/blocked_content/tab_under_blocker_browsertest.cc",
+      "../browser/ui/managed_ui_browsertest.cc",
       "../browser/ui/omnibox/lookalike_url_navigation_observer_browsertest.cc",
       "../browser/ui/tabs/pinned_tab_service_browsertest.cc",
 
@@ -4169,9 +4170,6 @@
     sources += [
       "../browser/notifications/win/notification_image_retainer_unittest.cc",
       "../browser/notifications/win/notification_template_builder_unittest.cc",
-      "../browser/ui/desktop_ios_promotion/desktop_ios_promotion_bubble_controller_unittest.cc",
-      "../browser/ui/desktop_ios_promotion/desktop_ios_promotion_util_unittest.cc",
-      "../browser/ui/desktop_ios_promotion/sms_service_unittest.cc",
       "../browser/ui/input_method/input_method_engine_unittest.cc",
       "../test/data/resource.rc",
     ]
diff --git a/chrome/tools/build/win/create_installer_archive.py b/chrome/tools/build/win/create_installer_archive.py
index 60c1215..0e12f2a1 100755
--- a/chrome/tools/build/win/create_installer_archive.py
+++ b/chrome/tools/build/win/create_installer_archive.py
@@ -94,12 +94,11 @@
   RunSystemCommand(cmd, verbose)
 
 
-def CopyAllNonComponentFilesToStagingDir(config, distribution, staging_dir,
-                                         build_dir, enable_hidpi):
+def CopyAllFilesToStagingDir(config, distribution, staging_dir, build_dir,
+                             enable_hidpi):
   """Copies the files required for installer archive.
   Copies all common files required for various distributions of Chromium and
   also files for the specific Chromium build specified by distribution.
-  Files that might be needed in a component build are copied later.
   """
   CopySectionFilesToStagingDir(config, 'GENERAL', staging_dir, build_dir)
   if distribution:
@@ -542,27 +541,15 @@
                                 options.output_name)
 
   # Copy the files from the build dir.
-  CopyAllNonComponentFilesToStagingDir(config, options.distribution,
-                                       staging_dir, options.build_dir,
-                                       options.enable_hidpi)
+  CopyAllFilesToStagingDir(config, options.distribution,
+                           staging_dir, options.build_dir,
+                           options.enable_hidpi)
 
   if options.component_build == '1':
     DoComponentBuildTasks(staging_dir, options.build_dir,
                           options.target_arch, options.setup_runtime_deps,
                           options.chrome_runtime_deps, current_version)
 
-  # 7za/7zr don't have a flag to set the timestamp of files in the archive
-  # (important for build reproducibility). Since everything gets copied into
-  # a staging dir, just set the on-disk mtime of everything in the staging
-  # dir instead.
-  if options.timestamp is not None:
-    timestamp_mtime_atime = (options.timestamp, options.timestamp)
-    for path, dirs, files in os.walk(staging_dir):
-      for f in files:
-        os.utime(os.path.join(path, f), timestamp_mtime_atime)
-      for d in dirs:
-        os.utime(os.path.join(path, d), timestamp_mtime_atime)
-
   version_numbers = current_version.split('.')
   current_build_number = version_numbers[2] + '.' + version_numbers[3]
   prev_build_number = ''
@@ -636,8 +623,6 @@
       help='Specify the target architecture for installer - this is used '
            'to determine which CRT runtime files to pull and package '
            'with the installer archive {x86|x64}.')
-  parser.add_option('--timestamp', type=int,
-      help='If set, used as timestamp for all files in the archive.')
   parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
                     default=False)
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 8f7a8c60..c632b82 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-11229.0.0
\ No newline at end of file
+11230.0.0
\ No newline at end of file
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index d0e7305..cf40992 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -47,6 +47,12 @@
       "assistant_manager_service_impl.h",
       "assistant_settings_manager_impl.cc",
       "assistant_settings_manager_impl.h",
+      "chromium_api_delegate.cc",
+      "chromium_api_delegate.h",
+      "chromium_http_connection.cc",
+      "chromium_http_connection.h",
+      "default_url_request_context_getter.cc",
+      "default_url_request_context_getter.h",
       "platform/audio_input_provider_impl.cc",
       "platform/audio_input_provider_impl.h",
       "platform/audio_media_data_source.cc",
@@ -78,9 +84,11 @@
       "//libassistant/shared/internal_api/c:api_wrappers_entrypoint",
       "//libassistant/shared/public",
       "//libassistant/shared/public:export",
+      "//net",
       "//services/network/public/cpp",
       "//services/network/public/mojom",
       "//ui/base",
+      "//url",
     ]
 
     libs = [ "$root_out_dir/libassistant.so" ]
diff --git a/chromeos/services/assistant/chromium_api_delegate.cc b/chromeos/services/assistant/chromium_api_delegate.cc
new file mode 100644
index 0000000..31e8aa7
--- /dev/null
+++ b/chromeos/services/assistant/chromium_api_delegate.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 "chromeos/services/assistant/chromium_api_delegate.h"
+
+#include "chromeos/services/assistant/default_url_request_context_getter.h"
+
+namespace chromeos {
+namespace assistant {
+
+ChromiumApiDelegate::ChromiumApiDelegate()
+    : http_connection_factory_(
+          base::MakeRefCounted<DefaultURLRequestContextGetter>(
+              "chromium_http_connection")) {}
+
+ChromiumApiDelegate::~ChromiumApiDelegate() = default;
+
+assistant_client::HttpConnectionFactory*
+ChromiumApiDelegate::GetHttpConnectionFactory() {
+  return &http_connection_factory_;
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/chromium_api_delegate.h b/chromeos/services/assistant/chromium_api_delegate.h
new file mode 100644
index 0000000..b958832
--- /dev/null
+++ b/chromeos/services/assistant/chromium_api_delegate.h
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_ASSISTANT_CHROMIUM_API_DELEGATE_H_
+#define CHROMEOS_SERVICES_ASSISTANT_CHROMIUM_API_DELEGATE_H_
+
+#include "chromeos/services/assistant/chromium_http_connection.h"
+
+#include <memory>
+
+#include "base/macros.h"
+#include "libassistant/shared/internal_api/fuchsia_api_helper.h"
+
+namespace chromeos {
+namespace assistant {
+
+class ChromiumHttpConnectionFactory;
+
+class ChromiumApiDelegate : public assistant_client::FuchsiaApiDelegate {
+ public:
+  ChromiumApiDelegate();
+  ~ChromiumApiDelegate() override;
+  // assistant_client::FuchsiaApiDelegate overrides:
+  assistant_client::HttpConnectionFactory* GetHttpConnectionFactory() override;
+
+ private:
+  ChromiumHttpConnectionFactory http_connection_factory_;
+  DISALLOW_COPY_AND_ASSIGN(ChromiumApiDelegate);
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_CHROMIUM_API_DELEGATE_H_
diff --git a/chromeos/services/assistant/chromium_http_connection.cc b/chromeos/services/assistant/chromium_http_connection.cc
new file mode 100644
index 0000000..3d49ce56
--- /dev/null
+++ b/chromeos/services/assistant/chromium_http_connection.cc
@@ -0,0 +1,375 @@
+// 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.
+
+// The file comes from Google Home(cast) implementation.
+
+#include "chromeos/services/assistant/chromium_http_connection.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_fetcher_response_writer.h"
+#include "net/url_request/url_request_context_getter.h"
+
+using net::URLFetcher;
+using net::URLRequestStatus;
+using HttpResponseHeaders = net::HttpResponseHeaders;
+using assistant_client::HttpConnection;
+
+namespace chromeos {
+namespace assistant {
+
+namespace {
+
+// Passes response data to ChromiumHttpConnection's Delegate as it's received
+// from URLFetcher. Only should be active if EnablePartialResults() has been
+// called.
+class URLFetcherPartialResponseWriter : public ::net::URLFetcherResponseWriter {
+ public:
+  URLFetcherPartialResponseWriter(HttpConnection::Delegate* delegate,
+                                  const ::net::URLFetcher* fetcher)
+      : delegate_(delegate) {
+    DCHECK(delegate_);
+    DCHECK(fetcher);
+    // See comments in Initialize(). This class assumes URLFetcher is not setup
+    // to automatically retry on error (which is URLFetcher's default behavior).
+    DCHECK_EQ(fetcher->GetMaxRetriesOn5xx(), 0);
+  }
+
+  ~URLFetcherPartialResponseWriter() override = default;
+
+  // ::net::URLFetcherResponseWriter implementation:
+  int Initialize(::net::CompletionOnceCallback callback) override {
+    // The API states that "Calling this method again after a Initialize()
+    // success results in discarding already written data". Libassistant's
+    // HttpConnection API does not provide a way of doing this. However, this is
+    // not an issue because URLFetcher only calls Initialize() multiple times
+    // for:
+    // * Automatic retries of a 500 status.
+    // * Automatic retries when the network changes.
+    // Both of these automatic retries are explicitly disabled in
+    // ChromiumHttpConnection, so no action is required here. The DCHECK below
+    // should fail if this assumption is wrong.
+    DCHECK_EQ(total_bytes_written_, 0);
+    return ::net::OK;
+  }
+
+  int Write(::net::IOBuffer* buffer,
+            int num_bytes,
+            ::net::CompletionOnceCallback callback) override {
+    DCHECK(buffer);
+    VLOG(2) << "Notifying Delegate of partial response data";
+    std::string response(buffer->data(), num_bytes);
+    delegate_->OnPartialResponse(response);
+    total_bytes_written_ += num_bytes;
+    return num_bytes;
+  }
+
+  int Finish(int net_error, ::net::CompletionOnceCallback callback) override {
+    return ::net::OK;
+  }
+
+ private:
+  // ChromiumHttpConnection owns URLFetcher, which owns
+  // URLFetcherPartialResponseWriter. Since HttpConnection::Delegate must
+  // outlive the top-level ChromiumHttpConnection, a raw pointer is safe here.
+  HttpConnection::Delegate* const delegate_;
+  int64_t total_bytes_written_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(URLFetcherPartialResponseWriter);
+};
+
+}  // namespace
+
+ChromiumHttpConnectionFactory::ChromiumHttpConnectionFactory(
+    scoped_refptr<::net::URLRequestContextGetter> url_request_context_getter)
+    : url_request_context_getter_(url_request_context_getter) {}
+
+ChromiumHttpConnectionFactory::~ChromiumHttpConnectionFactory() = default;
+
+HttpConnection* ChromiumHttpConnectionFactory::Create(
+    HttpConnection::Delegate* delegate) {
+  return new ChromiumHttpConnection(url_request_context_getter_, delegate);
+}
+
+ChromiumHttpConnection::ChromiumHttpConnection(
+    scoped_refptr<::net::URLRequestContextGetter> url_request_context_getter,
+    Delegate* delegate)
+    : url_request_context_getter_(url_request_context_getter),
+      delegate_(delegate),
+      network_task_runner_(url_request_context_getter->GetNetworkTaskRunner()) {
+  DCHECK(url_request_context_getter_);
+  DCHECK(delegate_);
+  DCHECK(network_task_runner_);
+
+  // URLFetcher does not ignore client cert requests by default.
+  // We do not have such certificates.
+  // TODO(igorc): Talk to Cast platform to see if we can do better.
+  URLFetcher::SetIgnoreCertificateRequests(true);
+
+  // Add a reference, so |this| cannot go away until Close() is called.
+  AddRef();
+}
+
+ChromiumHttpConnection::~ChromiumHttpConnection() {
+  // The destructor may be called on non-network thread when the connection
+  // is cancelled early, for example due to a reconfigure event.
+  DCHECK(state_ == State::DESTROYED);
+}
+
+void ChromiumHttpConnection::SetRequest(const std::string& url, Method method) {
+  network_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&ChromiumHttpConnection::SetRequestOnThread, this,
+                            url, method));
+}
+
+void ChromiumHttpConnection::SetRequestOnThread(const std::string& url,
+                                                Method method) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(state_ == State::NEW);
+  url_ = GURL(url);
+  method_ = method;
+}
+
+void ChromiumHttpConnection::AddHeader(const std::string& name,
+                                       const std::string& value) {
+  network_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&ChromiumHttpConnection::AddHeaderOnThread, this,
+                            name, value));
+}
+
+void ChromiumHttpConnection::AddHeaderOnThread(const std::string& name,
+                                               const std::string& value) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(state_ == State::NEW);
+  // From https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2:
+  // "Multiple message-header fields with the same field-name MAY be present in
+  // a message if and only if the entire field-value for that header field is
+  // defined as a comma-separated list [i.e., #(values)]. It MUST be possible to
+  // combine the multiple header fields into one "field-name: field-value" pair,
+  // without changing the semantics of the message, by appending each subsequent
+  // field-value to the first, each separated by a comma."
+  std::string existing_value;
+  if (headers_.GetHeader(name, &existing_value)) {
+    headers_.SetHeader(name, existing_value + ',' + value);
+  } else {
+    headers_.SetHeader(name, value);
+  }
+}
+
+void ChromiumHttpConnection::SetUploadContent(const std::string& content,
+                                              const std::string& content_type) {
+  network_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&ChromiumHttpConnection::SetUploadContentOnThread,
+                            this, content, content_type));
+}
+
+void ChromiumHttpConnection::SetUploadContentOnThread(
+    const std::string& content,
+    const std::string& content_type) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(state_ == State::NEW);
+  upload_content_ = content;
+  upload_content_type_ = content_type;
+  chunked_upload_content_type_ = "";
+}
+
+void ChromiumHttpConnection::SetChunkedUploadContentType(
+    const std::string& content_type) {
+  network_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ChromiumHttpConnection::SetChunkedUploadContentTypeOnThread,
+                 this, content_type));
+}
+
+void ChromiumHttpConnection::SetChunkedUploadContentTypeOnThread(
+    const std::string& content_type) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(state_ == State::NEW);
+  upload_content_ = "";
+  upload_content_type_ = "";
+  chunked_upload_content_type_ = content_type;
+}
+
+void ChromiumHttpConnection::EnableHeaderResponse() {
+  NOTIMPLEMENTED();
+}
+
+void ChromiumHttpConnection::EnablePartialResults() {
+  network_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&ChromiumHttpConnection::EnablePartialResultsOnThread, this));
+}
+
+void ChromiumHttpConnection::EnablePartialResultsOnThread() {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(state_ == State::NEW);
+  handle_partial_response_ = true;
+}
+
+void ChromiumHttpConnection::Start() {
+  VLOG(2) << "Requested to start connection";
+  network_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&ChromiumHttpConnection::StartOnThread, this));
+}
+
+void ChromiumHttpConnection::StartOnThread() {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  DCHECK(state_ == State::NEW);
+  state_ = State::STARTED;
+
+  if (!url_.is_valid()) {
+    // Handle invalid URL to prevent URLFetcher crashes.
+    state_ = State::COMPLETED;
+    VLOG(2) << "Completing connection with invalid URL";
+    delegate_->OnNetworkError(-1, "Invalid GURL");
+    return;
+  }
+
+  URLFetcher::RequestType request_type;
+  switch (method_) {
+    case Method::GET:
+      request_type = URLFetcher::RequestType::GET;
+      break;
+    case Method::POST:
+      request_type = URLFetcher::RequestType::POST;
+      break;
+    case Method::HEAD:
+      request_type = URLFetcher::RequestType::HEAD;
+      break;
+  }
+
+  DCHECK(!url_fetcher_);
+  url_fetcher_ = URLFetcher::Create(url_, request_type, this);
+  url_fetcher_->SetLoadFlags(::net::LOAD_DO_NOT_SEND_AUTH_DATA |
+                             ::net::LOAD_DO_NOT_SAVE_COOKIES |
+                             ::net::LOAD_DO_NOT_SEND_COOKIES);
+  url_fetcher_->SetExtraRequestHeaders(headers_.ToString());
+  url_fetcher_->SetAutomaticallyRetryOn5xx(false);
+  url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(false);
+
+  if (handle_partial_response_) {
+    url_fetcher_->SaveResponseWithWriter(
+        std::make_unique<URLFetcherPartialResponseWriter>(delegate_,
+                                                          url_fetcher_.get()));
+  }
+
+  if (!upload_content_type_.empty()) {
+    url_fetcher_->SetUploadData(upload_content_type_, upload_content_);
+  } else if (!chunked_upload_content_type_.empty() && method_ == Method::POST) {
+    url_fetcher_->SetChunkedUpload(chunked_upload_content_type_);
+  }
+
+  url_fetcher_->SetRequestContext(url_request_context_getter_.get());
+  url_fetcher_->Start();
+}
+
+void ChromiumHttpConnection::Pause() {
+  NOTIMPLEMENTED();
+}
+
+void ChromiumHttpConnection::Resume() {
+  NOTIMPLEMENTED();
+}
+
+void ChromiumHttpConnection::Close() {
+  VLOG(2) << "Requesting to close connection object";
+  network_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&ChromiumHttpConnection::CloseOnThread, this));
+}
+
+void ChromiumHttpConnection::CloseOnThread() {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  if (state_ == State::DESTROYED)
+    return;
+
+  VLOG(2) << "Closing connection object";
+  state_ = State::DESTROYED;
+  url_fetcher_ = nullptr;
+
+  delegate_->OnConnectionDestroyed();
+
+  Release();
+}
+
+void ChromiumHttpConnection::UploadData(const std::string& data,
+                                        bool is_last_chunk) {
+  network_task_runner_->PostTask(
+      FROM_HERE, base::Bind(&ChromiumHttpConnection::UploadDataOnThread, this,
+                            data, is_last_chunk));
+}
+
+void ChromiumHttpConnection::UploadDataOnThread(const std::string& data,
+                                                bool is_last_chunk) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  if (state_ != State::STARTED)
+    return;
+
+  // URLFetcher does not expose async IO to know when to add more data
+  // to the buffer. The write callback is received by
+  // HttpStreamParser::DoSendBody() and DoSendBodyComplete(), but there
+  // appears to be no way to know that it has happened.
+  if (url_fetcher_)
+    url_fetcher_->AppendChunkToUpload(data, is_last_chunk);
+}
+
+void ChromiumHttpConnection::OnURLFetchComplete(const URLFetcher* source) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  if (state_ != State::STARTED)
+    return;
+
+  state_ = State::COMPLETED;
+
+  DCHECK(url_fetcher_.get() == source);
+  int response_code = source->GetResponseCode();
+  if (response_code != URLFetcher::RESPONSE_CODE_INVALID) {
+    std::string response;
+    if (!source->GetResponseAsString(&response)) {
+      DCHECK(handle_partial_response_) << "Partial responses are disabled. "
+                                          "URLFetcher should be writing "
+                                          "response data to string";
+    }
+
+    VLOG(2) << "ChromiumHttpConnection completed with response_code="
+            << response_code;
+    std::string response_headers;
+    HttpResponseHeaders* headers = source->GetResponseHeaders();
+    if (headers)
+      response_headers = headers->raw_headers();
+
+    delegate_->OnCompleteResponse(response_code, response_headers, response);
+    return;
+  }
+
+  std::string message;
+  int error = source->GetStatus().error();
+  switch (source->GetStatus().status()) {
+    case URLRequestStatus::IO_PENDING:
+      message = "IO Pending";
+      break;
+    case URLRequestStatus::CANCELED:
+      message = "Canceled";
+      break;
+    case URLRequestStatus::FAILED:
+      message = "Failed";
+      break;
+    default:
+      message = "Unexpected URLFetcher status";
+      break;
+  }
+
+  VLOG(2) << "ChromiumHttpConnection completed with network error=" << error
+          << ": " << message;
+  delegate_->OnNetworkError(error, message);
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/chromium_http_connection.h b/chromeos/services/assistant/chromium_http_connection.h
new file mode 100644
index 0000000..b171581
--- /dev/null
+++ b/chromeos/services/assistant/chromium_http_connection.h
@@ -0,0 +1,122 @@
+// 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.
+
+// The file comes from Google Home(cast) implementation.
+
+#ifndef CHROMEOS_SERVICES_ASSISTANT_CHROMIUM_HTTP_CONNECTION_H_
+#define CHROMEOS_SERVICES_ASSISTANT_CHROMIUM_HTTP_CONNECTION_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "libassistant/shared/internal_api/http_connection.h"
+#include "net/http/http_request_headers.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+namespace chromeos {
+namespace assistant {
+
+// Implements libassistant's HttpConnection using Chromium //net library.
+class ChromiumHttpConnection
+    : public assistant_client::HttpConnection,
+      public ::net::URLFetcherDelegate,
+      public base::RefCountedThreadSafe<ChromiumHttpConnection> {
+ public:
+  ChromiumHttpConnection(
+      scoped_refptr<::net::URLRequestContextGetter> url_request_context_getter,
+      Delegate* delegate);
+
+  // assistant_client::HttpConnection implementation:
+  void SetRequest(const std::string& url, Method method) override;
+  void AddHeader(const std::string& name, const std::string& value) override;
+  void SetUploadContent(const std::string& content,
+                        const std::string& content_type) override;
+  void SetChunkedUploadContentType(const std::string& content_type) override;
+  void EnableHeaderResponse() override;
+  void EnablePartialResults() override;
+  void Start() override;
+  void Pause() override;
+  void Resume() override;
+  void Close() override;
+  void UploadData(const std::string& data, bool is_last_chunk) override;
+
+ protected:
+  ~ChromiumHttpConnection() override;
+
+ private:
+  friend class base::RefCountedThreadSafe<ChromiumHttpConnection>;
+
+  enum class State {
+    NEW,
+    STARTED,
+    COMPLETED,
+    DESTROYED,
+  };
+
+  // HttpConnection methods, re-scheduled on network thread:
+  void SetRequestOnThread(const std::string& url, Method method);
+  void AddHeaderOnThread(const std::string& name, const std::string& value);
+  void SetUploadContentOnThread(const std::string& content,
+                                const std::string& content_type);
+  void SetChunkedUploadContentTypeOnThread(const std::string& content_type);
+  void EnablePartialResultsOnThread();
+  void StartOnThread();
+  void CloseOnThread();
+  void UploadDataOnThread(const std::string& data, bool is_last_chunk);
+
+  // URLFetcherDelegate implementation:
+  void OnURLFetchComplete(const ::net::URLFetcher* source) override;
+
+  scoped_refptr<::net::URLRequestContextGetter> url_request_context_getter_;
+  Delegate* const delegate_;
+  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
+
+  State state_ = State::NEW;
+
+  // Parameters to be set before Start() call.
+  GURL url_;
+  Method method_ = Method::GET;
+  ::net::HttpRequestHeaders headers_;
+  std::string upload_content_;
+  std::string upload_content_type_;
+  std::string chunked_upload_content_type_;
+  bool handle_partial_response_ = false;
+
+  // |url_fetcher_| has to be accessed by network thread only. Some methods
+  // of URLFetcher (especially GetResponseAsString()) are not safe to access
+  // from other threads even under lock, since chromium accesses it as well.
+  std::unique_ptr<::net::URLFetcher> url_fetcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromiumHttpConnection);
+};
+
+class ChromiumHttpConnectionFactory
+    : public assistant_client::HttpConnectionFactory {
+ public:
+  ChromiumHttpConnectionFactory(
+      scoped_refptr<::net::URLRequestContextGetter> url_request_context_getter);
+  ~ChromiumHttpConnectionFactory() override;
+
+  // assistant_client::HttpConnectionFactory implementation:
+  assistant_client::HttpConnection* Create(
+      assistant_client::HttpConnection::Delegate* delegate) override;
+
+ private:
+  scoped_refptr<::net::URLRequestContextGetter> url_request_context_getter_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChromiumHttpConnectionFactory);
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_CHROMIUM_HTTP_CONNECTION_H_
diff --git a/chromeos/services/assistant/default_url_request_context_getter.cc b/chromeos/services/assistant/default_url_request_context_getter.cc
new file mode 100644
index 0000000..0575a476
--- /dev/null
+++ b/chromeos/services/assistant/default_url_request_context_getter.cc
@@ -0,0 +1,110 @@
+// 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.
+
+// The file comes from Google Home(cast) implementation.
+
+#include "chromeos/services/assistant/default_url_request_context_getter.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "net/proxy_resolution/proxy_config_service.h"
+#include "net/proxy_resolution/proxy_config_service_fixed.h"
+#include "net/proxy_resolution/proxy_resolution_service.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
+
+namespace {
+
+std::unique_ptr<::net::ProxyConfigServiceFixed> GetProxyConfigurationFromParams(
+    const std::string& proxy_server,
+    const std::string& bypass_list) {
+  ::net::ProxyConfig proxy_config;
+  proxy_config.proxy_rules().ParseFromString(proxy_server);
+  proxy_config.proxy_rules().bypass_rules.ParseFromString(bypass_list);
+  ::net::ProxyConfigWithAnnotation annotated_config(proxy_config,
+                                                    NO_TRAFFIC_ANNOTATION_YET);
+
+  return std::make_unique<::net::ProxyConfigServiceFixed>(
+      ::net::ProxyConfigServiceFixed(annotated_config));
+}
+}  // namespace
+
+namespace chromeos {
+namespace assistant {
+
+DefaultURLRequestContextGetter::DefaultURLRequestContextGetter(
+    const std::string& network_thread_name)
+    : thread_(new base::Thread(network_thread_name)) {
+  thread_->StartWithOptions(
+      base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
+  network_task_runner_ = thread_->task_runner();
+  DCHECK(network_task_runner_);
+}
+
+DefaultURLRequestContextGetter::DefaultURLRequestContextGetter(
+    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
+    : network_task_runner_(network_task_runner) {
+  DCHECK(network_task_runner_);
+}
+
+DefaultURLRequestContextGetter::~DefaultURLRequestContextGetter() {
+  if (request_context_) {
+    // The context should be destroyed on the network thread.
+    network_task_runner_->DeleteSoon(FROM_HERE, request_context_.release());
+  }
+  if (thread_)
+    thread_->Stop();
+}
+
+void DefaultURLRequestContextGetter::CreateContext() {
+  // Context must be created on network thread since its internal objects
+  // create and enforce thread checkers.
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+  ::net::URLRequestContextBuilder builder;
+  // Set direct proxy configuration.
+  builder.set_proxy_config_service(GetProxyConfigurationFromParams("", ""));
+  builder.DisableHttpCache();
+  request_context_ = builder.Build();
+  CHECK(request_context_);
+}
+
+::net::URLRequestContext*
+DefaultURLRequestContextGetter::GetURLRequestContext() {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+  if (!request_context_)
+    CreateContext();
+
+  return request_context_.get();
+}
+
+scoped_refptr<base::SingleThreadTaskRunner>
+DefaultURLRequestContextGetter::GetNetworkTaskRunner() const {
+  return network_task_runner_;
+}
+
+void DefaultURLRequestContextGetter::SetProxyConfiguration(
+    const std::string& proxy_server,
+    const std::string& bypass_list) {
+  network_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&DefaultURLRequestContextGetter::SetProxyConfigurationInternal,
+                 this, proxy_server, bypass_list));
+}
+
+void DefaultURLRequestContextGetter::SetProxyConfigurationInternal(
+    const std::string& proxy_server,
+    const std::string& bypass_list) {
+  DCHECK(network_task_runner_->BelongsToCurrentThread());
+
+  GetURLRequestContext()->proxy_resolution_service()->ResetConfigService(
+      GetProxyConfigurationFromParams(proxy_server, bypass_list));
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/default_url_request_context_getter.h b/chromeos/services/assistant/default_url_request_context_getter.h
new file mode 100644
index 0000000..c96d36b
--- /dev/null
+++ b/chromeos/services/assistant/default_url_request_context_getter.h
@@ -0,0 +1,68 @@
+// 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.
+
+// The file comes from Google Home(cast) implementation.
+
+#ifndef CHROMEOS_SERVICES_ASSISTANT_DEFAULT_URL_REQUEST_CONTEXT_GETTER_H_
+#define CHROMEOS_SERVICES_ASSISTANT_DEFAULT_URL_REQUEST_CONTEXT_GETTER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace base {
+class Thread;
+}  // namespace base
+
+namespace chromeos {
+namespace assistant {
+
+// A default URLRequestContextGetter implementation for creating a URL request
+// context for HTTP-related communications in the voice UI client.
+//
+// URL request context will have no HTTP caching.
+//
+// Instance of this class should be kept and reused for as long as possible.
+// Some internal objects, such as HostResolver are not always safe to destroy
+// and may cause random crashes (b/30282661).
+class DefaultURLRequestContextGetter : public ::net::URLRequestContextGetter {
+ public:
+  // Creates a new task runner thread with the given name.
+  explicit DefaultURLRequestContextGetter(
+      const std::string& network_thread_name);
+
+  // Uses the provided |network_task_runner| as the network task runner.
+  explicit DefaultURLRequestContextGetter(
+      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner);
+
+  // net::URLRequestContextGetter implementation:
+  ::net::URLRequestContext* GetURLRequestContext() override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
+      const override;
+
+  void SetProxyConfiguration(const std::string& proxy_server,
+                             const std::string& bypass_list);
+
+ private:
+  ~DefaultURLRequestContextGetter() override;
+
+  void CreateContext();
+
+  void SetProxyConfigurationInternal(const std::string& proxy_server,
+                                     const std::string& bypass_list);
+
+  // |thread_| is non-null if created by this class.
+  std::unique_ptr<base::Thread> thread_;
+  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
+  std::unique_ptr<::net::URLRequestContext> request_context_;
+
+  DISALLOW_COPY_AND_ASSIGN(DefaultURLRequestContextGetter);
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_DEFAULT_URL_REQUEST_CONTEXT_GETTER_H_
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 65b098ba8..c7338c6a 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -313,6 +313,8 @@
 void AutofillAgent::TextFieldDidEndEditing(const WebInputElement& element) {
   GetAutofillDriver()->DidEndTextFieldEditing();
   password_autofill_agent_->DidEndTextFieldEditing();
+  if (password_generation_agent_)
+    password_generation_agent_->DidEndTextFieldEditing(element);
 }
 
 void AutofillAgent::SetUserGestureRequired(bool required) {
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index ece9fc2..b0a5deaa 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -290,6 +290,7 @@
     password_generation::LogPasswordGenerationEvent(
         password_generation::PASSWORD_DELETED_BY_AUTOFILLING);
     PasswordNoLongerGenerated();
+    generation_element_.SetShouldRevealPassword(false);
   }
 }
 
@@ -558,11 +559,7 @@
 
 bool PasswordGenerationAgent::FocusedNodeHasChanged(
     const blink::WebNode& node) {
-  if (!generation_element_.IsNull())
-    generation_element_.SetShouldRevealPassword(false);
-
   if (node.IsNull() || !node.IsElementNode()) {
-    AutomaticGenerationStatusChanged(false);
     return false;
   }
 
@@ -576,7 +573,6 @@
   if (element && element->IsPasswordFieldForAutofill())
     last_focused_password_element_ = *element;
   if (!element || *element != generation_element_) {
-    AutomaticGenerationStatusChanged(false);
     return false;
   }
 
@@ -584,6 +580,8 @@
     if (generation_element_.Value().length() <
         kMinimumLengthForEditedPassword) {
       PasswordNoLongerGenerated();
+      if (generation_element_.Value().IsEmpty())
+        generation_element_.SetShouldRevealPassword(false);
     } else {
       generation_element_.SetShouldRevealPassword(true);
       ShowEditingPopup();
@@ -604,6 +602,14 @@
   return false;
 }
 
+void PasswordGenerationAgent::DidEndTextFieldEditing(
+    const blink::WebInputElement& element) {
+  if (!element.IsNull() && element == generation_element_) {
+    AutomaticGenerationStatusChanged(false);
+    generation_element_.SetShouldRevealPassword(false);
+  }
+}
+
 bool PasswordGenerationAgent::TextDidChangeInTextField(
     const WebInputElement& element) {
   if (element != generation_element_) {
@@ -623,6 +629,9 @@
     return false;
   }
 
+  if (element.Value().IsEmpty())
+    generation_element_.SetShouldRevealPassword(false);
+
   if (!password_is_generated_ &&
       element.Value().length() > kMaximumCharsForGenerationOffer) {
     // User has rejected the feature and has started typing a password.
@@ -705,7 +714,6 @@
   // Do not treat the password as generated, either here or in the browser.
   password_is_generated_ = false;
   password_edited_ = false;
-  generation_element_.SetShouldRevealPassword(false);
   for (WebInputElement& password : generation_form_data_->password_elements)
     password.SetAutofillState(WebAutofillState::kNotFilled);
   password_generation::LogPasswordGenerationEvent(
diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h
index 66ff3e4db..259aaf5 100644
--- a/components/autofill/content/renderer/password_generation_agent.h
+++ b/components/autofill/content/renderer/password_generation_agent.h
@@ -67,6 +67,13 @@
   // Returns true if the newly focused node caused the generation UI to show.
   bool FocusedNodeHasChanged(const blink::WebNode& node);
 
+  // Event forwarded by AutofillAgent from WebAutofillClient, informing that
+  // the text field editing has ended, which means that the field is not
+  // focused anymore. This is required for Android, where moving focus
+  // to a non-focusable element doesn't result in |FocusedNodeHasChanged|
+  // being called.
+  void DidEndTextFieldEditing(const blink::WebInputElement& element);
+
   // Called when new form controls are inserted.
   void OnDynamicFormsSeen();
 
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 611cd681..f6b65645 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -181,6 +181,8 @@
     "webdata/autofill_entry.h",
     "webdata/autofill_profile_data_type_controller.cc",
     "webdata/autofill_profile_data_type_controller.h",
+    "webdata/autofill_profile_model_type_controller.cc",
+    "webdata/autofill_profile_model_type_controller.h",
     "webdata/autofill_profile_sync_bridge.cc",
     "webdata/autofill_profile_sync_bridge.h",
     "webdata/autofill_profile_sync_difference_tracker.cc",
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index ef73d50d..ffe33f33 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -167,12 +167,12 @@
         kQueryId, suggestions, /*autoselect_first_suggestion=*/false);
   }
 
+  base::MessageLoop message_loop_;
+
   testing::NiceMock<MockAutofillClient> autofill_client_;
   std::unique_ptr<testing::NiceMock<MockAutofillDriver>> autofill_driver_;
   std::unique_ptr<MockAutofillManager> autofill_manager_;
   std::unique_ptr<AutofillExternalDelegate> external_delegate_;
-
-  base::MessageLoop message_loop_;
 };
 
 // Test that our external delegate called the virtual methods at the right time.
diff --git a/components/autofill/core/browser/autofill_merge_unittest.cc b/components/autofill/core/browser/autofill_merge_unittest.cc
index 6f83de7..caf3483 100644
--- a/components/autofill/core/browser/autofill_merge_unittest.cc
+++ b/components/autofill/core/browser/autofill_merge_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/country_names.h"
@@ -182,6 +183,7 @@
   // Deserializes |str| into a field type.
   ServerFieldType StringToFieldType(const std::string& str);
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   TestAutofillClient autofill_client_;
   PersonalDataManagerMock personal_data_;
   std::unique_ptr<FormDataImporter> form_data_importer_;
diff --git a/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc b/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc
new file mode 100644
index 0000000..db2726e
--- /dev/null
+++ b/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.cc
@@ -0,0 +1,59 @@
+// 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/autofill/core/browser/webdata/autofill_profile_model_type_controller.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "components/autofill/core/common/autofill_prefs.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/driver/sync_client.h"
+#include "components/sync/driver/sync_service.h"
+
+namespace browser_sync {
+
+AutofillProfileModelTypeController::AutofillProfileModelTypeController(
+    std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk,
+    syncer::SyncClient* sync_client)
+    : ModelTypeController(syncer::AUTOFILL_PROFILE,
+                          std::move(delegate_on_disk)),
+      sync_client_(sync_client),
+      currently_enabled_(IsEnabled()) {
+  pref_registrar_.Init(sync_client_->GetPrefService());
+  pref_registrar_.Add(
+      autofill::prefs::kAutofillProfileEnabled,
+      base::BindRepeating(
+          &AutofillProfileModelTypeController::OnUserPrefChanged,
+          base::Unretained(this)));
+}
+
+AutofillProfileModelTypeController::~AutofillProfileModelTypeController() =
+    default;
+
+bool AutofillProfileModelTypeController::ReadyForStart() const {
+  DCHECK(CalledOnValidThread());
+  return currently_enabled_;
+}
+
+void AutofillProfileModelTypeController::OnUserPrefChanged() {
+  DCHECK(CalledOnValidThread());
+
+  bool new_enabled = IsEnabled();
+  if (currently_enabled_ == new_enabled)
+    return;
+  currently_enabled_ = new_enabled;
+
+  sync_client_->GetSyncService()->ReadyForStartChanged(type());
+}
+
+bool AutofillProfileModelTypeController::IsEnabled() {
+  DCHECK(CalledOnValidThread());
+
+  // Require the user-visible pref to be enabled to sync Autofill Profile data.
+  return autofill::prefs::IsProfileAutofillEnabled(
+      sync_client_->GetPrefService());
+}
+
+}  // namespace browser_sync
diff --git a/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h b/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h
new file mode 100644
index 0000000..2a6a409
--- /dev/null
+++ b/components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h
@@ -0,0 +1,53 @@
+// 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_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_MODEL_TYPE_CONTROLLER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_MODEL_TYPE_CONTROLLER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/sync/driver/model_type_controller.h"
+
+namespace syncer {
+class ModelTypeControllerDelegate;
+class SyncClient;
+}  // namespace syncer
+
+namespace browser_sync {
+
+// Controls syncing of the AUTOFILL_PROFILE data type.
+class AutofillProfileModelTypeController : public syncer::ModelTypeController {
+ public:
+  AutofillProfileModelTypeController(
+      std::unique_ptr<syncer::ModelTypeControllerDelegate> delegate_on_disk,
+      syncer::SyncClient* sync_client);
+  ~AutofillProfileModelTypeController() override;
+
+  // DataTypeController overrides.
+  bool ReadyForStart() const override;
+
+ private:
+  // Callback for changes to the autofill pref.
+  void OnUserPrefChanged();
+
+  // Returns true if the pref is set such that autofill sync should be enabled.
+  bool IsEnabled();
+
+  syncer::SyncClient* const sync_client_;
+
+  // Registrar for listening to prefs::kAutofillProfileEnabled.
+  PrefChangeRegistrar pref_registrar_;
+
+  // Stores whether we're currently syncing autofill data. This is the last
+  // value computed by IsEnabled.
+  bool currently_enabled_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutofillProfileModelTypeController);
+};
+
+}  // namespace browser_sync
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_PROFILE_MODEL_TYPE_CONTROLLER_H_
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 480cae2f..01c34996 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -33,6 +33,8 @@
     "actions/reset_action.h",
     "actions/select_option_action.cc",
     "actions/select_option_action.h",
+    "actions/set_attribute_action.cc",
+    "actions/set_attribute_action.h",
     "actions/set_form_field_value_action.cc",
     "actions/set_form_field_value_action.h",
     "actions/show_details_action.cc",
diff --git a/components/autofill_assistant/browser/actions/action.cc b/components/autofill_assistant/browser/actions/action.cc
index 8b7d46be..f09c3a66 100644
--- a/components/autofill_assistant/browser/actions/action.cc
+++ b/components/autofill_assistant/browser/actions/action.cc
@@ -32,12 +32,12 @@
 }
 
 // static
-std::vector<std::string> Action::ExtractSelectors(
-    const google::protobuf::RepeatedPtrField<std::string>& selectors_proto) {
-  std::vector<std::string> selectors;
-  for (const auto& selector : selectors_proto) {
-    selectors.emplace_back(selector);
+std::vector<std::string> Action::ExtractVector(
+    const google::protobuf::RepeatedPtrField<std::string>& repeated_strings) {
+  std::vector<std::string> vector;
+  for (const auto& string : repeated_strings) {
+    vector.emplace_back(string);
   }
-  return selectors;
+  return vector;
 }
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/action.h b/components/autofill_assistant/browser/actions/action.h
index dab72979..651ac23 100644
--- a/components/autofill_assistant/browser/actions/action.h
+++ b/components/autofill_assistant/browser/actions/action.h
@@ -38,9 +38,9 @@
   virtual void InternalProcessAction(ActionDelegate* delegate,
                                      ProcessActionCallback callback) = 0;
 
-  // Returns selectors as a string from a repeated proto field.
-  static std::vector<std::string> ExtractSelectors(
-      const google::protobuf::RepeatedPtrField<std::string>& selectors_proto);
+  // Returns vector of string from a repeated proto field.
+  static std::vector<std::string> ExtractVector(
+      const google::protobuf::RepeatedPtrField<std::string>& repeated_strings);
 
   void UpdateProcessedAction(ProcessedActionStatusProto status);
 
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index e1da591..05cd8b7 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -115,6 +115,12 @@
                              bool simulate_key_presses,
                              base::OnceCallback<void(bool)> callback) = 0;
 
+  // Set the |value| of the |attribute| of the element given by |selectors|.
+  virtual void SetAttribute(const std::vector<std::string>& selectors,
+                            const std::vector<std::string>& attribute,
+                            const std::string& value,
+                            base::OnceCallback<void(bool)> callback) = 0;
+
   // Return the outerHTML of an element given by |selectors|.
   virtual void GetOuterHtml(
       const std::vector<std::string>& selectors,
diff --git a/components/autofill_assistant/browser/actions/autofill_action.cc b/components/autofill_assistant/browser/actions/autofill_action.cc
index 5b9ba74..0fb02765 100644
--- a/components/autofill_assistant/browser/actions/autofill_action.cc
+++ b/components/autofill_assistant/browser/actions/autofill_action.cc
@@ -96,7 +96,7 @@
     prompt_ = proto.use_address().prompt();
     name_ = proto.use_address().name();
     selectors_ =
-        ExtractSelectors(proto.use_address().form_field_element().selectors());
+        ExtractVector(proto.use_address().form_field_element().selectors());
     fill_form_message_ = proto.use_address().strings().fill_form();
     check_form_message_ = proto.use_address().strings().check_form();
     required_fields_value_status_.resize(
@@ -108,7 +108,7 @@
     prompt_ = proto.use_card().prompt();
     name_ = "";
     selectors_ =
-        ExtractSelectors(proto.use_card().form_field_element().selectors());
+        ExtractVector(proto.use_card().form_field_element().selectors());
     fill_form_message_ = proto.use_card().strings().fill_form();
     check_form_message_ = proto.use_card().strings().check_form();
     show_overlay_ = proto.use_card().show_overlay();
@@ -289,7 +289,7 @@
     auto& required_address_field = proto_.use_address().required_fields(i);
     DCHECK_GT(required_address_field.element().selectors_size(), 0);
     batch_element_checker_->AddFieldValueCheck(
-        ExtractSelectors(required_address_field.element().selectors()),
+        ExtractVector(required_address_field.element().selectors()),
         base::BindOnce(&AutofillAction::OnGetRequiredFieldValue,
                        // this instance owns batch_element_checker_
                        base::Unretained(this), i));
@@ -396,7 +396,7 @@
   DCHECK_GT(
       required_fields.Get(required_fields_index).element().selectors_size(), 0);
   delegate->SetFieldValue(
-      ExtractSelectors(
+      ExtractVector(
           required_fields.Get(required_fields_index).element().selectors()),
       fallback_value,
       required_fields.Get(required_fields_index).simulate_key_presses(),
diff --git a/components/autofill_assistant/browser/actions/click_action.cc b/components/autofill_assistant/browser/actions/click_action.cc
index 7362fbf..47fa1cc 100644
--- a/components/autofill_assistant/browser/actions/click_action.cc
+++ b/components/autofill_assistant/browser/actions/click_action.cc
@@ -23,7 +23,7 @@
                                         ProcessActionCallback callback) {
   DCHECK_GT(proto_.click().element_to_click().selectors_size(), 0);
   delegate->WaitForElement(
-      ExtractSelectors(proto_.click().element_to_click().selectors()),
+      ExtractVector(proto_.click().element_to_click().selectors()),
       base::BindOnce(&ClickAction::OnWaitForElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
                      std::move(callback)));
@@ -39,7 +39,7 @@
   }
 
   delegate->ClickElement(
-      ExtractSelectors(proto_.click().element_to_click().selectors()),
+      ExtractVector(proto_.click().element_to_click().selectors()),
       base::BindOnce(&::autofill_assistant::ClickAction::OnClick,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/components/autofill_assistant/browser/actions/focus_element_action.cc b/components/autofill_assistant/browser/actions/focus_element_action.cc
index 01b4994..42a734b 100644
--- a/components/autofill_assistant/browser/actions/focus_element_action.cc
+++ b/components/autofill_assistant/browser/actions/focus_element_action.cc
@@ -29,7 +29,7 @@
     delegate->ShowStatusMessage(focus_element.title());
   }
   delegate->WaitForElement(
-      ExtractSelectors(focus_element.element().selectors()),
+      ExtractVector(focus_element.element().selectors()),
       base::BindOnce(&FocusElementAction::OnWaitForElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
                      std::move(callback)));
@@ -45,7 +45,7 @@
   }
 
   delegate->FocusElement(
-      ExtractSelectors(proto_.focus_element().element().selectors()),
+      ExtractVector(proto_.focus_element().element().selectors()),
       base::BindOnce(&FocusElementAction::OnFocusElement,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/components/autofill_assistant/browser/actions/highlight_element_action.cc b/components/autofill_assistant/browser/actions/highlight_element_action.cc
index 1503c4c..d63a097 100644
--- a/components/autofill_assistant/browser/actions/highlight_element_action.cc
+++ b/components/autofill_assistant/browser/actions/highlight_element_action.cc
@@ -24,7 +24,7 @@
     ProcessActionCallback callback) {
   DCHECK_GT(proto_.highlight_element().element().selectors_size(), 0);
   delegate->WaitForElement(
-      ExtractSelectors(proto_.highlight_element().element().selectors()),
+      ExtractVector(proto_.highlight_element().element().selectors()),
       base::BindOnce(&HighlightElementAction::OnWaitForElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
                      std::move(callback)));
@@ -40,7 +40,7 @@
   }
 
   delegate->HighlightElement(
-      ExtractSelectors(proto_.highlight_element().element().selectors()),
+      ExtractVector(proto_.highlight_element().element().selectors()),
       base::BindOnce(&HighlightElementAction::OnHighlightElement,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 8a3715c..4d2acae 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -106,6 +106,12 @@
                void(const std::vector<std::string>& selectors,
                     const std::string& value,
                     base::OnceCallback<void(bool)>& callback));
+
+  MOCK_METHOD4(SetAttribute,
+               void(const std::vector<std::string>& selectors,
+                    const std::vector<std::string>& attribute,
+                    const std::string& value,
+                    base::OnceCallback<void(bool)> callback));
   MOCK_METHOD2(
       GetOuterHtml,
       void(const std::vector<std::string>& selectors,
diff --git a/components/autofill_assistant/browser/actions/select_option_action.cc b/components/autofill_assistant/browser/actions/select_option_action.cc
index 224f4211a..b59e8f7 100644
--- a/components/autofill_assistant/browser/actions/select_option_action.cc
+++ b/components/autofill_assistant/browser/actions/select_option_action.cc
@@ -28,7 +28,7 @@
   DCHECK_GT(select_option.element().selectors_size(), 0);
 
   delegate->WaitForElement(
-      ExtractSelectors(select_option.element().selectors()),
+      ExtractVector(select_option.element().selectors()),
       base::BindOnce(&SelectOptionAction::OnWaitForElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
                      std::move(callback)));
@@ -44,7 +44,7 @@
   }
 
   delegate->SelectOption(
-      ExtractSelectors(proto_.select_option().element().selectors()),
+      ExtractVector(proto_.select_option().element().selectors()),
       proto_.select_option().selected_option(),
       base::BindOnce(&::autofill_assistant::SelectOptionAction::OnSelectOption,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
diff --git a/components/autofill_assistant/browser/actions/set_attribute_action.cc b/components/autofill_assistant/browser/actions/set_attribute_action.cc
new file mode 100644
index 0000000..5806b5ce
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/set_attribute_action.cc
@@ -0,0 +1,55 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/actions/set_attribute_action.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+
+namespace autofill_assistant {
+
+SetAttributeAction::SetAttributeAction(const ActionProto& proto)
+    : Action(proto), weak_ptr_factory_(this) {
+  DCHECK_GT(proto_.set_attribute().element().selectors_size(), 0);
+  DCHECK_GT(proto_.set_attribute().attribute_size(), 0);
+}
+
+SetAttributeAction::~SetAttributeAction() {}
+
+void SetAttributeAction::InternalProcessAction(ActionDelegate* delegate,
+                                               ProcessActionCallback callback) {
+  delegate->WaitForElement(
+      ExtractVector(proto_.set_attribute().element().selectors()),
+      base::BindOnce(&SetAttributeAction::OnWaitForElement,
+                     weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
+                     std::move(callback)));
+}
+
+void SetAttributeAction::OnWaitForElement(ActionDelegate* delegate,
+                                          ProcessActionCallback callback,
+                                          bool element_found) {
+  if (!element_found) {
+    UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
+    std::move(callback).Run(std::move(processed_action_proto_));
+    return;
+  }
+
+  delegate->SetAttribute(
+      ExtractVector(proto_.set_attribute().element().selectors()),
+      ExtractVector(proto_.set_attribute().attribute()),
+      proto_.set_attribute().value(),
+      base::BindOnce(&SetAttributeAction::OnSetAttribute,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void SetAttributeAction::OnSetAttribute(ProcessActionCallback callback,
+                                        bool status) {
+  UpdateProcessedAction(status ? ACTION_APPLIED : OTHER_ACTION_STATUS);
+  std::move(callback).Run(std::move(processed_action_proto_));
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/set_attribute_action.h b/components/autofill_assistant/browser/actions/set_attribute_action.h
new file mode 100644
index 0000000..8bcb123
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/set_attribute_action.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 COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_SET_ATTRIBUTE_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_SET_ATTRIBUTE_ACTION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+
+namespace autofill_assistant {
+// An action to set the attribute of an element.
+class SetAttributeAction : public Action {
+ public:
+  explicit SetAttributeAction(const ActionProto& proto);
+  ~SetAttributeAction() override;
+
+ private:
+  // Overrides Action:
+  void InternalProcessAction(ActionDelegate* delegate,
+                             ProcessActionCallback callback) override;
+
+  void OnWaitForElement(ActionDelegate* delegate,
+                        ProcessActionCallback callback,
+                        bool element_found);
+  void OnSetAttribute(ProcessActionCallback callback, bool status);
+
+  base::WeakPtrFactory<SetAttributeAction> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SetAttributeAction);
+};
+
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_SET_ATTRIBUTE_ACTION_H_
diff --git a/components/autofill_assistant/browser/actions/set_form_field_value_action.cc b/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
index 2baf391..d52b2ca 100644
--- a/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
+++ b/components/autofill_assistant/browser/actions/set_form_field_value_action.cc
@@ -26,7 +26,7 @@
     ActionDelegate* delegate,
     ProcessActionCallback callback) {
   delegate->WaitForElement(
-      ExtractSelectors(proto_.set_form_value().element().selectors()),
+      ExtractVector(proto_.set_form_value().element().selectors()),
       base::BindOnce(&SetFormFieldValueAction::OnWaitForElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
                      std::move(callback)));
@@ -44,7 +44,7 @@
   // TODO(crbug.com/806868): Add flag to allow simulating key presses to set
   // field value.
   delegate->SetFieldValue(
-      ExtractSelectors(proto_.set_form_value().element().selectors()),
+      ExtractVector(proto_.set_form_value().element().selectors()),
       proto_.set_form_value().value(0).text(),
       /* simulate_key_presses= */ false,
       base::BindOnce(&SetFormFieldValueAction::OnSetFieldValue,
diff --git a/components/autofill_assistant/browser/actions/upload_dom_action.cc b/components/autofill_assistant/browser/actions/upload_dom_action.cc
index a949380..c893c37 100644
--- a/components/autofill_assistant/browser/actions/upload_dom_action.cc
+++ b/components/autofill_assistant/browser/actions/upload_dom_action.cc
@@ -23,7 +23,7 @@
                                             ProcessActionCallback callback) {
   DCHECK_GT(proto_.upload_dom().tree_root().selectors_size(), 0);
   delegate->WaitForElement(
-      ExtractSelectors(proto_.upload_dom().tree_root().selectors()),
+      ExtractVector(proto_.upload_dom().tree_root().selectors()),
       base::BindOnce(&UploadDomAction::OnWaitForElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
                      std::move(callback)));
@@ -39,7 +39,7 @@
   }
 
   delegate->GetOuterHtml(
-      ExtractSelectors(proto_.upload_dom().tree_root().selectors()),
+      ExtractVector(proto_.upload_dom().tree_root().selectors()),
       base::BindOnce(&UploadDomAction::OnGetOuterHtml,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/components/autofill_assistant/browser/actions/wait_for_dom_action.cc b/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
index 92a877ad..3e36cf3 100644
--- a/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
+++ b/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
@@ -30,7 +30,7 @@
   DCHECK_GT(proto_.wait_for_dom().selectors_size(), 0);
   batch_element_checker_ = delegate->CreateBatchElementChecker();
   batch_element_checker_->AddElementCheck(
-      kVisibilityCheck, ExtractSelectors(proto_.wait_for_dom().selectors()),
+      kVisibilityCheck, ExtractVector(proto_.wait_for_dom().selectors()),
       base::DoNothing());
 
   base::TimeDelta duration = kDefaultCheckDuration;
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index ac9fd81..a0a815a 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -15,6 +15,7 @@
 #include "components/autofill_assistant/browser/actions/navigate_action.h"
 #include "components/autofill_assistant/browser/actions/reset_action.h"
 #include "components/autofill_assistant/browser/actions/select_option_action.h"
+#include "components/autofill_assistant/browser/actions/set_attribute_action.h"
 #include "components/autofill_assistant/browser/actions/set_form_field_value_action.h"
 #include "components/autofill_assistant/browser/actions/show_details_action.h"
 #include "components/autofill_assistant/browser/actions/show_progress_bar_action.h"
@@ -230,6 +231,10 @@
         actions->emplace_back(std::make_unique<ShowProgressBarAction>(action));
         break;
       }
+      case ActionProto::ActionInfoCase::kSetAttribute: {
+        actions->emplace_back(std::make_unique<SetAttributeAction>(action));
+        break;
+      }
       default:
       case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
         DLOG(ERROR) << "Unknown or unsupported action with action_case="
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index f0bbcf53..e557658 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -162,6 +162,14 @@
       selectors, value, simulate_key_presses, std::move(callback));
 }
 
+void ScriptExecutor::SetAttribute(const std::vector<std::string>& selectors,
+                                  const std::vector<std::string>& attribute,
+                                  const std::string& value,
+                                  base::OnceCallback<void(bool)> callback) {
+  delegate_->GetWebController()->SetAttribute(selectors, attribute, value,
+                                              std::move(callback));
+}
+
 void ScriptExecutor::GetOuterHtml(
     const std::vector<std::string>& selectors,
     base::OnceCallback<void(bool, const std::string&)> callback) {
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index f7d6224..04b79bf 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -96,6 +96,10 @@
                      const std::string& value,
                      bool simulate_key_presses,
                      base::OnceCallback<void(bool)> callback) override;
+  void SetAttribute(const std::vector<std::string>& selectors,
+                    const std::vector<std::string>& attribute,
+                    const std::string& value,
+                    base::OnceCallback<void(bool)> callback) override;
   void GetOuterHtml(
       const std::vector<std::string>& selectors,
       base::OnceCallback<void(bool, const std::string&)> callback) override;
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index f959cc0..7c52ce05 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -211,6 +211,7 @@
     ResetProto reset = 34;
     StopProto stop = 35;
     GetPaymentInformationProto get_payment_information = 36;
+    SetAttributeProto set_attribute = 37;
   }
 
   // Set to true to make the client remove any contextual information if the
@@ -483,3 +484,16 @@
   // The value to set.
   repeated KeyPress value = 2;
 }
+
+// Set an element attribute to a specific value.
+message SetAttributeProto {
+  // A reference to the form element whose attribute should be set.
+  optional ElementReferenceProto element = 1;
+
+  // The attribute to set, e.g. ["style", "position"] to change
+  // element.style.position.
+  repeated string attribute = 2;
+
+  // The value to set.
+  optional string value = 3;
+}
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index 21a84552..122603ff 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -78,6 +78,16 @@
          this.dispatchEvent(e);
        })";
 
+// Javascript code to set an attribute of a node to a given value.
+const char* const kSetAttributeScript =
+    R"(function (attribute, value) {
+         let receiver = this;
+         for (let i = 0; i < attribute.length - 1; i++) {
+           receiver = receiver[attribute[i]];
+         }
+         receiver[attribute[attribute.length - 1]] = value;
+       })";
+
 // Javascript code to get the outerHTML of a node.
 // TODO(crbug.com/806868): Investigate if using DOM.GetOuterHtml would be a
 // better solution than injecting Javascript code.
@@ -884,6 +894,60 @@
   OnResult(result && !result->HasExceptionDetails(), std::move(callback));
 }
 
+void WebController::SetAttribute(const std::vector<std::string>& selectors,
+                                 const std::vector<std::string>& attribute,
+                                 const std::string& value,
+                                 base::OnceCallback<void(bool)> callback) {
+  DCHECK_GT(selectors.size(), 0u);
+  DCHECK_GT(attribute.size(), 0u);
+  FindElement(selectors,
+              /* strict_mode= */ true,
+              base::BindOnce(&WebController::OnFindElementForSetAttribute,
+                             weak_ptr_factory_.GetWeakPtr(), attribute, value,
+                             std::move(callback)));
+}
+
+void WebController::OnFindElementForSetAttribute(
+    const std::vector<std::string>& attribute,
+    const std::string& value,
+    base::OnceCallback<void(bool)> callback,
+    std::unique_ptr<FindElementResult> element_result) {
+  const std::string object_id = element_result->object_id;
+  if (object_id.empty()) {
+    OnResult(false, std::move(callback));
+    return;
+  }
+
+  base::Value::ListStorage attribute_values;
+  for (const std::string& string : attribute) {
+    attribute_values.emplace_back(base::Value(string));
+  }
+
+  std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
+  arguments.emplace_back(runtime::CallArgument::Builder()
+                             .SetValue(base::Value::ToUniquePtrValue(
+                                 base::Value(attribute_values)))
+                             .Build());
+  arguments.emplace_back(
+      runtime::CallArgument::Builder()
+          .SetValue(base::Value::ToUniquePtrValue(base::Value(value)))
+          .Build());
+  devtools_client_->GetRuntime()->CallFunctionOn(
+      runtime::CallFunctionOnParams::Builder()
+          .SetObjectId(object_id)
+          .SetArguments(std::move(arguments))
+          .SetFunctionDeclaration(std::string(kSetAttributeScript))
+          .Build(),
+      base::BindOnce(&WebController::OnSetAttribute,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void WebController::OnSetAttribute(
+    base::OnceCallback<void(bool)> callback,
+    std::unique_ptr<runtime::CallFunctionOnResult> result) {
+  OnResult(result && !result->HasExceptionDetails(), std::move(callback));
+}
+
 void WebController::GetOuterHtml(
     const std::vector<std::string>& selectors,
     base::OnceCallback<void(bool, const std::string&)> callback) {
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index 1ea2e626..d3b8cd3 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -105,6 +105,12 @@
                              bool simulate_key_presses,
                              base::OnceCallback<void(bool)> callback);
 
+  // Set the |value| of the |attribute| of the element given by |selectors|.
+  virtual void SetAttribute(const std::vector<std::string>& selectors,
+                            const std::vector<std::string>& attribute,
+                            const std::string& value,
+                            base::OnceCallback<void(bool)> callback);
+
   // Return the outerHTML of |selectors|.
   virtual void GetOuterHtml(
       const std::vector<std::string>& selectors,
@@ -280,13 +286,6 @@
       const std::string& value,
       base::OnceCallback<void(bool)> callback,
       bool clear_status);
-  void OnFindElementForSetFieldValue(
-      const std::string& value,
-      base::OnceCallback<void(bool)> callback,
-      std::unique_ptr<FindElementResult> element_result);
-  void OnSetValueAttribute(
-      base::OnceCallback<void(bool)> callback,
-      std::unique_ptr<runtime::CallFunctionOnResult> result);
   void OnClickElementForDispatchKeyEvent(
       const std::string& value,
       base::OnceCallback<void(bool)> callback,
@@ -300,6 +299,20 @@
   void OnDispatchKeyUpEvent(const std::string& value,
                             size_t index,
                             base::OnceCallback<void(bool)> callback);
+  void OnFindElementForSetAttribute(
+      const std::vector<std::string>& attribute,
+      const std::string& value,
+      base::OnceCallback<void(bool)> callback,
+      std::unique_ptr<FindElementResult> element_result);
+  void OnSetAttribute(base::OnceCallback<void(bool)> callback,
+                      std::unique_ptr<runtime::CallFunctionOnResult> result);
+  void OnFindElementForSetFieldValue(
+      const std::string& value,
+      base::OnceCallback<void(bool)> callback,
+      std::unique_ptr<FindElementResult> element_result);
+  void OnSetValueAttribute(
+      base::OnceCallback<void(bool)> callback,
+      std::unique_ptr<runtime::CallFunctionOnResult> result);
   void OnFindElementForGetOuterHtml(
       base::OnceCallback<void(bool, const std::string&)> callback,
       std::unique_ptr<FindElementResult> element_result);
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index 07703e4..8d244687 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -247,6 +247,27 @@
     std::move(done_callback).Run();
   }
 
+  bool SetAttribute(const std::vector<std::string>& selectors,
+                    const std::vector<std::string>& attribute,
+                    const std::string& value) {
+    base::RunLoop run_loop;
+    bool result;
+    web_controller_->SetAttribute(
+        selectors, attribute, value,
+        base::BindOnce(&WebControllerBrowserTest::OnSetAttribute,
+                       base::Unretained(this), run_loop.QuitClosure(),
+                       &result));
+    run_loop.Run();
+    return result;
+  }
+
+  void OnSetAttribute(const base::Closure& done_callback,
+                      bool* result_output,
+                      bool result) {
+    *result_output = result;
+    std::move(done_callback).Run();
+  }
+
  protected:
   std::unique_ptr<WebController> web_controller_;
 
@@ -521,6 +542,22 @@
       SetFieldValue(a_selector, "foobar", /* simulate_key_presses= */ false));
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SetAttribute) {
+  std::vector<std::string> selectors;
+  std::vector<std::string> attribute;
+
+  selectors.emplace_back("#full_height_section");
+  attribute.emplace_back("style");
+  attribute.emplace_back("backgroundColor");
+  std::string value = "red";
+
+  EXPECT_TRUE(SetAttribute(selectors, attribute, value));
+  const std::string javascript = R"(
+    document.querySelector("#full_height_section").style.backgroundColor;
+  )";
+  EXPECT_EQ(value, content::EvalJs(shell(), javascript));
+}
+
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ConcurrentGetFieldsValue) {
   std::vector<std::vector<std::string>> selectors;
   std::vector<std::string> expected_values;
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 9e001c8..361d04e 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/autofill_wallet_model_type_controller.h"
 #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_data_type_controller.h"
+#include "components/autofill/core/browser/webdata/autofill_profile_model_type_controller.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h"
@@ -152,18 +153,26 @@
     // Autocomplete sync is enabled by default.  Register unless explicitly
     // disabled.
     if (!disabled_types.Has(syncer::AUTOFILL)) {
-      controllers.push_back(CreateWebDataModelTypeController(
+      controllers.push_back(std::make_unique<ModelTypeController>(
           syncer::AUTOFILL,
-          base::BindRepeating(&AutocompleteDelegateFromDataService)));
+          std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
+              db_thread_, base::BindRepeating(
+                              &AutocompleteDelegateFromDataService,
+                              base::RetainedRef(web_data_service_on_disk_)))));
     }
 
     // Autofill sync is enabled by default.  Register unless explicitly
     // disabled.
     if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) {
       if (FeatureList::IsEnabled(switches::kSyncUSSAutofillProfile)) {
-        controllers.push_back(CreateWebDataModelTypeController(
-            syncer::AUTOFILL_PROFILE,
-            base::BindRepeating(&AutofillProfileDelegateFromDataService)));
+        controllers.push_back(
+            std::make_unique<AutofillProfileModelTypeController>(
+                std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
+                    db_thread_,
+                    base::BindRepeating(
+                        &AutofillProfileDelegateFromDataService,
+                        base::RetainedRef(web_data_service_on_disk_))),
+                sync_client_));
       } else {
         controllers.push_back(
             std::make_unique<AutofillProfileDataTypeController>(
@@ -482,19 +491,6 @@
 }
 
 std::unique_ptr<ModelTypeController>
-ProfileSyncComponentsFactoryImpl::CreateWebDataModelTypeController(
-    syncer::ModelType type,
-    const base::RepeatingCallback<
-        base::WeakPtr<syncer::ModelTypeControllerDelegate>(
-            autofill::AutofillWebDataService*)>& delegate_from_web_data) {
-  return std::make_unique<ModelTypeController>(
-      type, std::make_unique<syncer::ProxyModelTypeControllerDelegate>(
-                db_thread_, base::BindRepeating(
-                                delegate_from_web_data,
-                                base::RetainedRef(web_data_service_on_disk_))));
-}
-
-std::unique_ptr<ModelTypeController>
 ProfileSyncComponentsFactoryImpl::CreateWalletModelTypeController(
     syncer::ModelType type,
     const base::RepeatingCallback<
diff --git a/components/browser_sync/profile_sync_components_factory_impl.h b/components/browser_sync/profile_sync_components_factory_impl.h
index c81266ba..249395f 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.h
+++ b/components/browser_sync/profile_sync_components_factory_impl.h
@@ -87,23 +87,16 @@
   std::unique_ptr<syncer::ModelTypeController>
   CreateModelTypeControllerForModelRunningOnUIThread(syncer::ModelType type);
 
-  // Factory function for ModelTypeController instances for autofill-related
-  // datatypes, which live in |db_thread_| and have a delegate accesible via
+  // Factory function for ModelTypeController instances for wallet-related
+  // datatypes, which live in |db_thread_| and have a delegate accessible via
   // AutofillWebDataService.
-  std::unique_ptr<syncer::ModelTypeController> CreateWebDataModelTypeController(
-      syncer::ModelType type,
-      const base::RepeatingCallback<
-          base::WeakPtr<syncer::ModelTypeControllerDelegate>(
-              autofill::AutofillWebDataService*)>& delegate_from_web_data);
-  // Same as above, but for AUTOFILL_WALLET_* datatypes.
   std::unique_ptr<syncer::ModelTypeController> CreateWalletModelTypeController(
       syncer::ModelType type,
       const base::RepeatingCallback<
           base::WeakPtr<syncer::ModelTypeControllerDelegate>(
               autofill::AutofillWebDataService*)>& delegate_from_web_data);
-  // Same as above, but datatypes supporting STORAGE_IN_MEMORY implemented
-  // as an independent AutofillWebDataService, namely
-  // |web_data_service_in_memory_|.
+  // Same as above, but supporting STORAGE_IN_MEMORY implemented as an
+  // independent AutofillWebDataService, namely |web_data_service_in_memory_|.
   std::unique_ptr<syncer::ModelTypeController>
   CreateWalletModelTypeControllerWithInMemorySupport(
       syncer::ModelType type,
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 38033e0..e918b3b9 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -200,7 +200,7 @@
     ~InitParams();
 
     std::unique_ptr<syncer::SyncClient> sync_client;
-    identity::IdentityManager* identity_manager;
+    identity::IdentityManager* identity_manager = nullptr;
     SigninScopedDeviceIdCallback signin_scoped_device_id_callback;
     GaiaCookieManagerService* gaia_cookie_manager_service = nullptr;
     std::vector<invalidation::IdentityProvider*>
@@ -208,7 +208,7 @@
     StartBehavior start_behavior = MANUAL_START;
     syncer::NetworkTimeUpdateCallback network_time_update_callback;
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
-    network::NetworkConnectionTracker* network_connection_tracker;
+    network::NetworkConnectionTracker* network_connection_tracker = nullptr;
     std::string debug_identifier;
     version_info::Channel channel = version_info::Channel::UNKNOWN;
     bool user_events_separate_pref_group = false;
diff --git a/components/data_reduction_proxy/core/common/BUILD.gn b/components/data_reduction_proxy/core/common/BUILD.gn
index bd23bdd..6d9547fd 100644
--- a/components/data_reduction_proxy/core/common/BUILD.gn
+++ b/components/data_reduction_proxy/core/common/BUILD.gn
@@ -127,6 +127,7 @@
   ]
 
   deps = [
+    "//mojo/public/mojom/base",
     "//services/network/public/mojom",
   ]
 }
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 0f5f7ad0..2b2661573 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -107,6 +107,8 @@
       "shell_surface_base.h",
       "text_input.cc",
       "text_input.h",
+      "wm_helper_chromeos.cc",
+      "wm_helper_chromeos.h",
       "xdg_shell_surface.cc",
       "xdg_shell_surface.h",
     ]
diff --git a/components/exo/pointer.cc b/components/exo/pointer.cc
index 9f7aa40..621899ba 100644
--- a/components/exo/pointer.cc
+++ b/components/exo/pointer.cc
@@ -12,6 +12,7 @@
 #include "components/exo/shell_surface_base.h"
 #include "components/exo/surface.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_result.h"
 #include "ui/aura/client/cursor_client.h"
@@ -81,7 +82,7 @@
       capture_ratio_(GetCaptureDisplayInfo().GetDensityRatio()),
       cursor_capture_source_id_(base::UnguessableToken::Create()),
       cursor_capture_weak_ptr_factory_(this) {
-  auto* helper = WMHelper::GetInstance();
+  WMHelperChromeOS* helper = WMHelperChromeOS::GetInstance();
   helper->AddPreTargetHandler(this);
   helper->AddDisplayConfigurationObserver(this);
   // TODO(sky): CursorClient does not exist in mash
@@ -98,7 +99,7 @@
   }
   if (pinch_delegate_)
     pinch_delegate_->OnPointerDestroying(this);
-  auto* helper = WMHelper::GetInstance();
+  WMHelperChromeOS* helper = WMHelperChromeOS::GetInstance();
   helper->RemoveDisplayConfigurationObserver(this);
   helper->RemovePreTargetHandler(this);
   // TODO(sky): CursorClient does not exist in mash
@@ -471,7 +472,7 @@
 }
 
 void Pointer::UpdateCursor() {
-  auto* helper = WMHelper::GetInstance();
+  WMHelper* helper = WMHelper::GetInstance();
   aura::client::CursorClient* cursor_client = helper->GetCursorClient();
   // TODO(crbug.com/631103): CursorClient does not exist in mash yet.
   if (!cursor_client)
diff --git a/components/exo/pointer.h b/components/exo/pointer.h
index ee8dfc1..1d66994f 100644
--- a/components/exo/pointer.h
+++ b/components/exo/pointer.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "ash/display/window_tree_host_manager.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
diff --git a/components/exo/test/exo_test_base.cc b/components/exo/test/exo_test_base.cc
index 9a193515..352df0a03 100644
--- a/components/exo/test/exo_test_base.cc
+++ b/components/exo/test/exo_test_base.cc
@@ -9,6 +9,7 @@
 #include "components/exo/test/exo_test_helper.h"
 #include "components/exo/test/test_client_controlled_state_delegate.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "ui/aura/env.h"
 #include "ui/base/ime/input_method_factory.h"
 #include "ui/wm/core/wm_core_switches.h"
@@ -29,7 +30,8 @@
   command_line->AppendSwitch(wm::switches::kWindowAnimationsDisabled);
   ui::SetUpInputMethodFactoryForTesting();
   AshTestBase::SetUp();
-  wm_helper_ = std::make_unique<WMHelper>(ash::Shell::Get()->aura_env());
+  wm_helper_ =
+      std::make_unique<WMHelperChromeOS>(ash::Shell::Get()->aura_env());
   WMHelper::SetInstance(wm_helper_.get());
   test::TestClientControlledStateDelegate::InstallFactory();
 }
diff --git a/components/exo/wayland/clients/test/wayland_client_test.cc b/components/exo/wayland/clients/test/wayland_client_test.cc
index c69fdfb..fe44c7d 100644
--- a/components/exo/wayland/clients/test/wayland_client_test.cc
+++ b/components/exo/wayland/clients/test/wayland_client_test.cc
@@ -22,6 +22,7 @@
 #include "components/exo/file_helper.h"
 #include "components/exo/wayland/server.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/events/gesture_detection/gesture_configuration.h"
@@ -143,7 +144,8 @@
   gesture_config->set_long_press_time_in_ms(1000);
   gesture_config->set_max_touch_move_in_pixels_for_click(5);
 
-  wm_helper_ = std::make_unique<WMHelper>(ash::Shell::Get()->aura_env());
+  wm_helper_ =
+      std::make_unique<WMHelperChromeOS>(ash::Shell::Get()->aura_env());
   WMHelper::SetInstance(wm_helper_.get());
   display_ = std::make_unique<Display>(nullptr, nullptr, nullptr);
   wayland_server_ = exo::wayland::Server::Create(display_.get());
diff --git a/components/exo/wayland/server.cc b/components/exo/wayland/server.cc
index 0eb2b25..9a850da 100644
--- a/components/exo/wayland/server.cc
+++ b/components/exo/wayland/server.cc
@@ -95,6 +95,7 @@
 #include "components/exo/touch_delegate.h"
 #include "components/exo/touch_stylus_delegate.h"
 #include "components/exo/wm_helper.h"
+#include "components/exo/wm_helper_chromeos.h"
 #include "components/exo/xdg_shell_surface.h"
 #include "services/viz/public/interfaces/compositing/compositor_frame_sink.mojom.h"
 #include "third_party/skia/include/core/SkRegion.h"
@@ -2558,7 +2559,7 @@
       : display_(display),
         remote_shell_resource_(remote_shell_resource),
         weak_ptr_factory_(this) {
-    auto* helper = WMHelper::GetInstance();
+    WMHelperChromeOS* helper = WMHelperChromeOS::GetInstance();
     helper->AddTabletModeObserver(this);
     helper->AddActivationObserver(this);
     display::Screen::GetScreen()->AddObserver(this);
@@ -2581,7 +2582,7 @@
     SendActivated(helper->GetActiveWindow(), nullptr);
   }
   ~WaylandRemoteShell() override {
-    auto* helper = WMHelper::GetInstance();
+    WMHelperChromeOS* helper = WMHelperChromeOS::GetInstance();
     helper->RemoveTabletModeObserver(this);
     helper->RemoveActivationObserver(this);
     display::Screen::GetScreen()->RemoveObserver(this);
diff --git a/components/exo/wm_helper.cc b/components/exo/wm_helper.cc
index 1fbe2e4..e5c62e8 100644
--- a/components/exo/wm_helper.cc
+++ b/components/exo/wm_helper.cc
@@ -3,35 +3,17 @@
 // found in the LICENSE file.
 
 #include "components/exo/wm_helper.h"
-
-#include "ash/shell.h"
-#include "ash/wm/tablet_mode/tablet_mode_controller.h"
-#include "base/memory/singleton.h"
-#include "ui/aura/client/drag_drop_delegate.h"
-#include "ui/aura/client/focus_client.h"
-#include "ui/base/dragdrop/drag_drop_types.h"
-#include "ui/display/manager/display_configurator.h"
-#include "ui/display/manager/display_manager.h"
-#include "ui/display/types/display_snapshot.h"
-#include "ui/wm/public/activation_client.h"
+#include "ui/compositor/compositor_vsync_manager.h"
 
 namespace exo {
-namespace {
-WMHelper* g_instance = nullptr;
 
-aura::Window* GetPrimaryRoot() {
-  return ash::Shell::Get()->GetPrimaryRootWindow();
-}
+namespace {
+
+WMHelper* g_instance = nullptr;
 
 }  // namespace
 
-////////////////////////////////////////////////////////////////////////////////
-// WMHelper, public:
-
-WMHelper::WMHelper(aura::Env* env)
-    : vsync_manager_(
-          GetPrimaryRoot()->layer()->GetCompositor()->vsync_manager()),
-      env_(env) {}
+WMHelper::WMHelper() {}
 
 WMHelper::~WMHelper() {}
 
@@ -52,172 +34,4 @@
   return !!g_instance;
 }
 
-void WMHelper::AddActivationObserver(wm::ActivationChangeObserver* observer) {
-  ash::Shell::Get()->activation_client()->AddObserver(observer);
-}
-
-void WMHelper::RemoveActivationObserver(
-    wm::ActivationChangeObserver* observer) {
-  ash::Shell::Get()->activation_client()->RemoveObserver(observer);
-}
-
-void WMHelper::AddFocusObserver(aura::client::FocusChangeObserver* observer) {
-  aura::client::GetFocusClient(GetPrimaryRoot())->AddObserver(observer);
-}
-
-void WMHelper::RemoveFocusObserver(
-    aura::client::FocusChangeObserver* observer) {
-  aura::client::GetFocusClient(GetPrimaryRoot())->RemoveObserver(observer);
-}
-
-void WMHelper::AddTabletModeObserver(ash::TabletModeObserver* observer) {
-  ash::Shell::Get()->tablet_mode_controller()->AddObserver(observer);
-}
-
-void WMHelper::RemoveTabletModeObserver(ash::TabletModeObserver* observer) {
-  ash::Shell::Get()->tablet_mode_controller()->RemoveObserver(observer);
-}
-
-void WMHelper::AddDisplayConfigurationObserver(
-    ash::WindowTreeHostManager::Observer* observer) {
-  ash::Shell::Get()->window_tree_host_manager()->AddObserver(observer);
-}
-
-void WMHelper::RemoveDisplayConfigurationObserver(
-    ash::WindowTreeHostManager::Observer* observer) {
-  ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(observer);
-}
-
-void WMHelper::AddDragDropObserver(DragDropObserver* observer) {
-  drag_drop_observers_.AddObserver(observer);
-}
-
-void WMHelper::RemoveDragDropObserver(DragDropObserver* observer) {
-  drag_drop_observers_.RemoveObserver(observer);
-}
-
-void WMHelper::SetDragDropDelegate(aura::Window* window) {
-  aura::client::SetDragDropDelegate(window, this);
-}
-
-void WMHelper::ResetDragDropDelegate(aura::Window* window) {
-  aura::client::SetDragDropDelegate(window, nullptr);
-}
-
-void WMHelper::AddVSyncObserver(
-    ui::CompositorVSyncManager::Observer* observer) {
-  vsync_manager_->AddObserver(observer);
-}
-
-void WMHelper::RemoveVSyncObserver(
-    ui::CompositorVSyncManager::Observer* observer) {
-  vsync_manager_->RemoveObserver(observer);
-}
-
-void WMHelper::OnDragEntered(const ui::DropTargetEvent& event) {
-  for (DragDropObserver& observer : drag_drop_observers_)
-    observer.OnDragEntered(event);
-}
-
-int WMHelper::OnDragUpdated(const ui::DropTargetEvent& event) {
-  int valid_operation = ui::DragDropTypes::DRAG_NONE;
-  for (DragDropObserver& observer : drag_drop_observers_)
-    valid_operation = valid_operation | observer.OnDragUpdated(event);
-  return valid_operation;
-}
-
-void WMHelper::OnDragExited() {
-  for (DragDropObserver& observer : drag_drop_observers_)
-    observer.OnDragExited();
-}
-
-int WMHelper::OnPerformDrop(const ui::DropTargetEvent& event) {
-  for (DragDropObserver& observer : drag_drop_observers_)
-    observer.OnPerformDrop(event);
-  // TODO(hirono): Return the correct result instead of always returning
-  // DRAG_MOVE.
-  return ui::DragDropTypes::DRAG_MOVE;
-}
-
-const display::ManagedDisplayInfo& WMHelper::GetDisplayInfo(
-    int64_t display_id) const {
-  return ash::Shell::Get()->display_manager()->GetDisplayInfo(display_id);
-}
-
-const std::vector<uint8_t>& WMHelper::GetDisplayIdentificationData(
-    int64_t display_id) const {
-  const auto& displays = ash::Shell::Get()
-                             ->window_tree_host_manager()
-                             ->display_configurator()
-                             ->cached_displays();
-
-  for (display::DisplaySnapshot* display : displays)
-    if (display->display_id() == display_id)
-      return display->edid();
-
-  static std::vector<uint8_t> no_data;
-  return no_data;
-}
-
-aura::Window* WMHelper::GetPrimaryDisplayContainer(int container_id) {
-  return ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
-                                  container_id);
-}
-
-aura::Window* WMHelper::GetActiveWindow() const {
-  return ash::Shell::Get()->activation_client()->GetActiveWindow();
-}
-
-aura::Window* WMHelper::GetFocusedWindow() const {
-  aura::client::FocusClient* focus_client =
-      aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
-  return focus_client->GetFocusedWindow();
-}
-
-aura::client::CursorClient* WMHelper::GetCursorClient() {
-  return aura::client::GetCursorClient(ash::Shell::GetPrimaryRootWindow());
-}
-
-void WMHelper::AddPreTargetHandler(ui::EventHandler* handler) {
-  ash::Shell::Get()->AddPreTargetHandler(handler);
-}
-
-void WMHelper::PrependPreTargetHandler(ui::EventHandler* handler) {
-  ash::Shell::Get()->AddPreTargetHandler(
-      handler, ui::EventTarget::Priority::kAccessibility);
-}
-
-void WMHelper::RemovePreTargetHandler(ui::EventHandler* handler) {
-  ash::Shell::Get()->RemovePreTargetHandler(handler);
-}
-
-void WMHelper::AddPostTargetHandler(ui::EventHandler* handler) {
-  ash::Shell::Get()->AddPostTargetHandler(handler);
-}
-
-void WMHelper::RemovePostTargetHandler(ui::EventHandler* handler) {
-  ash::Shell::Get()->RemovePostTargetHandler(handler);
-}
-
-bool WMHelper::IsTabletModeWindowManagerEnabled() const {
-  return ash::Shell::Get()
-      ->tablet_mode_controller()
-      ->IsTabletModeWindowManagerEnabled();
-}
-
-double WMHelper::GetDefaultDeviceScaleFactor() const {
-  if (!display::Display::HasInternalDisplay())
-    return 1.0;
-
-  if (display::Display::HasForceDeviceScaleFactor())
-    return display::Display::GetForcedDeviceScaleFactor();
-
-  display::DisplayManager* display_manager =
-      ash::Shell::Get()->display_manager();
-  const display::ManagedDisplayInfo& display_info =
-      display_manager->GetDisplayInfo(display::Display::InternalDisplayId());
-  DCHECK(display_info.display_modes().size());
-  return display_info.display_modes()[0].device_scale_factor();
-}
-
 }  // namespace exo
diff --git a/components/exo/wm_helper.h b/components/exo/wm_helper.h
index d43f7837..42c7b55 100644
--- a/components/exo/wm_helper.h
+++ b/components/exo/wm_helper.h
@@ -7,17 +7,13 @@
 
 #include <vector>
 
-#include "ash/display/window_tree_host_manager.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "ui/aura/client/drag_drop_delegate.h"
+#include "ui/aura/env.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/compositor/compositor_vsync_manager.h"
 
-namespace ash {
-class TabletModeObserver;
-}
-
 namespace aura {
 class env;
 class Window;
@@ -46,7 +42,7 @@
 
 namespace exo {
 
-// A helper class for accessing WindowManager related features.
+// Helper interface for accessing WindowManager related features.
 class WMHelper : public aura::client::DragDropDelegate {
  public:
   class DragDropObserver {
@@ -60,67 +56,60 @@
     virtual ~DragDropObserver() {}
   };
 
-  explicit WMHelper(aura::Env* env);
+  WMHelper();
   ~WMHelper() override;
 
   static void SetInstance(WMHelper* helper);
   static WMHelper* GetInstance();
   static bool HasInstance();
 
-  aura::Env* env() { return env_; }
+  virtual aura::Env* env() = 0;
 
-  void AddActivationObserver(wm::ActivationChangeObserver* observer);
-  void RemoveActivationObserver(wm::ActivationChangeObserver* observer);
-  void AddFocusObserver(aura::client::FocusChangeObserver* observer);
-  void RemoveFocusObserver(aura::client::FocusChangeObserver* observer);
-  void AddTabletModeObserver(ash::TabletModeObserver* observer);
-  void RemoveTabletModeObserver(ash::TabletModeObserver* observer);
+  virtual void AddActivationObserver(
+      wm::ActivationChangeObserver* observer) = 0;
+  virtual void RemoveActivationObserver(
+      wm::ActivationChangeObserver* observer) = 0;
+  virtual void AddFocusObserver(
+      aura::client::FocusChangeObserver* observer) = 0;
+  virtual void RemoveFocusObserver(
+      aura::client::FocusChangeObserver* observer) = 0;
 
-  void AddDisplayConfigurationObserver(
-      ash::WindowTreeHostManager::Observer* observer);
-  void RemoveDisplayConfigurationObserver(
-      ash::WindowTreeHostManager::Observer* observer);
-  void AddDragDropObserver(DragDropObserver* observer);
-  void RemoveDragDropObserver(DragDropObserver* observer);
-  void SetDragDropDelegate(aura::Window*);
-  void ResetDragDropDelegate(aura::Window*);
-  void AddVSyncObserver(ui::CompositorVSyncManager::Observer* observer);
-  void RemoveVSyncObserver(ui::CompositorVSyncManager::Observer* observer);
+  virtual void AddDragDropObserver(DragDropObserver* observer) = 0;
+  virtual void RemoveDragDropObserver(DragDropObserver* observer) = 0;
+  virtual void SetDragDropDelegate(aura::Window*) = 0;
+  virtual void ResetDragDropDelegate(aura::Window*) = 0;
+  virtual void AddVSyncObserver(
+      ui::CompositorVSyncManager::Observer* observer) = 0;
+  virtual void RemoveVSyncObserver(
+      ui::CompositorVSyncManager::Observer* observer) = 0;
 
-  const display::ManagedDisplayInfo& GetDisplayInfo(int64_t display_id) const;
-  const std::vector<uint8_t>& GetDisplayIdentificationData(
-      int64_t display_id) const;
+  virtual const display::ManagedDisplayInfo& GetDisplayInfo(
+      int64_t display_id) const = 0;
+  virtual const std::vector<uint8_t>& GetDisplayIdentificationData(
+      int64_t display_id) const = 0;
 
-  aura::Window* GetPrimaryDisplayContainer(int container_id);
-  aura::Window* GetActiveWindow() const;
-  aura::Window* GetFocusedWindow() const;
-  aura::client::CursorClient* GetCursorClient();
-  void AddPreTargetHandler(ui::EventHandler* handler);
-  void PrependPreTargetHandler(ui::EventHandler* handler);
-  void RemovePreTargetHandler(ui::EventHandler* handler);
-  void AddPostTargetHandler(ui::EventHandler* handler);
-  void RemovePostTargetHandler(ui::EventHandler* handler);
-  bool IsTabletModeWindowManagerEnabled() const;
-  double GetDefaultDeviceScaleFactor() const;
+  virtual aura::Window* GetPrimaryDisplayContainer(int container_id) = 0;
+  virtual aura::Window* GetActiveWindow() const = 0;
+  virtual aura::Window* GetFocusedWindow() const = 0;
+  virtual aura::client::CursorClient* GetCursorClient() = 0;
+  virtual void AddPreTargetHandler(ui::EventHandler* handler) = 0;
+  virtual void PrependPreTargetHandler(ui::EventHandler* handler) = 0;
+  virtual void RemovePreTargetHandler(ui::EventHandler* handler) = 0;
+  virtual void AddPostTargetHandler(ui::EventHandler* handler) = 0;
+  virtual void RemovePostTargetHandler(ui::EventHandler* handler) = 0;
+  virtual bool IsTabletModeWindowManagerEnabled() const = 0;
+  virtual double GetDefaultDeviceScaleFactor() const = 0;
 
   // Overridden from aura::client::DragDropDelegate:
-  void OnDragEntered(const ui::DropTargetEvent& event) override;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override;
-  void OnDragExited() override;
-  int OnPerformDrop(const ui::DropTargetEvent& event) override;
+  void OnDragEntered(const ui::DropTargetEvent& event) override = 0;
+  int OnDragUpdated(const ui::DropTargetEvent& event) override = 0;
+  void OnDragExited() override = 0;
+  int OnPerformDrop(const ui::DropTargetEvent& event) override = 0;
 
- private:
-  base::ObserverList<DragDropObserver>::Unchecked drag_drop_observers_;
-
-  // The most recently cached VSync parameters, sent to observers on addition.
-  base::TimeTicks vsync_timebase_;
-  base::TimeDelta vsync_interval_;
-  scoped_refptr<ui::CompositorVSyncManager> vsync_manager_;
-  aura::Env* const env_;
-
+ protected:
   DISALLOW_COPY_AND_ASSIGN(WMHelper);
 };
 
 }  // namespace exo
 
-#endif  // COMPONENTS_EXO_WM_HELPER_H_
+#endif  // COMPONENTS_EXO_WM_HELPER_H_
\ No newline at end of file
diff --git a/components/exo/wm_helper_chromeos.cc b/components/exo/wm_helper_chromeos.cc
new file mode 100644
index 0000000..883fae7
--- /dev/null
+++ b/components/exo/wm_helper_chromeos.cc
@@ -0,0 +1,219 @@
+// 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/exo/wm_helper_chromeos.h"
+#include "components/exo/wm_helper.h"
+
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/shell.h"
+#include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/memory/singleton.h"
+#include "ui/aura/client/drag_drop_delegate.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/display/manager/display_configurator.h"
+#include "ui/display/manager/display_manager.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/wm/public/activation_client.h"
+
+namespace exo {
+namespace {
+
+aura::Window* GetPrimaryRoot() {
+  return ash::Shell::Get()->GetPrimaryRootWindow();
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// WMHelperChromeOS, public:
+
+WMHelperChromeOS::WMHelperChromeOS(aura::Env* env)
+    : vsync_manager_(
+          GetPrimaryRoot()->layer()->GetCompositor()->vsync_manager()),
+      env_(env) {}
+
+WMHelperChromeOS::~WMHelperChromeOS() {}
+
+WMHelperChromeOS* WMHelperChromeOS::GetInstance() {
+  return static_cast<WMHelperChromeOS*>(WMHelper::GetInstance());
+}
+
+void WMHelperChromeOS::AddTabletModeObserver(
+    ash::TabletModeObserver* observer) {
+  ash::Shell::Get()->tablet_mode_controller()->AddObserver(observer);
+}
+
+void WMHelperChromeOS::RemoveTabletModeObserver(
+    ash::TabletModeObserver* observer) {
+  ash::Shell::Get()->tablet_mode_controller()->RemoveObserver(observer);
+}
+
+void WMHelperChromeOS::AddDisplayConfigurationObserver(
+    ash::WindowTreeHostManager::Observer* observer) {
+  ash::Shell::Get()->window_tree_host_manager()->AddObserver(observer);
+}
+
+void WMHelperChromeOS::RemoveDisplayConfigurationObserver(
+    ash::WindowTreeHostManager::Observer* observer) {
+  ash::Shell::Get()->window_tree_host_manager()->RemoveObserver(observer);
+}
+
+aura::Env* WMHelperChromeOS::env() {
+  return env_;
+}
+
+void WMHelperChromeOS::AddActivationObserver(
+    wm::ActivationChangeObserver* observer) {
+  ash::Shell::Get()->activation_client()->AddObserver(observer);
+}
+
+void WMHelperChromeOS::RemoveActivationObserver(
+    wm::ActivationChangeObserver* observer) {
+  ash::Shell::Get()->activation_client()->RemoveObserver(observer);
+}
+
+void WMHelperChromeOS::AddFocusObserver(
+    aura::client::FocusChangeObserver* observer) {
+  aura::client::GetFocusClient(GetPrimaryRoot())->AddObserver(observer);
+}
+
+void WMHelperChromeOS::RemoveFocusObserver(
+    aura::client::FocusChangeObserver* observer) {
+  aura::client::GetFocusClient(GetPrimaryRoot())->RemoveObserver(observer);
+}
+
+void WMHelperChromeOS::AddDragDropObserver(DragDropObserver* observer) {
+  drag_drop_observers_.AddObserver(observer);
+}
+
+void WMHelperChromeOS::RemoveDragDropObserver(DragDropObserver* observer) {
+  drag_drop_observers_.RemoveObserver(observer);
+}
+
+void WMHelperChromeOS::SetDragDropDelegate(aura::Window* window) {
+  aura::client::SetDragDropDelegate(window, this);
+}
+
+void WMHelperChromeOS::ResetDragDropDelegate(aura::Window* window) {
+  aura::client::SetDragDropDelegate(window, nullptr);
+}
+
+void WMHelperChromeOS::AddVSyncObserver(
+    ui::CompositorVSyncManager::Observer* observer) {
+  vsync_manager_->AddObserver(observer);
+}
+
+void WMHelperChromeOS::RemoveVSyncObserver(
+    ui::CompositorVSyncManager::Observer* observer) {
+  vsync_manager_->RemoveObserver(observer);
+}
+
+void WMHelperChromeOS::OnDragEntered(const ui::DropTargetEvent& event) {
+  for (DragDropObserver& observer : drag_drop_observers_)
+    observer.OnDragEntered(event);
+}
+
+int WMHelperChromeOS::OnDragUpdated(const ui::DropTargetEvent& event) {
+  int valid_operation = ui::DragDropTypes::DRAG_NONE;
+  for (DragDropObserver& observer : drag_drop_observers_)
+    valid_operation = valid_operation | observer.OnDragUpdated(event);
+  return valid_operation;
+}
+
+void WMHelperChromeOS::OnDragExited() {
+  for (DragDropObserver& observer : drag_drop_observers_)
+    observer.OnDragExited();
+}
+
+int WMHelperChromeOS::OnPerformDrop(const ui::DropTargetEvent& event) {
+  for (DragDropObserver& observer : drag_drop_observers_)
+    observer.OnPerformDrop(event);
+  // TODO(hirono): Return the correct result instead of always returning
+  // DRAG_MOVE.
+  return ui::DragDropTypes::DRAG_MOVE;
+}
+
+const display::ManagedDisplayInfo& WMHelperChromeOS::GetDisplayInfo(
+    int64_t display_id) const {
+  return ash::Shell::Get()->display_manager()->GetDisplayInfo(display_id);
+}
+
+const std::vector<uint8_t>& WMHelperChromeOS::GetDisplayIdentificationData(
+    int64_t display_id) const {
+  const auto& displays = ash::Shell::Get()
+                             ->window_tree_host_manager()
+                             ->display_configurator()
+                             ->cached_displays();
+
+  for (display::DisplaySnapshot* display : displays)
+    if (display->display_id() == display_id)
+      return display->edid();
+
+  static std::vector<uint8_t> no_data;
+  return no_data;
+}
+
+aura::Window* WMHelperChromeOS::GetPrimaryDisplayContainer(int container_id) {
+  return ash::Shell::GetContainer(ash::Shell::GetPrimaryRootWindow(),
+                                  container_id);
+}
+
+aura::Window* WMHelperChromeOS::GetActiveWindow() const {
+  return ash::Shell::Get()->activation_client()->GetActiveWindow();
+}
+
+aura::Window* WMHelperChromeOS::GetFocusedWindow() const {
+  aura::client::FocusClient* focus_client =
+      aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+  return focus_client->GetFocusedWindow();
+}
+
+aura::client::CursorClient* WMHelperChromeOS::GetCursorClient() {
+  return aura::client::GetCursorClient(ash::Shell::GetPrimaryRootWindow());
+}
+
+void WMHelperChromeOS::AddPreTargetHandler(ui::EventHandler* handler) {
+  ash::Shell::Get()->AddPreTargetHandler(handler);
+}
+
+void WMHelperChromeOS::PrependPreTargetHandler(ui::EventHandler* handler) {
+  ash::Shell::Get()->AddPreTargetHandler(
+      handler, ui::EventTarget::Priority::kAccessibility);
+}
+
+void WMHelperChromeOS::RemovePreTargetHandler(ui::EventHandler* handler) {
+  ash::Shell::Get()->RemovePreTargetHandler(handler);
+}
+
+void WMHelperChromeOS::AddPostTargetHandler(ui::EventHandler* handler) {
+  ash::Shell::Get()->AddPostTargetHandler(handler);
+}
+
+void WMHelperChromeOS::RemovePostTargetHandler(ui::EventHandler* handler) {
+  ash::Shell::Get()->RemovePostTargetHandler(handler);
+}
+
+bool WMHelperChromeOS::IsTabletModeWindowManagerEnabled() const {
+  return ash::Shell::Get()
+      ->tablet_mode_controller()
+      ->IsTabletModeWindowManagerEnabled();
+}
+
+double WMHelperChromeOS::GetDefaultDeviceScaleFactor() const {
+  if (!display::Display::HasInternalDisplay())
+    return 1.0;
+
+  if (display::Display::HasForceDeviceScaleFactor())
+    return display::Display::GetForcedDeviceScaleFactor();
+
+  display::DisplayManager* display_manager =
+      ash::Shell::Get()->display_manager();
+  const display::ManagedDisplayInfo& display_info =
+      display_manager->GetDisplayInfo(display::Display::InternalDisplayId());
+  DCHECK(display_info.display_modes().size());
+  return display_info.display_modes()[0].device_scale_factor();
+}
+
+}  // namespace exo
diff --git a/components/exo/wm_helper_chromeos.h b/components/exo/wm_helper_chromeos.h
new file mode 100644
index 0000000..299d474d
--- /dev/null
+++ b/components/exo/wm_helper_chromeos.h
@@ -0,0 +1,114 @@
+// 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_EXO_WM_HELPER_CHROMEOS_H_
+#define COMPONENTS_EXO_WM_HELPER_CHROMEOS_H_
+
+#include <vector>
+
+#include "ash/display/window_tree_host_manager.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/exo/wm_helper.h"
+#include "ui/aura/client/drag_drop_delegate.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/compositor/compositor_vsync_manager.h"
+
+namespace ash {
+class TabletModeObserver;
+}
+
+namespace aura {
+class env;
+class Window;
+namespace client {
+class CursorClient;
+class FocusChangeObserver;
+}  // namespace client
+}  // namespace aura
+
+namespace wm {
+class ActivationChangeObserver;
+}
+
+namespace display {
+class ManagedDisplayInfo;
+}
+
+namespace ui {
+class EventHandler;
+class DropTargetEvent;
+}  // namespace ui
+
+namespace wm {
+class ActivationChangeObserver;
+}
+
+namespace exo {
+
+// A ChromeOS-specific helper class for accessing WindowManager related
+// features.
+class WMHelperChromeOS : public WMHelper {
+ public:
+  explicit WMHelperChromeOS(aura::Env* env);
+  ~WMHelperChromeOS() override;
+  static WMHelperChromeOS* GetInstance();
+  void AddTabletModeObserver(ash::TabletModeObserver* observer);
+  void RemoveTabletModeObserver(ash::TabletModeObserver* observer);
+  void AddDisplayConfigurationObserver(
+      ash::WindowTreeHostManager::Observer* observer);
+  void RemoveDisplayConfigurationObserver(
+      ash::WindowTreeHostManager::Observer* observer);
+
+  // Overridden from WMHelper
+  aura::Env* env() override;
+  void AddActivationObserver(wm::ActivationChangeObserver* observer) override;
+  void RemoveActivationObserver(
+      wm::ActivationChangeObserver* observer) override;
+  void AddFocusObserver(aura::client::FocusChangeObserver* observer) override;
+  void RemoveFocusObserver(
+      aura::client::FocusChangeObserver* observer) override;
+  void AddDragDropObserver(DragDropObserver* observer) override;
+  void RemoveDragDropObserver(DragDropObserver* observer) override;
+  void SetDragDropDelegate(aura::Window*) override;
+  void ResetDragDropDelegate(aura::Window*) override;
+  void AddVSyncObserver(
+      ui::CompositorVSyncManager::Observer* observer) override;
+  void RemoveVSyncObserver(
+      ui::CompositorVSyncManager::Observer* observer) override;
+
+  const display::ManagedDisplayInfo& GetDisplayInfo(
+      int64_t display_id) const override;
+  const std::vector<uint8_t>& GetDisplayIdentificationData(
+      int64_t display_id) const override;
+
+  aura::Window* GetPrimaryDisplayContainer(int container_id) override;
+  aura::Window* GetActiveWindow() const override;
+  aura::Window* GetFocusedWindow() const override;
+  aura::client::CursorClient* GetCursorClient() override;
+  void AddPreTargetHandler(ui::EventHandler* handler) override;
+  void PrependPreTargetHandler(ui::EventHandler* handler) override;
+  void RemovePreTargetHandler(ui::EventHandler* handler) override;
+  void AddPostTargetHandler(ui::EventHandler* handler) override;
+  void RemovePostTargetHandler(ui::EventHandler* handler) override;
+  bool IsTabletModeWindowManagerEnabled() const override;
+  double GetDefaultDeviceScaleFactor() const override;
+
+  // Overridden from aura::client::DragDropDelegate:
+  void OnDragEntered(const ui::DropTargetEvent& event) override;
+  int OnDragUpdated(const ui::DropTargetEvent& event) override;
+  void OnDragExited() override;
+  int OnPerformDrop(const ui::DropTargetEvent& event) override;
+
+ private:
+  base::ObserverList<DragDropObserver>::Unchecked drag_drop_observers_;
+  scoped_refptr<ui::CompositorVSyncManager> vsync_manager_;
+  aura::Env* const env_;
+
+  DISALLOW_COPY_AND_ASSIGN(WMHelperChromeOS);
+};
+
+}  // namespace exo
+
+#endif  // COMPONENTS_EXO_WM_HELPER_CHROMEOS_H_
diff --git a/components/favicon/ios/web_favicon_driver.mm b/components/favicon/ios/web_favicon_driver.mm
index f33ffa2..9e7a62b 100644
--- a/components/favicon/ios/web_favicon_driver.mm
+++ b/components/favicon/ios/web_favicon_driver.mm
@@ -60,7 +60,7 @@
 
 GURL WebFaviconDriver::GetActiveURL() {
   web::NavigationItem* item =
-      web_state_->GetNavigationManager()->GetVisibleItem();
+      web_state_->GetNavigationManager()->GetLastCommittedItem();
   return item ? item->GetURL() : GURL();
 }
 
diff --git a/components/invalidation/impl/fcm_invalidation_service_unittest.cc b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
index edcf1abd..a86c425 100644
--- a/components/invalidation/impl/fcm_invalidation_service_unittest.cc
+++ b/components/invalidation/impl/fcm_invalidation_service_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "components/gcm_driver/gcm_driver.h"
 #include "components/gcm_driver/instance_id/instance_id.h"
@@ -139,6 +140,7 @@
     fake_invalidator_->EmitOnIncomingInvalidation(invalidation_map);
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<gcm::GCMDriver> gcm_driver_;
   std::unique_ptr<MockInstanceIDDriver> mock_instance_id_driver_;
   std::unique_ptr<MockInstanceID> mock_instance_id_;
diff --git a/components/invalidation/impl/non_blocking_invalidator.cc b/components/invalidation/impl/non_blocking_invalidator.cc
index d24dcb8..810b9773 100644
--- a/components/invalidation/impl/non_blocking_invalidator.cc
+++ b/components/invalidation/impl/non_blocking_invalidator.cc
@@ -34,7 +34,7 @@
       const scoped_refptr<base::SingleThreadTaskRunner>&
           invalidation_state_tracker_task_runner,
       const std::string& client_info,
-      scoped_refptr<net::URLRequestContextGetter> request_context_getter)
+      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
       : network_channel_creator(network_channel_creator),
         invalidator_client_id(invalidator_client_id),
         saved_invalidations(saved_invalidations),
@@ -43,7 +43,7 @@
         invalidation_state_tracker_task_runner(
             invalidation_state_tracker_task_runner),
         client_info(client_info),
-        request_context_getter(request_context_getter) {}
+        network_task_runner(network_task_runner) {}
 
   NetworkChannelCreator network_channel_creator;
   std::string invalidator_client_id;
@@ -53,7 +53,7 @@
   scoped_refptr<base::SingleThreadTaskRunner>
       invalidation_state_tracker_task_runner;
   std::string client_info;
-  scoped_refptr<net::URLRequestContextGetter> request_context_getter;
+  scoped_refptr<base::SingleThreadTaskRunner> network_task_runner;
 };
 
 namespace {
@@ -154,9 +154,8 @@
 
 void NonBlockingInvalidator::Core::Initialize(
     const NonBlockingInvalidator::InitializeOptions& initialize_options) {
-  DCHECK(initialize_options.request_context_getter);
-  network_task_runner_ =
-      initialize_options.request_context_getter->GetNetworkTaskRunner();
+  DCHECK(initialize_options.network_task_runner);
+  network_task_runner_ = initialize_options.network_task_runner;
   DCHECK(network_task_runner_->BelongsToCurrentThread());
   std::unique_ptr<SyncNetworkChannel> network_channel =
       initialize_options.network_channel_creator.Run();
@@ -222,10 +221,10 @@
     const std::string& invalidation_bootstrap_data,
     InvalidationStateTracker* invalidation_state_tracker,
     const std::string& client_info,
-    const scoped_refptr<net::URLRequestContextGetter>& request_context_getter)
+    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner)
     : invalidation_state_tracker_(invalidation_state_tracker),
       parent_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      network_task_runner_(request_context_getter->GetNetworkTaskRunner()),
+      network_task_runner_(network_task_runner),
       weak_ptr_factory_(this) {
   base::WeakPtr<NonBlockingInvalidator> weak_ptr_this =
       weak_ptr_factory_.GetWeakPtr();
@@ -236,7 +235,7 @@
   InitializeOptions initialize_options(
       network_channel_creator, invalidator_client_id, saved_invalidations,
       invalidation_bootstrap_data, weak_ptr_this,
-      base::ThreadTaskRunnerHandle::Get(), client_info, request_context_getter);
+      base::ThreadTaskRunnerHandle::Get(), client_info, network_task_runner);
 
   if (!network_task_runner_->PostTask(
           FROM_HERE, base::BindOnce(&NonBlockingInvalidator::Core::Initialize,
diff --git a/components/invalidation/impl/non_blocking_invalidator.h b/components/invalidation/impl/non_blocking_invalidator.h
index 2f49620..d5d30a9 100644
--- a/components/invalidation/impl/non_blocking_invalidator.h
+++ b/components/invalidation/impl/non_blocking_invalidator.h
@@ -54,8 +54,7 @@
       const std::string& invalidation_bootstrap_data,
       InvalidationStateTracker* invalidation_state_tracker,
       const std::string& client_info,
-      const scoped_refptr<net::URLRequestContextGetter>&
-          request_context_getter);
+      scoped_refptr<base::SingleThreadTaskRunner> network_task_runner);
 
   ~NonBlockingInvalidator() override;
 
diff --git a/components/invalidation/impl/non_blocking_invalidator_unittest.cc b/components/invalidation/impl/non_blocking_invalidator_unittest.cc
index 4ae9e028d0..eb147ae 100644
--- a/components/invalidation/impl/non_blocking_invalidator_unittest.cc
+++ b/components/invalidation/impl/non_blocking_invalidator_unittest.cc
@@ -48,15 +48,11 @@
         network::TestNetworkConnectionTracker::GetInstance();
     NetworkChannelCreator network_channel_creator =
         NonBlockingInvalidator::MakePushClientChannelCreator(notifier_options);
-    invalidator_.reset(
-        new NonBlockingInvalidator(
-            network_channel_creator,
-            invalidator_client_id,
-            UnackedInvalidationsMap(),
-            initial_state,
-            invalidation_state_tracker.get(),
-            "fake_client_info",
-            request_context_getter_));
+    invalidator_.reset(new NonBlockingInvalidator(
+        network_channel_creator, invalidator_client_id,
+        UnackedInvalidationsMap(), initial_state,
+        invalidation_state_tracker.get(), "fake_client_info",
+        request_context_getter_->GetNetworkTaskRunner()));
   }
 
   Invalidator* GetInvalidator() {
diff --git a/components/invalidation/impl/ticl_invalidation_service.cc b/components/invalidation/impl/ticl_invalidation_service.cc
index e83bccd..70e8f2ed 100644
--- a/components/invalidation/impl/ticl_invalidation_service.cc
+++ b/components/invalidation/impl/ticl_invalidation_service.cc
@@ -386,13 +386,12 @@
   UMA_HISTOGRAM_ENUMERATION(
       "Invalidations.NetworkChannel", network_channel, NETWORK_CHANNELS_COUNT);
   invalidator_.reset(new syncer::NonBlockingInvalidator(
-          network_channel_creator,
-          invalidation_state_tracker_->GetInvalidatorClientId(),
-          invalidation_state_tracker_->GetSavedInvalidations(),
-          invalidation_state_tracker_->GetBootstrapData(),
-          invalidation_state_tracker_.get(),
-          user_agent_,
-          request_context_));
+      network_channel_creator,
+      invalidation_state_tracker_->GetInvalidatorClientId(),
+      invalidation_state_tracker_->GetSavedInvalidations(),
+      invalidation_state_tracker_->GetBootstrapData(),
+      invalidation_state_tracker_.get(), user_agent_,
+      request_context_->GetNetworkTaskRunner()));
 
   UpdateInvalidatorCredentials();
 
diff --git a/components/invalidation/impl/ticl_invalidation_service_unittest.cc b/components/invalidation/impl/ticl_invalidation_service_unittest.cc
index 138a5f6..02dd216a 100644
--- a/components/invalidation/impl/ticl_invalidation_service_unittest.cc
+++ b/components/invalidation/impl/ticl_invalidation_service_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "components/gcm_driver/gcm_driver.h"
 #include "components/invalidation/impl/fake_invalidation_state_tracker.h"
@@ -102,6 +103,7 @@
     fake_invalidator_->EmitOnIncomingInvalidation(invalidation_map);
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   identity::IdentityTestEnvironment identity_test_env_;
   std::unique_ptr<gcm::GCMDriver> gcm_driver_;
   std::unique_ptr<invalidation::IdentityProvider> identity_provider_;
diff --git a/components/leveldb_proto/proto_database.h b/components/leveldb_proto/proto_database.h
index 7f7d321..9c708df 100644
--- a/components/leveldb_proto/proto_database.h
+++ b/components/leveldb_proto/proto_database.h
@@ -44,6 +44,10 @@
 
   // Asynchronously initializes the object with the specified |options|.
   // |callback| will be invoked on the calling thread when complete.
+  virtual void Init(const std::string& client_name,
+                    typename ProtoDatabase<T>::InitCallback callback) = 0;
+  // This version of Init is for compatibility, since many of the current
+  // proto database clients still use this.
   virtual void Init(const char* client_name,
                     const base::FilePath& database_dir,
                     const leveldb_env::Options& options,
diff --git a/components/leveldb_proto/testing/fake_db.h b/components/leveldb_proto/testing/fake_db.h
index 5753744..00b2fe1 100644
--- a/components/leveldb_proto/testing/fake_db.h
+++ b/components/leveldb_proto/testing/fake_db.h
@@ -16,13 +16,13 @@
 #include "base/files/file_path.h"
 #include "base/task/post_task.h"
 #include "base/test/test_simple_task_runner.h"
-#include "components/leveldb_proto/proto_database.h"
+#include "components/leveldb_proto/unique_proto_database.h"
 
 namespace leveldb_proto {
 namespace test {
 
 template <typename T>
-class FakeDB : public ProtoDatabase<T> {
+class FakeDB : public UniqueProtoDatabase<T> {
   using Callback = base::OnceCallback<void(bool)>;
 
  public:
@@ -122,7 +122,8 @@
 
 template <typename T>
 FakeDB<T>::FakeDB(EntryMap* db)
-    : ProtoDatabase<T>(base::MakeRefCounted<base::TestSimpleTaskRunner>()) {
+    : UniqueProtoDatabase<T>(
+          base::MakeRefCounted<base::TestSimpleTaskRunner>()) {
   db_ = db;
 }
 
diff --git a/components/leveldb_proto/unique_proto_database.h b/components/leveldb_proto/unique_proto_database.h
index 3b3fc0f..8240b262 100644
--- a/components/leveldb_proto/unique_proto_database.h
+++ b/components/leveldb_proto/unique_proto_database.h
@@ -19,33 +19,79 @@
 class UniqueProtoDatabase : public ProtoDatabase<T> {
  public:
   UniqueProtoDatabase(
-      const scoped_refptr<base::SequencedTaskRunner>& task_runner)
-      : ProtoDatabase<T>(task_runner) {}
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
 
+  UniqueProtoDatabase(
+      const base::FilePath& database_dir,
+      const leveldb_env::Options& options,
+      const scoped_refptr<base::SequencedTaskRunner>& task_runner);
+
+  // This version of Init is for compatibility, since many of the current
+  // proto database still use this.
   virtual void Init(const char* client_name,
                     const base::FilePath& database_dir,
                     const leveldb_env::Options& options,
-                    typename ProtoDatabase<T>::InitCallback callback) override {
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    db_ = std::make_unique<LevelDB>(client_name);
-    ProtoDatabase<T>::InitWithDatabase(db_.get(), database_dir, options,
-                                       std::move(callback));
-  }
+                    typename ProtoDatabase<T>::InitCallback callback) override;
 
-  virtual ~UniqueProtoDatabase() {
-    DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-    if (db_.get() &&
-        !this->db_wrapper_->task_runner()->DeleteSoon(FROM_HERE, db_.release()))
-      DLOG(WARNING) << "Proto database will not be deleted.";
-  }
+  virtual void Init(const std::string& client_name,
+                    typename ProtoDatabase<T>::InitCallback callback) override;
+
+  virtual ~UniqueProtoDatabase();
 
  private:
   THREAD_CHECKER(thread_checker_);
 
+  base::FilePath database_dir_;
+  leveldb_env::Options options_;
+
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   std::unique_ptr<LevelDB> db_;
 };
 
+template <typename T>
+UniqueProtoDatabase<T>::UniqueProtoDatabase(
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : ProtoDatabase<T>(task_runner) {}
+
+template <typename T>
+UniqueProtoDatabase<T>::UniqueProtoDatabase(
+    const base::FilePath& database_dir,
+    const leveldb_env::Options& options,
+    const scoped_refptr<base::SequencedTaskRunner>& task_runner)
+    : ProtoDatabase<T>(task_runner) {
+  database_dir_ = database_dir;
+  options_ = options;
+}
+
+template <typename T>
+UniqueProtoDatabase<T>::~UniqueProtoDatabase() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (db_.get() &&
+      !this->db_wrapper_->task_runner()->DeleteSoon(FROM_HERE, db_.release()))
+    DLOG(WARNING) << "Proto database will not be deleted.";
+}
+
+template <typename T>
+void UniqueProtoDatabase<T>::Init(
+    const std::string& client_name,
+    typename ProtoDatabase<T>::InitCallback callback) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  db_ = std::make_unique<LevelDB>(client_name.c_str());
+  ProtoDatabase<T>::InitWithDatabase(db_.get(), database_dir_, options_,
+                                     std::move(callback));
+}
+
+template <typename T>
+void UniqueProtoDatabase<T>::Init(
+    const char* client_name,
+    const base::FilePath& database_dir,
+    const leveldb_env::Options& options,
+    typename ProtoDatabase<T>::InitCallback callback) {
+  database_dir_ = database_dir;
+  options_ = options;
+  Init(std::string(client_name), std::move(callback));
+}
+
 }  // namespace leveldb_proto
 
 #endif  // COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
diff --git a/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc
index c1fe97d..59c9f37 100644
--- a/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_fetcher_impl_unittest.cc
@@ -177,11 +177,11 @@
             {{"send_top_languages", "true"},
              {"send_user_class", "true"},
              {"append_request_priority_as_query_parameter", "true"}}),
+        mock_task_runner_(new base::TestMockTimeTaskRunner(
+            base::TestMockTimeTaskRunner::Type::kBoundToThread)),
         params_manager_(ntp_snippets::kArticleSuggestionsFeature.name,
                         default_variation_params_,
-                        {ntp_snippets::kArticleSuggestionsFeature.name}),
-        mock_task_runner_(new base::TestMockTimeTaskRunner(
-            base::TestMockTimeTaskRunner::Type::kBoundToThread)) {
+                        {ntp_snippets::kArticleSuggestionsFeature.name}) {
     UserClassifier::RegisterProfilePrefs(utils_.pref_service()->registry());
     user_classifier_ = std::make_unique<UserClassifier>(
         utils_.pref_service(), base::DefaultClock::GetInstance());
@@ -259,13 +259,13 @@
 
  protected:
   std::map<std::string, std::string> default_variation_params_;
+  scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
   identity::IdentityTestEnvironment identity_test_env_;
   network::TestURLLoaderFactory test_url_loader_factory_;
 
  private:
   test::RemoteSuggestionsTestUtils utils_;
   variations::testing::VariationParamsManager params_manager_;
-  scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
   std::unique_ptr<RemoteSuggestionsFetcherImpl> fetcher_;
   std::unique_ptr<UserClassifier> user_classifier_;
   MockSnippetsAvailableCallback mock_callback_;
diff --git a/components/ntp_tiles/custom_links_manager_impl_unittest.cc b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
index 11c8539c..eceabe13b 100644
--- a/components/ntp_tiles/custom_links_manager_impl_unittest.cc
+++ b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
@@ -111,30 +111,23 @@
 };
 
 TEST_F(CustomLinksManagerImplTest, InitializeOnlyOnce) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  NTPTilesVector new_tiles = FillTestTiles(kTestCase2);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> empty_links;
-
   ASSERT_FALSE(custom_links_->IsInitialized());
   ASSERT_TRUE(custom_links_->GetLinks().empty());
 
   // Initialize.
-  EXPECT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  EXPECT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to initialize again. This should fail and leave the links intact.
-  EXPECT_FALSE(custom_links_->Initialize(new_tiles));
+  EXPECT_FALSE(custom_links_->Initialize(FillTestTiles(kTestCase2)));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, UninitializeDeletesOldLinks) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
 
   custom_links_->Uninitialize();
   EXPECT_TRUE(custom_links_->GetLinks().empty());
@@ -145,46 +138,37 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, ReInitializeWithNewLinks) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  NTPTilesVector new_tiles = FillTestTiles(kTestCase2);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> new_links = FillTestLinks(kTestCase2);
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
 
   custom_links_->Uninitialize();
   ASSERT_TRUE(custom_links_->GetLinks().empty());
 
   // Initialize with new links.
-  EXPECT_TRUE(custom_links_->Initialize(new_tiles));
-  EXPECT_EQ(new_links, custom_links_->GetLinks());
+  EXPECT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase2)));
+  EXPECT_EQ(FillTestLinks(kTestCase2), custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, AddLink) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> expected_links = initial_links;
-  expected_links.emplace_back(
-      Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false});
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Add link.
+  std::vector<Link> expected_links = initial_links;
+  expected_links.emplace_back(
+      Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false});
   EXPECT_TRUE(
       custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
   EXPECT_EQ(expected_links, custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, AddLinkWhenAtMaxLinks) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCaseMax);
-  std::vector<Link> initial_links = FillTestLinks(kTestCaseMax);
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCaseMax);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCaseMax)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to add link. This should fail and not modify the list.
@@ -194,11 +178,9 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, AddDuplicateLink) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to add duplicate link. This should fail and not modify the list.
@@ -208,44 +190,39 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, UpdateLink) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> links_after_update_url(initial_links);
-  links_after_update_url[0].url = GURL(kTestUrl);
-  links_after_update_url[0].is_most_visited = false;
-  std::vector<Link> links_after_update_title(links_after_update_url);
-  links_after_update_title[0].title = base::UTF8ToUTF16(kTestTitle);
-  std::vector<Link> links_after_update_both(initial_links);
-  links_after_update_both[0].is_most_visited = false;
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
 
   // Update the link's URL.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
                                         base::string16()));
-  EXPECT_EQ(links_after_update_url, custom_links_->GetLinks());
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestUrl),
+                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
+      custom_links_->GetLinks());
 
   // Update the link's title.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(),
                                         base::UTF8ToUTF16(kTestTitle)));
-  EXPECT_EQ(links_after_update_title, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+            custom_links_->GetLinks());
 
   // Update the link's URL and title.
   EXPECT_TRUE(
       custom_links_->UpdateLink(GURL(kTestUrl), GURL(kTestCase1[0].url),
                                 base::UTF8ToUTF16(kTestCase1[0].title)));
-  EXPECT_EQ(links_after_update_both, custom_links_->GetLinks());
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestCase1[0].url),
+                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
+      custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, UpdateLinkWithInvalidParams) {
-  const GURL kInvalidUrl = GURL("test");
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to update a link that does not exist. This should fail and not modify
@@ -260,20 +237,18 @@
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to pass an invalid URL. This should fail and not modify the list.
-  EXPECT_FALSE(custom_links_->UpdateLink(kInvalidUrl, GURL(),
+  EXPECT_FALSE(custom_links_->UpdateLink(GURL("test"), GURL(),
                                          base::UTF8ToUTF16(kTestTitle)));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
-  EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), kInvalidUrl,
+  EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL("test"),
                                          base::string16()));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, UpdateLinkWhenUrlAlreadyExists) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase2);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase2);
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase2);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase2)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to update a link with a URL that exists in the list. This should fail
@@ -284,25 +259,9 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, ReorderLink) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase3);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase3);
-  std::vector<Link> links_after_reorder1(
-      {Link{GURL(kTestCase3[2].url), base::UTF8ToUTF16(kTestCase3[2].title),
-            true},
-       Link{GURL(kTestCase3[0].url), base::UTF8ToUTF16(kTestCase3[0].title),
-            true},
-       Link{GURL(kTestCase3[1].url), base::UTF8ToUTF16(kTestCase3[1].title),
-            true}});
-  std::vector<Link> links_after_reorder2(
-      {Link{GURL(kTestCase3[0].url), base::UTF8ToUTF16(kTestCase3[0].title),
-            true},
-       Link{GURL(kTestCase3[2].url), base::UTF8ToUTF16(kTestCase3[2].title),
-            true},
-       Link{GURL(kTestCase3[1].url), base::UTF8ToUTF16(kTestCase3[1].title),
-            true}});
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase3);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase3)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to call reorder with the current index. This should fail and not modify
@@ -318,13 +277,34 @@
                                           initial_links.size()));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
+  // Try to call reorder with an invalid URL. This should fail and not modify
+  // the list.
+  EXPECT_FALSE(custom_links_->ReorderLink(GURL(kTestUrl), 0));
+  EXPECT_EQ(initial_links, custom_links_->GetLinks());
+  EXPECT_FALSE(custom_links_->ReorderLink(GURL("test"), 0));
+  EXPECT_EQ(initial_links, custom_links_->GetLinks());
+
   // Move the last link to the front.
   EXPECT_TRUE(custom_links_->ReorderLink(GURL(kTestCase3[2].url), (size_t)0));
-  EXPECT_EQ(links_after_reorder1, custom_links_->GetLinks());
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestCase3[2].url),
+                              base::UTF8ToUTF16(kTestCase3[2].title), true},
+                         Link{GURL(kTestCase3[0].url),
+                              base::UTF8ToUTF16(kTestCase3[0].title), true},
+                         Link{GURL(kTestCase3[1].url),
+                              base::UTF8ToUTF16(kTestCase3[1].title), true}}),
+      custom_links_->GetLinks());
 
   // Move the same link to the right.
   EXPECT_TRUE(custom_links_->ReorderLink(GURL(kTestCase3[2].url), (size_t)1));
-  EXPECT_EQ(links_after_reorder2, custom_links_->GetLinks());
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestCase3[0].url),
+                              base::UTF8ToUTF16(kTestCase3[0].title), true},
+                         Link{GURL(kTestCase3[2].url),
+                              base::UTF8ToUTF16(kTestCase3[2].title), true},
+                         Link{GURL(kTestCase3[1].url),
+                              base::UTF8ToUTF16(kTestCase3[1].title), true}}),
+      custom_links_->GetLinks());
 
   // Move the same link to the end.
   EXPECT_TRUE(custom_links_->ReorderLink(GURL(kTestCase3[2].url), (size_t)2));
@@ -332,14 +312,13 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, DeleteLink) {
+  // Initialize.
   NTPTilesVector initial_tiles;
   AddTile(&initial_tiles, kTestUrl, kTestTitle);
-  std::vector<Link> initial_links(
-      {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), true}});
-
-  // Initialize.
   ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_EQ(std::vector<Link>(
+                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), true}}),
+            custom_links_->GetLinks());
 
   // Delete link.
   EXPECT_TRUE(custom_links_->DeleteLink(GURL(kTestUrl)));
@@ -347,10 +326,8 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, DeleteLinkWhenUrlDoesNotExist) {
-  NTPTilesVector initial_tiles;
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  ASSERT_TRUE(custom_links_->Initialize(NTPTilesVector()));
   ASSERT_TRUE(custom_links_->GetLinks().empty());
 
   // Try to delete link. This should fail and not modify the list.
@@ -359,14 +336,9 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, UndoAddLink) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> expected_links = initial_links;
-  expected_links.emplace_back(
-      Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false});
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to undo before add is called. This should fail and not modify the list.
@@ -376,7 +348,11 @@
   // Add link.
   EXPECT_TRUE(
       custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
-  EXPECT_EQ(expected_links, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestCase1[0].url),
+                      base::UTF8ToUTF16(kTestCase1[0].title), true},
+                 {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}}),
+            custom_links_->GetLinks());
 
   // Undo add link.
   EXPECT_TRUE(custom_links_->UndoAction());
@@ -388,23 +364,18 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, UndoUpdateLink) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> links_after_update_url(initial_links);
-  links_after_update_url[0].url = GURL(kTestUrl);
-  links_after_update_url[0].is_most_visited = false;
-  std::vector<Link> links_after_update_title(initial_links);
-  links_after_update_title[0].title = base::UTF8ToUTF16(kTestTitle);
-  links_after_update_title[0].is_most_visited = false;
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Update the link's URL.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
                                         base::string16()));
-  EXPECT_EQ(links_after_update_url, custom_links_->GetLinks());
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestUrl),
+                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
+      custom_links_->GetLinks());
 
   // Undo update link.
   EXPECT_TRUE(custom_links_->UndoAction());
@@ -413,7 +384,9 @@
   // Update the link's title.
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(),
                                         base::UTF8ToUTF16(kTestTitle)));
-  EXPECT_EQ(links_after_update_title, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>({Link{GURL(kTestCase1[0].url),
+                                    base::UTF8ToUTF16(kTestTitle), false}}),
+            custom_links_->GetLinks());
 
   // Undo update link.
   EXPECT_TRUE(custom_links_->UndoAction());
@@ -425,12 +398,11 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, UndoDeleteLink) {
+  // Initialize.
   NTPTilesVector initial_tiles;
   AddTile(&initial_tiles, kTestUrl, kTestTitle);
   std::vector<Link> expected_links(
       {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), true}});
-
-  // Initialize.
   ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
   ASSERT_EQ(expected_links, custom_links_->GetLinks());
 
@@ -444,15 +416,13 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, UndoDeleteLinkAfterAdd) {
-  NTPTilesVector initial_tiles;
-  std::vector<Link> expected_links(
-      {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}});
-
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  ASSERT_TRUE(custom_links_->Initialize(NTPTilesVector()));
   ASSERT_TRUE(custom_links_->GetLinks().empty());
 
   // Add link.
+  std::vector<Link> expected_links(
+      {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}});
   ASSERT_TRUE(
       custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
   ASSERT_EQ(expected_links, custom_links_->GetLinks());
@@ -479,8 +449,8 @@
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase2)));
+  ASSERT_EQ(FillTestLinks(kTestCase2), custom_links_->GetLinks());
 
   // Delete a specific Most Visited link.
   EXPECT_CALL(callback, Run());
@@ -492,24 +462,24 @@
                                 {history::URLRow(GURL(kTestCase2[1].url))},
                                 /*favicon_urls=*/std::set<GURL>(),
                                 /*restrict_urls=*/base::nullopt));
-  EXPECT_EQ(expected_links, custom_links_->GetLinks());
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestCase2[0].url),
+                              base::UTF8ToUTF16(kTestCase2[0].title), true}}),
+      custom_links_->GetLinks());
 
   scoped_task_environment_.RunUntilIdle();
 }
 
 TEST_F(CustomLinksManagerImplTest,
        ShouldDeleteMostVisitedOnAllHistoryDeletion) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase2);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase2);
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase2)));
+  ASSERT_EQ(FillTestLinks(kTestCase2), custom_links_->GetLinks());
 
   // Delete all Most Visited links.
   EXPECT_CALL(callback, Run());
@@ -526,22 +496,18 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, ShouldNotDeleteCustomLinkOnHistoryDeletion) {
-  Link added_link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false};
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> links_after_add(initial_links);
-  links_after_add.emplace_back(added_link);
-  std::vector<Link> links_after_all_history_delete;
-  links_after_all_history_delete.emplace_back(added_link);
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  std::vector<Link> links_after_add(
+      {Link{GURL(kTestCase1[0].url), base::UTF8ToUTF16(kTestCase1[0].title),
+            true},
+       Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}});
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
   // Add link.
   ASSERT_TRUE(
       custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
@@ -566,22 +532,22 @@
                                 /*expired=*/false, history::URLRows(),
                                 /*favicon_urls=*/std::set<GURL>(),
                                 /*restrict_urls=*/base::nullopt));
-  EXPECT_EQ(links_after_all_history_delete, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+            custom_links_->GetLinks());
 
   scoped_task_environment_.RunUntilIdle();
 }
 
 TEST_F(CustomLinksManagerImplTest, ShouldIgnoreHistoryExpiredDeletions) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   EXPECT_CALL(callback, Run()).Times(0);
@@ -608,16 +574,14 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, ShouldIgnoreEmptyHistoryDeletions) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
+  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   EXPECT_CALL(callback, Run()).Times(0);
@@ -631,23 +595,19 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, ShouldNotUndoAfterHistoryDeletion) {
-  Link added_link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false};
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> links_after_add(initial_links);
-  links_after_add.emplace_back(added_link);
-  std::vector<Link> links_after_all_history_delete;
-  links_after_all_history_delete.emplace_back(added_link);
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
   // Add link.
+  std::vector<Link> links_after_add(
+      {Link{GURL(kTestCase1[0].url), base::UTF8ToUTF16(kTestCase1[0].title),
+            true},
+       Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}});
   ASSERT_TRUE(
       custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
   ASSERT_EQ(links_after_add, custom_links_->GetLinks());
@@ -667,28 +627,23 @@
 }
 
 TEST_F(CustomLinksManagerImplTest, UpdateListAfterRemoteChange) {
-  Link remote_link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false};
-  NTPTilesVector initial_tiles;
-  std::vector<Link> initial_links;
-  std::vector<Link> links_after_add = FillTestLinks(kTestCase1);
-  links_after_add[0].is_most_visited = false;
-  std::vector<Link> remote_links;
-  remote_links.emplace_back(remote_link);
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(NTPTilesVector()));
+  ASSERT_EQ(std::vector<Link>(), custom_links_->GetLinks());
 
   // Modifying ourselves should not notify.
   EXPECT_CALL(callback, Run()).Times(0);
   EXPECT_TRUE(custom_links_->AddLink(GURL(kTestCase1[0].url),
                                      base::UTF8ToUTF16(kTestCase1[0].title)));
-  EXPECT_EQ(links_after_add, custom_links_->GetLinks());
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestCase1[0].url),
+                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
+      custom_links_->GetLinks());
 
   // Modify the preference. This should notify and update the current list of
   // links.
@@ -696,16 +651,12 @@
   prefs_.SetUserPref(
       prefs::kCustomLinksList,
       std::make_unique<base::Value>(FillTestListStorage(kTestUrl, kTestTitle)));
-  EXPECT_EQ(remote_links, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+            custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, InitializeListAfterRemoteChange) {
-  Link remote_link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false};
-  NTPTilesVector initial_tiles;
-  std::vector<Link> initial_links;
-  std::vector<Link> remote_links(initial_links);
-  remote_links.emplace_back(remote_link);
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
@@ -721,22 +672,20 @@
       prefs::kCustomLinksList,
       std::make_unique<base::Value>(FillTestListStorage(kTestUrl, kTestTitle)));
   EXPECT_TRUE(custom_links_->IsInitialized());
-  EXPECT_EQ(remote_links, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+            custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, UninitializeListAfterRemoteChange) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> remote_links;
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
 
   // Modify the preference. This should notify and uninitialize custom links.
   EXPECT_CALL(callback, Run()).Times(2);
@@ -745,22 +694,18 @@
   prefs_.SetUserPref(prefs::kCustomLinksList,
                      std::make_unique<base::Value>(base::Value::ListStorage()));
   EXPECT_FALSE(custom_links_->IsInitialized());
-  EXPECT_EQ(remote_links, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(), custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, ClearThenUninitializeListAfterRemoteChange) {
-  NTPTilesVector initial_tiles = FillTestTiles(kTestCase1);
-  std::vector<Link> initial_links = FillTestLinks(kTestCase1);
-  std::vector<Link> remote_links;
-
   // Set up Most Visited callback.
   base::MockCallback<base::RepeatingClosure> callback;
   std::unique_ptr<base::CallbackList<void()>::Subscription> subscription =
       custom_links_->RegisterCallbackForOnChanged(callback.Get());
 
   // Initialize.
-  ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(initial_links, custom_links_->GetLinks());
+  ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
+  ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
 
   // Modify the preference. Simulates when the list preference is synced before
   // the initialized preference. This should notify and uninitialize custom
@@ -769,11 +714,11 @@
   prefs_.SetUserPref(prefs::kCustomLinksList,
                      std::make_unique<base::Value>(base::Value::ListStorage()));
   EXPECT_TRUE(custom_links_->IsInitialized());
-  EXPECT_EQ(remote_links, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(), custom_links_->GetLinks());
   prefs_.SetUserPref(prefs::kCustomLinksInitialized,
                      std::make_unique<base::Value>(false));
   EXPECT_FALSE(custom_links_->IsInitialized());
-  EXPECT_EQ(remote_links, custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(), custom_links_->GetLinks());
 }
 
 }  // namespace ntp_tiles
diff --git a/components/optimization_guide/test_component_creator.cc b/components/optimization_guide/test_component_creator.cc
index 26f5486..ac159f8 100644
--- a/components/optimization_guide/test_component_creator.cc
+++ b/components/optimization_guide/test_component_creator.cc
@@ -71,6 +71,86 @@
   return WriteConfigToFileAndReturnComponentInfo(config);
 }
 
+optimization_guide::ComponentInfo
+TestComponentCreator::CreateComponentInfoWithExperimentalPageHints(
+    optimization_guide::proto::OptimizationType optimization_type,
+    const std::vector<std::string>& page_hint_host_suffixes,
+    const std::vector<std::string>& experimental_resource_patterns) {
+  optimization_guide::proto::Configuration config;
+  for (const auto& page_hint_site : page_hint_host_suffixes) {
+    optimization_guide::proto::Hint* hint = config.add_hints();
+    hint->set_key(page_hint_site);
+    hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+
+    optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
+    page_hint->set_page_pattern("*");
+
+    optimization_guide::proto::Optimization* optimization =
+        page_hint->add_whitelisted_optimizations();
+    optimization->set_optimization_type(optimization_type);
+    optimization->set_experiment_name(kFooExperimentName);
+
+    for (auto resource_blocking_pattern : experimental_resource_patterns) {
+      optimization_guide::proto::ResourceLoadingHint* resource_loading_hint =
+          optimization->add_resource_loading_hints();
+      resource_loading_hint->set_loading_optimization_type(
+          optimization_guide::proto::LOADING_BLOCK_RESOURCE);
+      resource_loading_hint->set_resource_pattern(resource_blocking_pattern);
+    }
+  }
+
+  return WriteConfigToFileAndReturnComponentInfo(config);
+}
+
+optimization_guide::ComponentInfo
+TestComponentCreator::CreateComponentInfoWithMixPageHints(
+    optimization_guide::proto::OptimizationType optimization_type,
+    const std::vector<std::string>& page_hint_host_suffixes,
+    const std::vector<std::string>& experimental_resource_patterns,
+    const std::vector<std::string>& default_resource_patterns) {
+  optimization_guide::proto::Configuration config;
+  for (const auto& page_hint_site : page_hint_host_suffixes) {
+    optimization_guide::proto::Hint* hint = config.add_hints();
+    hint->set_key(page_hint_site);
+    hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX);
+
+    optimization_guide::proto::PageHint* page_hint = hint->add_page_hints();
+    page_hint->set_page_pattern("*");
+
+    // Add experimental patterns first so they get higher priority.
+    {
+      optimization_guide::proto::Optimization* optimization =
+          page_hint->add_whitelisted_optimizations();
+      optimization->set_optimization_type(optimization_type);
+      optimization->set_experiment_name(kFooExperimentName);
+
+      for (auto resource_blocking_pattern : experimental_resource_patterns) {
+        optimization_guide::proto::ResourceLoadingHint* resource_loading_hint =
+            optimization->add_resource_loading_hints();
+        resource_loading_hint->set_loading_optimization_type(
+            optimization_guide::proto::LOADING_BLOCK_RESOURCE);
+        resource_loading_hint->set_resource_pattern(resource_blocking_pattern);
+      }
+    }
+
+    {
+      optimization_guide::proto::Optimization* optimization =
+          page_hint->add_whitelisted_optimizations();
+      optimization->set_optimization_type(optimization_type);
+
+      for (auto resource_blocking_pattern : default_resource_patterns) {
+        optimization_guide::proto::ResourceLoadingHint* resource_loading_hint =
+            optimization->add_resource_loading_hints();
+        resource_loading_hint->set_loading_optimization_type(
+            optimization_guide::proto::LOADING_BLOCK_RESOURCE);
+        resource_loading_hint->set_resource_pattern(resource_blocking_pattern);
+      }
+    }
+  }
+
+  return WriteConfigToFileAndReturnComponentInfo(config);
+}
+
 base::FilePath TestComponentCreator::GetFilePath(std::string file_path_suffix) {
   base::ScopedAllowBlockingForTesting allow_blocking;
   EXPECT_TRUE(scoped_temp_dir_->IsValid() ||
diff --git a/components/optimization_guide/test_component_creator.h b/components/optimization_guide/test_component_creator.h
index aace49d..b4181ffb 100644
--- a/components/optimization_guide/test_component_creator.h
+++ b/components/optimization_guide/test_component_creator.h
@@ -16,6 +16,9 @@
 namespace optimization_guide {
 namespace testing {
 
+// Experiment name used for experimental resource loading hints.
+static const char kFooExperimentName[] = "foo_experiment";
+
 // Helper class to create test OptimizationHints components for testing.
 //
 // All temporary files and paths are cleaned up when this instance goes out of
@@ -31,14 +34,42 @@
   optimization_guide::ComponentInfo CreateComponentInfoWithTopLevelWhitelist(
       optimization_guide::proto::OptimizationType optimization_type,
       const std::vector<std::string>& whitelisted_host_suffixes);
+
   // Creates component data based on |whitelisted_host_suffixes| with page hints
   // for type |optimization_type| blocking resources specified by
-  // |resource_patterns|.and returns the ComponentInfo for it.
+  // |resource_patterns|, and returns the ComponentInfo for it.
   optimization_guide::ComponentInfo CreateComponentInfoWithPageHints(
       optimization_guide::proto::OptimizationType optimization_type,
       const std::vector<std::string>& whitelisted_host_suffixes,
       const std::vector<std::string>& resource_patterns);
 
+  // Creates component data based on |whitelisted_host_suffixes| with page hints
+  // for type |optimization_type| blocking resources specified by
+  // |experimental_resource_patterns|, and returns the ComponentInfo for it.
+  // The loading hints are set as experimental with experiment name set to
+  // kFooExperimentName.
+
+  // Creates component data for testing with experimental optimizations. It
+  // creates a PageHint (with page pattern "*" for each key in
+  // |whitelisted_host_suffixes| that each has resource blocking patterns from
+  // |experimental_resource_patterns|.
+  optimization_guide::ComponentInfo
+  CreateComponentInfoWithExperimentalPageHints(
+      optimization_guide::proto::OptimizationType optimization_type,
+      const std::vector<std::string>& whitelisted_host_suffixes,
+      const std::vector<std::string>& experimental_resource_patterns);
+
+  // Creates component data for testing with both default and experimental
+  // optimizations. It creates a PageHint (with page pattern "*" for each key in
+  // |whitelisted_host_suffixes| that each has resource blocking patterns from
+  // |default_resource_patterns| and |experimental_resource_patterns|. The
+  // experimental hints are guarded behind experiment kFooExperimentName.
+  optimization_guide::ComponentInfo CreateComponentInfoWithMixPageHints(
+      optimization_guide::proto::OptimizationType optimization_type,
+      const std::vector<std::string>& whitelisted_host_suffixes,
+      const std::vector<std::string>& experimental_resource_patterns,
+      const std::vector<std::string>& default_resource_patterns);
+
  private:
   // Returns the scoped temp directory path with the |file_path_suffix| that is
   // valid for the lifetime of this instance.
diff --git a/components/password_manager/core/browser/votes_uploader_unittest.cc b/components/password_manager/core/browser/votes_uploader_unittest.cc
index ac451aa3..534dab2 100644
--- a/components/password_manager/core/browser/votes_uploader_unittest.cc
+++ b/components/password_manager/core/browser/votes_uploader_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
 #include "components/autofill/core/browser/autofill_download_manager.h"
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
@@ -127,6 +128,7 @@
     return ASCIIToUTF16("field") + base::UintToString16(index);
   }
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   autofill::TestAutofillDriver test_autofill_driver_;
   autofill::TestAutofillClient test_autofill_client_;
   autofill::TestPersonalDataManager test_personal_data_manager_;
diff --git a/components/password_manager/core/common/password_manager_ui.h b/components/password_manager/core/common/password_manager_ui.h
index 15d8425..425bf5a 100644
--- a/components/password_manager/core/common/password_manager_ui.h
+++ b/components/password_manager/core/common/password_manager_ui.h
@@ -39,10 +39,6 @@
 
   // The user was prompted to sign in to Chrome after saving a password.
   CHROME_SIGN_IN_PROMO_STATE,
-
-  // The user was prompted with the Desktop to Mobile promotion after saving
-  // a password.
-  CHROME_DESKTOP_IOS_PROMO_STATE,
 };
 
 }  // namespace ui
diff --git a/components/policy/tools/generate_extension_admx.py b/components/policy/tools/generate_extension_admx.py
index ba0db85..a6a6b9f6 100755
--- a/components/policy/tools/generate_extension_admx.py
+++ b/components/policy/tools/generate_extension_admx.py
@@ -66,13 +66,19 @@
     self._BeginAdmlTemplate()
     self._BeginAdmxTemplate()
 
+    root_category_full_name = ('extension_' + self._extension_id)
+
+    # Add a category element for the root
+    self._AddCategory(self._extension_name, root_category_full_name ,
+                      'Google:Cat_Google')
+
     properties = self._schema['properties']
     for policy_name, policy_schema in properties.items():
-      self._AddPolicy(policy_name, policy_schema, 'extension',
+      self._AddPolicy(policy_name, policy_schema, root_category_full_name,
                       self._REGISTRY_KEY)
 
     return self._ToPrettyXml(self._admx_doc.toxml()), \
-           self._adml_doc.toxml()
+           self._ToPrettyXml(self._adml_doc.toxml())
 
   def _AddElement(self, parent, name):
     '''
@@ -131,16 +137,25 @@
     self._SetAttribute(namespace_elem, 'namespace', namespace)
     self._SetAttribute(namespace_elem, 'prefix', prefix)
 
-  def _AddCategory(self, display_name, name, parent_category):
+  def _AddCategory(self, display_name, category_full_name,
+                   parent_category_full_name):
     '''
     Adds an ADMX category.
 
+    Args:
+      display_name: The human-readable name of the category.
+      category_full_name:
+        A unique name for the category.
+        This is used in 'name' attributes, which may only contain lowercase
+        letters, uppercase letters, digits and the underscore character.
+      parent_category_full_name: The unique 'full name' of the parent category.
     '''
     category_elem = self._AddElement(self.categories_elem_, 'category')
-    self._SetAttribute(category_elem, 'displayName', display_name, name)
-    self._SetAttribute(category_elem, 'name', name)
+    self._SetAttribute(category_elem, 'displayName', display_name,
+                       category_full_name)
+    self._SetAttribute(category_elem, 'name', category_full_name)
     parent_category_elem = self._AddElement(category_elem, 'parentCategory')
-    self._SetAttribute(parent_category_elem, 'ref', parent_category)
+    self._SetAttribute(parent_category_elem, 'ref', parent_category_full_name)
 
   def _BeginAdmlTemplate(self):
     '''
@@ -191,17 +206,24 @@
     self._SetAttribute(definition_elem, 'name', 'SUPPORTED_WIN7')
 
     self.categories_elem_ = self._AddElement(root_elem, 'categories')
-    self._AddCategory(self._extension_name, 'extension', 'Google:Cat_Google')
-
     self._policies_elem = self._AddElement(root_elem, 'policies')
 
-  def _AddPolicy(self, policy_name, policy_schema, parent_category, parent_key):
+  def _AddPolicy(self, policy_name, policy_schema, parent_category_full_name,
+                 parent_key):
     '''
     Adds a policy with name |policy_name| and schema data |policy_schema| to
     the ADMX/ADML docs.
-
+    Args:
+      policy_name: The name of the policy.
+      policy_schema: Schema data of the policy.
+      parent_category_full_name:
+        The unique 'full name' of the category this policy should be placed
+        under.
+        This is used in 'name' attributes, which may only contain lowercase
+        letters, uppercase letters, digits and the underscore character.
+      parenty_key: The registry key of the parent.
     '''
-    policy_id = self._ToId(policy_name)
+    full_name = parent_category_full_name + '_' + self._ToId(policy_name)
     policy_title = policy_schema.get('title', policy_name)
 
     if 'id' in policy_schema:
@@ -216,36 +238,36 @@
 
     # For 'object' type items create a new category (folder) and add children.
     if (policy_schema['type'] == 'object'):
-      self._AddCategory(policy_title, policy_id, parent_category)
+      self._AddCategory(policy_title, full_name, parent_category_full_name)
       properties = policy_schema['properties']
       for child_policy_name, child_policy_schema in properties.items():
-        self._AddPolicy(child_policy_name, child_policy_schema, policy_id,
+        self._AddPolicy(child_policy_name, child_policy_schema, full_name,
                         parent_key + '\\' + policy_name)
     else:
       policy_elem = self._AddElement(self._policies_elem, 'policy')
       policy_desc = policy_schema.get('description', None)
-      self._SetAttribute(policy_elem, 'name', policy_name)
+      self._SetAttribute(policy_elem, 'name', full_name)
       self._SetAttribute(policy_elem, 'class', 'Both')
-      self._SetAttribute(policy_elem, 'displayName', policy_title, policy_id)
+      self._SetAttribute(policy_elem, 'displayName', policy_title, full_name)
       if policy_desc:
         self._SetAttribute(policy_elem, 'explainText', policy_desc,
-                           policy_id + '_Explain')
+                           full_name + '_Explain')
       self._SetAttribute(policy_elem, 'presentation',
-                         '$(presentation.%s)' % policy_id)
+                         '$(presentation.%s)' % full_name)
       self._SetAttribute(policy_elem, 'key', parent_key)
 
       parent_category_elem = self._AddElement(policy_elem, 'parentCategory')
-      self._SetAttribute(parent_category_elem, 'ref', parent_category)
+      self._SetAttribute(parent_category_elem, 'ref', parent_category_full_name)
 
       supported_on_elem = self._AddElement(policy_elem, 'supportedOn')
       self._SetAttribute(supported_on_elem, 'ref', 'SUPPORTED_WIN7')
 
-      desc_id = policy_id + '_Part'
+      desc_id = full_name + '_Part'
       presentation_elem = self._AddElement(self._presentation_table_elem,
                                            'presentation')
-      self._SetAttribute(presentation_elem, 'id', policy_id)
+      self._SetAttribute(presentation_elem, 'id', full_name)
       if policy_schema['type'] == 'boolean':
-        self._SetAttribute(policy_elem, 'valueName', policy_id)
+        self._SetAttribute(policy_elem, 'valueName', full_name)
 
         enabled_value_elem = self._AddElement(policy_elem, 'enabledValue')
         decimal_elem = self._AddElement(enabled_value_elem, 'decimal')
@@ -258,7 +280,7 @@
         elements_elem = self._AddElement(policy_elem, 'elements')
         decimal_elem = self._AddElement(elements_elem, 'decimal')
         self._SetAttribute(decimal_elem, 'id', desc_id)
-        self._SetAttribute(decimal_elem, 'valueName', policy_id)
+        self._SetAttribute(decimal_elem, 'valueName', full_name)
 
         textbox_elem = self._AddElement(presentation_elem, 'decimalTextBox')
         self._SetAttribute(textbox_elem, 'refId', desc_id)
@@ -271,7 +293,7 @@
         elements_elem = self._AddElement(policy_elem, 'elements')
         text_elem = self._AddElement(elements_elem, 'text')
         self._SetAttribute(text_elem, 'id', desc_id)
-        self._SetAttribute(text_elem, 'valueName', policy_id)
+        self._SetAttribute(text_elem, 'valueName', full_name)
 
         textbox_elem = self._AddElement(presentation_elem, 'textBox')
         self._SetAttribute(textbox_elem, 'refId', desc_id)
diff --git a/components/safe_browsing/browser/threat_details.cc b/components/safe_browsing/browser/threat_details.cc
index 540c067..76f34cee 100644
--- a/components/safe_browsing/browser/threat_details.cc
+++ b/components/safe_browsing/browser/threat_details.cc
@@ -211,10 +211,11 @@
   // the immediate parent, the siblings, and the children of the target ids.
   // By keeping the parent of the target and all of its children, this covers
   // the target's siblings as well.
-  std::vector<int> ids_to_keep;
-  // Keep track of ids that were kept to avoid duplication. We still need the
-  // vector above for handling the children where it is used like a queue.
-  std::unordered_set<int> kept_ids;
+  std::vector<int> element_ids_to_keep;
+  // Resource IDs are also tracked so that we remember which resources are
+  // attached to elements that we are keeping. This avoids deleting resources
+  // that are shared between kept elements and trimmed elements.
+  std::vector<int> kept_resource_ids;
   for (int target_id : target_ids) {
     const int parent_id = element_id_to_parent_id[target_id];
     if (parent_id == kElementIdNoParent) {
@@ -227,36 +228,55 @@
     // Otherwise, insert the parent ID into the list of ids to keep. This will
     // capture the parent and siblings of the target element, as well as each of
     // their children.
-    if (kept_ids.count(parent_id) == 0) {
-      ids_to_keep.push_back(parent_id);
-      kept_ids.insert(parent_id);
+    if (!base::ContainsValue(element_ids_to_keep, parent_id)) {
+      element_ids_to_keep.push_back(parent_id);
+
+      // Check if this element has a resource. If so, remember to also keep the
+      // resource.
+      const HTMLElement& elem = *elements_by_id[parent_id];
+      if (elem.has_resource_id()) {
+        kept_resource_ids.push_back(elem.resource_id());
+      }
     }
   }
 
-  // Walk through |ids_to_keep| and append the children of each of element to
-  // |ids_to_keep|. This is effectively a breadth-first traversal of the tree.
-  // The list will stop growing when we reach the leaf nodes that have no more
-  // children.
-  for (size_t index = 0; index < ids_to_keep.size(); ++index) {
-    int cur_element_id = ids_to_keep[index];
+  // Walk through |element_ids_to_keep| and append the children of each of
+  // element to |element_ids_to_keep|. This is effectively a breadth-first
+  // traversal of the tree. The list will stop growing when we reach the leaf
+  // nodes that have no more children.
+  for (size_t index = 0; index < element_ids_to_keep.size(); ++index) {
+    int cur_element_id = element_ids_to_keep[index];
     const HTMLElement& element = *(elements_by_id[cur_element_id]);
+    if (element.has_resource_id()) {
+      kept_resource_ids.push_back(element.resource_id());
+    }
     for (int child_id : element.child_ids()) {
-      ids_to_keep.push_back(child_id);
+      element_ids_to_keep.push_back(child_id);
+
+      // Check if each child element has a resource. If so, remember to also
+      // keep the resource.
+      const HTMLElement& child_element = *elements_by_id[child_id];
+      if (child_element.has_resource_id()) {
+        kept_resource_ids.push_back(child_element.resource_id());
+      }
     }
   }
   // Sort the list for easier lookup below.
-  std::sort(ids_to_keep.begin(), ids_to_keep.end());
+  std::sort(element_ids_to_keep.begin(), element_ids_to_keep.end());
 
   // Now we know which elements we want to keep, scan through |elements| and
-  // erase anything that we aren't keeping. If an erased element refers to a
-  // resource then remove it from |resources| as well.
+  // erase anything that we aren't keeping.
   for (auto element_iter = elements->begin();
        element_iter != elements->end();) {
     const HTMLElement& element = *element_iter->second;
 
     // Delete any elements that we do not want to keep.
-    if (!base::ContainsValue(ids_to_keep, element.id())) {
-      if (element.has_resource_id()) {
+    if (!base::ContainsValue(element_ids_to_keep, element.id())) {
+      // If this element has a resource then maybe delete the resouce too. Some
+      // resources may be shared between kept and trimmed elements, and those
+      // ones should not be deleted.
+      if (element.has_resource_id() &&
+          !base::ContainsValue(kept_resource_ids, element.resource_id())) {
         const std::string& resource_url =
             resource_id_to_url[element.resource_id()];
         resources->erase(resource_url);
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index b04d472..51247a5 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -797,6 +797,8 @@
     "driver/sync_api_component_factory_mock.h",
     "driver/sync_client_mock.cc",
     "driver/sync_client_mock.h",
+    "driver/test_sync_service.cc",
+    "driver/test_sync_service.h",
     "engine/fake_sync_engine.cc",
     "engine/fake_sync_engine.h",
     "engine/mock_sync_engine.cc",
diff --git a/components/sync/driver/sync_service_utils_unittest.cc b/components/sync/driver/sync_service_utils_unittest.cc
index ff097df2..e3ed453 100644
--- a/components/sync/driver/sync_service_utils_unittest.cc
+++ b/components/sync/driver/sync_service_utils_unittest.cc
@@ -5,84 +5,29 @@
 #include "components/sync/driver/sync_service_utils.h"
 
 #include <vector>
+
 #include "components/sync/base/model_type.h"
-#include "components/sync/driver/fake_sync_service.h"
 #include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/test_sync_service.h"
+#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
 
-class TestSyncService : public FakeSyncService {
- public:
-  TestSyncService() = default;
-  ~TestSyncService() override = default;
+namespace {
 
-  void SetDisableReasons(int disable_reasons) {
-    disable_reasons_ = disable_reasons;
-  }
-  void SetTransportState(TransportState state) { state_ = state; }
-  void SetLocalSyncEnabled(bool local) { local_sync_enabled_ = local; }
-  void SetPreferredDataTypes(const ModelTypeSet& types) {
-    preferred_data_types_ = types;
-  }
-  void SetActiveDataTypes(const ModelTypeSet& types) {
-    active_data_types_ = types;
-  }
-  void SetCustomPassphraseEnabled(bool enabled) {
-    custom_passphrase_enabled_ = enabled;
-  }
-  void SetSyncCycleComplete(bool complete) { sync_cycle_complete_ = complete; }
+static SyncCycleSnapshot MakeSyncCycleSnapshot() {
+  return SyncCycleSnapshot(
+      ModelNeutralState(), ProgressMarkerMap(), false, 5, 2, 7, false, 0,
+      base::Time::Now(), base::Time::Now(),
+      std::vector<int>(MODEL_TYPE_COUNT, 0),
+      std::vector<int>(MODEL_TYPE_COUNT, 0), sync_pb::SyncEnums::UNKNOWN_ORIGIN,
+      /*short_poll_interval=*/base::TimeDelta::FromMinutes(30),
+      /*long_poll_interval=*/base::TimeDelta::FromMinutes(180),
+      /*has_remaining_local_changes=*/false);
+}
 
-  // SyncService implementation.
-  int GetDisableReasons() const override { return disable_reasons_; }
-  TransportState GetTransportState() const override { return state_; }
-  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 (!IsSyncFeatureActive())
-      return ModelTypeSet();
-    return active_data_types_;
-  }
-  ModelTypeSet GetEncryptedDataTypes() const override {
-    if (!custom_passphrase_enabled_) {
-      // PASSWORDS are always encrypted.
-      return ModelTypeSet(syncer::PASSWORDS);
-    }
-    // Some types can never be encrypted, e.g. DEVICE_INFO and
-    // AUTOFILL_WALLET_DATA, so make sure we don't report them as encrypted.
-    return syncer::Intersection(preferred_data_types_,
-                                syncer::EncryptableUserTypes());
-  }
-  SyncCycleSnapshot GetLastCycleSnapshot() const override {
-    if (sync_cycle_complete_) {
-      return SyncCycleSnapshot(
-          ModelNeutralState(), ProgressMarkerMap(), false, 5, 2, 7, false, 0,
-          base::Time::Now(), base::Time::Now(),
-          std::vector<int>(MODEL_TYPE_COUNT, 0),
-          std::vector<int>(MODEL_TYPE_COUNT, 0),
-          sync_pb::SyncEnums::UNKNOWN_ORIGIN,
-          /*short_poll_interval=*/base::TimeDelta::FromMinutes(30),
-          /*long_poll_interval=*/base::TimeDelta::FromMinutes(180),
-          /*has_remaining_local_changes=*/false);
-    }
-    return SyncCycleSnapshot();
-  }
-  bool IsUsingSecondaryPassphrase() const override {
-    return custom_passphrase_enabled_;
-  }
-
- private:
-  int disable_reasons_ = DISABLE_REASON_PLATFORM_OVERRIDE;
-  TransportState state_ = TransportState::DISABLED;
-  bool sync_cycle_complete_ = false;
-  bool local_sync_enabled_ = false;
-  ModelTypeSet preferred_data_types_;
-  ModelTypeSet active_data_types_;
-  bool custom_passphrase_enabled_ = false;
-};
+}  // namespace
 
 TEST(SyncServiceUtilsTest, UploadToGoogleDisabledIfSyncNotAllowed) {
   TestSyncService service;
@@ -91,6 +36,7 @@
   // data types are enabled.
   service.SetDisableReasons(
       syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY);
+  service.SetTransportState(syncer::SyncService::TransportState::DISABLED);
 
   service.SetPreferredDataTypes(ProtocolTypes());
   service.SetActiveDataTypes(ProtocolTypes());
@@ -127,7 +73,7 @@
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 
   // Only after a sync cycle has been completed is upload actually ACTIVE.
-  service.SetSyncCycleComplete(true);
+  service.SetLastCycleSnapshot(MakeSyncCycleSnapshot());
   EXPECT_EQ(UploadState::ACTIVE,
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 }
@@ -136,7 +82,7 @@
   TestSyncService service;
   service.SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
   service.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
-  service.SetSyncCycleComplete(true);
+  service.SetLastCycleSnapshot(MakeSyncCycleSnapshot());
 
   // Sync is enabled only for a specific model type.
   service.SetPreferredDataTypes(ModelTypeSet(syncer::BOOKMARKS));
@@ -159,7 +105,7 @@
   TestSyncService service;
   service.SetDisableReasons(syncer::SyncService::DISABLE_REASON_NONE);
   service.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
-  service.SetSyncCycleComplete(true);
+  service.SetLastCycleSnapshot(MakeSyncCycleSnapshot());
 
   // Sync is enabled for some model types.
   service.SetPreferredDataTypes(
@@ -182,7 +128,7 @@
   service.SetPreferredDataTypes(ProtocolTypes());
   service.SetActiveDataTypes(ProtocolTypes());
   service.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
-  service.SetSyncCycleComplete(true);
+  service.SetLastCycleSnapshot(MakeSyncCycleSnapshot());
 
   // Sanity check: Upload is active now.
   ASSERT_EQ(UploadState::ACTIVE,
@@ -202,7 +148,7 @@
   service.SetPreferredDataTypes(ProtocolTypes());
   service.SetActiveDataTypes(ProtocolTypes());
   service.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
-  service.SetSyncCycleComplete(true);
+  service.SetLastCycleSnapshot(MakeSyncCycleSnapshot());
 
   // Sanity check: Upload is active now.
   ASSERT_EQ(UploadState::ACTIVE,
@@ -212,7 +158,7 @@
   GoogleServiceAuthError transient_error(
       GoogleServiceAuthError::CONNECTION_FAILED);
   ASSERT_TRUE(transient_error.IsTransientError());
-  service.set_auth_error(transient_error);
+  service.SetAuthError(transient_error);
 
   EXPECT_EQ(UploadState::INITIALIZING,
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
@@ -222,14 +168,14 @@
   GoogleServiceAuthError persistent_error(
       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
   ASSERT_TRUE(persistent_error.IsPersistentError());
-  service.set_auth_error(persistent_error);
+  service.SetAuthError(persistent_error);
 
   EXPECT_EQ(UploadState::NOT_ACTIVE,
             GetUploadToGoogleState(&service, syncer::BOOKMARKS));
 
   // Once the auth error is resolved (e.g. user re-authenticated), uploading is
   // active again.
-  service.set_auth_error(GoogleServiceAuthError(GoogleServiceAuthError::NONE));
+  service.SetAuthError(GoogleServiceAuthError(GoogleServiceAuthError::NONE));
   service.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
 
   EXPECT_EQ(UploadState::ACTIVE,
@@ -242,7 +188,7 @@
   service.SetPreferredDataTypes(ProtocolTypes());
   service.SetActiveDataTypes(ProtocolTypes());
   service.SetTransportState(syncer::SyncService::TransportState::ACTIVE);
-  service.SetSyncCycleComplete(true);
+  service.SetLastCycleSnapshot(MakeSyncCycleSnapshot());
 
   // Sanity check: Upload is ACTIVE, even for data types that are always
   // encrypted implicitly (PASSWORDS).
diff --git a/components/sync/driver/test_sync_service.cc b/components/sync/driver/test_sync_service.cc
new file mode 100644
index 0000000..143df22
--- /dev/null
+++ b/components/sync/driver/test_sync_service.cc
@@ -0,0 +1,223 @@
+// 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/driver/test_sync_service.h"
+
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/sync/driver/sync_token_status.h"
+
+namespace syncer {
+
+TestSyncService::TestSyncService() = default;
+
+TestSyncService::~TestSyncService() = default;
+
+void TestSyncService::SetDisableReasons(int disable_reasons) {
+  disable_reasons_ = disable_reasons;
+}
+
+void TestSyncService::SetTransportState(TransportState transport_state) {
+  transport_state_ = transport_state;
+}
+
+void TestSyncService::SetLocalSyncEnabled(bool local_sync_enabled) {
+  local_sync_enabled_ = local_sync_enabled;
+}
+
+void TestSyncService::SetAuthError(const GoogleServiceAuthError& auth_error) {
+  auth_error_ = auth_error;
+}
+
+void TestSyncService::SetPreferredDataTypes(const ModelTypeSet& types) {
+  preferred_data_types_ = types;
+}
+
+void TestSyncService::SetActiveDataTypes(const ModelTypeSet& types) {
+  active_data_types_ = types;
+}
+
+void TestSyncService::SetCustomPassphraseEnabled(bool enabled) {
+  custom_passphrase_enabled_ = enabled;
+}
+
+void TestSyncService::SetLastCycleSnapshot(const SyncCycleSnapshot& snapshot) {
+  last_cycle_snapshot_ = snapshot;
+}
+
+int TestSyncService::GetDisableReasons() const {
+  return disable_reasons_;
+}
+
+syncer::SyncService::TransportState TestSyncService::GetTransportState() const {
+  return transport_state_;
+}
+
+bool TestSyncService::IsLocalSyncEnabled() const {
+  return local_sync_enabled_;
+}
+
+AccountInfo TestSyncService::GetAuthenticatedAccountInfo() const {
+  return account_info_;
+}
+
+bool TestSyncService::IsAuthenticatedAccountPrimary() const {
+  return true;
+}
+
+const GoogleServiceAuthError& TestSyncService::GetAuthError() const {
+  return auth_error_;
+}
+
+bool TestSyncService::IsFirstSetupComplete() const {
+  return true;
+}
+
+void TestSyncService::SetFirstSetupComplete() {}
+
+std::unique_ptr<SyncSetupInProgressHandle>
+TestSyncService::GetSetupInProgressHandle() {
+  return nullptr;
+}
+
+bool TestSyncService::IsSetupInProgress() const {
+  return false;
+}
+
+ModelTypeSet TestSyncService::GetPreferredDataTypes() const {
+  return preferred_data_types_;
+}
+
+ModelTypeSet TestSyncService::GetActiveDataTypes() const {
+  return active_data_types_;
+}
+
+void TestSyncService::RequestStart() {}
+
+void TestSyncService::RequestStop(SyncService::SyncStopDataFate data_fate) {}
+
+void TestSyncService::OnDataTypeRequestsSyncStartup(ModelType type) {}
+
+void TestSyncService::OnUserChoseDatatypes(bool sync_everything,
+                                           ModelTypeSet chosen_types) {}
+
+void TestSyncService::TriggerRefresh(const ModelTypeSet& types) {}
+
+void TestSyncService::ReenableDatatype(ModelType type) {}
+
+void TestSyncService::ReadyForStartChanged(ModelType type) {}
+
+void TestSyncService::AddObserver(SyncServiceObserver* observer) {}
+
+void TestSyncService::RemoveObserver(SyncServiceObserver* observer) {}
+
+bool TestSyncService::HasObserver(const SyncServiceObserver* observer) const {
+  return false;
+}
+
+bool TestSyncService::IsPassphraseRequiredForDecryption() const {
+  return false;
+}
+
+base::Time TestSyncService::GetExplicitPassphraseTime() const {
+  return base::Time();
+}
+
+bool TestSyncService::IsUsingSecondaryPassphrase() const {
+  return custom_passphrase_enabled_;
+}
+
+void TestSyncService::EnableEncryptEverything() {}
+
+bool TestSyncService::IsEncryptEverythingEnabled() const {
+  return false;
+}
+
+void TestSyncService::SetEncryptionPassphrase(const std::string& passphrase) {}
+
+bool TestSyncService::SetDecryptionPassphrase(const std::string& passphrase) {
+  return false;
+}
+
+bool TestSyncService::IsCryptographerReady(const BaseTransaction* trans) const {
+  return false;
+}
+
+sync_sessions::OpenTabsUIDelegate* TestSyncService::GetOpenTabsUIDelegate() {
+  return nullptr;
+}
+
+UserShare* TestSyncService::GetUserShare() const {
+  return nullptr;
+}
+
+syncer::SyncTokenStatus TestSyncService::GetSyncTokenStatus() const {
+  return syncer::SyncTokenStatus();
+}
+
+bool TestSyncService::QueryDetailedSyncStatus(SyncStatus* result) const {
+  return false;
+}
+
+base::Time TestSyncService::GetLastSyncedTime() const {
+  return base::Time();
+}
+
+SyncCycleSnapshot TestSyncService::GetLastCycleSnapshot() const {
+  return last_cycle_snapshot_;
+}
+
+std::unique_ptr<base::Value> TestSyncService::GetTypeStatusMap() {
+  return std::make_unique<base::ListValue>();
+}
+
+const GURL& TestSyncService::sync_service_url() const {
+  return sync_service_url_;
+}
+
+std::string TestSyncService::unrecoverable_error_message() const {
+  return std::string();
+}
+
+base::Location TestSyncService::unrecoverable_error_location() const {
+  return base::Location();
+}
+
+void TestSyncService::AddProtocolEventObserver(
+    ProtocolEventObserver* observer) {}
+
+void TestSyncService::RemoveProtocolEventObserver(
+    ProtocolEventObserver* observer) {}
+
+void TestSyncService::AddTypeDebugInfoObserver(
+    TypeDebugInfoObserver* observer) {}
+
+void TestSyncService::RemoveTypeDebugInfoObserver(
+    TypeDebugInfoObserver* observer) {}
+
+base::WeakPtr<JsController> TestSyncService::GetJsController() {
+  return base::WeakPtr<JsController>();
+}
+
+void TestSyncService::GetAllNodes(
+    const base::Callback<void(std::unique_ptr<base::ListValue>)>& callback) {}
+
+bool TestSyncService::IsPassphraseRequired() const {
+  return false;
+}
+
+ModelTypeSet TestSyncService::GetEncryptedDataTypes() const {
+  if (!custom_passphrase_enabled_) {
+    // PASSWORDS are always encrypted.
+    return ModelTypeSet(syncer::PASSWORDS);
+  }
+  // Some types can never be encrypted, e.g. DEVICE_INFO and
+  // AUTOFILL_WALLET_DATA, so make sure we don't report them as encrypted.
+  return syncer::Intersection(GetPreferredDataTypes(),
+                              syncer::EncryptableUserTypes());
+}
+
+void TestSyncService::Shutdown() {}
+
+}  // namespace syncer
diff --git a/components/sync/driver/test_sync_service.h b/components/sync/driver/test_sync_service.h
new file mode 100644
index 0000000..4d62e082
--- /dev/null
+++ b/components/sync/driver/test_sync_service.h
@@ -0,0 +1,121 @@
+// 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_DRIVER_TEST_SYNC_SERVICE_H_
+#define COMPONENTS_SYNC_DRIVER_TEST_SYNC_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/signin/core/browser/account_info.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "url/gurl.h"
+
+namespace syncer {
+
+class TestSyncService : public SyncService {
+ public:
+  TestSyncService();
+  ~TestSyncService() override;
+
+  void SetDisableReasons(int disable_reasons);
+  void SetTransportState(TransportState transport_state);
+  void SetLocalSyncEnabled(bool local_sync_enabled);
+  void SetAuthError(const GoogleServiceAuthError& auth_error);
+  void SetPreferredDataTypes(const ModelTypeSet& types);
+  void SetActiveDataTypes(const ModelTypeSet& types);
+  void SetCustomPassphraseEnabled(bool enabled);
+  void SetLastCycleSnapshot(const SyncCycleSnapshot& snapshot);
+
+  // SyncService implementation.
+  int GetDisableReasons() const override;
+  TransportState GetTransportState() const override;
+  bool IsLocalSyncEnabled() const override;
+  AccountInfo GetAuthenticatedAccountInfo() const override;
+  bool IsAuthenticatedAccountPrimary() const override;
+  const GoogleServiceAuthError& GetAuthError() const override;
+
+  bool IsFirstSetupComplete() const override;
+  void SetFirstSetupComplete() override;
+
+  std::unique_ptr<SyncSetupInProgressHandle> GetSetupInProgressHandle()
+      override;
+  bool IsSetupInProgress() const override;
+
+  ModelTypeSet GetPreferredDataTypes() const override;
+  ModelTypeSet GetActiveDataTypes() const override;
+
+  void RequestStart() override;
+  void RequestStop(SyncStopDataFate data_fate) override;
+  void OnDataTypeRequestsSyncStartup(ModelType type) override;
+  void OnUserChoseDatatypes(bool sync_everything,
+                            ModelTypeSet chosen_types) override;
+  void TriggerRefresh(const ModelTypeSet& types) override;
+  void ReenableDatatype(ModelType type) override;
+  void ReadyForStartChanged(syncer::ModelType type) override;
+
+  void AddObserver(SyncServiceObserver* observer) override;
+  void RemoveObserver(SyncServiceObserver* observer) override;
+  bool HasObserver(const SyncServiceObserver* observer) const override;
+
+  bool IsPassphraseRequiredForDecryption() const override;
+  base::Time GetExplicitPassphraseTime() const override;
+  bool IsUsingSecondaryPassphrase() const override;
+  void EnableEncryptEverything() override;
+  bool IsEncryptEverythingEnabled() const override;
+  void SetEncryptionPassphrase(const std::string& passphrase) override;
+  bool SetDecryptionPassphrase(const std::string& passphrase) override;
+  bool IsCryptographerReady(const BaseTransaction* trans) const override;
+
+  sync_sessions::OpenTabsUIDelegate* GetOpenTabsUIDelegate() override;
+  UserShare* GetUserShare() const override;
+
+  SyncTokenStatus GetSyncTokenStatus() const override;
+  bool QueryDetailedSyncStatus(SyncStatus* result) const override;
+  base::Time GetLastSyncedTime() const override;
+  SyncCycleSnapshot GetLastCycleSnapshot() const override;
+  std::unique_ptr<base::Value> GetTypeStatusMap() override;
+  const GURL& sync_service_url() const override;
+  std::string unrecoverable_error_message() const override;
+  base::Location unrecoverable_error_location() const override;
+  void AddProtocolEventObserver(ProtocolEventObserver* observer) override;
+  void RemoveProtocolEventObserver(ProtocolEventObserver* observer) override;
+  void AddTypeDebugInfoObserver(TypeDebugInfoObserver* observer) override;
+  void RemoveTypeDebugInfoObserver(TypeDebugInfoObserver* observer) override;
+  base::WeakPtr<JsController> GetJsController() override;
+  void GetAllNodes(const base::Callback<void(std::unique_ptr<base::ListValue>)>&
+                       callback) override;
+
+  // DataTypeEncryptionHandler implementation.
+  bool IsPassphraseRequired() const override;
+  ModelTypeSet GetEncryptedDataTypes() const override;
+
+  // KeyedService implementation.
+  void Shutdown() override;
+
+ private:
+  int disable_reasons_ = DISABLE_REASON_NONE;
+  TransportState transport_state_ = TransportState::ACTIVE;
+  bool local_sync_enabled_ = false;
+  AccountInfo account_info_;
+  GoogleServiceAuthError auth_error_;
+
+  ModelTypeSet preferred_data_types_;
+  ModelTypeSet active_data_types_;
+
+  bool custom_passphrase_enabled_ = false;
+
+  SyncCycleSnapshot last_cycle_snapshot_;
+
+  GURL sync_service_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSyncService);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_DRIVER_TEST_SYNC_SERVICE_H_
diff --git a/components/sync/tools/sync_listen_notifications.cc b/components/sync/tools/sync_listen_notifications.cc
index dbc61a99..9900dfc 100644
--- a/components/sync/tools/sync_listen_notifications.cc
+++ b/components/sync/tools/sync_listen_notifications.cc
@@ -187,7 +187,7 @@
       null_invalidation_state_tracker.GetSavedInvalidations(),
       null_invalidation_state_tracker.GetBootstrapData(),
       &null_invalidation_state_tracker, kClientInfo,
-      notifier_options.request_context_getter));
+      notifier_options.request_context_getter->GetNetworkTaskRunner()));
 
   NotificationPrinter notification_printer;
 
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 4da3159..6fa3d5a 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -257,6 +257,8 @@
   skia_output_surface_sequence_id_ =
       scheduler_->CreateSequence(gpu::SchedulingPriority::kHigh);
 
+  GrContext* vulkan_gr_context =
+      is_using_vulkan() ? vulkan_context_provider()->GetGrContext() : nullptr;
   // Defer creation of the render thread. This is to prevent it from handling
   // IPC messages before the sandbox has been enabled and all other necessary
   // initialization has succeeded.
@@ -264,7 +266,7 @@
       gpu_preferences_, this, watchdog_thread_.get(), main_runner_, io_runner_,
       scheduler_.get(), sync_point_manager_, gpu_memory_buffer_factory_.get(),
       gpu_feature_info_, std::move(activity_flags),
-      std::move(default_offscreen_surface), vulkan_context_provider());
+      std::move(default_offscreen_surface), vulkan_gr_context);
 
   media_gpu_channel_manager_.reset(
       new media::MediaGpuChannelManager(gpu_channel_manager_.get()));
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_mac.mm b/content/browser/accessibility/browser_accessibility_state_impl_mac.mm
index 262083f..229a9f0 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_state_impl_mac.mm
@@ -16,6 +16,11 @@
 
 @end
 
+// Only available since 10.12.
+@interface NSWorkspace (AvailableSinceSierra)
+@property(readonly) BOOL accessibilityDisplayShouldReduceMotion;
+@end
+
 namespace content {
 
 void BrowserAccessibilityStateImpl::PlatformInitialize() {}
@@ -25,22 +30,26 @@
   NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
 
   SEL sel = @selector(accessibilityDisplayShouldIncreaseContrast);
-  if (![workspace respondsToSelector:sel])
-    return;
+  if ([workspace respondsToSelector:sel]) {
+    UMA_HISTOGRAM_BOOLEAN(
+        "Accessibility.Mac.DifferentiateWithoutColor",
+        workspace.accessibilityDisplayShouldDifferentiateWithoutColor);
+    UMA_HISTOGRAM_BOOLEAN("Accessibility.Mac.IncreaseContrast",
+                          workspace.accessibilityDisplayShouldIncreaseContrast);
+    UMA_HISTOGRAM_BOOLEAN(
+        "Accessibility.Mac.ReduceTransparency",
+        workspace.accessibilityDisplayShouldReduceTransparency);
 
-  UMA_HISTOGRAM_BOOLEAN(
-      "Accessibility.Mac.DifferentiateWithoutColor",
-      workspace.accessibilityDisplayShouldDifferentiateWithoutColor);
-  UMA_HISTOGRAM_BOOLEAN(
-      "Accessibility.Mac.IncreaseContrast",
-      workspace.accessibilityDisplayShouldIncreaseContrast);
-  UMA_HISTOGRAM_BOOLEAN(
-      "Accessibility.Mac.ReduceTransparency",
-      workspace.accessibilityDisplayShouldReduceTransparency);
+    UMA_HISTOGRAM_BOOLEAN(
+        "Accessibility.Mac.FullKeyboardAccessEnabled",
+        static_cast<NSApplication*>(NSApp).fullKeyboardAccessEnabled);
+  }
 
-  UMA_HISTOGRAM_BOOLEAN(
-      "Accessibility.Mac.FullKeyboardAccessEnabled",
-      static_cast<NSApplication*>(NSApp).fullKeyboardAccessEnabled);
+  sel = @selector(accessibilityDisplayShouldReduceMotion);
+  if ([workspace respondsToSelector:sel]) {
+    UMA_HISTOGRAM_BOOLEAN("Accessibility.Mac.ReduceMotion",
+                          workspace.accessibilityDisplayShouldReduceMotion);
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_state_impl_win.cc b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
index 16314d71..625be876 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl_win.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl_win.cc
@@ -96,6 +96,14 @@
   UMA_HISTOGRAM_BOOLEAN("Accessibility.WinStickyKeys",
                         0 != (sticky_keys.dwFlags & SKF_STICKYKEYSON));
 
+  // We only measure systems where SPI_GETCLIENTAREAANIMATION exists.
+  BOOL win_anim_enabled = TRUE;
+  if (SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &win_anim_enabled,
+                           0)) {
+    UMA_HISTOGRAM_BOOLEAN("Accessibility.Win.AnimationsEnabled",
+                          win_anim_enabled);
+  }
+
   // Get the file paths of all DLLs loaded.
   HANDLE process = GetCurrentProcess();
   HMODULE* modules = NULL;
diff --git a/content/browser/android/synchronous_compositor_sync_call_bridge.cc b/content/browser/android/synchronous_compositor_sync_call_bridge.cc
index 7b94dbe5..d56f3752 100644
--- a/content/browser/android/synchronous_compositor_sync_call_bridge.cc
+++ b/content/browser/android/synchronous_compositor_sync_call_bridge.cc
@@ -24,7 +24,6 @@
 
 SynchronousCompositorSyncCallBridge::~SynchronousCompositorSyncCallBridge() {
   DCHECK(frame_futures_.empty());
-  DCHECK(!window_android_in_vsync_);
 }
 
 void SynchronousCompositorSyncCallBridge::RemoteReady() {
@@ -88,12 +87,7 @@
   if (remote_state_ != RemoteState::READY)
     return false;
   CHECK(!begin_frame_response_valid_);
-  if (window_android_in_vsync_) {
-    DCHECK_EQ(window_android_in_vsync_, window_android);
-    return true;
-  }
-  window_android_in_vsync_ = window_android;
-  window_android_in_vsync_->AddBeginFrameCompletionCallback(base::BindOnce(
+  window_android->AddBeginFrameCompletionCallback(base::BindOnce(
       &SynchronousCompositorSyncCallBridge::BeginFrameCompleteOnUIThread,
       this));
   return true;
@@ -131,8 +125,6 @@
 
 void SynchronousCompositorSyncCallBridge::BeginFrameCompleteOnUIThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(window_android_in_vsync_);
-  window_android_in_vsync_ = nullptr;
 
   bool update_state = false;
   SyncCompositorCommonRendererParams render_params;
diff --git a/content/browser/android/synchronous_compositor_sync_call_bridge.h b/content/browser/android/synchronous_compositor_sync_call_bridge.h
index 5cf9236..fbf05bc 100644
--- a/content/browser/android/synchronous_compositor_sync_call_bridge.h
+++ b/content/browser/android/synchronous_compositor_sync_call_bridge.h
@@ -127,7 +127,6 @@
   const int routing_id_;
 
   // UI thread only.
-  ui::WindowAndroid* window_android_in_vsync_ = nullptr;
   SynchronousCompositorHost* host_;
 
   // Shared variables between the IO thread and UI thread.
diff --git a/content/browser/frame_host/navigation_controller_android.cc b/content/browser/frame_host/navigation_controller_android.cc
index 235f59a..e4b7eed 100644
--- a/content/browser/frame_host/navigation_controller_android.cc
+++ b/content/browser/frame_host/navigation_controller_android.cc
@@ -300,16 +300,6 @@
   }
 }
 
-ScopedJavaLocalRef<jstring>
-NavigationControllerAndroid::GetOriginalUrlForVisibleNavigationEntry(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
-  NavigationEntry* entry = navigation_controller_->GetVisibleEntry();
-  if (entry == NULL)
-    return ScopedJavaLocalRef<jstring>(env, NULL);
-  return ConvertUTF8ToJavaString(env, entry->GetOriginalRequestURL().spec());
-}
-
 void NavigationControllerAndroid::ClearSslPreferences(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj) {
@@ -389,42 +379,6 @@
   return navigation_controller_->RemoveEntryAtIndex(index);
 }
 
-jboolean NavigationControllerAndroid::CanCopyStateOver(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
-  return navigation_controller_->GetEntryCount() == 0 &&
-      !navigation_controller_->GetPendingEntry();
-}
-
-jboolean NavigationControllerAndroid::CanPruneAllButLastCommitted(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj) {
-  return navigation_controller_->CanPruneAllButLastCommitted();
-}
-
-void NavigationControllerAndroid::CopyStateFrom(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    jlong source_navigation_controller_android,
-    jboolean needs_reload) {
-  navigation_controller_->CopyStateFrom(
-      *(reinterpret_cast<NavigationControllerAndroid*>(
-            source_navigation_controller_android)
-            ->navigation_controller_),
-      needs_reload);
-}
-
-void NavigationControllerAndroid::CopyStateFromAndPrune(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    jlong source_navigation_controller_android,
-    jboolean replace_entry) {
-  navigation_controller_->CopyStateFromAndPrune(
-      reinterpret_cast<NavigationControllerAndroid*>(
-          source_navigation_controller_android)->navigation_controller_,
-      replace_entry);
-}
-
 ScopedJavaLocalRef<jstring> NavigationControllerAndroid::GetEntryExtraData(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
diff --git a/content/browser/frame_host/navigation_controller_android.h b/content/browser/frame_host/navigation_controller_android.h
index 0a3afdf..b9478af 100644
--- a/content/browser/frame_host/navigation_controller_android.h
+++ b/content/browser/frame_host/navigation_controller_android.h
@@ -108,10 +108,6 @@
       const base::android::JavaParamRef<jobject>& history,
       jboolean is_forward,
       jint max_entries);
-  base::android::ScopedJavaLocalRef<jstring>
-  GetOriginalUrlForVisibleNavigationEntry(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
   void ClearHistory(JNIEnv* env,
                     const base::android::JavaParamRef<jobject>& obj);
   int GetLastCommittedEntryIndex(
@@ -120,19 +116,6 @@
   jboolean RemoveEntryAtIndex(JNIEnv* env,
                               const base::android::JavaParamRef<jobject>& obj,
                               jint index);
-  jboolean CanCopyStateOver(JNIEnv* env,
-                            const base::android::JavaParamRef<jobject>& obj);
-  jboolean CanPruneAllButLastCommitted(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& obj);
-  void CopyStateFrom(JNIEnv* env,
-                     const base::android::JavaParamRef<jobject>& obj,
-                     jlong source_native_navigation_controller_android,
-                     jboolean needs_reload);
-  void CopyStateFromAndPrune(JNIEnv* env,
-                             const base::android::JavaParamRef<jobject>& obj,
-                             jlong source_native_navigation_controller_android,
-                             jboolean replace_entry);
   base::android::ScopedJavaLocalRef<jstring> GetEntryExtraData(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 2e6d707..369f108c8 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -58,6 +58,7 @@
 #include "content/browser/loader/resource_dispatcher_host_impl.h"
 #include "content/browser/renderer_host/input/input_router.h"
 #include "content/browser/renderer_host/input/synthetic_gesture_target.h"
+#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
 #include "content/browser/renderer_host/input/synthetic_touchscreen_pinch_gesture.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
@@ -66,7 +67,6 @@
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/common/frame_messages.h"
-#include "content/common/input/synthetic_pinch_gesture_params.h"
 #include "content/common/input_messages.h"
 #include "content/common/renderer.mojom.h"
 #include "content/common/view_messages.h"
@@ -13765,4 +13765,103 @@
                                         std::make_tuple("a", "b", "b"),
                                         std::make_tuple("a", "b", "c")));
 
+class ScrollingIntegrationTest : public SitePerProcessBrowserTest {
+ public:
+  ScrollingIntegrationTest() = default;
+  ~ScrollingIntegrationTest() override = default;
+
+  void DoScroll(const gfx::Point& point,
+                const gfx::Vector2d& distance,
+                SyntheticGestureParams::GestureSourceType source) {
+    SyntheticSmoothScrollGestureParams params;
+    params.gesture_source_type = source;
+    params.anchor = gfx::PointF(point);
+    params.distances.push_back(-distance);
+    params.precise_scrolling_deltas = true;
+
+    auto gesture = std::make_unique<SyntheticSmoothScrollGesture>(params);
+
+    // Runs until we get the SyntheticGestureCompleted callback
+    base::RunLoop run_loop;
+    GetRenderWidgetHostImpl()->QueueSyntheticGesture(
+        std::move(gesture),
+        base::BindLambdaForTesting([&](SyntheticGesture::Result result) {
+          EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+  }
+
+  double GetScrollTop() {
+    FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                              ->GetFrameTree()
+                              ->root();
+    return EvalJs(root, "window.scrollY").ExtractDouble();
+  }
+
+  void WaitForVerticalScroll() {
+    RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
+    gfx::Vector2dF default_scroll_offset;
+    while (frame_observer.LastRenderFrameMetadata()
+               .root_scroll_offset.value_or(default_scroll_offset)
+               .y() <= 0) {
+      frame_observer.WaitForMetadataChange();
+    }
+  }
+
+  RenderWidgetHostImpl* GetRenderWidgetHostImpl() {
+    FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                              ->GetFrameTree()
+                              ->root();
+    return root->current_frame_host()->GetRenderWidgetHost();
+  }
+};
+
+// Tests basic scrolling after navigating to a new origin works. Guards against
+// bugs like https://crbug.com/899234 which are caused by invalid
+// initialization due to the cross-origin provisional frame swap.
+IN_PROC_BROWSER_TEST_F(ScrollingIntegrationTest,
+                       ScrollAfterCrossOriginNavigation) {
+  // Navigate to the a.com domain first.
+  GURL url_domain_a(
+      embedded_test_server()->GetURL("a.com", "/simple_page.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), url_domain_a));
+
+  // Now navigate to baz.com, this should cause a cross-origin navigation which
+  // will load into a provisional frame and then swap in as a local main frame.
+  // This test ensures all the correct initialization takes place in the
+  // renderer so that a basic scrolling smoke test works.
+  GURL url_domain_b(embedded_test_server()->GetURL(
+      "baz.com", "/scrollable_page_with_iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), url_domain_b));
+  ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
+
+  {
+    // TODO(bokan): We currently don't have a good way to know when the
+    // compositor's scrolling layers are ready after changes on the main thread.
+    // We wait a timeout but that's really a hack. Fixing is tracked in
+    // https://crbug.com/897520
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(),
+        base::TimeDelta::FromMilliseconds(3000));
+    run_loop.Run();
+  }
+
+  SyntheticGestureParams::GestureSourceType source;
+
+// TODO(bokan): Mac doesn't support touch events and for an unknown reason,
+// Android doesn't like mouse wheel here. https://crbug.com/897520.
+#if defined(OS_ANDROID)
+  source = SyntheticGestureParams::TOUCH_INPUT;
+#else
+  source = SyntheticGestureParams::TOUCHPAD_INPUT;
+#endif
+
+  // Perform the scroll (below the iframe), ensure it's correctly processed.
+  DoScroll(gfx::Point(100, 110), gfx::Vector2d(0, 500), source);
+  WaitForVerticalScroll();
+  EXPECT_GT(GetScrollTop(), 0);
+}
+
 }  // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index f82e81a..79ae6f5 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/strings/string_util.h"
+#include "base/task/task_features.h"
 #include "build/build_config.h"
 #include "content/common/content_switches_internal.h"
 #include "content/public/common/content_features.h"
@@ -406,6 +407,9 @@
   WebRuntimeFeatures::EnableScheduledScriptStreaming(
       base::FeatureList::IsEnabled(features::kScheduledScriptStreaming));
 
+  WebRuntimeFeatures::EnableMergeBlockingNonBlockingPools(
+      base::FeatureList::IsEnabled(base::kMergeBlockingNonBlockingPools));
+
   if (base::FeatureList::IsEnabled(features::kLazyFrameLoading))
     WebRuntimeFeatures::EnableLazyFrameLoading(true);
   if (base::FeatureList::IsEnabled(features::kLazyFrameVisibleLoadTimeMetrics))
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index ab36d92e..f4b8f5aa 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -450,6 +450,7 @@
     "//third_party/android_support_test_runner:runner_java",
     "//third_party/blink/public:android_mojo_bindings_java",
     "//third_party/blink/public:blink_headers_java",
+    "//third_party/hamcrest:hamcrest_java",
     "//third_party/jsr-305:jsr_305_javalib",
     "//third_party/junit",
     "//ui/android:ui_java",
diff --git a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
index 9eae857..700dcf1c 100644
--- a/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/framehost/NavigationControllerImpl.java
@@ -154,7 +154,6 @@
     }
 
     @Override
-    @VisibleForTesting
     public void clearHistory() {
         if (mNativeNavigationControllerAndroid != 0) {
             nativeClearHistory(mNativeNavigationControllerAndroid);
@@ -181,12 +180,6 @@
     }
 
     @Override
-    public String getOriginalUrlForVisibleNavigationEntry() {
-        if (mNativeNavigationControllerAndroid == 0) return null;
-        return nativeGetOriginalUrlForVisibleNavigationEntry(mNativeNavigationControllerAndroid);
-    }
-
-    @Override
     public void clearSslPreferences() {
         if (mNativeNavigationControllerAndroid != 0) {
             nativeClearSslPreferences(mNativeNavigationControllerAndroid);
@@ -242,38 +235,6 @@
     }
 
     @Override
-    public boolean canCopyStateOver() {
-        return mNativeNavigationControllerAndroid != 0
-                && nativeCanCopyStateOver(mNativeNavigationControllerAndroid);
-    }
-
-    @Override
-    public boolean canPruneAllButLastCommitted() {
-        return mNativeNavigationControllerAndroid != 0
-                && nativeCanPruneAllButLastCommitted(mNativeNavigationControllerAndroid);
-    }
-
-    @Override
-    public void copyStateFrom(NavigationController source, boolean needsReload) {
-        if (mNativeNavigationControllerAndroid == 0) return;
-        NavigationControllerImpl sourceImpl = (NavigationControllerImpl) source;
-        if (sourceImpl.mNativeNavigationControllerAndroid == 0) return;
-        nativeCopyStateFrom(mNativeNavigationControllerAndroid,
-                sourceImpl.mNativeNavigationControllerAndroid, needsReload);
-    }
-
-    @Override
-    public void copyStateFromAndPrune(NavigationController source, boolean replaceEntry) {
-        if (mNativeNavigationControllerAndroid == 0) return;
-        NavigationControllerImpl sourceImpl = (NavigationControllerImpl) source;
-        if (sourceImpl.mNativeNavigationControllerAndroid == 0) return;
-        nativeCopyStateFromAndPrune(
-                mNativeNavigationControllerAndroid,
-                sourceImpl.mNativeNavigationControllerAndroid,
-                replaceEntry);
-    }
-
-    @Override
     public String getEntryExtraData(int index, String key) {
         if (mNativeNavigationControllerAndroid == 0) return null;
         return nativeGetEntryExtraData(mNativeNavigationControllerAndroid, index, key);
@@ -327,8 +288,6 @@
             Object history);
     private native void nativeGetDirectedNavigationHistory(long nativeNavigationControllerAndroid,
             NavigationHistory history, boolean isForward, int itemLimit);
-    private native String nativeGetOriginalUrlForVisibleNavigationEntry(
-            long nativeNavigationControllerAndroid);
     private native void nativeClearSslPreferences(long nativeNavigationControllerAndroid);
     private native boolean nativeGetUseDesktopUserAgent(long nativeNavigationControllerAndroid);
     private native void nativeSetUseDesktopUserAgent(long nativeNavigationControllerAndroid,
@@ -339,13 +298,6 @@
     private native int nativeGetLastCommittedEntryIndex(long nativeNavigationControllerAndroid);
     private native boolean nativeRemoveEntryAtIndex(long nativeNavigationControllerAndroid,
             int index);
-    private native boolean nativeCanCopyStateOver(long nativeNavigationControllerAndroid);
-    private native boolean nativeCanPruneAllButLastCommitted(
-            long nativeNavigationControllerAndroid);
-    private native void nativeCopyStateFrom(long nativeNavigationControllerAndroid,
-            long sourceNavigationControllerAndroid, boolean needsReload);
-    private native void nativeCopyStateFromAndPrune(long nativeNavigationControllerAndroid,
-            long sourceNavigationControllerAndroid, boolean replaceEntry);
     private native String nativeGetEntryExtraData(
             long nativeNavigationControllerAndroid, int index, String key);
     private native void nativeSetEntryExtraData(
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java
index 11c285c..b81aad1 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationController.java
@@ -102,7 +102,6 @@
      * Clears NavigationController's page history in both backwards and
      * forwards directions.
      */
-    @VisibleForTesting
     public void clearHistory();
 
     /**
@@ -122,13 +121,6 @@
     public NavigationHistory getDirectedNavigationHistory(boolean isForward, int itemLimit);
 
     /**
-     * Get Original URL for current Navigation entry of NavigationController.
-     * @return The original request URL for the current navigation entry, or null if there is no
-     *         current entry.
-     */
-    public String getOriginalUrlForVisibleNavigationEntry();
-
-    /**
      * Clears SSL preferences for this NavigationController.
      */
     public void clearSslPreferences();
@@ -173,32 +165,6 @@
     public boolean removeEntryAtIndex(int index);
 
     /**
-     * @return Whether it is safe to call CopyStateFrom (i.e. the navigation state is empty).
-     */
-    public boolean canCopyStateOver();
-
-    /**
-     * @return Whether it is safe to call CopyStateFromAndPrune.
-     */
-    public boolean canPruneAllButLastCommitted();
-
-    /**
-     * Copies the navigation state from the given controller to this one. This one should be empty.
-     * @param source A source of the navigation state
-     * @param needsReload Indicates whether a reload needs to happen when activated.
-     */
-    public void copyStateFrom(NavigationController source, boolean needsReload);
-
-    /**
-     * A variant of CopyStateFrom. Removes all entries from this except the last committed entry,
-     * and inserts all entries from |source| before and including its last committed entry.
-     * See navigation_controller.h for more detailed description.
-     * @param source A source of the navigation state
-     * @param replaceEntry Whether to replace the current entry in source
-     */
-    public void copyStateFromAndPrune(NavigationController source, boolean replaceEntry);
-
-    /**
      * Gets extra data on the {@link NavigationEntry} at {@code index}.
      * @param index The index of the navigation entry.
      * @param key The data key.
diff --git a/content/public/test/content_mock_cert_verifier.cc b/content/public/test/content_mock_cert_verifier.cc
index 6bede2b..5eb4358 100644
--- a/content/public/test/content_mock_cert_verifier.cc
+++ b/content/public/test/content_mock_cert_verifier.cc
@@ -64,11 +64,13 @@
 
 void ContentMockCertVerifier::CertVerifier::
     EnsureNetworkServiceTestInitialized() {
-  if (network_service_test_)
-    return;
-
-  ServiceManagerConnection::GetForProcess()->GetConnector()->BindInterface(
-      mojom::kNetworkServiceName, &network_service_test_);
+  if (!network_service_test_) {
+    ServiceManagerConnection::GetForProcess()->GetConnector()->BindInterface(
+        mojom::kNetworkServiceName, &network_service_test_);
+  }
+  // TODO(crbug.com/901026): Make sure the network process is started to avoid a
+  // deadlock on Android.
+  network_service_test_.FlushForTesting();
 }
 
 ContentMockCertVerifier::ContentMockCertVerifier()
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.cc b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
index 410baa4..1b43545 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.cc
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.cc
@@ -182,6 +182,13 @@
   return streams_;
 }
 
+std::vector<std::string> FakeRtpReceiver::stream_ids() const {
+  std::vector<std::string> stream_ids;
+  for (const auto& stream : streams_)
+    stream_ids.push_back(stream->id());
+  return stream_ids;
+}
+
 cricket::MediaType FakeRtpReceiver::media_type() const {
   NOTIMPLEMENTED();
   return cricket::MEDIA_TYPE_AUDIO;
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.h b/content/renderer/media/webrtc/mock_peer_connection_impl.h
index 3ae1fba..15a3fbc 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.h
+++ b/content/renderer/media/webrtc/mock_peer_connection_impl.h
@@ -56,6 +56,7 @@
   rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track() const override;
   std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> streams()
       const override;
+  std::vector<std::string> stream_ids() const override;
   cricket::MediaType media_type() const override;
   std::string id() const override;
   webrtc::RtpParameters GetParameters() const override;
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
index c33a355..80530a8 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
@@ -14,9 +14,11 @@
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/child/child_process.h"
+#include "content/renderer/media/stream/media_stream_audio_source.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map.h"
+#include "content/renderer/media/webrtc/webrtc_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/web_heap.h"
@@ -59,47 +61,303 @@
   WebRtcSetDescriptionObserver::States states_;
 };
 
-// TODO(hbos): This only tests WebRtcSetRemoteDescriptionObserverHandler,
-// parameterize the test to make it also test
-// WebRtcSetLocalDescriptionObserverHandler and with "surface_receivers_only" as
-// both true and false. https://crbug.com/865006
-class WebRtcSetRemoteDescriptionObserverHandlerTest : public ::testing::Test {
+enum class ObserverHandlerType {
+  kLocal,
+  kRemote,
+};
+
+// Because webrtc observer interfaces are different classes,
+// WebRtcSetLocalDescriptionObserverHandler and
+// WebRtcSetRemoteDescriptionObserverHandler have different class hierarchies
+// despite implementing the same behavior. This wrapper hides these differences.
+class ObserverHandlerWrapper {
  public:
+  ObserverHandlerWrapper(
+      ObserverHandlerType handler_type,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
+      scoped_refptr<webrtc::PeerConnectionInterface> pc,
+      scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map,
+      scoped_refptr<WebRtcSetDescriptionObserver> observer,
+      bool surface_receivers_only)
+      : signaling_task_runner_(std::move(signaling_task_runner)),
+        handler_type_(handler_type),
+        local_handler_(nullptr),
+        remote_handler_(nullptr) {
+    switch (handler_type_) {
+      case ObserverHandlerType::kLocal:
+        local_handler_ = WebRtcSetLocalDescriptionObserverHandler::Create(
+            std::move(main_task_runner), signaling_task_runner_, std::move(pc),
+            std::move(track_adapter_map), std::move(observer),
+            surface_receivers_only);
+        break;
+      case ObserverHandlerType::kRemote:
+        remote_handler_ = WebRtcSetRemoteDescriptionObserverHandler::Create(
+            std::move(main_task_runner), signaling_task_runner_, std::move(pc),
+            std::move(track_adapter_map), std::move(observer),
+            surface_receivers_only);
+        break;
+    }
+  }
+
+  void InvokeOnComplete(webrtc::RTCError error) {
+    switch (handler_type_) {
+      case ObserverHandlerType::kLocal:
+        if (error.ok())
+          InvokeLocalHandlerOnSuccess();
+        else
+          InvokeLocalHandlerOnFailure(std::move(error));
+        break;
+      case ObserverHandlerType::kRemote:
+        InvokeRemoteHandlerOnComplete(std::move(error));
+        break;
+    }
+  }
+
+ private:
+  void InvokeLocalHandlerOnSuccess() {
+    base::RunLoop run_loop;
+    signaling_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ObserverHandlerWrapper::
+                           InvokeLocalHandlerOnSuccessOnSignalingThread,
+                       base::Unretained(this), base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+  void InvokeLocalHandlerOnSuccessOnSignalingThread(base::RunLoop* run_loop) {
+    local_handler_->OnSuccess();
+    run_loop->Quit();
+  }
+
+  void InvokeLocalHandlerOnFailure(webrtc::RTCError error) {
+    base::RunLoop run_loop;
+    signaling_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ObserverHandlerWrapper::
+                           InvokeLocalHandlerOnFailureOnSignalingThread,
+                       base::Unretained(this), std::move(error),
+                       base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+  void InvokeLocalHandlerOnFailureOnSignalingThread(webrtc::RTCError error,
+                                                    base::RunLoop* run_loop) {
+    local_handler_->OnFailure(std::move(error));
+    run_loop->Quit();
+  }
+
+  void InvokeRemoteHandlerOnComplete(webrtc::RTCError error) {
+    base::RunLoop run_loop;
+    signaling_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ObserverHandlerWrapper::
+                           InvokeRemoteHandlerOnCompleteOnSignalingThread,
+                       base::Unretained(this), std::move(error),
+                       base::Unretained(&run_loop)));
+    run_loop.Run();
+  }
+  void InvokeRemoteHandlerOnCompleteOnSignalingThread(webrtc::RTCError error,
+                                                      base::RunLoop* run_loop) {
+    remote_handler_->OnSetRemoteDescriptionComplete(std::move(error));
+    run_loop->Quit();
+  }
+
+  scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner_;
+  ObserverHandlerType handler_type_;
+  scoped_refptr<WebRtcSetLocalDescriptionObserverHandler> local_handler_;
+  scoped_refptr<WebRtcSetRemoteDescriptionObserverHandler> remote_handler_;
+};
+
+enum class StateSurfacerType {
+  kTransceivers,
+  kReceiversOnly,
+};
+
+using TestVariety = std::tuple<ObserverHandlerType, StateSurfacerType>;
+
+struct PrintToStringTestVariety {
+  std::string operator()(
+      const testing::TestParamInfo<TestVariety>& info) const {
+    ObserverHandlerType handler_type = std::get<0>(info.param);
+    StateSurfacerType surfacer_type = std::get<1>(info.param);
+    std::string str;
+    switch (handler_type) {
+      case ObserverHandlerType::kLocal:
+        str += "LocalDescriptionWith";
+        break;
+      case ObserverHandlerType::kRemote:
+        str += "RemoteDescriptionWith";
+        break;
+    }
+    switch (surfacer_type) {
+      case StateSurfacerType::kTransceivers:
+        str += "TransceiverStates";
+        break;
+      case StateSurfacerType::kReceiversOnly:
+        str += "ReceiverStates";
+        break;
+    }
+    return str;
+  }
+};
+
+// Using parameterization, this class is used to test both
+// WebRtcSetLocalDescriptionObserverHandler and
+// WebRtcSetRemoteDescriptionObserverHandler. The handlers, used for
+// setLocalDescription() and setRemoteDescription() respectively, are virtually
+// identical in terms of functionality but have different class hierarchies due
+// to webrtc observer interfaces being different classes.
+//
+// Each handler is testable under two modes of operation: surfacing state
+// information about transceivers (includes both senders and receivers), or only
+// surfacing receiver state information. Unified Plan requires the former and
+// Plan B requires the latter.
+//
+// Parameterization allows easily running the same tests for each handler and
+// mode, as specified by the TestVariety.
+class WebRtcSetDescriptionObserverHandlerTest
+    : public ::testing::TestWithParam<TestVariety> {
+ public:
+  WebRtcSetDescriptionObserverHandlerTest()
+      : handler_type_(std::get<0>(GetParam())),
+        surfacer_type_(std::get<1>(GetParam())) {}
+
   void SetUp() override {
     pc_ = new webrtc::MockPeerConnectionInterface;
     dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
     main_thread_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
-    scoped_refptr<WebRtcMediaStreamTrackAdapterMap> map =
-        new WebRtcMediaStreamTrackAdapterMap(dependency_factory_.get(),
-                                             main_thread_);
+    track_adapter_map_ = new WebRtcMediaStreamTrackAdapterMap(
+        dependency_factory_.get(), main_thread_);
     observer_ = new WebRtcSetDescriptionObserverForTest();
-    observer_handler_ = WebRtcSetRemoteDescriptionObserverHandler::Create(
-        main_thread_, dependency_factory_->GetWebRtcSignalingThread(), pc_, map,
-        observer_, true /* surface_receivers_only*/);
+    observer_handler_ = std::make_unique<ObserverHandlerWrapper>(
+        handler_type_, main_thread_,
+        dependency_factory_->GetWebRtcSignalingThread(), pc_,
+        track_adapter_map_, observer_,
+        surfacer_type_ == StateSurfacerType::kReceiversOnly);
   }
 
   void TearDown() override { blink::WebHeap::CollectAllGarbageForTesting(); }
 
-  void InvokeOnSetRemoteDescriptionComplete(webrtc::RTCError error) {
-    base::RunLoop run_loop;
-    dependency_factory_->GetWebRtcSignalingThread()->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            &WebRtcSetRemoteDescriptionObserverHandlerTest::
-                InvokeOnSetRemoteDescriptionCompleteOnSignalingThread,
-            base::Unretained(this), std::move(error),
-            base::Unretained(&run_loop)));
-    run_loop.Run();
+  blink::WebMediaStreamTrack CreateLocalTrack(const std::string& id) {
+    blink::WebMediaStreamSource web_source;
+    web_source.Initialize(
+        blink::WebString::FromUTF8(id), blink::WebMediaStreamSource::kTypeAudio,
+        blink::WebString::FromUTF8("local_audio_track"), false);
+    MediaStreamAudioSource* audio_source = new MediaStreamAudioSource(true);
+    // Takes ownership of |audio_source|.
+    web_source.SetExtraData(audio_source);
+
+    blink::WebMediaStreamTrack web_track;
+    web_track.Initialize(web_source.Id(), web_source);
+    audio_source->ConnectToTrack(web_track);
+    return web_track;
+  }
+
+  void CreateTransceivers() {
+    ASSERT_EQ(StateSurfacerType::kTransceivers, surfacer_type_);
+
+    auto web_local_track = CreateLocalTrack("local_track");
+    auto local_track_adapter =
+        track_adapter_map_->GetOrCreateLocalTrackAdapter(web_local_track);
+    scoped_refptr<webrtc::MediaStreamTrackInterface> local_track =
+        local_track_adapter->webrtc_track();
+    rtc::scoped_refptr<webrtc::RtpSenderInterface> sender(
+        new rtc::RefCountedObject<FakeRtpSender>(
+            local_track.get(), std::vector<std::string>({"local_stream"})));
+    // A requirement of WebRtcSet[Local/Remote]DescriptionObserverHandler is
+    // that local tracks have existing track adapters when the callback is
+    // invoked. In practice this would be ensured by RTCPeerConnectionHandler.
+    // Here in testing, we ensure it by adding it to |local_track_adapters_|.
+    local_track_adapters_.push_back(std::move(local_track_adapter));
+
+    scoped_refptr<MockWebRtcAudioTrack> remote_track =
+        MockWebRtcAudioTrack::Create("remote_track");
+    scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
+        new rtc::RefCountedObject<MockMediaStream>("remote_stream"));
+    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver(
+        new rtc::RefCountedObject<FakeRtpReceiver>(
+            remote_track.get(),
+            std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
+                {remote_stream.get()})));
+    rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver(
+        new rtc::RefCountedObject<FakeRtpTransceiver>(
+            cricket::MEDIA_TYPE_AUDIO, sender, receiver, base::nullopt, false,
+            webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt));
+    transceivers_.push_back(transceiver);
+    EXPECT_CALL(*pc_, GetTransceivers()).WillRepeatedly(Return(transceivers_));
+  }
+
+  void ExpectMatchingTransceivers() {
+    ASSERT_EQ(StateSurfacerType::kTransceivers, surfacer_type_);
+    ASSERT_EQ(1u, transceivers_.size());
+
+    auto transceiver = transceivers_[0];
+    auto sender = transceiver->sender();
+    auto receiver = transceiver->receiver();
+    EXPECT_EQ(1u, observer_->states().transceiver_states.size());
+    const RtpTransceiverState& transceiver_state =
+        observer_->states().transceiver_states[0];
+    // Inspect transceiver states.
+    EXPECT_TRUE(transceiver_state.is_initialized());
+    EXPECT_EQ(transceiver.get(), transceiver_state.webrtc_transceiver());
+    EXPECT_TRUE(OptionalEquals(transceiver_state.mid(), transceiver->mid()));
+    EXPECT_EQ(transceiver_state.stopped(), transceiver->stopped());
+    EXPECT_TRUE(transceiver_state.direction() == transceiver->direction());
+    EXPECT_TRUE(OptionalEquals(transceiver_state.current_direction(),
+                               transceiver->current_direction()));
+    EXPECT_TRUE(OptionalEquals(transceiver_state.fired_direction(),
+                               transceiver->fired_direction()));
+    // Inspect sender states.
+    EXPECT_TRUE(transceiver_state.sender_state());
+    const RtpSenderState& sender_state = *transceiver_state.sender_state();
+    EXPECT_TRUE(sender_state.is_initialized());
+    EXPECT_EQ(sender.get(), sender_state.webrtc_sender());
+    EXPECT_EQ(sender->track(), sender_state.track_ref()->webrtc_track());
+    EXPECT_EQ(sender->stream_ids(), sender_state.stream_ids());
+    // Inspect receiver states.
+    EXPECT_TRUE(transceiver_state.receiver_state());
+    const RtpReceiverState& receiver_state =
+        *transceiver_state.receiver_state();
+    EXPECT_TRUE(receiver_state.is_initialized());
+    EXPECT_EQ(receiver.get(), receiver_state.webrtc_receiver());
+    EXPECT_EQ(receiver->track(), receiver_state.track_ref()->webrtc_track());
+    EXPECT_EQ(receiver->stream_ids(), receiver_state.stream_ids());
+  }
+
+  void CreateReceivers() {
+    ASSERT_EQ(StateSurfacerType::kReceiversOnly, surfacer_type_);
+
+    scoped_refptr<MockWebRtcAudioTrack> remote_track =
+        MockWebRtcAudioTrack::Create("remote_track");
+    scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
+        new rtc::RefCountedObject<MockMediaStream>("remote_stream"));
+    rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver(
+        new rtc::RefCountedObject<FakeRtpReceiver>(
+            remote_track.get(),
+            std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
+                {remote_stream.get()})));
+    receivers_.push_back(receiver);
+    EXPECT_CALL(*pc_, GetReceivers()).WillRepeatedly(Return(receivers_));
+  }
+
+  void ExpectMatchingReceivers() {
+    ASSERT_EQ(StateSurfacerType::kReceiversOnly, surfacer_type_);
+    ASSERT_EQ(1u, receivers_.size());
+
+    auto receiver = receivers_[0];
+    EXPECT_EQ(1u, observer_->states().transceiver_states.size());
+    const RtpTransceiverState& transceiver_state =
+        observer_->states().transceiver_states[0];
+    EXPECT_FALSE(transceiver_state.sender_state());
+    EXPECT_TRUE(transceiver_state.receiver_state());
+    const RtpReceiverState& receiver_state =
+        *transceiver_state.receiver_state();
+    EXPECT_TRUE(receiver_state.is_initialized());
+    EXPECT_EQ(receiver.get(), receiver_state.webrtc_receiver());
+    EXPECT_EQ(receiver->track(), receiver_state.track_ref()->webrtc_track());
+    EXPECT_EQ(receiver->stream_ids(), receiver_state.stream_ids());
   }
 
  protected:
-  void InvokeOnSetRemoteDescriptionCompleteOnSignalingThread(
-      webrtc::RTCError error,
-      base::RunLoop* run_loop) {
-    observer_handler_->OnSetRemoteDescriptionComplete(std::move(error));
-    run_loop->Quit();
-  }
-
   // The ScopedTaskEnvironment prevents the ChildProcess from leaking a
   // TaskScheduler.
   base::test::ScopedTaskEnvironment scoped_task_environment_;
@@ -108,64 +366,66 @@
   scoped_refptr<webrtc::MockPeerConnectionInterface> pc_;
   std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
+  scoped_refptr<WebRtcMediaStreamTrackAdapterMap> track_adapter_map_;
   scoped_refptr<WebRtcSetDescriptionObserverForTest> observer_;
-  scoped_refptr<WebRtcSetRemoteDescriptionObserverHandler> observer_handler_;
 
+  ObserverHandlerType handler_type_;
+  StateSurfacerType surfacer_type_;
+  std::unique_ptr<ObserverHandlerWrapper> observer_handler_;
+
+  std::vector<rtc::scoped_refptr<webrtc::RtpTransceiverInterface>>
+      transceivers_;
+  std::vector<std::unique_ptr<WebRtcMediaStreamTrackAdapterMap::AdapterRef>>
+      local_track_adapters_;
+  // Used instead of |transceivers_| when |surfacer_type_| is
+  // StateSurfacerType::kReceiversOnly.
   std::vector<rtc::scoped_refptr<webrtc::RtpReceiverInterface>> receivers_;
 };
 
-TEST_F(WebRtcSetRemoteDescriptionObserverHandlerTest, OnSuccess) {
-  scoped_refptr<MockWebRtcAudioTrack> added_track =
-      MockWebRtcAudioTrack::Create("added_track");
-  scoped_refptr<webrtc::MediaStreamInterface> added_stream(
-      new rtc::RefCountedObject<MockMediaStream>("added_stream"));
-  scoped_refptr<webrtc::RtpReceiverInterface> added_receiver(
-      new rtc::RefCountedObject<FakeRtpReceiver>(
-          added_track.get(),
-          std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
-              {added_stream.get()})));
+TEST_P(WebRtcSetDescriptionObserverHandlerTest, OnSuccess) {
+  switch (surfacer_type_) {
+    case StateSurfacerType::kTransceivers:
+      CreateTransceivers();
+      break;
+    case StateSurfacerType::kReceiversOnly:
+      CreateReceivers();
+      break;
+  }
 
-  receivers_.push_back(added_receiver.get());
   EXPECT_CALL(*pc_, signaling_state())
       .WillRepeatedly(Return(webrtc::PeerConnectionInterface::kStable));
-  EXPECT_CALL(*pc_, GetReceivers()).WillRepeatedly(Return(receivers_));
 
-  InvokeOnSetRemoteDescriptionComplete(webrtc::RTCError::OK());
+  observer_handler_->InvokeOnComplete(webrtc::RTCError::OK());
   EXPECT_TRUE(observer_->called());
   EXPECT_TRUE(observer_->error().ok());
 
   EXPECT_EQ(webrtc::PeerConnectionInterface::kStable,
             observer_->states().signaling_state);
 
-  EXPECT_EQ(1u, observer_->states().transceiver_states.size());
-  const RtpTransceiverState& transceiver_state =
-      observer_->states().transceiver_states[0];
-  EXPECT_FALSE(transceiver_state.sender_state());
-  EXPECT_TRUE(transceiver_state.receiver_state());
-  const RtpReceiverState& receiver_state = *transceiver_state.receiver_state();
-  EXPECT_EQ(added_receiver, receiver_state.webrtc_receiver());
-  EXPECT_EQ(added_track, receiver_state.track_ref()->webrtc_track());
-  EXPECT_EQ(1u, receiver_state.stream_ids().size());
-  EXPECT_EQ(added_stream->id(), receiver_state.stream_ids()[0]);
+  switch (surfacer_type_) {
+    case StateSurfacerType::kTransceivers:
+      ExpectMatchingTransceivers();
+      break;
+    case StateSurfacerType::kReceiversOnly:
+      ExpectMatchingReceivers();
+      break;
+  }
 }
 
-TEST_F(WebRtcSetRemoteDescriptionObserverHandlerTest, OnFailure) {
-  scoped_refptr<MockWebRtcAudioTrack> added_track =
-      MockWebRtcAudioTrack::Create("added_track");
-  scoped_refptr<webrtc::MediaStreamInterface> added_stream(
-      new rtc::RefCountedObject<MockMediaStream>("added_stream"));
-  scoped_refptr<webrtc::RtpReceiverInterface> added_receiver(
-      new rtc::RefCountedObject<FakeRtpReceiver>(
-          added_track.get(),
-          std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
-              {added_stream.get()})));
+TEST_P(WebRtcSetDescriptionObserverHandlerTest, OnFailure) {
+  switch (surfacer_type_) {
+    case StateSurfacerType::kTransceivers:
+      CreateTransceivers();
+      break;
+    case StateSurfacerType::kReceiversOnly:
+      CreateReceivers();
+      break;
+  }
 
-  receivers_.push_back(added_receiver.get());
   EXPECT_CALL(*pc_, signaling_state())
       .WillRepeatedly(Return(webrtc::PeerConnectionInterface::kStable));
-  EXPECT_CALL(*pc_, GetReceivers()).WillRepeatedly(Return(receivers_));
 
-  InvokeOnSetRemoteDescriptionComplete(
+  observer_handler_->InvokeOnComplete(
       webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Oh noes!"));
   EXPECT_TRUE(observer_->called());
   EXPECT_FALSE(observer_->error().ok());
@@ -175,16 +435,62 @@
   EXPECT_EQ(webrtc::PeerConnectionInterface::kStable,
             observer_->states().signaling_state);
 
-  EXPECT_EQ(1u, observer_->states().transceiver_states.size());
-  const RtpTransceiverState& transceiver_state =
-      observer_->states().transceiver_states[0];
-  EXPECT_FALSE(transceiver_state.sender_state());
-  EXPECT_TRUE(transceiver_state.receiver_state());
-  const RtpReceiverState& receiver_state = *transceiver_state.receiver_state();
-  EXPECT_EQ(added_receiver, receiver_state.webrtc_receiver());
-  EXPECT_EQ(added_track, receiver_state.track_ref()->webrtc_track());
-  EXPECT_EQ(1u, receiver_state.stream_ids().size());
-  EXPECT_EQ(added_stream->id(), receiver_state.stream_ids()[0]);
+  switch (surfacer_type_) {
+    case StateSurfacerType::kTransceivers:
+      ExpectMatchingTransceivers();
+      break;
+    case StateSurfacerType::kReceiversOnly:
+      ExpectMatchingReceivers();
+      break;
+  }
 }
 
+// Test coverage for https://crbug.com/897251. If the webrtc peer connection is
+// implemented to invoke the callback with a delay it might already have been
+// closed when the observer is invoked. A closed RTCPeerConnection is allowed to
+// be garbage collected. In rare circumstances, the RTCPeerConnection,
+// RTCPeerConnectionHandler and any local track adapters may thus have been
+// deleted when the observer attempts to surface transceiver state information.
+// This test insures that TransceiverStateSurfacer::Initialize() does not crash
+// due to track adapters not existing.
+TEST_P(WebRtcSetDescriptionObserverHandlerTest,
+       ClosePeerConnectionBeforeCallback) {
+  switch (surfacer_type_) {
+    case StateSurfacerType::kTransceivers:
+      CreateTransceivers();
+      break;
+    case StateSurfacerType::kReceiversOnly:
+      CreateReceivers();
+      break;
+  }
+
+  // Simulate the peer connection having been closed and local track adapters
+  // destroyed before the observer was invoked.
+  EXPECT_CALL(*pc_, signaling_state())
+      .WillRepeatedly(Return(webrtc::PeerConnectionInterface::kClosed));
+  local_track_adapters_.clear();
+
+  observer_handler_->InvokeOnComplete(webrtc::RTCError::OK());
+  EXPECT_TRUE(observer_->called());
+  EXPECT_TRUE(observer_->error().ok());
+
+  EXPECT_EQ(webrtc::PeerConnectionInterface::kClosed,
+            observer_->states().signaling_state);
+
+  EXPECT_EQ(0u, observer_->states().transceiver_states.size());
+}
+
+INSTANTIATE_TEST_CASE_P(
+    /* no prefix */,
+    WebRtcSetDescriptionObserverHandlerTest,
+    ::testing::Values(std::make_tuple(ObserverHandlerType::kLocal,
+                                      StateSurfacerType::kTransceivers),
+                      std::make_tuple(ObserverHandlerType::kRemote,
+                                      StateSurfacerType::kTransceivers),
+                      std::make_tuple(ObserverHandlerType::kLocal,
+                                      StateSurfacerType::kReceiversOnly),
+                      std::make_tuple(ObserverHandlerType::kRemote,
+                                      StateSurfacerType::kReceiversOnly)),
+    PrintToStringTestVariety());
+
 }  // namespace content
diff --git a/content/shell/android/BUILD.gn b/content/shell/android/BUILD.gn
index 224c505..326b3be 100644
--- a/content/shell/android/BUILD.gn
+++ b/content/shell/android/BUILD.gn
@@ -19,18 +19,10 @@
   ]
 }
 
-generate_jni_registration("content_shell_jni_registration") {
-  testonly = true
-  target = ":content_shell_apk"
-  output = "$root_gen_dir/content/shell/android/${target_name}.h"
-  exception_files = jni_exception_files
-}
-
 shared_library("libcontent_shell_content_view") {
   testonly = true
   deps = [
     ":content_shell_jni_headers",
-    ":content_shell_jni_registration",
     "//components/crash/content/browser",
     "//content/shell:content_shell_lib",
     "//content/shell:pak",
@@ -49,12 +41,13 @@
   sources = [
     "shell_library_loader.cc",
   ]
+  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 shared_library("libcontent_native_test") {
   testonly = true
   deps = [
-    ":content_test_jni_registration",
     "//base",
     "//content/public/test/android:content_native_test_support",
     "//content/shell:content_shell_lib",
@@ -63,6 +56,8 @@
   sources = [
     "shell_test_library_loader.cc",
   ]
+  configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  configs += [ "//build/config/android:hide_all_but_jni" ]
 }
 
 android_resources("content_shell_java_resources") {
@@ -216,13 +211,6 @@
   ]
 }
 
-generate_jni_registration("content_test_jni_registration") {
-  testonly = true
-  target = ":content_shell_test_apk__apk"
-  output = "$root_gen_dir/content/shell/android/${target_name}.h"
-  exception_files = jni_exception_files
-}
-
 instrumentation_test_apk("content_shell_test_apk") {
   deps = [
     "//base:base_java_test_support",
diff --git a/content/shell/android/shell_library_loader.cc b/content/shell/android/shell_library_loader.cc
index 24810f8..e21b58d1 100644
--- a/content/shell/android/shell_library_loader.cc
+++ b/content/shell/android/shell_library_loader.cc
@@ -3,27 +3,15 @@
 // found in the LICENSE file.
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_utils.h"
-#include "base/android/library_loader/library_loader_hooks.h"
-#include "base/bind.h"
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
-#include "content/shell/android/content_shell_jni_registration.h"
 #include "content/shell/app/shell_main_delegate.h"
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (!RegisterMainDexNatives(env))
-    return -1;
 
-  // Do not register JNI methods in secondary dex for non-browser process.
-  bool is_browser_process =
-      !base::android::IsSelectiveJniRegistrationEnabled(env);
-  if (is_browser_process && !RegisterNonMainDexNatives(env))
-    return -1;
   if (!content::android::OnJNIOnLoadInit())
     return -1;
 
diff --git a/content/shell/android/shell_test_library_loader.cc b/content/shell/android/shell_test_library_loader.cc
index 7b47e54c..dda0556 100644
--- a/content/shell/android/shell_test_library_loader.cc
+++ b/content/shell/android/shell_test_library_loader.cc
@@ -3,26 +3,14 @@
 // found in the LICENSE file.
 
 #include "base/android/jni_android.h"
-#include "base/android/jni_utils.h"
 #include "content/public/app/content_jni_onload.h"
 #include "content/public/app/content_main.h"
 #include "content/public/browser/android/compositor.h"
-#include "content/shell/android/content_test_jni_registration.h"
 #include "content/shell/app/shell_main_delegate.h"
 
 // This is called by the VM when the shared library is first loaded.
 JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
   base::android::InitVM(vm);
-  JNIEnv* env = base::android::AttachCurrentThread();
-  if (!base::android::IsSelectiveJniRegistrationEnabled(env)) {
-    if (!RegisterNonMainDexNatives(env)) {
-      return -1;
-    }
-  }
-
-  if (!RegisterMainDexNatives(env)) {
-    return -1;
-  }
   if (!content::android::OnJNIOnLoadInit())
     return -1;
   content::Compositor::Initialize();
diff --git a/extensions/browser/api/hid/OWNERS b/extensions/browser/api/hid/OWNERS
index 6279377..4179744 100644
--- a/extensions/browser/api/hid/OWNERS
+++ b/extensions/browser/api/hid/OWNERS
@@ -1,4 +1,5 @@
+mattreynolds@chromium.org
 reillyg@chromium.org
 rockot@google.com
 
-# COMPONENT: Platform>Extensions>API
\ No newline at end of file
+# COMPONENT: Platform>Extensions>API
diff --git a/extensions/browser/app_window/app_window_registry.h b/extensions/browser/app_window/app_window_registry.h
index f8de42a7..a6141971 100644
--- a/extensions/browser/app_window/app_window_registry.h
+++ b/extensions/browser/app_window/app_window_registry.h
@@ -9,10 +9,9 @@
 #include <set>
 #include <string>
 
-#include "base/callback.h"
-#include "base/compiler_specific.h"
 #include "base/memory/singleton.h"
 #include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/devtools_agent_host_observer.h"
@@ -33,7 +32,7 @@
 class AppWindowRegistry : public KeyedService,
                           public content::DevToolsAgentHostObserver {
  public:
-  class Observer {
+  class Observer : public base::CheckedObserver {
    public:
     // Called just after an app window was added.
     virtual void OnAppWindowAdded(AppWindow* app_window);
@@ -51,7 +50,7 @@
     virtual void OnAppWindowActivated(AppWindow* app_window);
 
    protected:
-    virtual ~Observer();
+    ~Observer() override;
   };
 
   typedef std::list<AppWindow*> AppWindowList;
@@ -155,7 +154,7 @@
   content::BrowserContext* context_;
   AppWindowList app_windows_;
   InspectedWindowSet inspected_windows_;
-  base::ObserverList<Observer>::Unchecked observers_;
+  base::ObserverList<Observer> observers_;
 };
 
 }  // namespace extensions
diff --git a/google_apis/gaia/google_service_auth_error.cc b/google_apis/gaia/google_service_auth_error.cc
index 3feba3cf..4c699c0 100644
--- a/google_apis/gaia/google_service_auth_error.cc
+++ b/google_apis/gaia/google_service_auth_error.cc
@@ -12,6 +12,27 @@
 #include "base/strings/stringprintf.h"
 #include "net/base/net_errors.h"
 
+namespace {
+const char* InvalidCredentialsReasonToString(
+    GoogleServiceAuthError::InvalidGaiaCredentialsReason reason) {
+  using InvalidGaiaCredentialsReason =
+      GoogleServiceAuthError::InvalidGaiaCredentialsReason;
+  switch (reason) {
+    case InvalidGaiaCredentialsReason::UNKNOWN:
+      return "unknown";
+    case InvalidGaiaCredentialsReason::CREDENTIALS_REJECTED_BY_SERVER:
+      return "credentials rejected by server";
+    case InvalidGaiaCredentialsReason::CREDENTIALS_REJECTED_BY_CLIENT:
+      return "credentials rejected by client";
+    case InvalidGaiaCredentialsReason::CREDENTIALS_MISSING:
+      return "credentials missing";
+    case InvalidGaiaCredentialsReason::NUM_REASONS:
+      NOTREACHED();
+      return "";
+  }
+}
+}  // namespace
+
 GoogleServiceAuthError::Captcha::Captcha() : image_width(0), image_height(0) {
 }
 
@@ -183,8 +204,8 @@
       return std::string();
     case INVALID_GAIA_CREDENTIALS:
       return base::StringPrintf(
-          "Invalid credentials (%d).",
-          static_cast<int>(invalid_gaia_credentials_reason_));
+          "Invalid credentials (%s).",
+          InvalidCredentialsReasonToString(invalid_gaia_credentials_reason_));
     case USER_NOT_SIGNED_UP:
       return "Not authorized.";
     case CONNECTION_FAILED:
@@ -213,7 +234,8 @@
       return "Less secure apps may not authenticate with this account. "
              "Please visit: "
              "https://www.google.com/settings/security/lesssecureapps";
-    default:
+    case HOSTED_NOT_ALLOWED_DEPRECATED:
+    case NUM_STATES:
       NOTREACHED();
       return std::string();
   }
diff --git a/google_apis/gaia/google_service_auth_error_unittest.cc b/google_apis/gaia/google_service_auth_error_unittest.cc
index 4035256..4980f3cf 100644
--- a/google_apis/gaia/google_service_auth_error_unittest.cc
+++ b/google_apis/gaia/google_service_auth_error_unittest.cc
@@ -68,7 +68,8 @@
   EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason::
                 CREDENTIALS_REJECTED_BY_SERVER,
             error.GetInvalidGaiaCredentialsReason());
-  EXPECT_EQ("Invalid credentials (1).", error.ToString());
+  EXPECT_EQ("Invalid credentials (credentials rejected by server).",
+            error.ToString());
 }
 
 TEST(GoogleServiceAuthErrorTest, AuthErrorNone) {
diff --git a/google_apis/gcm/base/socket_stream_unittest.cc b/google_apis/gcm/base/socket_stream_unittest.cc
index e62a695..918a411 100644
--- a/google_apis/gcm/base/socket_stream_unittest.cc
+++ b/google_apis/gcm/base/socket_stream_unittest.cc
@@ -222,8 +222,11 @@
   base::RunLoop run_loop;
   int net_error = net::ERR_FAILED;
   const GURL kDestination("https://example.com");
+  network::mojom::ProxyResolvingSocketOptionsPtr options =
+      network::mojom::ProxyResolvingSocketOptions::New();
+  options->use_tls = true;
   mojo_socket_factory_ptr_->CreateProxyResolvingSocket(
-      kDestination, true /* use_tls */,
+      kDestination, std::move(options),
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
       mojo::MakeRequest(&mojo_socket_ptr_), nullptr /* observer */,
       base::BindLambdaForTesting(
diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc
index 85cde7a..18a3ee6 100644
--- a/google_apis/gcm/engine/connection_factory_impl.cc
+++ b/google_apis/gcm/engine/connection_factory_impl.cc
@@ -348,8 +348,11 @@
           "but does not have any effect on other Google Cloud messages."
         )");
 
+  network::mojom::ProxyResolvingSocketOptionsPtr options =
+      network::mojom::ProxyResolvingSocketOptions::New();
+  options->use_tls = true;
   socket_factory_->CreateProxyResolvingSocket(
-      current_endpoint, true /* use_tls */,
+      current_endpoint, std::move(options),
       net::MutableNetworkTrafficAnnotationTag(traffic_annotation),
       mojo::MakeRequest(&socket_), nullptr /* observer */,
       base::BindOnce(&ConnectionFactoryImpl::OnConnectDone,
diff --git a/google_apis/gcm/engine/connection_handler_impl_unittest.cc b/google_apis/gcm/engine/connection_handler_impl_unittest.cc
index b0e781e4..8da8b854 100644
--- a/google_apis/gcm/engine/connection_handler_impl_unittest.cc
+++ b/google_apis/gcm/engine/connection_handler_impl_unittest.cc
@@ -236,8 +236,11 @@
   base::RunLoop run_loop;
   int net_error = net::ERR_FAILED;
   const GURL kDestination("https://example.com");
+  network::mojom::ProxyResolvingSocketOptionsPtr options =
+      network::mojom::ProxyResolvingSocketOptions::New();
+  options->use_tls = true;
   mojo_socket_factory_ptr_->CreateProxyResolvingSocket(
-      kDestination, true /* use_tls */,
+      kDestination, std::move(options),
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
       mojo::MakeRequest(&mojo_socket_ptr_), nullptr /* observer */,
       base::BindLambdaForTesting(
diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn
index be5b33b5..67136e00 100644
--- a/gpu/command_buffer/service/BUILD.gn
+++ b/gpu/command_buffer/service/BUILD.gn
@@ -5,7 +5,6 @@
 import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
 import("//third_party/protobuf/proto_library.gni")
-import("//gpu/vulkan/features.gni")
 
 group("service") {
   if (is_component_build) {
@@ -294,7 +293,6 @@
     "//gpu/command_buffer/common:gles2_utils",
     "//gpu/config",
     "//gpu/ipc/common:surface_handle_type",
-    "//gpu/vulkan:buildflags",
     "//third_party/angle:angle_image_util",
     "//third_party/angle:commit_id",
     "//third_party/angle:translator",
@@ -309,10 +307,6 @@
     "//ui/gl/init",
   ]
 
-  if (enable_vulkan) {
-    deps += [ "//components/viz/common:vulkan_context_provider" ]
-  }
-
   if (is_mac) {
     # Required by gles2_cmd_decoder.cc on Mac.
     libs = [
diff --git a/gpu/command_buffer/service/DEPS b/gpu/command_buffer/service/DEPS
index 1373db1..7c6a3bce 100644
--- a/gpu/command_buffer/service/DEPS
+++ b/gpu/command_buffer/service/DEPS
@@ -1,7 +1,6 @@
 include_rules = [
   "+cc/paint",
   "+third_party/skia",
-  "+components/viz/common/gpu/vulkan_context_provider.h",
   "+components/viz/common/resources/resource_format.h",
   "+components/viz/common/resources/resource_format_utils.h",
   "+components/viz/common/resources/resource_sizes.h",
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.cc b/gpu/command_buffer/service/raster_decoder_context_state.cc
index 9e605ae3..3fad5d903 100644
--- a/gpu/command_buffer/service/raster_decoder_context_state.cc
+++ b/gpu/command_buffer/service/raster_decoder_context_state.cc
@@ -9,17 +9,12 @@
 #include "gpu/command_buffer/common/activity_flags.h"
 #include "gpu/command_buffer/service/service_transfer_cache.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
-#include "gpu/vulkan/buildflags.h"
 #include "ui/gl/gl_bindings.h"
 #include "ui/gl/gl_context.h"
 #include "ui/gl/gl_share_group.h"
 #include "ui/gl/gl_surface.h"
 #include "ui/gl/init/create_gr_gl_interface.h"
 
-#if BUILDFLAG(ENABLE_VULKAN)
-#include "components/viz/common/gpu/vulkan_context_provider.h"
-#endif
-
 namespace gpu {
 namespace raster {
 
@@ -27,10 +22,13 @@
     scoped_refptr<gl::GLShareGroup> share_group,
     scoped_refptr<gl::GLSurface> surface,
     scoped_refptr<gl::GLContext> context,
-    bool use_virtualized_gl_contexts)
+    bool use_virtualized_gl_contexts,
+    GrContext* vulkan_gr_context)
     : share_group(std::move(share_group)),
       surface(std::move(surface)),
       context(std::move(context)),
+      gr_context(vulkan_gr_context),
+      use_vulkan_gr_context(!!gr_context),
       use_virtualized_gl_contexts(use_virtualized_gl_contexts) {
   if (base::ThreadTaskRunnerHandle::IsSet()) {
     base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
@@ -38,27 +36,6 @@
   }
 }
 
-RasterDecoderContextState::RasterDecoderContextState(
-    viz::VulkanContextProvider* vulkan_context_provider)
-#if BUILDFLAG(ENABLE_VULKAN)
-    : vk_context_provider(vulkan_context_provider),
-      gr_context(vk_context_provider->GetGrContext()),
-      use_vulkan_gr_context(true)
-#endif
-{
-// This constructor should not be called if Vulkan is not enabled.
-#if !BUILDFLAG(ENABLE_VULKAN)
-  DCHECK(false);
-#endif
-
-  // gr_context should not be null.
-  DCHECK(gr_context);
-  if (base::ThreadTaskRunnerHandle::IsSet()) {
-    base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-        this, "RasterDecoderContextState", base::ThreadTaskRunnerHandle::Get());
-  }
-}
-
 RasterDecoderContextState::~RasterDecoderContextState() {
   if (gr_context)
     gr_context->abandonContext();
diff --git a/gpu/command_buffer/service/raster_decoder_context_state.h b/gpu/command_buffer/service/raster_decoder_context_state.h
index 26d3f4f..1d8d006 100644
--- a/gpu/command_buffer/service/raster_decoder_context_state.h
+++ b/gpu/command_buffer/service/raster_decoder_context_state.h
@@ -19,10 +19,6 @@
 class GLSurface;
 }  // namespace gl
 
-namespace viz {
-class VulkanContextProvider;
-}  // namespace viz
-
 namespace gpu {
 class GpuDriverBugWorkarounds;
 class GpuProcessActivityFlags;
@@ -34,16 +30,11 @@
     : public base::RefCounted<RasterDecoderContextState>,
       public base::trace_event::MemoryDumpProvider {
  public:
-  // Used for GL.
   RasterDecoderContextState(scoped_refptr<gl::GLShareGroup> share_group,
                             scoped_refptr<gl::GLSurface> surface,
                             scoped_refptr<gl::GLContext> context,
-                            bool use_virtualized_gl_contexts);
-
-  // Used for Vulkan.
-  RasterDecoderContextState(
-      viz::VulkanContextProvider* vulkan_context_provider);
-
+                            bool use_virtualized_gl_contexts,
+                            GrContext* vulkan_gr_context = nullptr);
   void InitializeGrContext(const GpuDriverBugWorkarounds& workarounds,
                            GrContextOptions::PersistentCache* cache,
                            GpuProcessActivityFlags* activity_flags = nullptr,
@@ -51,14 +42,10 @@
   void PurgeMemory(
       base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
 
-  // These should be null for Vulkan mode.
   scoped_refptr<gl::GLShareGroup> share_group;
   scoped_refptr<gl::GLSurface> surface;
   scoped_refptr<gl::GLContext> context;
-
-  // Should be null for GL mode.
-  viz::VulkanContextProvider* vk_context_provider = nullptr;
-  GrContext* gr_context = nullptr;
+  GrContext* gr_context;
   sk_sp<GrContext> owned_gr_context;
   std::unique_ptr<ServiceTransferCache> transfer_cache;
   const bool use_vulkan_gr_context = false;
diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc
index 901c611..39d421b 100644
--- a/gpu/ipc/service/gpu_channel_manager.cc
+++ b/gpu/ipc/service/gpu_channel_manager.cc
@@ -63,7 +63,7 @@
     const GpuFeatureInfo& gpu_feature_info,
     GpuProcessActivityFlags activity_flags,
     scoped_refptr<gl::GLSurface> default_offscreen_surface,
-    viz::VulkanContextProvider* vulkan_context_provider)
+    GrContext* vulkan_gr_context)
     : task_runner_(task_runner),
       io_task_runner_(io_task_runner),
       gpu_preferences_(gpu_preferences),
@@ -84,7 +84,7 @@
       memory_pressure_listener_(
           base::Bind(&GpuChannelManager::HandleMemoryPressure,
                      base::Unretained(this))),
-      vulkan_context_provider_(vulkan_context_provider),
+      vulkan_gr_context_(vulkan_gr_context),
       weak_factory_(this) {
   DCHECK(task_runner->BelongsToCurrentThread());
   DCHECK(io_task_runner);
@@ -432,15 +432,9 @@
   }
 
   // TODO(penghuang): https://crbug.com/899735 Handle device lost for Vulkan.
-  if (vulkan_context_provider_) {
-    raster_decoder_context_state_ =
-        new raster::RasterDecoderContextState(vulkan_context_provider_);
-  } else {
-    raster_decoder_context_state_ = new raster::RasterDecoderContextState(
-        std::move(share_group), std::move(surface), std::move(context),
-        use_virtualized_gl_contexts);
-  }
-
+  raster_decoder_context_state_ = new raster::RasterDecoderContextState(
+      std::move(share_group), std::move(surface), std::move(context),
+      use_virtualized_gl_contexts, vulkan_gr_context_);
   const bool enable_raster_transport =
       gpu_feature_info_.status_values[GPU_FEATURE_TYPE_OOP_RASTERIZATION] ==
       gpu::kGpuFeatureStatusEnabled;
diff --git a/gpu/ipc/service/gpu_channel_manager.h b/gpu/ipc/service/gpu_channel_manager.h
index 301cc1b..e0b60d571 100644
--- a/gpu/ipc/service/gpu_channel_manager.h
+++ b/gpu/ipc/service/gpu_channel_manager.h
@@ -37,6 +37,8 @@
 #include "ui/gl/gl_surface.h"
 #include "url/gurl.h"
 
+class GrContext;
+
 namespace gl {
 class GLShareGroup;
 }
@@ -66,19 +68,18 @@
 class GPU_IPC_SERVICE_EXPORT GpuChannelManager
     : public raster::GrShaderCache::Client {
  public:
-  GpuChannelManager(
-      const GpuPreferences& gpu_preferences,
-      GpuChannelManagerDelegate* delegate,
-      GpuWatchdogThread* watchdog,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
-      Scheduler* scheduler,
-      SyncPointManager* sync_point_manager,
-      GpuMemoryBufferFactory* gpu_memory_buffer_factory,
-      const GpuFeatureInfo& gpu_feature_info,
-      GpuProcessActivityFlags activity_flags,
-      scoped_refptr<gl::GLSurface> default_offscreen_surface,
-      viz::VulkanContextProvider* vulkan_context_provider = nullptr);
+  GpuChannelManager(const GpuPreferences& gpu_preferences,
+                    GpuChannelManagerDelegate* delegate,
+                    GpuWatchdogThread* watchdog,
+                    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+                    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+                    Scheduler* scheduler,
+                    SyncPointManager* sync_point_manager,
+                    GpuMemoryBufferFactory* gpu_memory_buffer_factory,
+                    const GpuFeatureInfo& gpu_feature_info,
+                    GpuProcessActivityFlags activity_flags,
+                    scoped_refptr<gl::GLSurface> default_offscreen_surface,
+                    GrContext* vulkan_gr_context = nullptr);
   ~GpuChannelManager() override;
 
   GpuChannelManagerDelegate* delegate() const { return delegate_; }
@@ -240,9 +241,9 @@
   scoped_refptr<raster::RasterDecoderContextState>
       raster_decoder_context_state_;
 
-  // With --enable-vulkan, the vulkan_context_provider_ will be set from
+  // With --enable-vulkan, the vulkan_gr_context_ will be set from
   // viz::GpuServiceImpl. The raster decoders will use it for rasterization.
-  viz::VulkanContextProvider* vulkan_context_provider_;
+  GrContext* vulkan_gr_context_;
 
   // Member variables should appear before the WeakPtrFactory, to ensure
   // that any WeakPtrs to Controller are invalidated before its members
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index a1126f9..de1ef8a6 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -210,7 +210,11 @@
         UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
     _incognitoBlocker.autoresizingMask =
         UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
-    [_window addSubview:_incognitoBlocker];
+
+    // Adding |_incognitoBlocker| to |_window| won't cover overlay windows such
+    // as fullscreen video.  Instead use the sharedApplication |keyWindow|.
+    UIWindow* window = [[UIApplication sharedApplication] keyWindow];
+    [window addSubview:_incognitoBlocker];
   }
 
   // Do not save cookies if it is already in progress.
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index afeee2f..5edf049 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -278,6 +278,7 @@
                                  startupInformation:startup_information_mock_
                                 applicationDelegate:main_application_delegate_];
       [app_state_ setWindow:window];
+      [window makeKeyAndVisible];
     }
     return app_state_;
   }
diff --git a/ios/chrome/app/resources/Info.plist b/ios/chrome/app/resources/Info.plist
index 98996f7..059aac0a 100644
--- a/ios/chrome/app/resources/Info.plist
+++ b/ios/chrome/app/resources/Info.plist
@@ -126,6 +126,10 @@
 	<true/>
 	<key>UIRequiresPersistentWiFi</key>
 	<true/>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>arm64</string>
+	</array>
 	<key>UIStatusBarStyle</key>
 	<string>UIStatusBarStyleLightContent</string>
 	<key>UIStatusBarHidden</key>
diff --git a/ios/chrome/browser/browser_state/BUILD.gn b/ios/chrome/browser/browser_state/BUILD.gn
index 2c8f350c..45139e1 100644
--- a/ios/chrome/browser/browser_state/BUILD.gn
+++ b/ios/chrome/browser/browser_state/BUILD.gn
@@ -84,7 +84,6 @@
     "//ios/chrome/browser/browser_state_metrics",
     "//ios/chrome/browser/browsing_data",
     "//ios/chrome/browser/content_settings",
-    "//ios/chrome/browser/desktop_promotion",
     "//ios/chrome/browser/dom_distiller",
     "//ios/chrome/browser/download",
     "//ios/chrome/browser/favicon",
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 c5f6200..b409a70 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
@@ -13,7 +13,6 @@
 #include "ios/chrome/browser/bookmarks/startup_task_runner_service_factory.h"
 #include "ios/chrome/browser/browsing_data/browsing_data_remover_factory.h"
 #include "ios/chrome/browser/content_settings/cookie_settings_factory.h"
-#include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h"
 #include "ios/chrome/browser/dom_distiller/dom_distiller_service_factory.h"
 #include "ios/chrome/browser/download/browser_download_service_factory.h"
 #include "ios/chrome/browser/favicon/favicon_service_factory.h"
@@ -108,7 +107,6 @@
   BrowserListSessionServiceFactory::GetInstance();
   BrowsingDataRemoverFactory::GetInstance();
   ConsentAuditorFactory::GetInstance();
-  DesktopPromotionSyncServiceFactory::GetInstance();
   feature_engagement::TrackerFactory::GetInstance();
   IOSChromeGCMProfileServiceFactory::GetInstance();
   IOSChromeLargeIconCacheFactory::GetInstance();
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc b/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
index 9bc94a70..a065b43 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_manager_impl.cc
@@ -31,7 +31,6 @@
 #include "ios/chrome/browser/browser_state_metrics/browser_state_metrics.h"
 #include "ios/chrome/browser/chrome_constants.h"
 #include "ios/chrome/browser/chrome_paths.h"
-#include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h"
 #include "ios/chrome/browser/invalidation/ios_chrome_deprecated_profile_invalidation_provider_factory.h"
 #include "ios/chrome/browser/pref_names.h"
 #include "ios/chrome/browser/signin/account_consistency_service_factory.h"
@@ -227,7 +226,6 @@
   // Initialization needs to happen after the browser context is available
   // because ProfileSyncService needs the URL context getter.
   UnifiedConsentServiceFactory::GetForBrowserState(browser_state);
-  DesktopPromotionSyncServiceFactory::GetForBrowserState(browser_state);
 }
 
 void ChromeBrowserStateManagerImpl::AddBrowserStateToCache(
diff --git a/ios/chrome/browser/desktop_promotion/BUILD.gn b/ios/chrome/browser/desktop_promotion/BUILD.gn
deleted file mode 100644
index 8cc9298..0000000
--- a/ios/chrome/browser/desktop_promotion/BUILD.gn
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2017 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("desktop_promotion") {
-  sources = [
-    "desktop_promotion_sync_service.cc",
-    "desktop_promotion_sync_service.h",
-    "desktop_promotion_sync_service_factory.cc",
-    "desktop_promotion_sync_service_factory.h",
-  ]
-  deps = [
-    "//base",
-    "//components/browser_sync",
-    "//components/keyed_service/core",
-    "//components/keyed_service/ios",
-    "//components/metrics",
-    "//components/pref_registry",
-    "//components/prefs",
-    "//ios/chrome/browser",
-    "//ios/chrome/browser/browser_state",
-    "//ios/chrome/browser/sync",
-    "//ios/chrome/common",
-  ]
-}
diff --git a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.cc b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.cc
deleted file mode 100644
index 805143c..0000000
--- a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.h"
-
-#include "base/logging.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/stl_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/time/time.h"
-#include "components/pref_registry/pref_registry_syncable.h"
-#include "components/prefs/pref_service.h"
-#include "components/sync/driver/sync_service.h"
-#include "ios/chrome/browser/pref_names.h"
-
-namespace {
-
-// These values are written to logs.  New values can be added, but existing
-// values must never be reordered or deleted and reused.
-const char* kDesktopIOSPromotionEntrypointHistogramPrefix[] = {
-    "SavePasswordsNewBubble", "BookmarksNewBubble", "BookmarksFootNote",
-    "HistoryPage",
-};
-
-}  // namespace
-
-DesktopPromotionSyncService::DesktopPromotionSyncService(
-    PrefService* pref_service,
-    syncer::SyncService* sync_service)
-    : pref_service_(pref_service), sync_service_(sync_service) {
-  DCHECK(pref_service_);
-  DCHECK(sync_service_);
-  sync_service_->AddObserver(this);
-}
-
-DesktopPromotionSyncService::~DesktopPromotionSyncService() {
-  DCHECK(!sync_service_);
-}
-
-void DesktopPromotionSyncService::Shutdown() {
-  if (sync_service_) {
-    sync_service_->RemoveObserver(this);
-    sync_service_ = nullptr;
-  }
-}
-
-void DesktopPromotionSyncService::OnStateChanged(
-    syncer::SyncService* sync_service) {
-  DCHECK_EQ(sync_service, sync_service_);
-  if (desktop_metrics_logger_initiated_ ||
-      !sync_service_->GetActiveDataTypes().Has(syncer::PRIORITY_PREFERENCES)) {
-    return;
-  }
-
-  desktop_metrics_logger_initiated_ = true;
-  const bool done_logging =
-      pref_service_->GetBoolean(prefs::kDesktopIOSPromotionDone);
-  const double last_impression =
-      pref_service_->GetDouble(prefs::kDesktopIOSPromotionLastImpression);
-  const base::TimeDelta delta =
-      base::Time::Now() - base::Time::FromDoubleT(last_impression);
-  if (done_logging || delta.InDays() >= 7) {
-    sync_service_->RemoveObserver(this);
-    sync_service_ = nullptr;
-
-    // If the user was eligible but didn't see the promo on the last 7 days and
-    // installed Chrome then their eligiblity pref is reset to false.
-    const bool is_eligible =
-        pref_service_->GetBoolean(prefs::kDesktopIOSPromotionEligible);
-    if (delta.InDays() >= 7 && is_eligible)
-      pref_service_->SetBoolean(prefs::kDesktopIOSPromotionEligible, false);
-    return;
-  }
-
-  // This user have seen the promotion in the last 7 days so it may be a
-  // reason of the installation.
-  const int sms_entrypoint =
-      pref_service_->GetInteger(prefs::kDesktopIOSPromotionSMSEntryPoint);
-  const int shown_entrypoints =
-      pref_service_->GetInteger(prefs::kDesktopIOSPromotionShownEntryPoints);
-
-  // Entry points are represented on the preference by integers [1..4].
-  // Entry points constants are defined on:
-  // chrome/browser/ui/desktop_ios_promotion/desktop_ios_promotion_util.h
-  const int entrypoint_prefixes_count =
-      base::size(kDesktopIOSPromotionEntrypointHistogramPrefix);
-  for (int i = 1; i < entrypoint_prefixes_count + 1; i++) {
-    // Note this fakes an enum UMA using an exact linear UMA, since the enum is
-    // a modification of another enum, but isn't defined directly.
-    if (sms_entrypoint == i) {
-      UMA_HISTOGRAM_EXACT_LINEAR("DesktopIOSPromotion.SMSSent.IOSSigninReason",
-                                 i, entrypoint_prefixes_count + 1);
-      // If the time delta is negative due to client bad clock we log 0 instead.
-      base::Histogram::FactoryGet(
-          base::StringPrintf(
-              "DesktopIOSPromotion.%s.SMSToSigninTime",
-              kDesktopIOSPromotionEntrypointHistogramPrefix[i - 1]),
-          1, 168, 24, base::Histogram::kUmaTargetedHistogramFlag)
-          ->Add(std::max(0, delta.InHours()));
-    } else {
-      // If the user saw this promotion type, log that it could be a reason
-      // for the signin.
-      if ((1 << i) & shown_entrypoints)
-        UMA_HISTOGRAM_EXACT_LINEAR("DesktopIOSPromotion.NoSMS.IOSSigninReason",
-                                   i, entrypoint_prefixes_count + 1);
-    }
-  }
-
-  // Check the variation id preference, if it's set then log to UMA that the
-  // user has seen this promotion variation on desktop.
-  int promo_variation_id =
-      pref_service_->GetInteger(prefs::kDesktopIOSPromotionVariationId);
-  if (promo_variation_id != 0) {
-    if (sms_entrypoint != 0) {
-      base::UmaHistogramSparse(
-          "DesktopIOSPromotion.SMSSent.VariationSigninReason",
-          promo_variation_id);
-    } else {
-      base::UmaHistogramSparse(
-          "DesktopIOSPromotion.NoSMS.VariationSigninReason",
-          promo_variation_id);
-    }
-  }
-
-  pref_service_->SetBoolean(prefs::kDesktopIOSPromotionDone, true);
-  sync_service_->RemoveObserver(this);
-  sync_service_ = nullptr;
-}
-
-// static
-void DesktopPromotionSyncService::RegisterDesktopPromotionUserPrefs(
-    user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterBooleanPref(
-      prefs::kDesktopIOSPromotionEligible, false,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterIntegerPref(
-      prefs::kDesktopIOSPromotionSMSEntryPoint, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterIntegerPref(
-      prefs::kDesktopIOSPromotionShownEntryPoints, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterDoublePref(
-      prefs::kDesktopIOSPromotionLastImpression, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterBooleanPref(
-      prefs::kDesktopIOSPromotionDone, false,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-  registry->RegisterIntegerPref(
-      prefs::kDesktopIOSPromotionVariationId, 0,
-      user_prefs::PrefRegistrySyncable::SYNCABLE_PRIORITY_PREF);
-}
diff --git a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.h b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.h
deleted file mode 100644
index 669f5fce..0000000
--- a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_DESKTOP_PROMOTION_DESKTOP_PROMOTION_SYNC_SERVICE_H
-#define CHROME_BROWSER_DESKTOP_PROMOTION_DESKTOP_PROMOTION_SYNC_SERVICE_H
-
-#include "base/macros.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/sync/driver/sync_service_observer.h"
-
-class PrefService;
-
-namespace syncer {
-class SyncService;
-}
-
-namespace user_prefs {
-class PrefRegistrySyncable;
-}
-
-// This class is responsible for observing the SyncService. Once the
-// priority preferences are synced, it will check the desktop promotion
-// pref and if eligible it will log the desktop promotion metrics to
-// uma and mark the promotion cycle as completed in a pref.
-class DesktopPromotionSyncService : public KeyedService,
-                                    public syncer::SyncServiceObserver {
- public:
-  // Only the DesktopPromotionSyncServiceFactory and tests should call this.
-  DesktopPromotionSyncService(PrefService* pref_service,
-                              syncer::SyncService* sync_service);
-
-  ~DesktopPromotionSyncService() override;
-
-  // KeyedService implementation.
-  void Shutdown() override;
-
-  // syncer::SyncServiceObserver implementation.
-  void OnStateChanged(syncer::SyncService* sync) override;
-
-  // Register profile specific desktop promotion related preferences.
-  static void RegisterDesktopPromotionUserPrefs(
-      user_prefs::PrefRegistrySyncable* registry);
-
- private:
-  PrefService* pref_service_ = nullptr;
-  syncer::SyncService* sync_service_ = nullptr;
-  bool desktop_metrics_logger_initiated_ = false;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopPromotionSyncService);
-};
-
-#endif  // CHROME_BROWSER_DESKTOP_PROMOTION_DESKTOP_PROMOTION_SYNC_SERVICE_H
diff --git a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.cc b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.cc
deleted file mode 100644
index 27c2d16..0000000
--- a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h"
-
-#include <memory>
-
-#include "components/browser_sync/profile_sync_service.h"
-#include "components/keyed_service/ios/browser_state_dependency_manager.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.h"
-#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
-
-// static
-DesktopPromotionSyncService*
-DesktopPromotionSyncServiceFactory::GetForBrowserState(
-    ios::ChromeBrowserState* browser_state) {
-  return static_cast<DesktopPromotionSyncService*>(
-      GetInstance()->GetServiceForBrowserState(browser_state, true));
-}
-
-// static
-DesktopPromotionSyncServiceFactory*
-DesktopPromotionSyncServiceFactory::GetInstance() {
-  return base::Singleton<DesktopPromotionSyncServiceFactory>::get();
-}
-
-DesktopPromotionSyncServiceFactory::DesktopPromotionSyncServiceFactory()
-    : BrowserStateKeyedServiceFactory(
-          "DesktopPromotionSyncService",
-          BrowserStateDependencyManager::GetInstance()) {
-  DependsOn(ProfileSyncServiceFactory::GetInstance());
-}
-
-DesktopPromotionSyncServiceFactory::~DesktopPromotionSyncServiceFactory() =
-    default;
-
-std::unique_ptr<KeyedService>
-DesktopPromotionSyncServiceFactory::BuildServiceInstanceFor(
-    web::BrowserState* context) const {
-  ios::ChromeBrowserState* browser_state =
-      ios::ChromeBrowserState::FromBrowserState(context);
-  return std::make_unique<DesktopPromotionSyncService>(
-      browser_state->GetPrefs(),
-      ProfileSyncServiceFactory::GetForBrowserState(browser_state));
-}
-
-bool DesktopPromotionSyncServiceFactory::ServiceIsNULLWhileTesting() const {
-  return true;
-}
diff --git a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h b/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h
deleted file mode 100644
index a096bc94..0000000
--- a/ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service_factory.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_SIGNIN_DESKTOP_PROMOTION_SYNC_SERVICE_FACTORY_H_
-#define IOS_CHROME_BROWSER_SIGNIN_DESKTOP_PROMOTION_SYNC_SERVICE_FACTORY_H_
-
-#include "base/macros.h"
-#include "base/memory/singleton.h"
-#include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
-
-namespace ios {
-class ChromeBrowserState;
-}
-
-class DesktopPromotionSyncService;
-
-// Singleton that owns all DesktopPromotionSyncService and associates them with
-// ios::ChromeBrowserState.
-class DesktopPromotionSyncServiceFactory
-    : public BrowserStateKeyedServiceFactory {
- public:
-  static DesktopPromotionSyncService* GetForBrowserState(
-      ios::ChromeBrowserState* browser_state);
-  static DesktopPromotionSyncServiceFactory* GetInstance();
-
- private:
-  friend struct base::DefaultSingletonTraits<
-      DesktopPromotionSyncServiceFactory>;
-
-  DesktopPromotionSyncServiceFactory();
-  ~DesktopPromotionSyncServiceFactory() override;
-
-  // BrowserStateKeyedServiceFactory implementation.
-  std::unique_ptr<KeyedService> BuildServiceInstanceFor(
-      web::BrowserState* context) const override;
-  bool ServiceIsNULLWhileTesting() const override;
-
-  DISALLOW_COPY_AND_ASSIGN(DesktopPromotionSyncServiceFactory);
-};
-
-#endif  // IOS_CHROME_BROWSER_SIGNIN_DESKTOP_PROMOTION_SYNC_SERVICE_FACTORY_H_
diff --git a/ios/chrome/browser/payments/BUILD.gn b/ios/chrome/browser/payments/BUILD.gn
index 588b77ad..51231dc 100644
--- a/ios/chrome/browser/payments/BUILD.gn
+++ b/ios/chrome/browser/payments/BUILD.gn
@@ -48,6 +48,7 @@
     "//ios/chrome/browser/signin",
     "//ios/web",
     "//net",
+    "//services/identity/public/cpp:cpp",
     "//ui/base",
     "//url",
   ]
@@ -121,5 +122,6 @@
     "//ios/web",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
+    "//services/identity/public/cpp:cpp",
   ]
 }
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index d0849e2..a80bed1 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -26,7 +26,6 @@
 #include "components/payments/core/payment_shipping_option.h"
 #include "components/payments/core/web_payment_request.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/autofill/address_normalizer_factory.h"
 #include "ios/chrome/browser/autofill/validation_rules_storage_factory.h"
@@ -34,8 +33,9 @@
 #import "ios/chrome/browser/metrics/ukm_url_recorder.h"
 #import "ios/chrome/browser/payments/ios_payment_instrument.h"
 #import "ios/chrome/browser/payments/payment_request_util.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/web/public/web_state/web_state.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
@@ -179,10 +179,10 @@
 }
 
 std::string PaymentRequest::GetAuthenticatedEmail() const {
-  const SigninManager* signin_manager =
-      ios::SigninManagerFactory::GetForBrowserStateIfExists(browser_state_);
-  if (signin_manager && signin_manager->IsAuthenticated())
-    return signin_manager->GetAuthenticatedAccountInfo().email;
+  const identity::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForBrowserStateIfExists(browser_state_);
+  if (identity_manager && identity_manager->HasPrimaryAccount())
+    return identity_manager->GetPrimaryAccountInfo().email;
   else
     return std::string();
 }
diff --git a/ios/chrome/browser/payments/payment_request_unittest_base.h b/ios/chrome/browser/payments/payment_request_unittest_base.h
index 9e66b84..baa3f42 100644
--- a/ios/chrome/browser/payments/payment_request_unittest_base.h
+++ b/ios/chrome/browser/payments/payment_request_unittest_base.h
@@ -25,7 +25,11 @@
 class CreditCard;
 }  // namespace autofill
 
-class SigninManager;
+namespace identity {
+class IdentityTestEnvironment;
+}  // namespace identity
+
+class IdentityTestEnvironmentChromeBrowserStateAdaptor;
 
 // Base class for various payment request related unit tests. This purposely
 // does not inherit from PlatformTest (testing::Test) so that it can be used
@@ -45,7 +49,7 @@
   void AddAutofillProfile(const autofill::AutofillProfile& profile);
   void AddCreditCard(const autofill::CreditCard& card);
 
-  SigninManager* GetSigninManager();
+  identity::IdentityTestEnvironment* identity_test_env();
 
   payments::TestPaymentRequest* payment_request() {
     return payment_request_.get();
@@ -72,6 +76,8 @@
   autofill::TestPersonalDataManager personal_data_manager_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<payments::TestPaymentRequest> payment_request_;
+  std::unique_ptr<IdentityTestEnvironmentChromeBrowserStateAdaptor>
+      identity_test_env_adaptor_;
 };
 
 #endif  // IOS_CHROME_BROWSER_PAYMENTS_PAYMENT_REQUEST_UNITTEST_BASE_H_
diff --git a/ios/chrome/browser/payments/payment_request_unittest_base.mm b/ios/chrome/browser/payments/payment_request_unittest_base.mm
index 9523e23..32d0544 100644
--- a/ios/chrome/browser/payments/payment_request_unittest_base.mm
+++ b/ios/chrome/browser/payments/payment_request_unittest_base.mm
@@ -6,10 +6,9 @@
 
 #include "components/payments/core/payment_prefs.h"
 #include "components/payments/core/payments_test_util.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "ios/chrome/browser/payments/payment_request_test_util.h"
-#include "ios/chrome/browser/signin/fake_signin_manager_builder.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -21,13 +20,14 @@
 PaymentRequestUnitTestBase::~PaymentRequestUnitTestBase() {}
 
 void PaymentRequestUnitTestBase::DoSetUp() {
-  TestChromeBrowserState::Builder test_cbs_builder;
-  test_cbs_builder.AddTestingFactory(
-      ios::SigninManagerFactory::GetInstance(),
-      base::BindRepeating(&ios::BuildFakeSigninManager));
-  chrome_browser_state_ = test_cbs_builder.Build();
+  chrome_browser_state_ = IdentityTestEnvironmentChromeBrowserStateAdaptor::
+      CreateChromeBrowserStateForIdentityTestEnvironment();
   web_state_.SetBrowserState(chrome_browser_state_.get());
   personal_data_manager_.SetPrefService(pref_service_.get());
+
+  identity_test_env_adaptor_ =
+      std::make_unique<IdentityTestEnvironmentChromeBrowserStateAdaptor>(
+          chrome_browser_state_.get());
 }
 
 void PaymentRequestUnitTestBase::DoTearDown() {
@@ -51,7 +51,7 @@
   personal_data_manager_.AddCreditCard(card);
 }
 
-SigninManager* PaymentRequestUnitTestBase::GetSigninManager() {
-  return ios::SigninManagerFactory::GetForBrowserState(
-      chrome_browser_state_.get());
+identity::IdentityTestEnvironment*
+PaymentRequestUnitTestBase::identity_test_env() {
+  return identity_test_env_adaptor_->identity_test_env();
 }
diff --git a/ios/chrome/browser/pref_names.cc b/ios/chrome/browser/pref_names.cc
index ce3407d..4800e4c 100644
--- a/ios/chrome/browser/pref_names.cc
+++ b/ios/chrome/browser/pref_names.cc
@@ -148,33 +148,4 @@
 const char kOmniboxGeolocationLastAuthorizationAlertVersion[] =
     "ios.omnibox.geolocation_last_authorization_alert_version";
 
-// Index of the entry point that initiated sending the SMS to the user for the
-// "desktop to iOS" promotion (see DesktopIOSPromotion.IOSSigninReason histogram
-// for details).
-const char kDesktopIOSPromotionSMSEntryPoint[] =
-    "ios.desktop_ios_promo_sms_entrypoint";
-
-// Indexes of the entry points presented to the user for "desktop to iOS"
-// promotion
-const char kDesktopIOSPromotionShownEntryPoints[] =
-    "ios.desktop_ios_promo_shown_entrypoints";
-
-// Timestamp of the last "desktop to iOS" promotion SMS dispatch or of the last
-// impression (if no SMS was sent).
-const char kDesktopIOSPromotionLastImpression[] =
-    "ios.desktop_ios_promo_last_impression";
-
-// True if the "desktop to iOS" promotion was successful, i.e. user installed
-// the application and signed in after seeing the promotion and receiving the
-// SMS.
-const char kDesktopIOSPromotionDone[] = "ios.desktop_ios_promo_done";
-
-// True if the user is eligible to receive "desktop to iOS" promotion.
-const char kDesktopIOSPromotionEligible[] = "ios.desktoptomobileeligible";
-
-// Integer that represents which variation of title and text of the
-// "desktop to iOS" promotion was presented to the user on desktop.
-const char kDesktopIOSPromotionVariationId[] =
-    "ios.desktop_ios_promo_variation_id";
-
 }  // namespace prefs
diff --git a/ios/chrome/browser/pref_names.h b/ios/chrome/browser/pref_names.h
index 332f7c50..3e009738 100644
--- a/ios/chrome/browser/pref_names.h
+++ b/ios/chrome/browser/pref_names.h
@@ -49,13 +49,6 @@
 extern const char kOmniboxGeolocationAuthorizationState[];
 extern const char kOmniboxGeolocationLastAuthorizationAlertVersion[];
 
-extern const char kDesktopIOSPromotionSMSEntryPoint[];
-extern const char kDesktopIOSPromotionShownEntryPoints[];
-extern const char kDesktopIOSPromotionLastImpression[];
-extern const char kDesktopIOSPromotionDone[];
-extern const char kDesktopIOSPromotionEligible[];
-extern const char kDesktopIOSPromotionVariationId[];
-
 }  // namespace prefs
 
 #endif  // IOS_CHROME_BROWSER_PREF_NAMES_H_
diff --git a/ios/chrome/browser/prefs/BUILD.gn b/ios/chrome/browser/prefs/BUILD.gn
index 9f414ac..fffd7eb61 100644
--- a/ios/chrome/browser/prefs/BUILD.gn
+++ b/ios/chrome/browser/prefs/BUILD.gn
@@ -62,7 +62,6 @@
     "//components/web_resource",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
-    "//ios/chrome/browser/desktop_promotion",
     "//ios/chrome/browser/first_run",
     "//ios/chrome/browser/geolocation",
     "//ios/chrome/browser/memory",
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm
index 7fb8049..49f7874 100644
--- a/ios/chrome/browser/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -42,7 +42,6 @@
 #include "components/variations/service/variations_service.h"
 #include "components/web_resource/web_resource_pref_names.h"
 #include "ios/chrome/browser/browser_state/browser_state_info_cache.h"
-#include "ios/chrome/browser/desktop_promotion/desktop_promotion_sync_service.h"
 #include "ios/chrome/browser/first_run/first_run.h"
 #import "ios/chrome/browser/geolocation/omnibox_geolocation_local_state.h"
 #import "ios/chrome/browser/memory/memory_debugger_manager.h"
@@ -103,7 +102,6 @@
 
 void RegisterBrowserStatePrefs(user_prefs::PrefRegistrySyncable* registry) {
   autofill::prefs::RegisterProfilePrefs(registry);
-  DesktopPromotionSyncService::RegisterDesktopPromotionUserPrefs(registry);
   dom_distiller::DistilledPagePrefs::RegisterProfilePrefs(registry);
   FirstRun::RegisterProfilePrefs(registry);
   gcm::GCMChannelStatusSyncer::RegisterProfilePrefs(registry);
diff --git a/ios/chrome/browser/signin/identity_manager_factory.cc b/ios/chrome/browser/signin/identity_manager_factory.cc
index 2fd138d..3cb2c60 100644
--- a/ios/chrome/browser/signin/identity_manager_factory.cc
+++ b/ios/chrome/browser/signin/identity_manager_factory.cc
@@ -62,6 +62,13 @@
 }
 
 // static
+identity::IdentityManager* IdentityManagerFactory::GetForBrowserStateIfExists(
+    ios::ChromeBrowserState* browser_state) {
+  return static_cast<IdentityManagerWrapper*>(
+      GetInstance()->GetServiceForBrowserState(browser_state, false));
+}
+
+// static
 IdentityManagerFactory* IdentityManagerFactory::GetInstance() {
   return base::Singleton<IdentityManagerFactory>::get();
 }
diff --git a/ios/chrome/browser/signin/identity_manager_factory.h b/ios/chrome/browser/signin/identity_manager_factory.h
index b4b6916..1e0f765 100644
--- a/ios/chrome/browser/signin/identity_manager_factory.h
+++ b/ios/chrome/browser/signin/identity_manager_factory.h
@@ -22,6 +22,8 @@
  public:
   static identity::IdentityManager* GetForBrowserState(
       ios::ChromeBrowserState* browser_state);
+  static identity::IdentityManager* GetForBrowserStateIfExists(
+      ios::ChromeBrowserState* browser_state);
 
   // Returns an instance of the IdentityManagerFactory singleton.
   static IdentityManagerFactory* GetInstance();
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index 8120ba5f..3f6256d5 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -59,13 +59,12 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/material_components",
     "//ios/chrome/browser/ui/signin_interaction/public",
-    "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/unified_consent",
     "//ios/chrome/common",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/images",
     "//ios/public/provider/chrome/browser/signin",
-    "//ios/third_party/material_components_ios",
+    "//services/identity/public/cpp:cpp",
     "//ui/base",
     "//ui/gfx",
     "//url",
@@ -169,7 +168,6 @@
     "//ios/chrome/browser/unified_consent",
     "//ios/chrome/test:test_support",
     "//ios/public/provider/chrome/browser/signin:test_support",
-    "//ios/third_party/material_components_ios",
     "//ios/web/public/test",
     "//testing/gtest",
     "//third_party/ocmock",
@@ -203,5 +201,6 @@
     "//ios/public/provider/chrome/browser/signin",
     "//ios/public/provider/chrome/browser/signin:test_support",
     "//ios/third_party/earl_grey:earl_grey+link",
+    "//services/identity/public/cpp:cpp",
   ]
 }
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
index 1c81248b..790f8a7e 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
@@ -15,7 +15,6 @@
 #include "base/timer/timer.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/strings/grit/components_strings.h"
 #include "google_apis/gaia/gaia_auth_util.h"
@@ -25,7 +24,7 @@
 #include "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
 #import "ios/chrome/browser/signin/constants.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #include "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
@@ -37,6 +36,7 @@
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -252,8 +252,8 @@
   if (AuthenticationServiceFactory::GetForBrowserState(browserState)
           ->IsAuthenticatedIdentityManaged()) {
     NSString* hostedDomain = base::SysUTF8ToNSString(
-        ios::SigninManagerFactory::GetForBrowserState(browserState)
-            ->GetAuthenticatedAccountInfo()
+        IdentityManagerFactory::GetForBrowserState(browserState)
+            ->GetPrimaryAccountInfo()
             .hosted_domain);
     [self promptSwitchFromManagedEmail:lastSignedInEmail
                       withHostedDomain:hostedDomain
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
index 60b267cb..25daae7 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
@@ -8,11 +8,11 @@
 
 #include "base/strings/sys_string_conversions.h"
 #include "components/signin/core/browser/account_info.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -47,9 +47,8 @@
 
   ios::ChromeBrowserState* browser_state =
       chrome_test_util::GetOriginalBrowserState();
-  AccountInfo info =
-      ios::SigninManagerFactory::GetForBrowserState(browser_state)
-          ->GetAuthenticatedAccountInfo();
+  AccountInfo info = IdentityManagerFactory::GetForBrowserState(browser_state)
+                         ->GetPrimaryAccountInfo();
 
   GREYAssertEqual(base::SysNSStringToUTF8(identity.gaiaID), info.gaia,
                   @"Unexpected Gaia ID of the signed in user [expected = "
@@ -65,8 +64,8 @@
 
   ios::ChromeBrowserState* browser_state =
       chrome_test_util::GetOriginalBrowserState();
-  GREYAssertFalse(ios::SigninManagerFactory::GetForBrowserState(browser_state)
-                      ->IsAuthenticated(),
+  GREYAssertFalse(IdentityManagerFactory::GetForBrowserState(browser_state)
+                      ->HasPrimaryAccount(),
                   @"Unexpected signed in user");
 }
 
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_item.mm b/ios/chrome/browser/ui/authentication/signin_promo_item.mm
index 8a35861..8a690fdf6 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_item.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_item.mm
@@ -61,9 +61,7 @@
   self = [super initWithFrame:frame];
   if (self) {
     UIView* contentView = self.contentView;
-    _signinPromoView =
-        [[SigninPromoView alloc] initWithFrame:self.bounds
-                                         style:SigninPromoViewUIRefresh];
+    _signinPromoView = [[SigninPromoView alloc] initWithFrame:self.bounds];
     _signinPromoView.translatesAutoresizingMaskIntoConstraints = NO;
     [contentView addSubview:_signinPromoView];
 
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view.h b/ios/chrome/browser/ui/authentication/signin_promo_view.h
index f21dc72..e6f7b16 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view.h
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view.h
@@ -9,18 +9,8 @@
 
 #include "components/signin/core/browser/signin_metrics.h"
 
-@class MDCFlatButton;
 @protocol SigninPromoViewDelegate;
 
-typedef NS_ENUM(NSInteger, SigninPromoViewUI) {
-  // The legacy SigninPromoViewUI style.
-  SigninPromoViewUILegacy,
-  // The SigninPromoViewRefreshUI style. In order for this value to take effect
-  // the UIRefreshPhase1 flag also needs to be enabled. Otherwise
-  // SigninPromoViewUILegacy style will still be used.
-  SigninPromoViewUIRefresh,
-};
-
 typedef NS_ENUM(NSInteger, SigninPromoViewMode) {
   // No identity available on the device.
   SigninPromoViewModeColdState,
@@ -62,12 +52,8 @@
 // |textLabel|.
 @property(nonatomic, readonly) CGFloat horizontalPadding;
 
-// Designated initializer. |signinPromoViewUI| sets the style for the
-// SigninPromoView to legacy or refreshed. This same view is used on Collections
-// and Settings, while the Collections UI will be refreshed soon, we still need
-// to support the legacy UI for settings.
+// Designated initializer.
 - (instancetype)initWithFrame:(CGRect)frame
-                        style:(SigninPromoViewUI)signinPromoViewUI
     NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view.mm b/ios/chrome/browser/ui/authentication/signin_promo_view.mm
index aeac138f..21f63f5 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view.mm
@@ -8,13 +8,10 @@
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view_delegate.h"
 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
 #include "ios/chrome/grit/ios_strings.h"
-#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -24,20 +21,8 @@
 namespace {
 // Horizontal padding for label and buttons.
 const CGFloat kHorizontalPadding = 40;
-// Spacing within stackView.
-const CGFloat kLegacySubViewVerticalSpacing = 14;
-// StackView vertical padding.
-const CGFloat kLegacyStackViewVerticalPadding = 20.0;
-// StackView horizontal padding.
-const CGFloat kLegacyStackViewHorizontalPadding = 15.0;
-// Vertical padding for buttons.
-const CGFloat kButtonVerticalPadding = 10;
 // Image size for warm state.
 const CGFloat kProfileImageFixedSize = 48;
-// Size for the close button width and height.
-const CGFloat kCloseButtonSize = 24;
-// Padding for the close button.
-const CGFloat kCloseButtonPadding = 8;
 
 // UI Refresh Constants:
 // Text label gray color.
@@ -77,7 +62,6 @@
 @property(nonatomic, readwrite) UIButton* primaryButton;
 @property(nonatomic, readwrite) UIButton* secondaryButton;
 @property(nonatomic, readwrite) UIButton* closeButton;
-@property(nonatomic, assign) BOOL loadRefreshUI;
 @end
 
 @implementation SigninPromoView {
@@ -91,15 +75,10 @@
 @synthesize primaryButton = _primaryButton;
 @synthesize secondaryButton = _secondaryButton;
 @synthesize closeButton = _closeButton;
-@synthesize loadRefreshUI = _loadRefreshUI;
 
-- (instancetype)initWithFrame:(CGRect)frame
-                        style:(SigninPromoViewUI)signinPromoViewUI {
+- (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
-    _loadRefreshUI = (IsUIRefreshPhase1Enabled() &&
-                      signinPromoViewUI == SigninPromoViewUIRefresh);
-
     // Set the whole element as accessible to take advantage of the
     // accessibilityCustomActions.
     self.isAccessibilityElement = YES;
@@ -117,40 +96,23 @@
     _textLabel.translatesAutoresizingMaskIntoConstraints = NO;
     _textLabel.numberOfLines = 0;
     _textLabel.textAlignment = NSTextAlignmentCenter;
-    if (_loadRefreshUI) {
-      _textLabel.lineBreakMode = NSLineBreakByWordWrapping;
-      _textLabel.font =
-          [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
-      _textLabel.textColor = UIColorFromRGB(kGrayHexColor);
-    } else {
-      _textLabel.font = [MDCTypography buttonFont];
-      _textLabel.textColor = [[MDCPalette greyPalette] tint900];
-    }
+    _textLabel.lineBreakMode = NSLineBreakByWordWrapping;
+    _textLabel.font =
+        [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+    _textLabel.textColor = UIColorFromRGB(kGrayHexColor);
 
     // Create and setup primary button.
     UIButton* primaryButton;
     UIEdgeInsets primaryButtonInsets;
-    if (_loadRefreshUI) {
-      primaryButton = [[UIButton alloc] init];
-      primaryButton.backgroundColor = UIColorFromRGB(kBlueHexColor);
-      [primaryButton.titleLabel
-          setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]];
-      primaryButton.layer.cornerRadius = kButtonCornerRadius;
-      primaryButton.clipsToBounds = YES;
-      primaryButtonInsets = UIEdgeInsetsMake(
-          kButtonTitleVerticalContentInset, kButtonTitleHorizontalContentInset,
-          kButtonTitleVerticalContentInset, kButtonTitleHorizontalContentInset);
-    } else {
-      primaryButton = [[MDCFlatButton alloc] init];
-      MDCFlatButton* materialButton =
-          base::mac::ObjCCastStrict<MDCFlatButton>(primaryButton);
-      [materialButton setBackgroundColor:[[MDCPalette cr_bluePalette] tint500]
-                                forState:UIControlStateNormal];
-      materialButton.inkColor = [UIColor colorWithWhite:1 alpha:0.2];
-      primaryButtonInsets =
-          UIEdgeInsetsMake(kButtonVerticalPadding, kHorizontalPadding,
-                           kButtonVerticalPadding, kHorizontalPadding);
-    }
+    primaryButton = [[UIButton alloc] init];
+    primaryButton.backgroundColor = UIColorFromRGB(kBlueHexColor);
+    [primaryButton.titleLabel
+        setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]];
+    primaryButton.layer.cornerRadius = kButtonCornerRadius;
+    primaryButton.clipsToBounds = YES;
+    primaryButtonInsets = UIEdgeInsetsMake(
+        kButtonTitleVerticalContentInset, kButtonTitleHorizontalContentInset,
+        kButtonTitleVerticalContentInset, kButtonTitleHorizontalContentInset);
     _primaryButton = primaryButton;
     DCHECK(_primaryButton);
     _primaryButton.accessibilityIdentifier = kSigninPromoPrimaryButtonId;
@@ -165,21 +127,11 @@
 
     // Create and setup seconday button.
     UIButton* secondaryButton;
-    if (_loadRefreshUI) {
-      secondaryButton = [[UIButton alloc] init];
-      [secondaryButton.titleLabel
-          setFont:[UIFont
-                      preferredFontForTextStyle:UIFontTextStyleSubheadline]];
-      [secondaryButton setTitleColor:UIColorFromRGB(kBlueHexColor)
-                            forState:UIControlStateNormal];
-    } else {
-      secondaryButton = [[MDCFlatButton alloc] init];
-      MDCFlatButton* materialButton =
-          base::mac::ObjCCastStrict<MDCFlatButton>(secondaryButton);
-      materialButton.uppercaseTitle = NO;
-      [materialButton setTitleColor:[[MDCPalette cr_bluePalette] tint500]
-                           forState:UIControlStateNormal];
-    }
+    secondaryButton = [[UIButton alloc] init];
+    [secondaryButton.titleLabel
+        setFont:[UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]];
+    [secondaryButton setTitleColor:UIColorFromRGB(kBlueHexColor)
+                          forState:UIControlStateNormal];
     _secondaryButton = secondaryButton;
     DCHECK(_secondaryButton);
     _secondaryButton.translatesAutoresizingMaskIntoConstraints = NO;
@@ -196,8 +148,7 @@
     verticalStackView.alignment = UIStackViewAlignmentCenter;
     verticalStackView.axis = UILayoutConstraintAxisVertical;
     verticalStackView.translatesAutoresizingMaskIntoConstraints = NO;
-    verticalStackView.spacing = _loadRefreshUI ? kStackViewSubViewSpacing
-                                               : kLegacySubViewVerticalSpacing;
+    verticalStackView.spacing = kStackViewSubViewSpacing;
     [self addSubview:verticalStackView];
 
     // Create close button and adds it directly to self.
@@ -212,65 +163,31 @@
     _closeButton.hidden = YES;
     [self addSubview:_closeButton];
 
-    // Add legacys or UIRefresh constraints for the stackView.
-    if (_loadRefreshUI) {
-      [NSLayoutConstraint activateConstraints:@[
-        [verticalStackView.leadingAnchor
-            constraintEqualToAnchor:self.leadingAnchor
-                           constant:kStackViewHorizontalPadding],
-        [verticalStackView.trailingAnchor
-            constraintEqualToAnchor:self.trailingAnchor
-                           constant:-kStackViewHorizontalPadding],
-        [verticalStackView.topAnchor
-            constraintEqualToAnchor:self.topAnchor
-                           constant:kStackViewVerticalPadding],
-        [verticalStackView.bottomAnchor
-            constraintEqualToAnchor:self.bottomAnchor
-                           constant:-kStackViewVerticalPadding],
-        [_imageView.heightAnchor
-            constraintEqualToConstant:kImageViewWidthHeight],
-        [_imageView.widthAnchor
-            constraintEqualToConstant:kImageViewWidthHeight],
-        // Close button constraints.
-        [_closeButton.topAnchor constraintEqualToAnchor:self.topAnchor],
-        [_closeButton.trailingAnchor
-            constraintEqualToAnchor:self.trailingAnchor
-                           constant:kCloseButtonTrailingMargin],
-        [_closeButton.heightAnchor
-            constraintEqualToConstant:kCloseButtonWidthHeight],
-        [_closeButton.widthAnchor
-            constraintEqualToConstant:kCloseButtonWidthHeight],
-      ]];
-    } else {
-      [NSLayoutConstraint activateConstraints:@[
-        [verticalStackView.leadingAnchor
-            constraintEqualToAnchor:self.leadingAnchor
-                           constant:kLegacyStackViewHorizontalPadding],
-        [verticalStackView.trailingAnchor
-            constraintEqualToAnchor:self.trailingAnchor
-                           constant:-kLegacyStackViewHorizontalPadding],
-        [verticalStackView.topAnchor
-            constraintEqualToAnchor:self.topAnchor
-                           constant:kLegacyStackViewVerticalPadding],
-        [verticalStackView.bottomAnchor
-            constraintEqualToAnchor:self.bottomAnchor
-                           constant:-kLegacyStackViewVerticalPadding],
-        // Close button constraints.
-        [_closeButton.topAnchor constraintEqualToAnchor:self.topAnchor
-                                               constant:kCloseButtonPadding],
-        [_closeButton.trailingAnchor
-            constraintEqualToAnchor:self.trailingAnchor
-                           constant:-kCloseButtonPadding],
-        [_closeButton.heightAnchor constraintEqualToConstant:kCloseButtonSize],
-        [_closeButton.widthAnchor constraintEqualToConstant:kCloseButtonSize],
-        [_primaryButton.leadingAnchor
-            constraintEqualToAnchor:verticalStackView.leadingAnchor
-                           constant:kStackViewHorizontalPadding],
-        [_primaryButton.trailingAnchor
-            constraintEqualToAnchor:verticalStackView.trailingAnchor
-                           constant:-kStackViewHorizontalPadding],
-      ]];
-    }
+    [NSLayoutConstraint activateConstraints:@[
+      [verticalStackView.leadingAnchor
+          constraintEqualToAnchor:self.leadingAnchor
+                         constant:kStackViewHorizontalPadding],
+      [verticalStackView.trailingAnchor
+          constraintEqualToAnchor:self.trailingAnchor
+                         constant:-kStackViewHorizontalPadding],
+      [verticalStackView.topAnchor
+          constraintEqualToAnchor:self.topAnchor
+                         constant:kStackViewVerticalPadding],
+      [verticalStackView.bottomAnchor
+          constraintEqualToAnchor:self.bottomAnchor
+                         constant:-kStackViewVerticalPadding],
+      [_imageView.heightAnchor constraintEqualToConstant:kImageViewWidthHeight],
+      [_imageView.widthAnchor constraintEqualToConstant:kImageViewWidthHeight],
+      // Close button constraints.
+      [_closeButton.topAnchor constraintEqualToAnchor:self.topAnchor],
+      [_closeButton.trailingAnchor
+          constraintEqualToAnchor:self.trailingAnchor
+                         constant:kCloseButtonTrailingMargin],
+      [_closeButton.heightAnchor
+          constraintEqualToConstant:kCloseButtonWidthHeight],
+      [_closeButton.widthAnchor
+          constraintEqualToConstant:kCloseButtonWidthHeight],
+    ]];
     // Default mode.
     _mode = SigninPromoViewModeColdState;
     [self activateColdMode];
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_unittest.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_unittest.mm
index 938c3e27..c7b55fd2 100644
--- a/ios/chrome/browser/ui/authentication/signin_promo_view_unittest.mm
+++ b/ios/chrome/browser/ui/authentication/signin_promo_view_unittest.mm
@@ -17,8 +17,7 @@
 TEST_F(SigninPromoViewTest, ChromiumLogoImage) {
   UIWindow* currentWindow = [[UIApplication sharedApplication] keyWindow];
   SigninPromoView* view =
-      [[SigninPromoView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)
-                                       style:SigninPromoViewUIRefresh];
+      [[SigninPromoView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
   view.mode = SigninPromoViewModeColdState;
   [currentWindow.rootViewController.view addSubview:view];
   UIImage* chromiumLogo = view.imageView.image;
@@ -37,8 +36,7 @@
 TEST_F(SigninPromoViewTest, SecondaryButtonVisibility) {
   UIWindow* currentWindow = [[UIApplication sharedApplication] keyWindow];
   SigninPromoView* view =
-      [[SigninPromoView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)
-                                       style:SigninPromoViewUIRefresh];
+      [[SigninPromoView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
   view.mode = SigninPromoViewModeColdState;
   [currentWindow.rootViewController.view addSubview:view];
   EXPECT_TRUE(view.secondaryButton.hidden);
diff --git a/ios/chrome/browser/ui/bookmarks/cells/bookmark_table_signin_promo_cell.mm b/ios/chrome/browser/ui/bookmarks/cells/bookmark_table_signin_promo_cell.mm
index 127c834..674cf60 100644
--- a/ios/chrome/browser/ui/bookmarks/cells/bookmark_table_signin_promo_cell.mm
+++ b/ios/chrome/browser/ui/bookmarks/cells/bookmark_table_signin_promo_cell.mm
@@ -35,9 +35,7 @@
   self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
   if (self) {
     UIView* contentView = self.contentView;
-    _signinPromoView =
-        [[SigninPromoView alloc] initWithFrame:self.bounds
-                                         style:SigninPromoViewUIRefresh];
+    _signinPromoView = [[SigninPromoView alloc] initWithFrame:self.bounds];
     _signinPromoView.translatesAutoresizingMaskIntoConstraints = NO;
     [contentView addSubview:_signinPromoView];
     NSArray* visualConstraints = @[
diff --git a/ios/chrome/browser/ui/payments/BUILD.gn b/ios/chrome/browser/ui/payments/BUILD.gn
index cab28d2..124f4b4 100644
--- a/ios/chrome/browser/ui/payments/BUILD.gn
+++ b/ios/chrome/browser/ui/payments/BUILD.gn
@@ -242,6 +242,8 @@
     "//ios/web",
     "//ios/web/public/test",
     "//ios/web/public/test/fakes",
+    "//services/identity/public/cpp:cpp",
+    "//services/identity/public/cpp:test_support",
     "//testing/gmock",
     "//testing/gtest",
     "//third_party/libaddressinput:strings_grit",
diff --git a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
index 6dca456..19d570f 100644
--- a/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_mediator_unittest.mm
@@ -16,7 +16,6 @@
 #include "components/payments/core/payment_shipping_option.h"
 #include "components/payments/core/strings_util.h"
 #include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/payments/payment_request_unittest_base.h"
 #include "ios/chrome/browser/payments/payment_request_util.h"
@@ -27,6 +26,8 @@
 #import "ios/chrome/browser/ui/payments/cells/payment_method_item.h"
 #import "ios/chrome/browser/ui/payments/cells/payments_text_item.h"
 #import "ios/chrome/browser/ui/payments/cells/price_item.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
 #include "testing/platform_test.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -415,9 +416,9 @@
                              false);
 
   // Make sure the user is signed out.
-  if (GetSigninManager()->IsAuthenticated()) {
-    GetSigninManager()->SignOut(signin_metrics::SIGNOUT_TEST,
-                                signin_metrics::SignoutDelete::IGNORE_METRIC);
+  auto* identity_manager = identity_test_env()->identity_manager();
+  if (identity_manager->HasPrimaryAccount()) {
+    identity_test_env()->ClearPrimaryAccount();
   }
 
   // Footer item should be of type CollectionViewFooterItem.
@@ -430,8 +431,7 @@
                           IDS_PAYMENTS_CARD_AND_ADDRESS_SETTINGS_SIGNED_OUT)]);
 
   // Fake a signed in user.
-  GetSigninManager()->SetAuthenticatedAccountInfo("12345",
-                                                  "username@example.com");
+  identity_test_env()->SetPrimaryAccount("username@example.com");
 
   item = [mediator() footerItem];
   footer_item = base::mac::ObjCCastStrict<CollectionViewFooterItem>(item);
@@ -451,8 +451,7 @@
                           IDS_PAYMENTS_CARD_AND_ADDRESS_SETTINGS)]);
 
   // Sign the user out.
-  GetSigninManager()->SignOut(signin_metrics::SIGNOUT_TEST,
-                              signin_metrics::SignoutDelete::IGNORE_METRIC);
+  identity_test_env()->ClearPrimaryAccount();
 
   // The signed in state has no effect on the footer text if the first
   // transaction has completed.
diff --git a/ios/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
index 1c2efbb..3d560ba 100644
--- a/ios/chrome/browser/ui/recent_tabs/BUILD.gn
+++ b/ios/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -20,7 +20,6 @@
     "//base",
     "//components/browser_sync",
     "//components/sessions",
-    "//components/signin/core/browser",
     "//components/sync",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browser_state",
@@ -100,7 +99,6 @@
     "//base",
     "//components/browser_sync",
     "//components/browser_sync:test_support",
-    "//components/signin/core/browser",
     "//components/sync_sessions",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/signin",
@@ -109,6 +107,8 @@
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/test:test_support",
     "//ios/web/public/test",
+    "//services/identity/public/cpp",
+    "//services/identity/public/cpp:test_support",
     "//testing/gtest",
     "//third_party/ocmock",
   ]
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_unittest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_unittest.mm
index eee894eb..a5532eb 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_unittest.mm
@@ -10,10 +10,8 @@
 
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/browser_sync/profile_sync_service_mock.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/sync_sessions/open_tabs_ui_delegate.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
 #include "ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service.h"
@@ -23,6 +21,8 @@
 #include "ios/chrome/test/block_cleanup_test.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
+#include "services/identity/public/cpp/identity_manager.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
@@ -135,13 +135,14 @@
   void SetupSyncState(BOOL signedIn,
                       BOOL syncEnabled,
                       BOOL hasForeignSessions) {
-    SigninManager* siginManager = ios::SigninManagerFactory::GetForBrowserState(
-        chrome_browser_state_.get());
-    if (signedIn)
-      siginManager->SetAuthenticatedAccountInfo("test", "test");
-    else if (siginManager->IsAuthenticated())
-      siginManager->SignOut(signin_metrics::SIGNOUT_TEST,
-                            signin_metrics::SignoutDelete::IGNORE_METRIC);
+    if (signedIn) {
+      identity_test_env_.MakePrimaryAccountAvailable("test@test.com");
+    } else if (identity_test_env_.identity_manager()->HasPrimaryAccount()) {
+      identity_test_env_.identity_manager()->ClearPrimaryAccount(
+          identity::IdentityManager::ClearAccountTokensAction::kDefault,
+          signin_metrics::SIGNOUT_TEST,
+          signin_metrics::SignoutDelete::IGNORE_METRIC);
+    }
 
     SyncSetupServiceMock* syncSetupService = static_cast<SyncSetupServiceMock*>(
         SyncSetupServiceFactory::GetForBrowserState(
@@ -186,6 +187,7 @@
   web::TestWebThreadBundle thread_bundle_;
   GoogleServiceAuthError no_error_;
   IOSChromeScopedTestingLocalState local_state_;
+  identity::IdentityTestEnvironment identity_test_env_;
 
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<OpenTabsUIDelegateMock> open_tabs_ui_delegate_;
diff --git a/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm b/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm
index b1d3fe2..dd09d54 100644
--- a/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services_settings_egtest.mm
@@ -102,9 +102,7 @@
   [self assertPersonalizedServicesCollapsed:YES];
   [self togglePersonalizedServicesSection];
   [self assertPersonalizedServicesCollapsed:NO];
-  [[EarlGrey selectElementWithMatcher:self.scrollViewMatcher]
-      performAction:grey_scrollToContentEdgeWithStartPoint(kGREYContentEdgeTop,
-                                                           0.1f, 0.1f)];
+  [self scrollUp];
   [self togglePersonalizedServicesSection];
   [self assertPersonalizedServicesCollapsed:YES];
 }
@@ -113,9 +111,7 @@
 - (void)testToggleNonPersonalizedServices {
   [self openGoogleServicesSettings];
   [self assertNonPersonalizedServicesCollapsed:NO];
-  [[EarlGrey selectElementWithMatcher:self.scrollViewMatcher]
-      performAction:grey_scrollToContentEdgeWithStartPoint(kGREYContentEdgeTop,
-                                                           0.1f, 0.1f)];
+  [self scrollUp];
   [self toggleNonPersonalizedServicesSection];
   [self assertNonPersonalizedServicesCollapsed:YES];
   [self toggleNonPersonalizedServicesSection];
@@ -148,6 +144,34 @@
       assertWithMatcher:grey_nil()];
 }
 
+// Tests that "Activity and Interactions" switch should be disabled when the
+// "History" sync is off.
+- (void)testActivityAndInteractionsDisabledWithHistoryDisabled {
+  [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [self resetUnifiedConsent];
+  [self openGoogleServicesSettings];
+  // "Activity and Interactions" is enabled.
+  [self
+      assertSwitchCellWithTitleID:
+          IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_TEXT
+                     detailTextID:
+                         IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_DETAIL
+                          enabled:YES];
+  [self scrollUp];
+  // Turn off "History".
+  [[self cellElementInteractionWithTitleID:
+             IDS_IOS_GOOGLE_SERVICES_SETTINGS_HISTORY_TEXT
+                              detailTextID:0] performAction:grey_tap()];
+  [self scrollUp];
+  // "Activity and Interactions" is disabled.
+  [self
+      assertSwitchCellWithTitleID:
+          IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_TEXT
+                     detailTextID:
+                         IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_DETAIL
+                          enabled:NO];
+}
+
 #pragma mark - Helpers
 
 // Resets the unified consent given by the user.
@@ -156,6 +180,7 @@
   prefService->SetBoolean(kUnifiedConsentGiven, false);
 }
 
+// Opens the Google services settings.
 - (void)openGoogleServicesSettings {
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:GoogleServicesSettingsButton()];
@@ -165,6 +190,14 @@
       assertWithMatcher:grey_notNil()];
 }
 
+// Scrolls Google services settings to the top.
+- (void)scrollUp {
+  [[EarlGrey selectElementWithMatcher:self.scrollViewMatcher]
+      performAction:grey_scrollToContentEdgeWithStartPoint(kGREYContentEdgeTop,
+                                                           0.1f, 0.1f)];
+}
+
+// Toggles personalized services section.
 - (void)togglePersonalizedServicesSection {
   [[EarlGrey
       selectElementWithMatcher:
@@ -173,6 +206,7 @@
       performAction:grey_tap()];
 }
 
+// Toggles non personalized services section.
 - (void)toggleNonPersonalizedServicesSection {
   [[EarlGrey
       selectElementWithMatcher:
@@ -181,32 +215,44 @@
       performAction:grey_tap()];
 }
 
-// Returns GREYElementInteraction for a cell based on the title string ID and
-// the detail text string ID. |detailTextID| should be set to 0 if it doesn't
-// exist in the cell.
-- (GREYElementInteraction*)cellElementInteractionWithTitleID:(int)titleID
-                                                detailTextID:(int)detailTextID {
+// Returns grey matcher for a cell with |titleID| and |detailTextID|.
+- (id<GREYMatcher>)cellMatcherWithTitleID:(int)titleID
+                             detailTextID:(int)detailTextID {
   NSString* accessibilityLabel = GetNSString(titleID);
   if (detailTextID) {
     accessibilityLabel =
         [NSString stringWithFormat:@"%@, %@", accessibilityLabel,
                                    GetNSString(detailTextID)];
   }
-  id<GREYMatcher> cellMatcher =
-      grey_allOf(grey_accessibilityLabel(accessibilityLabel),
-                 grey_kindOfClass([UICollectionViewCell class]),
-                 grey_sufficientlyVisible(), nil);
+  return grey_allOf(grey_accessibilityLabel(accessibilityLabel),
+                    grey_kindOfClass([UICollectionViewCell class]),
+                    grey_sufficientlyVisible(), nil);
+}
+
+// Returns GREYElementInteraction for |matcher|, with a scroll down action.
+- (GREYElementInteraction*)elementInteractionWithGreyMatcher:
+    (id<GREYMatcher>)matcher {
   // Needs to scroll slowly to make sure to not miss a cell if it is not
   // currently on the screen. It should not be bigger than the visible part
   // of the collection view.
   const CGFloat kPixelsToScroll = 300;
   id<GREYAction> searchAction =
       grey_scrollInDirection(kGREYDirectionDown, kPixelsToScroll);
-  return [[EarlGrey selectElementWithMatcher:cellMatcher]
+  return [[EarlGrey selectElementWithMatcher:matcher]
          usingSearchAction:searchAction
       onElementWithMatcher:self.scrollViewMatcher];
 }
 
+// Returns GREYElementInteraction for a cell based on the title string ID and
+// the detail text string ID. |detailTextID| should be set to 0 if it doesn't
+// exist in the cell.
+- (GREYElementInteraction*)cellElementInteractionWithTitleID:(int)titleID
+                                                detailTextID:(int)detailTextID {
+  id<GREYMatcher> cellMatcher =
+      [self cellMatcherWithTitleID:titleID detailTextID:detailTextID];
+  return [self elementInteractionWithGreyMatcher:cellMatcher];
+}
+
 // Asserts that a cell exists, based on its title string ID and its detail text
 // string ID. |detailTextID| should be set to 0 if it doesn't exist in the cell.
 - (void)assertCellWithTitleID:(int)titleID detailTextID:(int)detailTextID {
@@ -214,11 +260,33 @@
       assertWithMatcher:grey_notNil()];
 }
 
+// Asserts that the switch is enabled/disabled inside a cell with |titleID| and
+// |detailTextID|.
+- (void)assertSwitchCellWithTitleID:(int)titleID
+                       detailTextID:(int)detailTextID
+                            enabled:(BOOL)enabled {
+  id<GREYMatcher> cellMatcher =
+      [self cellMatcherWithTitleID:titleID detailTextID:detailTextID];
+  id<GREYMatcher> enabledMatcher = grey_enabled();
+  if (!enabled) {
+    enabledMatcher = grey_not(grey_enabled());
+  }
+  id<GREYMatcher> switchMatcher =
+      grey_allOf(enabledMatcher, grey_kindOfClass([UISwitch class]),
+                 grey_ancestor(cellMatcher), nil);
+  GREYElementInteraction* element =
+      [self elementInteractionWithGreyMatcher:switchMatcher];
+  [element assertWithMatcher:grey_notNil()];
+}
+
+// Asserts that the sync everthing section cell is visible.
 - (void)assertSyncEverythingSection {
   [self assertCellWithTitleID:IDS_IOS_GOOGLE_SERVICES_SETTINGS_SYNC_EVERYTHING
                  detailTextID:0];
 }
 
+// Asserts that the personalized service section is visible and collapsed or
+// expended.
 - (void)assertPersonalizedServicesCollapsed:(BOOL)collapsed {
   [self
       assertCellWithTitleID:
@@ -259,6 +327,8 @@
   }
 }
 
+// Asserts that the non-personalized service section is visible and collapsed or
+// expended.
 - (void)assertNonPersonalizedServicesCollapsed:(BOOL)collapsed {
   [self
       assertCellWithTitleID:
diff --git a/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm
index 7f65de84..5bade72f 100644
--- a/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/google_services_settings_mediator.mm
@@ -149,6 +149,9 @@
 @property(nonatomic, strong, readonly) ItemArray personalizedItems;
 // Item for the autocomplete wallet feature.
 @property(nonatomic, strong, readonly) SyncSwitchItem* autocompleteWalletItem;
+// Item for the activity and interactions feature.
+@property(nonatomic, strong, readonly)
+    SyncSwitchItem* syncActivityAndInteractionsItem;
 // Collapsible item for the non-personalized section.
 @property(nonatomic, strong, readonly)
     SettingsCollapsibleItem* nonPersonalizedServicesItem;
@@ -180,6 +183,7 @@
 @synthesize syncPersonalizationItem = _syncPersonalizationItem;
 @synthesize personalizedItems = _personalizedItems;
 @synthesize autocompleteWalletItem = _autocompleteWalletItem;
+@synthesize syncActivityAndInteractionsItem = _syncActivityAndInteractionsItem;
 @synthesize nonPersonalizedServicesItem = _nonPersonalizedServicesItem;
 @synthesize nonPersonalizedItems = _nonPersonalizedItems;
 
@@ -382,14 +386,6 @@
                 detailStringID:0
                      commandID:GoogleServicesSettingsCommandIDToggleDataTypeSync
                       dataType:SyncSetupService::kSyncReadingList];
-    SyncSwitchItem* syncActivityAndInteractionsItem = [self
-        switchItemWithItemType:SyncActivityAndInteractionsItemType
-                  textStringID:
-                      IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_TEXT
-                detailStringID:
-                    IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_DETAIL
-                     commandID:GoogleServicesSettingsCommandIDToggleDataTypeSync
-                      dataType:SyncSetupService::kSyncUserEvent];
     CollectionViewTextItem* syncGoogleActivityControlsItem = [self
         textItemWithItemType:SyncGoogleActivityControlsItemType
                 textStringID:
@@ -417,7 +413,7 @@
     _personalizedItems = @[
       syncBookmarksItem, syncHistoryItem, syncPasswordsItem, syncOpenTabsItem,
       syncAutofillItem, syncSettingsItem, syncReadingListItem,
-      self.autocompleteWalletItem, syncActivityAndInteractionsItem,
+      self.autocompleteWalletItem, self.syncActivityAndInteractionsItem,
       syncGoogleActivityControlsItem, encryptionItem, manageSyncedDataItem
     ];
   }
@@ -438,6 +434,20 @@
   return _autocompleteWalletItem;
 }
 
+- (SyncSwitchItem*)syncActivityAndInteractionsItem {
+  if (!_syncActivityAndInteractionsItem) {
+    _syncActivityAndInteractionsItem = [self
+        switchItemWithItemType:SyncActivityAndInteractionsItemType
+                  textStringID:
+                      IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_TEXT
+                detailStringID:
+                    IDS_IOS_GOOGLE_SERVICES_SETTINGS_ACTIVITY_AND_INTERACTIONS_DETAIL
+                     commandID:GoogleServicesSettingsCommandIDToggleDataTypeSync
+                      dataType:SyncSetupService::kSyncUserEvent];
+  }
+  return _syncActivityAndInteractionsItem;
+}
+
 - (SettingsCollapsibleItem*)nonPersonalizedServicesItem {
   if (!_nonPersonalizedServicesItem) {
     _nonPersonalizedServicesItem = [self
@@ -622,6 +632,14 @@
     // Autocomplete wallet item should be disabled when autofill is off.
     self.autocompleteWalletItem.on = false;
   }
+  syncer::ModelType historyModelType =
+      _syncSetupService->GetModelType(SyncSetupService::kSyncOmniboxHistory);
+  BOOL isHistoryOn = _syncSetupService->IsDataTypePreferred(historyModelType);
+  self.syncActivityAndInteractionsItem.enabled = enabled && isHistoryOn;
+  if (!isHistoryOn) {
+    // Activity and interactions item should be disabled when history is off.
+    self.syncActivityAndInteractionsItem.on = false;
+  }
 }
 
 // Updates the non-personalized section according to the user consent.
@@ -790,6 +808,11 @@
 
 - (void)onSyncStateChanged {
   [self updatePersonalizedSection];
+  // TODO(crbug.com/899791): Should reloads only the updated items (instead of
+  // reload the full section), and get ride of
+  // |self.personalizedSectionBeingAnimated|. This will get a smoother animation
+  // for "Autocomplete wall" switch and "Sync Activity and Interactions" switch
+  // when being tapped by the user.
   if (!self.personalizedSectionBeingAnimated) {
     CollectionViewModel* model = self.consumer.collectionViewModel;
     NSMutableIndexSet* sectionIndexToReload = [NSMutableIndexSet indexSet];
@@ -797,9 +820,12 @@
                                               PersonalizedSectionIdentifier]];
     [self.consumer reloadSections:sectionIndexToReload];
   } else {
-    // Needs to reload only the autocomplete wallet item (which is part of the
-    // personalized section), if the autofill feature changed state.
+    // |self.autocompleteWalletItem| needs to be reloaded in case the autofill
+    // data type changed state.
     [self.consumer reloadItem:self.autocompleteWalletItem];
+    // |self.syncActivityAndInteractionsItem| needs to be reloaded in case
+    // the history data type changed state.
+    [self.consumer reloadItem:self.syncActivityAndInteractionsItem];
   }
   [self updateSyncErrorSectionAndNotifyConsumer:YES];
 }
diff --git a/ios/chrome/browser/ui/signin_interaction/BUILD.gn b/ios/chrome/browser/ui/signin_interaction/BUILD.gn
index ba2b5d8..0f68aef5 100644
--- a/ios/chrome/browser/ui/signin_interaction/BUILD.gn
+++ b/ios/chrome/browser/ui/signin_interaction/BUILD.gn
@@ -25,6 +25,7 @@
     "//ios/chrome/browser/unified_consent",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/signin",
+    "//services/identity/public/cpp:cpp",
   ]
 }
 
diff --git a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller.mm b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller.mm
index 97eaeab..cc24dc8b 100644
--- a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller.mm
+++ b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller.mm
@@ -9,13 +9,12 @@
 #include "base/strings/sys_string_conversions.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/profile_management_switches.h"
-#include "components/signin/core/browser/signin_manager.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/unified_consent/feature.h"
 #import "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/signin/authentication_service.h"
 #include "ios/chrome/browser/signin/authentication_service_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/chrome/browser/signin/signin_util.h"
 #import "ios/chrome/browser/ui/authentication/authentication_ui_util.h"
 #import "ios/chrome/browser/ui/authentication/chrome_signin_view_controller.h"
@@ -24,6 +23,7 @@
 #import "ios/public/provider/chrome/browser/signin/chrome_identity.h"
 #import "ios/public/provider/chrome/browser/signin/chrome_identity_interaction_manager.h"
 #import "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -139,8 +139,8 @@
   signin_metrics::LogSigninAccessPointStarted(accessPoint_, promoAction_);
   completionCallback_ = [completion copy];
   AccountInfo accountInfo =
-      ios::SigninManagerFactory::GetForBrowserState(browserState_)
-          ->GetAuthenticatedAccountInfo();
+      IdentityManagerFactory::GetForBrowserState(browserState_)
+          ->GetPrimaryAccountInfo();
   std::string emailToReauthenticate = accountInfo.email;
   std::string idToReauthenticate = accountInfo.gaia;
   if (emailToReauthenticate.empty() || idToReauthenticate.empty()) {
diff --git a/ios/chrome/browser/ui/table_view/cells/table_view_signin_promo_item.mm b/ios/chrome/browser/ui/table_view/cells/table_view_signin_promo_item.mm
index de0f45bf..8457b09c6 100644
--- a/ios/chrome/browser/ui/table_view/cells/table_view_signin_promo_item.mm
+++ b/ios/chrome/browser/ui/table_view/cells/table_view_signin_promo_item.mm
@@ -58,8 +58,7 @@
   self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
   if (self) {
     SigninPromoView* signinPromoView =
-        [[SigninPromoView alloc] initWithFrame:CGRectZero
-                                         style:SigninPromoViewUIRefresh];
+        [[SigninPromoView alloc] initWithFrame:CGRectZero];
     self.signinPromoView = signinPromoView;
     self.signinPromoView.translatesAutoresizingMaskIntoConstraints = NO;
     [self.contentView addSubview:self.signinPromoView];
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
index acb2f04..20241f5 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
+++ b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
@@ -7,11 +7,13 @@
 #include <utility>
 #include <vector>
 
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/values.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/sync/base/weak_handle.h"
 #include "components/sync/driver/about_sync_util.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/engine/cycle/commit_counters.h"
 #include "components/sync/engine/cycle/status_counters.h"
@@ -24,18 +26,25 @@
 #include "ios/web/public/web_thread.h"
 #include "ios/web/public/webui/web_ui_ios.h"
 
-using syncer::JsEventDetails;
-using syncer::ModelTypeSet;
-using syncer::WeakHandle;
+namespace {
+
+// Returns the initial state of the "include specifics" flag, based on whether
+// or not the corresponding command-line switch is set.
+bool GetIncludeSpecificsInitialState() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(
+      switches::kSyncIncludeSpecificsInProtocolLog);
+}
+
+}  // namespace
 
 SyncInternalsMessageHandler::SyncInternalsMessageHandler()
-    : is_registered_(false),
-      is_registered_for_counters_(false),
+    : include_specifics_(GetIncludeSpecificsInitialState()),
       weak_ptr_factory_(this) {}
 
 SyncInternalsMessageHandler::~SyncInternalsMessageHandler() {
-  if (js_controller_)
+  if (js_controller_) {
     js_controller_->RemoveJsEventHandler(this);
+  }
 
   syncer::SyncService* service = GetSyncService();
   if (service && service->HasObserver(this)) {
@@ -75,8 +84,9 @@
           base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
-      syncer::sync_ui_util::kGetAllNodes,
-      base::BindRepeating(&SyncInternalsMessageHandler::HandleGetAllNodes,
+      syncer::sync_ui_util::kRequestIncludeSpecificsInitialState,
+      base::BindRepeating(&SyncInternalsMessageHandler::
+                              HandleRequestIncludeSpecificsInitialState,
                           base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
@@ -84,6 +94,33 @@
       base::BindRepeating(
           &SyncInternalsMessageHandler::HandleSetIncludeSpecifics,
           base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      syncer::sync_ui_util::kRequestStart,
+      base::BindRepeating(&SyncInternalsMessageHandler::HandleRequestStart,
+                          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      syncer::sync_ui_util::kRequestStopKeepData,
+      base::BindRepeating(
+          &SyncInternalsMessageHandler::HandleRequestStopKeepData,
+          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      syncer::sync_ui_util::kRequestStopClearData,
+      base::BindRepeating(
+          &SyncInternalsMessageHandler::HandleRequestStopClearData,
+          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      syncer::sync_ui_util::kTriggerRefresh,
+      base::BindRepeating(&SyncInternalsMessageHandler::HandleTriggerRefresh,
+                          base::Unretained(this)));
+
+  web_ui()->RegisterMessageCallback(
+      syncer::sync_ui_util::kGetAllNodes,
+      base::BindRepeating(&SyncInternalsMessageHandler::HandleGetAllNodes,
+                          base::Unretained(this)));
 }
 
 void SyncInternalsMessageHandler::HandleRegisterForEvents(
@@ -129,15 +166,25 @@
     const base::ListValue* args) {
   DCHECK(args->empty());
   base::DictionaryValue event_details;
-  std::unique_ptr<base::ListValue> type_list(new base::ListValue());
-  ModelTypeSet protocol_types = syncer::ProtocolTypes();
+  auto type_list = std::make_unique<base::ListValue>();
+  syncer::ModelTypeSet protocol_types = syncer::ProtocolTypes();
   for (syncer::ModelType type : protocol_types) {
     type_list->AppendString(ModelTypeToString(type));
   }
   event_details.Set(syncer::sync_ui_util::kTypes, std::move(type_list));
-  web_ui()->CallJavascriptFunction(
-      syncer::sync_ui_util::kDispatchEvent,
-      base::Value(syncer::sync_ui_util::kOnReceivedListOfTypes), event_details);
+  DispatchEvent(syncer::sync_ui_util::kOnReceivedListOfTypes, event_details);
+}
+
+void SyncInternalsMessageHandler::HandleRequestIncludeSpecificsInitialState(
+    const base::ListValue* args) {
+  DCHECK(args->empty());
+
+  base::DictionaryValue value;
+  value.SetBoolean(syncer::sync_ui_util::kIncludeSpecifics,
+                   GetIncludeSpecificsInitialState());
+
+  DispatchEvent(syncer::sync_ui_util::kOnReceivedIncludeSpecificsInitialState,
+                value);
 }
 
 void SyncInternalsMessageHandler::HandleGetAllNodes(
@@ -161,6 +208,59 @@
   include_specifics_ = args->GetList()[0].GetBool();
 }
 
+void SyncInternalsMessageHandler::HandleRequestStart(
+    const base::ListValue* args) {
+  DCHECK_EQ(0U, args->GetSize());
+
+  syncer::SyncService* service = GetSyncService();
+  if (!service) {
+    return;
+  }
+
+  service->RequestStart();
+  // If the service was previously stopped with CLEAR_DATA, then the
+  // "first-setup-complete" bit was also cleared, and now the service wouldn't
+  // fully start up. So set that too.
+  service->SetFirstSetupComplete();
+}
+
+void SyncInternalsMessageHandler::HandleRequestStopKeepData(
+    const base::ListValue* args) {
+  DCHECK_EQ(0U, args->GetSize());
+
+  syncer::SyncService* service = GetSyncService();
+  if (!service) {
+    return;
+  }
+
+  service->RequestStop(syncer::SyncService::KEEP_DATA);
+}
+
+void SyncInternalsMessageHandler::HandleRequestStopClearData(
+    const base::ListValue* args) {
+  DCHECK_EQ(0U, args->GetSize());
+
+  syncer::SyncService* service = GetSyncService();
+  if (!service) {
+    return;
+  }
+
+  service->RequestStop(syncer::SyncService::CLEAR_DATA);
+}
+
+void SyncInternalsMessageHandler::HandleTriggerRefresh(
+    const base::ListValue* args) {
+  syncer::SyncService* service = GetSyncService();
+  if (!service) {
+    return;
+  }
+
+  // Only allowed to trigger refresh/schedule nudges for protocol types, things
+  // like PROXY_TABS are not allowed.
+  service->TriggerRefresh(syncer::Intersection(service->GetActiveDataTypes(),
+                                               syncer::ProtocolTypes()));
+}
+
 void SyncInternalsMessageHandler::OnReceivedAllNodes(
     int request_id,
     std::unique_ptr<base::ListValue> nodes) {
@@ -177,9 +277,7 @@
     const syncer::ProtocolEvent& event) {
   std::unique_ptr<base::DictionaryValue> value(
       syncer::ProtocolEvent::ToValue(event, include_specifics_));
-  web_ui()->CallJavascriptFunction(
-      syncer::sync_ui_util::kDispatchEvent,
-      base::Value(syncer::sync_ui_util::kOnProtocolEvent), *value);
+  DispatchEvent(syncer::sync_ui_util::kOnProtocolEvent, *value);
 }
 
 void SyncInternalsMessageHandler::OnCommitCountersUpdated(
@@ -208,17 +306,15 @@
   details->SetString(syncer::sync_ui_util::kModelType, ModelTypeToString(type));
   details->SetString(syncer::sync_ui_util::kCounterType, counter_type);
   details->Set(syncer::sync_ui_util::kCounters, std::move(value));
-  web_ui()->CallJavascriptFunction(
-      syncer::sync_ui_util::kDispatchEvent,
-      base::Value(syncer::sync_ui_util::kOnCountersUpdated), *details);
+  DispatchEvent(syncer::sync_ui_util::kOnCountersUpdated, *details);
 }
 
-void SyncInternalsMessageHandler::HandleJsEvent(const std::string& name,
-                                                const JsEventDetails& details) {
+void SyncInternalsMessageHandler::HandleJsEvent(
+    const std::string& name,
+    const syncer::JsEventDetails& details) {
   DVLOG(1) << "Handling event: " << name << " with details "
            << details.ToString();
-  web_ui()->CallJavascriptFunction(syncer::sync_ui_util::kDispatchEvent,
-                                   base::Value(name), details.Get());
+  DispatchEvent(name, details.Get());
 }
 
 void SyncInternalsMessageHandler::SendAboutInfo() {
@@ -226,9 +322,7 @@
   std::unique_ptr<base::DictionaryValue> value =
       syncer::sync_ui_util::ConstructAboutInformation(sync_service,
                                                       GetChannel());
-  web_ui()->CallJavascriptFunction(
-      syncer::sync_ui_util::kDispatchEvent,
-      base::Value(syncer::sync_ui_util::kOnAboutInfoUpdated), *value);
+  DispatchEvent(syncer::sync_ui_util::kOnAboutInfoUpdated, *value);
 }
 
 // Gets the SyncService of the underlying original profile. May return null.
@@ -238,3 +332,10 @@
   return ProfileSyncServiceFactory::GetForBrowserState(
       browser_state->GetOriginalChromeBrowserState());
 }
+
+void SyncInternalsMessageHandler::DispatchEvent(
+    const std::string& name,
+    const base::Value& details_value) {
+  web_ui()->CallJavascriptFunction(syncer::sync_ui_util::kDispatchEvent,
+                                   base::Value(name), details_value);
+}
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
index 174c366..7b69aed8 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
+++ b/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
@@ -10,6 +10,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/values.h"
 #include "components/sync/driver/sync_service_observer.h"
 #include "components/sync/engine/cycle/type_debug_info_observer.h"
 #include "components/sync/engine/events/protocol_event_observer.h"
@@ -17,10 +18,6 @@
 #include "components/sync/js/js_event_handler.h"
 #include "ios/web/public/webui/web_ui_ios_message_handler.h"
 
-namespace base {
-class DictionaryValue;
-}  // namespace base
-
 namespace syncer {
 class SyncService;
 }  // namespace syncer
@@ -46,9 +43,12 @@
   // Fires an event to send updated info back to the page.
   void HandleRequestUpdatedAboutInfo(const base::ListValue* args);
 
-  // Fires and event to send the list of types back to the page.
+  // Fires an event to send the list of types back to the page.
   void HandleRequestListOfTypes(const base::ListValue* args);
 
+  // Fires an event to send the initial state of the "include specifics" flag.
+  void HandleRequestIncludeSpecificsInitialState(const base::ListValue* args);
+
   // Handler for getAllNodes message.  Needs a |request_id| argument.
   void HandleGetAllNodes(const base::ListValue* args);
 
@@ -56,6 +56,18 @@
   // protocol events when sent to be displayed.
   void HandleSetIncludeSpecifics(const base::ListValue* args);
 
+  // Handler for requestStart message.
+  void HandleRequestStart(const base::ListValue* args);
+
+  // Handler for requestStopKeepData message.
+  void HandleRequestStopKeepData(const base::ListValue* args);
+
+  // Handler for requestStopClearData message.
+  void HandleRequestStopClearData(const base::ListValue* args);
+
+  // Handler for triggerRefresh message.
+  void HandleTriggerRefresh(const base::ListValue* args);
+
   // syncer::JsEventHandler implementation.
   void HandleJsEvent(const std::string& name,
                      const syncer::JsEventDetails& details) override;
@@ -94,14 +106,16 @@
 
   syncer::SyncService* GetSyncService();
 
+  void DispatchEvent(const std::string& name, const base::Value& details_value);
+
   base::WeakPtr<syncer::JsController> js_controller_;
 
   // A flag used to prevent double-registration with ProfileSyncService.
-  bool is_registered_;
+  bool is_registered_ = false;
 
   // A flag used to prevent double-registration as TypeDebugInfoObserver with
   // ProfileSyncService.
-  bool is_registered_for_counters_;
+  bool is_registered_for_counters_ = false;
 
   // Whether specifics should be included when converting protocol events to a
   // human readable format.
diff --git a/ios/chrome/content_widget_extension/Info.plist b/ios/chrome/content_widget_extension/Info.plist
index 856ad294..dd101db 100644
--- a/ios/chrome/content_widget_extension/Info.plist
+++ b/ios/chrome/content_widget_extension/Info.plist
@@ -31,5 +31,9 @@
 	</dict>
 	<key>KSChannelChromeScheme</key>
 	<string>${CHROME_CHANNEL_SCHEME}</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>arm64</string>
+	</array>
 </dict>
 </plist>
diff --git a/ios/chrome/search_widget_extension/Info.plist b/ios/chrome/search_widget_extension/Info.plist
index 8be5029..dbb9c60 100644
--- a/ios/chrome/search_widget_extension/Info.plist
+++ b/ios/chrome/search_widget_extension/Info.plist
@@ -31,5 +31,9 @@
 	</dict>
 	<key>KSChannelChromeScheme</key>
 	<string>${CHROME_CHANNEL_SCHEME}</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>arm64</string>
+	</array>
 </dict>
 </plist>
diff --git a/ios/chrome/share_extension/Info.plist b/ios/chrome/share_extension/Info.plist
index ccc246f..efa361c 100644
--- a/ios/chrome/share_extension/Info.plist
+++ b/ios/chrome/share_extension/Info.plist
@@ -46,5 +46,9 @@
 		<key>NSExtensionPointIdentifier</key>
 		<string>com.apple.share-services</string>
 	</dict>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>arm64</string>
+	</array>
 </dict>
 </plist>
diff --git a/ios/web_view/DEPS b/ios/web_view/DEPS
index aa6a13b2..d3dd360 100644
--- a/ios/web_view/DEPS
+++ b/ios/web_view/DEPS
@@ -2,4 +2,10 @@
   # The subdirectories in ios/web_view/ will manually allow their own include
   # directories in ios/web_view/ so we disallow all of them.
   "-ios/web_view",
+
+  # includes from application layer are not allowed.
+  "-ios/chrome"
+
+  "+ios/web/public",
+  "-ios/web/public/test/fakes/test_browser_state.h",
 ]
diff --git a/jingle/glue/fake_ssl_client_socket.cc b/jingle/glue/fake_ssl_client_socket.cc
index 65204fc..6f2a73f 100644
--- a/jingle/glue/fake_ssl_client_socket.cc
+++ b/jingle/glue/fake_ssl_client_socket.cc
@@ -102,6 +102,20 @@
   return transport_socket_->Read(buf, buf_len, std::move(callback));
 }
 
+int FakeSSLClientSocket::ReadIfReady(net::IOBuffer* buf,
+                                     int buf_len,
+                                     net::CompletionOnceCallback callback) {
+  DCHECK_EQ(next_handshake_state_, STATE_NONE);
+  DCHECK(handshake_completed_);
+  return transport_socket_->ReadIfReady(buf, buf_len, std::move(callback));
+}
+
+int FakeSSLClientSocket::CancelReadIfReady() {
+  DCHECK_EQ(next_handshake_state_, STATE_NONE);
+  DCHECK(handshake_completed_);
+  return transport_socket_->CancelReadIfReady();
+}
+
 int FakeSSLClientSocket::Write(
     net::IOBuffer* buf,
     int buf_len,
diff --git a/jingle/glue/fake_ssl_client_socket.h b/jingle/glue/fake_ssl_client_socket.h
index 7445ac03..27b25f064 100644
--- a/jingle/glue/fake_ssl_client_socket.h
+++ b/jingle/glue/fake_ssl_client_socket.h
@@ -51,6 +51,10 @@
   int Read(net::IOBuffer* buf,
            int buf_len,
            net::CompletionOnceCallback callback) override;
+  int ReadIfReady(net::IOBuffer* buf,
+                  int buf_len,
+                  net::CompletionOnceCallback callback) override;
+  int CancelReadIfReady() override;
   int Write(
       net::IOBuffer* buf,
       int buf_len,
diff --git a/jingle/glue/network_service_async_socket.cc b/jingle/glue/network_service_async_socket.cc
index 4399c59..46daeee0 100644
--- a/jingle/glue/network_service_async_socket.cc
+++ b/jingle/glue/network_service_async_socket.cc
@@ -22,11 +22,13 @@
 
 NetworkServiceAsyncSocket::NetworkServiceAsyncSocket(
     GetProxyResolvingFactoryCallback get_socket_factory_callback,
+    bool use_fake_tls_handshake,
     size_t read_buf_size,
     size_t write_buf_size,
     const net::NetworkTrafficAnnotationTag& traffic_annotation)
     : get_socket_factory_callback_(get_socket_factory_callback),
       socket_observer_binding_(this),
+      use_fake_tls_handshake_(use_fake_tls_handshake),
       state_(STATE_CLOSED),
       error_(ERROR_NONE),
       net_error_(net::OK),
@@ -160,8 +162,12 @@
   network::mojom::SocketObserverPtr socket_observer;
   network::mojom::SocketObserverRequest socket_observer_request =
       mojo::MakeRequest(&socket_observer);
+  network::mojom::ProxyResolvingSocketOptionsPtr options =
+      network::mojom::ProxyResolvingSocketOptions::New();
+  options->use_tls = false;
+  options->fake_tls_handshake = use_fake_tls_handshake_;
   socket_factory_->CreateProxyResolvingSocket(
-      GURL("https://" + dest_host_port_pair.ToString()), false /*use_tls*/,
+      GURL("https://" + dest_host_port_pair.ToString()), std::move(options),
       net::MutableNetworkTrafficAnnotationTag(traffic_annotation_),
       mojo::MakeRequest(&socket_), std::move(socket_observer),
       base::BindOnce(&NetworkServiceAsyncSocket::ProcessConnectDone,
diff --git a/jingle/glue/network_service_async_socket.h b/jingle/glue/network_service_async_socket.h
index fb90487..27c4204 100644
--- a/jingle/glue/network_service_async_socket.h
+++ b/jingle/glue/network_service_async_socket.h
@@ -34,6 +34,7 @@
  public:
   NetworkServiceAsyncSocket(
       GetProxyResolvingFactoryCallback get_socket_factory_callback,
+      bool use_fake_tls_handshake,
       size_t read_buf_size,
       size_t write_buf_size,
       const net::NetworkTrafficAnnotationTag& traffic_annotation);
@@ -212,6 +213,8 @@
   // Used to route error notifications here.
   mojo::Binding<network::mojom::SocketObserver> socket_observer_binding_;
 
+  bool use_fake_tls_handshake_;
+
   // buzz::AsyncSocket state.
   buzz::AsyncSocket::State state_;
   buzz::AsyncSocket::Error error_;
diff --git a/jingle/glue/network_service_async_socket_unittest.cc b/jingle/glue/network_service_async_socket_unittest.cc
index da455e7..39f7137 100644
--- a/jingle/glue/network_service_async_socket_unittest.cc
+++ b/jingle/glue/network_service_async_socket_unittest.cc
@@ -187,7 +187,7 @@
   // mojom::ProxyResolvingSocketFactory implementation.
   void CreateProxyResolvingSocket(
       const GURL& url,
-      bool use_tls,
+      network::mojom::ProxyResolvingSocketOptionsPtr options,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       network::mojom::ProxyResolvingSocketRequest request,
       network::mojom::SocketObserverPtr observer,
@@ -317,6 +317,7 @@
         base::BindRepeating(
             &NetworkServiceAsyncSocketTest::BindToProxyResolvingSocketFactory,
             base::Unretained(this)),
+        false, /* use_fake_tls_handshake */
         14, 20, TRAFFIC_ANNOTATION_FOR_TESTS));
 
     ns_async_socket_->SignalConnected.connect(
diff --git a/media/audio/win/core_audio_util_win.cc b/media/audio/win/core_audio_util_win.cc
index 7b1c81b..8b7030b 100644
--- a/media/audio/win/core_audio_util_win.cc
+++ b/media/audio/win/core_audio_util_win.cc
@@ -21,6 +21,7 @@
 #include "base/win/scoped_handle.h"
 #include "base/win/scoped_propvariant.h"
 #include "base/win/scoped_variant.h"
+#include "base/win/windows_version.h"
 #include "media/audio/audio_device_description.h"
 #include "media/base/media_switches.h"
 
@@ -183,6 +184,10 @@
   }
 }
 
+bool IAudioClient3IsSupported() {
+  return CoreAudioUtil::GetIAudioClientVersion() >= 3;
+}
+
 std::string GetDeviceID(IMMDevice* device) {
   ScopedCoMem<WCHAR> device_id_com;
   std::string device_id;
@@ -367,6 +372,21 @@
   return audio_client;
 }
 
+// Creates and activates an IAudioClient3 COM object given the selected
+// endpoint device.
+ComPtr<IAudioClient3> CreateClientInternal3(IMMDevice* audio_device,
+                                            const UMALogCallback& uma_log_cb) {
+  if (!audio_device)
+    return ComPtr<IAudioClient3>();
+
+  ComPtr<IAudioClient3> audio_client;
+  HRESULT hr = audio_device->Activate(
+      __uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL, &audio_client);
+  DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr;
+  uma_log_cb.Run(UmaLogStep::CREATE_CLIENT, hr);
+  return audio_client;
+}
+
 HRESULT GetPreferredAudioParametersInternal(IAudioClient* client,
                                             bool is_output_device,
                                             AudioParameters* params,
@@ -378,34 +398,43 @@
     return hr;
 
   // Preferred sample rate.
-  int sample_rate = mix_format.Format.nSamplesPerSec;
+  const int sample_rate = mix_format.Format.nSamplesPerSec;
 
   int min_frames_per_buffer = 0;
   int max_frames_per_buffer = 0;
-  int frames_per_buffer;
+  int frames_per_buffer = 0;
 
-  ComPtr<IAudioClient3> audio_client_3;
-  hr = client->QueryInterface(audio_client_3.GetAddressOf());
-  if (SUCCEEDED(hr)) {
-    UINT32 default_period_frames;
-    UINT32 fundamental_period_frames;
-    UINT32 min_period_frames;
-    UINT32 max_period_frames;
-    hr = audio_client_3->GetSharedModeEnginePeriod(
-        &(mix_format.Format), &default_period_frames,
-        &fundamental_period_frames, &min_period_frames, &max_period_frames);
+  const bool supports_iac3 = IAudioClient3IsSupported();
 
-    uma_log_cb.Run(UmaLogStep::GET_SHARED_MODE_ENGINE_PERIOD, hr);
+  if (supports_iac3) {
+    // Try to obtain an IAudioClient3 interface from the IAudioClient object.
+    // Use ComPtr::As for doing QueryInterface calls on COM objects.
+    ComPtr<IAudioClient> audio_client(client);
+    ComPtr<IAudioClient3> audio_client_3;
+    hr = audio_client.As(&audio_client_3);
     if (SUCCEEDED(hr)) {
-      min_frames_per_buffer = min_period_frames;
-      max_frames_per_buffer = max_period_frames;
-      frames_per_buffer = default_period_frames;
+      UINT32 default_period_frames = 0;
+      UINT32 fundamental_period_frames = 0;
+      UINT32 min_period_frames = 0;
+      UINT32 max_period_frames = 0;
+      hr = audio_client_3->GetSharedModeEnginePeriod(
+          &(mix_format.Format), &default_period_frames,
+          &fundamental_period_frames, &min_period_frames, &max_period_frames);
+
+      uma_log_cb.Run(UmaLogStep::GET_SHARED_MODE_ENGINE_PERIOD, hr);
+      if (SUCCEEDED(hr)) {
+        min_frames_per_buffer = min_period_frames;
+        max_frames_per_buffer = max_period_frames;
+        frames_per_buffer = default_period_frames;
+      }
+      DVLOG(1) << "IAudioClient3 => min_period_frames: " << min_period_frames;
+      DVLOG(1) << "IAudioClient3 => frames_per_buffer: " << frames_per_buffer;
     }
   }
 
   // If we don't have access to IAudioClient3 or if the call to
   // GetSharedModeEnginePeriod() fails we fall back to GetDevicePeriod().
-  if (FAILED(hr)) {
+  if (!supports_iac3 || FAILED(hr)) {
     REFERENCE_TIME default_period = 0;
     hr = CoreAudioUtil::GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED,
                                         &default_period);
@@ -416,11 +445,11 @@
     // We are using the native device period to derive the smallest possible
     // buffer size in shared mode. Note that the actual endpoint buffer will be
     // larger than this size but it will be possible to fill it up in two calls.
-    // TODO(henrika): ensure that this scheme works for capturing as well.
     frames_per_buffer = static_cast<int>(
         sample_rate * CoreAudioUtil::ReferenceTimeToTimeDelta(default_period)
                           .InSecondsF() +
         0.5);
+    DVLOG(1) << "IAudioClient => frames_per_buffer: " << frames_per_buffer;
   }
 
   ChannelLayout channel_layout = GetChannelLayout(mix_format);
@@ -484,6 +513,19 @@
   return base::TimeDelta::FromMicroseconds(0.1 * time + 0.5);
 }
 
+uint32_t CoreAudioUtil::GetIAudioClientVersion() {
+  if (base::win::GetVersion() >= base::win::VERSION_WIN10) {
+    // Minimum supported client: Windows 10.
+    // Minimum supported server: Windows Server 2016
+    return 3;
+  } else if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+    // Minimum supported client: Windows 8.
+    // Minimum supported server: Windows Server 2012.
+    return 2;
+  }
+  return 1;
+}
+
 AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() {
   const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
   if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio))
@@ -702,14 +744,20 @@
                                                  EDataFlow data_flow,
                                                  ERole role) {
   ComPtr<IMMDevice> device(CreateDevice(device_id, data_flow, role));
-
   return CreateClientInternal(device.Get(),
                               base::BindRepeating(&LogUMAEmptyCb));
 }
 
+ComPtr<IAudioClient3> CoreAudioUtil::CreateClient3(const std::string& device_id,
+                                                   EDataFlow data_flow,
+                                                   ERole role) {
+  ComPtr<IMMDevice> device(CreateDevice(device_id, data_flow, role));
+  return CreateClientInternal3(device.Get(),
+                               base::BindRepeating(&LogUMAEmptyCb));
+}
+
 HRESULT CoreAudioUtil::GetSharedModeMixFormat(
     IAudioClient* client, WAVEFORMATPCMEX* format) {
-  VLOG(1) << __FUNCTION__;
   ScopedCoMem<WAVEFORMATPCMEX> format_pcmex;
   HRESULT hr = client->GetMixFormat(
       reinterpret_cast<WAVEFORMATEX**>(&format_pcmex));
@@ -814,7 +862,6 @@
 HRESULT CoreAudioUtil::GetPreferredAudioParameters(const std::string& device_id,
                                                    bool is_output_device,
                                                    AudioParameters* params) {
-  DVLOG(1) << __FUNCTION__;
   UMALogCallback uma_log_cb(
       is_output_device ? base::BindRepeating(&LogUMAPreferredOutputParams)
                        : base::BindRepeating(&LogUMAEmptyCb));
@@ -883,16 +930,21 @@
     stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
   DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags;
 
+  const bool supports_iac3 = IAudioClient3IsSupported();
+
   HRESULT hr;
-  if (requested_buffer_size > 0) {
+  if (supports_iac3 && requested_buffer_size > 0) {
+    // Try to obtain an IAudioClient3 interface from the IAudioClient object.
+    // Use ComPtr::As for doing QueryInterface calls on COM objects.
+    ComPtr<IAudioClient> audio_client(client);
     ComPtr<IAudioClient3> audio_client_3;
-    hr = client->QueryInterface(audio_client_3.GetAddressOf());
+    hr = audio_client.As(&audio_client_3);
     if (FAILED(hr)) {
-      DVLOG(1) << "Failed to QueryInterface on IAudioClient3 with explicit "
-                  "buffer size: "
-               << std::hex << hr;
+      DVLOG(1) << "Failed to obtain IAudioClient3 interface: " << std::hex
+               << hr;
       return hr;
     }
+    // Initialize a low-latency client using IAudioClient3.
     hr = audio_client_3->InitializeSharedAudioStream(
         stream_flags, requested_buffer_size, &(format->Format), session_guid);
     if (FAILED(hr)) {
diff --git a/media/audio/win/core_audio_util_win.h b/media/audio/win/core_audio_util_win.h
index e554b0c2a46..06ea22c 100644
--- a/media/audio/win/core_audio_util_win.h
+++ b/media/audio/win/core_audio_util_win.h
@@ -47,6 +47,10 @@
   // Example: double s = RefererenceTimeToTimeDelta(t).InMillisecondsF();
   static base::TimeDelta ReferenceTimeToTimeDelta(REFERENCE_TIME time);
 
+  // Returns 1, 2, or 3 corresponding to the highest version of IAudioClient
+  // the platform supports.
+  static uint32_t GetIAudioClientVersion();
+
   // Returns AUDCLNT_SHAREMODE_EXCLUSIVE if --enable-exclusive-mode is used
   // as command-line flag and AUDCLNT_SHAREMODE_SHARED otherwise (default).
   static AUDCLNT_SHAREMODE GetShareMode();
@@ -113,10 +117,12 @@
   // manage the flow of audio data between the application and an audio endpoint
   // device.
 
-  // Create an IAudioClient instance for a specific device _or_ the default
+  // Create an IAudioClient instance for a specific device or the default
   // device if AudioDeviceDescription::IsDefaultDevice(device_id).
   static Microsoft::WRL::ComPtr<IAudioClient>
   CreateClient(const std::string& device_id, EDataFlow data_flow, ERole role);
+  static Microsoft::WRL::ComPtr<IAudioClient3>
+  CreateClient3(const std::string& device_id, EDataFlow data_flow, ERole role);
 
   // Get the mix format that the audio engine uses internally for processing
   // of shared-mode streams. This format is not necessarily a format that the
@@ -190,8 +196,6 @@
                                       uint32_t* endpoint_buffer_size,
                                       const GUID* session_guid);
 
-  // TODO(henrika): add ExclusiveModeInitialize(...)
-
   // Create an IAudioRenderClient client for an existing IAudioClient given by
   // |client|. The IAudioRenderClient interface enables a client to write
   // output data to a rendering endpoint buffer.
diff --git a/media/audio/win/core_audio_util_win_unittest.cc b/media/audio/win/core_audio_util_win_unittest.cc
index 3415f98..37614a3 100644
--- a/media/audio/win/core_audio_util_win_unittest.cc
+++ b/media/audio/win/core_audio_util_win_unittest.cc
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include "base/macros.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -43,6 +44,12 @@
   ScopedCOMInitializer com_init_;
 };
 
+TEST_F(CoreAudioUtilWinTest, GetIAudioClientVersion) {
+  uint32_t client_version = CoreAudioUtil::GetIAudioClientVersion();
+  EXPECT_GE(client_version, 1u);
+  EXPECT_LE(client_version, 3u);
+}
+
 TEST_F(CoreAudioUtilWinTest, NumberOfActiveDevices) {
   ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
 
@@ -79,7 +86,7 @@
 
   // Create default devices for all flow/role combinations above.
   ComPtr<IMMDevice> audio_device;
-  for (size_t i = 0; i < arraysize(data); ++i) {
+  for (size_t i = 0; i < base::size(data); ++i) {
     audio_device = CoreAudioUtil::CreateDevice(
         AudioDeviceDescription::kDefaultDeviceId, data[i].flow, data[i].role);
     EXPECT_TRUE(audio_device.Get());
@@ -132,7 +139,7 @@
   // Get name and ID of default devices for all flow/role combinations above.
   ComPtr<IMMDevice> audio_device;
   AudioDeviceName device_name;
-  for (size_t i = 0; i < arraysize(data); ++i) {
+  for (size_t i = 0; i < base::size(data); ++i) {
     audio_device = CoreAudioUtil::CreateDevice(
         AudioDeviceDescription::kDefaultDeviceId, data[i].flow, data[i].role);
     EXPECT_TRUE(SUCCEEDED(
@@ -152,7 +159,7 @@
   // Enumerate all active input and output devices and fetch the ID of
   // the associated device.
   EDataFlow flows[] = { eRender , eCapture };
-  for (size_t i = 0; i < arraysize(flows); ++i) {
+  for (size_t i = 0; i < base::size(flows); ++i) {
     ComPtr<IMMDeviceCollection> collection;
     ASSERT_TRUE(SUCCEEDED(enumerator->EnumAudioEndpoints(
         flows[i], DEVICE_STATE_ACTIVE, collection.GetAddressOf())));
@@ -199,13 +206,38 @@
 
   EDataFlow data[] = {eRender, eCapture};
 
-  for (size_t i = 0; i < arraysize(data); ++i) {
+  for (size_t i = 0; i < base::size(data); ++i) {
     ComPtr<IAudioClient> client = CoreAudioUtil::CreateClient(
         AudioDeviceDescription::kDefaultDeviceId, data[i], eConsole);
     EXPECT_TRUE(client.Get());
   }
 }
 
+TEST_F(CoreAudioUtilWinTest, CreateClient3) {
+  ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable() &&
+                          CoreAudioUtil::GetIAudioClientVersion() >= 3);
+
+  EDataFlow data[] = {eRender, eCapture};
+
+  for (size_t i = 0; i < base::size(data); ++i) {
+    ComPtr<IAudioClient3> client3 = CoreAudioUtil::CreateClient3(
+        AudioDeviceDescription::kDefaultDeviceId, data[i], eConsole);
+    EXPECT_TRUE(client3.Get());
+  }
+
+  // Use ComPtr notation to achieve the same thing as above. ComPtr::As wraps
+  // QueryInterface calls on existing COM objects. In this case we use an
+  // existing IAudioClient to obtain the IAudioClient3 interface.
+  for (size_t i = 0; i < base::size(data); ++i) {
+    ComPtr<IAudioClient> client = CoreAudioUtil::CreateClient(
+        AudioDeviceDescription::kDefaultDeviceId, data[i], eConsole);
+    EXPECT_TRUE(client.Get());
+    ComPtr<IAudioClient3> client3;
+    EXPECT_TRUE(SUCCEEDED(client.As(&client3)));
+    EXPECT_TRUE(client3.Get());
+  }
+}
+
 TEST_F(CoreAudioUtilWinTest, GetSharedModeMixFormat) {
   ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
 
@@ -257,7 +289,7 @@
 
   // Verify that the device periods are valid for the default render and
   // capture devices.
-  for (size_t i = 0; i < arraysize(data); ++i) {
+  for (size_t i = 0; i < base::size(data); ++i) {
     ComPtr<IAudioClient> client;
     REFERENCE_TIME shared_time_period = 0;
     REFERENCE_TIME exclusive_time_period = 0;
@@ -281,7 +313,7 @@
 
   // Verify that the preferred audio parameters are OK for the default render
   // and capture devices.
-  for (size_t i = 0; i < arraysize(data); ++i) {
+  for (size_t i = 0; i < base::size(data); ++i) {
     AudioParameters params;
     EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
         AudioDeviceDescription::kDefaultDeviceId, data[i] == eRender,
@@ -365,7 +397,7 @@
   WAVEFORMATPCMEX format;
   uint32_t endpoint_buffer_size = 0;
 
-  for (size_t i = 0; i < arraysize(data); ++i) {
+  for (size_t i = 0; i < base::size(data); ++i) {
     ComPtr<IAudioClient> client;
     ComPtr<IAudioRenderClient> render_client;
     ComPtr<IAudioCaptureClient> capture_client;
diff --git a/mojo/core/scoped_process_handle.cc b/mojo/core/scoped_process_handle.cc
index 720c728..65dfcf78 100644
--- a/mojo/core/scoped_process_handle.cc
+++ b/mojo/core/scoped_process_handle.cc
@@ -48,10 +48,7 @@
   BOOL ok = ::DuplicateHandle(GetCurrentProcessHandle(), handle,
                               GetCurrentProcessHandle(), &handle, 0, FALSE,
                               DUPLICATE_SAME_ACCESS);
-
-  // TODO(https://crbug.com/887576): Revert this to a DCHECK once we have sorted
-  // out the cause of handle verifier failures.
-  PCHECK(ok);
+  DCHECK(ok);
 #endif
   return ScopedProcessHandle(handle);
 }
diff --git a/net/quic/OWNERS b/net/quic/OWNERS
index caafd5f..1835f5b 100644
--- a/net/quic/OWNERS
+++ b/net/quic/OWNERS
@@ -1,4 +1,3 @@
-ckrasic@chromium.org
 rch@chromium.org
 zhongyi@chromium.org
 
diff --git a/services/device/OWNERS b/services/device/OWNERS
index 597d1d3..958daf9 100644
--- a/services/device/OWNERS
+++ b/services/device/OWNERS
@@ -1,6 +1,7 @@
 # TEAM: device-dev@chromium.org
 
 blundell@chromium.org
+mattreynolds@chromium.org
 reillyg@chromium.org
 rockot@google.com
 
diff --git a/services/identity/public/cpp/identity_test_environment.cc b/services/identity/public/cpp/identity_test_environment.cc
index 37b703a6..c59fc34d 100644
--- a/services/identity/public/cpp/identity_test_environment.cc
+++ b/services/identity/public/cpp/identity_test_environment.cc
@@ -77,7 +77,7 @@
   SigninManagerBase::RegisterPrefs(pref_service_.registry());
 
   account_tracker_.Initialize(&pref_service_, base::FilePath());
-
+  signin_manager_.Initialize(&pref_service_);
 }
 
 IdentityManagerDependenciesOwner::~IdentityManagerDependenciesOwner() {}
diff --git a/services/network/DEPS b/services/network/DEPS
index 2e5b4b7..4522a5ff 100644
--- a/services/network/DEPS
+++ b/services/network/DEPS
@@ -10,6 +10,8 @@
   "+components/prefs",
   "+crypto",
   "+ipc",
+  # FakeSSLClientSocket
+  "+jingle/glue",
   "+net",
   "+sandbox",
   "+services/proxy_resolver/public/mojom",
diff --git a/services/network/proxy_resolving_socket_factory_mojo.cc b/services/network/proxy_resolving_socket_factory_mojo.cc
index 53dd763..e5f1a85 100644
--- a/services/network/proxy_resolving_socket_factory_mojo.cc
+++ b/services/network/proxy_resolving_socket_factory_mojo.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "jingle/glue/fake_ssl_client_socket.h"
 #include "net/url_request/url_request_context.h"
 #include "services/network/proxy_resolving_client_socket.h"
 #include "services/network/proxy_resolving_client_socket_factory.h"
@@ -24,13 +25,19 @@
 
 void ProxyResolvingSocketFactoryMojo::CreateProxyResolvingSocket(
     const GURL& url,
-    bool use_tls,
+    mojom::ProxyResolvingSocketOptionsPtr options,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
     mojom::ProxyResolvingSocketRequest request,
     mojom::SocketObserverPtr observer,
     CreateProxyResolvingSocketCallback callback) {
+  std::unique_ptr<net::StreamSocket> net_socket =
+      factory_impl_.CreateSocket(url, options && options->use_tls);
+  if (options && options->fake_tls_handshake)
+    net_socket = std::make_unique<jingle_glue::FakeSSLClientSocket>(
+        std::move(net_socket));
+
   auto socket = std::make_unique<ProxyResolvingSocketMojo>(
-      factory_impl_.CreateSocket(url, use_tls),
+      std::move(net_socket),
       static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation),
       std::move(observer), &tls_socket_factory_);
   ProxyResolvingSocketMojo* socket_raw = socket.get();
diff --git a/services/network/proxy_resolving_socket_factory_mojo.h b/services/network/proxy_resolving_socket_factory_mojo.h
index 59d33bdb..a268497 100644
--- a/services/network/proxy_resolving_socket_factory_mojo.h
+++ b/services/network/proxy_resolving_socket_factory_mojo.h
@@ -31,7 +31,7 @@
   // mojom::ProxyResolvingSocketFactory implementation.
   void CreateProxyResolvingSocket(
       const GURL& url,
-      bool use_tls,
+      mojom::ProxyResolvingSocketOptionsPtr options,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation,
       mojom::ProxyResolvingSocketRequest request,
       mojom::SocketObserverPtr observer,
diff --git a/services/network/proxy_resolving_socket_mojo.cc b/services/network/proxy_resolving_socket_mojo.cc
index 86a5749..2f6909c6 100644
--- a/services/network/proxy_resolving_socket_mojo.cc
+++ b/services/network/proxy_resolving_socket_mojo.cc
@@ -14,7 +14,7 @@
 namespace network {
 
 ProxyResolvingSocketMojo::ProxyResolvingSocketMojo(
-    std::unique_ptr<ProxyResolvingClientSocket> socket,
+    std::unique_ptr<net::StreamSocket> socket,
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
     mojom::SocketObserverPtr observer,
     TLSSocketFactory* tls_socket_factory)
diff --git a/services/network/proxy_resolving_socket_mojo.h b/services/network/proxy_resolving_socket_mojo.h
index 866c1de..0b810546 100644
--- a/services/network/proxy_resolving_socket_mojo.h
+++ b/services/network/proxy_resolving_socket_mojo.h
@@ -26,7 +26,7 @@
       public TLSSocketFactory::Delegate {
  public:
   ProxyResolvingSocketMojo(
-      std::unique_ptr<ProxyResolvingClientSocket> socket,
+      std::unique_ptr<net::StreamSocket> socket,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       mojom::SocketObserverPtr observer,
       TLSSocketFactory* tls_socket_factory);
@@ -57,7 +57,7 @@
 
   mojom::SocketObserverPtr observer_;
   TLSSocketFactory* tls_socket_factory_;
-  std::unique_ptr<ProxyResolvingClientSocket> socket_;
+  std::unique_ptr<net::StreamSocket> socket_;
   const net::NetworkTrafficAnnotationTag traffic_annotation_;
   mojom::ProxyResolvingSocketFactory::CreateProxyResolvingSocketCallback
       connect_callback_;
diff --git a/services/network/proxy_resolving_socket_mojo_unittest.cc b/services/network/proxy_resolving_socket_mojo_unittest.cc
index ac180df..62730e17 100644
--- a/services/network/proxy_resolving_socket_mojo_unittest.cc
+++ b/services/network/proxy_resolving_socket_mojo_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
+#include "jingle/glue/fake_ssl_client_socket.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "net/base/net_errors.h"
@@ -54,6 +55,7 @@
  public:
   ProxyResolvingSocketTestBase(bool use_tls)
       : use_tls_(use_tls),
+        fake_tls_handshake_(false),
         scoped_task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::IO) {}
 
@@ -114,8 +116,12 @@
       mojo::ScopedDataPipeProducerHandle* send_pipe_handle_out) {
     base::RunLoop run_loop;
     int net_error = net::ERR_FAILED;
+    network::mojom::ProxyResolvingSocketOptionsPtr options =
+        network::mojom::ProxyResolvingSocketOptions::New();
+    options->use_tls = use_tls_;
+    options->fake_tls_handshake = fake_tls_handshake_;
     factory_ptr_->CreateProxyResolvingSocket(
-        url, use_tls_,
+        url, std::move(options),
         net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
         std::move(request), std::move(socket_observer),
         base::BindLambdaForTesting(
@@ -141,11 +147,13 @@
   }
 
   bool use_tls() const { return use_tls_; }
+  void set_fake_tls_handshake(bool val) { fake_tls_handshake_ = val; }
 
   mojom::ProxyResolvingSocketFactory* factory() { return factory_ptr_.get(); }
 
  private:
   const bool use_tls_;
+  bool fake_tls_handshake_;
   base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<net::MockClientSocketFactory> mock_client_socket_factory_;
   std::unique_ptr<TestURLRequestContextWithProxy> context_with_proxy_;
@@ -334,6 +342,45 @@
   DISALLOW_COPY_AND_ASSIGN(ProxyResolvingSocketMojoTest);
 };
 
+TEST_F(ProxyResolvingSocketMojoTest, ConnectWithFakeTLSHandshake) {
+  const GURL kDestination("https://example.com:443");
+  const char kTestMsg[] = "abcdefghij";
+  const size_t kMsgSize = strlen(kTestMsg);
+
+  Init("DIRECT");
+  set_fake_tls_handshake(true);
+
+  base::StringPiece client_hello =
+      jingle_glue::FakeSSLClientSocket::GetSslClientHello();
+  base::StringPiece server_hello =
+      jingle_glue::FakeSSLClientSocket::GetSslServerHello();
+  std::vector<net::MockRead> reads = {
+      net::MockRead(net::ASYNC, server_hello.data(), server_hello.length(), 1),
+      net::MockRead(net::ASYNC, 2, kTestMsg),
+      net::MockRead(net::ASYNC, net::OK, 3)};
+
+  std::vector<net::MockWrite> writes = {net::MockWrite(
+      net::ASYNC, client_hello.data(), client_hello.length(), 0)};
+
+  net::StaticSocketDataProvider data_provider(reads, writes);
+  data_provider.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
+  mock_client_socket_factory()->AddSocketDataProvider(&data_provider);
+
+  mojom::ProxyResolvingSocketPtr socket;
+  mojo::ScopedDataPipeConsumerHandle client_socket_receive_handle;
+  mojo::ScopedDataPipeProducerHandle client_socket_send_handle;
+  net::IPEndPoint actual_remote_addr;
+  EXPECT_EQ(net::OK,
+            CreateSocketSync(mojo::MakeRequest(&socket),
+                             nullptr /* socket_observer*/, &actual_remote_addr,
+                             kDestination, &client_socket_receive_handle,
+                             &client_socket_send_handle));
+
+  EXPECT_EQ(kTestMsg, Read(&client_socket_receive_handle, kMsgSize));
+  EXPECT_TRUE(data_provider.AllReadDataConsumed());
+  EXPECT_TRUE(data_provider.AllWriteDataConsumed());
+}
+
 // Tests that when ProxyResolvingSocketPtr is destroyed but not the
 // ProxyResolvingSocketFactory, the connect callback is not dropped.
 // Regression test for https://crbug.com/862608.
@@ -350,7 +397,7 @@
   base::RunLoop run_loop;
   int net_error = net::OK;
   factory()->CreateProxyResolvingSocket(
-      kDestination, false,
+      kDestination, nullptr,
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
       mojo::MakeRequest(&socket), nullptr /* observer */,
       base::BindLambdaForTesting(
diff --git a/services/network/public/mojom/proxy_resolving_socket.mojom b/services/network/public/mojom/proxy_resolving_socket.mojom
index 11c9358..4f683280 100644
--- a/services/network/public/mojom/proxy_resolving_socket.mojom
+++ b/services/network/public/mojom/proxy_resolving_socket.mojom
@@ -36,14 +36,23 @@
           handle<data_pipe_producer>? send_stream);
 };
 
+struct ProxyResolvingSocketOptions {
+  // Establish a TLS connection on top of the TCP connection.
+  bool use_tls = false;
+
+  // Tries to do a fake TLS handshake on the connection.
+  // This is sometimes used with XMPP to pass through proxies.
+  // See jingle_glue::FakeSSLClientSocket for more details.
+  bool fake_tls_handshake = false;
+};
+
 // Factory interface for creating ProxyResolvingSocket. Each factory instance
 // has separate socket pools from the NetworkContext which created the
 // factory instance.
 interface ProxyResolvingSocketFactory {
   // Creates a socket connected to |url|. This connection might be done through
-  // proxies if any is set in system's proxy settings. If |use_tls|, a TLS
-  // connection will be established on top of a TCP connection. On success,
-  // |result| is net::OK. Caller is to use |send_stream| to send data and
+  // proxies if any is set in system's proxy settings. On success, |result| is
+  // net::OK. Caller is to use |send_stream| to send data and
   // |receive_stream| to receive data over the connection. On failure, |result|
   // is a network error code. |local_addr| contains the local address of the
   // socket. |peer_addr| contains the peer address. If socket is connected to a
@@ -54,7 +63,8 @@
   //
   // Any sockets that are created but are yet to be destroyed will be destroyed
   // when the implementation of this factory goes away.
-  CreateProxyResolvingSocket(url.mojom.Url url, bool use_tls,
+  CreateProxyResolvingSocket(url.mojom.Url url,
+      ProxyResolvingSocketOptions? options,
       MutableNetworkTrafficAnnotationTag traffic_annotation,
       ProxyResolvingSocket& socket,
       SocketObserver? observer)
diff --git a/services/network/tls_client_socket_unittest.cc b/services/network/tls_client_socket_unittest.cc
index 7d869b7..b695b091 100644
--- a/services/network/tls_client_socket_unittest.cc
+++ b/services/network/tls_client_socket_unittest.cc
@@ -167,7 +167,7 @@
     base::RunLoop run_loop;
     int net_error = net::ERR_FAILED;
     proxy_resolving_factory_->CreateProxyResolvingSocket(
-        url, false /* use_tls */,
+        url, nullptr /* options */,
         net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
         std::move(request), nullptr /* observer */,
         base::BindLambdaForTesting(
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index a3257ad..5d93162b 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -139,8 +139,6 @@
 #   define SK_SUPPORT_LEGACY_ANISOTROPIC_MIPMAP_SCALE
 #endif
 
-#define SK_LEGACY_OP_COLOR_AS_BYTES
-
 // Remove this after we fixed all the issues related to the new SDF algorithm
 // (https://codereview.chromium.org/1643143002)
 #ifndef SK_USE_LEGACY_DISTANCE_FIELDS
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index bf76c638..1778f97 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1522,29 +1522,6 @@
             ]
         }
     ],
-    "DesktopIOSPromotion": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "BookmarksBubblePromotionV1",
-                    "params": {
-                        "body_text_id": "0",
-                        "entry_point": "BookmarksBubblePromotion",
-                        "max_views": "3",
-                        "promo_variation_id": "6",
-                        "sms_id": "19001511",
-                        "title_text_id": "0"
-                    },
-                    "enable_features": [
-                        "DesktopIOSPromotion"
-                    ]
-                }
-            ]
-        }
-    ],
     "DetectingHeavyPages": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 84d7b51..1bb16c50 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -205,7 +205,6 @@
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-border-box-border-radius-004.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-042.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-044.html [ Pass ]
-crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-048.html [ Failure ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-050.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-circle-055.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-content-box-border-radius-002.html [ Pass ]
@@ -218,7 +217,6 @@
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-margin-box-border-radius-004.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-margin-box-border-radius-008.html [ Pass ]
 crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-padding-box-border-radius-002.html [ Pass ]
-crbug.com/591099 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/shapes1/shape-outside-polygon-020.html [ Failure ]
 crbug.com/591099 external/wpt/editing/run/bold.html [ Pass ]
 crbug.com/591099 external/wpt/editing/run/fontname.html [ Pass ]
 crbug.com/591099 external/wpt/editing/run/formatblock.html [ Pass ]
@@ -305,7 +303,7 @@
 crbug.com/591099 http/tests/devtools/network/network-datasaver-warning.js [ Failure ]
 crbug.com/591099 http/tests/devtools/persistence/persistence-merge-editor-tabs.js [ Failure Pass ]
 crbug.com/591099 http/tests/devtools/service-workers/service-worker-v8-cache.js [ Pass ]
-crbug.com/591099 http/tests/images/feature-policy-image-compression-cached-image.html [ Failure ]
+crbug.com/591099 http/tests/images/feature-policy-unoptimized-images-cached-image.html [ Failure ]
 crbug.com/591099 http/tests/images/restyle-decode-error.html [ Failure ]
 crbug.com/591099 http/tests/intersection-observer/v2/cross-origin-effects.html [ Failure ]
 crbug.com/591099 http/tests/intersection-observer/v2/cross-origin-occlusion.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
index 8c1835b..ab11555 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-gen-property-trees
@@ -97,7 +97,6 @@
 Bug(none) virtual/video-surface-layer/ [ Skip ]
 Bug(none) virtual/webrtc-wpt-unified-plan/ [ Skip ]
 Bug(none) virtual/windows-directwrite/ [ Skip ]
-Bug(none) virtual/without-smil/ [ Skip ]
 Bug(none) vr/ [ Skip ]
 Bug(none) wake_lock/ [ Skip ]
 Bug(none) wasm/ [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
index afc249b..c76fe7aa6 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -93,7 +93,6 @@
 Bug(none) virtual/user-activation-v2/ [ Skip ]
 Bug(none) virtual/video-surface-layer/ [ Skip ]
 Bug(none) virtual/windows-directwrite/ [ Skip ]
-Bug(none) virtual/without-smil/ [ Skip ]
 
 crbug.com/778875 virtual/threaded/animations/animationworklet/ [ Skip ]
 crbug.com/841567 virtual/threaded/fast/scrolling [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/SmokeTests b/third_party/WebKit/LayoutTests/SmokeTests
index d486cc1..9259186 100644
--- a/third_party/WebKit/LayoutTests/SmokeTests
+++ b/third_party/WebKit/LayoutTests/SmokeTests
@@ -1002,7 +1002,6 @@
 virtual/stable/webexposed/internal-properties-should-not-be-exposed.html
 virtual/threaded/animations/stability/pseudo-element-animation-with-marker-crash.html
 virtual/threaded/printing/page-and-element-geometry-match.html
-virtual/without-smil/svg/animations/exposed/effect.html
 wake_lock/wakelock-api.html
 wake_lock/wakelock-in-nested-frame.html
 webaudio/AudioParam/audioparam-setValueCurve-duration.html
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 712a9f1..458212c 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -5457,9 +5457,6 @@
 #Sheriff 2018-10-18
 crbug.com/896765 [ Linux Mac Win ] http/tests/performance-timing/element-timing/observe-child-element.html [ Failure Pass ]
 
-#Sheriff 2018-10-22
-crbug.com/897075 [ Win10 ] inspector-protocol/css/css-coverage-new-stylesheet.js [ Failure Pass ]
-
 #Sheriff 2018-10-23
 crbug.com/898214 [ Mac10.11 ] virtual/outofblink-cors-ns/external/wpt/fetch/api/credentials/authentication-basic.any.html [ Failure ]
 crbug.com/898050 [ Mac Win ] fast/workers/taskqueue/basic.html [ Pass Timeout ]
@@ -5471,27 +5468,11 @@
 crbug.com/898378 [ Mac10.13 ] virtual/scroll_customization/fast/scroll-behavior/smooth-scroll/keyboard-scroll.html [ Timeout ]
 
 # Sheriff 2018-10-24
-crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-image-compression.html [ Failure ]
 crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-image-policies-with-border-radius.html [ Failure ]
 crbug.com/889185 [ Win10 ] http/tests/images/feature-policy-legacy-formats.html [ Failure ]
-crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-edge-cases.html [ Failure ]
-crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-forced-layout.html [ Failure ]
-crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-responsive-image.html [ Failure ]
-crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image-styles.html [ Failure ]
-crbug.com/889185 [ Win10 ] images/feature-policy-max-downscaling-image.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/exotic-color-space/images/feature-policy-max-downscaling-image.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles.html [ Failure ]
-crbug.com/889185 [ Win10 ] virtual/gpu-rasterization/images/feature-policy-max-downscaling-image.html [ Failure ]
 
 # Sheriff 2018-10-25
-crbug.com/874576 http/tests/images/feature-policy-image-compression-cached-image.html [ Skip ]
+crbug.com/874576 http/tests/images/feature-policy-unoptimized-images-cached-image.html [ Skip ]
 
 # Sheriff 2018-10-26
 ### See crbug.com/891427 comment near the top of this file:
@@ -5501,7 +5482,7 @@
 
 # Sheriff 2018-10-29
 crbug.com/899710 [ Linux Win ] virtual/threaded/http/tests/devtools/tracing/timeline-paint/timeline-paint-with-layout-invalidations.js [ Failure Pass Timeout ]
-crbug.com/899715 [ Linux ] virtual/threaded/fast/scroll-behavior/wheel-and-touch-scroll-use-count.html [ Failure Pass ]
+crbug.com/766357 [ Linux Mac Win ]  virtual/threaded/fast/scroll-behavior/wheel-and-touch-scroll-use-count.html [ Failure Pass ]
 crbug.com/898987 [ Android ] synthetic_gestures/smooth-scroll-tiny-delta.html [ Failure Pass ]
 
 # Sheriff 2018-10-30
@@ -5525,7 +5506,7 @@
 crbug.com/v8/8405 http/tests/devtools/sources/debugger/live-edit-no-reveal.js [ Skip ]
 
 #Sheriff 2018-11-02
-crbug.com/901314 [ Win7 Mac10.10 Mac10.11 ] inspector-protocol/css/css-coverage-new-stylesheet.js [ Failure Pass ]
+crbug.com/901314 inspector-protocol/css/css-coverage-new-stylesheet.js [ Failure Pass ]
 crbug.com/901489 [ Mac10.13 ] http/tests/security/document-domain-canonicalizes.html [ Crash Pass ]
 crbug.com/901489 [ Mac10.13 ] virtual/outofblink-cors-ns/http/tests/security/document-domain-canonicalizes.html [ Crash Pass ]
 crbug.com/901489 [ Mac10.13 ] virtual/outofblink-cors/http/tests/security/document-domain-canonicalizes.html [ Crash Pass ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 389c123..832af77f 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -211,11 +211,6 @@
     "args": ["--stable-release-mode"]
   },
   {
-    "prefix": "without-smil",
-    "base": "svg/animations/exposed",
-    "args": ["--disable-blink-features=SMIL"]
-  },
-  {
     "prefix": "linux-subpixel",
     "base": "platform/linux/fast/text/subpixel",
     "args": ["--enable-webkit-text-subpixel-positioning"]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/cspro-not-enforced-in-worker.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/cspro-not-enforced-in-worker.html
new file mode 100644
index 0000000..784cdc88
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/cspro-not-enforced-in-worker.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+    <script src='/resources/testharness.js'></script>
+    <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+    <!-- This tests that a report only policy is not treated as enforcing when
+         inherited by a worker. This manifests in particular for `unsafe-eval`
+         in this bug crbug.com/777076  -->
+    <script nonce="abc">
+      var t1 = async_test("Check that inline is allowed since the inherited policy is report only");
+      var t2 = async_test("Check that eval is allowed since the inherited policy is report only");
+
+      var w = new Worker("support/eval.js");
+      w.onmessage = function(e) {
+        if (e.data == "unsafe-inline allowed") t1.done();
+        else if (e.data == "unsafe-eval allowed") t2.done();
+      }
+    </script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/cspro-not-enforced-in-worker.html.sub.headers b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/cspro-not-enforced-in-worker.html.sub.headers
new file mode 100644
index 0000000..877e192
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/cspro-not-enforced-in-worker.html.sub.headers
@@ -0,0 +1 @@
+Content-Security-Policy-Report-Only: script-src 'self' 'nonce-abc';
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/support/eval.js b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/support/eval.js
new file mode 100644
index 0000000..d8ba2a558
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/generic/support/eval.js
@@ -0,0 +1,2 @@
+postMessage('unsafe-inline allowed');
+eval("postMessage('unsafe-eval allowed')");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html
new file mode 100644
index 0000000..5357aa2e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html
@@ -0,0 +1,19 @@
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <!-- Content-Security-Policy-Report-Only: script-src 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}} -->
+</head>
+<body>
+  <script>
+    var t = async_test("Eval is allowed because the CSP is report-only");
+    try {
+      eval("t.done()");
+    } catch {
+      t.step(function() { assert_true(false, "The eval should have execute succesfully"); })
+    }
+  </script>
+
+  <script async defer src="../support/checkReport.sub.js?reportField=violated-directive&reportValue=script-src%20%27unsafe-inline%27"></script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html.sub.headers b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html.sub.headers
new file mode 100644
index 0000000..37a04b5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode-and-sends-report.html.sub.headers
@@ -0,0 +1,2 @@
+Set-Cookie: eval-allowed-in-report-only-mode-and-sends-report={{$id:uuid()}}; Path=/content-security-policy/script-src
+Content-Security-Policy-Report-Only: script-src 'unsafe-inline'; report-uri ../support/report.py?op=put&reportID={{$id}}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode.html
new file mode 100644
index 0000000..eebc8f02
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <!-- Content-Security-Policy-Report-Only: script-src 'unsafe-inline' -->
+</head>
+<body>
+  <script>
+    var t = async_test("Eval is allowed because the CSP is report-only");
+    try {
+      eval("t.done()");
+    } catch {
+      t.step(function() { assert_true(false, "The eval should have execute succesfully"); })
+    }
+  </script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode.html.sub.headers b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode.html.sub.headers
new file mode 100644
index 0000000..b9b5d81a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/eval-allowed-in-report-only-mode.html.sub.headers
@@ -0,0 +1 @@
+Content-Security-Policy-Report-Only: script-src 'unsafe-inline'
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/constructor-required-fields.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/constructor-required-fields.html
new file mode 100644
index 0000000..1260c491
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/securitypolicyviolation/constructor-required-fields.html
@@ -0,0 +1,239 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+    // basic tests.
+    test(function() {
+      assert_throws(TypeError(),
+                    function() { new SecurityPolicyViolationEvent(); });
+    }, "SecurityPolicyViolationEvent constructor should throw with no parameters");
+
+    test(function() {
+      assert_not_equals(new SecurityPolicyViolationEvent("securitypolicyviolation", {
+        documentURI: "http://example.com",
+        referrer: "http://example.com",
+        blockedURI: "http://example.com",
+        violatedDirective: "default-src",
+        effectiveDirective: "default-src",
+        originalPolicy: "default-src 'none'",
+        sourceFile: "example.js",
+        sample: "<script>alert('1');</scr" + "ipt>",
+        disposition: "enforce",
+        statusCode: 200,
+        lineNumber: 1,
+        columnNumber: 1,
+      }), undefined);
+    }, "SecurityPolicyViolationEvent constructor works with an init dict");
+
+    // missing required members
+    test(function() {
+      assert_throws(TypeError(),
+        function() { new SecurityPolicyViolationEvent("securitypolicyviolation", {
+          // documentURI: "http://example.com",
+          referrer: "http://example.com",
+          blockedURI: "http://example.com",
+          violatedDirective: "default-src",
+          effectiveDirective: "default-src",
+          originalPolicy: "default-src 'none'",
+          sourceFile: "example.js",
+          sample: "<script>alert('1');</scr" + "ipt>",
+          disposition: "enforce",
+          statusCode: 200,
+          lineNumber: 1,
+          columnNumber: 1,
+      })});
+    }, "SecurityPolicyViolationEvent constructor requires documentURI");
+
+    test(function() {
+      assert_throws(TypeError(),
+        function() { new SecurityPolicyViolationEvent("securitypolicyviolation", {
+          documentURI: "http://example.com",
+          referrer: "http://example.com",
+          blockedURI: "http://example.com",
+          // violatedDirective: "default-src",
+          effectiveDirective: "default-src",
+          originalPolicy: "default-src 'none'",
+          sourceFile: "example.js",
+          sample: "<script>alert('1');</scr" + "ipt>",
+          disposition: "enforce",
+          statusCode: 200,
+          lineNumber: 1,
+          columnNumber: 1,
+      })});
+    }, "SecurityPolicyViolationEvent constructor requires violatedDirective");
+
+    test(function() {
+      assert_throws(TypeError(),
+        function() { new SecurityPolicyViolationEvent("securitypolicyviolation", {
+          documentURI: "http://example.com",
+          referrer: "http://example.com",
+          blockedURI: "http://example.com",
+          violatedDirective: "default-src",
+          // effectiveDirective: "default-src",
+          originalPolicy: "default-src 'none'",
+          sourceFile: "example.js",
+          sample: "<script>alert('1');</scr" + "ipt>",
+          disposition: "enforce",
+          statusCode: 200,
+          lineNumber: 1,
+          columnNumber: 1,
+      })});
+    }, "SecurityPolicyViolationEvent constructor requires effectiveDirective");
+
+    test(function() {
+      assert_throws(TypeError(),
+        function() { new SecurityPolicyViolationEvent("securitypolicyviolation", {
+          documentURI: "http://example.com",
+          referrer: "http://example.com",
+          blockedURI: "http://example.com",
+          violatedDirective: "default-src",
+          effectiveDirective: "default-src",
+          // originalPolicy: "default-src 'none'",
+          sourceFile: "example.js",
+          sample: "<script>alert('1');</scr" + "ipt>",
+          disposition: "enforce",
+          statusCode: 200,
+          lineNumber: 1,
+          columnNumber: 1,
+      })});
+    }, "SecurityPolicyViolationEvent constructor requires originalPolicy");
+
+    test(function() {
+      assert_throws(TypeError(),
+        function() { new SecurityPolicyViolationEvent("securitypolicyviolation", {
+          documentURI: "http://example.com",
+          referrer: "http://example.com",
+          blockedURI: "http://example.com",
+          violatedDirective: "default-src",
+          effectiveDirective: "default-src",
+          originalPolicy: "default-src 'none'",
+          sourceFile: "example.js",
+          sample: "<script>alert('1');</scr" + "ipt>",
+          // disposition: "enforce",
+          statusCode: 200,
+          lineNumber: 1,
+          columnNumber: 1,
+      })});
+    }, "SecurityPolicyViolationEvent constructor requires disposition");
+
+    test(function() {
+      assert_throws(TypeError(),
+        function() { new SecurityPolicyViolationEvent("securitypolicyviolation", {
+          documentURI: "http://example.com",
+          referrer: "http://example.com",
+          blockedURI: "http://example.com",
+          violatedDirective: "default-src",
+          effectiveDirective: "default-src",
+          originalPolicy: "default-src 'none'",
+          sourceFile: "example.js",
+          sample: "<script>alert('1');</scr" + "ipt>",
+          disposition: "enforce",
+          // statusCode: 200,
+          lineNumber: 1,
+          columnNumber: 1,
+      })});
+    }, "SecurityPolicyViolationEvent constructor requires statusCode");
+
+    // missing optional members
+    test(function() {
+      assert_not_equals(new SecurityPolicyViolationEvent("securitypolicyviolation", {
+        documentURI: "http://example.com",
+        // referrer: "http://example.com",
+        blockedURI: "http://example.com",
+        violatedDirective: "default-src",
+        effectiveDirective: "default-src",
+        originalPolicy: "default-src 'none'",
+        sourceFile: "example.js",
+        sample: "<script>alert('1');</scr" + "ipt>",
+        disposition: "enforce",
+        statusCode: 200,
+        lineNumber: 1,
+        columnNumber: 1,
+      }), undefined);
+    }, "SecurityPolicyViolationEvent constructor does not require referrer");
+
+    test(function() {
+      assert_not_equals(new SecurityPolicyViolationEvent("securitypolicyviolation", {
+        documentURI: "http://example.com",
+        referrer: "http://example.com",
+        // blockedURI: "http://example.com",
+        violatedDirective: "default-src",
+        effectiveDirective: "default-src",
+        originalPolicy: "default-src 'none'",
+        sourceFile: "example.js",
+        sample: "<script>alert('1');</scr" + "ipt>",
+        disposition: "enforce",
+        statusCode: 200,
+        lineNumber: 1,
+        columnNumber: 1,
+      }), undefined);
+    }, "SecurityPolicyViolationEvent constructor does not require blockedURI");
+
+    test(function() {
+      assert_not_equals(new SecurityPolicyViolationEvent("securitypolicyviolation", {
+        documentURI: "http://example.com",
+        referrer: "http://example.com",
+        blockedURI: "http://example.com",
+        violatedDirective: "default-src",
+        effectiveDirective: "default-src",
+        originalPolicy: "default-src 'none'",
+        // sourceFile: "example.js",
+        sample: "<script>alert('1');</scr" + "ipt>",
+        disposition: "enforce",
+        statusCode: 200,
+        lineNumber: 1,
+        columnNumber: 1,
+      }), undefined);
+    }, "SecurityPolicyViolationEvent constructor does not require sourceFile");
+
+    test(function() {
+      assert_not_equals(new SecurityPolicyViolationEvent("securitypolicyviolation", {
+        documentURI: "http://example.com",
+        referrer: "http://example.com",
+        blockedURI: "http://example.com",
+        violatedDirective: "default-src",
+        effectiveDirective: "default-src",
+        originalPolicy: "default-src 'none'",
+        sourceFile: "example.js",
+        // sample: "<script>alert('1');</scr" + "ipt>",
+        disposition: "enforce",
+        statusCode: 200,
+        lineNumber: 1,
+        columnNumber: 1,
+      }), undefined);
+    }, "SecurityPolicyViolationEvent constructor does not require sample");
+
+    test(function() {
+      assert_not_equals(new SecurityPolicyViolationEvent("securitypolicyviolation", {
+        documentURI: "http://example.com",
+        referrer: "http://example.com",
+        blockedURI: "http://example.com",
+        violatedDirective: "default-src",
+        effectiveDirective: "default-src",
+        originalPolicy: "default-src 'none'",
+        sourceFile: "example.js",
+        sample: "<script>alert('1');</scr" + "ipt>",
+        disposition: "enforce",
+        statusCode: 200,
+        // lineNumber: 1,
+        columnNumber: 1,
+      }), undefined);
+    }, "SecurityPolicyViolationEvent constructor does not require lineNumber");
+
+    test(function() {
+      assert_not_equals(new SecurityPolicyViolationEvent("securitypolicyviolation", {
+        documentURI: "http://example.com",
+        referrer: "http://example.com",
+        blockedURI: "http://example.com",
+        violatedDirective: "default-src",
+        effectiveDirective: "default-src",
+        originalPolicy: "default-src 'none'",
+        sourceFile: "example.js",
+        sample: "<script>alert('1');</scr" + "ipt>",
+        disposition: "enforce",
+        statusCode: 200,
+        lineNumber: 1,
+        // columnNumber: 1,
+      }), undefined);
+    }, "SecurityPolicyViolationEvent constructor does not require columnNumber");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/pointer-events.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/pointer-events.html
index 3e069e76..091e985 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/pointer-events.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/pointer-events.html
@@ -15,9 +15,9 @@
 
 runPropertyTests('pointer-events', [
   { syntax: 'bounding-box' },
-  { syntax: 'visiblePainted' },
-  { syntax: 'visibleFill' },
-  { syntax: 'visibleStroke' },
+  { syntax: 'visiblepainted' },
+  { syntax: 'visiblefill' },
+  { syntax: 'visiblestroke' },
   { syntax: 'visible' },
   { syntax: 'painted' },
   { syntax: 'fill' },
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-transformclip-expected.png b/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-transformclip-expected.png
index c841985..46c6cfce 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-transformclip-expected.png
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-transformclip-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html b/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
index 843dde0..a4ed618d 100644
--- a/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
+++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/independent-inheritance-fast-path.html
@@ -13,7 +13,7 @@
 // TODO(napper): Generate this table from CSSProperties.in.
 var independent_properties = [
     // [Property name, [list of valid keywords]]. Each keyword is tested in consecutive pairs.
-    ["pointerEvents", ["none", "auto", "stroke", "fill", "painted", "visible", "visibleStroke", "visibleFill", "visiblePainted", "bounding-box", "all"]],
+    ["pointerEvents", ["none", "auto", "stroke", "fill", "painted", "visible", "visiblestroke", "visiblefill", "visiblepainted", "bounding-box", "all"]],
     ["visibility", ["visible", "hidden", "collapse"]],
     ["whiteSpace", ["normal", "pre", "pre-wrap", "pre-line", "nowrap"]],
     ["borderCollapse", ["separate", "collapse"]],
diff --git a/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor-expected.txt b/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor-expected.txt
index 85f78a4..ca3f152 100644
--- a/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor-expected.txt
@@ -15,21 +15,27 @@
 PASS new SecurityPolicyViolationEvent('eventType').lineNumber is 0
 PASS new SecurityPolicyViolationEvent('eventType').columnNumber is 0
 PASS new SecurityPolicyViolationEvent('eventType').statusCode is 0
-PASS new SecurityPolicyViolationEvent('eventType', { bubbles: false }).bubbles is true
-PASS new SecurityPolicyViolationEvent('eventType', { bubbles: true }).bubbles is true
-PASS new SecurityPolicyViolationEvent('eventType', { cancelable: false }).cancelable is false
-PASS new SecurityPolicyViolationEvent('eventType', { cancelable: true }).cancelable is false
-PASS new SecurityPolicyViolationEvent('eventType', { documentURI: 'foo' }).documentURI is "foo"
-PASS new SecurityPolicyViolationEvent('eventType', { referrer: 'foo' }).referrer is "foo"
-PASS new SecurityPolicyViolationEvent('eventType', { blockedURI: 'foo' }).blockedURI is "foo"
-PASS new SecurityPolicyViolationEvent('eventType', { violatedDirective: 'foo' }).violatedDirective is "foo"
-PASS new SecurityPolicyViolationEvent('eventType', { effectiveDirective: 'foo' }).effectiveDirective is "foo"
-PASS new SecurityPolicyViolationEvent('eventType', { originalPolicy: 'foo' }).originalPolicy is "foo"
-PASS new SecurityPolicyViolationEvent('eventType', { sourceFile: 'foo' }).sourceFile is "foo"
-PASS new SecurityPolicyViolationEvent('eventType', { disposition: 'foo' }).disposition threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': The provided value 'foo' is not a valid enum value of type SecurityPolicyViolationEventDisposition..
-PASS new SecurityPolicyViolationEvent('eventType', { lineNumber: 42 }).lineNumber is 42
-PASS new SecurityPolicyViolationEvent('eventType', { columnNumber: 42 }).columnNumber is 42
-PASS new SecurityPolicyViolationEvent('eventType', { statusCode: 42 }).statusCode is 42
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ bubbles: false})).bubbles is true
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ bubbles: true})).bubbles is true
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ cancelable: false})).cancelable is false
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ cancelable: true})).cancelable is false
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ documentURI: 'foo' })).documentURI is "foo"
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ referrer: 'foo' })).referrer is "foo"
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ blockedURI: 'foo' })).blockedURI is "foo"
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ violatedDirective: 'foo' })).violatedDirective is "foo"
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ effectiveDirective: 'foo' })).effectiveDirective is "foo"
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ originalPolicy: 'foo' })).originalPolicy is "foo"
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ sourceFile: 'foo' })).sourceFile is "foo"
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ disposition: 'foo' })) threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': The provided value 'foo' is not a valid enum value of type SecurityPolicyViolationEventDisposition..
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ lineNumber: 42 })).lineNumber is 42
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ columnNumber: 42 })).columnNumber is 42
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({ statusCode: 42 })).statusCode is 42
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({}, "documentURI")) threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': required member documentURI is undefined..
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({}, "violatedDirective")) threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': required member violatedDirective is undefined..
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({}, "effectiveDirective")) threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': required member effectiveDirective is undefined..
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({}, "originalPolicy")) threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': required member originalPolicy is undefined..
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({}, "statusCode")) threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': required member statusCode is undefined..
+PASS new SecurityPolicyViolationEvent('eventType', add_required_members({}, "disposition")) threw exception TypeError: Failed to construct 'SecurityPolicyViolationEvent': required member disposition is undefined..
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor.html b/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor.html
index 424af49..7ff6301 100644
--- a/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor.html
+++ b/third_party/WebKit/LayoutTests/fast/events/constructors/security-policy-violation-event-constructor.html
@@ -26,25 +26,52 @@
 shouldBe("new SecurityPolicyViolationEvent('eventType').columnNumber", "0");
 shouldBe("new SecurityPolicyViolationEvent('eventType').statusCode", "0");
 
+/*
+required  DOMString documentURI;
+required  DOMString violatedDirective;
+required  DOMString effectiveDirective;
+required  DOMString originalPolicy;
+required  SecurityPolicyViolationEventDisposition disposition;
+required  unsigned short statusCode;
+*/
+// This function will add to the init dict any missing required members
+function add_required_members(init_dict, deleted_member) {
+  ["documentURI", "violatedDirective", "effectiveDirective", "originalPolicy"].forEach(function(member) {
+    if (!(member in init_dict)) init_dict[member] = 'bar';
+  })
+  if (!("statusCode" in init_dict)) init_dict["statusCode"] = 200;
+  if (!("disposition" in init_dict)) init_dict["disposition"] = 'enforce';
+
+  // for tests that test the absence of required members
+  if (deleted_member != undefined && deleted_member in init_dict) delete init_dict[deleted_member];
+
+  return init_dict;
+}
+
 // bubbles is always true.
-shouldBeTrue("new SecurityPolicyViolationEvent('eventType', { bubbles: false }).bubbles");
-shouldBeTrue("new SecurityPolicyViolationEvent('eventType', { bubbles: true }).bubbles");
+shouldBeTrue("new SecurityPolicyViolationEvent('eventType', add_required_members({ bubbles: false})).bubbles");
+shouldBeTrue("new SecurityPolicyViolationEvent('eventType', add_required_members({ bubbles: true})).bubbles");
 
 // cancelable is always false.
-shouldBeFalse("new SecurityPolicyViolationEvent('eventType', { cancelable: false }).cancelable");
-shouldBeFalse("new SecurityPolicyViolationEvent('eventType', { cancelable: true }).cancelable");
+shouldBeFalse("new SecurityPolicyViolationEvent('eventType', add_required_members({ cancelable: false})).cancelable");
+shouldBeFalse("new SecurityPolicyViolationEvent('eventType', add_required_members({ cancelable: true})).cancelable");
 
 // String members are passed.
 ["documentURI", "referrer", "blockedURI", "violatedDirective", "effectiveDirective", "originalPolicy", "sourceFile"].forEach(function(member) {
-    shouldBeEqualToString("new SecurityPolicyViolationEvent('eventType', { " + member + ": 'foo' })." + member, "foo");
+    shouldBeEqualToString("new SecurityPolicyViolationEvent('eventType', add_required_members({ " + member + ": 'foo' }))." + member, "foo");
 });
 
 // Enum members throw on unknown initializer values.
-shouldThrow("new SecurityPolicyViolationEvent('eventType', { disposition: 'foo' }).disposition");
+shouldThrow("new SecurityPolicyViolationEvent('eventType', add_required_members({ disposition: 'foo' }))");
 
 // Number members are passed.
 ["lineNumber", "columnNumber", "statusCode"].forEach(function(member) {
-    shouldBe("new SecurityPolicyViolationEvent('eventType', { " + member + ": 42 })." + member, "42");
+    shouldBe("new SecurityPolicyViolationEvent('eventType', add_required_members({ " + member + ": 42 }))." + member, "42");
+});
+
+// Missing required members throw
+["documentURI", "violatedDirective", "effectiveDirective", "originalPolicy", "statusCode", "disposition"].forEach(function(member) {
+   shouldThrow("new SecurityPolicyViolationEvent('eventType', add_required_members({}, \"" + member + "\"))")
 });
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-cached-image-expected.png b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-cached-image-expected.png
deleted file mode 100644
index 453c7f4..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-cached-image-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-cached-image.html b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-cached-image.html
deleted file mode 100644
index a433b7b0..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-cached-image.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<style>
-body {
-  font: 10px Ahem;
-}
-</style>
-<body>
-  <iframe src="resources/frame-with-compression-test-images.html" allow="image-compression 'none'" width="440" height="180"></iframe>
-  <iframe src="resources/frame-with-compression-test-images.html" allow="image-compression 'none'" width="440" height="180"></iframe>
-  <iframe src="resources/frame-with-compression-test-images.html" allow="image-compression 'none'" width="440" height="180"></iframe>
-</body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius.html b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius.html
index 26d7f24..7e42e73 100644
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius.html
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-policies-with-border-radius.html
@@ -4,4 +4,4 @@
   font: 10px Ahem;
 }
 </style>
-<body><iframe src="resources/frame-with-images-with-border-radius.html" allow="legacy-image-formats 'none'; image-compression 'none'" width="440" height="180"></iframe></body>
+<body><iframe src="resources/frame-with-images-with-border-radius.html" allow="legacy-image-formats 'none'; unoptimized-images 'none'" width="440" height="180"></iframe></body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images-cached-image.html b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images-cached-image.html
new file mode 100644
index 0000000..f2ca41f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images-cached-image.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<style>
+body {
+  font: 10px Ahem;
+}
+</style>
+<body>
+  <iframe src="resources/frame-with-compression-test-images.html" allow="unoptimized-images 'none'" width="440" height="180"></iframe>
+  <iframe src="resources/frame-with-compression-test-images.html" allow="unoptimized-images 'none'" width="440" height="180"></iframe>
+  <iframe src="resources/frame-with-compression-test-images.html" allow="unoptimized-images 'none'" width="440" height="180"></iframe>
+</body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression.html b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images.html
similarity index 60%
rename from third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression.html
rename to third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images.html
index 4e003bc..dd0142a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression.html
+++ b/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-unoptimized-images.html
@@ -4,4 +4,4 @@
   font: 10px Ahem;
 }
 </style>
-<body><iframe src="resources/frame-with-compression-test-images.html" allow="image-compression 'none'" width="440" height="180"></iframe></body>
+<body><iframe src="resources/frame-with-compression-test-images.html" allow="unoptimized-images 'none'" width="440" height="180"></iframe></body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report-expected.txt
deleted file mode 100644
index 1276159..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CONSOLE ERROR: line 12: [Report Only] Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
-
-ALERT: PASS: eval() allowed!
-CSP report received:
-CONTENT_TYPE: application/csp-report
-HTTP_REFERER: http://127.0.0.1:8000/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report.php
-REQUEST_METHOD: POST
-=== POST DATA ===
-{"csp-report":{"document-uri":"http://127.0.0.1:8000/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report.php","referrer":"","violated-directive":"script-src","effective-directive":"script-src","original-policy":"script-src 'self' 'unsafe-inline'; report-uri resources/save-report.php?test=eval-allowed-in-report-only-mode-and-sends-report.php","disposition":"report","blocked-uri":"eval","line-number":12,"column-number":13,"source-file":"http://127.0.0.1:8000/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report.php","status-code":200,"script-sample":""}}
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report.php b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report.php
deleted file mode 100644
index f6cbb48..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-and-sends-report.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-header("Content-Security-Policy-Report-Only: script-src 'self' 'unsafe-inline'; report-uri resources/save-report.php?test=eval-allowed-in-report-only-mode-and-sends-report.php");
-?>
-<!DOCTYPE html>
-<html>
-<head>
-    <script>
-        if (window.internals)
-            internals.settings.setExperimentalContentSecurityPolicyFeaturesEnabled(false);
-    </script>
-</head>
-<body>
-    <script>
-        try {
-            eval("alert('PASS: eval() allowed!')");
-        } catch (e) {
-            console.log('FAIL: eval() blocked!');
-        }
-    </script>
-    <script src="resources/go-to-echo-report.js"></script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-expected.txt
deleted file mode 100644
index 94513ae..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-CONSOLE ERROR: The Content Security Policy 'script-src 'self' 'unsafe-inline'' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header.
-CONSOLE ERROR: line 12: [Report Only] Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'".
-
-ALERT: PASS: eval() executed as expected.
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode.php b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode.php
deleted file mode 100644
index dce3a94..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/eval-allowed-in-report-only-mode.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-header("Content-Security-Policy-Report-Only: script-src 'self' 'unsafe-inline'");
-?>
-<!DOCTYPE html>
-<html>
-<head>
-    <script>
-        if (window.testRunner)
-            testRunner.dumpAsText();
-    </script>
-</head>
-<body>
-    <script>
-        try {
-            eval("alert('PASS: eval() executed as expected.');");
-        } catch(e) {
-            alert("FAIL: eval() threw an exception.");
-        }
-    </script>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases.html
deleted file mode 100644
index 7e39d3f..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <iframe id="simple" src="resources/frame-with-max-downscaling-image-test-images-edge-cases.html"
-          allow="max-downscaling-image 'none'" width="600" height="500">
-  </iframe>
-</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout.html
deleted file mode 100644
index d17c89fd..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<!DOCTYPE html>
-<iframe src="resources/frame-with-max-downscaling-image-test-images-forced-layout.html"
-        allow="max-downscaling-image 'none'" frameborder="0">
-</iframe>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-resize.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-resize.html
deleted file mode 100644
index 8028141..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-resize.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <iframe id="simple" src="resources/frame-with-max-downscaling-image-test-images-resize.html"
-          allow="max-downscaling-image 'none'" width="600" height="500">
-  </iframe>
-</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image.html
deleted file mode 100644
index 9bbc039..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE html>
-<link rel="match" href="feature-policy-max-downscaling-image-responsive-image-expected.html">
-
-<body>
-  <iframe id="simple" src="resources/frame-with-max-downscaling-image-responsive-images.html" allow="max-downscaling-image 'none'" width="600" height="500">
-  </iframe>
-</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles.html
deleted file mode 100644
index e5d6b9c..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <iframe id="simple" src="resources/frame-with-max-downscaling-image-test-images-styles.html"
-          allow="max-downscaling-image 'none'" width="600" height="500">
-  </iframe>
-</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image.html b/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image.html
deleted file mode 100644
index 9c33795..0000000
--- a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <iframe id="simple" src="resources/frame-with-max-downscaling-image-test-images.html"
-          allow="max-downscaling-image 'none'" width="600" height="500">
-  </iframe>
-</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-edge-cases.html b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-edge-cases.html
new file mode 100644
index 0000000..46b0bb2
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-edge-cases.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+  <iframe id="simple" src="resources/frame-with-oversized-images-edge-cases.html"
+          allow="oversized-images 'none'" width="600" height="500">
+  </iframe>
+</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-forced-layout.html b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-forced-layout.html
new file mode 100644
index 0000000..4ff8c02
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-forced-layout.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<iframe src="resources/frame-with-oversized-images-forced-layout.html"
+        allow="oversized-images 'none'" frameborder="0">
+</iframe>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-resize.html b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-resize.html
new file mode 100644
index 0000000..736adaa3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-resize.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+  <iframe id="simple" src="resources/frame-with-oversized-images-resize.html"
+          allow="oversized-images 'none'" width="600" height="500">
+  </iframe>
+</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-responsive-image.html b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-responsive-image.html
new file mode 100644
index 0000000..772e6dd
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-responsive-image.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<link rel="match" href="feature-policy-oversized-images-responsive-image-expected.html">
+
+<body>
+  <iframe id="simple" src="resources/frame-with-oversized-images-responsive-images.html" allow="oversized-images 'none'" width="600" height="500">
+  </iframe>
+</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-styles.html b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-styles.html
new file mode 100644
index 0000000..687b68b4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images-styles.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+  <iframe id="simple" src="resources/frame-with-oversized-images-styles.html"
+          allow="oversized-images 'none'" width="600" height="500">
+  </iframe>
+</body>
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images.html b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images.html
new file mode 100644
index 0000000..922b21b3
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/images/feature-policy-oversized-images.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+  <iframe id="simple" src="resources/frame-with-oversized-images.html"
+          allow="oversized-images 'none'" width="600" height="500">
+  </iframe>
+</body>
diff --git a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-responsive-images-expected.html b/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-responsive-images-expected.html
deleted file mode 100644
index 6f04c44..0000000
--- a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-responsive-images-expected.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE html>
-<body>
-  <img src="green-256x256.jpg" style="filter:invert(1);"  width="127" height="127">
-  <img src="green-256x256.jpg" style="filter:invert(1);"  width="127" height="127">
-  <img src="green-256x256.jpg" width="128" height="128">
-  <img src="green-256x256.jpg" width="129" height="129">
-</body>
diff --git a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-edge-cases.html b/third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-edge-cases.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-edge-cases.html
rename to third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-edge-cases.html
diff --git a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-forced-layout.html b/third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-forced-layout.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-forced-layout.html
rename to third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-forced-layout.html
diff --git a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-resize.html b/third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-resize.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-resize.html
rename to third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-resize.html
diff --git a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-responsive-images.html b/third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-responsive-images.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-responsive-images.html
rename to third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-responsive-images.html
diff --git a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-styles.html b/third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-styles.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images-styles.html
rename to third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images-styles.html
diff --git a/third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images.html b/third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/resources/frame-with-max-downscaling-image-test-images.html
rename to third_party/WebKit/LayoutTests/images/resources/frame-with-oversized-images.html
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/svg-resize-from-near-zero-expected.html b/third_party/WebKit/LayoutTests/paint/invalidation/svg/svg-resize-from-near-zero-expected.html
new file mode 100644
index 0000000..0d760d18
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/svg-resize-from-near-zero-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<div style="width: 200px; height: 200px; background: green"></div>
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/svg/svg-resize-from-near-zero.html b/third_party/WebKit/LayoutTests/paint/invalidation/svg/svg-resize-from-near-zero.html
new file mode 100644
index 0000000..80b721f9
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/svg/svg-resize-from-near-zero.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<svg id="svg" viewBox="0 0 100 100" style="width: 200px; height: 0.1px; display: block">
+  <rect width="100" height="100" fill="green"/>
+</svg>
+<script src="../../../resources/run-after-layout-and-paint.js"></script>
+<script>
+runAfterLayoutAndPaint(_ => {
+  svg.style.height = "200px";
+}, true);
+</script>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-expected.png
index fcc402a..1198e02 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-shadow-expected.png
index 07027c9..9925c65e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/canvas/canvas-composite-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.png b/third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-unoptimized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/http/tests/images/feature-policy-image-compression-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/http/tests/images/feature-policy-unoptimized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/exotic-color-space/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/virtual/gpu-rasterization/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-expected.png
index 0d10a60..4195fe55 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-shadow-expected.png
index 2a575dc3..7b7db41 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/canvas/canvas-composite-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-compression-expected.png b/third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-unoptimized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-image-compression-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/http/tests/images/feature-policy-unoptimized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-resize-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-resize-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-resize-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-resize-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-resize-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-resize-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-resize-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-resize-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/exotic-color-space/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/virtual/gpu-rasterization/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-expected.png
index ce3c6420c..51e5736 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-shadow-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-shadow-expected.png
index 58ab59da..896d81af 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-shadow-expected.png
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/canvas/canvas-composite-shadow-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-compression-expected.png b/third_party/WebKit/LayoutTests/platform/win/http/tests/images/feature-policy-unoptimized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/http/tests/images/feature-policy-image-compression-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/http/tests/images/feature-policy-unoptimized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-resize-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-resize-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/images/feature-policy-max-downscaling-image-resize-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-resize-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-resize-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-resize-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-resize-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-resize-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/exotic-color-space/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/exotic-color-space/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-edge-cases-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-edge-cases-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-edge-cases-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-forced-layout-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-forced-layout-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-forced-layout-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-responsive-image-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-responsive-image-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-responsive-image-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-styles-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win7/virtual/gpu-rasterization/images/feature-policy-max-downscaling-image-styles-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/virtual/gpu-rasterization/images/feature-policy-oversized-images-styles-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg-root-expected.txt b/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg-root-expected.txt
index 267634b..22fa57e2 100644
--- a/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg-root-expected.txt
+++ b/third_party/WebKit/LayoutTests/virtual/paint-touchaction-rects/fast/events/touch/compositor-touch-hit-rects-svg-root-expected.txt
@@ -1,3 +1,3 @@
-: #document scrolling (9, 9, 51, 52)
+: #document scrolling (8, 8, 53, 54)
 
 
diff --git a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/README.txt b/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/README.txt
deleted file mode 100644
index 766aff5..0000000
--- a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/README.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# This suite runs tests with the --disable-blink-features=smil flag.
-Deprecation of SMIL is tracked in http://crbug.com/482689.
diff --git a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/effect-expected.txt b/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/effect-expected.txt
deleted file mode 100644
index c0cd4f6..0000000
--- a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/effect-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
- PASS rect.width.animVal.value is 100
-PASS rect.height.animVal.value is 200
-PASS rect["transform"].animVal.numberOfItems is 1
-PASS rect["transform"].animVal.getItem(0).angle is 300
diff --git a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/effect.html b/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/effect.html
deleted file mode 100644
index 73126f5..0000000
--- a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/effect.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="../../../../../resources/js-test.js"></script>
-  <script>
-    'use strict';
-
-    function checkWidth() {
-      var rect = document.getElementById('rect');
-
-      shouldBe('rect.width.animVal.value', '100');
-      shouldBe('rect.height.animVal.value', '200');
-      shouldBe('rect["transform"].animVal.numberOfItems', '1');
-      shouldBe('rect["transform"].animVal.getItem(0).angle', '300');
-
-      if (window.testRunner) {
-        testRunner.dumpAsText();
-        testRunner.notifyDone();
-      }
-    }
-
-    jsTestIsAsync = true;
-    requestAnimationFrame(checkWidth);
-    if (window.testRunner) {
-      testRunner.waitUntilDone();
-    }
-  </script>
-</head>
-<body>
-  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <discard>
-      <rect id="rect" width="100" height="200" fill="#123456" transform="rotate(300)">
-        <set attributeName="width" to="400"/>
-        <animate attributeName="height" from="500" to="500" dur="1min"/>
-        <animateTransform attributeName="transform" type="rotate" from="600" to="600" dur="1min"/>
-        <animateColor attributeName="fill" from="blue" to="blue" dur="1min"/>
-        <animateMotion from="100" to="100" dur="1min">
-          <mpath xlink:href="#motionPath" />
-        </animateMotion>
-      </rect>
-    </discard>
-    <path id="motionPath" d="m0,0 h200 v300 z" stroke-width="5" stroke="green"/>
-  </svg>
-  <p id="description"></p>
-  <div id="console"></div>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/interface-expected.txt b/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/interface-expected.txt
deleted file mode 100644
index 618dd77b..0000000
--- a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/interface-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-PASS svg.animationsPaused is undefined.
-PASS svg.getCurrentTime is undefined.
-PASS svg.pauseAnimations is undefined.
-PASS svg.setCurrentTime is undefined.
-PASS svg.unpauseAnimations is undefined.
-PASS animation.beginElement is undefined.
-PASS animation.beginElementAt is undefined.
-PASS animation.endElement is undefined.
-PASS animation.endElementAt is undefined.
-PASS animation.getCurrentTime is undefined.
-PASS animation.getSimpleDuration is undefined.
-PASS animation.getStartTime is undefined.
-PASS animation.onbegin is undefined.
-PASS animation.onend is undefined.
-PASS animation.onrepeat is undefined.
-PASS animation.targetElement is undefined.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/interface.html b/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/interface.html
deleted file mode 100644
index 8a4683f..0000000
--- a/third_party/WebKit/LayoutTests/virtual/without-smil/svg/animations/exposed/interface.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-  <script src="../../../../../resources/js-test.js"></script>
-</head>
-<body>
-  <svg id="svg" xmlns="http://www.w3.org/2000/svg">
-    <rect width="100" height="200">
-      <set id="animation" attributeName="width" to="300"/>
-    </rect>
-  </svg>
-
-  <script>
-    'use strict';
-
-    var svg = document.getElementById('svg');
-    var animation = document.getElementById('animation');
-
-    shouldBeUndefined("svg.animationsPaused");
-    shouldBeUndefined("svg.getCurrentTime");
-    shouldBeUndefined("svg.pauseAnimations");
-    shouldBeUndefined("svg.setCurrentTime");
-    shouldBeUndefined("svg.unpauseAnimations");
-
-    shouldBeUndefined("animation.beginElement");
-    shouldBeUndefined("animation.beginElementAt");
-    shouldBeUndefined("animation.endElement");
-    shouldBeUndefined("animation.endElementAt");
-    shouldBeUndefined("animation.getCurrentTime");
-    shouldBeUndefined("animation.getSimpleDuration");
-    shouldBeUndefined("animation.getStartTime");
-    shouldBeUndefined("animation.onbegin");
-    shouldBeUndefined("animation.onend");
-    shouldBeUndefined("animation.onrepeat");
-    shouldBeUndefined("animation.targetElement");
-
-    if (window.testRunner)
-        testRunner.dumpAsText();
-  </script>
-
-  <p id="description"></p>
-  <div id="console"></div>
-</body>
-</html>
diff --git a/third_party/android_build_tools/bundletool/README.chromium b/third_party/android_build_tools/bundletool/README.chromium
index b36b085..9cae0aff 100644
--- a/third_party/android_build_tools/bundletool/README.chromium
+++ b/third_party/android_build_tools/bundletool/README.chromium
@@ -1,6 +1,6 @@
 Name: Android SDK bundletool
 Short Name:  bundletool
-Version: 0.6.0
+Version: 0.6.2
 License: Apache Version 2.0
 License File: NOT_SHIPPED
 Security Critical: No
diff --git a/third_party/android_build_tools/bundletool/cipd.yaml b/third_party/android_build_tools/bundletool/cipd.yaml
index 3431e3f..2d6e626a 100644
--- a/third_party/android_build_tools/bundletool/cipd.yaml
+++ b/third_party/android_build_tools/bundletool/cipd.yaml
@@ -5,10 +5,10 @@
 # Remember to increment the versions for the commands in this comment block.
 # Before creating a new CIPD package run the following to make sure the tag you
 # are adding does not already exist (otherwise you break the build):
-# cipd describe chromium/third_party/android_tools_bundletool -version "version:0.6.0-cr0"
+# cipd describe chromium/third_party/android_tools_bundletool -version "version:0.6.2-cr0"
 # To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:0.6.0-cr0
+# cipd create --pkg-def cipd.yaml -tag version:0.6.2-cr0
 package: chromium/third_party/android_tools_bundletool
 description: Android SDK tool to manage App Bundles
 data:
-  - file: bundletool-all-0.6.0.jar
+  - file: bundletool-all-0.6.2.jar
diff --git a/third_party/blink/common/feature_policy/feature_policy.cc b/third_party/blink/common/feature_policy/feature_policy.cc
index 649acc0e..4a413e46 100644
--- a/third_party/blink/common/feature_policy/feature_policy.cc
+++ b/third_party/blink/common/feature_policy/feature_policy.cc
@@ -264,7 +264,7 @@
         FeaturePolicy::FeatureDefault::EnableForSelf},
        {mojom::FeaturePolicyFeature::kGyroscope,
         FeaturePolicy::FeatureDefault::EnableForSelf},
-       {mojom::FeaturePolicyFeature::kImageCompression,
+       {mojom::FeaturePolicyFeature::kUnoptimizedImages,
         FeaturePolicy::FeatureDefault::EnableForAll},
        {mojom::FeaturePolicyFeature::kLayoutAnimations,
         FeaturePolicy::FeatureDefault::EnableForAll},
@@ -274,7 +274,7 @@
         FeaturePolicy::FeatureDefault::EnableForAll},
        {mojom::FeaturePolicyFeature::kMagnetometer,
         FeaturePolicy::FeatureDefault::EnableForSelf},
-       {mojom::FeaturePolicyFeature::kMaxDownscalingImage,
+       {mojom::FeaturePolicyFeature::kOversizedImages,
         FeaturePolicy::FeatureDefault::EnableForAll},
        {mojom::FeaturePolicyFeature::kMicrophone,
         FeaturePolicy::FeatureDefault::EnableForSelf},
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
index 9cdfc6e..508e0c03 100644
--- a/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
+++ b/third_party/blink/public/mojom/feature_policy/feature_policy.mojom
@@ -83,10 +83,10 @@
   // Controls which image formats are allowed to be used in the document.
   kLegacyImageFormats = 22,
   // When disallowed, requires images to have a reasonable byte-to-pixel ratio.
-  kImageCompression = 23,
+  kUnoptimizedImages = 23,
   // When disallowed, restricts source image size to be no more 2x larger than
   // the image's containing block.
-  kMaxDownscalingImage = 25,
+  kOversizedImages = 25,
   // Controls access to Picture-in-Picture.
   kPictureInPicture = 26,
   // Controls the ability to block and interfere with vertical scrolling.
diff --git a/third_party/blink/public/platform/web_font_render_style.h b/third_party/blink/public/platform/web_font_render_style.h
index b378736..05ae529 100644
--- a/third_party/blink/public/platform/web_font_render_style.h
+++ b/third_party/blink/public/platform/web_font_render_style.h
@@ -35,6 +35,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
+class SkFont;
 class SkFontMgr;
 
 namespace blink {
@@ -72,6 +73,7 @@
   void OverrideWith(const WebFontRenderStyle& other);
 
   void ApplyToSkPaint(SkPaint&, float device_scale_factor) const;
+  void ApplyToSkFont(SkFont*, float device_scale_factor) const;
 
   // Each of the use* members below can take one of three values:
   //   0: off
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 7e0b1c5..b0d34f66b 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -209,6 +209,7 @@
   BLINK_PLATFORM_EXPORT static void EnableAutoplayIgnoresWebAudio(bool);
   BLINK_PLATFORM_EXPORT static void EnableMediaControlsExpandGesture(bool);
   BLINK_PLATFORM_EXPORT static void EnableHrefTranslate(bool);
+  BLINK_PLATFORM_EXPORT static void EnableMergeBlockingNonBlockingPools(bool);
 
  private:
   WebRuntimeFeatures();
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
index a63e1514..9ca324ff 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.cc
@@ -463,9 +463,10 @@
     // a non-blocking task, since we know now that all the data is received and
     // we will no longer block.
     //
-    // TODO(874080): Once we have mutable task traits, simply unmark the
-    // existing task as no longer MayBlock.
+    // TODO(874080): Remove this once blocking and non-blocking pools are
+    // merged.
     if (RuntimeEnabledFeatures::ScheduledScriptStreamingEnabled() &&
+        !RuntimeEnabledFeatures::MergeBlockingNonBlockingPoolsEnabled() &&
         !blocking_task_started_or_cancelled_.test_and_set()) {
       std::unique_ptr<v8::ScriptCompiler::ScriptStreamingTask>
           script_streaming_task(
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer.h b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
index 7088e5c..5e79b9f1 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer.h
@@ -148,7 +148,7 @@
 
   // Flag used to allow atomic cancelling and reposting of the streaming task
   // when the load completes without the task yet starting.
-  // TODO(874080): Remove once we can mutate task traits.
+  // TODO(874080): Remove this once blocking and non-blocking pools are merged.
   std::atomic_flag blocking_task_started_or_cancelled_ = ATOMIC_FLAG_INIT;
 
   // Whether the script source code should be retrieved from the Resource
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index f2c12ca5..8bdfd43 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1767,6 +1767,8 @@
     "css/parser/sizes_attribute_parser_test.cc",
     "css/parser/sizes_calc_parser_test.cc",
     "css/properties/css_parsing_utils_test.cc",
+    "css/properties/css_property_ref_test.cc",
+    "css/properties/longhands/custom_property_test.cc",
     "css/resolver/css_variable_data_test.cc",
     "css/resolver/css_variable_resolver_test.cc",
     "css/resolver/font_builder_test.cc",
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 40455b0..1a67448 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -394,6 +394,8 @@
     "properties/css_parsing_utils.cc",
     "properties/css_parsing_utils.h",
     "properties/css_property_base_custom.cc",
+    "properties/css_property_ref.cc",
+    "properties/css_property_ref.h",
     "properties/longhand.h",
     "properties/longhands/align_content_custom.cc",
     "properties/longhands/align_items_custom.cc",
@@ -480,6 +482,8 @@
     "properties/longhands/counter_increment_custom.cc",
     "properties/longhands/counter_reset_custom.cc",
     "properties/longhands/cursor_custom.cc",
+    "properties/longhands/custom_property.cc",
+    "properties/longhands/custom_property.h",
     "properties/longhands/cx_custom.cc",
     "properties/longhands/cy_custom.cc",
     "properties/longhands/d_custom.cc",
diff --git a/third_party/blink/renderer/core/css/css_font_face_rule.idl b/third_party/blink/renderer/core/css/css_font_face_rule.idl
index 5b9cddb..2e2a7530 100644
--- a/third_party/blink/renderer/core/css/css_font_face_rule.idl
+++ b/third_party/blink/renderer/core/css/css_font_face_rule.idl
@@ -18,14 +18,7 @@
  * Boston, MA 02110-1301, USA.
  */
 
-// The CSSFontFaceRule interface was introduced in DOM Level 2 Style, but has
-// been replaced by an entirely different interface in CSS Fonts:
-// https://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSFontFaceRule
 // https://drafts.csswg.org/css-fonts/#om-fontface
-//
-// TODO(foolip): Make CSSFontFaceRule match new spec and/or change spec to match
-// what's implemented. https://crbug.com/709013
-// https://github.com/w3c/csswg-drafts/issues/825
 
 interface CSSFontFaceRule : CSSRule {
     [Measure] readonly attribute CSSStyleDeclaration style;
diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5
index d9682c498..bb449f14 100644
--- a/third_party/blink/renderer/core/css/css_properties.json5
+++ b/third_party/blink/renderer/core/css/css_properties.json5
@@ -2522,8 +2522,8 @@
       inherited: true,
       field_template: "keyword",
       keywords: [
-        "none", "auto", "stroke", "fill", "painted", "visible", "visibleStroke",
-        "visibleFill", "visiblePainted", "bounding-box", "all"
+        "none", "auto", "stroke", "fill", "painted", "visible", "visiblestroke",
+        "visiblefill", "visiblepainted", "bounding-box", "all"
       ],
       typedom_types: ["Keyword"],
       default_value: "auto",
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index b944b4c..69673287 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -797,9 +797,9 @@
     //
     // pointer-events
     //
-    "visiblePainted",
-    "visibleFill",
-    "visibleStroke",
+    "visiblepainted",
+    "visiblefill",
+    "visiblestroke",
     //visible
     "painted",
     "fill",
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
index 1f7fb1b..87b0f7b8 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_fast_paths.cc
@@ -665,7 +665,7 @@
     case CSSPropertyPointerEvents:
       return value_id == CSSValueVisible || value_id == CSSValueNone ||
              value_id == CSSValueAll || value_id == CSSValueAuto ||
-             (value_id >= CSSValueVisiblePainted &&
+             (value_id >= CSSValueVisiblepainted &&
               value_id <= CSSValueBoundingBox);
     case CSSPropertyPosition:
       return value_id == CSSValueStatic || value_id == CSSValueRelative ||
diff --git a/third_party/blink/renderer/core/css/properties/css_property_ref.cc b/third_party/blink/renderer/core/css/properties/css_property_ref.cc
new file mode 100644
index 0000000..5679752
--- /dev/null
+++ b/third_party/blink/renderer/core/css/properties/css_property_ref.cc
@@ -0,0 +1,27 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
+
+#include "third_party/blink/renderer/core/dom/document.h"
+
+namespace blink {
+
+CSSPropertyRef::CSSPropertyRef(const String& name, const Document& document)
+    : property_id_(cssPropertyID(name)) {
+  if (property_id_ == CSSPropertyVariable)
+    custom_property_ = CustomProperty(AtomicString(name), document);
+}
+
+CSSPropertyRef::CSSPropertyRef(const CSSProperty& property)
+    : property_id_(property.PropertyID()) {
+  if (property.PropertyID() == CSSPropertyVariable) {
+    if (!Variable::IsStaticInstance(property))
+      custom_property_ = static_cast<const CustomProperty&>(property);
+    else
+      property_id_ = CSSPropertyInvalid;
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/css_property_ref.h b/third_party/blink/renderer/core/css/properties/css_property_ref.h
new file mode 100644
index 0000000..2c2d197f
--- /dev/null
+++ b/third_party/blink/renderer/core/css/properties/css_property_ref.h
@@ -0,0 +1,67 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_CSS_PROPERTY_REF_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_CSS_PROPERTY_REF_H_
+
+#include "third_party/blink/renderer/core/css/properties/longhands/custom_property.h"
+
+namespace blink {
+
+class Document;
+
+// Use this class to acquire a reference to a CSSProperty instance. The
+// reference returned by GetProperty() may point to the embedded CustomProperty
+// object, hence this reference is only valid for the lifetime of the
+// CSSPropertyRef object.
+//
+// Usage:
+//
+//   CSSPropertyRef ref(some_string, document);
+//
+//   if (ref.IsValid()) {
+//     LOG(INFO) << ref.GetProperty().GetName();
+//   }
+//
+// Note that any CSSPropertyRef constructor may produce an invalid
+// CSSPropertyRef (e.g. if a non-existent property name is provided), so be
+// sure to always check IsValid() before calling GetProperty().
+class CORE_EXPORT CSSPropertyRef {
+  DISALLOW_NEW();
+
+ public:
+  // Look up (or create) a CSSProperty.
+  //
+  // If the incoming 'name' is not a CSS property, the CSSProperty is invalid.
+  CSSPropertyRef(const String& name, const Document&);
+
+  // If you already have a CSSProperty& object, you may use it to get
+  // a CSSPropertyRef again.
+  //
+  // Note that the CSSProperty& returned by GetProperty() may be different
+  // than the incoming CSSProperty&.
+  //
+  // Note also that this CSSPropertyRef is invalid if the incoming CSSProperty&
+  // is the static Variable instance.
+  CSSPropertyRef(const CSSProperty&);
+
+  bool IsValid() const { return property_id_ != CSSPropertyInvalid; }
+
+  const CSSProperty& GetProperty() const {
+    DCHECK(IsValid());
+    if (property_id_ == CSSPropertyVariable)
+      return custom_property_;
+    return CSSProperty::Get(property_id_);
+  }
+
+  void Trace(blink::Visitor* visitor) { visitor->Trace(custom_property_); }
+
+ private:
+  CSSPropertyID property_id_;
+  CustomProperty custom_property_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_CSS_PROPERTY_REF_H_
diff --git a/third_party/blink/renderer/core/css/properties/css_property_ref_test.cc b/third_party/blink/renderer/core/css/properties/css_property_ref_test.cc
new file mode 100644
index 0000000..20afc3a
--- /dev/null
+++ b/third_party/blink/renderer/core/css/properties/css_property_ref_test.cc
@@ -0,0 +1,78 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/properties/css_property_ref.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/property_descriptor.h"
+#include "third_party/blink/renderer/core/css/property_registration.h"
+#include "third_party/blink/renderer/core/css/property_registry.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+
+namespace blink {
+
+namespace {
+
+class CSSPropertyRefTest : public PageTestBase {
+ public:
+  void RegisterProperty(const String& name,
+                        const String& syntax,
+                        const String& initial_value,
+                        bool is_inherited) {
+    DummyExceptionStateForTesting exception_state;
+    PropertyDescriptor* property_descriptor = PropertyDescriptor::Create();
+    property_descriptor->setName(name);
+    property_descriptor->setSyntax(syntax);
+    property_descriptor->setInitialValue(initial_value);
+    property_descriptor->setInherits(is_inherited);
+    PropertyRegistration::registerProperty(&GetDocument(), property_descriptor,
+                                           exception_state);
+    EXPECT_FALSE(exception_state.HadException());
+  }
+};
+
+}  // namespace
+
+TEST_F(CSSPropertyRefTest, LookupUnregistred) {
+  CSSPropertyRef ref("--x", GetDocument());
+  EXPECT_TRUE(ref.IsValid());
+  EXPECT_EQ(ref.GetProperty().PropertyID(), CSSPropertyVariable);
+}
+
+TEST_F(CSSPropertyRefTest, LookupRegistered) {
+  RegisterProperty("--x", "<length>", "42px", false);
+  CSSPropertyRef ref("--x", GetDocument());
+  EXPECT_TRUE(ref.IsValid());
+  EXPECT_EQ(ref.GetProperty().PropertyID(), CSSPropertyVariable);
+}
+
+TEST_F(CSSPropertyRefTest, LookupStandard) {
+  CSSPropertyRef ref("font-size", GetDocument());
+  EXPECT_TRUE(ref.IsValid());
+  EXPECT_EQ(ref.GetProperty().PropertyID(), CSSPropertyFontSize);
+}
+
+TEST_F(CSSPropertyRefTest, IsValid) {
+  CSSPropertyRef ref("nosuchproperty", GetDocument());
+  EXPECT_FALSE(ref.IsValid());
+}
+
+TEST_F(CSSPropertyRefTest, FromCustomProperty) {
+  CustomProperty custom(AtomicString("--x"), GetDocument());
+  CSSPropertyRef ref(custom);
+  EXPECT_TRUE(ref.IsValid());
+  EXPECT_EQ(ref.GetProperty().PropertyID(), CSSPropertyVariable);
+}
+
+TEST_F(CSSPropertyRefTest, FromStandardProperty) {
+  CSSPropertyRef ref(GetCSSPropertyFontSize());
+  EXPECT_TRUE(ref.IsValid());
+  EXPECT_EQ(ref.GetProperty().PropertyID(), CSSPropertyFontSize);
+}
+
+TEST_F(CSSPropertyRefTest, FromStaticVariableInstance) {
+  CSSPropertyRef ref(GetCSSPropertyVariable());
+  EXPECT_FALSE(ref.IsValid());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc b/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
new file mode 100644
index 0000000..a15acd8
--- /dev/null
+++ b/third_party/blink/renderer/core/css/properties/longhands/custom_property.cc
@@ -0,0 +1,24 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/properties/longhands/custom_property.h"
+
+#include "third_party/blink/renderer/core/css/property_registration.h"
+#include "third_party/blink/renderer/core/css/property_registry.h"
+
+namespace blink {
+
+CustomProperty::CustomProperty(const AtomicString& name,
+                               const Document& document)
+    : name_(name), registration_(PropertyRegistration::From(&document, name)) {}
+
+bool CustomProperty::IsInherited() const {
+  return !registration_ || registration_->Inherits();
+}
+
+const AtomicString& CustomProperty::GetPropertyNameAtomicString() const {
+  return name_;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/custom_property.h b/third_party/blink/renderer/core/css/properties/longhands/custom_property.h
new file mode 100644
index 0000000..4f27041
--- /dev/null
+++ b/third_party/blink/renderer/core/css/properties/longhands/custom_property.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_LONGHANDS_CUSTOM_PROPERTY_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_LONGHANDS_CUSTOM_PROPERTY_H_
+
+#include "third_party/blink/renderer/core/css/properties/longhands/variable.h"
+
+#include "third_party/blink/renderer/core/css/property_registration.h"
+
+namespace blink {
+
+// Represents a custom property (both registered and unregistered).
+//
+// Unlike all other CSSProperty instances, instances of this class are
+// allocated dynamically on demand. (See CSSPropertyRef).
+//
+// TODO(andruud): Move functionality from Variable to here, and eventually
+// remove Variable.
+class CORE_EXPORT CustomProperty : public Variable {
+  DISALLOW_NEW();
+
+ public:
+  CustomProperty() = default;
+  CustomProperty(const AtomicString& name, const Document&);
+
+  bool IsInherited() const override;
+  const AtomicString& GetPropertyNameAtomicString() const override;
+
+  void Trace(blink::Visitor* visitor) { visitor->Trace(registration_); }
+
+ private:
+  AtomicString name_;
+  Member<const PropertyRegistration> registration_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PROPERTIES_LONGHANDS_CUSTOM_PROPERTY_H_
diff --git a/third_party/blink/renderer/core/css/properties/longhands/custom_property_test.cc b/third_party/blink/renderer/core/css/properties/longhands/custom_property_test.cc
new file mode 100644
index 0000000..1a39717
--- /dev/null
+++ b/third_party/blink/renderer/core/css/properties/longhands/custom_property_test.cc
@@ -0,0 +1,69 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/properties/longhands/custom_property.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/property_descriptor.h"
+#include "third_party/blink/renderer/core/css/property_registration.h"
+#include "third_party/blink/renderer/core/css/property_registry.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+
+namespace blink {
+
+namespace {
+
+class CustomPropertyTest : public PageTestBase {
+ public:
+  void RegisterProperty(const String& name,
+                        const String& syntax,
+                        const String& initial_value,
+                        bool is_inherited) {
+    DummyExceptionStateForTesting exception_state;
+    PropertyDescriptor* property_descriptor = PropertyDescriptor::Create();
+    property_descriptor->setName(name);
+    property_descriptor->setSyntax(syntax);
+    property_descriptor->setInitialValue(initial_value);
+    property_descriptor->setInherits(is_inherited);
+    PropertyRegistration::registerProperty(&GetDocument(), property_descriptor,
+                                           exception_state);
+    EXPECT_FALSE(exception_state.HadException());
+  }
+};
+
+}  // namespace
+
+TEST_F(CustomPropertyTest, UnregisteredPropertyIsInherited) {
+  CustomProperty property("--x", GetDocument());
+  EXPECT_TRUE(property.IsInherited());
+}
+
+TEST_F(CustomPropertyTest, RegisteredNonInheritedPropertyIsNotInherited) {
+  RegisterProperty("--x", "<length>", "42px", false);
+  CustomProperty property("--x", GetDocument());
+  EXPECT_FALSE(property.IsInherited());
+}
+
+TEST_F(CustomPropertyTest, RegisteredInheritedPropertyIsInherited) {
+  RegisterProperty("--x", "<length>", "42px", true);
+  CustomProperty property("--x", GetDocument());
+  EXPECT_TRUE(property.IsInherited());
+}
+
+TEST_F(CustomPropertyTest, StaticVariableInstance) {
+  CustomProperty property("--x", GetDocument());
+  EXPECT_FALSE(Variable::IsStaticInstance(property));
+  EXPECT_TRUE(Variable::IsStaticInstance(GetCSSPropertyVariable()));
+}
+
+TEST_F(CustomPropertyTest, PropertyID) {
+  CustomProperty property("--x", GetDocument());
+  EXPECT_EQ(CSSPropertyVariable, property.PropertyID());
+}
+
+TEST_F(CustomPropertyTest, GetPropertyNameAtomicString) {
+  CustomProperty property("--x", GetDocument());
+  EXPECT_EQ(AtomicString("--x"), property.GetPropertyNameAtomicString());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/variable.cc b/third_party/blink/renderer/core/css/properties/longhands/variable.cc
index 780fada..2e53783 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/variable.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/variable.cc
@@ -77,4 +77,8 @@
   }
 }
 
+bool Variable::IsStaticInstance(const CSSProperty& property) {
+  return &property == &GetCSSPropertyVariable();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/properties/longhands/variable.h b/third_party/blink/renderer/core/css/properties/longhands/variable.h
index 6faf1e6..3d308b41 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/variable.h
+++ b/third_party/blink/renderer/core/css/properties/longhands/variable.h
@@ -13,7 +13,9 @@
 
 namespace blink {
 
-class Variable final : public Longhand {
+// TODO(andruud): Remove this class when the static Variable instance
+// (as returned by GetCSSPropertyVariable()) has been removed.
+class CORE_EXPORT Variable : public Longhand {
  public:
   constexpr Variable() : Longhand() {}
 
@@ -37,6 +39,8 @@
 
   void ApplyValue(StyleResolverState& state,
                   const CSSValue& value) const override;
+
+  static bool IsStaticInstance(const CSSProperty&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 5bdd414..d05d8e63 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2744,13 +2744,6 @@
   layout_view_->Compositor()->SetNeedsCompositingUpdate(
       kCompositingUpdateAfterCompositingInputChange);
 
-  {
-    ReattachLegacyLayoutObjectList legacy_layout_objects(*this);
-    AttachContext context;
-    ContainerNode::AttachLayoutTree(context);
-    legacy_layout_objects.ForceLegacyLayoutIfNeeded();
-  }
-
   // The TextAutosizer can't update layout view info while the Document is
   // detached, so update now in case anything changed.
   if (TextAutosizer* autosizer = GetTextAutosizer())
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 3435df9..799f0948 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -466,7 +466,6 @@
 
 void Node::setDistributeScroll(V8ScrollStateCallback* scroll_state_callback,
                                const String& native_scroll_behavior) {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().SetDistributeScroll(
       this, ScrollStateCallbackV8Impl::Create(scroll_state_callback,
                                               native_scroll_behavior));
@@ -474,28 +473,23 @@
 
 void Node::setApplyScroll(V8ScrollStateCallback* scroll_state_callback,
                           const String& native_scroll_behavior) {
-  DCHECK(IsElementNode());
   SetApplyScroll(ScrollStateCallbackV8Impl::Create(scroll_state_callback,
                                                    native_scroll_behavior));
 }
 
 void Node::SetApplyScroll(ScrollStateCallback* scroll_state_callback) {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().SetApplyScroll(this, scroll_state_callback);
 }
 
 void Node::RemoveApplyScroll() {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().RemoveApplyScroll(this);
 }
 
 ScrollStateCallback* Node::GetApplyScroll() {
-  DCHECK(IsElementNode());
   return GetScrollCustomizationCallbacks().GetApplyScroll(this);
 }
 
 void Node::NativeDistributeScroll(ScrollState& scroll_state) {
-  DCHECK(IsElementNode());
   if (scroll_state.FullyConsumed())
     return;
 
@@ -518,10 +512,11 @@
 }
 
 void Node::NativeApplyScroll(ScrollState& scroll_state) {
-  DCHECK(IsElementNode());
+  if (!GetLayoutObject())
+    return;
 
   // All elements in the scroll chain should be boxes.
-  DCHECK(!GetLayoutObject() || GetLayoutObject()->IsBox());
+  DCHECK(GetLayoutObject()->IsBox());
 
   if (scroll_state.FullyConsumed())
     return;
@@ -535,15 +530,7 @@
   // updateStyleAndLayoutIgnorePendingStylesheetsForNode.
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
 
-  LayoutBox* box_to_scroll = nullptr;
-
-  if (this == GetDocument().documentElement())
-    box_to_scroll = GetDocument().GetLayoutView();
-  else if (GetLayoutObject())
-    box_to_scroll = ToLayoutBox(GetLayoutObject());
-
-  if (!box_to_scroll)
-    return;
+  LayoutBox* box_to_scroll = ToLayoutBox(GetLayoutObject());
 
   ScrollableArea* scrollable_area =
       box_to_scroll->EnclosingBox()->GetScrollableArea();
@@ -571,7 +558,6 @@
 
 void Node::CallDistributeScroll(ScrollState& scroll_state) {
   TRACE_EVENT0("input", "Node::CallDistributeScroll");
-  DCHECK(IsElementNode());
   ScrollStateCallback* callback =
       GetScrollCustomizationCallbacks().GetDistributeScroll(this);
 
@@ -607,7 +593,6 @@
 
 void Node::CallApplyScroll(ScrollState& scroll_state) {
   TRACE_EVENT0("input", "Node::CallApplyScroll");
-  DCHECK(IsElementNode());
   // Hits ASSERTs when trying to determine whether we need to scroll on main
   // or CC. http://crbug.com/625676.
   DisableCompositingQueryAsserts disabler;
@@ -652,7 +637,6 @@
 
 void Node::WillBeginCustomizedScrollPhase(
     ScrollCustomization::ScrollDirection direction) {
-  DCHECK(IsElementNode());
   DCHECK(!GetScrollCustomizationCallbacks().InScrollPhase(this));
   LayoutBox* box = GetLayoutBox();
   if (!box)
@@ -666,7 +650,6 @@
 }
 
 void Node::DidEndCustomizedScrollPhase() {
-  DCHECK(IsElementNode());
   GetScrollCustomizationCallbacks().SetInScrollPhase(this, false);
 }
 
@@ -2923,6 +2906,11 @@
   }
 }
 
+bool Node::IsEffectiveRootScroller() const {
+  return GetLayoutObject() ? GetLayoutObject()->IsEffectiveRootScroller()
+                           : false;
+}
+
 WebPluginContainerImpl* Node::GetWebPluginContainer() const {
   if (!IsHTMLObjectElement(this) && !IsHTMLEmbedElement(this)) {
     return nullptr;
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index 5b15e790..1a4925a2 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -861,6 +861,8 @@
     return GetFlag(kInDOMNodeRemovedHandler);
   }
 
+  bool IsEffectiveRootScroller() const;
+
   // If the node is a plugin, then this returns its WebPluginContainer.
   WebPluginContainerImpl* GetWebPluginContainer() const;
 
diff --git a/third_party/blink/renderer/core/events/security_policy_violation_event.cc b/third_party/blink/renderer/core/events/security_policy_violation_event.cc
index 5cf68a65..2893d3b4 100644
--- a/third_party/blink/renderer/core/events/security_policy_violation_event.cc
+++ b/third_party/blink/renderer/core/events/security_policy_violation_event.cc
@@ -36,13 +36,17 @@
 }  // namespace
 
 SecurityPolicyViolationEvent::SecurityPolicyViolationEvent(
-    const AtomicString& type,
-    const SecurityPolicyViolationEventInit* initializer)
+    const AtomicString& type)
     : Event(type, Bubbles::kYes, Cancelable::kNo, ComposedMode::kComposed),
       disposition_(kContentSecurityPolicyHeaderTypeEnforce),
       line_number_(0),
       column_number_(0),
-      status_code_(0) {
+      status_code_(0) {}
+
+SecurityPolicyViolationEvent::SecurityPolicyViolationEvent(
+    const AtomicString& type,
+    const SecurityPolicyViolationEventInit* initializer)
+    : SecurityPolicyViolationEvent(type) {
   if (initializer->hasDocumentURI())
     document_uri_ = initializer->documentURI();
   if (initializer->hasReferrer())
diff --git a/third_party/blink/renderer/core/events/security_policy_violation_event.h b/third_party/blink/renderer/core/events/security_policy_violation_event.h
index 540007f..e46c0220 100644
--- a/third_party/blink/renderer/core/events/security_policy_violation_event.h
+++ b/third_party/blink/renderer/core/events/security_policy_violation_event.h
@@ -37,6 +37,10 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  static SecurityPolicyViolationEvent* Create(const AtomicString& type) {
+    return new SecurityPolicyViolationEvent(type);
+  }
+
   static SecurityPolicyViolationEvent* Create(
       const AtomicString& type,
       const SecurityPolicyViolationEventInit* initializer) {
@@ -63,6 +67,7 @@
   void Trace(blink::Visitor* visitor) override { Event::Trace(visitor); }
 
  private:
+  explicit SecurityPolicyViolationEvent(const AtomicString& type);
   SecurityPolicyViolationEvent(
       const AtomicString& type,
       const SecurityPolicyViolationEventInit* initializer);
diff --git a/third_party/blink/renderer/core/events/security_policy_violation_event.idl b/third_party/blink/renderer/core/events/security_policy_violation_event.idl
index c928820..42afd10 100644
--- a/third_party/blink/renderer/core/events/security_policy_violation_event.idl
+++ b/third_party/blink/renderer/core/events/security_policy_violation_event.idl
@@ -30,7 +30,8 @@
 };
 
 [
-    Constructor(DOMString type, optional SecurityPolicyViolationEventInit eventInitDict)
+  Constructor(DOMString type),
+  Constructor(DOMString type, SecurityPolicyViolationEventInit eventInitDict)
 ] interface SecurityPolicyViolationEvent : Event {
     // TODO(foolip): The spec says "documentURL".
     [Measure] readonly attribute DOMString documentURI;
diff --git a/third_party/blink/renderer/core/events/security_policy_violation_event_init.idl b/third_party/blink/renderer/core/events/security_policy_violation_event_init.idl
index 4230272..47d2cab 100644
--- a/third_party/blink/renderer/core/events/security_policy_violation_event_init.idl
+++ b/third_party/blink/renderer/core/events/security_policy_violation_event_init.idl
@@ -5,18 +5,18 @@
 // https://w3c.github.io/webappsec-csp/#idl-index
 
 dictionary SecurityPolicyViolationEventInit : EventInit {
-    // TODO(foolip): The spec says "documentURL".
-    DOMString documentURI;
-    DOMString referrer;
-    // TODO(foolip): The spec says "blockedURL".
-    DOMString blockedURI;
-    DOMString violatedDirective;
-    DOMString effectiveDirective;
-    DOMString originalPolicy;
-    SecurityPolicyViolationEventDisposition disposition;
-    DOMString sourceFile;
-    unsigned short statusCode;
-    long      lineNumber;
-    long      columnNumber;
-    DOMString sample;
+              // TODO(foolip): The spec says "documentURL".
+    required  DOMString documentURI;
+              DOMString referrer;
+              // TODO(foolip): The spec says "blockedURL".
+              DOMString blockedURI;
+    required  DOMString violatedDirective;
+    required  DOMString effectiveDirective;
+    required  DOMString originalPolicy;
+    required  SecurityPolicyViolationEventDisposition disposition;
+              DOMString sourceFile;
+    required  unsigned short statusCode;
+              long      lineNumber;
+              long      columnNumber;
+              DOMString sample;
 };
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy.cc b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
index ced6f04..afb2f74 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy.cc
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy.cc
@@ -253,15 +253,15 @@
       default_feature_name_map.Set("document-write",
                                    mojom::FeaturePolicyFeature::kDocumentWrite);
       default_feature_name_map.Set(
-          "image-compression", mojom::FeaturePolicyFeature::kImageCompression);
+          "unoptimized-images",
+          mojom::FeaturePolicyFeature::kUnoptimizedImages);
       default_feature_name_map.Set("lazyload",
                                    mojom::FeaturePolicyFeature::kLazyLoad);
       default_feature_name_map.Set(
           "legacy-image-formats",
           mojom::FeaturePolicyFeature::kLegacyImageFormats);
       default_feature_name_map.Set(
-          "max-downscaling-image",
-          mojom::FeaturePolicyFeature::kMaxDownscalingImage);
+          "oversized-images", mojom::FeaturePolicyFeature::kOversizedImages);
       default_feature_name_map.Set("unsized-media",
                                    mojom::FeaturePolicyFeature::kUnsizedMedia);
       default_feature_name_map.Set(
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index 2775a24..51884a1 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -709,8 +709,9 @@
         "Policy directive: ",
         script_state, exception_status, content);
   }
-  return CheckEval(
-      OperativeDirective(ContentSecurityPolicy::DirectiveType::kScriptSrc));
+  return IsReportOnly() ||
+         CheckEval(OperativeDirective(
+             ContentSecurityPolicy::DirectiveType::kScriptSrc));
 }
 
 bool CSPDirectiveList::AllowWasmEval(
@@ -726,8 +727,9 @@
         "Content Security Policy directive: ",
         script_state, exception_status, content);
   }
-  return CheckWasmEval(
-      OperativeDirective(ContentSecurityPolicy::DirectiveType::kScriptSrc));
+  return IsReportOnly() ||
+         CheckWasmEval(OperativeDirective(
+             ContentSecurityPolicy::DirectiveType::kScriptSrc));
 }
 
 bool CSPDirectiveList::AllowPluginType(
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index ec6c016..bf49675 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2086,7 +2086,7 @@
     viewport_scrollable_area_ = root_frame_viewport;
 
     page->GlobalRootScrollerController().InitializeViewportScrollCallback(
-        *root_frame_viewport);
+        *root_frame_viewport, *frame_->GetDocument());
   }
 }
 
@@ -3545,14 +3545,12 @@
       !frame_->GetDocument() || !frame_->GetPage())
     return false;
 
-  const TopDocumentRootScrollerController& controller =
-      frame_->GetPage()->GlobalRootScrollerController();
-
   if (!LayoutViewport())
     return false;
 
-  return root_scroller_util::ScrollableAreaForRootScroller(
-             controller.GlobalRootScroller()) == LayoutViewport();
+  const TopDocumentRootScrollerController& controller =
+      frame_->GetPage()->GlobalRootScrollerController();
+  return controller.RootScrollerArea() == LayoutViewport();
 }
 
 AXObjectCache* LocalFrameView::ExistingAXObjectCache() const {
diff --git a/third_party/blink/renderer/core/input/scroll_manager.cc b/third_party/blink/renderer/core/input/scroll_manager.cc
index 81dfda6..8305daaf 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.cc
+++ b/third_party/blink/renderer/core/input/scroll_manager.cc
@@ -86,14 +86,14 @@
 void ScrollManager::Trace(blink::Visitor* visitor) {
   visitor->Trace(frame_);
   visitor->Trace(scroll_gesture_handling_node_);
-  visitor->Trace(previous_gesture_scrolled_element_);
+  visitor->Trace(previous_gesture_scrolled_node_);
   visitor->Trace(scrollbar_handling_scroll_gesture_);
   visitor->Trace(resize_scrollable_area_);
 }
 
 void ScrollManager::ClearGestureScrollState() {
   scroll_gesture_handling_node_ = nullptr;
-  previous_gesture_scrolled_element_ = nullptr;
+  previous_gesture_scrolled_node_ = nullptr;
   delta_consumed_for_scroll_sequence_ = false;
   did_scroll_x_for_scroll_gesture_ = false;
   did_scroll_y_for_scroll_gesture_ = false;
@@ -127,9 +127,8 @@
   return nullptr;
 }
 
-static bool CanPropagate(const ScrollState& scroll_state,
-                         const Element& element) {
-  ScrollableArea* scrollable_area = element.GetLayoutBox()->GetScrollableArea();
+static bool CanPropagate(const ScrollState& scroll_state, const Node& node) {
+  ScrollableArea* scrollable_area = node.GetLayoutBox()->GetScrollableArea();
   if (!scrollable_area)
     return true;
 
@@ -138,10 +137,10 @@
     return true;
 
   return (scroll_state.deltaXHint() == 0 ||
-          element.GetComputedStyle()->OverscrollBehaviorX() ==
+          node.GetComputedStyle()->OverscrollBehaviorX() ==
               EOverscrollBehavior::kAuto) &&
          (scroll_state.deltaYHint() == 0 ||
-          element.GetComputedStyle()->OverscrollBehaviorY() ==
+          node.GetComputedStyle()->OverscrollBehaviorY() ==
               EOverscrollBehavior::kAuto);
 }
 
@@ -153,38 +152,27 @@
 
   DCHECK(start_node.GetLayoutObject());
   LayoutBox* cur_box = start_node.GetLayoutObject()->EnclosingBox();
-  Element* document_element = frame_->GetDocument()->documentElement();
 
   // Scrolling propagates along the containing block chain and ends at the
-  // RootScroller element. The RootScroller element will have a custom
-  // applyScroll callback that scrolls the frame or element.
+  // RootScroller node. The RootScroller node will have a custom applyScroll
+  // callback that performs scrolling as well as associated "root" actions like
+  // browser control movement and overscroll glow.
   while (cur_box) {
     Node* cur_node = cur_box->GetNode();
-    Element* cur_element = nullptr;
 
-    // FIXME: this should reject more elements, as part of crbug.com/410974.
-    if (cur_node && cur_node->IsElementNode()) {
-      cur_element = ToElement(cur_node);
-    } else if (cur_node && cur_node->IsDocumentNode()) {
-      // In normal circumastances, the documentElement will be the root
-      // scroller but the documentElement itself isn't a containing block,
-      // that'll be the document node rather than the element.
-      cur_element = document_element;
-    }
+    if (cur_node) {
+      if (CanScroll(scroll_state, *cur_node))
+        scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
 
-    if (cur_element) {
-      if (CanScroll(scroll_state, *cur_element))
-        scroll_chain.push_front(DOMNodeIds::IdForNode(cur_element));
-      if (IsViewportScrollingElement(*cur_element) ||
-          cur_element == document_element)
+      if (cur_node->IsEffectiveRootScroller())
         break;
 
-      if (!CanPropagate(scroll_state, *cur_element)) {
+      if (!CanPropagate(scroll_state, *cur_node)) {
         // We should add the first node with non-auto overscroll-behavior to
         // the scroll chain regardlessly, as it's the only node we can latch to.
         if (scroll_chain.empty() ||
-            scroll_chain.front() != DOMNodeIds::IdForNode(cur_element)) {
-          scroll_chain.push_front(DOMNodeIds::IdForNode(cur_element));
+            scroll_chain.front() != DOMNodeIds::IdForNode(cur_node)) {
+          scroll_chain.push_front(DOMNodeIds::IdForNode(cur_node));
         }
         break;
       }
@@ -195,25 +183,18 @@
 }
 
 bool ScrollManager::CanScroll(const ScrollState& scroll_state,
-                              const Element& current_element) {
-  ScrollableArea* scrollable_area = nullptr;
-  if (IsViewportScrollingElement(current_element) ||
-      current_element == *(frame_->GetDocument()->documentElement())) {
-    if (!current_element.GetLayoutObject())
-      return false;
+                              const Node& current_node) {
+  if (!current_node.GetLayoutBox())
+    return false;
 
-    if (frame_->IsMainFrame())
-      return true;
+  // We need to always add the root scroller in the main frame even if the
+  // viewport isn't scrollable since we can always pinch-zoom and scroll as
+  // well as for overscroll effects.
+  if (current_node.IsEffectiveRootScroller() && frame_->IsMainFrame())
+    return true;
 
-    // For subframes, the viewport is added to the scroll chain only if it can
-    // actually consume some delta hints. The main frame always gets added
-    // since it produces overscroll effects.
-    scrollable_area =
-        frame_->View() ? frame_->View()->GetScrollableArea() : nullptr;
-  }
-
-  if (!scrollable_area && current_element.GetLayoutBox())
-    scrollable_area = current_element.GetLayoutBox()->GetScrollableArea();
+  ScrollableArea* scrollable_area =
+      current_node.GetLayoutBox()->GetScrollableArea();
 
   if (!scrollable_area)
     return false;
@@ -288,8 +269,6 @@
       DCHECK(main_frame.IsMainFrame());
 
       scrollable_area = main_frame.View()->GetScrollableArea();
-    } else if (node == document.documentElement()) {
-      scrollable_area = document.GetLayoutView()->GetScrollableArea();
     } else {
       scrollable_area = box->GetScrollableArea();
     }
@@ -427,7 +406,7 @@
         scroll_gesture_handling_node_->ParentOrShadowHostNode();
 
   if (!scroll_gesture_handling_node_)
-    scroll_gesture_handling_node_ = frame_->GetDocument()->documentElement();
+    scroll_gesture_handling_node_ = frame_->GetDocument();
 
   if (!scroll_gesture_handling_node_ ||
       !scroll_gesture_handling_node_->GetLayoutObject()) {
@@ -490,7 +469,7 @@
   if (!node || !node->GetLayoutObject()) {
     TRACE_EVENT_INSTANT0("input", "Lost scroll_gesture_handling_node",
                          TRACE_EVENT_SCOPE_THREAD);
-    if (previous_gesture_scrolled_element_) {
+    if (previous_gesture_scrolled_node_) {
       // When the scroll_gesture_handling_node_ gets deleted in the middle of
       // scrolling call HandleGestureScrollEvent to start scrolling a new node
       // if possible.
@@ -565,18 +544,17 @@
   scroll_state_data->delta_consumed_for_scroll_sequence =
       delta_consumed_for_scroll_sequence_;
   ScrollState* scroll_state = ScrollState::Create(std::move(scroll_state_data));
-  if (previous_gesture_scrolled_element_) {
+  if (previous_gesture_scrolled_node_) {
     // The ScrollState needs to know what the current
     // native scrolling element is, so that for an
     // inertial scroll that shouldn't propagate, only the
     // currently scrolling element responds.
     scroll_state->SetCurrentNativeScrollingNode(
-        previous_gesture_scrolled_element_);
+        previous_gesture_scrolled_node_);
   }
 
   CustomizedScroll(*scroll_state);
-  previous_gesture_scrolled_element_ =
-      ToElement(scroll_state->CurrentNativeScrollingNode());
+  previous_gesture_scrolled_node_ = scroll_state->CurrentNativeScrollingNode();
   delta_consumed_for_scroll_sequence_ =
       scroll_state->DeltaConsumedForScrollSequence();
 
@@ -586,9 +564,11 @@
   did_scroll_x_for_scroll_gesture_ |= did_scroll_x;
   did_scroll_y_for_scroll_gesture_ |= did_scroll_y;
 
-  if ((!previous_gesture_scrolled_element_ ||
-       !IsViewportScrollingElement(*previous_gesture_scrolled_element_)) &&
-      GetPage())
+  // TODO(bokan): This looks like it resets overscroll if any *effective* root
+  // scroller is scrolled. This should probably be if the *global* root
+  // scroller is scrolled.
+  if (!previous_gesture_scrolled_node_ ||
+      !previous_gesture_scrolled_node_->IsEffectiveRootScroller())
     GetPage()->GetOverscrollController().ResetAccumulated(did_scroll_x,
                                                           did_scroll_y);
 
@@ -631,16 +611,13 @@
 }
 
 LayoutBox* ScrollManager::LayoutBoxForSnapping() const {
-  if (!previous_gesture_scrolled_element_)
+  if (!previous_gesture_scrolled_node_)
     return nullptr;
-  Element* document_element = frame_->GetDocument()->documentElement();
-  return previous_gesture_scrolled_element_ == document_element
-             ? frame_->GetDocument()->GetLayoutView()
-             : previous_gesture_scrolled_element_->GetLayoutBox();
+  return previous_gesture_scrolled_node_->GetLayoutBox();
 }
 
 void ScrollManager::SnapAtGestureScrollEnd() {
-  if (!previous_gesture_scrolled_element_ ||
+  if (!previous_gesture_scrolled_node_ ||
       (!did_scroll_x_for_scroll_gesture_ && !did_scroll_y_for_scroll_gesture_))
     return;
   SnapCoordinator* snap_coordinator =
@@ -699,8 +676,7 @@
   scroll_state_data->delta_consumed_for_scroll_sequence =
       delta_consumed_for_scroll_sequence_;
   ScrollState* scroll_state = ScrollState::Create(std::move(scroll_state_data));
-  scroll_state->SetCurrentNativeScrollingNode(
-      previous_gesture_scrolled_element_);
+  scroll_state->SetCurrentNativeScrollingNode(previous_gesture_scrolled_node_);
 
   CustomizedScroll(*scroll_state);
   FloatPoint end_position = scrollable_area->ScrollPosition();
@@ -761,16 +737,6 @@
       .HandleGestureScrollEvent(gesture_event);
 }
 
-bool ScrollManager::IsViewportScrollingElement(const Element& element) const {
-  // The root scroller is the one Element on the page designated to perform
-  // "viewport actions" like browser controls movement and overscroll glow.
-  if (!frame_->GetDocument())
-    return false;
-
-  return frame_->GetDocument()->GetRootScrollerController().ScrollsViewport(
-      element);
-}
-
 WebInputEventResult ScrollManager::HandleGestureScrollEvent(
     const WebGestureEvent& gesture_event) {
   if (!frame_->View())
@@ -806,7 +772,7 @@
     last_gesture_scroll_over_embedded_content_view_ =
         result.IsOverEmbeddedContentView();
     scroll_gesture_handling_node_ = event_target;
-    previous_gesture_scrolled_element_ = nullptr;
+    previous_gesture_scrolled_node_ = nullptr;
     delta_consumed_for_scroll_sequence_ = false;
     did_scroll_x_for_scroll_gesture_ = false;
     did_scroll_y_for_scroll_gesture_ = false;
@@ -977,16 +943,16 @@
           scroll_state.deltaXHint(), scroll_state.deltaYHint());
   for (auto id : current_scroll_chain_) {
     Node* node = DOMNodeIds::NodeForId(id);
-    if (node && node->IsElementNode())
-      ToElement(node)->WillBeginCustomizedScrollPhase(direction);
+    if (node)
+      node->WillBeginCustomizedScrollPhase(direction);
   }
 }
 
 void ScrollManager::NotifyScrollPhaseEndForCustomizedScroll() {
   for (auto id : current_scroll_chain_) {
     Node* node = DOMNodeIds::NodeForId(id);
-    if (node && node->IsElementNode())
-      ToElement(node)->DidEndCustomizedScrollPhase();
+    if (node)
+      node->DidEndCustomizedScrollPhase();
   }
 }
 
diff --git a/third_party/blink/renderer/core/input/scroll_manager.h b/third_party/blink/renderer/core/input/scroll_manager.h
index 7223e31..c76f8eb 100644
--- a/third_party/blink/renderer/core/input/scroll_manager.h
+++ b/third_party/blink/renderer/core/input/scroll_manager.h
@@ -117,14 +117,12 @@
 
   Page* GetPage() const;
 
-  bool IsViewportScrollingElement(const Element&) const;
-
   bool HandleScrollGestureOnResizer(Node*, const WebGestureEvent&);
 
   void RecomputeScrollChain(const Node& start_node,
                             const ScrollState&,
                             std::deque<DOMNodeId>& scroll_chain);
-  bool CanScroll(const ScrollState&, const Element& current_element);
+  bool CanScroll(const ScrollState&, const Node& current_node);
 
   // scroller_size is set only when scrolling non root scroller.
   void ComputeScrollRelatedMetrics(
@@ -153,11 +151,11 @@
 
   bool last_gesture_scroll_over_embedded_content_view_;
 
-  // The most recent element to scroll natively during this scroll
+  // The most recent Node to scroll natively during this scroll
   // sequence. Null if no native element has scrolled this scroll
   // sequence, or if the most recent element to scroll used scroll
   // customization.
-  Member<Element> previous_gesture_scrolled_element_;
+  Member<Node> previous_gesture_scrolled_node_;
 
   // True iff some of the delta has been consumed for the current
   // scroll sequence in this frame, or any child frames. Only used
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 07d46b5..2db0196 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -50,13 +50,14 @@
 namespace blink {
 
 namespace {
-constexpr float kmax_downscaling_ratio = 2.0f;
+constexpr float kmax_oversize_ratio = 2.0f;
 
 bool CheckForOptimizedImagePolicy(const LocalFrame& frame,
                                   LayoutImage* layout_image,
                                   ImageResourceContent* new_image) {
-  // Invert the image if the document does not have the 'legacy-image-formats'
-  // feature enabled, and the image is not one of the allowed formats.
+  // Render the image as a placeholder image if the document does not have the
+  // 'legacy-image-formats' feature enabled, and the image is not one of the
+  // allowed formats.
   if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled() &&
       !frame.DeprecatedIsFeatureEnabled(
           mojom::FeaturePolicyFeature::kLegacyImageFormats)) {
@@ -64,28 +65,29 @@
       return true;
     }
   }
-  // Invert the image if the document does not have the image-compression'
-  // feature enabled and the image is not sufficiently-well-compressed.
+  // Render the image as a placeholder image if the document does not have the
+  // 'unoptimized-images' feature enabled and the image is not
+  // sufficiently-well-compressed.
   if (RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled() &&
       !frame.DeprecatedIsFeatureEnabled(
-          mojom::FeaturePolicyFeature::kImageCompression)) {
+          mojom::FeaturePolicyFeature::kUnoptimizedImages)) {
     if (!new_image->IsAcceptableCompressionRatio())
       return true;
   }
   return false;
 }
 
-bool CheckForMaxDownscalingImagePolicy(const LocalFrame& frame,
-                                       ImageResourceContent* new_image,
-                                       LayoutImage* layout_image) {
+bool CheckForOversizedImagesPolicy(const LocalFrame& frame,
+                                   ImageResourceContent* new_image,
+                                   LayoutImage* layout_image) {
   DCHECK(new_image);
   if (!RuntimeEnabledFeatures::ExperimentalProductivityFeaturesEnabled() ||
       frame.DeprecatedIsFeatureEnabled(
-          mojom::FeaturePolicyFeature::kMaxDownscalingImage))
+          mojom::FeaturePolicyFeature::kOversizedImages))
     return false;
   if (auto* image = new_image->GetImage()) {
-    // Invert the image if the image's size is more than 2 times bigger than the
-    // size it is being laid-out by.
+    // Render the image as a placeholder image if the image's size is more
+    // than 2 times bigger than the size it is being laid-out by.
     LayoutUnit layout_width = layout_image->ContentWidth();
     LayoutUnit layout_height = layout_image->ContentHeight();
     int image_width = image->width();
@@ -94,9 +96,9 @@
     if (layout_width > 0 && layout_height > 0 && image_width > 0 &&
         image_height > 0) {
       double device_pixel_ratio = frame.DevicePixelRatio();
-      if (LayoutUnit(image_width / (kmax_downscaling_ratio *
-                                    device_pixel_ratio)) > layout_width ||
-          LayoutUnit(image_height / (kmax_downscaling_ratio *
+      if (LayoutUnit(image_width / (kmax_oversize_ratio * device_pixel_ratio)) >
+              layout_width ||
+          LayoutUnit(image_height / (kmax_oversize_ratio *
                                      device_pixel_ratio)) > layout_height)
         return true;
     }
@@ -113,8 +115,8 @@
       did_increment_visually_non_empty_pixel_count_(false),
       is_generated_content_(false),
       image_device_pixel_ratio_(1.0f),
-      is_legacy_format_or_compressed_image_(false),
-      is_downscaled_image_(false) {}
+      is_legacy_format_or_unoptimized_image_(false),
+      is_oversized_image_(false) {}
 
 LayoutImage* LayoutImage::CreateAnonymous(PseudoElement& pseudo) {
   LayoutImage* image = new LayoutImage(nullptr);
@@ -274,11 +276,11 @@
   // Check for optimized image policies.
   if (View() && View()->GetFrameView()) {
     const LocalFrame& frame = View()->GetFrameView()->GetFrame();
-    is_legacy_format_or_compressed_image_ =
+    is_legacy_format_or_unoptimized_image_ =
         CheckForOptimizedImagePolicy(frame, this, new_image);
     if (auto* image_element = ToHTMLImageElementOrNull(GetNode())) {
-      is_downscaled_image_ =
-          CheckForMaxDownscalingImagePolicy(frame, new_image, this);
+      is_oversized_image_ =
+          CheckForOversizedImagesPolicy(frame, new_image, this);
     }
   }
 
@@ -490,7 +492,7 @@
 }
 
 bool LayoutImage::IsImagePolicyViolated() const {
-  return is_downscaled_image_ || is_legacy_format_or_compressed_image_;
+  return is_oversized_image_ || is_legacy_format_or_unoptimized_image_;
 }
 
 void LayoutImage::UpdateAfterLayout() {
@@ -502,7 +504,7 @@
 
       if (image_resource_ && image_resource_->CachedImage()) {
         // Check for optimized image policies.
-        is_downscaled_image_ = CheckForMaxDownscalingImagePolicy(
+        is_oversized_image_ = CheckForOversizedImagesPolicy(
             frame, image_resource_->CachedImage(), this);
       }
     }
diff --git a/third_party/blink/renderer/core/layout/layout_image.h b/third_party/blink/renderer/core/layout/layout_image.h
index cc59319..1992fb0 100644
--- a/third_party/blink/renderer/core/layout/layout_image.h
+++ b/third_party/blink/renderer/core/layout/layout_image.h
@@ -159,10 +159,10 @@
   float image_device_pixel_ratio_;
 
   // These flags indicate if the image violates one or more optimized image
-  // policies. When any policy is violated, the image should be rendered with
-  // inverted color.
-  bool is_legacy_format_or_compressed_image_;
-  bool is_downscaled_image_;
+  // policies. When any policy is violated, the image should be rendered as a
+  // placeholder image.
+  bool is_legacy_format_or_unoptimized_image_;
+  bool is_oversized_image_;
 };
 
 DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutImage, IsLayoutImage());
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
index f9f1759..f8f00e4 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
@@ -210,7 +210,7 @@
   // border-box relative, flip it around the size of the border box, rather than
   // the size of the containing block (padding box).
   LayoutUnit block_top_or_left =
-      parent_style->IsFlippedBlocksWritingMode()
+      container_style->IsFlippedBlocksWritingMode()
           ? container_border_box_logical_height - static_block
           : static_block;
 
@@ -221,7 +221,7 @@
           ? NGPhysicalOffset(inline_left_or_top, block_top_or_left)
           : NGPhysicalOffset(block_top_or_left, inline_left_or_top);
   NGStaticPosition static_position =
-      NGStaticPosition::Create(parent_style->GetWritingMode(),
+      NGStaticPosition::Create(container_style->GetWritingMode(),
                                parent_style->Direction(), static_location);
 
   // Set correct container for inline containing blocks.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 9a04a84..75bc75b 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -190,6 +190,7 @@
       // TODO(layoutng): Figure out why these two call can't be inside the
       // !constraint_space.IsIntermediateLayout() block below.
       UpdateShapeOutsideInfoIfNeeded(
+          *layout_result,
           constraint_space.PercentageResolutionSize().inline_size);
       // We may need paint invalidation even if we can reuse layout, as our
       // paint offset/visual rect may have changed due to relative
@@ -263,6 +264,19 @@
     FinishLayout(block_flow, constraint_space, break_token, layout_result);
   }
 
+  // We always need to update the ShapeOutsideInfo even if the layout is
+  // intermediate (e.g. called during a min/max pass).
+  //
+  // If a shape-outside float is present in an orthogonal flow, when
+  // calculating the min/max-size (by performing an intermediate layout), we
+  // might calculate this incorrectly, as the layout won't take into account the
+  // shape-outside area.
+  //
+  // TODO(ikilpatrick): This should be fixed by moving the shape-outside data
+  // to the NGLayoutResult, removing this "side" data-structure.
+  UpdateShapeOutsideInfoIfNeeded(
+      *layout_result, constraint_space.PercentageResolutionSize().inline_size);
+
   return layout_result;
 }
 
@@ -585,9 +599,6 @@
   box_->UpdateAfterLayout();
   box_->ClearNeedsLayout();
 
-  UpdateShapeOutsideInfoIfNeeded(
-      constraint_space.PercentageResolutionSize().inline_size);
-
   // Overflow computation depends on this being set.
   if (box_->IsLayoutBlockFlow()) {
     LayoutBlockFlow* block_flow = ToLayoutBlockFlow(box_);
@@ -877,9 +888,12 @@
   builder.SetPadding(padding);
 
   CopyBaselinesFromOldLayout(constraint_space, &builder);
+
+  scoped_refptr<NGLayoutResult> layout_result = builder.ToBoxFragment();
   UpdateShapeOutsideInfoIfNeeded(
-      constraint_space.PercentageResolutionSize().inline_size);
-  return builder.ToBoxFragment();
+      *layout_result, constraint_space.PercentageResolutionSize().inline_size);
+
+  return layout_result;
 }
 
 void NGBlockNode::CopyBaselinesFromOldLayout(
@@ -942,19 +956,24 @@
 // current shape machinery requires setting the size of the float after layout
 // in the parents writing mode.
 void NGBlockNode::UpdateShapeOutsideInfoIfNeeded(
+    const NGLayoutResult& layout_result,
     LayoutUnit percentage_resolution_inline_size) {
   if (!box_->IsFloating() || !box_->GetShapeOutsideInfo())
     return;
 
+  // The box_ may not have a valid size yet (due to an intermediate layout),
+  // use the fragment's size instead.
+  DCHECK(layout_result.PhysicalFragment());
+  LayoutSize box_size = layout_result.PhysicalFragment()->Size().ToLayoutSize();
+
   // TODO(ikilpatrick): Ideally this should be moved to a NGLayoutResult
   // computing the shape area. There may be an issue with the new fragmentation
   // model and computing the correct sizes of shapes.
   ShapeOutsideInfo* shape_outside = box_->GetShapeOutsideInfo();
   LayoutBlock* containing_block = box_->ContainingBlock();
   shape_outside->SetReferenceBoxLogicalSize(
-      containing_block->IsHorizontalWritingMode()
-          ? box_->Size()
-          : box_->Size().TransposedSize());
+      containing_block->IsHorizontalWritingMode() ? box_size
+                                                  : box_size.TransposedSize());
   shape_outside->SetPercentageResolutionInlineSize(
       percentage_resolution_inline_size);
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index 8aff0dfa..38826d98 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -136,6 +136,7 @@
                                                const NGConstraintSpace&);
 
   void UpdateShapeOutsideInfoIfNeeded(
+      const NGLayoutResult&,
       LayoutUnit percentage_resolution_inline_size);
 };
 
diff --git a/third_party/blink/renderer/core/layout/pointer_events_hit_rules.cc b/third_party/blink/renderer/core/layout/pointer_events_hit_rules.cc
index de77d980..adb53d3 100644
--- a/third_party/blink/renderer/core/layout/pointer_events_hit_rules.cc
+++ b/third_party/blink/renderer/core/layout/pointer_events_hit_rules.cc
@@ -48,7 +48,7 @@
       case EPointerEvents::kBoundingBox:
         can_hit_bounding_box = true;
         break;
-      case EPointerEvents::kVisiblePainted:
+      case EPointerEvents::kVisiblepainted:
       case EPointerEvents::kAuto:  // "auto" is like "visiblePainted" when in
                                    // SVG content
         require_fill = true;
@@ -59,11 +59,11 @@
         can_hit_fill = true;
         can_hit_stroke = true;
         break;
-      case EPointerEvents::kVisibleFill:
+      case EPointerEvents::kVisiblefill:
         require_visible = true;
         can_hit_fill = true;
         break;
-      case EPointerEvents::kVisibleStroke:
+      case EPointerEvents::kVisiblestroke:
         require_visible = true;
         can_hit_stroke = true;
         break;
@@ -90,7 +90,7 @@
       case EPointerEvents::kBoundingBox:
         can_hit_bounding_box = true;
         break;
-      case EPointerEvents::kVisiblePainted:
+      case EPointerEvents::kVisiblepainted:
       case EPointerEvents::kAuto:  // "auto" is like "visiblePainted" when in
                                    // SVG content
         require_visible = true;
@@ -99,8 +99,8 @@
         can_hit_fill = true;
         can_hit_stroke = true;
         break;
-      case EPointerEvents::kVisibleFill:
-      case EPointerEvents::kVisibleStroke:
+      case EPointerEvents::kVisiblefill:
+      case EPointerEvents::kVisiblestroke:
       case EPointerEvents::kVisible:
         require_visible = true;
         can_hit_fill = true;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
index 9047e4a0..62a15b2e 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.cc
@@ -469,19 +469,6 @@
   return LayoutRect(EnclosingIntRect(visual_rect));
 }
 
-bool LayoutSVGRoot::PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const {
-  // The rule extends LayoutBox's instead of LayoutReplaced's.
-  if (!LayoutBox::PaintedOutputOfObjectHasNoEffectRegardlessOfSize())
-    return false;
-
-  if (SVGResources* resources =
-          SVGResourcesCache::CachedResourcesForLayoutObject(*this)) {
-    if (resources->Masker())
-      return false;
-  }
-  return true;
-}
-
 // This method expects local CSS box coordinates.
 // Callers with local SVG viewport coordinates should first apply the
 // localToBorderBoxTransform to convert from SVG viewport coordinates to local
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
index a79fff4..cb516fc 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root.h
@@ -91,8 +91,6 @@
 
   const char* GetName() const override { return "LayoutSVGRoot"; }
 
-  bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const final;
-
  private:
   bool ComputeShouldClipOverflow() const override {
     return LayoutBox::ComputeShouldClipOverflow() || ShouldApplyViewportClip();
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
index 3887f1e..1548d8e 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_root_test.cc
@@ -134,7 +134,7 @@
 
   const LayoutSVGRoot& root =
       *ToLayoutSVGRoot(GetLayoutObjectByElementId("svg"));
-  EXPECT_TRUE(root.PaintedOutputOfObjectHasNoEffectRegardlessOfSize());
+  EXPECT_FALSE(root.PaintedOutputOfObjectHasNoEffectRegardlessOfSize());
 }
 
 TEST_F(LayoutSVGRootTest,
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
index a6e7a0b..388e2f7 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
@@ -111,9 +111,7 @@
 }
 
 RootScrollerController::RootScrollerController(Document& document)
-    : document_(&document),
-      effective_root_scroller_(&document),
-      document_has_document_element_(false) {}
+    : document_(&document), effective_root_scroller_(&document) {}
 
 void RootScrollerController::Trace(blink::Visitor* visitor) {
   visitor->Trace(document_);
@@ -189,50 +187,38 @@
       new_effective_root_scroller = implicit_root_scroller_;
   }
 
-  // TODO(bokan): This is a terrible hack but required because the viewport
-  // apply scroll works on Elements rather than Nodes. If we're going from
-  // !documentElement to documentElement, we can't early out even if the root
-  // scroller didn't change since the global root scroller didn't have an
-  // Element previously to put it's ViewportScrollCallback onto. We need this
-  // to kick the global root scroller to recompute itself. We can remove this
-  // if ScrollCustomization is moved to the Node rather than Element.
-  bool old_has_document_element = document_has_document_element_;
-  document_has_document_element_ = document_->documentElement();
-
-  if (old_has_document_element || !document_has_document_element_) {
-    if (effective_root_scroller_ == new_effective_root_scroller)
-      return;
-  }
+  if (effective_root_scroller_ == new_effective_root_scroller)
+    return;
 
   Node* old_effective_root_scroller = effective_root_scroller_;
   effective_root_scroller_ = new_effective_root_scroller;
 
-  if (new_effective_root_scroller != old_effective_root_scroller) {
-    if (LayoutBoxModelObject* new_obj =
-            new_effective_root_scroller->GetLayoutBoxModelObject()) {
-      if (new_obj->Layer()) {
-        new_effective_root_scroller->GetLayoutBoxModelObject()
-            ->Layer()
-            ->SetNeedsCompositingInputsUpdate();
-      }
+  DCHECK(new_effective_root_scroller);
+  if (LayoutBoxModelObject* new_obj =
+          new_effective_root_scroller->GetLayoutBoxModelObject()) {
+    if (new_obj->Layer()) {
+      new_effective_root_scroller->GetLayoutBoxModelObject()
+          ->Layer()
+          ->SetNeedsCompositingInputsUpdate();
     }
-    if (old_effective_root_scroller) {
-      if (LayoutBoxModelObject* old_obj =
-              old_effective_root_scroller->GetLayoutBoxModelObject()) {
-        if (old_obj->Layer()) {
-          old_effective_root_scroller->GetLayoutBoxModelObject()
-              ->Layer()
-              ->SetNeedsCompositingInputsUpdate();
-        }
-      }
-    }
-    if (auto* object = old_effective_root_scroller->GetLayoutObject())
-      object->SetIsEffectiveRootScroller(false);
-
-    if (auto* object = new_effective_root_scroller->GetLayoutObject())
-      object->SetIsEffectiveRootScroller(true);
   }
 
+  DCHECK(old_effective_root_scroller);
+  if (LayoutBoxModelObject* old_obj =
+          old_effective_root_scroller->GetLayoutBoxModelObject()) {
+    if (old_obj->Layer()) {
+      old_effective_root_scroller->GetLayoutBoxModelObject()
+          ->Layer()
+          ->SetNeedsCompositingInputsUpdate();
+    }
+  }
+
+  if (auto* object = old_effective_root_scroller->GetLayoutObject())
+    object->SetIsEffectiveRootScroller(false);
+
+  if (auto* object = new_effective_root_scroller->GetLayoutObject())
+    object->SetIsEffectiveRootScroller(true);
+
   ApplyRootScrollerProperties(*old_effective_root_scroller);
   ApplyRootScrollerProperties(*effective_root_scroller_);
 
@@ -419,13 +405,6 @@
       effective_root_scroller_);
 }
 
-bool RootScrollerController::ScrollsViewport(const Element& element) const {
-  if (effective_root_scroller_->IsDocumentNode())
-    return element == document_->documentElement();
-
-  return element == effective_root_scroller_.Get();
-}
-
 void RootScrollerController::ElementRemoved(const Element& element) {
   if (element != effective_root_scroller_.Get())
     return;
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
index cdffaa1..07daee1e 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h
@@ -78,13 +78,6 @@
   // scroller.
   PaintLayer* RootScrollerPaintLayer() const;
 
-  // Used to determine which Element should scroll the viewport.  This is
-  // needed since Blink's scrolling machinery works on Elements whereas the
-  // document *Node* also scrolls so we need to designate an element one
-  // Element as the viewport scroller. Sadly, this is *not* the
-  // document.scrollingElement in general.
-  bool ScrollsViewport(const Element&) const;
-
   void ElementRemoved(const Element&);
 
   // In the "implicit root scroller" mode, we might promote an element to
@@ -162,8 +155,6 @@
   HeapHashSet<WeakMember<Element>> implicit_candidates_;
 
   WeakMember<Element> implicit_root_scroller_;
-
-  bool document_has_document_element_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 090e705..7c5ea06 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -201,16 +201,9 @@
 TEST_F(RootScrollerTest, TestDefaultRootScroller) {
   Initialize("overflow-scrolling.html");
 
-  RootScrollerController& controller =
-      MainFrame()->GetDocument()->GetRootScrollerController();
-
   ASSERT_EQ(nullptr, MainFrame()->GetDocument()->rootScroller());
-
   EXPECT_EQ(MainFrame()->GetDocument(),
             EffectiveRootScroller(MainFrame()->GetDocument()));
-
-  Element* html_element = MainFrame()->GetDocument()->documentElement();
-  EXPECT_TRUE(controller.ScrollsViewport(*html_element));
 }
 
 // Make sure that replacing the documentElement doesn't change the effective
@@ -542,13 +535,13 @@
   const TopDocumentRootScrollerController& main_controller =
       MainFrame()->GetDocument()->GetPage()->GlobalRootScrollerController();
 
-  // No root scroller set, the documentElement should be the effective root
-  // and the main LocalFrameView's scroll layer should be the layer to use.
+  // No root scroller set, the document node should be the global root and the
+  // main LocalFrameView's scroll layer should be the layer to use.
   {
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               MainFrameView()->LayoutViewport()->LayerForScrolling());
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
   }
 
   // Set a root scroller in the iframe. Since the main document didn't set a
@@ -559,7 +552,7 @@
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               MainFrameView()->LayoutViewport()->LayerForScrolling());
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
   }
 
   // Setting the iframe as the root scroller in the main frame should now
@@ -573,14 +566,14 @@
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               container_scroller->LayerForScrolling());
     EXPECT_FALSE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
     EXPECT_TRUE(
         main_controller.IsViewportScrollCallback(container->GetApplyScroll()));
   }
 
-  // Unsetting the root scroller in the iframe should reset its effective
-  // root scroller to the iframe's documentElement and thus the iframe's
-  // documentElement becomes the global root scroller.
+  // Unsetting the root scroller in the iframe should reset its effective root
+  // scroller to the iframe's document node and thus it becomes the global root
+  // scroller.
   {
     SetAndSelectRootScroller(*iframe->contentDocument(), nullptr);
     EXPECT_EQ(main_controller.RootScrollerLayer(), iframe->contentDocument()
@@ -590,23 +583,23 @@
     EXPECT_FALSE(
         main_controller.IsViewportScrollCallback(container->GetApplyScroll()));
     EXPECT_FALSE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        iframe->contentDocument()->documentElement()->GetApplyScroll()));
+        iframe->contentDocument()->GetApplyScroll()));
   }
 
   // Finally, unsetting the main frame's root scroller should reset it to the
-  // documentElement and corresponding layer.
+  // document node and corresponding layer.
   {
     SetAndSelectRootScroller(*MainFrame()->GetDocument(), nullptr);
     EXPECT_EQ(main_controller.RootScrollerLayer(),
               MainFrameView()->LayoutViewport()->LayerForScrolling());
     EXPECT_TRUE(main_controller.IsViewportScrollCallback(
-        MainFrame()->GetDocument()->documentElement()->GetApplyScroll()));
+        MainFrame()->GetDocument()->GetApplyScroll()));
     EXPECT_FALSE(
         main_controller.IsViewportScrollCallback(container->GetApplyScroll()));
     EXPECT_FALSE(main_controller.IsViewportScrollCallback(
-        iframe->contentDocument()->documentElement()->GetApplyScroll()));
+        iframe->contentDocument()->GetApplyScroll()));
   }
 }
 
@@ -883,8 +876,7 @@
   const TopDocumentRootScrollerController& global_controller =
       MainFrame()->GetDocument()->GetPage()->GlobalRootScrollerController();
 
-  ASSERT_EQ(MainFrame()->GetDocument()->documentElement(),
-            global_controller.GlobalRootScroller());
+  ASSERT_EQ(MainFrame()->GetDocument(), global_controller.GlobalRootScroller());
 
   MainFrameView()->UpdateAllLifecyclePhases();
   GraphicsLayer* scroll_layer = global_controller.RootScrollerLayer();
@@ -900,8 +892,7 @@
   non_main_local_root->GetFrameView()->UpdateAllLifecyclePhases();
   helper_.LocalMainFrame()->GetFrameView()->UpdateAllLifecyclePhases();
 
-  EXPECT_EQ(MainFrame()->GetDocument()->documentElement(),
-            global_controller.GlobalRootScroller());
+  EXPECT_EQ(MainFrame()->GetDocument(), global_controller.GlobalRootScroller());
   EXPECT_EQ(global_controller.RootScrollerLayer(), scroll_layer);
   EXPECT_EQ(global_controller.RootContainerLayer(), container_layer);
 }
@@ -947,8 +938,7 @@
   const TopDocumentRootScrollerController& global_controller =
       MainFrame()->GetDocument()->GetPage()->GlobalRootScrollerController();
 
-  EXPECT_EQ(MainFrame()->GetDocument()->documentElement(),
-            global_controller.GlobalRootScroller());
+  EXPECT_EQ(MainFrame()->GetDocument(), global_controller.GlobalRootScroller());
   EXPECT_EQ(MainFrameView()->LayoutViewport()->LayerForScrolling(),
             global_controller.RootScrollerLayer());
 }
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
index 322ce06d..655e9b3 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.cc
@@ -21,48 +21,14 @@
 
 namespace root_scroller_util {
 
-ScrollableArea* ScrollableAreaForRootScroller(const Node* node) {
-  if (!node)
-    return nullptr;
-
-  if (node->IsDocumentNode() || node == node->GetDocument().documentElement()) {
-    if (!node->GetDocument().View())
-      return nullptr;
-
-    // For a FrameView, we use the layoutViewport rather than the
-    // getScrollableArea() since that could be the RootFrameViewport. The
-    // rootScroller's ScrollableArea will be swapped in as the layout viewport
-    // in RootFrameViewport so we need to ensure we get the layout viewport.
-    return node->GetDocument().View()->LayoutViewport();
-  }
-
-  DCHECK(node->IsElementNode());
-  const Element* element = ToElement(node);
-
-  if (!element->GetLayoutObject() || !element->GetLayoutObject()->IsBox())
-    return nullptr;
-
-  return ToLayoutBoxModelObject(element->GetLayoutObject())
-      ->GetScrollableArea();
-}
-
 PaintLayer* PaintLayerForRootScroller(const Node* node) {
   if (!node)
     return nullptr;
 
-  if (node->IsDocumentNode() || node == node->GetDocument().documentElement()) {
-    if (!node->GetDocument().GetLayoutView())
-      return nullptr;
-
-    return node->GetDocument().GetLayoutView()->Layer();
-  }
-
-  DCHECK(node->IsElementNode());
-  const Element* element = ToElement(node);
-  if (!element->GetLayoutObject() || !element->GetLayoutObject()->IsBox())
+  if (!node->GetLayoutObject() || !node->GetLayoutObject()->IsBox())
     return nullptr;
 
-  LayoutBox* box = ToLayoutBox(element->GetLayoutObject());
+  LayoutBox* box = ToLayoutBox(node->GetLayoutObject());
   return box->Layer();
 }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
index 152aba0d..241f0d8 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_util.h
@@ -10,15 +10,9 @@
 class LayoutBox;
 class Node;
 class PaintLayer;
-class ScrollableArea;
 
 namespace root_scroller_util {
 
-// Returns the ScrollableArea that's associated with the root scroller Node.
-// For the <html> element and document Node this will be the FrameView or root
-// PaintLayerScrollableArea.
-ScrollableArea* ScrollableAreaForRootScroller(const Node*);
-
 // Returns the PaintLayer that'll be used as the root scrolling layer. For the
 // <html> element and document Node, this returns the LayoutView's PaintLayer
 // rather than <html>'s since scrolling is handled by LayoutView.
diff --git a/third_party/blink/renderer/core/page/scrolling/scroll_state.cc b/third_party/blink/renderer/core/page/scrolling/scroll_state.cc
index 74469c6..9ed86e3 100644
--- a/third_party/blink/renderer/core/page/scrolling/scroll_state.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scroll_state.cc
@@ -16,9 +16,6 @@
 Node* NodeForId(DOMNodeId node_id) {
   Node* node = DOMNodeIds::NodeForId(node_id);
   DCHECK(node);
-  if (!node)
-    return nullptr;
-  DCHECK(node->IsElementNode());
   return node;
 }
 }  // namespace
diff --git a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
index b410a42..689128538 100644
--- a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.cc
@@ -24,6 +24,18 @@
 
 namespace blink {
 
+namespace {
+
+ScrollableArea* GetScrollableArea(Node* node) {
+  if (!node || !node->GetLayoutObject() ||
+      !node->GetLayoutObject()->IsBoxModelObject())
+    return nullptr;
+
+  return ToLayoutBoxModelObject(node->GetLayoutObject())->GetScrollableArea();
+}
+
+}  // namespace
+
 // static
 TopDocumentRootScrollerController* TopDocumentRootScrollerController::Create(
     Page& page) {
@@ -40,25 +52,34 @@
 }
 
 void TopDocumentRootScrollerController::DidChangeRootScroller() {
-  RecomputeGlobalRootScroller();
+  Node* target = FindGlobalRootScroller();
+  UpdateGlobalRootScroller(target);
 }
 
 void TopDocumentRootScrollerController::DidResizeViewport() {
-  if (!GlobalRootScroller())
+  if (!GlobalRootScroller() || !GlobalRootScroller()->GetDocument().IsActive())
     return;
 
+  if (!GlobalRootScroller()->GetLayoutObject())
+    return;
+
+  DCHECK(GlobalRootScroller()->GetLayoutObject()->IsBoxModelObject());
+
+  LayoutBoxModelObject* layout_object =
+      ToLayoutBoxModelObject(GlobalRootScroller()->GetLayoutObject());
+
   // Top controls can resize the viewport without invalidating compositing or
   // paint so we need to do that manually here.
-  DCHECK(GlobalRootScroller()->IsElementNode());
-  ToElement(GlobalRootScroller())->SetNeedsCompositingUpdate();
+  if (layout_object->HasLayer()) {
+    layout_object->Layer()->SetNeedsCompositingInputsUpdate();
+    layout_object->Layer()->UpdateSelfPaintingLayer();
+  }
 
-  if (GlobalRootScroller()->GetLayoutObject())
-    GlobalRootScroller()->GetLayoutObject()->SetNeedsPaintPropertyUpdate();
+  layout_object->SetNeedsPaintPropertyUpdate();
 }
 
 ScrollableArea* TopDocumentRootScrollerController::RootScrollerArea() const {
-  return root_scroller_util::ScrollableAreaForRootScroller(
-      GlobalRootScroller());
+  return GetScrollableArea(GlobalRootScroller());
 }
 
 IntSize TopDocumentRootScrollerController::RootScrollerVisibleArea() const {
@@ -86,9 +107,6 @@
   Node* root_scroller =
       &TopDocument()->GetRootScrollerController().EffectiveRootScroller();
 
-  if (root_scroller->IsDocumentNode())
-    return TopDocument()->documentElement();
-
   while (root_scroller && root_scroller->IsFrameOwnerElement()) {
     HTMLFrameOwnerElement* frame_owner = ToHTMLFrameOwnerElement(root_scroller);
     DCHECK(frame_owner);
@@ -99,9 +117,6 @@
 
     root_scroller =
         &iframe_document->GetRootScrollerController().EffectiveRootScroller();
-
-    if (root_scroller->IsDocumentNode())
-      return iframe_document->documentElement();
   }
 
   return root_scroller;
@@ -111,8 +126,7 @@
   if (!node || !node->GetDocument().IsActive())
     return;
 
-  ScrollableArea* area =
-      root_scroller_util::ScrollableAreaForRootScroller(node);
+  ScrollableArea* area = GetScrollableArea(node);
 
   if (!area || !area->Layer())
     return;
@@ -129,16 +143,15 @@
   }
 }
 
-void TopDocumentRootScrollerController::RecomputeGlobalRootScroller() {
+void TopDocumentRootScrollerController::UpdateGlobalRootScroller(
+    Node* new_global_root_scroller) {
   if (!viewport_apply_scroll_)
     return;
 
-  Node* target = FindGlobalRootScroller();
-  if (target == global_root_scroller_)
+  if (new_global_root_scroller == global_root_scroller_)
     return;
 
-  ScrollableArea* target_scroller =
-      root_scroller_util::ScrollableAreaForRootScroller(target);
+  ScrollableArea* target_scroller = GetScrollableArea(new_global_root_scroller);
 
   if (!target_scroller)
     return;
@@ -149,24 +162,23 @@
   // Use disable-native-scroll since the ViewportScrollCallback needs to
   // apply scroll actions both before (BrowserControls) and after (overscroll)
   // scrolling the element so it will apply scroll to the element itself.
-  target->SetApplyScroll(viewport_apply_scroll_);
+  new_global_root_scroller->SetApplyScroll(viewport_apply_scroll_);
 
   Node* old_root_scroller = global_root_scroller_;
 
-  global_root_scroller_ = target;
+  global_root_scroller_ = new_global_root_scroller;
 
   // Ideally, scroll customization would pass the current element to scroll to
   // the apply scroll callback but this doesn't happen today so we set it
   // through a back door here. This is also needed by the
-  // ViewportScrollCallback to swap the target into the layout viewport
-  // in RootFrameViewport.
+  // ViewportScrollCallback to swap the new global root scroller into the
+  // layout viewport in RootFrameViewport.
   viewport_apply_scroll_->SetScroller(target_scroller);
 
   SetNeedsCompositingUpdateOnAncestors(old_root_scroller);
-  SetNeedsCompositingUpdateOnAncestors(target);
+  SetNeedsCompositingUpdateOnAncestors(new_global_root_scroller);
 
-  if (ScrollableArea* area = root_scroller_util::ScrollableAreaForRootScroller(
-          old_root_scroller)) {
+  if (ScrollableArea* area = GetScrollableArea(old_root_scroller)) {
     if (old_root_scroller->GetDocument().IsActive())
       area->DidChangeGlobalRootScroller();
   }
@@ -216,13 +228,14 @@
 }
 
 void TopDocumentRootScrollerController::InitializeViewportScrollCallback(
-    RootFrameViewport& root_frame_viewport) {
+    RootFrameViewport& root_frame_viewport,
+    Document& main_document) {
   DCHECK(page_);
   viewport_apply_scroll_ = ViewportScrollCallback::Create(
       &page_->GetBrowserControls(), &page_->GetOverscrollController(),
       root_frame_viewport);
 
-  RecomputeGlobalRootScroller();
+  UpdateGlobalRootScroller(&main_document);
 }
 
 bool TopDocumentRootScrollerController::IsViewportScrollCallback(
@@ -234,8 +247,7 @@
 }
 
 GraphicsLayer* TopDocumentRootScrollerController::RootScrollerLayer() const {
-  ScrollableArea* area =
-      root_scroller_util::ScrollableAreaForRootScroller(global_root_scroller_);
+  ScrollableArea* area = GetScrollableArea(global_root_scroller_);
 
   if (!area)
     return nullptr;
@@ -250,9 +262,7 @@
 }
 
 GraphicsLayer* TopDocumentRootScrollerController::RootContainerLayer() const {
-  ScrollableArea* area =
-      root_scroller_util::ScrollableAreaForRootScroller(global_root_scroller_);
-
+  ScrollableArea* area = GetScrollableArea(global_root_scroller_);
   return area ? area->LayerForContainer() : nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h
index 2891a6dd..9059095 100644
--- a/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h
+++ b/third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h
@@ -46,7 +46,7 @@
   // This method needs to be called to create a ViewportScrollCallback that
   // will be used to apply viewport scrolling actions like browser controls
   // movement and overscroll glow.
-  void InitializeViewportScrollCallback(RootFrameViewport&);
+  void InitializeViewportScrollCallback(RootFrameViewport&, Document&);
 
   // Returns true if the given ScrollStateCallback is the
   // ViewportScrollCallback managed by this class.
@@ -63,8 +63,8 @@
 
   PaintLayer* RootScrollerPaintLayer() const;
 
-  // Returns the Node that's the global root scroller.  See README.md for
-  // the difference between this and the root scroller types in
+  // Returns the Node that's the global root scroller.  See README.md for the
+  // difference between this and the root scroller types in
   // RootScrollerController.
   Node* GlobalRootScroller() const;
 
@@ -92,10 +92,9 @@
   // to find its effective root scroller.
   Node* FindGlobalRootScroller();
 
-  // Should be called to ensure the correct element is currently set as the
-  // global root scroller and that all appropriate state changes are made if
-  // it changes.
-  void RecomputeGlobalRootScroller();
+  // Should be called to set a new node as the global root scroller and that
+  // all appropriate state changes are made if it changes.
+  void UpdateGlobalRootScroller(Node* new_global_root_scroller);
 
   Document* TopDocument() const;
 
diff --git a/third_party/blink/renderer/core/paint/box_painter.cc b/third_party/blink/renderer/core/paint/box_painter.cc
index db0717c..dd2839ff 100644
--- a/third_party/blink/renderer/core/paint/box_painter.cc
+++ b/third_party/blink/renderer/core/paint/box_painter.cc
@@ -254,12 +254,6 @@
                                    const LayoutPoint& paint_offset,
                                    const LayoutRect& paint_rect,
                                    const DisplayItemClient& background_client) {
-  // TODO(sunxd): ReplacedPainter only record hit test data for svg root which
-  // skips clip. We should move the conditions and ReplacedPainter's
-  // RecordHitTestData here.
-  if (layout_box_.IsLayoutReplaced())
-    return;
-
   // Hit test display items are only needed for compositing. This flag is used
   // for for printing and drag images which do not need hit testing.
   if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers)
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
index fcdd5ab5..78f2846 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector.cc
@@ -269,7 +269,6 @@
       }
       std::unique_ptr<ImageRecord> record = std::make_unique<ImageRecord>();
       record->node_id = node_id;
-      record->frame_index = frame_index_;
       record->first_size = rect_size;
       record->first_paint_index = ++first_paint_index_max_;
       record->image_url =
@@ -289,6 +288,7 @@
   if (id_record_map_.Contains(node_id) &&
       IsJustLoaded(cachedImg, *id_record_map_.at(node_id))) {
     records_pending_timing_.push(node_id);
+    id_record_map_.at(node_id)->frame_index = frame_index_;
     id_record_map_.at(node_id)->loaded = true;
   }
 }
diff --git a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
index cf63a0d..3f11b3c 100644
--- a/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/image_paint_timing_detector_test.cc
@@ -205,31 +205,27 @@
   EXPECT_EQ(LargestPaintStoredResult(), base::TimeTicks());
 }
 
-TEST_F(ImagePaintTimingDetectorTest,
-       LargestImagePaint_OneSwapPromiseForOneFrame) {
+// This test dipicts a situation when a smaller image has loaded, but a larger
+// image is loading. When we call analyze, the result will be empty because
+// we don't know when the largest image will finish loading. We wait until
+// next analysis to make the judgement again.
+// This bahavior is the same with Last Image Paint as well.
+TEST_F(ImagePaintTimingDetectorTest, DiscardAnalysisWhenLargestIsLoading) {
   SetBodyInnerHTML(R"HTML(
     <div id="parent">
-      <img id="1"></img>
-      <img id="2"></img>
+      <img height="5" width="5" id="1"></img>
+      <img height="9" width="9" id="2"></img>
     </div>
   )HTML");
   SetImageAndPaint("1", 5, 5);
   GetFrameView().UpdateAllLifecyclePhases();
+  ImageRecord* record;
+  InvokeCallback();
+  record = FindLargestPaintCandidate();
+  EXPECT_FALSE(record);
 
   SetImageAndPaint("2", 9, 9);
   GetFrameView().UpdateAllLifecyclePhases();
-
-  InvokeCallback();
-  ImageRecord* record;
-  record = FindLargestPaintCandidate();
-  EXPECT_TRUE(record);
-#if defined(OS_MACOSX)
-  EXPECT_EQ(record->first_size, 90);
-#else
-  EXPECT_EQ(record->first_size, 81);
-#endif
-  EXPECT_TRUE(record->first_paint_time_after_loaded.is_null());
-
   InvokeCallback();
   record = FindLargestPaintCandidate();
   EXPECT_TRUE(record);
@@ -241,6 +237,34 @@
   EXPECT_FALSE(record->first_paint_time_after_loaded.is_null());
 }
 
+// This is to prove that a swap time is assigned only to nodes of the frame who
+// register the swap time. In other words, swap time A should match frame A;
+// swap time B should match frame B.
+TEST_F(ImagePaintTimingDetectorTest, MatchSwapTimeToNodesOfDifferentFrames) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="parent">
+      <img height="5" width="5" id="smaller"></img>
+      <img height="9" width="9" id="larger"></img>
+    </div>
+  )HTML");
+
+  SetImageAndPaint("larger", 9, 9);
+  GetFrameView().UpdateAllLifecyclePhases();
+  SetImageAndPaint("smaller", 5, 5);
+  GetFrameView().UpdateAllLifecyclePhases();
+  InvokeCallback();
+  // record1 is the larger.
+  ImageRecord* record1 = FindLargestPaintCandidate();
+  const base::TimeTicks record1Time = record1->first_paint_time_after_loaded;
+  GetDocument().getElementById("parent")->RemoveChild(
+      GetDocument().getElementById("larger"));
+  GetFrameView().UpdateAllLifecyclePhases();
+  InvokeCallback();
+  // record2 is the smaller.
+  ImageRecord* record2 = FindLargestPaintCandidate();
+  EXPECT_NE(record1Time, record2->first_paint_time_after_loaded);
+}
+
 TEST_F(ImagePaintTimingDetectorTest,
        LargestImagePaint_UpdateResultWhenLargestChanged) {
   TimeTicks time1 = CurrentTimeTicks();
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index 931dcb1..0f3dd94 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2325,9 +2325,7 @@
 
   const TopDocumentRootScrollerController& controller =
       GetLayoutBox()->GetDocument().GetPage()->GlobalRootScrollerController();
-
-  return root_scroller_util::ScrollableAreaForRootScroller(
-             controller.GlobalRootScroller()) == this;
+  return controller.RootScrollerArea() == this;
 }
 
 bool PaintLayerScrollableArea::ScheduleAnimation() {
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.cc b/third_party/blink/renderer/core/paint/replaced_painter.cc
index 19da39d4..7abfe68 100644
--- a/third_party/blink/renderer/core/paint/replaced_painter.cc
+++ b/third_party/blink/renderer/core/paint/replaced_painter.cc
@@ -138,10 +138,6 @@
   if (skip_clip || !layout_replaced_.PhysicalContentBoxRect().IsEmpty()) {
     ScopedReplacedContentPaintState content_paint_state(paint_state,
                                                         layout_replaced_);
-    if (RuntimeEnabledFeatures::PaintTouchActionRectsEnabled()) {
-      RecordHitTestData(content_paint_state.GetPaintInfo(),
-                        content_paint_state.PaintOffset());
-    }
     layout_replaced_.PaintReplaced(content_paint_state.GetPaintInfo(),
                                    content_paint_state.PaintOffset());
   }
@@ -176,30 +172,6 @@
   }
 }
 
-void ReplacedPainter::RecordHitTestData(const PaintInfo& paint_info,
-                                        const LayoutPoint& paint_offset) {
-  // Hit test display items are only needed for compositing. This flag is used
-  // for for printing and drag images which do not need hit testing.
-  if (paint_info.GetGlobalPaintFlags() & kGlobalPaintFlattenCompositingLayers)
-    return;
-
-  if (paint_info.phase != PaintPhase::kForeground)
-    return;
-
-  // If an object is not visible, it does not participate in hit testing.
-  if (layout_replaced_.StyleRef().Visibility() != EVisibility::kVisible)
-    return;
-
-  auto touch_action = layout_replaced_.EffectiveWhitelistedTouchAction();
-  if (touch_action == TouchAction::kTouchActionAuto)
-    return;
-
-  auto rect = layout_replaced_.VisualOverflowRect();
-  rect.MoveBy(paint_offset);
-  HitTestData::RecordHitTestRect(paint_info.context, layout_replaced_,
-                                 HitTestRect(rect, touch_action));
-}
-
 bool ReplacedPainter::ShouldPaint(const ScopedPaintState& paint_state) const {
   const auto& paint_info = paint_state.GetPaintInfo();
   if (paint_info.phase != PaintPhase::kForeground &&
diff --git a/third_party/blink/renderer/core/paint/replaced_painter.h b/third_party/blink/renderer/core/paint/replaced_painter.h
index 6da46de..3b14fd5 100644
--- a/third_party/blink/renderer/core/paint/replaced_painter.h
+++ b/third_party/blink/renderer/core/paint/replaced_painter.h
@@ -26,10 +26,6 @@
   bool ShouldPaint(const ScopedPaintState&) const;
 
  private:
-  // Paint a hit test display item and record hit test data. This should be
-  // called in the background paint phase even if there is no other painted
-  // content.
-  void RecordHitTestData(const PaintInfo&, const LayoutPoint& paint_offset);
   const LayoutReplaced& layout_replaced_;
 };
 
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.cc b/third_party/blink/renderer/core/svg/svg_animation_element.cc
index c3e36ccb..6c4c0b3 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.cc
@@ -40,7 +40,6 @@
       animation_valid_(false),
       calc_mode_(kCalcModeLinear),
       animation_mode_(kNoAnimation) {
-  DCHECK(RuntimeEnabledFeatures::SMILEnabled());
   UseCounter::Count(document, WebFeature::kSVGAnimationElement);
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.idl b/third_party/blink/renderer/core/svg/svg_animation_element.idl
index 0eaf56f..a56ef3d 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.idl
@@ -26,7 +26,6 @@
 
 // https://svgwg.org/specs/animations/#InterfaceSVGAnimationElement
 
-[RuntimeEnabled=SMIL]
 interface SVGAnimationElement : SVGElement {
     readonly attribute SVGElement targetElement;
 
diff --git a/third_party/blink/renderer/core/svg/svg_discard_element.cc b/third_party/blink/renderer/core/svg/svg_discard_element.cc
index ad99768e..2545693 100644
--- a/third_party/blink/renderer/core/svg/svg_discard_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_discard_element.cc
@@ -36,7 +36,6 @@
 
 inline SVGDiscardElement::SVGDiscardElement(Document& document)
     : SVGSMILElement(svg_names::kDiscardTag, document) {
-  DCHECK(RuntimeEnabledFeatures::SMILEnabled());
 }
 
 DEFINE_NODE_FACTORY(SVGDiscardElement)
diff --git a/third_party/blink/renderer/core/svg/svg_discard_element.idl b/third_party/blink/renderer/core/svg/svg_discard_element.idl
index a6db8fa..be85d164 100644
--- a/third_party/blink/renderer/core/svg/svg_discard_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_discard_element.idl
@@ -30,6 +30,5 @@
 
 // https://svgwg.org/specs/animations/#InterfaceSVGDiscardElement
 
-[RuntimeEnabled=SMIL]
 interface SVGDiscardElement : SVGElement {
 };
diff --git a/third_party/blink/renderer/core/svg/svg_document_extensions.cc b/third_party/blink/renderer/core/svg/svg_document_extensions.cc
index 3283831..4d1b965d 100644
--- a/third_party/blink/renderer/core/svg/svg_document_extensions.cc
+++ b/third_party/blink/renderer/core/svg/svg_document_extensions.cc
@@ -43,7 +43,6 @@
 
 void SVGDocumentExtensions::AddWebAnimationsPendingSVGElement(
     SVGElement& element) {
-  DCHECK(RuntimeEnabledFeatures::WebAnimationsSVGEnabled());
   web_animations_pending_svg_elements_.insert(&element);
 }
 
@@ -54,12 +53,10 @@
 }
 
 void SVGDocumentExtensions::ServiceAnimations() {
-  if (RuntimeEnabledFeatures::SMILEnabled()) {
-    HeapVector<Member<SVGSVGElement>> time_containers;
-    CopyToVector(time_containers_, time_containers);
-    for (const auto& container : time_containers)
-      container->TimeContainer()->ServiceAnimations();
-  }
+  HeapVector<Member<SVGSVGElement>> time_containers;
+  CopyToVector(time_containers_, time_containers);
+  for (const auto& container : time_containers)
+    container->TimeContainer()->ServiceAnimations();
 
   SVGElementSet web_animations_pending_svg_elements;
   web_animations_pending_svg_elements.swap(
diff --git a/third_party/blink/renderer/core/svg/svg_mpath_element.cc b/third_party/blink/renderer/core/svg/svg_mpath_element.cc
index 8b7f73e5..b49d09d 100644
--- a/third_party/blink/renderer/core/svg/svg_mpath_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_mpath_element.cc
@@ -29,7 +29,6 @@
 
 inline SVGMPathElement::SVGMPathElement(Document& document)
     : SVGElement(svg_names::kMPathTag, document), SVGURIReference(this) {
-  DCHECK(RuntimeEnabledFeatures::SMILEnabled());
 }
 
 void SVGMPathElement::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/svg/svg_mpath_element.idl b/third_party/blink/renderer/core/svg/svg_mpath_element.idl
index c3933b2b..7889bbb 100644
--- a/third_party/blink/renderer/core/svg/svg_mpath_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_mpath_element.idl
@@ -25,7 +25,6 @@
 
 // https://svgwg.org/specs/animations/#InterfaceSVGMPathElement
 
-[RuntimeEnabled=SMIL]
 interface SVGMPathElement : SVGElement {
 };
 
diff --git a/third_party/blink/renderer/core/svg/svg_svg_element.cc b/third_party/blink/renderer/core/svg/svg_svg_element.cc
index 521d776..5aef38d 100644
--- a/third_party/blink/renderer/core/svg/svg_svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_svg_element.cc
@@ -514,17 +514,15 @@
     if (root_parent.GetDocument().IsXMLDocument())
       UseCounter::Count(GetDocument(), WebFeature::kSVGSVGElementInXMLDocument);
 
-    if (RuntimeEnabledFeatures::SMILEnabled()) {
-      GetDocument().AccessSVGExtensions().AddTimeContainer(this);
+    GetDocument().AccessSVGExtensions().AddTimeContainer(this);
 
-      // Animations are started at the end of document parsing and after firing
-      // the load event, but if we miss that train (deferred programmatic
-      // element insertion for example) we need to initialize the time container
-      // here.
-      if (!GetDocument().Parsing() && GetDocument().LoadEventFinished() &&
-          !TimeContainer()->IsStarted())
-        TimeContainer()->Start();
-    }
+    // Animations are started at the end of document parsing and after firing
+    // the load event, but if we miss that train (deferred programmatic
+    // element insertion for example) we need to initialize the time container
+    // here.
+    if (!GetDocument().Parsing() && GetDocument().LoadEventFinished() &&
+        !TimeContainer()->IsStarted())
+      TimeContainer()->Start();
   }
   return SVGGraphicsElement::InsertedInto(root_parent);
 }
diff --git a/third_party/blink/renderer/core/svg/svg_svg_element.idl b/third_party/blink/renderer/core/svg/svg_svg_element.idl
index 4cc98bb..0643aed 100644
--- a/third_party/blink/renderer/core/svg/svg_svg_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_svg_element.idl
@@ -59,11 +59,11 @@
 
     // SVG Animations
     // https://svgwg.org/specs/animations/#InterfaceSVGSVGElement
-    [MeasureAs=SVGSMILPausing, RuntimeEnabled=SMIL] void pauseAnimations();
-    [MeasureAs=SVGSMILPausing, RuntimeEnabled=SMIL] void unpauseAnimations();
-    [MeasureAs=SVGSMILPausing, RuntimeEnabled=SMIL] boolean animationsPaused();
-    [MeasureAs=SVGSMILCurrentTime, RuntimeEnabled=SMIL] float getCurrentTime();
-    [MeasureAs=SVGSMILCurrentTime, RuntimeEnabled=SMIL] void setCurrentTime(float seconds);
+    [MeasureAs=SVGSMILPausing] void pauseAnimations();
+    [MeasureAs=SVGSMILPausing] void unpauseAnimations();
+    [MeasureAs=SVGSMILPausing] boolean animationsPaused();
+    [MeasureAs=SVGSMILCurrentTime] float getCurrentTime();
+    [MeasureAs=SVGSMILCurrentTime] void setCurrentTime(float seconds);
 };
 
 SVGSVGElement implements SVGFitToViewBox;
diff --git a/third_party/blink/renderer/core/svg/svg_tag_names.json5 b/third_party/blink/renderer/core/svg/svg_tag_names.json5
index 2591fb1..53fc167c 100644
--- a/third_party/blink/renderer/core/svg/svg_tag_names.json5
+++ b/third_party/blink/renderer/core/svg/svg_tag_names.json5
@@ -12,35 +12,21 @@
     {
       name: "animate",
       noTypeHelpers: true,
-      runtimeEnabled: "SMIL",
     },
     {
       name: "animateColor",
       JSInterfaceName: "SVGElement",
       interfaceName: "SVGUnknownElement",
       noConstructor: true,
-      runtimeEnabled: "SMIL",
     },
-    {
-      name: "animateMotion",
-      runtimeEnabled: "SMIL",
-    },
-    {
-      name: "animateTransform",
-      runtimeEnabled: "SMIL",
-    },
-    {
-      name: "set",
-      runtimeEnabled: "SMIL",
-    },
+    "animateMotion",
+    "animateTransform",
+    "set",
     "circle",
     "clipPath",
     "defs",
     "desc",
-    {
-      name: "discard",
-      runtimeEnabled: "SMIL",
-    },
+    "discard",
     "ellipse",
     "feBlend",
     "feColorMatrix",
@@ -79,7 +65,6 @@
     {
       name: "mpath",
       interfaceName: "SVGMPathElement",
-      runtimeEnabled: "SMIL",
     },
     "path",
     "pattern",
diff --git a/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc b/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc
index b5cbf790..99cd441 100644
--- a/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/accessibility_object_model_test.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
+namespace test {
 
 namespace {
 
@@ -400,4 +401,5 @@
 
 }  // namespace
 
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 3074afb..6752719 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -3508,6 +3508,13 @@
   return common_ancestor;
 }
 
+String AXObject::ToString() const {
+  return AXObject::InternalRoleName(RoleValue())
+             .GetString()
+             .EncodeForDebugging() +
+         ": " + ComputedName().EncodeForDebugging();
+}
+
 VisiblePosition AXObject::VisiblePositionForIndex(int) const {
   return VisiblePosition();
 }
@@ -3563,8 +3570,7 @@
 }
 
 std::ostream& operator<<(std::ostream& stream, const AXObject& obj) {
-  return stream << AXObject::InternalRoleName(obj.RoleValue()) << ": "
-                << obj.ComputedName();
+  return stream << obj.ToString().Utf8().data();
 }
 
 void AXObject::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 5630e59..ab8424f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -49,6 +49,7 @@
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "ui/accessibility/ax_enums.mojom-blink.h"
 
@@ -1007,6 +1008,9 @@
                                               int* index_in_ancestor1,
                                               int* index_in_ancestor2);
 
+  // Returns a string representation of this object.
+  String ToString() const;
+
  protected:
   AXID id_;
   AXObjectVector children_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
index 1176333..669e007 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h"
 
 namespace blink {
+namespace test {
 
 // TODO(nektar): Break test up into multiple tests.
 TEST_F(AccessibilityTest, IsARIAWidget) {
@@ -55,4 +56,5 @@
       *root->getElementById("focusable-parent")));
 }
 
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
index ed63425..9f433bca 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_test.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h"
 
 namespace blink {
+namespace test {
 
 TEST_F(AccessibilityTest, IsDescendantOf) {
   SetBodyInnerHTML(R"HTML(<button id="button">button</button>)HTML");
@@ -164,4 +165,5 @@
   EXPECT_EQ(GetAXObjectCache().InOrderTraversalBegin(), iter);
 }
 
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.cc b/third_party/blink/renderer/modules/accessibility/ax_position.cc
index dd812d09..6473518 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.cc
@@ -17,6 +17,7 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_layout_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
 
@@ -675,6 +676,26 @@
   return PositionWithAffinity(range.EndPosition(), affinity_);
 }
 
+String AXPosition::ToString() const {
+  if (!IsValid())
+    return "Invalid AXPosition";
+
+  StringBuilder builder;
+  if (IsTextPosition()) {
+    builder.Append("AX text position in ");
+    builder.Append(container_object_->ToString());
+    builder.Append(", ");
+    builder.Append(String::Format("%d", TextOffset()));
+    return builder.ToString();
+  }
+
+  builder.Append("AX object anchored position in ");
+  builder.Append(container_object_->ToString());
+  builder.Append(", ");
+  builder.Append(String::Format("%d", ChildIndex()));
+  return builder.ToString();
+}
+
 // static
 const AXObject* AXPosition::FindNeighboringUnignoredObject(
     const Document& document,
@@ -805,15 +826,7 @@
 }
 
 std::ostream& operator<<(std::ostream& ostream, const AXPosition& position) {
-  if (!position.IsValid())
-    return ostream << "Invalid AXPosition";
-  if (position.IsTextPosition()) {
-    return ostream << "AX text position in " << *position.ContainerObject()
-                   << ", " << position.TextOffset();
-  }
-  return ostream << "AX object anchored position in "
-                 << *position.ContainerObject() << ", "
-                 << position.ChildIndex();
+  return ostream << position.ToString().Utf8().data();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position.h b/third_party/blink/renderer/modules/accessibility/ax_position.h
index a2447816..4cd52f8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_position.h
@@ -5,15 +5,17 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_POSITION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_POSITION_H_
 
-#include <base/logging.h>
 #include <stdint.h>
+
 #include <ostream>
 
+#include <base/logging.h>
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/core/editing/text_affinity.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
@@ -145,6 +147,9 @@
       const AXPositionAdjustmentBehavior =
           AXPositionAdjustmentBehavior::kMoveLeft) const;
 
+  // Returns a string representation of this object.
+  String ToString() const;
+
  private:
   // Only used by static Create... methods.
   explicit AXPosition(const AXObject& container);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_position_test.cc b/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
index 90d30a0..3d3a25c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_position_test.cc
@@ -15,6 +15,7 @@
 #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
+namespace test {
 
 namespace {
 
@@ -1393,4 +1394,5 @@
   EXPECT_EQ(ax_after, ax_position_after_from_dom.ChildAfterTreePosition());
 }
 
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_range.cc b/third_party/blink/renderer/modules/accessibility/ax_range.cc
index 2e71132..650e566a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_range.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_range.cc
@@ -70,6 +70,12 @@
                  AXPosition::CreateLastPositionInObject(container));
 }
 
+String AXRange::ToString() const {
+  if (!IsValid())
+    return "Invalid AXRange";
+  return "AXRange from " + Start().ToString() + " to " + End().ToString();
+}
+
 bool operator==(const AXRange& a, const AXRange& b) {
   DCHECK(a.IsValid() && b.IsValid());
   return a.Start() == b.Start() && a.End() == b.End();
@@ -80,9 +86,7 @@
 }
 
 std::ostream& operator<<(std::ostream& ostream, const AXRange& range) {
-  if (!range.IsValid())
-    return ostream << "Invalid AXRange";
-  return ostream << "AXRange from " << range.Start() << " to " << range.End();
+  return ostream << range.ToString().Utf8().data();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_range.h b/third_party/blink/renderer/modules/accessibility/ax_range.h
index a5cb182..b5cd3b7 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_range.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_range.h
@@ -5,13 +5,15 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_RANGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_RANGE_H_
 
-#include <base/logging.h>
 #include <stdint.h>
+
 #include <ostream>
 
+#include <base/logging.h>
 #include "third_party/blink/renderer/modules/accessibility/ax_position.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
@@ -44,6 +46,9 @@
   // Creates an |AXRange| encompassing the contents of the given |AXObject|.
   static AXRange RangeOfContents(const AXObject&);
 
+  // Returns a string representation of this object.
+  String ToString() const;
+
  private:
   AXPosition start_;
   AXPosition end_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_range_test.cc b/third_party/blink/renderer/modules/accessibility/ax_range_test.cc
index 25ade264..2fef56e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_range_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_range_test.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h"
 
 namespace blink {
+namespace test {
 
 TEST_F(AccessibilityTest, CommonAncestorContainerOfRange) {
   SetBodyInnerHTML(R"HTML(<input id='input' type='text' value='value'>"
@@ -79,4 +80,5 @@
             paragraph_range.End());
 }
 
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection.cc b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
index f0b8908..77b0a13 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection.cc
@@ -207,6 +207,12 @@
   frame_selection.SetSelection(selection, SetSelectionOptions());
 }
 
+String AXSelection::ToString() const {
+  if (!IsValid())
+    return "Invalid AXSelection";
+  return "AXSelection from " + Base().ToString() + " to " + Extent().ToString();
+}
+
 bool operator==(const AXSelection& a, const AXSelection& b) {
   DCHECK(a.IsValid() && b.IsValid());
   return a.Base() == b.Base() && a.Extent() == b.Extent();
@@ -217,10 +223,7 @@
 }
 
 std::ostream& operator<<(std::ostream& ostream, const AXSelection& selection) {
-  if (!selection.IsValid())
-    return ostream << "Invalid AXSelection";
-  return ostream << "AXSelection from " << selection.Base() << " to "
-                 << selection.Extent();
+  return ostream << selection.ToString().Utf8().data();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection.h b/third_party/blink/renderer/modules/accessibility/ax_selection.h
index fbcecc8..1383540 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection.h
@@ -5,14 +5,16 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_SELECTION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_SELECTION_H_
 
-#include <base/logging.h>
 #include <stdint.h>
+
 #include <ostream>
 
+#include <base/logging.h>
 #include "third_party/blink/renderer/core/editing/forward.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_position.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
@@ -56,6 +58,9 @@
   void Select(
       const AXSelectionBehavior = AXSelectionBehavior::kExtendToValidDOMRange);
 
+  // Returns a string representation of this object.
+  String ToString() const;
+
  private:
   AXSelection();
 
diff --git a/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc b/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
index bcabc2dc0..f919c1e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_selection_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_selection.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/editing/position.h"
@@ -15,6 +16,7 @@
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h"
 
 namespace blink {
+namespace test {
 
 //
 // Basic tests.
@@ -43,8 +45,10 @@
   EXPECT_EQ(3, dom_selection.Base().OffsetInContainerNode());
   EXPECT_EQ(text, dom_selection.Extent().AnchorNode());
   EXPECT_EQ(5, dom_selection.Extent().OffsetInContainerNode());
-  EXPECT_EQ("<Paragraph: ><StaticText: Hel^lo|>",
-            GetSelectionText(ax_selection));
+  EXPECT_EQ(
+      "++<Paragraph>\n"
+      "++++<StaticText: Hel^lo|>\n",
+      GetSelectionText(ax_selection));
 }
 
 TEST_F(AccessibilitySelectionTest, SetSelectionInTextWithWhiteSpace) {
@@ -70,8 +74,10 @@
   EXPECT_EQ(8, dom_selection.Base().OffsetInContainerNode());
   EXPECT_EQ(text, dom_selection.Extent().AnchorNode());
   EXPECT_EQ(10, dom_selection.Extent().OffsetInContainerNode());
-  EXPECT_EQ("<Paragraph: ><StaticText: Hel^lo|>",
-            GetSelectionText(ax_selection));
+  EXPECT_EQ(
+      "++<Paragraph>\n"
+      "++++<StaticText: Hel^lo|>\n",
+      GetSelectionText(ax_selection));
 }
 
 //
@@ -132,4 +138,17 @@
   EXPECT_EQ("", GetSelectionText(ax_selection_extend));
 }
 
+//
+// Declarative tests.
+//
+
+TEST_F(AccessibilitySelectionTest, List) {
+  RunSelectionTest("list");
+}
+
+TEST_F(AccessibilitySelectionTest, table) {
+  RunSelectionTest("table");
+}
+
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc b/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc
index 13950c5b..f7b8fc010 100644
--- a/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.cc
@@ -4,22 +4,39 @@
 
 #include "third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h"
 
+#include <algorithm>
+#include <iterator>
+#include <vector>
+
+#include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/dom/character_data.h"
 #include "third_party/blink/renderer/core/dom/container_node.h"
 #include "third_party/blink/renderer/core/dom/node.h"
+#include "third_party/blink/renderer/core/editing/frame_selection.h"
+#include "third_party/blink/renderer/core/editing/position.h"
+#include "third_party/blink/renderer/core/editing/selection_template.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_position.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_selection.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/shared_buffer.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
+namespace test {
 
 namespace {
 
+constexpr char kSelectionTestsRelativePath[] = "selection/";
+constexpr char kTestFileSuffix[] = ".html";
+constexpr char kAXTestExpectationSuffix[] = "-ax.txt";
+
 // Serialize accessibility subtree to selection text.
 // Adds a '^' at the selection anchor offset and a '|' at the focus offset.
 class AXSelectionSerializer final {
@@ -27,12 +44,14 @@
 
  public:
   explicit AXSelectionSerializer(const AXSelection& selection)
-      : selection_(selection) {}
+      : tree_level_(0), selection_(selection) {}
+  ~AXSelectionSerializer() = default;
 
   std::string Serialize(const AXObject& subtree) {
     if (!selection_.IsValid())
       return {};
     SerializeSubtree(subtree);
+    DCHECK_EQ(tree_level_, 0);
     return builder_.ToString().Utf8().data();
   }
 
@@ -41,7 +60,7 @@
     builder_.Append('<');
     builder_.Append(AXObject::InternalRoleName(text_object.RoleValue()));
     builder_.Append(": ");
-    const String name = text_object.ComputedName() + '>';
+    const String name = text_object.ComputedName() + ">\n";
     const AXObject& base_container = *selection_.Base().ContainerObject();
     const AXObject& extent_container = *selection_.Extent().ContainerObject();
 
@@ -103,9 +122,14 @@
   void HandleObject(const AXObject& object) {
     builder_.Append('<');
     builder_.Append(AXObject::InternalRoleName(object.RoleValue()));
-    builder_.Append(": ");
-    builder_.Append(object.ComputedName());
-    builder_.Append('>');
+
+    String name = object.ComputedName();
+    if (name.length()) {
+      builder_.Append(": ");
+      builder_.Append(name);
+    }
+
+    builder_.Append(">\n");
     SerializeSubtree(object);
   }
 
@@ -125,116 +149,163 @@
   }
 
   void SerializeSubtree(const AXObject& subtree) {
-    for (const Member<AXObject>& child : subtree.Children()) {
-      if (!child)
-        continue;
+    if (subtree.ChildCount() == 0)
+      return;
+    for (const AXObject* child : subtree.Children()) {
+      DCHECK(child);
       const auto position = AXPosition::CreatePositionBeforeObject(*child);
       HandleSelection(position);
+      ++tree_level_;
+      builder_.Append(
+          String::FromUTF8(std::string(tree_level_ * 2, '+').c_str()));
       if (position.IsTextPosition()) {
         HandleTextObject(*child);
       } else {
         HandleObject(*child);
       }
+      --tree_level_;
     }
     HandleSelection(AXPosition::CreateLastPositionInObject(subtree));
   }
 
   StringBuilder builder_;
+  int tree_level_;
   AXSelection selection_;
 };
 
 // Deserializes an HTML snippet with or without selection markers to an
-// accessibility tree. A '^' could be present at the selection anchor offset and
-// a '|' at the focus offset. If multiple markers are present, the deserializer
-// will DCHECK. If there are no markers, no selection will be made. We don't
-// allow '^' and '|' markers to appear in anything other than the contents of an
-// HTML node, e.g. they are not permitted in aria-labels.
+// |AXSelection| object. A '^' could be present at the selection anchor offset
+// and a '|' at the focus offset. If multiple markers are present, the
+// deserializer will return multiple |AXSelection| objects. If there are
+// multiple markers, the first '|' in DOM order will be matched with the first
+// '^' marker, the second '|' with the second '^', and so on. If there are more
+// '|'s than '^'s or vice versa, the deserializer will DCHECK. If there are no
+// markers, no |AXSelection| objects will be returned. We don't allow '^' and
+// '|' markers to appear in anything other than the contents of an HTML node,
+// e.g. they are not permitted in aria-labels.
 class AXSelectionDeserializer final {
   STACK_ALLOCATED();
 
  public:
-  AXSelectionDeserializer(AXObjectCacheImpl& cache)
-      : ax_object_cache_(&cache), base_(), extent_() {}
+  explicit AXSelectionDeserializer(AXObjectCacheImpl& cache)
+      : ax_object_cache_(&cache),
+        anchors_(MakeGarbageCollected<VectorOfPairs<Node, int>>()),
+        foci_(MakeGarbageCollected<VectorOfPairs<Node, int>>()) {}
   ~AXSelectionDeserializer() = default;
 
   // Creates an accessibility tree rooted at the given HTML element from the
-  // provided HTML snippet, selects the part of the tree indicated by the
-  // selection markers in the snippet if present, and returns the tree's root.
-  const AXSelection Deserialize(const std::string& html_snippet,
-                                HTMLElement& element) {
+  // provided HTML snippet and returns |AXSelection| objects that can select the
+  // parts of the tree indicated by the selection markers in the snippet.
+  const Vector<AXSelection> Deserialize(const std::string& html_snippet,
+                                        HTMLElement& element) {
     element.SetInnerHTMLFromString(String::FromUTF8(html_snippet.c_str()));
+    element.GetDocument().View()->UpdateAllLifecyclePhases();
     AXObject* root = ax_object_cache_->GetOrCreate(&element);
-    if (!root)
-      return AXSelection::Builder().Build();
+    if (!root || root->IsDetached())
+      return {};
 
     FindSelectionMarkers(*root);
-    if (base_ && extent_) {
+    DCHECK((foci_->size() == 1 && anchors_->size() == 0) ||
+           anchors_->size() == foci_->size())
+        << "There should be an equal number of '^'s and '|'s in the HTML that "
+           "is being deserialized, or if caret placement is required, only a "
+           "single '|'.";
+    if (foci_->IsEmpty())
+      return {};
+
+    Vector<AXSelection> ax_selections;
+    if (anchors_->IsEmpty()) {
+      // Handle the case when there is just a single '|' marker representing the
+      // position of the caret.
+      DCHECK(foci_->at(0).first);
+      const Position caret(foci_->at(0).first, foci_->at(0).second);
+      const auto ax_caret = AXPosition::FromPosition(caret);
       AXSelection::Builder builder;
-      AXSelection ax_selection =
-          builder.SetBase(base_).SetExtent(extent_).Build();
-      ax_selection.Select();
-      return ax_selection;
+      ax_selections.push_back(
+          builder.SetBase(ax_caret).SetExtent(ax_caret).Build());
+      return ax_selections;
     }
 
-    return AXSelection::Builder().Build();
+    for (size_t i = 0; i < foci_->size(); ++i) {
+      DCHECK(anchors_->at(i).first);
+      const Position base(*anchors_->at(i).first, anchors_->at(i).second);
+      const auto ax_base = AXPosition::FromPosition(base);
+
+      DCHECK(foci_->at(i).first);
+      const Position extent(*foci_->at(i).first, foci_->at(i).second);
+      const auto ax_extent = AXPosition::FromPosition(extent);
+      AXSelection::Builder builder;
+      ax_selections.push_back(
+          builder.SetBase(ax_base).SetExtent(ax_extent).Build());
+    }
+
+    return ax_selections;
   }
 
  private:
   void HandleCharacterData(const AXObject& text_object) {
     CharacterData* const node = ToCharacterData(text_object.GetNode());
-    int base_offset = -1;
-    int extent_offset = -1;
+    Vector<int> base_offsets;
+    Vector<int> extent_offsets;
+    unsigned number_of_markers = 0;
     StringBuilder builder;
     for (unsigned i = 0; i < node->length(); ++i) {
       const UChar character = node->data()[i];
       if (character == '^') {
-        DCHECK_EQ(base_offset, -1) << "Only one '^' is allowed " << text_object;
-        base_offset = static_cast<int>(i);
+        base_offsets.push_back(static_cast<int>(i - number_of_markers));
+        ++number_of_markers;
         continue;
       }
+
       if (character == '|') {
-        DCHECK_EQ(extent_offset, -1)
-            << "Only one '|' is allowed " << text_object;
-        extent_offset = static_cast<int>(i);
+        extent_offsets.push_back(static_cast<int>(i - number_of_markers));
+        ++number_of_markers;
         continue;
       }
+
       builder.Append(character);
     }
 
-    LOG(ERROR) << "Nektar\n" << base_offset << extent_offset;
-    if (base_offset == -1 && extent_offset == -1)
+    if (base_offsets.IsEmpty() && extent_offsets.IsEmpty())
       return;
 
-    // Remove the markers, otherwise they would be duplicated if the AX
-    // selection is re-serialized.
+    // Remove the markers, otherwise they would be duplicated if the AXSelection
+    // is re-serialized.
     node->setData(builder.ToString());
+    node->GetDocument().View()->UpdateAllLifecyclePhases();
 
-    if (node->length() == 0) {
+    //
+    // Non-text selection.
+    //
+
+    if (node->ContainsOnlyWhitespaceOrEmpty()) {
       // Since the text object contains only selection markers, this indicates
       // that this is a request for a non-text selection.
-      if (base_offset >= 0)
-        base_ = AXPosition::CreatePositionBeforeObject(text_object);
-      if (extent_offset >= 0)
-        extent_ = AXPosition::CreatePositionBeforeObject(text_object);
+      Node* const parent = node->ParentOrShadowHostNode();
+      int index_in_parent = static_cast<int>(node->NodeIndex());
 
-      ContainerNode* const parent_node = node->parentNode();
-      DCHECK(parent_node);
-      parent_node->removeChild(node);
+      for (size_t i = 0; i < base_offsets.size(); ++i)
+        anchors_->emplace_back(parent, index_in_parent);
+
+      for (size_t i = 0; i < extent_offsets.size(); ++i)
+        foci_->emplace_back(parent, index_in_parent);
+
       return;
     }
 
-    if (base_offset >= 0)
-      base_ = AXPosition::CreatePositionInTextObject(text_object, base_offset);
-    if (extent_offset >= 0) {
-      extent_ =
-          AXPosition::CreatePositionInTextObject(text_object, extent_offset);
-    }
+    //
+    // Text selection.
+    //
+
+    for (int base_offset : base_offsets)
+      anchors_->emplace_back(node, base_offset);
+    for (int extent_offset : extent_offsets)
+      foci_->emplace_back(node, extent_offset);
   }
 
   void HandleObject(const AXObject& object) {
-    for (const Member<AXObject>& child : object.Children()) {
-      if (!child)
-        continue;
+    for (const AXObject* child : object.Children()) {
+      DCHECK(child);
       FindSelectionMarkers(*child);
     }
   }
@@ -243,14 +314,19 @@
     const Node* node = root.GetNode();
     if (node && node->IsCharacterDataNode()) {
       HandleCharacterData(root);
+      // |root| will need to be detached and replaced with an updated AXObject.
       return;
     }
     HandleObject(root);
   }
 
-  Member<AXObjectCacheImpl> const ax_object_cache_;
-  AXPosition base_;
-  AXPosition extent_;
+  Persistent<AXObjectCacheImpl> const ax_object_cache_;
+
+  // Pairs of anchor nodes + anchor offsets.
+  Persistent<VectorOfPairs<Node, int>> anchors_;
+
+  // Pairs of focus nodes + focus offsets.
+  Persistent<VectorOfPairs<Node, int>> foci_;
 };
 
 }  // namespace
@@ -259,10 +335,17 @@
     LocalFrameClient* local_frame_client)
     : AccessibilityTest(local_frame_client) {}
 
+std::string AccessibilitySelectionTest::GetCurrentSelectionText() const {
+  const SelectionInDOMTree selection =
+      GetFrame().Selection().GetSelectionInDOMTree();
+  const auto ax_selection = AXSelection::FromSelection(selection);
+  return GetSelectionText(ax_selection);
+}
+
 std::string AccessibilitySelectionTest::GetSelectionText(
     const AXSelection& selection) const {
   const AXObject* root = GetAXRootObject();
-  if (!root)
+  if (!root || root->IsDetached())
     return {};
   return AXSelectionSerializer(selection).Serialize(*root);
 }
@@ -278,15 +361,63 @@
   HTMLElement* body = GetDocument().body();
   if (!body)
     return AXSelection::Builder().Build();
-  return AXSelectionDeserializer(GetAXObjectCache())
-      .Deserialize(selection_text, *body);
+  const Vector<AXSelection> ax_selections =
+      AXSelectionDeserializer(GetAXObjectCache())
+          .Deserialize(selection_text, *body);
+  if (ax_selections.IsEmpty())
+    return AXSelection::Builder().Build();
+  return ax_selections.front();
 }
 
 const AXSelection AccessibilitySelectionTest::SetSelectionText(
     const std::string& selection_text,
     HTMLElement& element) const {
-  return AXSelectionDeserializer(GetAXObjectCache())
-      .Deserialize(selection_text, element);
+  const Vector<AXSelection> ax_selections =
+      AXSelectionDeserializer(GetAXObjectCache())
+          .Deserialize(selection_text, element);
+  if (ax_selections.IsEmpty())
+    return AXSelection::Builder().Build();
+  return ax_selections.front();
 }
 
+void AccessibilitySelectionTest::RunSelectionTest(
+    const std::string& test_name) const {
+  static const std::string separator_line = '\n' + std::string(80, '=') + '\n';
+  const String relative_path = String::FromUTF8(kSelectionTestsRelativePath) +
+                               String::FromUTF8(test_name.c_str());
+  const String test_path = AccessibilityTestDataPath(relative_path);
+
+  const String test_file = test_path + String::FromUTF8(kTestFileSuffix);
+  scoped_refptr<SharedBuffer> test_file_buffer = ReadFromFile(test_file);
+  auto test_file_chars = test_file_buffer->CopyAs<std::vector<char>>();
+  std::string test_file_contents;
+  std::copy(test_file_chars.begin(), test_file_chars.end(),
+            std::back_inserter(test_file_contents));
+
+  const String ax_file = test_path + String::FromUTF8(kAXTestExpectationSuffix);
+  scoped_refptr<SharedBuffer> ax_file_buffer = ReadFromFile(ax_file);
+  auto ax_file_chars = ax_file_buffer->CopyAs<std::vector<char>>();
+  std::string ax_file_contents;
+  std::copy(ax_file_chars.begin(), ax_file_chars.end(),
+            std::back_inserter(ax_file_contents));
+
+  HTMLElement* body = GetDocument().body();
+  ASSERT_NE(nullptr, body);
+  Vector<AXSelection> ax_selections =
+      AXSelectionDeserializer(GetAXObjectCache())
+          .Deserialize(test_file_contents, *body);
+  std::string actual_ax_file_contents;
+
+  for (auto& ax_selection : ax_selections) {
+    ax_selection.Select();
+    actual_ax_file_contents += separator_line;
+    actual_ax_file_contents += ax_selection.ToString().Utf8().data();
+    actual_ax_file_contents += separator_line;
+    actual_ax_file_contents += GetCurrentSelectionText();
+  }
+
+  EXPECT_EQ(ax_file_contents, actual_ax_file_contents);
+}
+
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h b/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h
index 0ee6a45..8b936aa9 100644
--- a/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h
+++ b/third_party/blink/renderer/modules/accessibility/testing/accessibility_selection_test.h
@@ -16,6 +16,8 @@
 class AXSelection;
 class LocalFrameClient;
 
+namespace test {
+
 // Makes writing and debugging selection tests easier.
 class AccessibilitySelectionTest : public AccessibilityTest {
   USING_FAST_MALLOC(AccessibilitySelectionTest);
@@ -24,12 +26,19 @@
   AccessibilitySelectionTest(LocalFrameClient* local_frame_client = nullptr);
 
  protected:
-  // Gets the inner HTML of the accessibility tree and annotates it with markers
-  // indicating the anchor and focus of |selection|.
+  // Gets a text representation of the accessibility tree that is currently
+  // selected and annotates it with markers indicating the anchor and focus of
+  // |selection|.
+  std::string GetCurrentSelectionText() const;
+
+  // Gets a text representation of the accessibility tree encompassing
+  // |selection| and annotates it with markers indicating the anchor and focus
+  // of |selection|.
   std::string GetSelectionText(const AXSelection& selection) const;
 
-  // Gets the inner HTML of the accessibility subtree rooted at |subtree| and
-  // annotates it with markers indicating the anchor and focus of |selection|.
+  // Gets a text representation of the accessibility subtree rooted at |subtree|
+  // and encompassing |selection|, and annotates it with markers indicating the
+  // anchor and focus of |selection|.
   std::string GetSelectionText(const AXSelection& selection,
                                const AXObject& subtree) const;
 
@@ -41,8 +50,15 @@
   // the accessibility subtree at |element|.
   const AXSelection SetSelectionText(const std::string& selection_text,
                                      HTMLElement& element) const;
+
+  // Compares two HTML files containing a DOM selection and the equivalent
+  // accessibility selection.
+  void RunSelectionTest(const std::string& test_name) const;
+
+ private:
 };
 
+}  // namespace test
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_TESTING_ACCESSIBILITY_SELECTION_TEST_H_
diff --git a/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc b/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc
index 4cbf314..09fab64 100644
--- a/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc
+++ b/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 
 namespace blink {
+namespace test {
 
 AccessibilityTest::AccessibilityTest(LocalFrameClient* local_frame_client)
     : RenderingTest(local_frame_client) {}
@@ -56,11 +57,12 @@
 
   stream << std::string(level * 2, '+');
   stream << *root << std::endl;
-  for (const Member<AXObject> child : root->Children()) {
+  for (const AXObject* child : root->Children()) {
     DCHECK(child);
-    PrintAXTreeHelper(stream, child.Get(), level + 1);
+    PrintAXTreeHelper(stream, child, level + 1);
   }
   return stream;
 }
 
+}  // namespace test
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h b/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h
index 2b2a454..6e91867 100644
--- a/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h
+++ b/third_party/blink/renderer/modules/accessibility/testing/accessibility_test.h
@@ -19,6 +19,8 @@
 class AXObjectCacheImpl;
 class LocalFrameClient;
 
+namespace test {
+
 class AccessibilityTest : public RenderingTest {
   USING_FAST_MALLOC(AccessibilityTest);
 
@@ -47,6 +49,7 @@
   std::unique_ptr<AXContext> ax_context_;
 };
 
+}  // namespace test
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_TESTING_ACCESSIBILITY_TEST_H_
diff --git a/third_party/blink/renderer/modules/accessibility/testing/data/selection/list-ax.txt b/third_party/blink/renderer/modules/accessibility/testing/data/selection/list-ax.txt
new file mode 100644
index 0000000..cdb8cd6d
--- /dev/null
+++ b/third_party/blink/renderer/modules/accessibility/testing/data/selection/list-ax.txt
@@ -0,0 +1,47 @@
+
+================================================================================
+AXSelection from AX object anchored position in "List": "", 1 to AX object anchored position in "List": "", 3
+================================================================================
+++<List>
+++++<ListItem>
+++++++<ListMarker: 1. >
+++++++<StaticText: tic>
+^++++<ListItem>
+++++++<ListMarker: 2. >
+++++++<StaticText: tac>
+++++<ListItem>
+++++++<ListMarker: 3. >
+++++++<StaticText: toe>
+|++<List>
+++++<ListItem>
+++++++<StaticText: tic>
+++++<StaticText:  >
+++++<ListItem>
+++++++<StaticText: tac>
+++++<StaticText:  >
+++++<ListItem>
+++++++<StaticText: toe>
+
+================================================================================
+AXSelection from AX object anchored position in "WebArea": "", 1 to AX object anchored position in "WebArea": "", 2
+================================================================================
+++<List>
+++++<ListItem>
+++++++<ListMarker: 1. >
+++++++<StaticText: tic>
+++++<ListItem>
+++++++<ListMarker: 2. >
+++++++<StaticText: tac>
+++++<ListItem>
+++++++<ListMarker: 3. >
+++++++<StaticText: toe>
+^++<List>
+++++<ListItem>
+++++++<StaticText: tic>
+++++<StaticText:  >
+++++<ListItem>
+++++++<StaticText: tac>
+++++<StaticText:  >
+++++<ListItem>
+++++++<StaticText: toe>
+|
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/accessibility/testing/data/selection/table-ax.txt b/third_party/blink/renderer/modules/accessibility/testing/data/selection/table-ax.txt
new file mode 100644
index 0000000..4d1fd86
--- /dev/null
+++ b/third_party/blink/renderer/modules/accessibility/testing/data/selection/table-ax.txt
@@ -0,0 +1,100 @@
+
+================================================================================
+AXSelection from AX object anchored position in "WebArea": "", 0 to AX object anchored position in "WebArea": "", 0
+================================================================================
+|++<Table>
+++++<Row>
+++++++<ColumnHeader: Sum>
+++++++++<StaticText: Sum>
+++++++<ColumnHeader: Subtraction>
+++++++++<StaticText: Subtraction>
+++++<Row>
+++++++<Cell: 10>
+++++++++<StaticText: 10>
+++++++<Cell: 7>
+++++++++<StaticText: 7>
+++++<Row>
+++++++<Cell: 2>
+++++++++<StaticText: 2>
+++++++<Cell: 4>
+++++++++<StaticText: 4>
+++++<Row>
+++++++<Cell: 12>
+++++++++<StaticText: 12>
+++++++<Cell: 3>
+++++++++<StaticText: 3>
+
+================================================================================
+AXSelection from AX object anchored position in "WebArea": "", 0 to AX object anchored position in "WebArea": "", 0
+================================================================================
+|++<Table>
+++++<Row>
+++++++<ColumnHeader: Sum>
+++++++++<StaticText: Sum>
+++++++<ColumnHeader: Subtraction>
+++++++++<StaticText: Subtraction>
+++++<Row>
+++++++<Cell: 10>
+++++++++<StaticText: 10>
+++++++<Cell: 7>
+++++++++<StaticText: 7>
+++++<Row>
+++++++<Cell: 2>
+++++++++<StaticText: 2>
+++++++<Cell: 4>
+++++++++<StaticText: 4>
+++++<Row>
+++++++<Cell: 12>
+++++++++<StaticText: 12>
+++++++<Cell: 3>
+++++++++<StaticText: 3>
+
+================================================================================
+AXSelection from AX object anchored position in "WebArea": "", 0 to AX object anchored position in "WebArea": "", 0
+================================================================================
+|++<Table>
+++++<Row>
+++++++<ColumnHeader: Sum>
+++++++++<StaticText: Sum>
+++++++<ColumnHeader: Subtraction>
+++++++++<StaticText: Subtraction>
+++++<Row>
+++++++<Cell: 10>
+++++++++<StaticText: 10>
+++++++<Cell: 7>
+++++++++<StaticText: 7>
+++++<Row>
+++++++<Cell: 2>
+++++++++<StaticText: 2>
+++++++<Cell: 4>
+++++++++<StaticText: 4>
+++++<Row>
+++++++<Cell: 12>
+++++++++<StaticText: 12>
+++++++<Cell: 3>
+++++++++<StaticText: 3>
+
+================================================================================
+AXSelection from AX text position in "StaticText": "Sum", 0 to AX text position in "StaticText": "Subtraction", 11
+================================================================================
+++<Table>
+++++<Row>
+++++++<ColumnHeader: Sum>
+^++++++++<StaticText: ^Sum>
+++++++<ColumnHeader: Subtraction>
+++++++++<StaticText: Subtraction|>
+++++<Row>
+++++++<Cell: 10>
+++++++++<StaticText: 10>
+++++++<Cell: 7>
+++++++++<StaticText: 7>
+++++<Row>
+++++++<Cell: 2>
+++++++++<StaticText: 2>
+++++++<Cell: 4>
+++++++++<StaticText: 4>
+++++<Row>
+++++++<Cell: 12>
+++++++++<StaticText: 12>
+++++++<Cell: 3>
+++++++++<StaticText: 3>
diff --git a/third_party/blink/renderer/modules/accessibility/testing/data/selection/table.html b/third_party/blink/renderer/modules/accessibility/testing/data/selection/table.html
new file mode 100644
index 0000000..d9587cde
--- /dev/null
+++ b/third_party/blink/renderer/modules/accessibility/testing/data/selection/table.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <table border="1">
+      <thead>
+        <tr>
+          <th>^Sum</th>
+          <th>Subtraction|</th>
+        </tr>
+      </thead>
+      <tfoot>
+        ^<tr>
+          <td>12</td>
+          <td>3</td>
+        </tr>|
+      </tfoot>
+      <tbody>
+        <tr>
+          ^<td>10</td>
+          <td>7</td>|
+        </tr>
+        ^<tr>
+          <td>2</td>
+          <td>4</td>
+        </tr>|
+      </tbody>
+    </table>
+  </body>
+</html>
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string.cc b/third_party/blink/renderer/platform/bindings/parkable_string.cc
index a8f35c5..81a1d353 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
+#include "base/timer/elapsed_timer.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h"
@@ -30,6 +31,37 @@
   UMA_HISTOGRAM_ENUMERATION("Memory.MovableStringParkingAction", action);
 }
 
+void RecordStatistics(size_t size,
+                      base::TimeDelta duration,
+                      ParkableStringImpl::ParkingAction action) {
+  size_t throughput_mb_s =
+      static_cast<size_t>(size / duration.InSecondsF()) / 1000000;
+  size_t size_kb = size / 1000;
+  if (action == ParkableStringImpl::ParkingAction::kParkedInBackground) {
+    UMA_HISTOGRAM_COUNTS_10000("Memory.ParkableString.Compression.SizeKb",
+                               size_kb);
+    // Size is at least 10kB, and at most ~1MB, and compression throughput
+    // ranges from single-digit MB/s to ~40MB/s depending on the CPU, hence
+    // the range.
+    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+        "Memory.ParkableString.Compression.Latency", duration,
+        base::TimeDelta::FromMicroseconds(500), base::TimeDelta::FromSeconds(1),
+        100);
+    UMA_HISTOGRAM_COUNTS_1000(
+        "Memory.ParkableString.Compression.ThroughputMBps", throughput_mb_s);
+  } else {
+    UMA_HISTOGRAM_COUNTS_10000("Memory.ParkableString.Decompression.SizeKb",
+                               size_kb);
+    UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
+        "Memory.ParkableString.Decompression.Latency", duration,
+        base::TimeDelta::FromMicroseconds(500), base::TimeDelta::FromSeconds(1),
+        100);
+    // Decompression speed can go up to >500MB/s.
+    UMA_HISTOGRAM_COUNTS_1000(
+        "Memory.ParkableString.Decompression.ThroughputMBps", throughput_mb_s);
+  }
+}
+
 void AsanPoisonString(const String& string) {
 #if defined(ADDRESS_SANITIZER)
   if (string.IsNull())
@@ -207,6 +239,7 @@
   TRACE_EVENT1("blink", "ParkableStringImpl::Unpark", "size",
                CharactersSizeInBytes());
   DCHECK(compressed_);
+  base::ElapsedTimer timer;
 
   base::StringPiece compressed_string_piece(
       reinterpret_cast<const char*>(compressed_->data()),
@@ -238,12 +271,14 @@
   compressed_ = nullptr;
   string_ = uncompressed;
   state_ = State::kUnparked;
+  ParkableStringManager::Instance().OnUnparked(this, string_.Impl());
 
   bool backgrounded =
       ParkableStringManager::Instance().IsRendererBackgrounded();
-  RecordParkingAction(backgrounded ? ParkingAction::kUnparkedInBackground
-                                   : ParkingAction::kUnparkedInForeground);
-  ParkableStringManager::Instance().OnUnparked(this, string_.Impl());
+  auto action = backgrounded ? ParkingAction::kUnparkedInBackground
+                             : ParkingAction::kUnparkedInForeground;
+  RecordParkingAction(action);
+  RecordStatistics(CharactersSizeInBytes(), timer.Elapsed(), action);
 }
 
 void ParkableStringImpl::OnParkingCompleteOnMainThread(
@@ -282,6 +317,7 @@
   TRACE_EVENT1("blink", "ParkableStringImpl::CompressInBackground", "size",
                params->size);
 
+  base::ElapsedTimer timer;
   // Compression touches the string.
   AsanUnpoisonString(params->string->string_);
   base::StringPiece data(reinterpret_cast<const char*>(params->data),
@@ -299,6 +335,7 @@
   AsanPoisonString(params->string->string_);
 
   auto* task_runner = params->callback_task_runner.get();
+  size_t size = params->size;
   PostCrossThreadTask(
       *task_runner, FROM_HERE,
       CrossThreadBind(
@@ -309,6 +346,7 @@
                                                   std::move(compressed));
           },
           WTF::Passed(std::move(params)), WTF::Passed(std::move(compressed))));
+  RecordStatistics(size, timer.Elapsed(), ParkingAction::kParkedInBackground);
 }
 
 ParkableString::ParkableString(scoped_refptr<StringImpl>&& impl) {
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
index 754ebf6..38fc9ec 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
@@ -350,6 +350,8 @@
 #endif  // defined(ADDRESS_SANITIZER)
 
 TEST_F(ParkableStringTest, Compression) {
+  base::HistogramTester histogram_tester;
+
   ParkableString parkable(MakeLargeString().Impl());
   ParkableStringImpl* impl = parkable.Impl();
   EXPECT_TRUE(impl->Park());
@@ -362,6 +364,19 @@
   parkable.ToString();
   EXPECT_FALSE(impl->is_parked());
   EXPECT_EQ(nullptr, impl->compressed_);
+
+  histogram_tester.ExpectUniqueSample(
+      "Memory.ParkableString.Compression.SizeKb", kSizeKb, 1);
+  histogram_tester.ExpectTotalCount("Memory.ParkableString.Compression.Latency",
+                                    1);
+  histogram_tester.ExpectTotalCount(
+      "Memory.ParkableString.Compression.ThroughputMBps", 1);
+  histogram_tester.ExpectUniqueSample(
+      "Memory.ParkableString.Decompression.SizeKb", kSizeKb, 1);
+  histogram_tester.ExpectTotalCount(
+      "Memory.ParkableString.Decompression.Latency", 1);
+  histogram_tester.ExpectTotalCount(
+      "Memory.ParkableString.Decompression.ThroughputMBps", 1);
 }
 
 }  // namespace blink
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 e84072c..5194e02 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -580,4 +580,8 @@
   RuntimeEnabledFeatures::SetBackgroundFetchUploadsEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableMergeBlockingNonBlockingPools(bool enable) {
+  RuntimeEnabledFeatures::SetMergeBlockingNonBlockingPoolsEnabled(enable);
+}
+
 }  // namespace blink
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 99b0665..e8529448 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.cc
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.cc
@@ -20,6 +20,7 @@
 
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
 
+#include "SkFont.h"
 #include "SkTypeface.h"
 #include "build/build_config.h"
 #include "hb-ot.h"
@@ -281,13 +282,9 @@
 
 #if !defined(OS_MACOSX)
 bool FontPlatformData::FontContainsCharacter(UChar32 character) {
-  SkPaint paint;
-  SetupSkPaint(&paint);
-  paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
-
-  uint16_t glyph;
-  paint.textToGlyphs(&character, sizeof(character), &glyph);
-  return glyph;
+  SkFont font;
+  SetupSkFont(&font);
+  return font.unicharToGlyph(character);
 }
 #endif
 
@@ -327,6 +324,20 @@
 
   font->setEmbeddedBitmapText(!avoid_embedded_bitmaps_);
 }
+
+void FontPlatformData::SetupSkFont(SkFont* font,
+                                   float device_scale_factor,
+                                   const Font*) const {
+  style_.ApplyToSkFont(font, device_scale_factor);
+
+  const float ts = text_size_ >= 0 ? text_size_ : 12;
+  font->setSize(SkFloatToScalar(ts));
+  font->setTypeface(typeface_);
+  font->setEmbolden(synthetic_bold_);
+  font->setSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
+
+  font->setEmbeddedBitmaps(!avoid_embedded_bitmaps_);
+}
 #endif
 
 }  // namespace blink
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 68c0ab4..7c69393 100644
--- a/third_party/blink/renderer/platform/fonts/font_platform_data.h
+++ b/third_party/blink/renderer/platform/fonts/font_platform_data.h
@@ -65,6 +65,7 @@
 }
 #endif  // defined(OS_MACOSX)
 
+class SkFont;
 class SkTypeface;
 typedef uint32_t SkFontID;
 
@@ -153,9 +154,14 @@
   const WebFontRenderStyle& GetFontRenderStyle() const { return style_; }
 #endif
 
+  // TODO(reed): SetupSkPaint is deprecated. Remove this once all call sites
+  // are moved to SetupSkFont.
   void SetupSkPaint(SkPaint*,
                     float device_scale_factor = 1,
                     const Font* = nullptr) const;
+  void SetupSkFont(SkFont*,
+                   float device_scale_factor = 1,
+                   const Font* = nullptr) const;
 
 #if defined(OS_WIN)
   int PaintTextFlags() const { return paint_text_flags_; }
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 7de8624..b3e63ab 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
@@ -33,6 +33,7 @@
 #import "third_party/blink/renderer/platform/layout_test_support.h"
 #import "third_party/blink/renderer/platform/wtf/retain_ptr.h"
 #import "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#import "third_party/skia/include/core/SkFont.h"
 #import "third_party/skia/include/core/SkStream.h"
 #import "third_party/skia/include/ports/SkTypeface_mac.h"
 
@@ -163,6 +164,58 @@
     paint->setHinting(SkPaint::kNo_Hinting);
 }
 
+void FontPlatformData::SetupSkFont(SkFont* skfont,
+                                   float,
+                                   const Font* font) const {
+  bool should_smooth_fonts = true;
+  bool should_antialias = true;
+  bool should_subpixel_position = true;
+
+  if (font) {
+    switch (font->GetFontDescription().FontSmoothing()) {
+      case kAntialiased:
+        should_smooth_fonts = false;
+        break;
+      case kSubpixelAntialiased:
+        break;
+      case kNoSmoothing:
+        should_antialias = false;
+        should_smooth_fonts = false;
+        break;
+      case kAutoSmoothing:
+        // For the AutoSmooth case, don't do anything! Keep the default
+        // settings.
+        break;
+    }
+  }
+
+  if (LayoutTestSupport::IsRunningLayoutTest()) {
+    should_smooth_fonts = false;
+    should_antialias = should_antialias &&
+                       LayoutTestSupport::IsFontAntialiasingEnabledForTest();
+    should_subpixel_position =
+        LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest();
+  }
+
+  skfont->DEPRECATED_setAntiAlias(should_antialias);
+  skfont->setEmbeddedBitmaps(false);
+  const float ts = text_size_ >= 0 ? text_size_ : 12;
+  skfont->setSize(SkFloatToScalar(ts));
+  skfont->setTypeface(typeface_);
+  skfont->setEmbolden(synthetic_bold_);
+  skfont->setSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
+  skfont->DEPRECATED_setLCDRender(should_smooth_fonts);
+  skfont->setSubpixel(should_subpixel_position);
+
+  // When rendering using CoreGraphics, disable hinting when
+  // webkit-font-smoothing:antialiased or text-rendering:geometricPrecision is
+  // used.  See crbug.com/152304
+  if (font &&
+      (font->GetFontDescription().FontSmoothing() == kAntialiased ||
+       font->GetFontDescription().TextRendering() == kGeometricPrecision))
+    skfont->setHinting(SkFont::kNo_Hinting);
+}
+
 FontPlatformData::FontPlatformData(NSFont* ns_font,
                                    float size,
                                    bool synthetic_bold,
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
index eda92e1..1b3f6a1 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.cc
@@ -39,10 +39,9 @@
     builder_rotation_ = pending_canvas_rotation_;
   }
 
-  SkPaint run_font;
-  run_font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-  pending_font_data_->PlatformData().SetupSkPaint(&run_font,
-                                                  device_scale_factor_, &font_);
+  SkFont run_font;
+  pending_font_data_->PlatformData().SetupSkFont(&run_font,
+                                                 device_scale_factor_, &font_);
 
   const auto run_size = pending_glyphs_.size();
   const auto& buffer = HasPendingVerticalOffsets()
diff --git a/third_party/blink/renderer/platform/fonts/web_font_render_style.cc b/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
index 4e0a0847..b5bbf134d 100644
--- a/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
+++ b/third_party/blink/renderer/platform/fonts/web_font_render_style.cc
@@ -9,6 +9,8 @@
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/layout_test_support.h"
 
+#include <SkFont.h>
+
 namespace blink {
 
 namespace {
@@ -115,4 +117,23 @@
   font.setSubpixelText(force_subpixel_positioning || use_subpixel_positioning);
 }
 
+void WebFontRenderStyle::ApplyToSkFont(SkFont* font,
+                                       float device_scale_factor) const {
+  auto sk_hint_style = static_cast<SkFont::Hinting>(hint_style);
+  font->DEPRECATED_setAntiAlias(use_anti_alias);
+  font->setHinting(sk_hint_style);
+  font->setEmbeddedBitmaps(use_bitmaps);
+  font->setForceAutoHinting(use_auto_hint);
+  if (use_anti_alias)
+    font->DEPRECATED_setLCDRender(use_subpixel_rendering);
+
+  // Force-enable subpixel positioning, except when full hinting is requested on
+  // low-dpi screen or when running layout tests.
+  bool force_subpixel_positioning =
+      !LayoutTestSupport::IsRunningLayoutTest() &&
+      (sk_hint_style != SkFont::kFull_Hinting || device_scale_factor > 1.0f);
+
+  font->setSubpixel(force_subpixel_positioning || use_subpixel_positioning);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc b/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
index 0240b3b..8e1edae 100644
--- a/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
+++ b/third_party/blink/renderer/platform/fonts/win/font_platform_data_win.cc
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/platform/fonts/font_platform_data.h"
 
 #include <windows.h>
+#include "SkFont.h"
 #include "SkTypeface.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
 #include "third_party/blink/renderer/platform/layout_test_support.h"
@@ -72,6 +73,35 @@
   font->setEmbeddedBitmapText(!avoid_embedded_bitmaps_);
 }
 
+void FontPlatformData::SetupSkFont(SkFont* font, float, const Font*) const {
+  font->setSize(SkFloatToScalar(text_size_));
+  font->setTypeface(typeface_);
+  font->setEmbolden(synthetic_bold_);
+  font->setSkewX(synthetic_italic_ ? -SK_Scalar1 / 4 : 0);
+
+  uint32_t text_flags = PaintTextFlags();
+  font->DEPRECATED_setAntiAlias(
+      SkToBool(text_flags & SkPaint::kAntiAlias_Flag));
+  font->DEPRECATED_setLCDRender(
+      SkToBool(text_flags & SkPaint::kLCDRenderText_Flag));
+  font->setSubpixel(SkToBool(text_flags & SkPaint::kSubpixelText_Flag));
+
+  // Only use sub-pixel positioning if anti aliasing is enabled. Otherwise,
+  // without font smoothing, subpixel text positioning leads to uneven spacing
+  // since subpixel test placement coordinates would be passed to Skia, which
+  // only has non-antialiased glyphs to draw, so they necessarily get clamped at
+  // pixel positions, which leads to uneven spacing, either too close or too far
+  // away from adjacent glyphs. We avoid this by linking the two flags.
+  if (text_flags & SkPaint::kAntiAlias_Flag)
+    font->setSubpixel(true);
+
+  if (LayoutTestSupport::IsRunningLayoutTest() &&
+      !LayoutTestSupport::IsTextSubpixelPositioningAllowedForTest())
+    font->setSubpixel(false);
+
+  font->setEmbeddedBitmaps(!avoid_embedded_bitmaps_);
+}
+
 static bool IsWebFont(const String& family_name) {
   // Web-fonts have artifical names constructed to always be:
   // 1. 24 characters, followed by a '\0'
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 86c26f4c..5c56187 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -770,6 +770,9 @@
       name: "MediaStreamTrackContentHint",
       status: "stable",
     },
+    {
+      name: "MergeBlockingNonBlockingPools",
+    },
     // This is enabled by default on Windows only. The only part that's
     // "experimental" is the support on other platforms.
     {
@@ -1191,10 +1194,6 @@
       name: "SlimmingPaintV2",
     },
     {
-      name: "SMIL",
-      status: "stable",
-    },
-    {
       name: "SmoothScrollJSIntervention",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/scheduler/test/fuzzer/simple_thread_impl.h b/third_party/blink/renderer/platform/scheduler/test/fuzzer/simple_thread_impl.h
index da8cc0a..d1d1f1e 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fuzzer/simple_thread_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fuzzer/simple_thread_impl.h
@@ -36,7 +36,7 @@
   void Run() override;
 
   // Owner of this class.
-  ThreadPoolManager* thread_pool_manager_;
+  ThreadPoolManager* thread_pool_manager_ = nullptr;
 
   // Time in which the thread is created.
   TimeTicks initial_time_;
@@ -44,7 +44,7 @@
   // The object pointed to by |thread_manager_| is created and destructed from
   // the Run function. This is necessary since it has to be constructed from the
   // thread it should be bound to and destructed from the same thread.
-  ThreadManager* thread_manager_;
+  ThreadManager* thread_manager_ = nullptr;
 
   // Used by the Run function to only terminate when |this| is destructed, and
   // this is used so that |thread_data_| will live as long as |this|.
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
index c4abb8f..cfb3395 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.cc
@@ -206,6 +206,7 @@
       "WorkerThread.Runtime", delta, base::TimeDelta::FromSeconds(1),
       base::TimeDelta::FromDays(1), 50 /* bucket count */);
   task_queue_throttler_.reset();
+  idle_helper_.Shutdown();
   helper()->Shutdown();
 }
 
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.cc b/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
index d3e60da..af08330 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.cc
@@ -122,6 +122,14 @@
           .Append(WebStringToFilePath(relative_path)));
 }
 
+String AccessibilityTestDataPath(const String& relative_path) {
+  return FilePathToWebString(
+      BlinkRootFilePath()
+          .Append(
+              FILE_PATH_LITERAL("renderer/modules/accessibility/testing/data"))
+          .Append(WebStringToFilePath(relative_path)));
+}
+
 scoped_refptr<SharedBuffer> ReadFromFile(const String& path) {
   base::FilePath file_path = blink::WebStringToFilePath(path);
   std::string buffer;
diff --git a/third_party/blink/renderer/platform/testing/unit_test_helpers.h b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
index e3fd7b9a..a04c53d 100644
--- a/third_party/blink/renderer/platform/testing/unit_test_helpers.h
+++ b/third_party/blink/renderer/platform/testing/unit_test_helpers.h
@@ -70,6 +70,12 @@
 // specified.
 String PlatformTestDataPath(const String& relative_path = String());
 
+// Returns test data absolute path for accessibility unittests, i.e.
+// <blinkRootDir>/renderer/modules/accessibility/testing/data/<relativePath>. It
+// returns the top accessibility test directory if |relativePath| was not
+// specified.
+String AccessibilityTestDataPath(const String& relative_path = String());
+
 scoped_refptr<SharedBuffer> ReadFromFile(const String& path);
 
 class LineReader {
diff --git a/third_party/byte_buddy/BUILD.gn b/third_party/byte_buddy/BUILD.gn
index b0c1b5d..6e9c9a1 100644
--- a/third_party/byte_buddy/BUILD.gn
+++ b/third_party/byte_buddy/BUILD.gn
@@ -7,17 +7,20 @@
 java_prebuilt("byte_buddy_java") {
   supports_android = true
   testonly = true
+  enable_bytecode_checks = false
   jar_path = "lib/byte-buddy.jar"
 }
 
 java_prebuilt("byte_buddy_agent_java") {
   supports_android = true
   testonly = true
+  enable_bytecode_checks = false
   jar_path = "lib/byte-buddy-agent.jar"
 }
 
 android_java_prebuilt("byte_buddy_android_java") {
   testonly = true
+  enable_bytecode_checks = false
   deps = [
     "//third_party/android_tools:dx_25_0_2_java",
   ]
diff --git a/tools/gdb/gdb_chrome.py b/tools/gdb/gdb_chrome.py
index fdbf6ff..c8e2594 100644
--- a/tools/gdb/gdb_chrome.py
+++ b/tools/gdb/gdb_chrome.py
@@ -30,6 +30,11 @@
 
 sys.path.insert(0, os.path.join(
     os.path.dirname(os.path.abspath(__file__)),
+    'util'))
+import class_methods
+
+sys.path.insert(0, os.path.join(
+    os.path.dirname(os.path.abspath(__file__)),
     '..', '..', 'third_party', 'blink', 'tools', 'gdb'))
 try:
   import blink
@@ -375,3 +380,29 @@
 
 
 gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
+
+
+"""Implementations of inlined libc++ std container functions."""
+@class_methods.Class('std::__1::vector', template_types=['T'])
+class LibcppVector(object):
+    @class_methods.member_function('T&', 'operator[]', ['int'])
+    def element(obj, i):
+        return obj['__begin_'][i]
+
+    @class_methods.member_function('size_t', 'size', [])
+    def size(obj):
+        return obj['__end_'] - obj['__begin_']
+
+@class_methods.Class('std::__1::unique_ptr', template_types=['T'])
+class LibcppUniquePtr(object):
+    @class_methods.member_function('T*', 'get', [])
+    def get(obj):
+        return obj['__ptr_']['__value_']
+
+    @class_methods.member_function('T*', 'operator->', [])
+    def arrow(obj):
+        return obj['__ptr_']['__value_']
+
+    @class_methods.member_function('T&', 'operator*', [])
+    def dereference(obj):
+        return obj['__ptr_']['__value_'].dereference()
diff --git a/tools/gdb/util/class_methods.py b/tools/gdb/util/class_methods.py
new file mode 100644
index 0000000..2cb7e25
--- /dev/null
+++ b/tools/gdb/util/class_methods.py
@@ -0,0 +1,185 @@
+# Copyright (c) 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Helper library for defining XMethod implementations on C++ classes.
+
+Include this library and then define python implementations of C++ methods
+using the Class and member_function decorator functions.
+"""
+
+import gdb
+import gdb.xmethod
+import operator
+import re
+
+
+class MemberFunction(object):
+  def __init__(self, return_type, name, arguments, wrapped_function):
+    self.return_type = return_type
+    self.name = name
+    self.arguments = arguments
+    self.function_ = wrapped_function
+
+  def __call__(self, *args):
+    self.function_(*args)
+
+
+def member_function(return_type, name, arguments):
+  """Decorate a member function.
+
+  See Class decorator for example usage within a class.
+
+  Args:
+    return_type: The return type of the function (e.g. 'int')
+    name: The function name (e.g. 'sum')
+    arguments: The argument types for this function (e.g. ['int', 'int'])
+
+    Each type can be a string (e.g. 'int', 'std::string', 'T*') or a
+    function which constructs the return type. See CreateTypeResolver
+    for details about type resolution.
+  """
+  def DefineMember(fn):
+    return MemberFunction(return_type, name, arguments, fn)
+  return DefineMember
+
+
+def Class(class_name, template_types):
+  """Decorate a python class with its corresponding C++ type.
+  Args:
+    class_name: The canonical string identifier for the class (e.g. base::Foo)
+    template_types: An array of names for each templated type (e.g. ['K',
+    'V'])
+
+  Example:
+    As an example, the following is an implementation of size() and operator[]
+    on std::__1::vector, functions which are normally inlined and not
+    normally callable from gdb.
+
+    @class_methods.Class('std::__1::vector', template_types=['T'])
+    class LibcppVector(object):
+      @class_methods.member_function('T&', 'operator[]', ['int'])
+      def element(obj, i):
+        return obj['__begin_'][i]
+
+      @class_methods.member_function('size_t', 'size', [])
+      def size(obj):
+        return obj['__end_'] - obj['__begin_']
+
+  Note:
+    Note that functions are looked up by the function name, which means that
+    functions cannot currently have overloaded implementations for different
+    arguments.
+  """
+
+  class MethodWorkerWrapper(gdb.xmethod.XMethod):
+    """Wrapper of an XMethodWorker class as an XMethod."""
+    def __init__(self, name, worker_class):
+      super(MethodWorkerWrapper, self).__init__(name)
+      self.name = name
+      self.worker_class = worker_class
+
+
+  class ClassMatcher(gdb.xmethod.XMethodMatcher):
+    """Matches member functions of one class template."""
+    def __init__(self, obj):
+      super(ClassMatcher, self).__init__(class_name)
+
+      # Constructs a regular expression to match this type.
+      self._class_regex = re.compile(
+          '^' + re.escape(class_name) +
+          ('<.*>' if template_types > 0 else '') + '$')
+
+      # Construct a dictionary and array of methods
+      self.dict = {}
+      self.methods = []
+      for name in dir(obj):
+        attr = getattr(obj, name)
+        if not isinstance(attr, MemberFunction):
+          continue
+
+        name = attr.name
+        return_type = CreateTypeResolver(attr.return_type)
+        arguments = [CreateTypeResolver(arg) for arg in
+                     attr.arguments]
+        method = MethodWorkerWrapper(
+            attr.name,
+            CreateTemplatedMethodWorker(return_type,
+                                        arguments, attr.function_))
+        self.methods.append(method)
+
+    def match(self, class_type, method_name):
+      if not re.match(self._class_regex, class_type.tag):
+        return None
+      templates = [class_type.template_argument(i) for i in
+                   range(len(template_types))]
+      return [method.worker_class(templates) for method in self.methods
+              if method.name == method_name and method.enabled]
+
+
+  def CreateTypeResolver(type_desc):
+    """Creates a callback which resolves to the appropriate type when
+    invoked.
+
+    This is a helper to allow specifying simple types as strings when
+    writing function descriptions. For complex cases, a callback can be
+    passed which will be invoked when template instantiation is known.
+
+    Args:
+      type_desc: A callback generating the type or a string description of
+          the type to lookup. Supported types are classes in the
+          template_classes array (e.g. T) which will be looked up when those
+          templated classes are known, or globally visible type names (e.g.
+          int, base::Foo).
+
+          Types can be modified by appending a '*' or '&' to denote a
+          pointer or reference.
+
+          If a callback is used, the callback will be passed an array of the
+          instantiated template types.
+
+    Note:
+      This does not parse complex types such as std::vector<T>::iterator,
+      to refer to types like these you must currently write a callback
+      which constructs the appropriate type.
+    """
+    if callable(type_desc):
+      return type_desc
+    if type_desc == 'void':
+      return lambda T: None
+    if type_desc[-1] == '&':
+      inner_resolver = CreateTypeResolver(type_desc[:-1])
+      return lambda template_types: inner_resolver(template_types).reference()
+    if type_desc[-1] == '*':
+      inner_resolver = CreateTypeResolver(type_desc[:-1])
+      return lambda template_types: inner_resolver(template_types).pointer()
+    try:
+      template_index = template_types.index(type_desc)
+      return operator.itemgetter(template_index)
+    except ValueError:
+      return lambda template_types: gdb.lookup_type(type_desc)
+
+
+  def CreateTemplatedMethodWorker(return_callback, args_callbacks,
+                                  method_callback):
+    class TemplatedMethodWorker(gdb.xmethod.XMethodWorker):
+      def __init__(self, templates):
+        super(TemplatedMethodWorker, self).__init__()
+        self._templates = templates
+
+      def get_arg_types(self):
+        return [cb(self._templates) for cb in args_callbacks]
+
+      def get_result_type(self, obj):
+        return return_callback(self._templates)
+
+      def __call__(self, *args):
+        return method_callback(*args)
+    return TemplatedMethodWorker
+
+  def DefineClass(obj):
+    matcher = ClassMatcher(obj)
+    gdb.xmethod.register_xmethod_matcher(None, matcher)
+    return matcher
+
+  return DefineClass
diff --git a/tools/generate_stubs/generate_stubs.py b/tools/generate_stubs/generate_stubs.py
index 1d28835..6746f61 100755
--- a/tools/generate_stubs/generate_stubs.py
+++ b/tools/generate_stubs/generate_stubs.py
@@ -3,29 +3,29 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-"""Creates windows and posix stub files for a given set of signatures.
+"""Creates Windows and POSIX stub files for a given set of signatures.
 
 For libraries that need to be loaded outside of the standard executable startup
 path mechanism, stub files need to be generated for the wanted functions.  In
-windows, this is done via "def" files and the delay load mechanism.  On a posix
+Windows, this is done via "def" files and the delay load mechanism.  On a POSIX
 system, a set of stub functions need to be generated that dispatch to functions
 found via dlsym.
 
 This script takes a set of files, where each file is a list of C-style
-signatures (one signature per line).  The output is either a windows def file,
-or a header + implementation file of stubs suitable for use in a posix system.
+signatures (one signature per line).  The output is either a Windows def file,
+or a header + implementation file of stubs suitable for use in a POSIX system.
 
-This script also handles varidiac functions, e.g.
+This script also handles variadic functions, e.g.
 void printf(const char* s, ...);
 
-TODO(hclam): Fix the situation for varidiac functions.
+TODO(hclam): Fix the situation for variadic functions.
 Stub for the above function will be generated and inside the stub function it
 is translated to:
 void printf(const char* s, ...) {
   printf_ptr(s, (void*)arg1);
 }
 
-Only one argument from the varidiac arguments is used and it will be used as
+Only one argument from the variadic arguments is used and it will be used as
 type void*.
 """
 
@@ -63,9 +63,9 @@
 #
 #   1) Starts with [_a-ZA-Z] (C++ spec 2.10).
 #   2) Continues with [_a-ZA-Z0-9] (C++ spec 2.10).
-#   3) Preceeds an opening parenthesis by 0 or more whitespace chars.
+#   3) Precedes an opening parenthesis by 0 or more whitespace chars.
 #
-# From that, all preceeding characters are considered the return value.
+# From that, all preceding characters are considered the return value.
 # Trailing characters should have a substring matching the form (.*).  That
 # is considered the arguments.
 SIGNATURE_REGEX = re.compile('(?P<return_type>.+?)'
@@ -75,7 +75,7 @@
 # Used for generating C++ identifiers.
 INVALID_C_IDENT_CHARS = re.compile('[^_a-zA-Z0-9]')
 
-# Constants defning the supported file types options.
+# Constants defining the supported file types options.
 FILE_TYPE_WIN_X86 = 'windows_lib'
 FILE_TYPE_WIN_X64 = 'windows_lib_x64'
 FILE_TYPE_POSIX_STUB = 'posix_stubs'
@@ -168,7 +168,7 @@
 """
 
 # The standard includes needed for the stub implementation file.  Takes one
-# string substition with the path to the associated stub header file.
+# string substitution with the path to the associated stub header file.
 IMPLEMENTATION_PREAMBLE = """// This is generated file. Do not modify directly.
 
 #include "%s"
@@ -217,7 +217,7 @@
 """)
 
 # Template for the module initialization check function.  This template
-# takes two parameteres: the function name, and the conditional used to
+# takes two parameters: the function name, and the conditional used to
 # verify the module's initialization.
 MODULE_INITIALIZATION_CHECK_FUNCTION = (
     """// Returns true if all stubs have been properly initialized.
@@ -360,7 +360,7 @@
   """Infers the module name from the input file path.
 
   The input filename is supposed to be in the form "ModuleName.sigs".
-  This function splits the filename from the extention on that basename of
+  This function splits the filename from the extension on that basename of
   the path and returns that as the module name.
 
   Args:
@@ -427,11 +427,11 @@
 
 
 def WriteWindowsDefFile(module_name, signatures, outfile):
-  """Writes a windows def file to the given output file object.
+  """Writes a Windows def file to the given output file object.
 
     The def file format is basically a list of function names.  Generation is
     simple.  After outputting the LIBRARY and EXPORTS lines, print out each
-    function name, one to a line, preceeded by 2 spaces.
+    function name, one to a line, preceded by 2 spaces.
 
   Args:
     module_name: The name of the module we are writing a stub for.
@@ -459,7 +459,7 @@
 
 def CreateWindowsLib(module_name, signatures, intermediate_dir, outdir_path,
                      machine):
-  """Creates a windows library file.
+  """Creates a Windows library file.
 
   Calling this function will create a lib file in the outdir_path that exports
   the signatures passed into the object.  A temporary def file will be created
@@ -474,7 +474,7 @@
     machine: String holding the machine type, 'X86' or 'X64'.
 
   Raises:
-    SubprocessError: If invoking the windows "lib" tool fails, this is raised
+    SubprocessError: If invoking the Windows "lib" tool fails, this is raised
                      with the error code.
   """
   def_file_path = os.path.join(intermediate_dir,
@@ -489,7 +489,7 @@
 
   # Invoke the "lib" program on Windows to create stub .lib files for the
   # generated definitions.  These .lib files can then be used during
-  # delayloading of the dynamic libraries.
+  # delay loading of the dynamic libraries.
   ret = QuietRun(['lib', '/nologo',
                   '/machine:' + machine,
                   '/def:' + def_file_path,
@@ -509,11 +509,11 @@
   delays loading of the dynamic library/resolution of the symbols until one of
   the needed functions are accessed.
 
-  In posix, RTLD_LAZY does something similar with DSOs.  This is the default
+  In POSIX, RTLD_LAZY does something similar with DSOs.  This is the default
   link mode for DSOs.  However, even though the symbol is not resolved until
   first usage, the DSO must be present at load time of the main binary.
 
-  To simulate the windows delay load procedure, we need to create a set of
+  To simulate the Windows delay load procedure, we need to create a set of
   stub functions that allow for correct linkage of the main binary, but
   dispatch to the dynamically resolved symbol when the module is initialized.
 
@@ -618,7 +618,7 @@
 
     Args:
       signature: A signature hash, as produced by ParseSignatures,
-                 representating the function signature.
+                 representing the function signature.
 
     Returns:
       A string with the declaration of the function pointer for the signature.
@@ -637,7 +637,7 @@
 
     Args:
       signature: A signature hash, as produced by ParseSignatures,
-                 representating the function signature.
+                 representing the function signature.
 
     Returns:
       A string with the stub function definition.
@@ -660,7 +660,7 @@
       arg_list = ''
 
     if arg_list != '' and len(arguments) > 1 and arguments[-1] == '...':
-      # If the last argment is ... then this is a variadic function.
+      # If the last argument is ... then this is a variadic function.
       if return_prefix != '':
         return VARIADIC_STUB_FUNCTION_DEFINITION % {
             'return_type': signature['return_type'],
@@ -717,7 +717,7 @@
     outfile.write(UMBRELLA_INITIALIZER_START % namespace)
     outfile.write(UMBRELLA_INITIALIZER_CLEANUP_FUNCTION)
 
-    # Create the initializaiton function that calls all module initializers,
+    # Create the initialization function that calls all module initializers,
     # checks if they succeeded, and backs out module loads on an error.
     outfile.write(UMBRELLA_INITIALIZER_INITIALIZE_FUNCTION_START)
     outfile.write(
@@ -758,7 +758,7 @@
     outfile.write(STUB_HEADER_PREAMBLE %
                   {'guard_name': header_guard, 'namespace': namespace})
 
-    # Generate the Initializer protoypes for each module.
+    # Generate the Initializer prototypes for each module.
     outfile.write('// Individual module initializer functions.\n')
     for name in module_names:
       outfile.write(MODULE_FUNCTION_PROTOTYPES % {
@@ -853,7 +853,7 @@
     ptr_names = ['%s_ptr' % sig['name'] for sig in self.signatures]
 
     # Construct the conditional expression to check the initialization of
-    # all the function pointers above.  It should generate a conjuntion
+    # all the function pointers above.  It should generate a conjunction
     # with each pointer on its own line, indented by six spaces to match
     # the indentation level of MODULE_INITIALIZATION_CHECK_FUNCTION.
     initialization_conditional = ' &&\n      '.join(ptr_names)
@@ -930,7 +930,7 @@
                     dest='extra_stub_header',
                     default=None,
                     help=('File to insert after the system includes in the '
-                          'generated stub implemenation file. Ignored for '
+                          'generated stub implementation file. Ignored for '
                           '%s and %s types.' %
                           (FILE_TYPE_WIN_X86, FILE_TYPE_WIN_X64)))
   parser.add_option('-m',
@@ -945,7 +945,7 @@
                     default='',
                     help=('A macro to place between the return type and '
                           'function name, e.g. MODULE_EXPORT, to control the '
-                          'visbility of the stub functions.'))
+                          'visibility of the stub functions.'))
 
   return parser
 
@@ -1022,12 +1022,12 @@
 
 def CreateWindowsLibForSigFiles(sig_files, out_dir, intermediate_dir, machine,
                                 export_macro):
-  """For each signature file, create a windows lib.
+  """For each signature file, create a Windows lib.
 
   Args:
     sig_files: Array of strings with the paths to each signature file.
     out_dir: String holding path to directory where the generated libs go.
-    intermediate_dir: String holding path to directory generated intermdiate
+    intermediate_dir: String holding path to directory generated intermediate
                       artifacts.
     machine: String holding the machine type, 'X86' or 'X64'.
     export_macro: A preprocessor macro used to annotate stub symbols with
@@ -1047,7 +1047,7 @@
 
 
 def CreateWindowsDefForSigFiles(sig_files, out_dir, module_name):
-  """For all signature files, create a single windows def file.
+  """For all signature files, create a single Windows def file.
 
   Args:
     sig_files: Array of strings with the paths to each signature file.
@@ -1075,7 +1075,7 @@
 def CreatePosixStubsForSigFiles(sig_files, stub_name, out_dir,
                                 intermediate_dir, path_from_source,
                                 extra_stub_header, export_macro):
-  """Create a posix stub library with a module for each signature file.
+  """Create a POSIX stub library with a module for each signature file.
 
   Args:
     sig_files: Array of strings with the paths to each signature file.
diff --git a/tools/generate_stubs/generate_stubs_unittest.py b/tools/generate_stubs/generate_stubs_unittest.py
index 107a9ce..448af21 100755
--- a/tools/generate_stubs/generate_stubs_unittest.py
+++ b/tools/generate_stubs/generate_stubs_unittest.py
@@ -6,7 +6,7 @@
 """Unittest for the generate_stubs.py.
 
 Since generate_stubs.py is a code generator, it is hard to do a very good
-test.  Instead of creating a golden-file test, which might be flakey, this
+test.  Instead of creating a golden-file test, which might be flaky, this
 test elects instead to verify that various components "exist" within the
 generated file as a sanity check.  In particular, there is a simple hit
 test to make sure that umbrella functions, etc., do try and include every
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 54ed5e1..e67e02af 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10628,6 +10628,9 @@
 </enum>
 
 <enum name="DesktopIOSPromotionDismissalReason">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <int value="0" label="Focus lost."/>
   <int value="1" label="No thanks clicked."/>
   <int value="2" label="Close button clicked."/>
@@ -10636,6 +10639,9 @@
 </enum>
 
 <enum name="DesktopIOSPromotionEntryPoint">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <int value="1" label="Save Passwords new bubble."/>
   <int value="2" label="Bookmarks new bubble."/>
   <int value="3" label="Bookmarks footnote."/>
@@ -20676,9 +20682,9 @@
   <int value="20" label="kMagnetometer"/>
   <int value="21" label="kUnsizedMedia"/>
   <int value="22" label="kLegacyImageFormats"/>
-  <int value="23" label="kImageCompression"/>
+  <int value="23" label="kUnoptimizedImages"/>
   <int value="24" label="kAnimations"/>
-  <int value="25" label="kMaxDownscalingImage"/>
+  <int value="25" label="kOversizedImages"/>
   <int value="26" label="kPictureInPicture"/>
   <int value="27" label="kVerticalScroll"/>
   <int value="28" label="kDocumentWrite"/>
@@ -29142,6 +29148,7 @@
   <int value="-1710772665" label="disable-my-files-navigation"/>
   <int value="-1703709912" label="enable-new-ntp"/>
   <int value="-1703308540" label="disable-webaudio"/>
+  <int value="-1701123067" label="ShowManagedUi:enabled"/>
   <int value="-1696366449" label="disable-permissions-bubbles"/>
   <int value="-1695774453" label="skip-extra-ash-window-positioning"/>
   <int value="-1694353093" label="ArcVpn:disabled"/>
@@ -30640,6 +30647,7 @@
   <int value="1111871757" label="ForceUnifiedConsentBump:enabled"/>
   <int value="1112051724" label="DetectingHeavyPages:enabled"/>
   <int value="1112885300" label="VizHitTestDrawQuad:disabled"/>
+  <int value="1113196543" label="ShowManagedUi:disabled"/>
   <int value="1113365156" label="tab-management-experiment-type-chive"/>
   <int value="1114629582" label="enable-floating-virtual-keyboard"/>
   <int value="1115476442" label="PolicyTool:disabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index ccdf890..0b286d7 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -313,7 +313,7 @@
 </histogram>
 
 <histogram name="Accessibility.Mac.DifferentiateWithoutColor"
-    enum="BooleanEnabled">
+    enum="BooleanEnabled" expires_after="2019-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;differentiate without color&quot; Mac system setting is
@@ -322,7 +322,7 @@
 </histogram>
 
 <histogram name="Accessibility.Mac.FullKeyboardAccessEnabled"
-    enum="BooleanEnabled">
+    enum="BooleanEnabled" expires_after="2019-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;full keyboard access&quot; Mac system setting is enabled.
@@ -330,7 +330,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.Mac.IncreaseContrast" enum="BooleanEnabled">
+<histogram name="Accessibility.Mac.IncreaseContrast" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;increase contrast&quot; Mac system setting is enabled.
@@ -338,7 +339,19 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.Mac.ReduceTransparency" enum="BooleanEnabled">
+<histogram name="Accessibility.Mac.ReduceMotion" enum="BooleanEnabled">
+  <owner>dmazzoni@chromium.org</owner>
+  <owner>smcgruer@chromium.org</owner>
+  <summary>
+    Tracks whether the accessibilityDisplayShouldReduceMotion system property is
+    enabled. The purpose is to inform the design of the prefers-reduced-motion
+    media feature; see http://crbug.com/722548. This is checked once, 45 seconds
+    after startup.
+  </summary>
+</histogram>
+
+<histogram name="Accessibility.Mac.ReduceTransparency" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>ellyjones@chromium.org</owner>
   <summary>
     Whether the &quot;reduce transparency&quot; Mac system setting is enabled.
@@ -391,13 +404,26 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.Win.AnimationsEnabled" enum="BooleanEnabled"
+    expires_after="2019-11-02">
+  <owner>dmazzoni@chromium.org</owner>
+  <owner>smcgruer@chromium.org</owner>
+  <summary>
+    Tracks whether the SPI_GETCLIENTAREAANIMATION system property is enabled.
+    The purpose is to inform the design of the prefers-reduced-motion media
+    feature; see http://crbug.com/722548. This is checked once, 45 seconds after
+    startup.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.WinAPIs" enum="AccessibilityWinAPIEnum">
   <owner>dmazzoni@chromium.org</owner>
   <owner>nektar@chromium.org</owner>
   <summary>Tracks usage of all public Windows accessibility APIs.</summary>
 </histogram>
 
-<histogram name="Accessibility.WinAudioDescription" enum="BooleanEnabled">
+<histogram name="Accessibility.WinAudioDescription" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>
@@ -406,7 +432,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.WinJAWS" enum="BooleanEnabled">
+<histogram name="Accessibility.WinJAWS" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>
@@ -415,7 +442,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.WinNVDA" enum="BooleanEnabled">
+<histogram name="Accessibility.WinNVDA" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>
@@ -424,7 +452,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.WinSAToGo" enum="BooleanEnabled">
+<histogram name="Accessibility.WinSAToGo" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>
@@ -433,7 +462,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.WinScreenReader" enum="BooleanEnabled">
+<histogram name="Accessibility.WinScreenReader" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>
@@ -444,7 +474,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.WinStickyKeys" enum="BooleanEnabled">
+<histogram name="Accessibility.WinStickyKeys" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>
@@ -452,7 +483,8 @@
   </summary>
 </histogram>
 
-<histogram name="Accessibility.WinZoomText" enum="BooleanEnabled">
+<histogram name="Accessibility.WinZoomText" enum="BooleanEnabled"
+    expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
   <owner>kenjibaheux@google.com</owner>
   <summary>
@@ -16787,6 +16819,10 @@
 </histogram>
 
 <histogram name="CrashReport.CrashBackgroundUploadDelay" units="ms">
+  <obsolete>
+    Deprecated as of 03/2018 when Chrome for iOS stopped uploading crash dumps
+    in the background.
+  </obsolete>
   <owner>olivierrobin@chromium.org</owner>
   <owner>pkl@chromium.org</owner>
   <summary>
@@ -16796,6 +16832,10 @@
 </histogram>
 
 <histogram name="CrashReport.PendingReportsOnBackgroundWakeUp">
+  <obsolete>
+    Deprecated as of 03/2018 when Chrome for iOS stopped uploading crash dumps
+    in the background.
+  </obsolete>
   <owner>olivierrobin@chromium.org</owner>
   <owner>pkl@chromium.org</owner>
   <summary>
@@ -19769,6 +19809,9 @@
 
 <histogram name="DesktopIOSPromotion.DismissalReason"
     enum="DesktopIOSPromotionDismissalReason">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>mrefaat@chromium.org</owner>
   <summary>
     The dimissal reason of the desktop to iOS promotion entry point.
@@ -19779,6 +19822,9 @@
 
 <histogram name="DesktopIOSPromotion.ImpressionFromEntryPoint"
     enum="DesktopIOSPromotionEntryPoint">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>mrefaat@chromium.org</owner>
   <summary>
     The type of the desktop to iOS promotion that was shown to the user. This
@@ -19788,6 +19834,9 @@
 
 <histogram name="DesktopIOSPromotion.IOSSigninReason"
     enum="DesktopIOSPromotionEntryPoint">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>mrefaat@chromium.org</owner>
   <summary>
     [IOS] For users who sign-in on Chrome for iOS, log the type of the desktop
@@ -19802,6 +19851,9 @@
 
 <histogram name="DesktopIOSPromotion.OAuthTokenCompletion"
     enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>justincohen@chromium.org</owner>
   <summary>
     Whether getting the OAuth token was successful for a desktop to iOS
@@ -19811,6 +19863,9 @@
 
 <histogram name="DesktopIOSPromotion.OAuthTokenResponseCode"
     enum="CombinedHttpResponseAndNetErrorCode">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>justincohen@chromium.org</owner>
   <summary>
     HTTP Response code returned by the server when trying to fetch the OAuth
@@ -19820,6 +19875,9 @@
 
 <histogram name="DesktopIOSPromotion.QueryPhoneNumberSucceeded"
     enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>mrefaat@chromium.org</owner>
   <summary>
     Whether the SMS service api called from the desktop to iOS promotion
@@ -19829,6 +19887,9 @@
 </histogram>
 
 <histogram name="DesktopIOSPromotion.SendSMSSucceeded" enum="BooleanSuccess">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>mrefaat@chromium.org</owner>
   <summary>
     Whether the SMS service api initiated SMS sending successfully from the
@@ -19837,6 +19898,9 @@
 </histogram>
 
 <histogram name="DesktopIOSPromotion.SMSToSigninTime" units="hours">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>mrefaat@chromium.org</owner>
   <summary>
     [IOS] The difference between the client time of triggering the SMS action on
@@ -19851,6 +19915,9 @@
 </histogram>
 
 <histogram base="true" name="DesktopIOSPromotion.VariationSigninReason">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <owner>mrefaat@chromium.org</owner>
   <summary>
     [IOS] For users who sign-in on Chrome for iOS, log which variation of the
@@ -48771,6 +48838,49 @@
   </summary>
 </histogram>
 
+<histogram name="Memory.ParkableString.Compression.Latency"
+    units="microseconds">
+  <owner>lizeb@chromium.org</owner>
+  <summary>Time to compress a parkable string, in ms.</summary>
+</histogram>
+
+<histogram name="Memory.ParkableString.Compression.SizeKb" units="KB">
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Size of a compressed parkable string, recorded at compression time.
+  </summary>
+</histogram>
+
+<histogram name="Memory.ParkableString.Compression.ThroughputMBps" units="MBps">
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Size of a compressed parkable string, recorded at compression time.
+  </summary>
+</histogram>
+
+<histogram name="Memory.ParkableString.Decompression.Latency"
+    units="microseconds">
+  <owner>lizeb@chromium.org</owner>
+  <summary>Time to decompress a parkable string, in ms.</summary>
+</histogram>
+
+<histogram name="Memory.ParkableString.Decompression.SizeKb" units="KB">
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Original size of a compressed parkable string, recorded at decompression
+    time.
+  </summary>
+</histogram>
+
+<histogram name="Memory.ParkableString.Decompression.ThroughputMBps"
+    units="MBps">
+  <owner>lizeb@chromium.org</owner>
+  <summary>
+    Original size of a compressed parkable string, recorded at decompression
+    time.
+  </summary>
+</histogram>
+
 <histogram name="Memory.PepperFlashPlugin" units="KB">
   <obsolete>
     Deprecated 11/2017. No direct replacement.
@@ -126544,6 +126654,9 @@
 
 <histogram_suffixes name="DesktopIOSEntryPoints" separator="."
     ordering="prefix">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <suffix name="BookmarksFootNote"/>
   <suffix name="BookmarksNewBubble"/>
   <suffix name="HistoryPage"/>
@@ -126554,6 +126667,9 @@
 
 <histogram_suffixes name="DesktopIOSPromotionSMSSent" separator="."
     ordering="prefix">
+  <obsolete>
+    Deprecated 11/2018 in issue 894963.
+  </obsolete>
   <suffix name="NoSMS" label="No SMS was sent from the promotion."/>
   <suffix name="SMSSent" label="SMS sent from the promotion."/>
   <affected-histogram name="DesktopIOSPromotion.IOSSigninReason"/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 74755cf..8397266 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -2860,6 +2860,13 @@
       render process used by other tabs. If this is undefined, TabPMF will be
       undefined.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="SubFrameProcessPMF.Excluded">
     <summary>
@@ -2868,11 +2875,25 @@
       the process has responsiblities outside the scope of the relevant tab. If
       this is non-zero, TabPMF will be undefined.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="SubFrameProcessPMF.Included">
     <summary>
       Number of render processes that contributed to SubFrameProcessPMF.Total.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="SubFrameProcessPMF.Total">
     <summary>
@@ -2881,6 +2902,13 @@
       hosts frames of other tabs, it isn't considered to be 'dedicated' and so
       it doesn't contribute to this total.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
   <metric name="TabPMF">
     <summary>
@@ -2889,6 +2917,13 @@
       responsible for hosting parts of this tab while hosting parts of other
       tabs.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
 </event>
 
diff --git a/tools/perf/benchmarks/dromaeo.py b/tools/perf/benchmarks/dromaeo.py
index 59590fc..f178ed0 100644
--- a/tools/perf/benchmarks/dromaeo.py
+++ b/tools/perf/benchmarks/dromaeo.py
@@ -2,109 +2,23 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import json
-import math
-import os
-
-from core import perf_benchmark
+from benchmarks import press
 
 from telemetry import benchmark
-from telemetry import page as page_module
-from telemetry.page import legacy_page_test
-from telemetry import story
-from telemetry.value import scalar
 
-
-class _DromaeoMeasurement(legacy_page_test.LegacyPageTest):
-
-  def __init__(self):
-    super(_DromaeoMeasurement, self).__init__()
-
-  def ValidateAndMeasurePage(self, page, tab, results):
-    tab.WaitForJavaScriptCondition(
-        'window.document.getElementById("pause") &&' +
-        'window.document.getElementById("pause").value == "Run"',
-        timeout=120)
-
-    # Start spying on POST request that will report benchmark results, and
-    # intercept result data.
-    tab.ExecuteJavaScript("""
-        (function() {
-          var real_jquery_ajax_ = window.jQuery;
-          window.results_ = "";
-          window.jQuery.ajax = function(request) {
-            if (request.url == "store.php") {
-              window.results_ = decodeURIComponent(request.data);
-              window.results_ = window.results_.substring(
-                window.results_.indexOf("=") + 1,
-                window.results_.lastIndexOf("&"));
-              real_jquery_ajax_(request);
-            }
-          };
-        })();""")
-    # Starts benchmark.
-    tab.ExecuteJavaScript('window.document.getElementById("pause").click();')
-
-    tab.WaitForJavaScriptCondition('!!window.results_', timeout=600)
-
-    score = json.loads(tab.EvaluateJavaScript('window.results_ || "[]"'))
-
-    def Escape(k):
-      chars = [' ', '.', '-', '/', '(', ')', '*']
-      for c in chars:
-        k = k.replace(c, '_')
-      return k
-
-    def AggregateData(container, key, value):
-      if key not in container:
-        container[key] = {'count': 0, 'sum': 0}
-      container[key]['count'] += 1
-      container[key]['sum'] += math.log(value)
-
-    suffix = page.url[page.url.index('?') + 1:]
-
-    def AddResult(name, value):
-      important = False
-      if name == suffix:
-        important = True
-      results.AddValue(scalar.ScalarValue(
-          results.current_page, Escape(name), 'runs/s', value, important))
-
-    aggregated = {}
-    for data in score:
-      AddResult('%s/%s' % (data['collection'], data['name']),
-                data['mean'])
-
-      top_name = data['collection'].split('-', 1)[0]
-      AggregateData(aggregated, top_name, data['mean'])
-
-      collection_name = data['collection']
-      AggregateData(aggregated, collection_name, data['mean'])
-
-    for key, value in aggregated.iteritems():
-      AddResult(key, math.exp(value['sum'] / value['count']))
+from page_sets import dromaeo_pages
 
 
 @benchmark.Info(component='Blink>Bindings',
                 emails=['jbroman@chromium.org',
                          'yukishiino@chromium.org',
                          'haraken@chromium.org'])
-class DromaeoBenchmark(perf_benchmark.PerfBenchmark):
-
-  test = _DromaeoMeasurement
+# pylint: disable=protected-access
+class DromaeoBenchmark(press._PressBenchmark):
 
   @classmethod
   def Name(cls):
     return 'dromaeo'
 
   def CreateStorySet(self, options):
-    archive_data_file = '../page_sets/data/dromaeo.json'
-    ps = story.StorySet(
-        archive_data_file=archive_data_file,
-        base_dir=os.path.dirname(os.path.abspath(__file__)),
-        cloud_storage_bucket=story.PUBLIC_BUCKET)
-    for query_param in ['dom-attr', 'dom-modify', 'dom-query', 'dom-traverse']:
-      url = 'http://dromaeo.com?%s' % query_param
-      ps.AddStory(page_module.Page(
-          url, ps, ps.base_dir, make_javascript_deterministic=False, name=url))
-    return ps
+    return dromaeo_pages.DromaeoStorySet()
diff --git a/tools/perf/benchmarks/press.py b/tools/perf/benchmarks/press.py
new file mode 100644
index 0000000..87825a06
--- /dev/null
+++ b/tools/perf/benchmarks/press.py
@@ -0,0 +1,31 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Base class for a PressBenchmark.
+
+This benchmark manages both PressStory objects that
+implement javascript based metrics as well as can
+compute TMBv2 metrics.
+
+Example implementation:
+
+  FooPressBenchmark(press._PressBenchmark):
+    @classmethod
+    def Name(clas):
+      return Foo;
+
+    def CreateStorySet():
+      // Return a set of stories inheriting from
+      // page_sets.PressStory
+
+    def CreateCoreTimelineBasedMeasurementOptions()
+      // Implement to define tracing metrics you
+      // want on top of any javascript metrics
+      // implemented in your stories
+"""
+from core import perf_benchmark
+
+from measurements import dual_metric_measurement
+
+class _PressBenchmark(perf_benchmark.PerfBenchmark):
+  test = dual_metric_measurement.DualMetricMeasurement
diff --git a/tools/perf/benchmarks/speedometer.py b/tools/perf/benchmarks/speedometer.py
index c6c64b7..df2549f 100644
--- a/tools/perf/benchmarks/speedometer.py
+++ b/tools/perf/benchmarks/speedometer.py
@@ -15,100 +15,20 @@
 these types of operations depends on the speed of the DOM APIs, the JavaScript
 engine, CSS style resolution, layout, and other technologies.
 """
-
-import os
-
-from core import perf_benchmark
-
 from telemetry import benchmark
-from telemetry import page as page_module
-from telemetry.page import legacy_page_test
-from telemetry import story
-from telemetry.value import list_of_scalar_values
 
-
-class SpeedometerMeasurement(legacy_page_test.LegacyPageTest):
-  enabled_suites = [
-      'VanillaJS-TodoMVC',
-      'EmberJS-TodoMVC',
-      'BackboneJS-TodoMVC',
-      'jQuery-TodoMVC',
-      'AngularJS-TodoMVC',
-      'React-TodoMVC',
-      'FlightJS-TodoMVC'
-  ]
-
-  def __init__(self):
-    super(SpeedometerMeasurement, self).__init__()
-
-  def ValidateAndMeasurePage(self, page, tab, results):
-    tab.WaitForDocumentReadyStateToBeComplete()
-    iterationCount = 10
-    # A single iteration on android takes ~75 seconds, the benchmark times out
-    # when running for 10 iterations.
-    if tab.browser.platform.GetOSName() == 'android':
-      iterationCount = 3
-
-    tab.ExecuteJavaScript("""
-        // Store all the results in the benchmarkClient
-        benchmarkClient._measuredValues = []
-        benchmarkClient.didRunSuites = function(measuredValues) {
-          benchmarkClient._measuredValues.push(measuredValues);
-          benchmarkClient._timeValues.push(measuredValues.total);
-        };
-        benchmarkClient.iterationCount = {{ count }};
-        startTest();
-        """,
-        count=iterationCount)
-    tab.WaitForJavaScriptCondition(
-        'benchmarkClient._finishedTestCount == benchmarkClient.testsCount',
-        timeout=600)
-    results.AddValue(list_of_scalar_values.ListOfScalarValues(
-        page, 'Total', 'ms',
-        tab.EvaluateJavaScript('benchmarkClient._timeValues'),
-        important=True))
-    results.AddValue(list_of_scalar_values.ListOfScalarValues(
-        page, 'RunsPerMinute', 'score',
-        tab.EvaluateJavaScript(
-            '[parseFloat(document.getElementById("result-number").innerText)];'
-        ),
-        important=True))
-
-    # Extract the timings for each suite
-    for suite_name in self.enabled_suites:
-      results.AddValue(list_of_scalar_values.ListOfScalarValues(
-          page, suite_name, 'ms',
-          tab.EvaluateJavaScript("""
-              var suite_times = [];
-              for(var i = 0; i < benchmarkClient.iterationCount; i++) {
-                suite_times.push(
-                    benchmarkClient._measuredValues[i].tests[{{ key }}].total);
-              };
-              suite_times;
-              """,
-              key=suite_name), important=False))
-
+import page_sets
+from benchmarks import press
 
 @benchmark.Info(emails=['hablich@chromium.org'],
                 component='Blink')
-class Speedometer(perf_benchmark.PerfBenchmark):
-  test = SpeedometerMeasurement
-
+class Speedometer(press._PressBenchmark): # pylint: disable=protected-access
   @classmethod
   def Name(cls):
     return 'speedometer'
 
   def CreateStorySet(self, options):
-    ps = story.StorySet(
-        base_dir=os.path.dirname(os.path.abspath(__file__)),
-        archive_data_file='../page_sets/data/speedometer.json',
-        cloud_storage_bucket=story.PUBLIC_BUCKET)
-    ps.AddStory(page_module.Page(
-        'http://browserbench.org/Speedometer/', ps, ps.base_dir,
-        make_javascript_deterministic=False,
-        name='http://browserbench.org/Speedometer/'))
-    return ps
-
+    return page_sets.SpeedometerStorySet()
 
 @benchmark.Info(emails=['hablich@chromium.org'],
                 component='Blink')
@@ -123,4 +43,5 @@
     return 'speedometer-future'
 
   def SetExtraBrowserOptions(self, options):
+    super(V8SpeedometerFuture, self).SetExtraBrowserOptions(options)
     options.AppendExtraBrowserArgs('--enable-features=V8VmFuture')
diff --git a/tools/perf/benchmarks/speedometer2.py b/tools/perf/benchmarks/speedometer2.py
index 5e32bda8..64e27ab 100644
--- a/tools/perf/benchmarks/speedometer2.py
+++ b/tools/perf/benchmarks/speedometer2.py
@@ -8,121 +8,21 @@
 import os
 import re
 
+from benchmarks import press
+
 from core import path_util
-from core import perf_benchmark
 
 from telemetry import benchmark
-from telemetry import page as page_module
-from telemetry.page import legacy_page_test
 from telemetry import story
-from telemetry.value import list_of_scalar_values
 
+from page_sets import speedometer2_pages
 
 _SPEEDOMETER_DIR = os.path.join(path_util.GetChromiumSrcDir(),
     'third_party', 'blink', 'perf_tests', 'speedometer')
-_SPEEDOMETER_SUITE_NAME_BASE = '{0}-TodoMVC'
-_SPEEDOMETER_SUITES = [
-  'VanillaJS',
-  'Vanilla-ES2015',
-  'Vanilla-ES2015-Babel-Webpack',
-  'React',
-  'React-Redux',
-  'EmberJS',
-  'EmberJS-Debug',
-  'BackboneJS',
-  'AngularJS',
-  'Angular2-TypeScript',
-  'VueJS',
-  'jQuery',
-  'Preact',
-  'Inferno',
-  'Elm',
-  'Flight'
-]
-
-
-class Speedometer2Measurement(legacy_page_test.LegacyPageTest):
-  def __init__(self, should_filter_suites, filtered_suite_names=None,
-               enable_smoke_test_mode=False):
-    super(Speedometer2Measurement, self).__init__()
-    self.should_filter_suites_ = should_filter_suites
-    self.filtered_suites_ = filtered_suite_names
-    self.enable_smoke_test_mode = enable_smoke_test_mode
-
-  def ValidateAndMeasurePage(self, page, tab, results):
-    tab.WaitForDocumentReadyStateToBeComplete()
-    iterationCount = 10
-    # A single iteration on android takes ~75 seconds, the benchmark times out
-    # when running for 10 iterations.
-    if tab.browser.platform.GetOSName() == 'android':
-      iterationCount = 3
-    # For a smoke test one iteration is sufficient
-    if self.enable_smoke_test_mode:
-      iterationCount = 1
-
-    if self.should_filter_suites_:
-      tab.ExecuteJavaScript("""
-        Suites.forEach(function(suite) {
-          suite.disabled = {{ filtered_suites }}.indexOf(suite.name) < 0;
-        });
-      """, filtered_suites=self.filtered_suites_)
-
-    enabled_suites = tab.EvaluateJavaScript("""
-      (function() {
-        var suitesNames = [];
-        Suites.forEach(function(s) {
-          if (!s.disabled)
-            suitesNames.push(s.name);
-        });
-        return suitesNames;
-       })();""")
-
-    tab.ExecuteJavaScript("""
-        // Store all the results in the benchmarkClient
-        var testDone = false;
-        var iterationCount = {{ count }};
-        var benchmarkClient = {};
-        var suiteValues = [];
-        benchmarkClient.didRunSuites = function(measuredValues) {
-          suiteValues.push(measuredValues);
-        };
-        benchmarkClient.didFinishLastIteration = function () {
-          testDone = true;
-        };
-        var runner = new BenchmarkRunner(Suites, benchmarkClient);
-        runner.runMultipleIterations(iterationCount);
-        """,
-        count=iterationCount)
-    tab.WaitForJavaScriptCondition('testDone', timeout=600)
-
-    if not self.should_filter_suites_:
-      results.AddValue(list_of_scalar_values.ListOfScalarValues(
-          page, 'Total', 'ms',
-          tab.EvaluateJavaScript('suiteValues.map(each => each.total)'),
-          important=True))
-      results.AddValue(list_of_scalar_values.ListOfScalarValues(
-          page, 'RunsPerMinute', 'score',
-          tab.EvaluateJavaScript('suiteValues.map(each => each.score)'),
-          important=True))
-
-    # Extract the timings for each suite
-    for suite_name in enabled_suites:
-      results.AddValue(list_of_scalar_values.ListOfScalarValues(
-          page, suite_name, 'ms',
-          tab.EvaluateJavaScript("""
-              var suite_times = [];
-              for(var i = 0; i < iterationCount; i++) {
-                suite_times.push(
-                    suiteValues[i].tests[{{ key }}].total);
-              };
-              suite_times;
-              """,
-              key=suite_name), important=False))
-
 
 @benchmark.Info(emails=['hablich@chromium.org'],
                 component='Blink')
-class Speedometer2(perf_benchmark.PerfBenchmark):
+class Speedometer2(press._PressBenchmark): # pylint: disable=protected-access
   """Speedometer2 Benchmark.
 
   Runs all the speedometer 2 suites by default. Add --suite=<regex> to filter
@@ -136,29 +36,15 @@
   def Name(cls):
     return 'speedometer2'
 
-  @staticmethod
-  def GetFullSuiteName(name):
-    return _SPEEDOMETER_SUITE_NAME_BASE.format(name)
-
-  @staticmethod
-  def GetSuites(suite_regex):
-    if not suite_regex:
-      return []
-    exp = re.compile(suite_regex)
-    return [name for name in _SPEEDOMETER_SUITES
-            if exp.search(Speedometer2.GetFullSuiteName(name))]
-
-  def CreatePageTest(self, options):
-      should_filter_suites = bool(options.suite)
-      filtered_suite_names = map(Speedometer2.GetFullSuiteName,
-          Speedometer2.GetSuites(options.suite))
-      return Speedometer2Measurement(should_filter_suites, filtered_suite_names,
-                                     self.enable_smoke_test_mode)
-
   def CreateStorySet(self, options):
+    should_filter_suites = bool(options.suite)
+    filtered_suite_names = map(
+        speedometer2_pages.Speedometer2Story.GetFullSuiteName,
+            speedometer2_pages.Speedometer2Story.GetSuites(options.suite))
+
     ps = story.StorySet(base_dir=_SPEEDOMETER_DIR)
-    ps.AddStory(page_module.Page(
-       'file://InteractiveRunner.html', ps, ps.base_dir, name='Speedometer2'))
+    ps.AddStory(speedometer2_pages.Speedometer2Story(ps, should_filter_suites,
+        filtered_suite_names, self.enable_smoke_test_mode))
     return ps
 
   @classmethod
@@ -170,7 +56,7 @@
   def ProcessCommandLineArgs(cls, parser, args):
     if args.suite:
       try:
-        if not Speedometer2.GetSuites(args.suite):
+        if not speedometer2_pages.Speedometer2Story.GetSuites(args.suite):
           raise parser.error('--suite: No matches.')
       except re.error:
         raise parser.error('--suite: Invalid regex.')
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 3912bbf..9bcaf51 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -42,9 +42,7 @@
 crbug.com/574483 [ Android_Svelte ] blink_perf.paint/* [ Skip ]
 crbug.com/799540 [ Nexus_5 ] blink_perf.paint/* [ Skip ]
 crbug.com/859979 [ Android_Webview ] blink_perf.paint/paint-offset-changes.html [ Skip ]
-crbug.com/901493 [ Nexus6_Webview ] blink_perf.paint/large-table-background-change-with-visible-collapsed-borders.html [ Skip ]
-crbug.com/901493 [ Nexus6_Webview ] blink_perf.paint/large-table-collapsed-border-change-with-backgrounds.html [ Skip ]
-crbug.com/901493 [ Nexus6_Webview ] blink_perf.paint/large-table-collapsed-border-change-with-text.html [ Skip ]
+crbug.com/901493 [ Nexus6_Webview ] blink_perf.paint/* [ Skip ]
 
 # Benchmark: blink_perf.shadow_dom
 crbug.com/702319 [ Nexus_5X ] blink_perf.shadow_dom/* [ Skip ]
diff --git a/tools/perf/measurements/dual_metric_measurement.py b/tools/perf/measurements/dual_metric_measurement.py
new file mode 100644
index 0000000..8342ce2e
--- /dev/null
+++ b/tools/perf/measurements/dual_metric_measurement.py
@@ -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.
+
+from telemetry.web_perf import story_test
+from telemetry.web_perf import timeline_based_measurement
+
+
+class DualMetricMeasurement(story_test.StoryTest):
+  """Test class for a benchmark that aggregates all metrics.
+
+    Assumes both javascript as well as tracing metrics might be defined.
+
+    All pages associated with this measurement must implement
+    GetJavascriptMetricValues()
+  """
+  def __init__(self, tbm_options):
+    super(DualMetricMeasurement, self).__init__()
+    # Only enable tracing if metrics have been specified.
+    if tbm_options.GetTimelineBasedMetrics():
+      self._tbm_test = timeline_based_measurement.TimelineBasedMeasurement(
+          tbm_options)
+      self._enable_tracing = True
+    else:
+      self._enable_tracing = False
+
+  def WillRunStory(self, platform):
+    if self._enable_tracing:
+      self._tbm_test.WillRunStory(platform)
+
+  def Measure(self, platform, results):
+    for value in results.current_page.GetJavascriptMetricValues():
+      results.AddValue(value)
+    # This call is necessary to convert the current ScalarValues to
+    # histograms before more histograms are added.  If we don't,
+    # when histograms get added by TBM2 page_test_results will see those and
+    # not convert any existing values because it assumes they are already
+    # converted.  Therefore, so the javascript metrics don't get dropped, we
+    # have to convert them first.
+    results.PopulateHistogramSet()
+    if self._enable_tracing:
+      self._tbm_test.Measure(platform, results)
+
+  def DidRunStory(self, platform, results):
+    if self._enable_tracing:
+      self._tbm_test.DidRunStory(platform, results)
+
diff --git a/tools/perf/page_sets/dromaeo_pages.py b/tools/perf/page_sets/dromaeo_pages.py
new file mode 100644
index 0000000..84d4cda5
--- /dev/null
+++ b/tools/perf/page_sets/dromaeo_pages.py
@@ -0,0 +1,96 @@
+# 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 json
+import math
+
+from telemetry import story
+from telemetry.value import scalar
+
+from page_sets import press_story
+
+class DromaeoStory(press_story.PressStory):
+
+  def __init__(self, url, ps):
+    self.URL = url
+    super(DromaeoStory, self).__init__(ps)
+
+
+  def ExecuteTest(self, action_runner):
+    action_runner.WaitForJavaScriptCondition(
+        'window.document.getElementById("pause") &&' +
+        'window.document.getElementById("pause").value == "Run"',
+        timeout=120)
+
+    # Start spying on POST request that will report benchmark results, and
+    # intercept result data.
+    action_runner.ExecuteJavaScript("""
+        (function() {
+          var real_jquery_ajax_ = window.jQuery;
+          window.results_ = "";
+          window.jQuery.ajax = function(request) {
+            if (request.url == "store.php") {
+              window.results_ = decodeURIComponent(request.data);
+              window.results_ = window.results_.substring(
+                window.results_.indexOf("=") + 1,
+                window.results_.lastIndexOf("&"));
+              real_jquery_ajax_(request);
+            }
+          };
+        })();""")
+    # Starts benchmark.
+    action_runner.ExecuteJavaScript(
+        'window.document.getElementById("pause").click();')
+
+    action_runner.WaitForJavaScriptCondition('!!window.results_', timeout=600)
+
+
+  def ParseTestResults(self, action_runner):
+    score = json.loads(
+        action_runner.EvaluateJavaScript('window.results_ || "[]"'))
+
+    def Escape(k):
+      chars = [' ', '.', '-', '/', '(', ')', '*']
+      for c in chars:
+        k = k.replace(c, '_')
+      return k
+
+    def AggregateData(container, key, value):
+      if key not in container:
+        container[key] = {'count': 0, 'sum': 0}
+      container[key]['count'] += 1
+      container[key]['sum'] += math.log(value)
+
+    suffix = self.url[self.url.index('?') + 1:]
+
+    def AddResult(name, value):
+      important = False
+      if name == suffix:
+        important = True
+      self.AddJavascriptMetricValue(scalar.ScalarValue(
+          self, Escape(name), 'runs/s', value, important))
+
+    aggregated = {}
+    for data in score:
+      AddResult('%s/%s' % (data['collection'], data['name']),
+                data['mean'])
+
+      top_name = data['collection'].split('-', 1)[0]
+      AggregateData(aggregated, top_name, data['mean'])
+
+      collection_name = data['collection']
+      AggregateData(aggregated, collection_name, data['mean'])
+
+    for key, value in aggregated.iteritems():
+      AddResult(key, math.exp(value['sum'] / value['count']))
+
+
+class DromaeoStorySet(story.StorySet):
+  def __init__(self):
+    super(DromaeoStorySet, self).__init__(
+        archive_data_file='../page_sets/data/dromaeo.json',
+        cloud_storage_bucket=story.PUBLIC_BUCKET)
+
+    for query_param in ['dom-attr', 'dom-modify', 'dom-query', 'dom-traverse']:
+      url = 'http://dromaeo.com?%s' % query_param
+      self.AddStory(DromaeoStory(url, self))
diff --git a/tools/perf/page_sets/press_story.py b/tools/perf/page_sets/press_story.py
new file mode 100644
index 0000000..26ae62f
--- /dev/null
+++ b/tools/perf/page_sets/press_story.py
@@ -0,0 +1,52 @@
+# 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.
+
+from telemetry.page import page as page_module
+
+
+class PressStory(page_module.Page):
+  """Base class for Press stories.
+
+    Override ExecuteTest to execute javascript on the page and
+    ParseTestResults to obtain javascript metrics from page.
+
+    Example Implementation:
+
+    class FooPressStory:
+      URL = 'http://foo'
+
+      def ExecuteTest(self, action_runner):
+        //Execute some javascript
+
+      def ParseTestResults(self, action_runner):
+        some_value = action_runner.EvaluateJavascript("some javascript")
+        self.AddJavascriptMetricValue(some_value)
+  """
+  URL = None
+  DETERMINISTIC_JS = False
+  NAME = None
+
+  def __init__(self, ps):
+    super(PressStory, self).__init__(
+        self.URL, ps,
+        base_dir=ps.base_dir,
+        make_javascript_deterministic=self.DETERMINISTIC_JS,
+        name=self.NAME if self.NAME else self.URL)
+    self._values = []
+
+  def GetJavascriptMetricValues(self):
+    return self._values
+
+  def AddJavascriptMetricValue(self, value):
+    self._values.append(value)
+
+  def ExecuteTest(self, action_runner):
+    pass
+
+  def ParseTestResults(self, action_runner):
+    pass
+
+  def RunPageInteractions(self, action_runner):
+    self.ExecuteTest(action_runner)
+    self.ParseTestResults(action_runner)
diff --git a/tools/perf/page_sets/speedometer2_pages.py b/tools/perf/page_sets/speedometer2_pages.py
new file mode 100644
index 0000000..2264c241
--- /dev/null
+++ b/tools/perf/page_sets/speedometer2_pages.py
@@ -0,0 +1,130 @@
+# 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.
+
+"""Apple's Speedometer 2 performance benchmark pages
+"""
+import re
+
+from telemetry.value import list_of_scalar_values
+
+from page_sets import press_story
+
+_SPEEDOMETER_SUITE_NAME_BASE = '{0}-TodoMVC'
+_SPEEDOMETER_SUITES = [
+  'VanillaJS',
+  'Vanilla-ES2015',
+  'Vanilla-ES2015-Babel-Webpack',
+  'React',
+  'React-Redux',
+  'EmberJS',
+  'EmberJS-Debug',
+  'BackboneJS',
+  'AngularJS',
+  'Angular2-TypeScript',
+  'VueJS',
+  'jQuery',
+  'Preact',
+  'Inferno',
+  'Elm',
+  'Flight'
+]
+
+class Speedometer2Story(press_story.PressStory):
+  URL = 'file://InteractiveRunner.html'
+  NAME = 'Speedometer2'
+
+  def __init__(self, ps, should_filter_suites, filtered_suite_names=None,
+               enable_smoke_test_mode=False):
+    super(Speedometer2Story, self).__init__(ps)
+    self._should_filter_suites = should_filter_suites
+    self._filtered_suite_names = filtered_suite_names
+    self._enable_smoke_test_mode = enable_smoke_test_mode
+    self._enabled_suites = []
+
+  @staticmethod
+  def GetFullSuiteName(name):
+    return _SPEEDOMETER_SUITE_NAME_BASE.format(name)
+
+  @staticmethod
+  def GetSuites(suite_regex):
+    if not suite_regex:
+      return []
+    exp = re.compile(suite_regex)
+    return [name for name in _SPEEDOMETER_SUITES
+            if exp.search(Speedometer2Story.GetFullSuiteName(name))]
+
+  def ExecuteTest(self, action_runner):
+    action_runner.tab.WaitForDocumentReadyStateToBeComplete()
+    iterationCount = 10
+    # A single iteration on android takes ~75 seconds, the benchmark times out
+    # when running for 10 iterations.
+    if action_runner.tab.browser.platform.GetOSName() == 'android':
+      iterationCount = 3
+    # For a smoke test one iteration is sufficient
+    if self._enable_smoke_test_mode:
+      iterationCount = 1
+
+    if self._should_filter_suites:
+      action_runner.ExecuteJavaScript("""
+        Suites.forEach(function(suite) {
+          suite.disabled = {{ filtered_suites }}.indexOf(suite.name) < 0;
+        });
+      """, filtered_suites=self._filtered_suites)
+
+    self._enabled_suites = action_runner.EvaluateJavaScript("""
+      (function() {
+        var suitesNames = [];
+        Suites.forEach(function(s) {
+          if (!s.disabled)
+            suitesNames.push(s.name);
+        });
+        return suitesNames;
+       })();""")
+
+    action_runner.ExecuteJavaScript("""
+        // Store all the results in the benchmarkClient
+        var testDone = false;
+        var iterationCount = {{ count }};
+        var benchmarkClient = {};
+        var suiteValues = [];
+        benchmarkClient.didRunSuites = function(measuredValues) {
+          suiteValues.push(measuredValues);
+        };
+        benchmarkClient.didFinishLastIteration = function () {
+          testDone = true;
+        };
+        var runner = new BenchmarkRunner(Suites, benchmarkClient);
+        runner.runMultipleIterations(iterationCount);
+        """,
+        count=iterationCount)
+    action_runner.WaitForJavaScriptCondition('testDone', timeout=600)
+
+
+  def ParseTestResults(self, action_runner):
+    if not self._should_filter_suites:
+      self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
+          self, 'Total', 'ms',
+          action_runner.EvaluateJavaScript(
+              'suiteValues.map(each => each.total)'),
+          important=True))
+      self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
+          self, 'RunsPerMinute', 'score',
+          action_runner.EvaluateJavaScript(
+              'suiteValues.map(each => each.score)'),
+          important=True))
+
+    # Extract the timings for each suite
+    for suite_name in self._enabled_suites:
+      self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
+          self, suite_name, 'ms',
+          action_runner.EvaluateJavaScript("""
+              var suite_times = [];
+              for(var i = 0; i < iterationCount; i++) {
+                suite_times.push(
+                    suiteValues[i].tests[{{ key }}].total);
+              };
+              suite_times;
+              """,
+              key=suite_name), important=False))
+
diff --git a/tools/perf/page_sets/speedometer_pages.py b/tools/perf/page_sets/speedometer_pages.py
new file mode 100644
index 0000000..564d1bc
--- /dev/null
+++ b/tools/perf/page_sets/speedometer_pages.py
@@ -0,0 +1,79 @@
+# 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.
+from telemetry import story
+
+from telemetry.value import list_of_scalar_values
+
+from page_sets import press_story
+
+class SpeedometerStory(press_story.PressStory):
+  URL='http://browserbench.org/Speedometer/'
+
+  enabled_suites = [
+      'VanillaJS-TodoMVC',
+      'EmberJS-TodoMVC',
+      'BackboneJS-TodoMVC',
+      'jQuery-TodoMVC',
+      'AngularJS-TodoMVC',
+      'React-TodoMVC',
+      'FlightJS-TodoMVC'
+  ]
+
+  def ExecuteTest(self, action_runner):
+    action_runner.tab.WaitForDocumentReadyStateToBeComplete()
+    iterationCount = 10
+    # A single iteration on android takes ~75 seconds, the benchmark times out
+    # when running for 10 iterations.
+    if action_runner.tab.browser.platform.GetOSName() == 'android':
+      iterationCount = 3
+
+    action_runner.ExecuteJavaScript("""
+        // Store all the results in the benchmarkClient
+        benchmarkClient._measuredValues = []
+        benchmarkClient.didRunSuites = function(measuredValues) {
+          benchmarkClient._measuredValues.push(measuredValues);
+          benchmarkClient._timeValues.push(measuredValues.total);
+        };
+        benchmarkClient.iterationCount = {{ count }};
+        startTest();
+        """,
+        count=iterationCount)
+    action_runner.WaitForJavaScriptCondition(
+        'benchmarkClient._finishedTestCount == benchmarkClient.testsCount',
+        timeout=600)
+
+  def ParseTestResults(self, action_runner):
+    self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
+        self, 'Total', 'ms',
+        action_runner.EvaluateJavaScript('benchmarkClient._timeValues'),
+        important=True))
+    self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
+        self, 'RunsPerMinute', 'score',
+        action_runner.EvaluateJavaScript(
+            '[parseFloat(document.getElementById("result-number").innerText)];'
+        ),
+        important=True))
+
+    # Extract the timings for each suite
+    for suite_name in self.enabled_suites:
+      self.AddJavascriptMetricValue(list_of_scalar_values.ListOfScalarValues(
+          self, suite_name, 'ms',
+          action_runner.EvaluateJavaScript("""
+              var suite_times = [];
+              for(var i = 0; i < benchmarkClient.iterationCount; i++) {
+                suite_times.push(
+                    benchmarkClient._measuredValues[i].tests[{{ key }}].total);
+              };
+              suite_times;
+              """,
+              key=suite_name), important=False))
+
+
+class SpeedometerStorySet(story.StorySet):
+  def __init__(self):
+    super(SpeedometerStorySet, self).__init__(
+        archive_data_file='data/speedometer.json',
+        cloud_storage_bucket=story.PUBLIC_BUCKET)
+
+    self.AddStory(SpeedometerStory(self))
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index c6075b193..7803b0f 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -70,7 +70,7 @@
  <item id="data_reduction_proxy_pingback" hash_code="68561428" type="0" content_hash_code="78407792" os_list="linux,windows" file_path="components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc"/>
  <item id="data_reduction_proxy_secure_proxy_check" hash_code="131236802" type="0" content_hash_code="122297136" os_list="linux,windows" file_path="components/data_reduction_proxy/core/browser/secure_proxy_checker.cc"/>
  <item id="data_reduction_proxy_warmup" hash_code="8250451" type="0" content_hash_code="6321249" os_list="linux,windows" file_path="components/data_reduction_proxy/core/browser/warmup_url_fetcher.cc"/>
- <item id="desktop_ios_promotion" hash_code="13694792" type="0" content_hash_code="19776951" os_list="windows" file_path="chrome/browser/ui/desktop_ios_promotion/sms_service.cc"/>
+ <item id="desktop_ios_promotion" hash_code="13694792" type="0" deprecated="2018-11-04" content_hash_code="19776951" file_path=""/>
  <item id="device_geolocation_request" hash_code="77673751" type="0" deprecated="2017-10-20" content_hash_code="97181773" file_path=""/>
  <item id="device_management_service" hash_code="117782019" type="0" content_hash_code="127535409" os_list="linux,windows" file_path="components/policy/core/common/cloud/device_management_service.cc"/>
  <item id="devtools_free_data_source" hash_code="22774132" type="0" content_hash_code="35733000" os_list="linux,windows" file_path="chrome/browser/ui/webui/devtools_ui.cc"/>
diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
index 54a015c..c15283ae 100644
--- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
+++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java
@@ -797,6 +797,15 @@
     }
 
     /**
+     * As long as there are still animations which haven't ended, this will return false.
+     * @return True if all known animations have ended.
+     */
+    @VisibleForTesting
+    public boolean haveAnimationsEnded() {
+        return mAnimationsOverContent.isEmpty();
+    }
+
+    /**
      * Pauses/Unpauses VSync. When VSync is paused the compositor for this window will idle, and
      * requestAnimationFrame callbacks won't fire, etc.
      */
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index a19e6e9..fb958b18 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -35,6 +35,9 @@
     TYPE_BUTTON_ITEM,  // Shows a row of buttons.
     TYPE_SUBMENU,      // Presents a submenu within another menu.
     TYPE_ACTIONABLE_SUBMENU,  // A SUBMENU that is also a COMMAND.
+    TYPE_HIGHLIGHTED,  // Performs an action when selected, and has a different
+                       // colored background. When placed at the bottom, the
+                       // background matches the menu's rounded corners.
   };
 
   virtual ~MenuModel() {}
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index 5561c41..4aa19f6 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -119,6 +119,15 @@
   AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id);
 }
 
+void SimpleMenuModel::AddHighlightedItemWithStringIdAndIcon(
+    int command_id,
+    int string_id,
+    const gfx::ImageSkia& icon) {
+  Item item(command_id, TYPE_HIGHLIGHTED, l10n_util::GetStringUTF16(string_id));
+  item.icon = gfx::Image(icon);
+  AppendItem(std::move(item));
+}
+
 void SimpleMenuModel::AddSeparator(MenuSeparatorType separator_type) {
   if (items_.empty()) {
     if (separator_type == NORMAL_SEPARATOR) {
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index c1df3036..ac1e1a7 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -89,6 +89,9 @@
   void AddCheckItemWithStringId(int command_id, int string_id);
   void AddRadioItem(int command_id, const base::string16& label, int group_id);
   void AddRadioItemWithStringId(int command_id, int string_id, int group_id);
+  void AddHighlightedItemWithStringIdAndIcon(int command_id,
+                                             int string_id,
+                                             const gfx::ImageSkia& icon);
 
   // Adds a separator of the specified type to the model.
   // - Adding a separator after another separator is always invalid if they
diff --git a/ui/message_center/cocoa/popup_collection.mm b/ui/message_center/cocoa/popup_collection.mm
index b4df87a8..8bf2df4 100644
--- a/ui/message_center/cocoa/popup_collection.mm
+++ b/ui/message_center/cocoa/popup_collection.mm
@@ -72,6 +72,11 @@
     [popup_collection_ updateNotification:notification_id];
   }
 
+  void OnBlockingStateChanged(
+      message_center::NotificationBlocker* blocker) override {
+    [popup_collection_ layoutNewNotifications];
+  }
+
  private:
   message_center::MessageCenter* message_center_;  // Weak, global.
 
diff --git a/ui/message_center/views/message_popup_collection.cc b/ui/message_center/views/message_popup_collection.cc
index a433782..e6d93b9 100644
--- a/ui/message_center/views/message_popup_collection.cc
+++ b/ui/message_center/views/message_popup_collection.cc
@@ -170,6 +170,11 @@
   Update();
 }
 
+void MessagePopupCollection::OnBlockingStateChanged(
+    NotificationBlocker* blocker) {
+  Update();
+}
+
 void MessagePopupCollection::AnimationEnded(const gfx::Animation* animation) {
   Update();
 }
diff --git a/ui/message_center/views/message_popup_collection.h b/ui/message_center/views/message_popup_collection.h
index 24112fc..c9daa1a 100644
--- a/ui/message_center/views/message_popup_collection.h
+++ b/ui/message_center/views/message_popup_collection.h
@@ -50,6 +50,7 @@
                              bool by_user) override;
   void OnNotificationUpdated(const std::string& notification_id) override;
   void OnCenterVisibilityChanged(Visibility visibility) override;
+  void OnBlockingStateChanged(NotificationBlocker* blocker) override;
 
   // AnimationDelegate:
   void AnimationEnded(const gfx::Animation* animation) override;
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc
index bbc913e..71a22e7 100644
--- a/ui/native_theme/common_theme.cc
+++ b/ui/native_theme/common_theme.cc
@@ -119,6 +119,12 @@
       return kDisabledTextColor;
     case NativeTheme::kColorId_MenuItemMinorTextColor:
       return SkColorSetA(SK_ColorBLACK, 0x89);
+    case NativeTheme::kColorId_HighlightedMenuItemBackgroundColor:
+      return gfx::kGoogleGrey050;
+    case NativeTheme::kColorId_HighlightedMenuItemForegroundColor:
+      return gfx::kGoogleGrey900;
+    case NativeTheme::kColorId_FocusedHighlightedMenuItemBackgroundColor:
+      return gfx::kGoogleGrey200;
 
     // Label
     case NativeTheme::kColorId_LabelEnabledColor:
diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h
index 3cda7db..2796e42 100644
--- a/ui/native_theme/native_theme.h
+++ b/ui/native_theme/native_theme.h
@@ -316,6 +316,9 @@
     kColorId_MenuSeparatorColor,
     kColorId_MenuBackgroundColor,
     kColorId_MenuBorderColor,
+    kColorId_HighlightedMenuItemBackgroundColor,
+    kColorId_HighlightedMenuItemForegroundColor,
+    kColorId_FocusedHighlightedMenuItemBackgroundColor,
     // Label
     kColorId_LabelEnabledColor,
     kColorId_LabelDisabledColor,
diff --git a/ui/native_theme/native_theme_dark_aura.cc b/ui/native_theme/native_theme_dark_aura.cc
index 04ab734..712fc0d 100644
--- a/ui/native_theme/native_theme_dark_aura.cc
+++ b/ui/native_theme/native_theme_dark_aura.cc
@@ -92,6 +92,9 @@
     case kColorId_MenuSeparatorColor:
     case kColorId_MenuBackgroundColor:
     case kColorId_MenuBorderColor:
+    case kColorId_HighlightedMenuItemBackgroundColor:
+    case kColorId_HighlightedMenuItemForegroundColor:
+    case kColorId_FocusedHighlightedMenuItemBackgroundColor:
     case kColorId_LinkDisabled:
     case kColorId_TabBottomBorder:
     case kColorId_TabTitleColorActive:
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 713232b..a7f13ef 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -979,11 +979,11 @@
     "controls/image_view_unittest.cc",
     "controls/label_unittest.cc",
     "controls/menu/menu_controller_unittest.cc",
-    "controls/menu/menu_footnote_unittest.cc",
     "controls/menu/menu_item_view_unittest.cc",
     "controls/menu/menu_model_adapter_unittest.cc",
     "controls/menu/menu_runner_cocoa_unittest.mm",
     "controls/menu/menu_runner_unittest.cc",
+    "controls/menu/submenu_view_unittest.cc",
     "controls/native/native_view_host_mac_unittest.mm",
     "controls/native/native_view_host_test_base.cc",
     "controls/native/native_view_host_test_base.h",
diff --git a/ui/views/controls/menu/menu_delegate.cc b/ui/views/controls/menu/menu_delegate.cc
index 308ffe3..56c3c51c 100644
--- a/ui/views/controls/menu/menu_delegate.cc
+++ b/ui/views/controls/menu/menu_delegate.cc
@@ -152,8 +152,4 @@
   return true;
 }
 
-View* MenuDelegate::CreateFootnoteView() {
-  return nullptr;
-}
-
 }  // namespace views
diff --git a/ui/views/controls/menu/menu_delegate.h b/ui/views/controls/menu/menu_delegate.h
index 7544162..70660518 100644
--- a/ui/views/controls/menu/menu_delegate.h
+++ b/ui/views/controls/menu/menu_delegate.h
@@ -35,7 +35,6 @@
 
 class MenuButton;
 class MenuItemView;
-class View;
 
 // MenuDelegate --------------------------------------------------------------
 
@@ -234,11 +233,6 @@
   // Returns true if the labels should reserve additional spacing for e.g.
   // submenu indicators at the end of the line.
   virtual bool ShouldReserveSpaceForSubmenuIndicator() const;
-
-  // Override this function to display a footnote view below the menu-items in a
-  // top-level menu. Overrides may construct the view; this will only be called
-  // once per menu.
-  virtual View* CreateFootnoteView();
 };
 
 }  // namespace views
diff --git a/ui/views/controls/menu/menu_footnote_unittest.cc b/ui/views/controls/menu/menu_footnote_unittest.cc
deleted file mode 100644
index 0472bece..0000000
--- a/ui/views/controls/menu/menu_footnote_unittest.cc
+++ /dev/null
@@ -1,118 +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 "ui/views/controls/menu/submenu_view.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/views/controls/menu/menu_controller.h"
-#include "ui/views/controls/menu/menu_delegate.h"
-#include "ui/views/controls/menu/menu_item_view.h"
-#include "ui/views/test/menu_test_utils.h"
-#include "ui/views/test/views_test_base.h"
-#include "ui/views/widget/widget.h"
-
-namespace views {
-namespace test {
-
-namespace {
-
-// MenuDelegate mock to control the CreateFootnoteView() method.
-class MockMenuDelegate : public MenuDelegate {
- public:
-  MockMenuDelegate() = default;
-  ~MockMenuDelegate() override = default;
-
-  void set_create_footnote_view_value(View* view) {
-    create_footnote_view_value_ = view;
-  }
-  int create_footnote_view_count() { return create_footnote_view_count_; }
-
-  // MenuDelegate:
-  View* CreateFootnoteView() override {
-    create_footnote_view_count_++;
-    return create_footnote_view_value_;
-  }
-
- private:
-  // The return value for the next CreateFootnoteView call.
-  View* create_footnote_view_value_ = nullptr;
-
-  // The number of times CreateFootnoteView was called.
-  int create_footnote_view_count_ = 0;
-
-  DISALLOW_COPY_AND_ASSIGN(MockMenuDelegate);
-};
-
-}  // namespace
-
-class MenuFootnoteTest : public ViewsTestBase {
- public:
-  MenuFootnoteTest();
-  ~MenuFootnoteTest() override;
-
-  // ViewsTestBase:
-  void SetUp() override {
-    ViewsTestBase::SetUp();
-
-    menu_delegate_ = std::make_unique<MockMenuDelegate>();
-    menu_item_view_ = new MenuItemView(menu_delegate_.get());
-    item_with_submenu_ = menu_item_view_->AppendSubMenu(0, base::string16());
-
-    owner_ = std::make_unique<Widget>();
-    Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
-    params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
-    owner_->Init(params);
-    owner_->Show();
-
-    menu_runner_ = std::make_unique<MenuRunner>(menu_item_view_, 0);
-  }
-
-  void TearDown() override {
-    if (owner_)
-      owner_->CloseNow();
-    ViewsTestBase::TearDown();
-  }
-
- protected:
-  // Owned by menu_runner_.
-  MenuItemView* menu_item_view_ = nullptr;
-
-  // An item with a submenu, in menu_item_view_.
-  MenuItemView* item_with_submenu_ = nullptr;
-
-  std::unique_ptr<MockMenuDelegate> menu_delegate_;
-  std::unique_ptr<MenuRunner> menu_runner_;
-  std::unique_ptr<Widget> owner_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MenuFootnoteTest);
-};
-
-MenuFootnoteTest::MenuFootnoteTest() = default;
-
-MenuFootnoteTest::~MenuFootnoteTest() = default;
-
-TEST_F(MenuFootnoteTest, TopLevelContainerShowsFootnote) {
-  View* footnote = new View();
-  menu_delegate_->set_create_footnote_view_value(footnote);
-  menu_runner_->RunMenuAt(owner_.get(), nullptr, gfx::Rect(),
-                          MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_NONE);
-  EXPECT_EQ(1, menu_delegate_->create_footnote_view_count());
-  EXPECT_TRUE(menu_item_view_->GetSubmenu()->Contains(footnote));
-}
-
-TEST_F(MenuFootnoteTest, SubmenuDoesNotShowFootnote) {
-  View* footnote = new View();
-  menu_delegate_->set_create_footnote_view_value(footnote);
-  menu_runner_->RunMenuAt(owner_.get(), nullptr, gfx::Rect(),
-                          MENU_ANCHOR_TOPLEFT, ui::MENU_SOURCE_NONE);
-  // Trigger the code that would create a footnote, then check that the footnote
-  // was not created.
-  item_with_submenu_->GetSubmenu()->GetScrollViewContainer();
-  EXPECT_FALSE(item_with_submenu_->GetSubmenu()->Contains(footnote));
-  EXPECT_EQ(1, menu_delegate_->create_footnote_view_count());
-}
-
-}  // namespace test
-}  // namespace views
diff --git a/ui/views/controls/menu/menu_item_view.cc b/ui/views/controls/menu/menu_item_view.cc
index 7b17d26..c26f578 100644
--- a/ui/views/controls/menu/menu_item_view.cc
+++ b/ui/views/controls/menu/menu_item_view.cc
@@ -196,6 +196,7 @@
     case NORMAL:
     case SEPARATOR:
     case EMPTY:
+    case HIGHLIGHTED:
       // No additional accessibility states currently for these menu states.
       break;
   }
@@ -692,6 +693,12 @@
   SchedulePaint();
 }
 
+void MenuItemView::SetCornerRadius(int radius) {
+  DCHECK_EQ(GetType(), HIGHLIGHTED);
+  corner_radius_ = radius;
+  invalidate_dimensions();  // Triggers preferred size recalculation.
+}
+
 MenuItemView::MenuItemView(MenuItemView* parent,
                            int command,
                            MenuItemView::Type type)
@@ -781,6 +788,7 @@
   submenu_arrow_image_view_ = nullptr;
   vertical_separator_ = nullptr;
   show_mnemonics_ = false;
+  corner_radius_ = 0;
   // Assign our ID, this allows SubmenuItemView to find MenuItemViews.
   set_id(kMenuItemViewID);
   has_icons_ = false;
@@ -925,26 +933,7 @@
   // Render the background. As MenuScrollViewContainer draws the background, we
   // only need the background when we want it to look different, as when we're
   // selected.
-  ui::NativeTheme* native_theme = GetNativeTheme();
-  if (render_selection) {
-    gfx::Rect item_bounds(0, 0, width(), height());
-    if (type_ == ACTIONABLE_SUBMENU) {
-      if (submenu_area_of_actionable_submenu_selected_) {
-        item_bounds = GetSubmenuAreaOfActionableSubmenu();
-      } else {
-        item_bounds = gfx::Rect(gfx::Size(
-            width() - MenuConfig::instance().actionable_submenu_width - 1,
-            height()));
-      }
-    }
-    AdjustBoundsForRTLUI(&item_bounds);
-
-    native_theme->Paint(canvas->sk_canvas(),
-                        ui::NativeTheme::kMenuItemBackground,
-                        ui::NativeTheme::kHovered,
-                        item_bounds,
-                        ui::NativeTheme::ExtraParams());
-  }
+  PaintBackground(canvas, mode, render_selection);
 
   const int top_margin = GetTopMargin();
   const int bottom_margin = GetBottomMargin();
@@ -999,6 +988,48 @@
     submenu_arrow_image_view_->SetImage(GetSubmenuArrowImage(icon_color));
 }
 
+void MenuItemView::PaintBackground(gfx::Canvas* canvas,
+                                   PaintButtonMode mode,
+                                   bool render_selection) {
+  if (GetType() == HIGHLIGHTED) {
+    // Highligted items always have a different-colored background, and ignore
+    // system theme.
+    ui::NativeTheme::ColorId color_id =
+        render_selection
+            ? ui::NativeTheme::
+                  kColorId_FocusedHighlightedMenuItemBackgroundColor
+            : ui::NativeTheme::kColorId_HighlightedMenuItemBackgroundColor;
+    cc::PaintFlags flags;
+    flags.setAntiAlias(true);
+    flags.setStyle(cc::PaintFlags::kFill_Style);
+    flags.setColor(GetNativeTheme()->GetSystemColor(color_id));
+    // Draw a rounded rect that spills outside of the clipping area, so that the
+    // rounded corners only show in the bottom 2 corners. Note that
+    // |corner_radius_| should only be set when the highlighted item is at the
+    // end of the menu.
+    gfx::RectF spilling_rect(GetLocalBounds());
+    spilling_rect.set_y(spilling_rect.y() - corner_radius_);
+    spilling_rect.set_height(spilling_rect.height() + corner_radius_);
+    canvas->DrawRoundRect(spilling_rect, corner_radius_, flags);
+  } else if (render_selection) {
+    gfx::Rect item_bounds = GetLocalBounds();
+    if (type_ == ACTIONABLE_SUBMENU) {
+      if (submenu_area_of_actionable_submenu_selected_) {
+        item_bounds = GetSubmenuAreaOfActionableSubmenu();
+      } else {
+        item_bounds.set_width(item_bounds.width() -
+                              MenuConfig::instance().actionable_submenu_width -
+                              1);
+      }
+    }
+    AdjustBoundsForRTLUI(&item_bounds);
+
+    GetNativeTheme()->Paint(
+        canvas->sk_canvas(), ui::NativeTheme::kMenuItemBackground,
+        ui::NativeTheme::kHovered, item_bounds, ui::NativeTheme::ExtraParams());
+  }
+}
+
 void MenuItemView::PaintMinorIconAndText(
     gfx::Canvas* canvas,
     const MenuDelegate::LabelStyle& style) {
@@ -1059,6 +1090,9 @@
   if (GetMenuController() && GetMenuController()->use_touchable_layout())
     color_id = ui::NativeTheme::kColorId_TouchableMenuItemLabelColor;
 
+  if (GetType() == HIGHLIGHTED)
+    color_id = ui::NativeTheme::kColorId_HighlightedMenuItemForegroundColor;
+
   return GetNativeTheme()->GetSystemColor(color_id);
 }
 
@@ -1074,23 +1108,28 @@
 }
 
 int MenuItemView::GetTopMargin() const {
-  if (top_margin_ >= 0)
-    return top_margin_;
-
-  const MenuItemView* root = GetRootMenuItem();
-  return root && root->has_icons_
-             ? MenuConfig::instance().item_top_margin
-             : MenuConfig::instance().item_no_icon_top_margin;
+  int margin = top_margin_;
+  if (margin < 0) {
+    const MenuItemView* root = GetRootMenuItem();
+    margin = root && root->has_icons_
+                 ? MenuConfig::instance().item_top_margin
+                 : MenuConfig::instance().item_no_icon_top_margin;
+  }
+  return margin + corner_radius_ / 2;
 }
 
 int MenuItemView::GetBottomMargin() const {
-  if (bottom_margin_ >= 0)
-    return bottom_margin_;
-
-  const MenuItemView* root = GetRootMenuItem();
-  return root && root->has_icons_
-             ? MenuConfig::instance().item_bottom_margin
-             : MenuConfig::instance().item_no_icon_bottom_margin;
+  int margin = bottom_margin_;
+  if (margin < 0) {
+    const MenuItemView* root = GetRootMenuItem();
+    margin = root && root->has_icons_
+                 ? MenuConfig::instance().item_bottom_margin
+                 : MenuConfig::instance().item_no_icon_bottom_margin;
+  }
+  // Add half of |corner_radius_| in both GetTopMargin() and GetBottomMargin(),
+  // so that they add up to exactly |corner_radius_|. When |corner_radius_| is
+  // odd, we need to add 1 here to avoid the height being off by 1.
+  return margin + corner_radius_ / 2 + (corner_radius_ % 2);
 }
 
 gfx::Size MenuItemView::GetChildPreferredSize() const {
diff --git a/ui/views/controls/menu/menu_item_view.h b/ui/views/controls/menu/menu_item_view.h
index 160a0069..f0e80ee 100644
--- a/ui/views/controls/menu/menu_item_view.h
+++ b/ui/views/controls/menu/menu_item_view.h
@@ -91,6 +91,9 @@
     CHECKBOX,            // Can be selected/checked to toggle a boolean state.
     RADIO,               // Can be selected/checked among a group of choices.
     SEPARATOR,           // Shows a horizontal line separator.
+    HIGHLIGHTED,         // Performs an action when selected, and has a
+                         // different colored background that merges with the
+                         // menu's rounded corners when placed at the bottom.
     EMPTY,  // EMPTY is a special type for empty menus that is only used
             // internally.
   };
@@ -359,6 +362,11 @@
   // there's no way to unset it for this MenuItemView!
   void SetForcedVisualSelection(bool selected);
 
+  // For items of type HIGHLIGHTED only: sets the radius of the item's
+  // background. This makes the menu item's background fit its container's
+  // border radius, if they are both the same value.
+  void SetCornerRadius(int radius);
+
  protected:
   // Creates a MenuItemView. This is used by the various AddXXX methods.
   MenuItemView(MenuItemView* parent, int command, Type type);
@@ -424,6 +432,12 @@
   // are not rendered.
   void PaintButton(gfx::Canvas* canvas, PaintButtonMode mode);
 
+  // Helper function for PaintButton(), draws the background for the button if
+  // appropriate.
+  void PaintBackground(gfx::Canvas* canvas,
+                       PaintButtonMode mode,
+                       bool render_selection);
+
   // Paints the right-side icon and text.
   void PaintMinorIconAndText(gfx::Canvas* canvas,
                              const MenuDelegate::LabelStyle& style);
@@ -568,6 +582,9 @@
   int top_margin_;
   int bottom_margin_;
 
+  // Corner radius in pixels, for HIGHLIGHTED items placed at the end of a menu.
+  int corner_radius_;
+
   // Horizontal icon margins in pixels, which can differ between MenuItems.
   // These values will be set in the layout process.
   mutable int left_icon_margin_;
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index cbccee1..08132c52 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -83,6 +83,9 @@
     case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
       type = MenuItemView::ACTIONABLE_SUBMENU;
       break;
+    case ui::MenuModel::TYPE_HIGHLIGHTED:
+      type = MenuItemView::HIGHLIGHTED;
+      break;
   }
 
   if (*type == MenuItemView::SEPARATOR) {
diff --git a/ui/views/controls/menu/menu_model_adapter_unittest.cc b/ui/views/controls/menu/menu_model_adapter_unittest.cc
index 00da8e3..392f6c3e 100644
--- a/ui/views/controls/menu/menu_model_adapter_unittest.cc
+++ b/ui/views/controls/menu/menu_model_adapter_unittest.cc
@@ -241,6 +241,9 @@
       case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
         EXPECT_EQ(views::MenuItemView::ACTIONABLE_SUBMENU, item->GetType());
         break;
+      case ui::MenuModel::TYPE_HIGHLIGHTED:
+        EXPECT_EQ(views::MenuItemView::HIGHLIGHTED, item->GetType());
+        break;
     }
 
     // Check enabled state.
@@ -312,6 +315,9 @@
       case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
         EXPECT_EQ(views::MenuItemView::ACTIONABLE_SUBMENU, item->GetType());
         break;
+      case ui::MenuModel::TYPE_HIGHLIGHTED:
+        EXPECT_EQ(views::MenuItemView::HIGHLIGHTED, item->GetType());
+        break;
     }
 
     // Check enabled state.
diff --git a/ui/views/controls/menu/menu_scroll_view_container.cc b/ui/views/controls/menu/menu_scroll_view_container.cc
index 2427254..0ad986a 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.cc
+++ b/ui/views/controls/menu/menu_scroll_view_container.cc
@@ -12,7 +12,6 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/views/border.h"
 #include "ui/views/bubble/bubble_border.h"
-#include "ui/views/bubble/footnote_container_view.h"
 #include "ui/views/controls/menu/menu_config.h"
 #include "ui/views/controls/menu/menu_controller.h"
 #include "ui/views/controls/menu/menu_item_view.h"
@@ -197,25 +196,19 @@
   bubble_border_->set_arrow_offset(offset);
 }
 
-void MenuScrollViewContainer::SetFootnoteView(View* view) {
-  DCHECK(view);
-  DCHECK(!footnote_container_);
-  footnote_container_ = new FootnoteContainerView(gfx::Insets(), view, 0);
-  content_view_->AddChildView(footnote_container_);
-  // Recreate the border. This updates the corner-radius for
-  // |footnote_container_| and margins, so |footnote_container_| can draw the
-  // bottom of the menu.
-  CreateBorder();
-}
-
-bool MenuScrollViewContainer::HasVisibleFootnote() {
-  return footnote_container_ && footnote_container_->visible();
+MenuItemView* MenuScrollViewContainer::GetFootnote() const {
+  MenuItemView* footnote = content_view_->GetLastItem();
+  if (!footnote || footnote->GetType() != MenuItemView::HIGHLIGHTED)
+    return nullptr;
+  return footnote;
 }
 
 gfx::Size MenuScrollViewContainer::CalculatePreferredSize() const {
   gfx::Size prefsize = scroll_view_->GetContents()->GetPreferredSize();
   gfx::Insets insets = GetInsets();
   prefsize.Enlarge(insets.width(), insets.height());
+  if (GetFootnote() && bubble_border_)
+    prefsize.Enlarge(0, bubble_border_->GetBorderCornerRadius());
   return prefsize;
 }
 
@@ -225,16 +218,19 @@
   int y = insets.top();
   int width = View::width() - insets.width();
   int content_height = height() - insets.height();
+  MenuItemView* footnote = GetFootnote();
   if (!scroll_up_button_->visible()) {
+    if (footnote && bubble_border_)
+      footnote->SetCornerRadius(bubble_border_->GetBorderCornerRadius());
     scroll_view_->SetBounds(x, y, width, content_height);
     scroll_view_->Layout();
-    if (footnote_container_ && bubble_border_) {
-      int radius = bubble_border_->GetBorderCornerRadius();
-      footnote_container_->SetCornerRadius(radius);
-    }
     return;
   }
 
+  // Don't round the footnote when the scroll button is visible.
+  if (footnote)
+    footnote->SetCornerRadius(0);
+
   gfx::Size pref = scroll_up_button_->GetPreferredSize();
   scroll_up_button_->SetBounds(x, y, width, pref.height());
   content_height -= pref.height();
@@ -246,10 +242,6 @@
                                  width, pref.height());
   content_height -= pref.height();
 
-  // Don't round the footnote when the scroll button is visible.
-  if (footnote_container_)
-    footnote_container_->SetCornerRadius(0);
-
   scroll_view_->SetBounds(x, scroll_view_y, width, content_height);
   scroll_view_->Layout();
 }
@@ -318,7 +310,7 @@
   const int horizontal_inset =
       menu_config.menu_horizontal_border_size + padding;
 
-  int bottom_inset = HasVisibleFootnote() ? 0 : vertical_inset;
+  int bottom_inset = GetFootnote() ? 0 : vertical_inset;
 
   if (use_outer_border) {
     SkColor color = GetNativeTheme()
@@ -346,7 +338,7 @@
     bubble_border_->set_md_shadow_elevation(
         menu_config.touchable_menu_shadow_elevation);
     gfx::Insets insets(menu_config.vertical_touchable_menu_item_padding, 0);
-    if (HasVisibleFootnote())
+    if (GetFootnote())
       insets.Set(menu_config.vertical_touchable_menu_item_padding, 0, 0, 0);
     scroll_view_->GetContents()->SetBorder(CreateEmptyBorder(insets));
   }
diff --git a/ui/views/controls/menu/menu_scroll_view_container.h b/ui/views/controls/menu/menu_scroll_view_container.h
index ea02becc..290fe81 100644
--- a/ui/views/controls/menu/menu_scroll_view_container.h
+++ b/ui/views/controls/menu/menu_scroll_view_container.h
@@ -12,7 +12,7 @@
 
 namespace views {
 
-class FootnoteContainerView;
+class MenuItemView;
 class SubmenuView;
 
 // MenuScrollViewContainer contains the SubmenuView (through a MenuScrollView)
@@ -57,7 +57,8 @@
 
   BubbleBorder::Arrow BubbleBorderTypeFromAnchor(MenuAnchorPosition anchor);
 
-  bool HasVisibleFootnote();
+  // Returns the last item in the menu if it is of type HIGHLIGHTED.
+  MenuItemView* GetFootnote() const;
 
   class MenuScrollView;
 
@@ -77,9 +78,6 @@
   // Weak reference to the currently set border.
   BubbleBorder* bubble_border_ = nullptr;
 
-  // A view to contain the footnote view, if it exists.
-  FootnoteContainerView* footnote_container_ = nullptr;
-
   DISALLOW_COPY_AND_ASSIGN(MenuScrollViewContainer);
 };
 
diff --git a/ui/views/controls/menu/submenu_view.cc b/ui/views/controls/menu/submenu_view.cc
index f88f565..04c38f5c 100644
--- a/ui/views/controls/menu/submenu_view.cc
+++ b/ui/views/controls/menu/submenu_view.cc
@@ -469,19 +469,20 @@
 MenuScrollViewContainer* SubmenuView::GetScrollViewContainer() {
   if (!scroll_view_container_) {
     scroll_view_container_ = new MenuScrollViewContainer(this);
-    if (GetMenuItem()->GetParentMenuItem() == nullptr) {
-      // Top-level menu, this may have a footnote. Submenus can't have a
-      // footnote, because they share the |MenuDelegate| with their parent.
-      View* footnote_view = GetMenuItem()->GetDelegate()->CreateFootnoteView();
-      if (footnote_view)
-        scroll_view_container_->SetFootnoteView(footnote_view);
-    }
     // Otherwise MenuHost would delete us.
     scroll_view_container_->set_owned_by_client();
   }
   return scroll_view_container_;
 }
 
+MenuItemView* SubmenuView::GetLastItem() {
+  for (int i = child_count() - 1; i >= 0; i--) {
+    if (child_at(i)->id() == MenuItemView::kMenuItemViewID)
+      return static_cast<MenuItemView*>(child_at(i));
+  }
+  return nullptr;
+}
+
 void SubmenuView::MenuHostDestroyed() {
   host_ = NULL;
   MenuController* controller = GetMenuItem()->GetMenuController();
diff --git a/ui/views/controls/menu/submenu_view.h b/ui/views/controls/menu/submenu_view.h
index 43747900..3705657 100644
--- a/ui/views/controls/menu/submenu_view.h
+++ b/ui/views/controls/menu/submenu_view.h
@@ -147,6 +147,9 @@
   // Returns the container for the SubmenuView.
   MenuScrollViewContainer* GetScrollViewContainer();
 
+  // Returns the last MenuItemView in this submenu.
+  MenuItemView* GetLastItem();
+
   // Invoked if the menu is prematurely destroyed. This can happen if the window
   // closes while the menu is shown. If invoked the SubmenuView must drop all
   // references to the MenuHost as the MenuHost is about to be deleted.
diff --git a/ui/views/controls/menu/submenu_view_unittest.cc b/ui/views/controls/menu/submenu_view_unittest.cc
new file mode 100644
index 0000000..9b24587
--- /dev/null
+++ b/ui/views/controls/menu/submenu_view_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/controls/menu/submenu_view.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/menu/menu_item_view.h"
+#include "ui/views/controls/menu/menu_runner.h"
+#include "ui/views/controls/menu/submenu_view.h"
+
+namespace views {
+
+TEST(SubmenuViewTest, GetLastItem) {
+  MenuItemView* parent = new MenuItemView(nullptr);
+  MenuRunner menu_runner(parent, 0);
+
+  SubmenuView* submenu = parent->CreateSubmenu();
+  EXPECT_EQ(nullptr, submenu->GetLastItem());
+
+  submenu->AddChildView(new View());
+  EXPECT_EQ(nullptr, submenu->GetLastItem());
+
+  MenuItemView* first = new MenuItemView(nullptr);
+  submenu->AddChildView(first);
+  EXPECT_EQ(first, submenu->GetLastItem());
+
+  submenu->AddChildView(new View());
+  EXPECT_EQ(first, submenu->GetLastItem());
+
+  MenuItemView* second = new MenuItemView(nullptr);
+  submenu->AddChildView(second);
+  EXPECT_EQ(second, submenu->GetLastItem());
+}
+
+}  // namespace views