diff --git a/DEPS b/DEPS
index 280d92e6..4ef66bc 100644
--- a/DEPS
+++ b/DEPS
@@ -109,7 +109,7 @@
   # 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': 'c0b96b43766ee4efd9f75247763fd936be76dff8',
+  'v8_revision': '686dfc3f13a5fe766bfc25f2a84a8c64d45aa4e7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -117,7 +117,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'e8dd07969872491eeb18cedae67ec892433aa5ca',
+  'angle_revision': 'c0b82333ab279be7efc4752fd24f9c87b00310b5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -165,7 +165,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '920acc5657065dbcce54ce3093368ce6db5aa66b',
+  'catapult_revision': '7453eba4feb2d90136a9415a6670ad41b866c93f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -595,7 +595,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '10f3ee914d209c26f4f0e38c96b44853eb13ebd1',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8241a7599974ed98de3e87c521b3a85047f614d3',
       'condition': 'checkout_linux',
   },
 
@@ -954,7 +954,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'f7f0def7921b6c6f23ff37e42ce2042f8363c3cc',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '40a2e1ea24495c6df976f194890e0843d52ada68',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1106,7 +1106,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '6d2f3f4cb8bac1f7c4a945c73d07a33df74f22f9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b5c6dd4349074a1ce98541a7959734772d833073',
+    Var('webrtc_git') + '/src.git' + '@' + 'cbcbc225687faf8226995c2823380adf06bd1afb',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1137,7 +1137,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c0e1056a6c8eb6f2cba9bcaa92c64e9c0ebcfaa7',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@bea5a39cb8956b75ebf37087530c71e3e6fdd6e3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 8b139b5..c818caf0 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -114,6 +114,9 @@
     'arc_auth': {
       'filepath': 'chrome/browser/chromeos/arc/arc_auth'
     },
+    'arc_common': {
+      'filepath': 'components/arc/common/',
+    },
     'arc_fileapi': {
       'filepath': 'chrome/browser/chromeos/arc/fileapi'
     },
@@ -1791,6 +1794,7 @@
             'yusukes+watch@chromium.org',
             'arc-reviews+chromium@google.com'],
     'arc_auth': ['khmel+watch@chromium.org'],
+    'arc_common': ['hashimoto+watch@chromium.org'],
     'arc_fileapi': ['nya+watch@chromium.org'],
     'arc_kiosk': ['poromov+watch@chromium.org'],
     'arc_net': ['abhishekbh@chromium.org',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 7ce5e92..90dd255 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -34,6 +34,7 @@
     "accelerators/accelerator_controller.h",
     "accessibility/accessibility_controller.h",
     "accessibility/accessibility_delegate.h",
+    "accessibility/ax_host_service.h",
     "accessibility/focus_ring_controller.h",
     "app_list/app_list_controller_impl.h",
     "detachable_base/detachable_base_handler.h",
@@ -131,6 +132,7 @@
     "accessibility/accessibility_observer.h",
     "accessibility/accessibility_panel_layout_manager.cc",
     "accessibility/accessibility_panel_layout_manager.h",
+    "accessibility/ax_host_service.cc",
     "accessibility/default_accessibility_delegate.cc",
     "accessibility/default_accessibility_delegate.h",
     "accessibility/focus_ring_controller.cc",
@@ -1687,6 +1689,7 @@
     "accessibility/accessibility_focus_ring_group_unittest.cc",
     "accessibility/accessibility_highlight_controller_unittest.cc",
     "accessibility/accessibility_panel_layout_manager_unittest.cc",
+    "accessibility/ax_host_service_unittest.cc",
     "accessibility/key_accessibility_enabler_unittest.cc",
     "accessibility/touch_accessibility_enabler_unittest.cc",
     "accessibility/touch_exploration_controller_unittest.cc",
diff --git a/ash/accessibility/accessibility_delegate.h b/ash/accessibility/accessibility_delegate.h
index c59d0e6..0a33e6d3 100644
--- a/ash/accessibility/accessibility_delegate.h
+++ b/ash/accessibility/accessibility_delegate.h
@@ -5,9 +5,15 @@
 #ifndef ASH_ACCESSIBILITY_ACCESSIBILITY_DELEGATE_H_
 #define ASH_ACCESSIBILITY_ACCESSIBILITY_DELEGATE_H_
 
+#include <vector>
+
 #include "ash/ash_export.h"
-#include "base/time/time.h"
-#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_tree_id.h"
+#include "ui/accessibility/ax_tree_update.h"
+
+namespace ui {
+struct AXEvent;
+}
 
 namespace ash {
 
@@ -36,6 +42,14 @@
   // is not saved, return a negative value.
   virtual double GetSavedScreenMagnifierScale() = 0;
 
+  // Automation API support for remote mojo apps.
+  // TODO(jamescook): Convert to mojo API.
+  virtual void DispatchAccessibilityEvent(
+      const ui::AXTreeID& tree_id,
+      const std::vector<ui::AXTreeUpdate>& updates,
+      const ui::AXEvent& event) = 0;
+  virtual void DispatchTreeDestroyedEvent(const ui::AXTreeID& tree_id) = 0;
+
   // NOTE: Prefer adding methods to AccessibilityController, see class comment.
 };
 
diff --git a/chrome/browser/chromeos/accessibility/ax_host_service.cc b/ash/accessibility/ax_host_service.cc
similarity index 70%
rename from chrome/browser/chromeos/accessibility/ax_host_service.cc
rename to ash/accessibility/ax_host_service.cc
index 6b128237..00ac720c 100644
--- a/chrome/browser/chromeos/accessibility/ax_host_service.cc
+++ b/ash/accessibility/ax_host_service.cc
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/accessibility/ax_host_service.h"
+#include "ash/accessibility/ax_host_service.h"
 
 #include "ash/accessibility/accessibility_controller.h"
+#include "ash/accessibility/accessibility_delegate.h"
 #include "ash/shell.h"
 #include "base/bind.h"
-#include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
-#include "chrome/common/extensions/chrome_extension_messages.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/service_context.h"
 #include "ui/accessibility/ax_event.h"
-#include "ui/aura/env.h"
 #include "ui/views/mus/ax_remote_host.h"
 
+namespace ash {
+
 AXHostService* AXHostService::instance_ = nullptr;
 
 bool AXHostService::automation_enabled_ = false;
@@ -23,12 +23,8 @@
   // AX tree ID is automatically assigned.
   DCHECK(!tree_id().empty());
 
-  // ash::Shell may not exist in tests.
-  if (ash::Shell::HasInstance()) {
-    // TODO(jamescook): Eliminate this when tree ID assignment is handed in ash.
-    ash::Shell::Get()->accessibility_controller()->set_remote_ax_tree_id(
-        tree_id());
-  }
+  // TODO(jamescook): Eliminate this when multiple remote trees are supported.
+  Shell::Get()->accessibility_controller()->set_remote_ax_tree_id(tree_id());
 
   DCHECK(!instance_);
   instance_ = this;
@@ -71,16 +67,12 @@
     const std::vector<ui::AXTreeUpdate>& updates,
     const ui::AXEvent& event) {
   CHECK_EQ(tree_id, this->tree_id());
-  ExtensionMsg_AccessibilityEventBundleParams event_bundle;
-  event_bundle.tree_id = tree_id;
-  for (const ui::AXTreeUpdate& update : updates)
-    event_bundle.updates.push_back(update);
-  event_bundle.events.push_back(event);
-  event_bundle.mouse_location = aura::Env::GetInstance()->last_mouse_location();
-
-  // Forward the tree updates and the event to the accessibility extension.
-  extensions::AutomationEventRouter::GetInstance()->DispatchAccessibilityEvents(
-      event_bundle);
+  // Use an in-process delegate back to Chrome for efficiency in classic ash.
+  // For mash we'll need to pass around a mojo interface pointer such that a
+  // remote app can talk directly to the browser process and not ping-pong
+  // through the ash process.
+  Shell::Get()->accessibility_delegate()->DispatchAccessibilityEvent(
+      tree_id, updates, event);
 }
 
 void AXHostService::PerformAction(const ui::AXActionData& data) {
@@ -104,6 +96,7 @@
 }
 
 void AXHostService::OnRemoteHostDisconnected() {
-  extensions::AutomationEventRouter::GetInstance()->DispatchTreeDestroyedEvent(
-      tree_id(), nullptr /* browser_context */);
+  Shell::Get()->accessibility_delegate()->DispatchTreeDestroyedEvent(tree_id());
 }
+
+}  // namespace ash
diff --git a/chrome/browser/chromeos/accessibility/ax_host_service.h b/ash/accessibility/ax_host_service.h
similarity index 84%
rename from chrome/browser/chromeos/accessibility/ax_host_service.h
rename to ash/accessibility/ax_host_service.h
index 9d2fa2a..b0809cde0 100644
--- a/chrome/browser/chromeos/accessibility/ax_host_service.h
+++ b/ash/accessibility/ax_host_service.h
@@ -2,26 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_AX_HOST_SERVICE_H_
-#define CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_AX_HOST_SERVICE_H_
+#ifndef ASH_ACCESSIBILITY_AX_HOST_SERVICE_H_
+#define ASH_ACCESSIBILITY_AX_HOST_SERVICE_H_
 
 #include <memory>
 
+#include "ash/ash_export.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "mojo/public/cpp/bindings/interface_ptr_set.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "services/service_manager/public/cpp/service.h"
 #include "ui/accessibility/ax_host_delegate.h"
+#include "ui/accessibility/ax_tree_id.h"
 #include "ui/accessibility/mojom/ax_host.mojom.h"
 
+namespace ash {
+
 // Forwards accessibility events from clients in other processes that use aura
 // and views (e.g. the Chrome OS keyboard shortcut_viewer) to accessibility
 // extensions. Renderers, PDF, etc. use a different path. Created when the first
 // client connects over mojo. Implements AXHostDelegate by routing actions over
 // mojo to the remote process.
-class AXHostService : public service_manager::Service,
-                      public ax::mojom::AXHost,
-                      public ui::AXHostDelegate {
+class ASH_EXPORT AXHostService : public service_manager::Service,
+                                 public ax::mojom::AXHost,
+                                 public ui::AXHostDelegate {
  public:
   AXHostService();
   ~AXHostService() override;
@@ -66,4 +70,6 @@
   DISALLOW_COPY_AND_ASSIGN(AXHostService);
 };
 
-#endif  // CHROME_BROWSER_CHROMEOS_ACCESSIBILITY_AX_HOST_SERVICE_H_
+}  // namespace ash
+
+#endif  // ASH_ACCESSIBILITY_AX_HOST_SERVICE_H_
diff --git a/chrome/browser/chromeos/accessibility/ax_host_service_unittest.cc b/ash/accessibility/ax_host_service_unittest.cc
similarity index 91%
rename from chrome/browser/chromeos/accessibility/ax_host_service_unittest.cc
rename to ash/accessibility/ax_host_service_unittest.cc
index 43ac4b48..4ecc260b 100644
--- a/chrome/browser/chromeos/accessibility/ax_host_service_unittest.cc
+++ b/ash/accessibility/ax_host_service_unittest.cc
@@ -2,8 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/accessibility/ax_host_service.h"
+#include "ash/accessibility/ax_host_service.h"
 
+#include "ash/test/ash_test_base.h"
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/test/scoped_task_environment.h"
@@ -11,6 +12,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/accessibility/mojom/ax_host.mojom.h"
 
+namespace ash {
 namespace {
 
 class TestAXRemoteHost : ax::mojom::AXRemoteHost {
@@ -51,16 +53,7 @@
   DISALLOW_COPY_AND_ASSIGN(TestAXRemoteHost);
 };
 
-class AXHostServiceTest : public testing::Test {
- public:
-  AXHostServiceTest() = default;
-  ~AXHostServiceTest() override = default;
-
- private:
-  base::test::ScopedTaskEnvironment scoped_task_enviroment_;
-
-  DISALLOW_COPY_AND_ASSIGN(AXHostServiceTest);
-};
+using AXHostServiceTest = AshTestBase;
 
 TEST_F(AXHostServiceTest, AddClientThenEnable) {
   AXHostService service;
@@ -120,3 +113,4 @@
 }
 
 }  // namespace
+}  // namespace ash
diff --git a/ash/accessibility/default_accessibility_delegate.cc b/ash/accessibility/default_accessibility_delegate.cc
index f57af83..f4b5ec3 100644
--- a/ash/accessibility/default_accessibility_delegate.cc
+++ b/ash/accessibility/default_accessibility_delegate.cc
@@ -40,4 +40,12 @@
   return std::numeric_limits<double>::min();
 }
 
+void DefaultAccessibilityDelegate::DispatchAccessibilityEvent(
+    const ui::AXTreeID& tree_id,
+    const std::vector<ui::AXTreeUpdate>& updates,
+    const ui::AXEvent& event) {}
+
+void DefaultAccessibilityDelegate::DispatchTreeDestroyedEvent(
+    const ui::AXTreeID& tree_id) {}
+
 }  // namespace ash
diff --git a/ash/accessibility/default_accessibility_delegate.h b/ash/accessibility/default_accessibility_delegate.h
index 4287df6..2f09148 100644
--- a/ash/accessibility/default_accessibility_delegate.h
+++ b/ash/accessibility/default_accessibility_delegate.h
@@ -11,6 +11,7 @@
 
 namespace ash {
 
+// Used in tests and non-production code like ash_shell_with_content.
 class ASH_EXPORT DefaultAccessibilityDelegate : public AccessibilityDelegate {
  public:
   DefaultAccessibilityDelegate();
@@ -21,6 +22,10 @@
   bool ShouldShowAccessibilityMenu() const override;
   void SaveScreenMagnifierScale(double scale) override;
   double GetSavedScreenMagnifierScale() override;
+  void DispatchAccessibilityEvent(const ui::AXTreeID& tree_id,
+                                  const std::vector<ui::AXTreeUpdate>& updates,
+                                  const ui::AXEvent& event) override;
+  void DispatchTreeDestroyedEvent(const ui::AXTreeID& tree_id) override;
 
  private:
   bool screen_magnifier_enabled_ = false;
diff --git a/ash/ash_service.cc b/ash/ash_service.cc
index 5ec00b29..399948e4 100644
--- a/ash/ash_service.cc
+++ b/ash/ash_service.cc
@@ -203,6 +203,7 @@
     service_manager::mojom::ServiceRequest service,
     const std::string& name,
     service_manager::mojom::PIDReceiverPtr pid_receiver) {
+  // TODO(jamescook): Create the AXHostService here under mash.
   DCHECK_EQ(name, ws::mojom::kServiceName);
   Shell::Get()->window_service_owner()->BindWindowService(std::move(service));
   if (base::FeatureList::IsEnabled(features::kMash)) {
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index 4c76da5..3f41ada8 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -31,27 +31,29 @@
 ShelfTooltipManager::ShelfTooltipManager(ShelfView* shelf_view)
     : timer_delay_(kTooltipAppearanceDelay),
       shelf_view_(shelf_view),
-      bubble_(nullptr),
       weak_factory_(this) {
   shelf_view_->shelf()->AddObserver(this);
-  Shell::Get()->AddPointerWatcher(this, views::PointerWatcherEventTypes::BASIC);
+  Shell::Get()->AddPreTargetHandler(this);
 }
 
 ShelfTooltipManager::~ShelfTooltipManager() {
-  Shell::Get()->RemovePointerWatcher(this);
+  Shell::Get()->RemovePreTargetHandler(this);
   shelf_view_->shelf()->RemoveObserver(this);
   if (shelf_view_->GetWidget() && shelf_view_->GetWidget()->GetNativeWindow())
     shelf_view_->GetWidget()->GetNativeWindow()->RemovePreTargetHandler(this);
 }
 
-void ShelfTooltipManager::Init() {
-  shelf_view_->GetWidget()->GetNativeWindow()->AddPreTargetHandler(this);
-}
-
-void ShelfTooltipManager::Close() {
+void ShelfTooltipManager::Close(bool animate) {
+  // Cancel any timer set to show a tooltip after a delay.
   timer_.Stop();
-  if (bubble_)
-    bubble_->GetWidget()->Close();
+  if (!bubble_)
+    return;
+  if (!animate) {
+    // Cancel the typical hiding animation to hide the bubble immediately.
+    ::wm::SetWindowVisibilityAnimationTransition(
+        bubble_->GetWidget()->GetNativeWindow(), ::wm::ANIMATE_NONE);
+  }
+  bubble_->GetWidget()->Close();
   bubble_ = nullptr;
 }
 
@@ -64,13 +66,8 @@
 }
 
 void ShelfTooltipManager::ShowTooltip(views::View* view) {
-  timer_.Stop();
-  if (bubble_) {
-    // Cancel the hiding animation to hide the old bubble immediately.
-    ::wm::SetWindowVisibilityAnimationTransition(
-        bubble_->GetWidget()->GetNativeWindow(), ::wm::ANIMATE_NONE);
-    Close();
-  }
+  // Hide the old bubble immediately, skipping the typical closing animation.
+  Close(false /*animate*/);
 
   if (!ShouldShowTooltipForView(view))
     return;
@@ -115,37 +112,23 @@
   }
 }
 
-void ShelfTooltipManager::OnPointerEventObserved(
-    const ui::PointerEvent& event,
-    const gfx::Point& location_in_screen,
-    gfx::NativeView target) {
-  if (event.type() != ui::ET_POINTER_DOWN || !bubble_)
-    return;
-
-  // If the click was outside the tooltip, always close it.
-  if (!bubble_->GetWidget()->GetWindowBoundsInScreen().Contains(
-          location_in_screen)) {
-    Close();
-    return;
-  }
-
-  // Close the bubble if appropriate.
-  if (bubble_->ShouldCloseOnPressDown())
-    Close();
-}
-
 void ShelfTooltipManager::OnMouseEvent(ui::MouseEvent* event) {
-  if (event->type() == ui::ET_MOUSE_EXITED) {
-    if (bubble_ && bubble_->ShouldCloseOnMouseExit())
-      Close();
-
-    // Don't show any tooltip we were planning on showing after a delay.
-    timer_.Stop();
+  if (bubble_ && event->type() == ui::ET_MOUSE_PRESSED) {
+    ProcessPressedEvent(*event);
     return;
   }
 
-  if (event->type() != ui::ET_MOUSE_MOVED)
+  if (bubble_ && event->type() == ui::ET_MOUSE_EXITED &&
+      bubble_->ShouldCloseOnMouseExit()) {
+    Close();
     return;
+  }
+
+  // The code below handles mouse move events within the shelf window.
+  if (event->type() != ui::ET_MOUSE_MOVED ||
+      event->target() != shelf_view_->GetWidget()->GetNativeWindow()) {
+    return;
+  }
 
   gfx::Point point = event->location();
   views::View::ConvertPointFromWidget(shelf_view_, &point);
@@ -161,6 +144,11 @@
     Close();
 }
 
+void ShelfTooltipManager::OnTouchEvent(ui::TouchEvent* event) {
+  if (bubble_ && event->type() == ui::ET_TOUCH_PRESSED)
+    ProcessPressedEvent(*event);
+}
+
 void ShelfTooltipManager::WillChangeVisibilityState(
     ShelfVisibilityState new_state) {
   if (new_state == SHELF_HIDDEN)
@@ -168,15 +156,8 @@
 }
 
 void ShelfTooltipManager::OnAutoHideStateChanged(ShelfAutoHideState new_state) {
-  if (new_state == SHELF_AUTO_HIDE_HIDDEN) {
-    timer_.Stop();
-    // AutoHide state change happens during an event filter, so immediate close
-    // may cause a crash in the HandleMouseEvent() after the filter.  So we just
-    // schedule the Close here.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::Bind(&ShelfTooltipManager::Close, weak_factory_.GetWeakPtr()));
-  }
+  if (new_state == SHELF_AUTO_HIDE_HIDDEN)
+    Close();
 }
 
 bool ShelfTooltipManager::ShouldShowTooltipForView(views::View* view) {
@@ -188,4 +169,12 @@
            shelf->GetAutoHideState() == SHELF_AUTO_HIDE_SHOWN));
 }
 
+void ShelfTooltipManager::ProcessPressedEvent(const ui::LocatedEvent& event) {
+  // Always close the tooltip on press events outside the tooltip.
+  if (bubble_->ShouldCloseOnPressDown() ||
+      event.target() != bubble_->GetWidget()->GetNativeWindow()) {
+    Close();
+  }
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_tooltip_manager.h b/ash/shelf/shelf_tooltip_manager.h
index c6e0748e..b7ff2a2 100644
--- a/ash/shelf/shelf_tooltip_manager.h
+++ b/ash/shelf/shelf_tooltip_manager.h
@@ -7,33 +7,32 @@
 
 #include "ash/ash_export.h"
 #include "ash/shelf/shelf_observer.h"
-#include "ash/shelf/shelf_tooltip_bubble_base.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "ui/events/event_handler.h"
-#include "ui/views/pointer_watcher.h"
+
+namespace ui {
+class LocatedEvent;
+}
 
 namespace views {
 class View;
 }
 
 namespace ash {
+class ShelfTooltipBubbleBase;
 class ShelfView;
 
 // ShelfTooltipManager manages the tooltip bubble that appears for shelf items.
 class ASH_EXPORT ShelfTooltipManager : public ui::EventHandler,
-                                       public views::PointerWatcher,
                                        public ShelfObserver {
  public:
   explicit ShelfTooltipManager(ShelfView* shelf_view);
   ~ShelfTooltipManager() override;
 
-  // Initializes the tooltip manager once the shelf is shown.
-  void Init();
-
-  // Closes the tooltip.
-  void Close();
+  // Closes the tooltip; uses an animation if |animate| is true.
+  void Close(bool animate = true);
 
   // Returns true if the tooltip is currently visible.
   bool IsVisible() const;
@@ -51,11 +50,7 @@
  protected:
   // ui::EventHandler overrides:
   void OnMouseEvent(ui::MouseEvent* event) override;
-
-  // views::PointerWatcher overrides:
-  void OnPointerEventObserved(const ui::PointerEvent& event,
-                              const gfx::Point& location_in_screen,
-                              gfx::NativeView target) override;
+  void OnTouchEvent(ui::TouchEvent* event) override;
 
   // ShelfObserver overrides:
   void WillChangeVisibilityState(ShelfVisibilityState new_state) override;
@@ -68,11 +63,13 @@
   // A helper function to check for shelf visibility and view validity.
   bool ShouldShowTooltipForView(views::View* view);
 
+  // A helper function to close the tooltip on mouse and touch press events.
+  void ProcessPressedEvent(const ui::LocatedEvent& event);
+
   int timer_delay_;
   base::OneShotTimer timer_;
-
   ShelfView* shelf_view_;
-  ShelfTooltipBubbleBase* bubble_;
+  ShelfTooltipBubbleBase* bubble_ = nullptr;
 
   base::WeakPtrFactory<ShelfTooltipManager> weak_factory_;
 
diff --git a/ash/shelf/shelf_tooltip_manager_unittest.cc b/ash/shelf/shelf_tooltip_manager_unittest.cc
index 9e3bd615..af4d66df 100644
--- a/ash/shelf/shelf_tooltip_manager_unittest.cc
+++ b/ash/shelf/shelf_tooltip_manager_unittest.cc
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/shelf/app_list_button.h"
 #include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_tooltip_bubble_base.h"
 #include "ash/shelf/shelf_view.h"
 #include "ash/shelf/shelf_view_test_api.h"
 #include "ash/shell.h"
@@ -122,10 +123,6 @@
   ASSERT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS,
             GetPrimaryShelf()->auto_hide_behavior());
   ASSERT_EQ(SHELF_AUTO_HIDE_HIDDEN, GetPrimaryShelf()->GetAutoHideState());
-
-  // Tooltip visibility change for auto hide may take time.
-  EXPECT_TRUE(tooltip_manager_->IsVisible());
-  RunAllPendingInMessageLoop();
   EXPECT_FALSE(tooltip_manager_->IsVisible());
 
   // Do not show the view if the shelf is hidden.
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 9316830..0f91ddd 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -1793,12 +1793,6 @@
   node_data->SetName(l10n_util::GetStringUTF8(IDS_ASH_SHELF_ACCESSIBLE_NAME));
 }
 
-void ShelfView::ViewHierarchyChanged(
-    const ViewHierarchyChangedDetails& details) {
-  if (details.is_add && details.child == this)
-    tooltip_.Init();
-}
-
 void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
   // Do not forward events to |shelf_| (which forwards events to the shelf
   // layout manager) as we do not want gestures on the overflow to open the app
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index f2b8206e..81febaa 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -392,8 +392,6 @@
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   FocusTraversable* GetPaneFocusTraversable() override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-  void ViewHierarchyChanged(
-      const ViewHierarchyChangedDetails& details) override;
 
   // Overridden from ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
diff --git a/ash/system/night_light/night_light_feature_pod_controller.cc b/ash/system/night_light/night_light_feature_pod_controller.cc
index 518f8eb..31739d8 100644
--- a/ash/system/night_light/night_light_feature_pod_controller.cc
+++ b/ash/system/night_light/night_light_feature_pod_controller.cc
@@ -14,6 +14,8 @@
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace ash {
@@ -47,11 +49,21 @@
   DCHECK(features::IsNightLightEnabled());
   Shell::Get()->night_light_controller()->Toggle();
   UpdateButton();
+
+  if (Shell::Get()->night_light_controller()->GetEnabled()) {
+    base::RecordAction(
+        base::UserMetricsAction("StatusArea_NightLight_Enabled"));
+  } else {
+    base::RecordAction(
+        base::UserMetricsAction("StatusArea_NightLight_Disabled"));
+  }
 }
 
 void NightLightFeaturePodController::OnLabelPressed() {
   DCHECK(features::IsNightLightEnabled());
   if (TrayPopupUtils::CanOpenWebUISettings()) {
+    base::RecordAction(
+        base::UserMetricsAction("StatusArea_NightLight_Settings"));
     Shell::Get()->system_tray_model()->client_ptr()->ShowDisplaySettings();
     tray_controller_->CloseBubble();
   }
diff --git a/ash/system/tray/system_tray_item_uma_type.h b/ash/system/tray/system_tray_item_uma_type.h
index cf85c4c..c2f34a2 100644
--- a/ash/system/tray/system_tray_item_uma_type.h
+++ b/ash/system/tray/system_tray_item_uma_type.h
@@ -41,7 +41,8 @@
   UMA_USER = 24,
   UMA_VPN = 25,
   UMA_NIGHT_LIGHT = 26,
-  UMA_COUNT = 27,
+  UMA_QUIET_MODE = 27,
+  UMA_COUNT = 28,
 };
 
 }  // namespace ash
diff --git a/ash/system/unified/quiet_mode_feature_pod_controller.cc b/ash/system/unified/quiet_mode_feature_pod_controller.cc
index dfbc682..6d43464 100644
--- a/ash/system/unified/quiet_mode_feature_pod_controller.cc
+++ b/ash/system/unified/quiet_mode_feature_pod_controller.cc
@@ -10,6 +10,8 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/unified/feature_pod_button.h"
 #include "ash/system/unified/unified_system_tray_controller.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/message_center/message_center.h"
 
@@ -55,6 +57,13 @@
   MessageCenter* message_center = MessageCenter::Get();
   bool is_quiet_mode = message_center->IsQuietMode();
   message_center->SetQuietMode(!is_quiet_mode);
+
+  if (message_center->IsQuietMode()) {
+    base::RecordAction(base::UserMetricsAction("StatusArea_QuietMode_Enabled"));
+  } else {
+    base::RecordAction(
+        base::UserMetricsAction("StatusArea_QuietMode_Disabled"));
+  }
 }
 
 void QuietModeFeaturePodController::OnLabelPressed() {
@@ -62,7 +71,7 @@
 }
 
 SystemTrayItemUmaType QuietModeFeaturePodController::GetUmaType() const {
-  return SystemTrayItemUmaType::UMA_NOT_RECORDED;
+  return SystemTrayItemUmaType::UMA_QUIET_MODE;
 }
 
 void QuietModeFeaturePodController::OnQuietModeChanged(bool in_quiet_mode) {
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index a583830..ccbf3be 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -40,6 +40,7 @@
 #include "ash/system/unified_accessibility_detailed_view_controller.h"
 #include "ash/wm/lock_state_controller.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/metrics/user_metrics.h"
 #include "base/numerics/ranges.h"
 #include "ui/gfx/animation/slide_animation.h"
 #include "ui/message_center/message_center.h"
@@ -65,6 +66,10 @@
   animation_->Reset(model->IsExpandedOnOpen() ? 1.0 : 0.0);
   animation_->SetSlideDuration(kExpandAnimationDurationMs);
   animation_->SetTweenType(gfx::Tween::EASE_IN_OUT);
+
+  Shell::Get()->metrics()->RecordUserMetricsAction(UMA_STATUS_AREA_MENU_OPENED);
+  UMA_HISTOGRAM_BOOLEAN("ChromeOS.SystemTray.IsExpandedOnOpen",
+                        model_->IsExpandedOnOpen());
 }
 
 UnifiedSystemTrayController::~UnifiedSystemTrayController() = default;
@@ -161,6 +166,9 @@
 }
 
 void UnifiedSystemTrayController::HandleClearAllAction() {
+  base::RecordAction(
+      base::UserMetricsAction("StatusArea_Notifications_ClearAll"));
+
   // When the animation is finished, OnClearAllAnimationEnded() is called.
   unified_view_->ShowClearAllAnimation();
 }
diff --git a/ash/wm/base_state.cc b/ash/wm/base_state.cc
index 8fef967e..6c9fab23 100644
--- a/ash/wm/base_state.cc
+++ b/ash/wm/base_state.cc
@@ -39,6 +39,7 @@
 
   if (event->IsBoundsEvent()) {
     HandleBoundsEvents(window_state, event);
+    window_state->UpdatePipRoundedCorners();
     return;
   }
   DCHECK(event->IsTransitionEvent());
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 3d69cce7..d31eec0 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -32,6 +32,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/views/painter.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/ime_util_chromeos.h"
 #include "ui/wm/core/window_util.h"
@@ -40,6 +41,9 @@
 namespace wm {
 namespace {
 
+// TODO(edcourtney): Move this to a PIP specific file, once it's created.
+const int kPipRoundedCornerRadius = 8;
+
 bool IsTabletModeEnabled() {
   return Shell::Get()
       ->tablet_mode_controller()
@@ -660,6 +664,28 @@
   CrossFadeAnimation(window_, std::move(old_layer_owner), animation_type);
 }
 
+void WindowState::UpdatePipRoundedCorners() {
+  auto* layer = window()->layer();
+  if (!IsPip()) {
+    if (layer)
+      layer->SetMaskLayer(nullptr);
+    pip_mask_.reset();
+    return;
+  }
+
+  gfx::Rect bounds = window()->bounds();
+  if (layer && (!pip_mask_ || pip_mask_->layer()->size() != bounds.size())) {
+    layer->SetMaskLayer(nullptr);
+    pip_mask_ = views::Painter::CreatePaintedLayer(
+        views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
+                                                    kPipRoundedCornerRadius));
+    pip_mask_->layer()->SetBounds(bounds);
+    pip_mask_->layer()->SetFillsBoundsOpaquely(false);
+    layer->SetFillsBoundsOpaquely(false);
+    layer->SetMaskLayer(pip_mask_->layer());
+  }
+}
+
 WindowState* GetActiveWindowState() {
   aura::Window* active = GetActiveWindow();
   return active ? GetWindowState(active) : nullptr;
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index 92fa9d0..c471434 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -17,6 +17,7 @@
 #include "base/optional.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/ui_base_types.h"
+#include "ui/compositor/layer_owner.h"
 #include "ui/gfx/animation/tween.h"
 
 namespace gfx {
@@ -399,6 +400,10 @@
       const gfx::Rect& bounds,
       gfx::Tween::Type animation_type = gfx::Tween::EASE_OUT);
 
+  // Updates rounded corners for PIP window states. Removes rounded corners
+  // for non-PIP window states.
+  void UpdatePipRoundedCorners();
+
   // aura::WindowObserver:
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
@@ -422,6 +427,9 @@
   bool cached_always_on_top_;
   bool allow_set_bounds_direct_ = false;
 
+  // Mask layer for PIP windows.
+  std::unique_ptr<ui::LayerOwner> pip_mask_ = nullptr;
+
   // A property to save the ratio between snapped window width and display
   // workarea width. It is used to update snapped window width on
   // AdjustSnappedBounds() when handling workspace events.
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 1ed0b8a..1a4c116b0 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2973,7 +2973,10 @@
       "android/java/src/org/chromium/base/memory/MemoryPressureUma.java",
       "android/java/src/org/chromium/base/task/AsyncTask.java",
       "android/java/src/org/chromium/base/task/ChromeThreadPoolExecutor.java",
+      "android/java/src/org/chromium/base/task/SequencedTaskRunner.java",
       "android/java/src/org/chromium/base/task/SerialExecutor.java",
+      "android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java",
+      "android/java/src/org/chromium/base/task/TaskRunner.java",
     ]
 
     # New versions of BuildConfig.java and NativeLibraries.java
diff --git a/base/android/java/src/org/chromium/base/task/SequencedTaskRunner.java b/base/android/java/src/org/chromium/base/task/SequencedTaskRunner.java
new file mode 100644
index 0000000..69118fd2
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/task/SequencedTaskRunner.java
@@ -0,0 +1,13 @@
+// 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.base.task;
+
+/**
+ * Tasks posted will be run in order with respect to this sequence, but they may be executed
+ * on arbitrary threads. Unless specified otherwise by the provider of a given
+ * SequencedTaskRunner, tasks posted to it have no ordering, nor mutual exclusion, execution
+ * guarantees w.r.t. other SequencedTaskRunners.
+ */
+public interface SequencedTaskRunner extends TaskRunner {}
diff --git a/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java b/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java
new file mode 100644
index 0000000..015b077
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/task/SingleThreadTaskRunner.java
@@ -0,0 +1,10 @@
+// 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.base.task;
+
+/**
+ * Tasks posted will be run in order on a single thread.
+ */
+public interface SingleThreadTaskRunner extends SequencedTaskRunner {}
diff --git a/base/android/java/src/org/chromium/base/task/TaskRunner.java b/base/android/java/src/org/chromium/base/task/TaskRunner.java
new file mode 100644
index 0000000..0a2eaac
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/task/TaskRunner.java
@@ -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.
+
+package org.chromium.base.task;
+
+/**
+ * A task queue that posts Java tasks onto the C++ browser scheduler, if loaded. Otherwise this
+ * will be backed by an {@link android.os.Handler} or the java thread pool. The TaskQueue interface
+ * provides no guarantee over the order or the thread on which the task will be executed.
+ *
+ * Very similar to {@link java.util.concurrent.Executor} but conforms to chromium terminology.
+ */
+public interface TaskRunner {
+    /**
+     * Posts a task to run immediately.
+     *
+     * @param task The task to be run immediately.
+     */
+    public void postTask(Runnable task);
+
+    /**
+     * Instructs the TaskRunner to initialize the native TaskRunner and migrate any tasks over to
+     * it.
+     */
+    void initNativeTaskRunner();
+}
diff --git a/base/bits.h b/base/bits.h
index a1c8b5de..cbd46c08 100644
--- a/base/bits.h
+++ b/base/bits.h
@@ -10,6 +10,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <type_traits>
+
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "build/build_config.h"
diff --git a/chrome/VERSION b/chrome/VERSION
index 10c3fb7..ac1ecde 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=71
 MINOR=0
-BUILD=3564
+BUILD=3565
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index b66fcea..27cb28a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -53,7 +53,7 @@
                 parameters.keySet().toArray(new String[parameters.size()]),
                 parameters.values().toArray(new String[parameters.size()]));
 
-        // Stop Autofill Assistant when the tab is detached from the activity.
+        // Shut down Autofill Assistant when the tab is detached from the activity.
         activityTab.addObserver(new EmptyTabObserver() {
             @Override
             public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
@@ -64,7 +64,7 @@
             }
         });
 
-        // Stop Autofill Assistant when the selected tab (foreground tab) is changed.
+        // Shut down Autofill Assistant when the selected tab (foreground tab) is changed.
         TabModel currentTabModel = activity.getTabModelSelector().getCurrentModel();
         currentTabModel.addObserver(new EmptyTabModelObserver() {
             @Override
@@ -122,6 +122,11 @@
     }
 
     @CalledByNative
+    private void onShutdown() {
+        mUiDelegate.shutdown();
+    }
+
+    @CalledByNative
     private void onUpdateScripts(String[] scriptNames, String[] scriptPaths) {
         List<AutofillAssistantUiDelegate.ScriptHandle> scriptHandles = new ArrayList<>();
         // Note that scriptNames and scriptPaths are one-on-one matched by index.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
index 276025a..912ce8d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiDelegate.java
@@ -103,8 +103,7 @@
         mBottomBar.findViewById(R.id.close_button).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                mFullContainer.setVisibility(View.GONE);
-                mClient.onDismiss();
+                shutdown();
             }
         });
         mBottomBar.findViewById(R.id.feedback_button)
@@ -164,4 +163,12 @@
     public void hideOverlay() {
         mOverlay.setVisibility(View.GONE);
     }
+
+    /**
+     * Shuts down the Autofill Assistant. The UI disappears and any associated state goes away.
+     */
+    public void shutdown() {
+        mFullContainer.setVisibility(View.GONE);
+        mClient.onDismiss();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleHostImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleHostImpl.java
index c67f6ffb..226921a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleHostImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dynamicmodule/ModuleHostImpl.java
@@ -10,7 +10,7 @@
  * The implementation of {@link IModuleHost}.
  */
 public class ModuleHostImpl extends BaseModuleHost {
-    private static final int VERSION = 3;
+    private static final int VERSION = 4;
     private static final int MINIMUM_MODULE_VERSION = 1;
 
     private final Context mApplicationContext;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
similarity index 60%
rename from chrome/android/java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java
rename to chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
index 6694ec7..3aa1d277 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/SadTab.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tab;
 
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -16,35 +17,170 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.FrameLayout.LayoutParams;
 import android.widget.TextView;
 
+import org.chromium.base.UserData;
+import org.chromium.base.VisibleForTesting;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.ui_metrics.SadTabEvent;
+import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
 
 /**
- * A factory class for creating the "Sad Tab" view, which is shown in place of a crashed renderer.
+ * Represent the sad tab displayed in place of a crashed renderer. Instantiated on the first
+ * |show()| request from a Tab, and destroyed together with it.
  */
-public class SadTabViewFactory {
+public class SadTab extends EmptyTabObserver implements UserData {
+    private static final Class<SadTab> USER_DATA_KEY = SadTab.class;
+
+    private final Tab mTab;
+
+    private View mView;
+
     /**
-     * @param context Context of the resulting Sad Tab view.
+     * Counts the number of successive refreshes on the sad tab page. The count is is reset after a
+     * successful page load.
+     */
+    private int mSadTabSuccessiveRefreshCounter;
+
+    public static SadTab from(Tab tab) {
+        SadTab sadTab = get(tab);
+        if (sadTab == null) {
+            sadTab = tab.getUserDataHost().setUserData(USER_DATA_KEY, new SadTab(tab));
+        }
+        return sadTab;
+    }
+
+    public static SadTab get(Tab tab) {
+        return tab.getUserDataHost().getUserData(USER_DATA_KEY);
+    }
+
+    public static boolean isShowing(Tab tab) {
+        SadTab sadTab = get(tab);
+        return sadTab != null ? sadTab.isShowing() : false;
+    }
+
+    @VisibleForTesting
+    public SadTab(Tab tab) {
+        mTab = tab;
+        mTab.addObserver(this);
+    }
+
+    /**
+     * Constructs and shows a sad tab (Aw, Snap!).
+     */
+    public void show() {
+        if (mTab.getWebContents() == null) return;
+
+        // Make sure we are not adding the "Aw, snap" view over an existing one.
+        assert mView == null;
+
+        // If the tab has crashed twice in a row change the sad tab view to the "Send Feedback"
+        // version and change the onClickListener.
+        final boolean showSendFeedbackView = mSadTabSuccessiveRefreshCounter >= 1;
+
+        Runnable suggestionAction = new Runnable() {
+            @Override
+            public void run() {
+                Activity activity = mTab.getWindowAndroid().getActivity().get();
+                assert activity != null;
+                HelpAndFeedback.getInstance(activity).show(activity,
+                        activity.getString(R.string.help_context_sad_tab),
+                        Profile.getLastUsedProfile(), null);
+            }
+        };
+
+        Runnable buttonAction = new Runnable() {
+            @Override
+            public void run() {
+                if (showSendFeedbackView) {
+                    mTab.getActivity().startHelpAndFeedback(
+                            mTab.getUrl(), "MobileSadTabFeedback", mTab.getProfile());
+                } else {
+                    mTab.reload();
+                }
+            }
+        };
+
+        mView = createView(
+                suggestionAction, buttonAction, showSendFeedbackView, mTab.isIncognito());
+        mSadTabSuccessiveRefreshCounter++;
+
+        // Show the sad tab inside ContentView.
+        mTab.getContentView().addView(mView,
+                new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+        mTab.notifyContentChanged();
+    }
+
+    /**
+     * Removes the sad tab view if present.
+     */
+    @VisibleForTesting
+    void removeIfPresent() {
+        if (isShowing()) {
+            mTab.getContentView().removeView(mView);
+            mTab.notifyContentChanged();
+        }
+        mView = null;
+    }
+
+    /**
+     * @return Whether or not the sad tab is showing.
+     */
+    public boolean isShowing() {
+        return mView != null && mView.getParent() == mTab.getContentView();
+    }
+
+    // TabObserver
+
+    @Override
+    public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) {
+        removeIfPresent();
+    }
+
+    @Override
+    public void onPageLoadStarted(Tab tab, String url) {
+        removeIfPresent();
+    }
+
+    @Override
+    public void onPageLoadFinished(Tab tab) {
+        // Reset the succressiveRefresh counter after successfully loading a page.
+        mSadTabSuccessiveRefreshCounter = 0;
+        removeIfPresent();
+    }
+
+    // UserData
+
+    @Override
+    public void destroy() {
+        mTab.removeObserver(this);
+    }
+
+    /**
      * @param suggestionAction {@link Runnable} to be executed when user clicks "try these
      *                        suggestions".
      * @param buttonAction {@link Runnable} to be executed when the button is pressed.
      *                     (e.g., refreshing the page or sending feedback)
      * @param showSendFeedbackView Whether to show the "send feedback" version of the Sad Tab view.
      * @param isIncognito Whether the Sad Tab view is being showin in an incognito tab.
-     * @return A "Sad Tab" view instance which is used in place of a crashed renderer.
+     * @return A {@link View} instance which is used in place of a crashed renderer.
      */
-    public static View createSadTabView(Context context, final Runnable suggestionAction,
-            final Runnable buttonAction, final boolean showSendFeedbackView, boolean isIncognito) {
+    protected View createView(final Runnable suggestionAction, Runnable buttonAction,
+            boolean showSendFeedbackView, boolean isIncognito) {
+        Context context = mTab.getThemedApplicationContext();
+
         // Inflate Sad tab and initialize.
-        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
+        LayoutInflater inflater =
+                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         View sadTabView = inflater.inflate(R.layout.sad_tab, null);
 
         TextView titleText = (TextView) sadTabView.findViewById(R.id.sad_tab_title);
@@ -65,12 +201,12 @@
         button.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                SadTabViewFactory.recordEvent(showSendFeedbackView, SadTabEvent.BUTTON_CLICKED);
+                recordEvent(showSendFeedbackView, SadTabEvent.BUTTON_CLICKED);
                 buttonAction.run();
             }
         });
 
-        SadTabViewFactory.recordEvent(showSendFeedbackView, SadTabEvent.DISPLAYED);
+        recordEvent(showSendFeedbackView, SadTabEvent.DISPLAYED);
 
         return sadTabView;
     }
@@ -85,7 +221,7 @@
     private static CharSequence getHelpMessage(
             Context context, final Runnable suggestionAction, final boolean showSendFeedback) {
         NoUnderlineClickableSpan linkSpan = new NoUnderlineClickableSpan((view) -> {
-            SadTabViewFactory.recordEvent(showSendFeedback, SadTabEvent.HELP_LINK_CLICKED);
+            recordEvent(showSendFeedback, SadTabEvent.HELP_LINK_CLICKED);
             suggestionAction.run();
         });
 
@@ -179,4 +315,10 @@
                     c, p, x + mXOffset, dir, top, baseline, bottom, text, start, end, first, l);
         }
     }
+
+    // Bare minimum set up so |isShowing| returns true.
+    @VisibleForTesting
+    public static void initForTesting(Tab tab, SadTab sadTab) {
+        tab.getUserDataHost().setUserData(USER_DATA_KEY, sadTab);
+    }
 }
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 caeceb16..bb77bdb 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
@@ -27,7 +27,6 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.Button;
 import android.widget.FrameLayout;
-import android.widget.FrameLayout.LayoutParams;
 import android.widget.PopupWindow;
 import android.widget.PopupWindow.OnDismissListener;
 
@@ -67,7 +66,6 @@
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
-import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
 import org.chromium.chrome.browser.media.ui.MediaSessionTabHelper;
 import org.chromium.chrome.browser.native_page.FrozenNativePage;
@@ -371,17 +369,6 @@
     private boolean mIsDetached;
 
     /**
-     * Reference to the current sadTabView if one is defined.
-     */
-    private View mSadTabView;
-
-    /**
-     * Counts the number of successive refreshes on the sad tab page. The count is is reset after a
-     * successful page load.
-     */
-    private int mSadTabSuccessiveRefreshCounter;
-
-    /**
      * Stores total data saved at the start of a page load. Used to calculate delta at the end of
      * page load, which is just an estimate of the data saved for the current page load since there
      * may be multiple pages loading at the same time. This estimate is used to get an idea of how
@@ -714,8 +701,6 @@
                 mIsNativePageCommitPending = maybeShowNativePage(params.getUrl(), false);
             }
 
-            removeSadTabIfPresent();
-
             // Clear the app association if the user navigated to a different page from the omnibox.
             if ((params.getTransitionType() & PageTransition.FROM_ADDRESS_BAR)
                     == PageTransition.FROM_ADDRESS_BAR) {
@@ -1617,7 +1602,6 @@
      */
     protected void didStartPageLoad(String validatedUrl, boolean showingErrorPage) {
         updateTitle();
-        removeSadTabIfPresent();
         mDataSavedOnStartPageLoad =
                 DataReductionProxySettings.getInstance().getContentLengthSavedInHistorySummary();
 
@@ -1634,9 +1618,6 @@
         updateTitle();
         updateFullscreenEnabledState();
 
-        // Reset the succressiveRefresh counter after successfully loading a page.
-        mSadTabSuccessiveRefreshCounter = 0;
-
         for (TabObserver observer : mObservers) observer.onPageLoadFinished(this);
         mIsBeingRestored = false;
 
@@ -1900,72 +1881,6 @@
     }
 
     /**
-     * Constructs and shows a sad tab (Aw, Snap!).
-     */
-    protected void showSadTab() {
-        if (getWebContents() == null) return;
-
-        // If the tab has crashed twice in a row change the sad tab view to the "Send Feedback"
-        // version and change the onClickListener.
-        final boolean showSendFeedbackView = mSadTabSuccessiveRefreshCounter >= 1;
-
-        Runnable suggestionAction = new Runnable() {
-            @Override
-            public void run() {
-                Activity activity = mWindowAndroid.getActivity().get();
-                assert activity != null;
-                HelpAndFeedback.getInstance(activity).show(activity,
-                        activity.getString(R.string.help_context_sad_tab),
-                        Profile.getLastUsedProfile(), null);
-            }
-        };
-
-        Runnable buttonAction = new Runnable() {
-            @Override
-            public void run() {
-                if (showSendFeedbackView) {
-                    getActivity().startHelpAndFeedback(
-                            getUrl(), "MobileSadTabFeedback", getProfile());
-                } else {
-                    reload();
-                }
-            }
-        };
-
-        // Make sure we are not adding the "Aw, snap" view over an existing one.
-        assert mSadTabView == null;
-
-        mSadTabView = SadTabViewFactory.createSadTabView(mThemedApplicationContext,
-                suggestionAction, buttonAction, showSendFeedbackView, mIncognito);
-        mSadTabSuccessiveRefreshCounter++;
-        // Show the sad tab inside ContentView.
-        mContentView.addView(mSadTabView,
-                new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-        notifyContentChanged();
-    }
-
-    /**
-     * Removes the sad tab view if present.
-     */
-    private void removeSadTabIfPresent() {
-        if (isShowingSadTab()) {
-            mContentView.removeView(mSadTabView);
-            notifyContentChanged();
-        }
-        mSadTabView = null;
-    }
-
-    /**
-     * Removes any existing sad tab view and shows it again. This "reloads" the tab without
-     * going through any formal loading logic.
-     */
-    @VisibleForTesting
-    public void reloadSadTabForTesting() {
-        removeSadTabIfPresent();
-        showSadTab();
-    }
-
-    /**
      * @return The ID of the renderer process that backs this tab or
      *         {@link #INVALID_RENDER_PROCESS_PID} if there is none.
      */
@@ -1979,13 +1894,13 @@
      * @return Whether or not the sad tab is showing.
      */
     public boolean isShowingSadTab() {
-        return mSadTabView != null && mSadTabView.getParent() == mContentView;
+        return SadTab.isShowing(this);
     }
 
     /**
      * Calls onContentChanged on all TabObservers and updates accessibility visibility.
      */
-    private void notifyContentChanged() {
+    void notifyContentChanged() {
         for (TabObserver observer : mObservers) observer.onContentChanged(this);
         updateAccessibilityVisibility();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index 6eebfec..766c09e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -122,7 +122,7 @@
             }
         } else {
             rendererCrashStatus = TAB_RENDERER_CRASH_STATUS_SHOWN_IN_FOREGROUND_APP;
-            mTab.showSadTab();
+            SadTab.from(mTab).show();
             // This is necessary to correlate histogram data with stability counts.
             RecordHistogram.recordBooleanHistogram("Stability.Android.RendererCrash", true);
         }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 3caaf8b2..14b2c4c 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -1431,8 +1431,8 @@
   "java/src/org/chromium/chrome/browser/tab/ChildBackgroundTabShowObserver.java",
   "java/src/org/chromium/chrome/browser/tab/EmptyTabObserver.java",
   "java/src/org/chromium/chrome/browser/tab/InterceptNavigationDelegateImpl.java",
+  "java/src/org/chromium/chrome/browser/tab/SadTab.java",
   "java/src/org/chromium/chrome/browser/tab/SadTabView.java",
-  "java/src/org/chromium/chrome/browser/tab/SadTabViewFactory.java",
   "java/src/org/chromium/chrome/browser/tab/Tab.java",
   "java/src/org/chromium/chrome/browser/tab/TabBrowserControlsOffsetHelper.java",
   "java/src/org/chromium/chrome/browser/tab/TabContextMenuItemDelegate.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java
index c766786..b753ac57 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabTest.java
@@ -51,6 +51,10 @@
         }
     };
 
+    private boolean isShowingSadTab() throws Exception {
+        return ThreadUtils.runOnUiThreadBlocking(() -> mTab.isShowingSadTab());
+    }
+
     @Before
     public void setUp() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
@@ -101,7 +105,7 @@
 
         Assert.assertFalse(mTab.needsReload());
         Assert.assertFalse(mTab.isHidden());
-        Assert.assertFalse(mTab.isShowingSadTab());
+        Assert.assertFalse(isShowingSadTab());
 
         // Stop the activity and simulate a killed renderer.
         ApplicationTestUtils.fireHomeScreenIntent(InstrumentationRegistry.getTargetContext());
@@ -114,7 +118,7 @@
             }
         });
         Assert.assertTrue(mTab.needsReload());
-        Assert.assertFalse(mTab.isShowingSadTab());
+        Assert.assertFalse(isShowingSadTab());
 
         ApplicationTestUtils.launchChrome(InstrumentationRegistry.getTargetContext());
 
@@ -126,7 +130,7 @@
             }
         });
         Assert.assertFalse(mTab.needsReload());
-        Assert.assertFalse(mTab.isShowingSadTab());
+        Assert.assertFalse(isShowingSadTab());
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java
index 09b6935..e3e7bbf 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/infobar/InfoBarTest.java
@@ -592,7 +592,7 @@
         // The renderer should have been killed and the InfoBar removed.
         mListener.removeInfoBarAnimationFinished("InfoBar not removed.");
         Assert.assertTrue("Wrong infobar count", mActivityTestRule.getInfoBars().isEmpty());
-        CriteriaHelper.pollInstrumentationThread(new Criteria() {
+        CriteriaHelper.pollUiThread(new Criteria() {
             @Override
             public boolean isSatisfied() {
                 return mActivityTestRule.getActivity().getActivityTab().isShowingSadTab();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
index fa10c22..ab05ac3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialogTest.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
 
+import android.os.Build;
 import android.support.test.filters.LargeTest;
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
@@ -20,7 +21,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -198,12 +199,8 @@
         });
     }
 
-    /**
-     * Continues to be flaky on bots which doesn't reproduce on local devices,
-     * continuing to investigate offline.
-     */
     @Test
-    @DisabledTest(message = "crbug.com/761060")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/761060")
     @LargeTest
     public void testNoSelection() throws Throwable {
         createDialog(false, Arrays.asList("image/*")); // Multi-select = false.
@@ -220,12 +217,8 @@
         dismissDialog();
     }
 
-    /**
-     * Continues to be flaky on bots which doesn't reproduce on local devices,
-     * continuing to investigate offline.
-     */
     @Test
-    @DisabledTest(message = "crbug.com/761060")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/761060")
     @LargeTest
     public void testSingleSelectionPhoto() throws Throwable {
         createDialog(false, Arrays.asList("image/*")); // Multi-select = false.
@@ -245,12 +238,8 @@
         dismissDialog();
     }
 
-    /**
-     * Continues to be flaky on bots which doesn't reproduce on local devices,
-     * continuing to investigate offline.
-     */
     @Test
-    @DisabledTest(message = "crbug.com/761060")
+    @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/761060")
     @LargeTest
     public void testMultiSelectionPhoto() throws Throwable {
         createDialog(true, Arrays.asList("image/*")); // Multi-select = true.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
index d3e2dec..7740228 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tab/SadTabTest.java
@@ -25,6 +25,8 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.net.test.util.TestWebServer;
 
+import java.util.concurrent.ExecutionException;
+
 /**
  * Tests related to the sad tab logic.
  */
@@ -41,6 +43,14 @@
         mActivityTestRule.startMainActivityOnBlankPage();
     }
 
+    private static boolean isShowingSadTab(Tab tab) {
+        try {
+            return ThreadUtils.runOnUiThreadBlocking(() -> tab.isShowingSadTab());
+        } catch (ExecutionException e) {
+            return false;
+        }
+    }
+
     /**
      * Verify that the sad tab is shown when the renderer crashes.
      */
@@ -50,9 +60,9 @@
     public void testSadTabShownWhenRendererProcessKilled() {
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
 
-        Assert.assertFalse(tab.isShowingSadTab());
+        Assert.assertFalse(isShowingSadTab(tab));
         simulateRendererKilled(tab, true);
-        Assert.assertTrue(tab.isShowingSadTab());
+        Assert.assertTrue(isShowingSadTab(tab));
     }
 
     /**
@@ -65,9 +75,9 @@
     public void testSadTabNotShownWhenRendererProcessKilledInBackround() {
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
 
-        Assert.assertFalse(tab.isShowingSadTab());
+        Assert.assertFalse(isShowingSadTab(tab));
         simulateRendererKilled(tab, false);
-        Assert.assertFalse(tab.isShowingSadTab());
+        Assert.assertFalse(isShowingSadTab(tab));
     }
 
     /**
@@ -127,16 +137,16 @@
     public void testSadTabPageButtonText() throws IllegalArgumentException, InterruptedException {
         final Tab tab = mActivityTestRule.getActivity().getActivityTab();
 
-        Assert.assertFalse(tab.isShowingSadTab());
+        Assert.assertFalse(isShowingSadTab(tab));
         simulateRendererKilled(tab, true);
-        Assert.assertTrue(tab.isShowingSadTab());
+        Assert.assertTrue(isShowingSadTab(tab));
         String actualText = getSadTabButton(tab).getText().toString();
         Assert.assertEquals("Expected the sad tab button to have the reload label",
                 mActivityTestRule.getActivity().getString(R.string.sad_tab_reload_label),
                 actualText);
 
         reloadSadTab(tab);
-        Assert.assertTrue(tab.isShowingSadTab());
+        Assert.assertTrue(isShowingSadTab(tab));
         actualText = getSadTabButton(tab).getText().toString();
         Assert.assertEquals(
                 "Expected the sad tab button to have the feedback label after the tab button "
@@ -147,7 +157,7 @@
         Assert.assertFalse(
                 "Expected about:blank to destroy the sad tab however the sad tab is still in "
                         + "view",
-                tab.isShowingSadTab());
+                isShowingSadTab(tab));
         simulateRendererKilled(tab, true);
         actualText = getSadTabButton(tab).getText().toString();
         Assert.assertEquals(
@@ -159,7 +169,7 @@
     /**
      * Helper method that kills the renderer on a UI thread.
      */
-    private void simulateRendererKilled(final Tab tab, final boolean visible) {
+    private static void simulateRendererKilled(final Tab tab, final boolean visible) {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
@@ -172,11 +182,13 @@
     /**
      * Helper method that reloads a tab with a SadTabView currently displayed.
      */
-    private void reloadSadTab(final Tab tab) {
+    private static void reloadSadTab(final Tab tab) {
         ThreadUtils.runOnUiThreadBlocking(new Runnable() {
             @Override
             public void run() {
-                tab.reloadSadTabForTesting();
+                SadTab sadTab = SadTab.from(tab);
+                sadTab.removeIfPresent();
+                sadTab.show();
             }
         });
     }
@@ -187,7 +199,7 @@
      * @return Returns the button that is on the SadTabView, null if SadTabView.
      *         doesn't exist.
      */
-    private Button getSadTabButton(Tab tab) {
+    private static Button getSadTabButton(Tab tab) {
         return (Button) tab.getContentView().findViewById(R.id.sad_tab_button);
     }
 
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index c34910d..4724b19 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-71.0.3561.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-71.0.3563.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app_shim/BUILD.gn b/chrome/app_shim/BUILD.gn
index 8e5763f8..06cbaf0 100644
--- a/chrome/app_shim/BUILD.gn
+++ b/chrome/app_shim/BUILD.gn
@@ -18,8 +18,10 @@
     "//chrome:strings",
     "//chrome/common",
     "//chrome/common:mojo_bindings",
+    "//content/public/browser",
     "//ipc",
     "//mojo/core/embedder",
+    "//ui/accelerated_widget_mac",
     "//ui/base",
     "//ui/views:views",
   ]
diff --git a/chrome/app_shim/DEPS b/chrome/app_shim/DEPS
index 90bfe99..7bca52a8 100644
--- a/chrome/app_shim/DEPS
+++ b/chrome/app_shim/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chrome/grit/generated_resources.h",
   "+chrome/installer/launcher_support",
+  "+content/public/browser",
   "+mojo/core/embedder",
 ]
diff --git a/chrome/app_shim/chrome_main_app_mode_mac.mm b/chrome/app_shim/chrome_main_app_mode_mac.mm
index 208384cd..6f345237 100644
--- a/chrome/app_shim/chrome_main_app_mode_mac.mm
+++ b/chrome/app_shim/chrome_main_app_mode_mac.mm
@@ -37,11 +37,14 @@
 #include "chrome/common/mac/app_shim.mojom.h"
 #include "chrome/common/mac/app_shim_param_traits.h"
 #include "chrome/grit/generated_resources.h"
+#include "content/public/browser/ns_view_bridge_factory_impl.h"
+#include "content/public/common/ns_view_bridge_factory.mojom.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
+#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/views/cocoa/bridge_factory_impl.h"
@@ -131,6 +134,8 @@
   void LaunchAppDone(apps::AppShimLaunchResult result) override;
   void CreateViewsBridgeFactory(
       views_bridge_mac::mojom::BridgeFactoryRequest request) override;
+  void CreateContentNSViewBridgeFactory(
+      content::mojom::NSViewBridgeFactoryAssociatedRequest request) override;
   void Hide() override;
   void UnhideWithoutActivation() override;
   void SetUserAttention(apps::AppShimAttentionType attention_type) override;
@@ -313,6 +318,11 @@
   views_bridge_mac::BridgeFactoryImpl::Get()->BindRequest(std::move(request));
 }
 
+void AppShimController::CreateContentNSViewBridgeFactory(
+    content::mojom::NSViewBridgeFactoryAssociatedRequest request) {
+  content::NSViewBridgeFactoryImpl::Get()->BindRequest(std::move(request));
+}
+
 void AppShimController::Hide() {
   [NSApp hide:nil];
 }
@@ -650,6 +660,7 @@
   AppShimController controller;
   base::MessageLoopForUI main_message_loop;
   base::PlatformThread::SetName("CrAppShimMain");
+  ui::WindowResizeHelperMac::Get()->Init(base::ThreadTaskRunnerHandle::Get());
 
   // In tests, launching Chrome does nothing, and we won't get a ping response,
   // so just assume the socket exists.
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 7b601373..f54ffe17 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2927,6 +2927,8 @@
       "status_icons/desktop_notification_balloon.h",
       "sync/glue/extension_data_type_controller.cc",
       "sync/glue/extension_data_type_controller.h",
+      "sync/glue/extension_model_type_controller.cc",
+      "sync/glue/extension_model_type_controller.h",
       "sync/glue/extension_setting_data_type_controller.cc",
       "sync/glue/extension_setting_data_type_controller.h",
       "sync/glue/theme_data_type_controller.cc",
@@ -3973,12 +3975,13 @@
     deps += [ "//media/mojo/interfaces" ]
 
     if (enable_widevine) {
-      # TODO(crbug.com/349182): Also check WIDEVINE_CDM_IS_COMPONENT.
-      sources += [
-        "component_updater/widevine_cdm_component_installer.cc",
-        "component_updater/widevine_cdm_component_installer.h",
-      ]
-      deps += [ "//third_party/widevine/cdm:headers" ]
+      if (enable_widevine_cdm_component) {
+        sources += [
+          "component_updater/widevine_cdm_component_installer.cc",
+          "component_updater/widevine_cdm_component_installer.h",
+        ]
+        deps += [ "//third_party/widevine/cdm:headers" ]
+      }
       if (is_win) {
         sources += [
           "media/widevine_hardware_caps_win.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 7793cb5d..5ce5910 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -215,11 +215,8 @@
 
 specific_include_rules = {
   "ash_service_registry\.cc": [
-    # The following are needed for classic mode to create the WindowService.
-    # Once Ash runs out of process no longer needed.
-    "+ash/shell.h",
-
     # Needed for classic mode.
+    "+ash/accessibility/ax_host_service.h",
     "+ash/ash_service.h",
   ],
   # TODO(mash): Fix. https://crbug.com/768439, https://crbug.com/768395.
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index da2e0dc..d770b45 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3298,11 +3298,6 @@
 #endif  // OS_CHROMEOS
 
 #if defined(OS_CHROMEOS)
-    {"enable-zip-archiver-packer",
-     flag_descriptions::kEnableZipArchiverPackerName,
-     flag_descriptions::kEnableZipArchiverPackerDescription, kOsCrOS,
-     ENABLE_DISABLE_VALUE_TYPE(chromeos::switches::kEnableZipArchiverPacker,
-                               chromeos::switches::kDisableZipArchiverPacker)},
     {"enable-zip-archiver-unpacker",
      flag_descriptions::kZipArchiverUnpackerName,
      flag_descriptions::kZipArchiverUnpackerDescription, kOsCrOS,
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index ca13abc..b217fbc40 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -87,6 +87,11 @@
       AttachCurrentThread(), java_autofill_assistant_ui_controller_);
 }
 
+void UiControllerAndroid::Shutdown() {
+  Java_AutofillAssistantUiController_onShutdown(
+      AttachCurrentThread(), java_autofill_assistant_ui_controller_);
+}
+
 void UiControllerAndroid::UpdateScripts(
     const std::vector<ScriptHandle>& scripts) {
   std::vector<std::string> script_paths;
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 3e04ded..df4a9d7 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -30,6 +30,7 @@
   void ShowStatusMessage(const std::string& message) override;
   void ShowOverlay() override;
   void HideOverlay() override;
+  void Shutdown() override;
   void UpdateScripts(const std::vector<ScriptHandle>& scripts) override;
   void ChooseAddress(
       base::OnceCallback<void(const std::string&)> callback) override;
diff --git a/chrome/browser/android/download/video_frame_thumbnail_converter.cc b/chrome/browser/android/download/video_frame_thumbnail_converter.cc
index 0b38ed6..208c34e 100644
--- a/chrome/browser/android/download/video_frame_thumbnail_converter.cc
+++ b/chrome/browser/android/download/video_frame_thumbnail_converter.cc
@@ -38,8 +38,7 @@
     cc::SkiaPaintCanvas canvas(skbitmap);
     renderer.Copy(frame, &canvas,
                   media::Context3D(context_provider_->ContextGL(),
-                                   context_provider_->GrContext()),
-                  context_provider_->ContextSupport());
+                                   context_provider_->GrContext()));
 
     std::move(pixel_callback).Run(true, std::move(skbitmap));
   }
diff --git a/chrome/browser/apps/app_shim/app_shim_host_mac.cc b/chrome/browser/apps/app_shim/app_shim_host_mac.cc
index adbe53af..5a148b2 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_mac.cc
+++ b/chrome/browser/apps/app_shim/app_shim_host_mac.cc
@@ -11,7 +11,10 @@
 #include "base/logging.h"
 #include "chrome/browser/apps/app_shim/app_shim_handler_mac.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/ns_view_bridge_factory_host.h"
+#include "content/public/common/ns_view_bridge_factory.mojom.h"
 #include "ui/base/ui_base_features.h"
+#include "ui/views/cocoa/bridge_factory_host.h"
 #include "ui/views_bridge_mac/mojo/bridge_factory.mojom.h"
 
 AppShimHost::AppShimHost()
@@ -65,14 +68,25 @@
     return;
 
   app_shim_ = std::move(app_shim_ptr);
-  // Create the interface that will be used by views::NativeWidgetMac to create
-  // NSWindows hosted in the app shim process.
   if (features::HostWindowsInAppShimProcess()) {
+    // Create the interface that will be used by views::NativeWidgetMac to
+    // create NSWindows hosted in the app shim process.
     views_bridge_mac::mojom::BridgeFactoryRequest views_bridge_factory_request;
     views_bridge_factory_host_ = std::make_unique<views::BridgeFactoryHost>(
         &views_bridge_factory_request);
     app_shim_->CreateViewsBridgeFactory(
         std::move(views_bridge_factory_request));
+
+    // Create the interface that will be used content::RenderWidgetHostView to
+    // create NSViews hosted in the app shim process.
+    content::mojom::NSViewBridgeFactoryAssociatedRequest
+        content_bridge_factory_request;
+    content_bridge_factory_ =
+        std::make_unique<content::NSViewBridgeFactoryHost>(
+            &content_bridge_factory_request,
+            views_bridge_factory_host_->GetHostId());
+    app_shim_->CreateContentNSViewBridgeFactory(
+        std::move(content_bridge_factory_request));
   }
   profile_path_ = profile_dir;
   app_id_ = app_id;
diff --git a/chrome/browser/apps/app_shim/app_shim_host_mac.h b/chrome/browser/apps/app_shim/app_shim_host_mac.h
index 6a45f791..73a76d1 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_mac.h
+++ b/chrome/browser/apps/app_shim/app_shim_host_mac.h
@@ -16,7 +16,14 @@
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
-#include "ui/views/cocoa/bridge_factory_host.h"
+
+namespace content {
+class NSViewBridgeFactoryHost;
+}  // namespace content
+
+namespace views {
+class BridgeFactoryHost;
+}  // namespace views
 
 // This is the counterpart to AppShimController in
 // chrome/app/chrome_main_app_mode_mac.mm. The AppShimHost owns itself, and is
@@ -64,6 +71,7 @@
   mojo::IsolatedConnection mojo_connection_;
   chrome::mojom::AppShimPtr app_shim_;
   std::unique_ptr<views::BridgeFactoryHost> views_bridge_factory_host_;
+  std::unique_ptr<content::NSViewBridgeFactoryHost> content_bridge_factory_;
   mojo::Binding<chrome::mojom::AppShimHost> host_binding_;
   std::string app_id_;
   base::FilePath profile_path_;
diff --git a/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc b/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc
index 9e4984ef..5dec953 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc
+++ b/chrome/browser/apps/app_shim/app_shim_host_mac_unittest.cc
@@ -10,6 +10,8 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/test/test_simple_task_runner.h"
 #include "chrome/common/mac/app_shim_param_traits.h"
 #include "ipc/ipc_message.h"
@@ -46,6 +48,8 @@
   }
   void CreateViewsBridgeFactory(
       views_bridge_mac::mojom::BridgeFactoryRequest request) override {}
+  void CreateContentNSViewBridgeFactory(
+      content::mojom::NSViewBridgeFactoryAssociatedRequest request) override {}
   void Hide() override {}
   void UnhideWithoutActivation() override {}
   void SetUserAttention(apps::AppShimAttentionType attention_type) override {}
@@ -83,9 +87,7 @@
 class AppShimHostTest : public testing::Test,
                         public apps::AppShimHandler {
  public:
-  AppShimHostTest()
-      : task_runner_(new base::TestSimpleTaskRunner),
-        task_runner_handle_(task_runner_) {}
+  AppShimHostTest() { task_runner_ = base::ThreadTaskRunnerHandle::Get(); }
 
   ~AppShimHostTest() override {
     if (host_)
@@ -93,7 +95,8 @@
     DCHECK(!host_);
   }
 
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner() {
+  void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner() {
     return task_runner_;
   }
   TestingAppShimHost* host() { return host_.get(); }
@@ -106,7 +109,7 @@
   }
 
   apps::AppShimLaunchResult GetLaunchResult() {
-    task_runner_->RunUntilIdle();
+    RunUntilIdle();
     return shim_->GetLaunchResult();
   }
 
@@ -149,8 +152,8 @@
     host_ = host->GetWeakPtr();
   }
 
-  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
-  base::ThreadTaskRunnerHandle task_runner_handle_;
+  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
 
   std::unique_ptr<TestingAppShim> shim_;
 
@@ -182,15 +185,15 @@
 
   GetMojoHost()->FocusApp(apps::APP_SHIM_FOCUS_NORMAL,
                           std::vector<base::FilePath>());
-  task_runner()->RunUntilIdle();
+  RunUntilIdle();
   EXPECT_EQ(1, focus_count_);
 
   GetMojoHost()->QuitApp();
-  task_runner()->RunUntilIdle();
+  RunUntilIdle();
   EXPECT_EQ(1, quit_count_);
 
   SimulateDisconnect();
-  task_runner()->RunUntilIdle();
+  RunUntilIdle();
   EXPECT_EQ(1, close_count_);
   EXPECT_EQ(nullptr, host());
   apps::AppShimHandler::RemoveHandler(kTestAppId);
diff --git a/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm b/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm
index 89ab8ca..958be60 100644
--- a/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm
+++ b/chrome/browser/apps/app_shim/app_shim_host_manager_browsertest_mac.mm
@@ -51,6 +51,8 @@
   void LaunchAppDone(apps::AppShimLaunchResult result) override {}
   void CreateViewsBridgeFactory(
       views_bridge_mac::mojom::BridgeFactoryRequest request) override {}
+  void CreateContentNSViewBridgeFactory(
+      content::mojom::NSViewBridgeFactoryAssociatedRequest request) override {}
   void Hide() override {}
   void UnhideWithoutActivation() override {}
   void SetUserAttention(apps::AppShimAttentionType attention_type) override {}
diff --git a/chrome/browser/ash_service_registry.cc b/chrome/browser/ash_service_registry.cc
index 0a694395..25d15c5 100644
--- a/chrome/browser/ash_service_registry.cc
+++ b/chrome/browser/ash_service_registry.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ash_service_registry.h"
 
+#include "ash/accessibility/ax_host_service.h"
 #include "ash/ash_service.h"
 #include "ash/components/quick_launch/public/mojom/constants.mojom.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
@@ -15,7 +16,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
-#include "chrome/browser/chromeos/accessibility/ax_host_service.h"
 #include "chrome/browser/chromeos/prefs/pref_connector_service.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/common/service_manager_connection.h"
@@ -84,20 +84,20 @@
     (*services)[ash::mojom::kPrefConnectorServiceName] = info;
   }
 
+  if (features::IsMultiProcessMash())
+    return;
+
   {
     // Register the accessibility host service.
     service_manager::EmbeddedServiceInfo info;
     info.task_runner = base::ThreadTaskRunnerHandle::Get();
     info.factory =
         base::BindRepeating([]() -> std::unique_ptr<service_manager::Service> {
-          return std::make_unique<AXHostService>();
+          return std::make_unique<ash::AXHostService>();
         });
     (*services)[ax::mojom::kAXHostServiceName] = info;
   }
 
-  if (features::IsMultiProcessMash())
-    return;
-
   (*services)[ash::mojom::kServiceName] =
       ash::AshService::CreateEmbeddedServiceInfo();
 }
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 341a5366..93496353 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -66,7 +66,6 @@
 #include "chrome/browser/component_updater/sth_set_component_installer.h"
 #include "chrome/browser/component_updater/subresource_filter_component_installer.h"
 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
-#include "chrome/browser/component_updater/widevine_cdm_component_installer.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
@@ -306,6 +305,10 @@
 #include "chrome/browser/vr/service/vr_service_impl.h"
 #endif
 
+#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
+#include "chrome/browser/component_updater/widevine_cdm_component_installer.h"
+#endif  // BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
+
 #if defined(USE_AURA)
 #include "services/service_manager/runner/common/client_util.h"
 #include "ui/aura/env.h"
@@ -549,9 +552,9 @@
   RegisterPepperFlashComponent(cus);
 #endif  // !defined(OS_ANDROID)
 
-#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
   RegisterWidevineCdmComponent(cus);
-#endif  // BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#endif  // BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
 
 #if BUILDFLAG(ENABLE_NACL) && !defined(OS_ANDROID)
 #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 0a8dc3a..6004ff7 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -92,6 +92,8 @@
     "//chromeos/components/proximity_auth/logging",
     "//chromeos/components/tether",
     "//chromeos/services/device_sync/public/cpp",
+    "//chromeos/services/machine_learning/public/cpp",
+    "//chromeos/services/machine_learning/public/mojom",
     "//chromeos/services/multidevice_setup/public/cpp",
     "//chromeos/services/multidevice_setup/public/cpp:android_sms_app_helper_delegate",
     "//chromeos/services/multidevice_setup/public/cpp:android_sms_pairing_state_tracker",
@@ -263,8 +265,6 @@
     "accessibility/accessibility_manager.h",
     "accessibility/accessibility_panel.cc",
     "accessibility/accessibility_panel.h",
-    "accessibility/ax_host_service.cc",
-    "accessibility/ax_host_service.h",
     "accessibility/chromevox_panel.cc",
     "accessibility/chromevox_panel.h",
     "accessibility/dictation_chromeos.cc",
@@ -1841,6 +1841,8 @@
     # Extension API implementations.
     "extensions/arc_apps_private_api.cc",
     "extensions/arc_apps_private_api.h",
+    "extensions/autotest_private/autotest_private_api.cc",
+    "extensions/autotest_private/autotest_private_api.h",
     "extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.cc",
     "extensions/backdrop_wallpaper_handlers/backdrop_wallpaper_handlers.h",
     "extensions/echo_private_api.cc",
@@ -2019,7 +2021,6 @@
     "../policy/default_geolocation_policy_handler_unittest.cc",
     "../resources/chromeos/zip_archiver/test/char_coding_test.cc",
     "../ui/browser_finder_chromeos_unittest.cc",
-    "accessibility/ax_host_service_unittest.cc",
     "app_mode/startup_app_launcher_unittest.cc",
     "apps/intent_helper/apps_navigation_throttle_unittest.cc",
     "apps/intent_helper/page_transition_util_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/arc_util.cc b/chrome/browser/chromeos/arc/arc_util.cc
index f1b3952e..7bdf54a5 100644
--- a/chrome/browser/chromeos/arc/arc_util.cc
+++ b/chrome/browser/chromeos/arc/arc_util.cc
@@ -686,4 +686,10 @@
   return supervision_transition;
 }
 
+bool IsPlayStoreAvailable() {
+  return (!IsRobotOrOfflineDemoAccountMode() ||
+          chromeos::DemoSession::IsDeviceInDemoMode()) &&
+         !ShouldArcAlwaysStartWithNoPlayStore();
+}
+
 }  // namespace arc
diff --git a/chrome/browser/chromeos/arc/arc_util.h b/chrome/browser/chromeos/arc/arc_util.h
index 590ac4c..6e6f117 100644
--- a/chrome/browser/chromeos/arc/arc_util.h
+++ b/chrome/browser/chromeos/arc/arc_util.h
@@ -9,6 +9,7 @@
 
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/callback_forward.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "components/arc/arc_supervision_transition.h"
 
 // Most utility should be put in components/arc/arc_util.{h,cc}, rather than
@@ -160,6 +161,10 @@
 // Returns the supervision transition status as stored in profile prefs.
 ArcSupervisionTransition GetSupervisionTransition(const Profile* profile);
 
+// Returns true if Play Store package is present and can be launched in this
+// session.
+bool IsPlayStoreAvailable();
+
 }  // namespace arc
 
 #endif  // CHROME_BROWSER_CHROMEOS_ARC_ARC_UTIL_H_
diff --git a/chrome/browser/chromeos/arc/arc_util_unittest.cc b/chrome/browser/chromeos/arc/arc_util_unittest.cc
index c80a382..d752835 100644
--- a/chrome/browser/chromeos/arc/arc_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/arc_util_unittest.cc
@@ -838,6 +838,40 @@
   EXPECT_FALSE(IsArcStatsReportingEnabled());
 }
 
+TEST_F(ChromeArcUtilTest, ArcStartModeDefault) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv({"", "--arc-availability=installed"});
+  EXPECT_TRUE(IsPlayStoreAvailable());
+}
+
+TEST_F(ChromeArcUtilTest, ArcStartModeDefaultPublicSession) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv({"", "--arc-availability=installed"});
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmail("public_user@gmail.com"),
+                    user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  EXPECT_FALSE(IsPlayStoreAvailable());
+}
+
+TEST_F(ChromeArcUtilTest, ArcStartModeDefaultDemoMode) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv({"", "--arc-availability=installed"});
+  chromeos::DemoSession::SetDemoConfigForTesting(
+      chromeos::DemoSession::DemoModeConfig::kOnline);
+  ScopedLogIn login(GetFakeUserManager(),
+                    AccountId::FromUserEmail("public_user@gmail.com"),
+                    user_manager::USER_TYPE_PUBLIC_ACCOUNT);
+  EXPECT_TRUE(IsPlayStoreAvailable());
+}
+
+TEST_F(ChromeArcUtilTest, ArcStartModeWithoutPlayStore) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(
+      {"", "--arc-availability=installed",
+       "--arc-start-mode=always-start-with-no-play-store"});
+  EXPECT_FALSE(IsPlayStoreAvailable());
+}
+
 using ArcMigrationTest = ChromeArcUtilTest;
 
 TEST_F(ArcMigrationTest, IsMigrationAllowedUnmanagedUser) {
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc
index c85555b..fb0db06 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.cc
@@ -234,8 +234,7 @@
   for (const auto& mapping : kAndroidMimeTypeMappings) {
     std::vector<base::StringPiece> extensions = base::SplitStringPiece(
         mapping.extensions, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-    auto iter = std::find(extensions.begin(), extensions.end(), ext);
-    if (iter != extensions.end())
+    if (base::ContainsValue(extensions, ext))
       return mapping.mime_type;
   }
   return std::string();
diff --git a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
index 3e2bb22..c45b34b 100644
--- a/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
+++ b/chrome/browser/chromeos/arc/input_method_manager/arc_input_method_manager_service_unittest.cc
@@ -107,9 +107,7 @@
 
     bool IsInputMethodAllowed(const std::string& ime_id) {
       return allowed_input_methods_.empty() ||
-             (std::find(allowed_input_methods_.begin(),
-                        allowed_input_methods_.end(),
-                        ime_id) != allowed_input_methods_.end());
+             base::ContainsValue(allowed_input_methods_, ime_id);
     }
 
     std::vector<std::tuple<std::string, im::InputMethodDescriptors>>
diff --git a/chrome/browser/extensions/api/autotest_private/DEPS b/chrome/browser/chromeos/extensions/autotest_private/DEPS
similarity index 100%
rename from chrome/browser/extensions/api/autotest_private/DEPS
rename to chrome/browser/chromeos/extensions/autotest_private/DEPS
diff --git a/chrome/browser/chromeos/extensions/autotest_private/OWNERS b/chrome/browser/chromeos/extensions/autotest_private/OWNERS
new file mode 100644
index 0000000..c345b9b
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/autotest_private/OWNERS
@@ -0,0 +1,3 @@
+achuith@chromium.org
+derat@chromium.org
+stevenjb@chromium.org
diff --git a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
similarity index 97%
rename from chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
rename to chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index e10ed44..189fb40 100644
--- a/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/extensions/api/autotest_private/autotest_private_api.h"
+#include "chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h"
 
 #include <memory>
 #include <sstream>
@@ -75,8 +75,7 @@
 
   auto permissions = std::make_unique<base::ListValue>();
   for (URLPatternSet::const_iterator perm = pattern_set.begin();
-       perm != pattern_set.end();
-       ++perm) {
+       perm != pattern_set.end(); ++perm) {
     permissions->AppendString(perm->GetAsString());
   }
 
@@ -241,8 +240,9 @@
 ExtensionFunction::ResponseAction AutotestPrivateLockScreenFunction::Run() {
   DVLOG(1) << "AutotestPrivateLockScreenFunction";
 #if defined(OS_CHROMEOS)
-  chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
-      RequestLockScreen();
+  chromeos::DBusThreadManager::Get()
+      ->GetSessionManagerClient()
+      ->RequestLockScreen();
 #endif
   return RespondNow(NoArguments());
 }
@@ -266,8 +266,7 @@
   ExtensionList all;
   all.insert(all.end(), extensions.begin(), extensions.end());
   all.insert(all.end(), disabled_extensions.begin(), disabled_extensions.end());
-  for (ExtensionList::const_iterator it = all.begin();
-       it != all.end(); ++it) {
+  for (ExtensionList::const_iterator it = all.begin(); it != all.end(); ++it) {
     const Extension* extension = it->get();
     std::string id = extension->id();
     std::unique_ptr<base::DictionaryValue> extension_value(
@@ -289,13 +288,11 @@
     extension_value->Set("apiPermissions", GetAPIPermissions(extension));
 
     Manifest::Location location = extension->location();
-    extension_value->SetBoolean("isComponent",
-                                location == Manifest::COMPONENT);
-    extension_value->SetBoolean("isInternal",
-                                location == Manifest::INTERNAL);
+    extension_value->SetBoolean("isComponent", location == Manifest::COMPONENT);
+    extension_value->SetBoolean("isInternal", location == Manifest::INTERNAL);
     extension_value->SetBoolean("isUserInstalled",
-        location == Manifest::INTERNAL ||
-        Manifest::IsUnpackedLocation(location));
+                                location == Manifest::INTERNAL ||
+                                    Manifest::IsUnpackedLocation(location));
     extension_value->SetBoolean("isEnabled", service->IsExtensionEnabled(id));
     extension_value->SetBoolean(
         "allowedInIncognito", util::IsIncognitoEnabled(id, browser_context()));
@@ -312,7 +309,7 @@
   return RespondNow(OneArgument(std::move(return_value)));
 }
 
-static int AccessArray(const volatile int arr[], const volatile int *index) {
+static int AccessArray(const volatile int arr[], const volatile int* index) {
   return arr[*index];
 }
 
@@ -904,10 +901,8 @@
   return new AutotestPrivateAPI();
 }
 
-AutotestPrivateAPI::AutotestPrivateAPI() : test_mode_(false) {
-}
+AutotestPrivateAPI::AutotestPrivateAPI() : test_mode_(false) {}
 
-AutotestPrivateAPI::~AutotestPrivateAPI() {
-}
+AutotestPrivateAPI::~AutotestPrivateAPI() {}
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
similarity index 96%
rename from chrome/browser/extensions/api/autotest_private/autotest_private_api.h
rename to chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index ff073d0..11f38f5 100644
--- a/chrome/browser/extensions/api/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_EXTENSIONS_API_AUTOTEST_PRIVATE_AUTOTEST_PRIVATE_API_H_
-#define CHROME_BROWSER_EXTENSIONS_API_AUTOTEST_PRIVATE_AUTOTEST_PRIVATE_API_H_
+#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_AUTOTEST_PRIVATE_AUTOTEST_PRIVATE_API_H_
+#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_AUTOTEST_PRIVATE_AUTOTEST_PRIVATE_API_H_
 
 #include <string>
 
@@ -382,7 +382,7 @@
 class AutotestPrivateAPI : public BrowserContextKeyedAPI {
  public:
   static BrowserContextKeyedAPIFactory<AutotestPrivateAPI>*
-      GetFactoryInstance();
+  GetFactoryInstance();
 
   // TODO(achuith): Replace these with a mock object for system calls.
   bool test_mode() const { return test_mode_; }
@@ -404,9 +404,9 @@
 
 template <>
 KeyedService*
-    BrowserContextKeyedAPIFactory<AutotestPrivateAPI>::BuildServiceInstanceFor(
-        content::BrowserContext* context) const;
+BrowserContextKeyedAPIFactory<AutotestPrivateAPI>::BuildServiceInstanceFor(
+    content::BrowserContext* context) const;
 
 }  // namespace extensions
 
-#endif  // CHROME_BROWSER_EXTENSIONS_API_AUTOTEST_PRIVATE_AUTOTEST_PRIVATE_API_H_
+#endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_AUTOTEST_PRIVATE_AUTOTEST_PRIVATE_API_H_
diff --git a/chrome/browser/extensions/api/autotest_private/autotest_private_apitest.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
similarity index 93%
rename from chrome/browser/extensions/api/autotest_private/autotest_private_apitest.cc
rename to chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
index 5db6970e..e1a8cfce 100644
--- a/chrome/browser/extensions/api/autotest_private/autotest_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc
@@ -6,7 +6,7 @@
 
 #include "build/build_config.h"
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
-#include "chrome/browser/extensions/api/autotest_private/autotest_private_api.h"
+#include "chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "components/arc/arc_util.h"
 
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index c9473e2a..1f69cc1 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -86,7 +86,6 @@
   bool tablet_mode = false;
   bool enable_drivefs = false;
   bool with_browser = false;
-  bool needs_zip = false;
   bool offline = false;
 };
 
@@ -97,13 +96,6 @@
   }
 };
 
-// ZipCase: FilesAppBrowserTest with zip/unzip support.
-struct ZipCase : public TestCase {
-  explicit ZipCase(const char* name) : TestCase(name) {
-    needs_zip = true;
-  }
-};
-
 // FilesApp browser test.
 class FilesAppBrowserTest : public FileManagerBrowserTestBase,
                             public ::testing::WithParamInterface<TestCase> {
@@ -155,8 +147,6 @@
     return GetParam().with_browser;
   }
 
-  bool GetNeedsZipSupport() const override { return GetParam().needs_zip; }
-
   bool GetIsOffline() const override { return GetParam().offline; }
 
  private:
@@ -245,16 +235,16 @@
 WRAPPED_INSTANTIATE_TEST_CASE_P(
     MAYBE_ZipFiles, /* zip_files.js */
     FilesAppBrowserTest,
-    ::testing::Values(ZipCase("zipFileOpenDownloads").InGuestMode(),
-                      ZipCase("zipFileOpenDownloads"),
-                      ZipCase("zipFileOpenDrive").EnableDriveFs(),
-                      ZipCase("zipFileOpenDrive"),
-                      ZipCase("zipFileOpenUsb"),
-                      ZipCase("zipCreateFileDownloads").InGuestMode(),
-                      ZipCase("zipCreateFileDownloads"),
-                      ZipCase("zipCreateFileDrive").EnableDriveFs(),
-                      ZipCase("zipCreateFileDrive"),
-                      ZipCase("zipCreateFileUsb")));
+    ::testing::Values(TestCase("zipFileOpenDownloads").InGuestMode(),
+                      TestCase("zipFileOpenDownloads"),
+                      TestCase("zipFileOpenDrive").EnableDriveFs(),
+                      TestCase("zipFileOpenDrive"),
+                      TestCase("zipFileOpenUsb"),
+                      TestCase("zipCreateFileDownloads").InGuestMode(),
+                      TestCase("zipCreateFileDownloads"),
+                      TestCase("zipCreateFileDrive").EnableDriveFs(),
+                      TestCase("zipCreateFileDrive"),
+                      TestCase("zipCreateFileUsb")));
 
 WRAPPED_INSTANTIATE_TEST_CASE_P(
     CreateNewFolder, /* create_new_folder.js */
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 9f12a8e..b3a7a0ef 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -116,7 +116,7 @@
   enum EntryType { FILE, DIRECTORY, TEAM_DRIVE };
 
   // Represents whether an entry appears in 'Share with Me' or not.
-  enum SharedOption { NONE, SHARED };
+  enum SharedOption { NONE, SHARED, SHARED_WITH_ME, NESTED_SHARED_WITH_ME };
 
   // The actual AddEntriesMessage contents.
 
@@ -278,6 +278,10 @@
                                         SharedOption* option) {
       if (value == "shared")
         *option = SHARED;
+      else if (value == "sharedWithMe")
+        *option = SHARED_WITH_ME;
+      else if (value == "nestedSharedWithMe")
+        *option = NESTED_SHARED_WITH_ME;
       else if (value == "none")
         *option = NONE;
       else
@@ -685,12 +689,16 @@
       case AddEntriesMessage::FILE:
         CreateFile(entry.source_file_name, parent_entry->resource_id(),
                    target_name, entry.mime_type,
-                   entry.shared_option == AddEntriesMessage::SHARED,
+                   entry.shared_option == AddEntriesMessage::SHARED ||
+                       entry.shared_option == AddEntriesMessage::SHARED_WITH_ME,
                    entry.last_modified_time, file_capabilities);
         break;
       case AddEntriesMessage::DIRECTORY:
-        CreateDirectory(parent_entry->resource_id(), target_name,
-                        entry.last_modified_time, file_capabilities);
+        CreateDirectory(
+            parent_entry->resource_id(), target_name, entry.last_modified_time,
+            entry.shared_option == AddEntriesMessage::SHARED ||
+                entry.shared_option == AddEntriesMessage::SHARED_WITH_ME,
+            file_capabilities);
         break;
       case AddEntriesMessage::TEAM_DRIVE:
         CreateTeamDrive(entry.team_drive_name, team_drive_capabilities);
@@ -716,6 +724,7 @@
       const std::string& parent_id,
       const std::string& target_name,
       const base::Time& modification_time,
+      bool shared_with_me,
       const google_apis::FileResourceCapabilities& capabilities) {
     google_apis::DriveApiErrorCode error = google_apis::DRIVE_OTHER_ERROR;
 
@@ -740,6 +749,11 @@
     base::RunLoop().RunUntilIdle();
     ASSERT_TRUE(error == google_apis::HTTP_SUCCESS);
     ASSERT_TRUE(entry);
+
+    if (shared_with_me) {
+      ASSERT_EQ(google_apis::HTTP_SUCCESS,
+                fake_drive_service_->SetFileAsSharedWithMe(entry->file_id()));
+    }
   }
 
   // Creates a test file with the given spec.
@@ -837,13 +851,15 @@
     const base::FilePath target_path = GetTargetPathForTestEntry(entry);
 
     entries_.insert(std::make_pair(target_path, entry));
+    fake_drivefs_->SetMetadata(
+        GetRelativeDrivePathForTestEntry(entry), entry.mime_type,
+        base::FilePath(entry.target_path).BaseName().value(), entry.pinned,
+        entry.shared_option == AddEntriesMessage::SharedOption::SHARED ||
+            entry.shared_option ==
+                AddEntriesMessage::SharedOption::SHARED_WITH_ME);
+
     switch (entry.type) {
       case AddEntriesMessage::FILE: {
-        fake_drivefs_->SetMetadata(
-            GetRelativeDrivePathForTestEntry(entry), entry.mime_type,
-            base::FilePath(entry.target_path).BaseName().value(), entry.pinned,
-            entry.shared_option == AddEntriesMessage::SharedOption::SHARED);
-
         if (entry.source_file_name.empty()) {
           ASSERT_EQ(0, base::WriteFile(target_path, "", 0));
           break;
@@ -908,7 +924,8 @@
     // Update the modified time of parent directories because they may be
     // also affected by the update of child items.
     if (path.DirName() != GetTeamDriveGrandRoot() &&
-        path.DirName() != GetMyDrivePath()) {
+        path.DirName() != GetMyDrivePath() &&
+        path.DirName() != GetSharedWithMePath()) {
       const auto it = entries_.find(path.DirName());
       if (it == entries_.end())
         return false;
@@ -921,14 +938,24 @@
   base::FilePath GetTargetPathForTestEntry(
       const AddEntriesMessage::TestEntryInfo& entry) {
     const base::FilePath target_path =
-        entry.team_drive_name.empty()
-            ? GetMyDrivePath().Append(entry.target_path)
-            : GetTeamDrivePath(entry.team_drive_name).Append(entry.target_path);
+        GetTargetBasePathForTestEntry(entry).Append(entry.target_path);
     if (entry.name_text != entry.target_path)
       return target_path.DirName().Append(entry.name_text);
     return target_path;
   }
 
+  base::FilePath GetTargetBasePathForTestEntry(
+      const AddEntriesMessage::TestEntryInfo& entry) {
+    if (entry.shared_option == AddEntriesMessage::SHARED_WITH_ME ||
+        entry.shared_option == AddEntriesMessage::NESTED_SHARED_WITH_ME) {
+      return GetSharedWithMePath();
+    }
+    if (!entry.team_drive_name.empty()) {
+      return GetTeamDrivePath(entry.team_drive_name);
+    }
+    return GetMyDrivePath();
+  }
+
   base::FilePath GetRelativeDrivePathForTestEntry(
       const AddEntriesMessage::TestEntryInfo& entry) {
     const base::FilePath target_path = GetTargetPathForTestEntry(entry);
@@ -945,6 +972,10 @@
     return mount_path().Append("team_drives");
   }
 
+  base::FilePath GetSharedWithMePath() {
+    return mount_path().Append(".files-by-id/123");
+  }
+
   base::FilePath GetTeamDrivePath(const std::string& team_drive_name) {
     return GetTeamDriveGrandRoot().Append(team_drive_name);
   }
@@ -1125,10 +1156,6 @@
   return false;
 }
 
-bool FileManagerBrowserTestBase::GetNeedsZipSupport() const {
-  return false;
-}
-
 bool FileManagerBrowserTestBase::GetIsOffline() const {
   return false;
 }
@@ -1208,6 +1235,11 @@
     return;
   }
 
+  if (name == "getDriveFsEnabled") {
+    *output = IsDriveFsTest() ? "true" : "false";
+    return;
+  }
+
   if (name == "getRootPaths") {
     // Obtain the root paths.
     const auto downloads_root = util::GetDownloadsMountPointName(profile());
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
index 3ad34f1b..93f3997 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -50,7 +50,6 @@
   virtual bool GetTabletMode() const;
   virtual bool GetEnableDriveFs() const;
   virtual bool GetRequiresStartupBrowser() const;
-  virtual bool GetNeedsZipSupport() const;
   virtual bool GetIsOffline() const;
 
   // Launches the test extension from GetTestExtensionManifestName() and uses
@@ -71,9 +70,6 @@
   // Returns true if the test requires DriveFS.
   bool IsDriveFsTest() const { return GetEnableDriveFs(); }
 
-  // Returns true if the test requires zip/unzip support.
-  bool IsZipTest() const { return GetNeedsZipSupport(); }
-
   // Returns true if Drive should act as if offline.
   bool IsOfflineTest() const { return GetIsOffline(); }
 
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_session.cc b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
index 4524a6d..e3e152f 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_session.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_session.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_util.h"
 #include "base/optional.h"
 #include "base/path_service.h"
+#include "base/stl_util.h"
 #include "base/sys_info.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/apps/platform_apps/app_load_service.h"
@@ -174,6 +175,11 @@
   bool is_demo_mode = is_demo_device_mode || is_demo_device_domain;
 
   const PrefService* prefs = g_browser_process->local_state();
+
+  // The testing browser process might not have local state.
+  if (!prefs)
+    return DemoModeConfig::kNone;
+
   // Demo mode config preference is set at the end of the demo setup after
   // device is enrolled.
   auto demo_config = DemoModeConfig::kNone;
@@ -337,9 +343,8 @@
   if (!net::NetworkChangeNotifier::IsOffline())
     return false;
 
-  return std::find(ignore_pin_policy_offline_apps_.begin(),
-                   ignore_pin_policy_offline_apps_.end(),
-                   app_id_or_package) != ignore_pin_policy_offline_apps_.end();
+  return base::ContainsValue(ignore_pin_policy_offline_apps_,
+                             app_id_or_package);
 }
 
 void DemoSession::SetExtensionsExternalLoader(
diff --git a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
index 0478e8f..ff9e7a5 100644
--- a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
+#include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/update_engine_client.h"
 #include "content/public/browser/notification_service.h"
 
@@ -65,6 +66,12 @@
   OobeInteractiveUITest() = default;
   ~OobeInteractiveUITest() override = default;
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kEnableMarketingOptInScreen);
+
+    OobeBaseTest::SetUpCommandLine(command_line);
+  }
+
   void TearDownOnMainThread() override {
     // If the login display is still showing, exit gracefully.
     if (LoginDisplayHost::default_host()) {
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 170bf91..a3bbd1e6 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -705,7 +705,9 @@
 
 void WizardController::ShowMarketingOptInScreen() {
   // Skip the screen for public sessions and non-regular ephemeral users.
-  if (IsPublicSessionOrEphemeralLogin()) {
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          chromeos::switches::kEnableMarketingOptInScreen) ||
+      IsPublicSessionOrEphemeralLogin()) {
     OnMarketingOptInFinished();
     return;
   }
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
index dca801b..83dc69bb 100644
--- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc
+++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -46,8 +46,8 @@
 #include "third_party/widevine/cdm/buildflags.h"
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
 
-#if !BUILDFLAG(ENABLE_WIDEVINE)
-#error This file should only be compiled when Widevine is enabled
+#if !BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
+#error This file should only be compiled when Widevine CDM component is enabled
 #endif
 
 using content::BrowserThread;
@@ -55,8 +55,6 @@
 
 namespace component_updater {
 
-#if defined(WIDEVINE_CDM_IS_COMPONENT)
-
 namespace {
 
 // CRX hash. The extension id is: oimompecagnajdejgnnjijobebaeigek.
@@ -515,14 +513,10 @@
                          absolute_cdm_install_dir, base::Passed(&manifest)));
 }
 
-#endif  // defined(WIDEVINE_CDM_IS_COMPONENT)
-
 void RegisterWidevineCdmComponent(ComponentUpdateService* cus) {
-#if defined(WIDEVINE_CDM_IS_COMPONENT)
   auto installer = base::MakeRefCounted<ComponentInstaller>(
       std::make_unique<WidevineCdmComponentInstallerPolicy>());
   installer->Register(cus, base::OnceClosure());
-#endif  // defined(WIDEVINE_CDM_IS_COMPONENT)
 }
 
 }  // namespace component_updater
diff --git a/chrome/browser/devtools/devtools_eye_dropper.h b/chrome/browser/devtools/devtools_eye_dropper.h
index 5c0c293..aaa87d2 100644
--- a/chrome/browser/devtools/devtools_eye_dropper.h
+++ b/chrome/browser/devtools/devtools_eye_dropper.h
@@ -12,6 +12,7 @@
 #include "components/viz/host/client_frame_sink_video_capturer.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents_observer.h"
+#include "media/renderers/paint_canvas_video_renderer.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace blink {
@@ -58,6 +59,7 @@
   content::RenderWidgetHost::MouseEventCallback mouse_event_callback_;
   content::RenderWidgetHost* host_;
   std::unique_ptr<viz::ClientFrameSinkVideoCapturer> video_capturer_;
+  media::PaintCanvasVideoRenderer video_renderer_;
   base::WeakPtrFactory<DevToolsEyeDropper> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsEyeDropper);
diff --git a/chrome/browser/download/offline_item_model.cc b/chrome/browser/download/offline_item_model.cc
index 25dee33..258ff97 100644
--- a/chrome/browser/download/offline_item_model.cc
+++ b/chrome/browser/download/offline_item_model.cc
@@ -80,6 +80,11 @@
   data->was_ui_notified_ = was_ui_notified;
 }
 
+base::FilePath OfflineItemModel::GetFileNameToReportUser() const {
+  return offline_item_ ? base::FilePath::FromUTF8Unsafe(offline_item_->title)
+                       : base::FilePath();
+}
+
 base::FilePath OfflineItemModel::GetTargetFilePath() const {
   return offline_item_ ? offline_item_->file_path : base::FilePath();
 }
diff --git a/chrome/browser/download/offline_item_model.h b/chrome/browser/download/offline_item_model.h
index 4a6c3c9..d2d47dc 100644
--- a/chrome/browser/download/offline_item_model.h
+++ b/chrome/browser/download/offline_item_model.h
@@ -39,6 +39,7 @@
   int PercentComplete() const override;
   bool WasUINotified() const override;
   void SetWasUINotified(bool should_notify) override;
+  base::FilePath GetFileNameToReportUser() const override;
   base::FilePath GetTargetFilePath() const override;
   void OpenDownload() override;
   void Pause() override;
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index ead993a..32b520fb 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -59,8 +59,6 @@
     "api/automation_internal/automation_event_router.h",
     "api/automation_internal/automation_internal_api.cc",
     "api/automation_internal/automation_internal_api.h",
-    "api/autotest_private/autotest_private_api.cc",
-    "api/autotest_private/autotest_private_api.h",
     "api/bookmark_manager_private/bookmark_manager_private_api.cc",
     "api/bookmark_manager_private/bookmark_manager_private_api.h",
     "api/bookmarks/bookmark_api_constants.cc",
@@ -997,8 +995,6 @@
       "//ash/public/cpp",
       "//chromeos/components/proximity_auth",
       "//chromeos/services/ime/public/mojom",
-      "//chromeos/services/machine_learning/public/cpp",
-      "//chromeos/services/machine_learning/public/mojom",
       "//components/arc",
       "//components/chrome_apps",
       "//components/constrained_window",
diff --git a/chrome/browser/extensions/api/autotest_private/OWNERS b/chrome/browser/extensions/api/autotest_private/OWNERS
deleted file mode 100644
index 408e726..0000000
--- a/chrome/browser/extensions/api/autotest_private/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-achuith@chromium.org
diff --git a/chrome/browser/extensions/component_loader.cc b/chrome/browser/extensions/component_loader.cc
index 167f528..ebbb3faa 100644
--- a/chrome/browser/extensions/component_loader.cc
+++ b/chrome/browser/extensions/component_loader.cc
@@ -323,9 +323,7 @@
 void ComponentLoader::AddZipArchiverExtension() {
 #if defined(OS_CHROMEOS)
   base::FilePath resources_path;
-  if ((chromeos::switches::IsZipArchiverPackerEnabled() ||
-       chromeos::switches::IsZipArchiverUnpackerEnabled()) &&
-      base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
+  if (base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
     AddWithNameAndDescriptionFromDir(
         resources_path.Append(extension_misc::kZipArchiverExtensionPath),
         extension_misc::kZipArchiverExtensionId,
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b7a3103..a787ba6b 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3274,10 +3274,6 @@
     "Enable Zero State Suggestions feature in Launcher, which will show "
     "suggetions when launcher search box is active with an empty query";
 
-const char kEnableZipArchiverPackerName[] = "ZIP archiver - Packer";
-const char kEnableZipArchiverPackerDescription[] =
-    "Enable the ability to archive files on Drive in the Files app";
-
 const char kEolNotificationName[] = "Disable Device End of Life notification.";
 const char kEolNotificationDescription[] =
     "Disable Notifcation when Device is End of Life.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 30b2ace..3474b4a 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1999,9 +1999,6 @@
 extern const char kEnableZeroStateSuggestionsName[];
 extern const char kEnableZeroStateSuggestionsDescription[];
 
-extern const char kEnableZipArchiverPackerName[];
-extern const char kEnableZipArchiverPackerDescription[];
-
 extern const char kEolNotificationName[];
 extern const char kEolNotificationDescription[];
 
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index 3c3ce39..c2bb7321 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -556,16 +556,9 @@
   TestSimplePlayback("bear-320x240-opus-av_enc-v.webm", kWebMOpusAudioVp9Video);
 }
 
-// TODO(xhwang): Test is flaky on Mac. https://crbug.com/835585
-#if defined(MACOS)
-#define MAYBE_Playback_Multiple_VideoAudio_WebM \
-  DISABLED_Playback_Multiple_VideoAudio_WebM
-#else
-#define MAYBE_Playback_Multiple_VideoAudio_WebM \
-  Playback_Multiple_VideoAudio_WebM
-#endif
+// TODO(xhwang): Test is flaky. https://crbug.com/890124.
 IN_PROC_BROWSER_TEST_P(EncryptedMediaTest,
-                       MAYBE_Playback_Multiple_VideoAudio_WebM) {
+                       DISABLED_Playback_Multiple_VideoAudio_WebM) {
   if (!IsPlayBackPossible(CurrentKeySystem())) {
     DVLOG(0) << "Skipping test - Playback_Multiple test requires playback.";
     return;
diff --git a/chrome/browser/password_manager/password_store_factory.cc b/chrome/browser/password_manager/password_store_factory.cc
index b0454989..7e626167 100644
--- a/chrome/browser/password_manager/password_store_factory.cc
+++ b/chrome/browser/password_manager/password_store_factory.cc
@@ -290,7 +290,6 @@
   ps->PreparePasswordHashData(GetSyncUsername(profile));
 #endif
 
-  password_manager_util::RemoveUselessCredentials(ps, profile->GetPrefs(), 60);
   auto network_context_getter = base::BindRepeating(
       [](Profile* profile) -> network::mojom::NetworkContext* {
         if (!g_browser_process->profile_manager()->IsValidProfile(profile))
@@ -299,11 +298,8 @@
             ->GetNetworkContext();
       },
       profile);
-  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&password_manager_util::ReportHttpMigrationMetrics, ps,
-                     network_context_getter),
-      base::TimeDelta::FromSeconds(60));
+  password_manager_util::RemoveUselessCredentials(ps, profile->GetPrefs(), 60,
+                                                  network_context_getter);
 
 #if defined(OS_WIN) || defined(OS_MACOSX) || \
     (defined(OS_LINUX) && !defined(OS_CHROMEOS))
diff --git a/chrome/browser/password_manager/password_store_mac.cc b/chrome/browser/password_manager/password_store_mac.cc
index c4ae8b3..6e9685c7 100644
--- a/chrome/browser/password_manager/password_store_mac.cc
+++ b/chrome/browser/password_manager/password_store_mac.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/password_manager/password_store_mac.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/os_crypt/os_crypt.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 
 using password_manager::MigrationStatus;
@@ -33,9 +34,13 @@
 
 PasswordStoreMac::~PasswordStoreMac() = default;
 
-void PasswordStoreMac::InitOnBackgroundSequence(
+bool PasswordStoreMac::InitOnBackgroundSequence(
     const syncer::SyncableService::StartSyncFlare& flare) {
-  PasswordStoreDefault::InitOnBackgroundSequence(flare);
+  if (!PasswordStoreDefault::InitOnBackgroundSequence(flare))
+    return false;
+
+  if (!OSCrypt::IsEncryptionAvailable())
+    return false;
 
   if (login_db() && (initial_status_ == MigrationStatus::NOT_STARTED ||
                      initial_status_ == MigrationStatus::FAILED_ONCE ||
@@ -53,6 +58,8 @@
       "PasswordManager.KeychainMigration.Status",
       static_cast<int>(initial_status_),
       static_cast<int>(MigrationStatus::MIGRATION_STATUS_COUNT));
+
+  return true;
 }
 
 void PasswordStoreMac::UpdateStatusPref(MigrationStatus status) {
diff --git a/chrome/browser/password_manager/password_store_mac.h b/chrome/browser/password_manager/password_store_mac.h
index 6503e4a..3c7431e 100644
--- a/chrome/browser/password_manager/password_store_mac.h
+++ b/chrome/browser/password_manager/password_store_mac.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "components/password_manager/core/browser/keychain_migration_status_mac.h"
+#include "components/password_manager/core/browser/password_store.h"
 #include "components/password_manager/core/browser/password_store_default.h"
 #include "components/prefs/pref_member.h"
 
@@ -35,7 +36,7 @@
   ~PasswordStoreMac() override;
 
   // PasswordStore:
-  void InitOnBackgroundSequence(
+  bool InitOnBackgroundSequence(
       const syncer::SyncableService::StartSyncFlare& flare) override;
 
   // Writes status to the prefs.
diff --git a/chrome/browser/prefs/pref_service_incognito_whitelist.cc b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
index c9617637..026dc1c 100644
--- a/chrome/browser/prefs/pref_service_incognito_whitelist.cc
+++ b/chrome/browser/prefs/pref_service_incognito_whitelist.cc
@@ -72,6 +72,9 @@
     bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
     bookmarks::prefs::kShowManagedBookmarksInBookmarkBar,
     bookmarks::prefs::kShowBookmarkBar,
+#if defined(OS_ANDROID)
+    prefs::kPartnerBookmarkMappings,
+#endif  // defined(OS_ANDROID)
 
     // Metrics preferences are out of profile scope and are merged between
     // incognito and regular modes.
@@ -206,37 +209,8 @@
 // audited, checked with owners, and removed or transfered to
 // |kPersistentPrefNames|.
 const char* const kTemporaryIncognitoWhitelist[] = {
-    // chrome/common/pref_names.h
-    prefs::kWebRTCMultipleRoutesEnabled,
-    prefs::kWebRTCNonProxiedUdpEnabled,
-    prefs::kWebRTCIPHandlingPolicy,
-
-    prefs::kDefaultTasksByMimeType,
-    prefs::kDefaultTasksBySuffix,
-
-    prefs::kDefaultAudioCaptureDevice,
-    prefs::kDefaultVideoCaptureDevice,
     prefs::kMediaDeviceIdSalt,
-
     prefs::kClearPluginLSODataEnabled,
-    prefs::kPepperFlashSettingsEnabled,
-
-    prefs::kPerformanceTracingEnabled,
-
-#if !defined(OS_ANDROID)
-    prefs::kMediaGalleriesUniqueId,
-    prefs::kMediaGalleriesRememberedGalleries,
-#endif  // !defined(OS_ANDROID)
-
-#if defined(OS_ANDROID)
-    prefs::kPartnerBookmarkMappings,
-#endif  // defined(OS_ANDROID)
-
-#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
-// prefs::kRestartInBackground,
-#endif
-
-    prefs::kBackgroundTracingLastUpload,
 };
 
 }  // namespace
diff --git a/chrome/browser/resources/chromeos/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
index 30a3a87..c9213194 100644
--- a/chrome/browser/resources/chromeos/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/chromevox/BUILD.gn
@@ -45,7 +45,6 @@
   "chromevox/background/tabs_api_handler.js",
   "chromevox/injected/active_indicator.js",
   "chromevox/injected/api_implementation.js",
-  "chromevox/injected/console_tts.js",
   "chromevox/injected/event_suspender.js",
   "chromevox/injected/event_watcher.js",
   "chromevox/injected/history.js",
@@ -125,6 +124,7 @@
   "cvox2/background/braille_command_handler.js",
   "cvox2/background/chromevox_state.js",
   "cvox2/background/command_handler.js",
+  "cvox2/background/console_tts.js",
   "cvox2/background/constants.js",
   "cvox2/background/cursors.js",
   "cvox2/background/custom_automation_event.js",
diff --git a/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js b/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js
index 9069c05..5ec2769 100644
--- a/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js
+++ b/chrome/browser/resources/chromeos/chromevox/braille/nav_braille.js
@@ -11,8 +11,6 @@
 
 goog.provide('cvox.NavBraille');
 
-goog.require('LogStore');
-goog.require('TextLog');
 goog.require('Spannable');
 
 /**
@@ -111,15 +109,3 @@
     endIndex: this.endIndex
   };
 };
-
-/**
- *  Output braille text to console.
- */
-cvox.NavBraille.prototype.brailleLogging = function() {
-  if (localStorage['enableBrailleLogging'] != 'true')
-    return;
-
-  var logStr = 'Braille "' + this.text.toString() + '"';
-  LogStore.getInstance().writeTextLog(logStr, TextLog.LogType.BRAILLE);
-  console.log(logStr);
-};
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js
index fca0d8f..5d4371e 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/background.js
@@ -9,6 +9,7 @@
 goog.provide('cvox.ChromeVoxBackground');
 
 goog.require('ChromeVoxState');
+goog.require('ConsoleTts');
 goog.require('EventStreamLogger');
 goog.require('LogStore');
 goog.require('Msgs');
@@ -21,7 +22,6 @@
 goog.require('cvox.ChromeVoxPrefs');
 goog.require('cvox.ClassicEarcons');
 goog.require('cvox.CompositeTts');
-goog.require('cvox.ConsoleTts');
 goog.require('cvox.ExtensionBridge');
 goog.require('cvox.InjectedScriptLoader');
 goog.require('cvox.NavBraille');
@@ -131,7 +131,7 @@
   this.prefs = new cvox.ChromeVoxPrefs();
   cvox.ChromeVoxBackground.readPrefs();
 
-  var consoleTts = cvox.ConsoleTts.getInstance();
+  var consoleTts = ConsoleTts.getInstance();
   consoleTts.setEnabled(this.prefs.getPrefs()['enableSpeechLogging'] == 'true');
 
   LogStore.getInstance();
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
index 25680de..766c29c2 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.js
@@ -9,6 +9,7 @@
 
 goog.provide('cvox.OptionsPage');
 
+goog.require('ConsoleTts');
 goog.require('EventStreamLogger');
 goog.require('Msgs');
 goog.require('PanelCommand');
@@ -19,7 +20,6 @@
 goog.require('cvox.ChromeVox');
 goog.require('cvox.ChromeVoxPrefs');
 goog.require('cvox.CommandStore');
-goog.require('cvox.ConsoleTts');
 goog.require('cvox.ExtensionBridge');
 goog.require('cvox.PlatformFilter');
 goog.require('cvox.PlatformUtil');
@@ -38,7 +38,7 @@
 
 /**
  * The ChromeVoxConsoleTts object.
- * @type {cvox.ConsoleTts}
+ * @type {ConsoleTts}
  */
 cvox.OptionsPage.consoleTts;
 
@@ -50,7 +50,7 @@
 cvox.OptionsPage.init = function() {
   cvox.OptionsPage.prefs = chrome.extension.getBackgroundPage().prefs;
   cvox.OptionsPage.consoleTts =
-      chrome.extension.getBackgroundPage().cvox.ConsoleTts.getInstance();
+      chrome.extension.getBackgroundPage().ConsoleTts.getInstance();
   cvox.OptionsPage.populateVoicesSelect();
   cvox.BrailleTable.getAll(function(tables) {
     /** @type {!Array<cvox.BrailleTable.Table>} */
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js
index bcf78ef1..159b38a 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/prefs.js
@@ -10,9 +10,9 @@
 
 goog.provide('cvox.ChromeVoxPrefs');
 
+goog.require('ConsoleTts');
 goog.require('EventStreamLogger');
 goog.require('cvox.ChromeVox');
-goog.require('cvox.ConsoleTts');
 goog.require('cvox.ExtensionBridge');
 goog.require('cvox.KeyMap');
 
@@ -303,7 +303,7 @@
 cvox.ChromeVoxPrefs.prototype.setLoggingPrefs = function(key, value) {
   localStorage[key] = value;
   if (key == 'enableSpeechLogging')
-    cvox.ConsoleTts.getInstance().setEnabled(value);
+    ConsoleTts.getInstance().setEnabled(value);
   else if (key == 'enableEventStreamLogging')
     EventStreamLogger.instance.notifyEventStreamFilterChangedAll(value);
 };
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/console_tts.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/console_tts.js
deleted file mode 100644
index 2fa0916d..0000000
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/console_tts.js
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview A TTS engine that writes to window.console.
- */
-
-goog.provide('cvox.ConsoleTts');
-
-goog.require('LogStore');
-goog.require('TextLog');
-goog.require('cvox.AbstractTts');
-goog.require('cvox.TtsInterface');
-
-/**
- * @constructor
- * @implements {cvox.TtsInterface}
- */
-cvox.ConsoleTts = function() {
-  /**
-   * True if the console TTS is enabled by the user.
-   * @type {boolean}
-   * @private
-   */
-  this.enabled_ = false;
-};
-goog.addSingletonGetter(cvox.ConsoleTts);
-
-
-/** @override */
-cvox.ConsoleTts.prototype.speak = function(textString, queueMode, properties) {
-  if (this.enabled_ && window['console']) {
-    var logStr = 'Speak';
-    if (queueMode == cvox.QueueMode.FLUSH) {
-      logStr += ' (I)';
-    } else if (queueMode == cvox.QueueMode.CATEGORY_FLUSH) {
-      logStr += ' (C)';
-    } else {
-      logStr += ' (Q)';
-    }
-    if (properties && properties.category) {
-      logStr += ' category=' + properties.category;
-    }
-    logStr += ' "' + textString + '"';
-    LogStore.getInstance().writeTextLog(logStr, TextLog.LogType.SPEECH);
-    console.log(logStr);
-  }
-  return this;
-};
-
-/** @override */
-cvox.ConsoleTts.prototype.isSpeaking = function() {
-  return false;
-};
-
-/** @override */
-cvox.ConsoleTts.prototype.stop = function() {
-  if (this.enabled_) {
-    console.log('Stop');
-  }
-};
-
-/** @override */
-cvox.ConsoleTts.prototype.addCapturingEventListener = function(listener) {};
-
-/** @override */
-cvox.ConsoleTts.prototype.increaseOrDecreaseProperty = function() {};
-
-/** @override */
-cvox.ConsoleTts.prototype.propertyToPercentage = function() {};
-
-/**
- * Sets the enabled bit.
- * @param {boolean} enabled The new enabled bit.
- */
-cvox.ConsoleTts.prototype.setEnabled = function(enabled) {
-  this.enabled_ = enabled;
-};
-
-/** @override */
-cvox.ConsoleTts.prototype.getDefaultProperty = function(property) {};
-
-/** @override */
-cvox.ConsoleTts.prototype.toggleSpeechOnOrOff = function() {};
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/init_globals.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/init_globals.js
index 243117e6..d140a9c 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/init_globals.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/init_globals.js
@@ -12,7 +12,6 @@
 goog.require('cvox.ChromeVox');
 goog.require('cvox.ChromeVoxEventWatcher');
 goog.require('cvox.CompositeTts');
-goog.require('cvox.ConsoleTts');
 goog.require('cvox.HostFactory');
 goog.require('cvox.NavigationManager');
 goog.require('cvox.Serializer');
@@ -35,8 +34,7 @@
 
   cvox.ChromeVox.tts = new cvox.CompositeTts()
                            .add(cvox.HostFactory.getTts())
-                           .add(cvox.History.getInstance())
-                           .add(cvox.ConsoleTts.getInstance());
+                           .add(cvox.History.getInstance());
 
   if (!cvox.ChromeVox.braille) {
     cvox.ChromeVox.braille = cvox.HostFactory.getBraille();
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands.js
index 991f5ab..356d52c 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/user_commands.js
@@ -22,7 +22,6 @@
 goog.require('cvox.ChromeVox');
 goog.require('cvox.ChromeVoxKbHandler');
 goog.require('cvox.CommandStore');
-goog.require('cvox.ConsoleTts');
 goog.require('cvox.ContextMenuWidget');
 goog.require('cvox.DomPredicates');
 goog.require('cvox.DomUtil');
@@ -572,9 +571,6 @@
     case 'stopHistoryRecording':
       cvox.History.getInstance().stopRecording();
       break;
-    case 'enableConsoleTts':
-      cvox.ConsoleTts.getInstance().setEnabled(true);
-      break;
     case 'toggleBrailleCaptions':
       cvox.ChromeVox.host.sendToBackgroundPage({
         'target': 'Prefs',
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/console_tts.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/console_tts.js
new file mode 100644
index 0000000..0c3896c
--- /dev/null
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/console_tts.js
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview A TTS engine that writes to window.console.
+ */
+
+goog.provide('ConsoleTts');
+
+goog.require('LogStore');
+goog.require('TextLog');
+goog.require('cvox.AbstractTts');
+goog.require('cvox.TtsInterface');
+
+/**
+ * @constructor
+ * @implements {cvox.TtsInterface}
+ */
+ConsoleTts = function() {
+  /**
+   * True if the console TTS is enabled by the user.
+   * @type {boolean}
+   * @private
+   */
+  this.enabled_ = false;
+};
+goog.addSingletonGetter(ConsoleTts);
+
+
+/** @override */
+ConsoleTts.prototype = {
+  speak: function(textString, queueMode, properties) {
+    if (this.enabled_ && window['console']) {
+      var logStr = 'Speak';
+      if (queueMode == cvox.QueueMode.FLUSH) {
+        logStr += ' (I)';
+      } else if (queueMode == cvox.QueueMode.CATEGORY_FLUSH) {
+        logStr += ' (C)';
+      } else {
+        logStr += ' (Q)';
+      }
+      if (properties && properties.category) {
+        logStr += ' category=' + properties.category;
+      }
+      logStr += ' "' + textString + '"';
+      LogStore.getInstance().writeTextLog(logStr, TextLog.LogType.SPEECH);
+      console.log(logStr);
+    }
+    return this;
+  },
+
+  /** @override */
+  isSpeaking: function() {
+    return false;
+  },
+
+  /** @override */
+  stop: function() {
+    if (this.enabled_) {
+      console.log('Stop');
+    }
+  },
+
+  /** @override */
+  addCapturingEventListener: function(listener) {},
+
+  /** @override */
+  increaseOrDecreaseProperty: function() {},
+
+  /** @override */
+  propertyToPercentage: function() {},
+
+  /**
+   * Sets the enabled bit.
+   * @param {boolean} enabled The new enabled bit.
+   */
+  setEnabled: function(enabled) {
+    this.enabled_ = enabled;
+  },
+
+  /** @override */
+  getDefaultProperty: function(property) {},
+
+  /** @override */
+  toggleSpeechOnOrOff: function() {}
+};
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
index 0ef9c11..8f7ca2e 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/output.js
@@ -1073,7 +1073,6 @@
 
       var output = new cvox.NavBraille(
           {text: buff, startIndex: startIndex, endIndex: endIndex});
-      output.brailleLogging();
 
       cvox.ChromeVox.braille.write(output);
       if (this.brailleRulesStr_.str) {
diff --git a/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js b/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js
index b77c546..bb0e4a1e 100644
--- a/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js
+++ b/chrome/browser/resources/chromeos/chromevox/host/chrome/braille_background.js
@@ -10,6 +10,7 @@
 
 goog.require('BrailleKeyEventRewriter');
 goog.require('ChromeVoxState');
+goog.require('LogStore');
 goog.require('cvox.BrailleDisplayManager');
 goog.require('cvox.BrailleInputHandler');
 goog.require('cvox.BrailleInterface');
@@ -73,6 +74,13 @@
   if (this.frozen_) {
     return;
   }
+
+  if (localStorage['enableBrailleLogging'] == 'true') {
+    var logStr = 'Braille "' + params.text.toString() + '"';
+    LogStore.getInstance().writeTextLog(logStr, TextLog.LogType.BRAILLE);
+    console.log(logStr);
+  }
+
   this.setContent_(params, null);
 };
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn b/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn
index e90607aa..a030b278 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/BUILD.gn
@@ -22,6 +22,7 @@
     "request.h",
     "volume.cc",
     "volume.h",
+    "volume_archive.cc",
     "volume_archive.h",
     "volume_archive_minizip.cc",
     "volume_archive_minizip.h",
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor.cc
index 14630495..1b3c7b42 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor.cc
@@ -26,14 +26,14 @@
   explicit JavaScriptCompressorRequestor(Compressor* compressor)
       : compressor_(compressor) {}
 
-  virtual void WriteChunkRequest(int64_t offset,
-                                 int64_t length,
-                                 const pp::VarArrayBuffer& buffer) {
+  void WriteChunkRequest(int64_t offset,
+                         int64_t length,
+                         const pp::VarArrayBuffer& buffer) override {
     compressor_->message_sender()->SendWriteChunk(compressor_->compressor_id(),
                                                   buffer, offset, length);
   }
 
-  virtual void ReadFileChunkRequest(int64_t length) {
+  void ReadFileChunkRequest(int64_t length) override {
     compressor_->message_sender()->SendReadFileChunk(
         compressor_->compressor_id(), length);
   }
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h
index b2d739a..0c8dbcc 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_archive_minizip.h
@@ -35,22 +35,22 @@
  public:
   explicit CompressorArchiveMinizip(CompressorStream* compressor_stream);
 
-  virtual ~CompressorArchiveMinizip();
+  ~CompressorArchiveMinizip() override;
 
   // Creates an archive object.
-  virtual bool CreateArchive();
+  bool CreateArchive() override;
 
   // Closes the archive.
-  virtual bool CloseArchive(bool has_error);
+  bool CloseArchive(bool has_error) override;
 
   // Cancels the compression process.
-  virtual void CancelArchive();
+  void CancelArchive() override;
 
   // Adds an entry to the archive.
-  virtual bool AddToArchive(const std::string& filename,
-                            int64_t file_size,
-                            int64_t modification_time,
-                            bool is_directory);
+  bool AddToArchive(const std::string& filename,
+                    int64_t file_size,
+                    int64_t modification_time,
+                    bool is_directory) override;
 
   // A getter function for zip_file_.
   zipFile zip_file() const { return zip_file_; }
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.cc
index 8a0a13c..bc15e66 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.cc
@@ -22,31 +22,19 @@
 CompressorIOJavaScriptStream::CompressorIOJavaScriptStream(
     JavaScriptCompressorRequestorInterface* requestor)
     : requestor_(requestor),
+      available_data_cond_(&shared_state_lock_),
+      data_written_cond_(&shared_state_lock_),
+      available_data_(false),
       buffer_offset_(-1),
       buffer_data_length_(0),
-      buffer_(std::make_unique<char[]>(kMaximumDataChunkSize)) {
-  pthread_mutex_init(&shared_state_lock_, nullptr);
-  pthread_cond_init(&available_data_cond_, nullptr);
-  pthread_cond_init(&data_written_cond_, nullptr);
+      buffer_(std::make_unique<char[]>(kMaximumDataChunkSize)) {}
 
-  pthread_mutex_lock(&shared_state_lock_);
-  available_data_ = false;
-  pthread_mutex_unlock(&shared_state_lock_);
-}
-
-CompressorIOJavaScriptStream::~CompressorIOJavaScriptStream() {
-  pthread_mutex_lock(&shared_state_lock_);
-  pthread_mutex_unlock(&shared_state_lock_);
-  pthread_cond_destroy(&data_written_cond_);
-  pthread_cond_destroy(&available_data_cond_);
-  pthread_mutex_destroy(&shared_state_lock_);
-}
+CompressorIOJavaScriptStream::~CompressorIOJavaScriptStream() = default;
 
 int64_t CompressorIOJavaScriptStream::Flush() {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
 
   if (buffer_data_length_ == 0) {
-    pthread_mutex_unlock(&shared_state_lock_);
     return 0;
   }
 
@@ -59,11 +47,11 @@
   requestor_->WriteChunkRequest(buffer_offset_, buffer_data_length_,
                                 array_buffer);
 
-  pthread_cond_wait(&data_written_cond_, &shared_state_lock_);
+  // TODO(amistry): Handle spurious wakeups.
+  data_written_cond_.Wait();
 
   int64_t written_bytes = written_bytes_;
   if (written_bytes < buffer_data_length_) {
-    pthread_mutex_unlock(&shared_state_lock_);
     return -1 /* Error */;
   }
 
@@ -71,14 +59,13 @@
   buffer_offset_ = -1;
   buffer_data_length_ = 0;
 
-  pthread_mutex_unlock(&shared_state_lock_);
   return written_bytes;
 }
 
 int64_t CompressorIOJavaScriptStream::Write(int64_t zip_offset,
                                             int64_t zip_length,
                                             const char* zip_buffer) {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
 
   // The offset from which the data should be written onto the archive.
   int64_t current_offset = zip_offset;
@@ -104,11 +91,11 @@
         (current_offset < buffer_offset_ ||
          buffer_offset_ + buffer_data_length_ <
              current_offset + left_length) /* 4 */) {
-      pthread_mutex_unlock(&shared_state_lock_);
       int64_t bytes_to_write = buffer_data_length_;
+
+      base::AutoUnlock aul(shared_state_lock_);
       if (Flush() != bytes_to_write)
         return -1;
-      pthread_mutex_lock(&shared_state_lock_);
     }
 
     // How many bytes we should copy to buffer_ in this iteration.
@@ -129,39 +116,36 @@
     left_length -= copy_length;
   } while (left_length > 0);
 
-  pthread_mutex_unlock(&shared_state_lock_);
   return zip_length;
 }
 
 int64_t CompressorIOJavaScriptStream::WriteChunkDone(int64_t written_bytes) {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
   written_bytes_ = written_bytes;
-  pthread_cond_signal(&data_written_cond_);
-  pthread_mutex_unlock(&shared_state_lock_);
+  data_written_cond_.Signal();
   return written_bytes;
 }
 
 int64_t CompressorIOJavaScriptStream::Read(int64_t bytes_to_read,
                                            char* destination_buffer) {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
 
   destination_buffer_ = destination_buffer;
   requestor_->ReadFileChunkRequest(bytes_to_read);
 
   while (!available_data_) {
-    pthread_cond_wait(&available_data_cond_, &shared_state_lock_);
+    available_data_cond_.Wait();
   }
 
   int64_t read_bytes = read_bytes_;
   available_data_ = false;
-  pthread_mutex_unlock(&shared_state_lock_);
   return read_bytes;
 }
 
 int64_t CompressorIOJavaScriptStream::ReadFileChunkDone(
     int64_t read_bytes,
     pp::VarArrayBuffer* array_buffer) {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
 
   // JavaScript sets a negative value in read_bytes if an error occurred while
   // reading a chunk.
@@ -173,7 +157,6 @@
 
   read_bytes_ = read_bytes;
   available_data_ = true;
-  pthread_cond_signal(&available_data_cond_);
-  pthread_mutex_unlock(&shared_state_lock_);
+  available_data_cond_.Signal();
   return read_bytes;
 }
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.h
index b967c4c..524fa36 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_io_javascript_stream.h
@@ -5,17 +5,16 @@
 #ifndef CHROME_BROWSER_RESOURCES_CHROMEOS_ZIP_ARCHIVER_CPP_COMPRESSOR_IO_JAVASCRIPT_STREAM_H_
 #define CHROME_BROWSER_RESOURCES_CHROMEOS_ZIP_ARCHIVER_CPP_COMPRESSOR_IO_JAVASCRIPT_STREAM_H_
 
-#include <pthread.h>
 #include <cstdint>
 #include <memory>
 #include <string>
 
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
 #include "chrome/browser/resources/chromeos/zip_archiver/cpp/compressor_stream.h"
 #include "ppapi/cpp/instance_handle.h"
 #include "ppapi/cpp/var_array_buffer.h"
 #include "ppapi/utility/completion_callback_factory.h"
-#include "ppapi/utility/threading/lock.h"
-#include "ppapi/utility/threading/simple_thread.h"
 
 class JavaScriptCompressorRequestorInterface;
 
@@ -24,31 +23,31 @@
   CompressorIOJavaScriptStream(
       JavaScriptCompressorRequestorInterface* requestor);
 
-  virtual ~CompressorIOJavaScriptStream();
+  ~CompressorIOJavaScriptStream() override;
 
   // Flushes the data in buffer_. Since minizip sends tons of write requests and
   // communication between C++ and JS is very expensive, we need to cache data
   // in buffer_ and send them in a lump.
-  virtual int64_t Flush();
+  int64_t Flush() override;
 
-  virtual int64_t Write(int64_t zip_offset,
-                        int64_t zip_length,
-                        const char* zip_buffer);
+  int64_t Write(int64_t zip_offset,
+                int64_t zip_length,
+                const char* zip_buffer) override;
 
-  virtual int64_t WriteChunkDone(int64_t write_bytes);
+  int64_t WriteChunkDone(int64_t write_bytes) override;
 
-  virtual int64_t Read(int64_t bytes_to_read, char* destination_buffer);
+  int64_t Read(int64_t bytes_to_read, char* destination_buffer) override;
 
-  virtual int64_t ReadFileChunkDone(int64_t read_bytes,
-                                    pp::VarArrayBuffer* buffer);
+  int64_t ReadFileChunkDone(int64_t read_bytes,
+                            pp::VarArrayBuffer* buffer) override;
 
  private:
   // A requestor that makes calls to JavaScript to read and write chunks.
   JavaScriptCompressorRequestorInterface* requestor_;
 
-  pthread_mutex_t shared_state_lock_;
-  pthread_cond_t available_data_cond_;
-  pthread_cond_t data_written_cond_;
+  base::Lock shared_state_lock_;
+  base::ConditionVariable available_data_cond_;
+  base::ConditionVariable data_written_cond_;
 
   // The bytelength of the data written onto the archive for the last write
   // chunk request. If this value is negative, some error occurred when writing
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
index a2b1662..bbb5f1fd 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume.cc
@@ -63,7 +63,7 @@
   pp::VarDictionary parent_entries =
       pp::VarDictionary(parent_metadata->Get("entries"));
 
-  unsigned int position = entry_path.find(kPathDelimiter);
+  std::string::size_type position = entry_path.find(kPathDelimiter);
   pp::VarDictionary entry_metadata;
   std::string entry_name;
 
@@ -114,16 +114,16 @@
   // JavaScriptRequestor does not own the volume pointer.
   explicit JavaScriptRequestor(Volume* volume) : volume_(volume) {}
 
-  virtual void RequestFileChunk(const std::string& request_id,
-                                int64_t offset,
-                                int64_t bytes_to_read) {
+  void RequestFileChunk(const std::string& request_id,
+                        int64_t offset,
+                        int64_t bytes_to_read) override {
     PP_DCHECK(offset >= 0);
     PP_DCHECK(bytes_to_read > 0);
     volume_->message_sender()->SendFileChunkRequest(
         volume_->file_system_id(), request_id, offset, bytes_to_read);
   }
 
-  virtual void RequestPassphrase(const std::string& request_id) {
+  void RequestPassphrase(const std::string& request_id) override {
     volume_->message_sender()->SendPassphraseRequest(volume_->file_system_id(),
                                                      request_id);
   }
@@ -136,8 +136,8 @@
 // Volume constructor.
 class VolumeArchiveFactory : public VolumeArchiveFactoryInterface {
  public:
-  virtual std::unique_ptr<VolumeArchive> Create(
-      std::unique_ptr<VolumeReader> reader) {
+  std::unique_ptr<VolumeArchive> Create(
+      std::unique_ptr<VolumeReader> reader) override {
     return std::make_unique<VolumeArchiveMinizip>(std::move(reader));
   }
 };
@@ -149,7 +149,7 @@
   // VolumeReaderFactory does not own the volume pointer.
   explicit VolumeReaderFactory(Volume* volume) : volume_(volume) {}
 
-  virtual std::unique_ptr<VolumeReader> Create(int64_t archive_size) {
+  std::unique_ptr<VolumeReader> Create(int64_t archive_size) override {
     return std::make_unique<VolumeReaderJavaScriptStream>(archive_size,
                                                           volume_->requestor());
   }
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.cc
new file mode 100644
index 0000000..27cdd0a
--- /dev/null
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.cc
@@ -0,0 +1,10 @@
+// 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/resources/chromeos/zip_archiver/cpp/volume_archive.h"
+
+VolumeArchive::VolumeArchive(std::unique_ptr<VolumeReader> reader)
+    : reader_(std::move(reader)) {}
+
+VolumeArchive::~VolumeArchive() = default;
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h
index ed835d0..4629ef3f 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive.h
@@ -17,10 +17,9 @@
 // to be thread safe and its methods shouldn't be called in parallel.
 class VolumeArchive {
  public:
-  explicit VolumeArchive(std::unique_ptr<VolumeReader> reader)
-      : reader_(std::move(reader)) {}
+  explicit VolumeArchive(std::unique_ptr<VolumeReader> reader);
 
-  virtual ~VolumeArchive() {}
+  virtual ~VolumeArchive();
 
   // For functions that need to return more than pass/fail results.
   enum Result {
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
index 804ee0ed..3031aa7 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.cc
@@ -246,8 +246,8 @@
   int64_t previous_offset = reader()->offset();
   char* buffer_pointer = static_cache_.get();
   int64_t left_length = static_cache_size_;
-  static_cache_offset_ =
-      std::max(reader()->archive_size() - static_cache_size_, 0LL);
+  static_cache_offset_ = std::max(reader()->archive_size() - static_cache_size_,
+                                  static_cast<int64_t>(0));
   if (reader()->Seek(static_cache_offset_, ZLIB_FILEFUNC_SEEK_SET) < 0) {
     set_error_message(kArchiveOpenError);
     return false /* Error */;
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
index dd114b05..0a91456 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_archive_minizip.h
@@ -45,31 +45,33 @@
  public:
   explicit VolumeArchiveMinizip(std::unique_ptr<VolumeReader> reader);
 
-  virtual ~VolumeArchiveMinizip();
+  ~VolumeArchiveMinizip() override;
 
   // See volume_archive_interface.h.
-  virtual bool Init(const std::string& encoding);
+  bool Init(const std::string& encoding) override;
 
   // See volume_archive_interface.h.
-  virtual VolumeArchive::Result GetCurrentFileInfo(std::string* path_name,
-                                                   bool* isEncodedInUtf8,
-                                                   int64_t* size,
-                                                   bool* is_directory,
-                                                   time_t* modification_time);
+  VolumeArchive::Result GetCurrentFileInfo(std::string* path_name,
+                                           bool* isEncodedInUtf8,
+                                           int64_t* size,
+                                           bool* is_directory,
+                                           time_t* modification_time) override;
 
-  virtual VolumeArchive::Result GoToNextFile();
+  VolumeArchive::Result GoToNextFile() override;
 
   // See volume_archive_interface.h.
-  virtual bool SeekHeader(const std::string& path_name);
+  bool SeekHeader(const std::string& path_name) override;
 
   // See volume_archive_interface.h.
-  virtual int64_t ReadData(int64_t offset, int64_t length, const char** buffer);
+  int64_t ReadData(int64_t offset,
+                   int64_t length,
+                   const char** buffer) override;
 
   // See volume_archive_interface.h.
-  virtual void MaybeDecompressAhead();
+  void MaybeDecompressAhead() override;
 
   // See volume_archive_interface.h.
-  virtual bool Cleanup();
+  bool Cleanup() override;
 
   int64_t reader_data_size() const { return reader_data_size_; }
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc
index 5e541cb0..221ad451 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.cc
@@ -19,15 +19,13 @@
       available_data_(false),
       read_error_(false),
       passphrase_error_(false),
+      available_data_cond_(&shared_state_lock_),
+      available_passphrase_cond_(&shared_state_lock_),
       offset_(0),
       // For first call -1 will force a chunk request from JavaScript as offset
       // parameter is 0.
       last_read_chunk_offset_(-1),
       read_ahead_array_buffer_ptr_(&first_array_buffer_) {
-  pthread_mutex_init(&shared_state_lock_, nullptr);
-  pthread_cond_init(&available_data_cond_, nullptr);
-  pthread_cond_init(&available_passphrase_cond_, nullptr);
-
   // Dummy Map the second buffer as first buffer is used for read ahead by
   // read_ahead_array_buffer_ptr_. This operation is required in order for Unmap
   // to correctly work in the destructor and VolumeReaderJavaScriptStream::Read.
@@ -35,10 +33,6 @@
 }
 
 VolumeReaderJavaScriptStream::~VolumeReaderJavaScriptStream() {
-  pthread_mutex_destroy(&shared_state_lock_);
-  pthread_cond_destroy(&available_data_cond_);
-  pthread_cond_destroy(&available_passphrase_cond_);
-
   // Unmap last mapped buffer. This is the other buffer to
   // read_ahead_array_buffer_ptr_ as read_ahead_array_buffer_ptr_ must be
   // available for SetBufferAndSignal to overwrite.
@@ -48,6 +42,14 @@
     second_array_buffer_.Unmap();
 }
 
+int64_t VolumeReaderJavaScriptStream::offset() {
+  return offset_;
+}
+
+int64_t VolumeReaderJavaScriptStream::archive_size() {
+  return archive_size_;
+}
+
 void VolumeReaderJavaScriptStream::SetBufferAndSignal(
     const pp::VarArrayBuffer& array_buffer,
     int64_t read_offset) {
@@ -68,7 +70,7 @@
   // buffer can still be used. In such case we should use it. That can greatly
   // improve traversing headers for archives with small files!
 
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
   if (read_offset == offset_ && !available_data_ && !read_error_) {
     // Signal VolumeReaderJavaScriptStream::Read to continue execution. Copies
     // buffer locally so minizip has the buffer in memory when working with
@@ -80,43 +82,38 @@
     *read_ahead_array_buffer_ptr_ = array_buffer;  // Copy operation.
     available_data_ = true;
 
-    pthread_cond_signal(&available_data_cond_);
+    available_data_cond_.Signal();
   }
-  pthread_mutex_unlock(&shared_state_lock_);
 }
 
 void VolumeReaderJavaScriptStream::ReadErrorSignal() {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
   read_error_ = true;  // Read error from JavaScript.
-  pthread_cond_signal(&available_data_cond_);
-  pthread_mutex_unlock(&shared_state_lock_);
+  available_data_cond_.Signal();
 }
 
 void VolumeReaderJavaScriptStream::SetPassphraseAndSignal(
     const std::string& passphrase) {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
   // Signal VolumeReaderJavaScriptStream::Passphrase to continue execution.
   available_passphrase_ = passphrase;
-  pthread_cond_signal(&available_passphrase_cond_);
-  pthread_mutex_unlock(&shared_state_lock_);
+  available_passphrase_cond_.Signal();
 }
 
 void VolumeReaderJavaScriptStream::PassphraseErrorSignal() {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
   passphrase_error_ = true;  // Passphrase error from JavaScript.
-  pthread_cond_signal(&available_passphrase_cond_);
-  pthread_mutex_unlock(&shared_state_lock_);
+  available_passphrase_cond_.Signal();
 }
 
 int64_t VolumeReaderJavaScriptStream::Read(int64_t bytes_to_read,
                                            const void** destination_buffer) {
   PP_DCHECK(bytes_to_read > 0);
 
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
 
   // No more data, so signal end of reading.
   if (offset_ >= archive_size_) {
-    pthread_mutex_unlock(&shared_state_lock_);
     return 0;
   }
 
@@ -129,15 +126,13 @@
     while (!available_data_) {  // Check again available data as first call
                                 // was done outside guarded zone.
       if (read_error_) {
-        pthread_mutex_unlock(&shared_state_lock_);
         return -1;
       }
-      pthread_cond_wait(&available_data_cond_, &shared_state_lock_);
+      available_data_cond_.Wait();
     }
   }
 
   if (read_error_) {  // Read ahead failed.
-    pthread_mutex_unlock(&shared_state_lock_);
     return -1;
   }
 
@@ -178,13 +173,12 @@
 
   // Read ahead next chunk with a length similar to current read.
   RequestChunk(bytes_to_read);
-  pthread_mutex_unlock(&shared_state_lock_);
 
   return bytes_read;
 }
 
 int64_t VolumeReaderJavaScriptStream::Seek(int64_t offset, int whence) {
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
 
   int64_t new_offset = offset_;
   switch (whence) {
@@ -199,17 +193,14 @@
       break;
     default:
       PP_NOTREACHED();
-      pthread_mutex_unlock(&shared_state_lock_);
       return -1;
   }
 
   if (new_offset < 0) {
-    pthread_mutex_unlock(&shared_state_lock_);
     return -1;
   }
 
   offset_ = new_offset;
-  pthread_mutex_unlock(&shared_state_lock_);
 
   return new_offset;
 }
@@ -224,25 +215,24 @@
   // The error is not recoverable. Once passphrase fails to be provided, it is
   // never asked again. Note, that still users are able to retry entering the
   // password, unless they click Cancel.
-  pthread_mutex_lock(&shared_state_lock_);
-  if (passphrase_error_) {
-    pthread_mutex_unlock(&shared_state_lock_);
-    return result;
+  {
+    base::AutoLock al(shared_state_lock_);
+    if (passphrase_error_) {
+      return result;
+    }
   }
-  pthread_mutex_unlock(&shared_state_lock_);
 
   // Request the passphrase outside of the lock.
   requestor_->RequestPassphrase(request_id_);
 
-  pthread_mutex_lock(&shared_state_lock_);
+  base::AutoLock al(shared_state_lock_);
   // Wait for the passphrase from JavaScript.
-  pthread_cond_wait(&available_passphrase_cond_, &shared_state_lock_);
+  // TODO(amistry): Handle spurious wakeups.
+  available_passphrase_cond_.Wait();
 
   if (!passphrase_error_)
     result.reset(new std::string(available_passphrase_));
 
-  pthread_mutex_unlock(&shared_state_lock_);
-
   return result;
 }
 
diff --git a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h
index 484211e..6490b0e 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader_javascript_stream.h
@@ -5,11 +5,12 @@
 #ifndef CHROME_BROWSER_RESOURCES_CHROMEOS_ZIP_ARCHIVER_CPP_VOLUME_READER_JAVASCRIPT_STREAM_H_
 #define CHROME_BROWSER_RESOURCES_CHROMEOS_ZIP_ARCHIVER_CPP_VOLUME_READER_JAVASCRIPT_STREAM_H_
 
-#include <pthread.h>
 #include <cstdint>
 #include <memory>
 #include <string>
 
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
 #include "chrome/browser/resources/chromeos/zip_archiver/cpp/volume_reader.h"
 #include "ppapi/cpp/var_array_buffer.h"
 
@@ -27,7 +28,7 @@
   VolumeReaderJavaScriptStream(int64_t archive_size,
                                JavaScriptRequestorInterface* requestor);
 
-  virtual ~VolumeReaderJavaScriptStream();
+  ~VolumeReaderJavaScriptStream() override;
 
   // Sets the internal array buffer used for reads and signal the blocked
   // VolumeReaderJavaScriptStream::Read to continue execution. Must be done in
@@ -59,10 +60,10 @@
   // See volume_reader.h for description. This method blocks on
   // available_data_cond_. SetBufferAndSignal should unblock it from another
   // thread.
-  virtual int64_t Read(int64_t bytes_to_read, const void** destination_buffer);
+  int64_t Read(int64_t bytes_to_read, const void** destination_buffer) override;
 
   // See volume_reader.h for description.
-  virtual int64_t Seek(int64_t offset, int whence);
+  int64_t Seek(int64_t offset, int whence) override;
 
   // Sets the request Id to be used by the reader.
   void SetRequestId(const std::string& request_id);
@@ -70,11 +71,11 @@
   // See volume_reader.h for description. The method blocks on
   // available_passphrase_cond_. SetPassphraseAndSignal should unblock it from
   // another thread.
-  virtual std::unique_ptr<std::string> Passphrase();
+  std::unique_ptr<std::string> Passphrase() override;
 
-  virtual int64_t offset() { return offset_; }
+  int64_t offset() override;
 
-  int64_t archive_size() { return archive_size_; }
+  int64_t archive_size() override;
 
  private:
   // Request a chunk of length number of bytes from JavaScript starting from
@@ -94,14 +95,11 @@
   std::string available_passphrase_;  // Stores a passphrase from JavaScript.
   bool passphrase_error_;  // Marks an error in getting the passphrase.
 
-  // Must use POSIX mutexes instead of pp::Lock because there is no pp::Cond.
-  // pp::Lock uses POSIX mutexes anyway on Linux, but pp::Lock can also pe used
-  // on other operating systems as Windows. For now this is not an issue as this
-  // extension is used only on Chromebooks. The shared_state_lock_ is used to
-  // protect members which are accessed by more than one thread.
-  pthread_mutex_t shared_state_lock_;
-  pthread_cond_t available_data_cond_;
-  pthread_cond_t available_passphrase_cond_;
+  // The shared_state_lock_ is used to protect members which are accessed by
+  // more than one thread.
+  base::Lock shared_state_lock_;
+  base::ConditionVariable available_data_cond_;
+  base::ConditionVariable available_passphrase_cond_;
 
   int64_t offset_;  // The offset from where read should be done.
   int64_t last_read_chunk_offset_;  // The offset reached after last call to
diff --git a/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.cc b/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.cc
index bfec94d..0836294 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.cc
+++ b/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.cc
@@ -16,10 +16,10 @@
   return 0;  // Not important.
 }
 
-int64_t FakeVolumeReader::Seek(int64_t offset, int whence) {
+int64_t FakeVolumeReader::Seek(int64_t offset, base::File::Whence whence) {
   return 0;  // Not important.
 }
 
-const char* FakeVolumeReader::Passphrase() {
+std::unique_ptr<std::string> FakeVolumeReader::Passphrase() {
   return NULL;  // Not important.
 }
diff --git a/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.h b/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.h
index 24f242f..1b2d631 100644
--- a/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.h
+++ b/chrome/browser/resources/chromeos/zip_archiver/unpacker-test/cpp/fake_volume_reader.h
@@ -15,12 +15,12 @@
 class FakeVolumeReader : public VolumeReader {
  public:
   FakeVolumeReader();
-  virtual ~FakeVolumeReader();
+  ~FakeVolumeReader() override;
 
-  int64_t Read(int64_t bytes_to_read, const void** destination_buffer);
+  int64_t Read(int64_t bytes_to_read, const void** destination_buffer) override;
   int64_t Skip(int64_t bytes_to_skip);
-  int64_t Seek(int64_t offset, int whence);
-  const char* Passphrase();
+  int64_t Seek(int64_t offset, base::File::Whence whence) override;
+  std::unique_ptr<std::string> Passphrase() override;
 };
 
 #endif  // CHROME_BROWSER_RESOURCES_CHROMEOS_ZIP_ARCHIVER_UNPACKER_TEST_CPP_FAKE_VOLUME_READER_H_
diff --git a/chrome/browser/resources/md_bookmarks/app.html b/chrome/browser/resources/md_bookmarks/app.html
index 348cd0a..14d0ae99 100644
--- a/chrome/browser/resources/md_bookmarks/app.html
+++ b/chrome/browser/resources/md_bookmarks/app.html
@@ -70,7 +70,7 @@
     <bookmarks-toolbar sidebar-width="[[sidebarWidth_]]" role="banner">
     </bookmarks-toolbar>
     <div id="main-container">
-       <div id="sidebar" role="navigation" aria-label="$i18n{sidebarAxLabel}">
+       <div id="sidebar" role="tree" aria-label="$i18n{sidebarAxLabel}">
          <bookmarks-folder-node item-id="0" depth="-1"></bookmarks-folder-node>
       </div>
       <div id="splitter"></div>
diff --git a/chrome/browser/resources/md_bookmarks/folder_node.html b/chrome/browser/resources/md_bookmarks/folder_node.html
index 8d766470..536e318 100644
--- a/chrome/browser/resources/md_bookmarks/folder_node.html
+++ b/chrome/browser/resources/md_bookmarks/folder_node.html
@@ -82,7 +82,8 @@
         on-contextmenu="onContextMenu_"
         tabindex$="[[getTabIndex_(selectedFolder_, itemId)]]"
         hidden="[[isRootFolder_(depth)]]"
-        role="treeitem">
+        role="treeitem"
+        aria-owns="descendants">
       <template is="dom-if" if="[[hasChildFolder_]]">
         <paper-icon-button-light id="arrow">
           <button on-click="toggleFolder_"
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.html b/chrome/browser/resources/print_preview/new/pages_settings.html
index 552596c5..22e39988 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.html
+++ b/chrome/browser/resources/print_preview/new/pages_settings.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/paper-radio-group/paper-radio-group.html">
 <link rel="import" href="../data/document_info.html">
 <link rel="import" href="../print_preview_utils.html">
 <link rel="import" href="input_behavior.html">
@@ -13,11 +14,15 @@
 <dom-module id="print-preview-pages-settings">
   <template>
     <style include="print-preview-shared">
+      :host {
+        --paper-radio-group-item-padding: 0;
+      }
+
       :host([error-state_='0']) #pageSettingsCustomInput {
         --cr-input-error-display: none;
       }
 
-      :host(:not([error-state_='0'])) #custom-radio-button {
+      :host(:not([error-state_='0'])) #customRadioButton {
         /* Margin = -1 * error height = -1 * (16px + 2 lines * line-height) */
         --cr-radio-button-disc: {
           margin-top: calc(-16px - 2 * .75rem);
@@ -38,30 +43,29 @@
         class="input-settings-section multirow-controls">
       <span slot="title">$i18n{pagesLabel}</span>
       <div slot="controls" id="controls">
-        <div class="radio">
-          <cr-radio-button name="pages" id="all-radio-button"
-              checked$="[[!customSelected_]]"
-              disabled$="[[getDisabled_(disabled)]]"
-              on-click="onAllRadioClick_">
-            <span>$i18n{optionAllPages}</span>
+        <paper-radio-group selectable="cr-radio-button" class="radio"
+            disabled$="[[controlsDisabled_]]"
+            selected="{{optionSelected_}}">
+          <cr-radio-button name="[[pagesValueEnum_.ALL]]" id="allRadioButton"
+              disabled$="[[controlsDisabled_]]"
+              label="$i18n{optionAllPages}">
           </cr-radio-button>
-          <cr-radio-button name="pages" id="custom-radio-button"
-              checked$="[[customSelected_]]"
-              disabled$="[[getDisabled_(disabled)]]"
-              aria-label="$i18n{optionCustomPages}"
-              on-click="onCustomRadioClick_"
-              on-focus="onCustomRadioFocus_"
-              on-blur="onCustomInputBlur_">
+          <cr-radio-button id="customRadioButton"
+              disabled$="[[controlsDisabled_]]"
+              name="[[pagesValueEnum_.CUSTOM]]"
+              on-blur="onCustomRadioBlur_"
+              aria-label="$i18n{optionCustomPages}">
             <cr-input id="pageSettingsCustomInput" type="text"
                 data-timeout-delay="500" on-keydown="onKeydown_"
-                disabled$="[[getDisabled_(disabled)]]" spellcheck="false"
+                disabled$="[[controlsDisabled_]]" spellcheck="false"
                 on-focus="onCustomInputFocus_" on-blur="onCustomInputBlur_"
+                tabindex="[[computeTabIndex_(optionSelected_)]]"
                 placeholder="$i18n{examplePageRangeText}"
                 error-message="[[getHintMessage_(errorState_,
                     documentInfo.pageCount)]]">
             </cr-input>
           </cr-radio-button>
-        </div>
+        </paper-radio-group>
       </div>
     </print-preview-settings-section>
   </template>
diff --git a/chrome/browser/resources/print_preview/new/pages_settings.js b/chrome/browser/resources/print_preview/new/pages_settings.js
index 6207c58..5bc0f1a 100644
--- a/chrome/browser/resources/print_preview/new/pages_settings.js
+++ b/chrome/browser/resources/print_preview/new/pages_settings.js
@@ -12,6 +12,12 @@
   OUT_OF_BOUNDS: 2,
 };
 
+/** @enum {string} */
+const PagesValue = {
+  ALL: 'all',
+  CUSTOM: 'custom',
+};
+
 Polymer({
   is: 'print-preview-pages-settings',
 
@@ -33,14 +39,28 @@
       computed: 'computeAllPagesArray_(documentInfo.pageCount)',
     },
 
-    /** @private {boolean} */
-    customSelected_: {
+    /** @private {string} */
+    optionSelected_: {
       type: Boolean,
-      value: false,
+      value: PagesValue.ALL,
+      observer: 'onOptionSelectedChange_',
     },
 
     disabled: Boolean,
 
+    /**
+     * Note: |disabled| specifies whether printing, and any settings section
+     * not in an error state, is disabled. |controlsDisabled_| specifies whether
+     * the pages section should be disabled, based on the value of |disabled|
+     * and the state of this section.
+     * @private {boolean} Whether this section is disabled.
+     */
+    controlsDisabled_: {
+      type: Boolean,
+      computed: 'computeControlsDisabled_(disabled, errorState_)',
+      observer: 'onControlsDisabledChanged_',
+    },
+
     /** @private {number} */
     errorState_: {
       type: Number,
@@ -52,7 +72,7 @@
     pagesToPrint_: {
       type: Array,
       computed: 'computePagesToPrint_(' +
-          'inputString_, customSelected_, allPagesArray_)',
+          'inputString_, optionSelected_, allPagesArray_)',
     },
 
     /** @private {!Array<{to: number, from: number}>} */
@@ -60,6 +80,15 @@
       type: Array,
       computed: 'computeRangesToPrint_(pagesToPrint_, allPagesArray_)',
     },
+
+    /**
+     * Mirroring the enum so that it can be used from HTML bindings.
+     * @private
+     */
+    pagesValueEnum_: {
+      type: Object,
+      value: PagesValue,
+    },
   },
 
   observers: [
@@ -91,7 +120,7 @@
    * |disabled|.
    * @private
    */
-  getDisabled_: function() {
+  computeControlsDisabled_: function() {
     // Disable the input if other settings are responsible for the error state.
     return this.errorState_ == PagesInputErrorState.NO_ERROR && this.disabled;
   },
@@ -123,7 +152,7 @@
    * @private
    */
   computePagesToPrint_: function() {
-    if (!this.customSelected_ || this.inputString_ === '') {
+    if (this.optionSelected_ === PagesValue.ALL || this.inputString_ === '') {
       this.errorState_ = PagesInputErrorState.NO_ERROR;
       return this.allPagesArray_;
     }
@@ -274,34 +303,17 @@
   },
 
   /** @private */
-  onAllRadioClick_: function() {
-    this.customSelected_ = false;
-  },
-
-  /**
-   * @param {Event} event Contains information about where focus came from.
-   * @private
-   */
-  onCustomRadioFocus_: function(event) {
-    if (event.relatedTarget !== this.$.pageSettingsCustomInput)
-      this.onCustomRadioClick_();
-  },
-
-  /** @private */
-  onCustomRadioClick_: function() {
-    /** @type {!CrInputElement} */ (this.$.pageSettingsCustomInput)
-        .inputElement.focus();
-  },
-
-  /** @private */
-  onCustomInputFocus_: function() {
-    this.customSelected_ = true;
+  onOptionSelectedChange_: function() {
+    if (this.optionSelected_ === PagesValue.CUSTOM) {
+      /** @type {!CrInputElement} */ (this.$.pageSettingsCustomInput)
+          .inputElement.focus();
+    }
   },
 
   /** @private */
   resetIfEmpty_: function() {
     if (this.inputString_ === '')
-      this.customSelected_ = false;
+      this.optionSelected_ = PagesValue.ALL;
   },
 
   /**
@@ -311,6 +323,24 @@
     if (e.key == 'Enter') {
       this.resetAndUpdate();
       this.resetIfEmpty_();
+    } else if (e.shiftKey && e.key == 'Tab') {
+      this.$.customRadioButton.focus();
+      e.stopPropagation();
+      e.preventDefault();
+    } else if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
+      e.stopPropagation();
+    }
+  },
+
+  /**
+   * @param {Event} event Contains information about where focus is going.
+   * @private
+   */
+  onCustomRadioBlur_: function(event) {
+    if (event.relatedTarget !=
+        /** @type {!CrInputElement} */
+        (this.$.pageSettingsCustomInput).inputElement) {
+      this.resetIfEmpty_();
     }
   },
 
@@ -321,8 +351,38 @@
   onCustomInputBlur_: function(event) {
     this.resetAndUpdate();
 
-    if (event.relatedTarget != this.$$('#custom-radio-button'))
+    if (event.relatedTarget != this.$.customRadioButton) {
       this.resetIfEmpty_();
+
+      // Manually set tab index to -1, so that this is not identified as the
+      // target for the radio group if the user navigates back.
+      this.$.customRadioButton.tabIndex = -1;
+    }
+  },
+
+  /** @private */
+  onCustomInputFocus_: function() {
+    if (this.optionSelected_ !== PagesValue.CUSTOM)
+      this.optionSelected_ = PagesValue.CUSTOM;
+  },
+
+  /** @private */
+  onControlsDisabledChanged_: function() {
+    if (!this.controlsDisabled_) {
+      if (this.optionSelected_ === PagesValue.CUSTOM)
+        this.$.allRadioButton.tabIndex = -1;
+      else
+        this.$.customRadioButton.tabIndex = -1;
+    }
+  },
+
+  /**
+   * Gets a tab index for the custom input if it can be tabbed to.
+   * @return {number}
+   * @private
+   */
+  computeTabIndex_: function() {
+    return this.optionSelected_ === PagesValue.CUSTOM ? 0 : -1;
   },
 
   /**
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 640ee8f..dbc0364e 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -68,10 +68,13 @@
 #include "components/sync/base/report_unrecoverable_error.h"
 #include "components/sync/driver/async_directory_type_controller.h"
 #include "components/sync/driver/sync_api_component_factory.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_util.h"
+#include "components/sync/driver/syncable_service_based_model_type_controller.h"
 #include "components/sync/engine/passive_model_worker.h"
 #include "components/sync/engine/sequenced_model_worker.h"
 #include "components/sync/engine/ui_model_worker.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/user_events/user_event_service.h"
 #include "components/sync_bookmarks/bookmark_sync_service.h"
 #include "components/sync_preferences/pref_service_syncable.h"
@@ -93,6 +96,7 @@
 #include "chrome/browser/extensions/api/storage/settings_sync_util.h"
 #include "chrome/browser/extensions/extension_sync_service.h"
 #include "chrome/browser/sync/glue/extension_data_type_controller.h"
+#include "chrome/browser/sync/glue/extension_model_type_controller.h"
 #include "chrome/browser/sync/glue/extension_setting_data_type_controller.h"
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
@@ -124,6 +128,7 @@
 using content::BrowserThread;
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 using browser_sync::ExtensionDataTypeController;
+using browser_sync::ExtensionModelTypeController;
 using browser_sync::ExtensionSettingDataTypeController;
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 using browser_sync::SearchEngineDataTypeController;
@@ -295,15 +300,31 @@
   // App sync is enabled by default.  Register unless explicitly
   // disabled.
   if (!disabled_types.Has(syncer::APPS)) {
-    controllers.push_back(std::make_unique<ExtensionDataTypeController>(
-        syncer::APPS, error_callback, this, profile_));
+    if (base::FeatureList::IsEnabled(switches::kSyncPseudoUSSApps)) {
+      controllers.push_back(std::make_unique<ExtensionModelTypeController>(
+          syncer::APPS, GetModelTypeStoreService()->GetStoreFactory(),
+          base::BindOnce(&ChromeSyncClient::GetSyncableServiceForType,
+                         base::Unretained(this), syncer::APPS),
+          profile_));
+    } else {
+      controllers.push_back(std::make_unique<ExtensionDataTypeController>(
+          syncer::APPS, error_callback, this, profile_));
+    }
   }
 
   // Extension sync is enabled by default.  Register unless explicitly
   // disabled.
   if (!disabled_types.Has(syncer::EXTENSIONS)) {
-    controllers.push_back(std::make_unique<ExtensionDataTypeController>(
-        syncer::EXTENSIONS, error_callback, this, profile_));
+    if (base::FeatureList::IsEnabled(switches::kSyncPseudoUSSExtensions)) {
+      controllers.push_back(std::make_unique<ExtensionModelTypeController>(
+          syncer::EXTENSIONS, GetModelTypeStoreService()->GetStoreFactory(),
+          base::BindOnce(&ChromeSyncClient::GetSyncableServiceForType,
+                         base::Unretained(this), syncer::EXTENSIONS),
+          profile_));
+    } else {
+      controllers.push_back(std::make_unique<ExtensionDataTypeController>(
+          syncer::EXTENSIONS, error_callback, this, profile_));
+    }
   }
 
   // Extension setting sync is enabled by default.  Register unless explicitly
@@ -324,8 +345,16 @@
 #if !defined(OS_ANDROID)
   // Theme sync is enabled by default.  Register unless explicitly disabled.
   if (!disabled_types.Has(syncer::THEMES)) {
-    controllers.push_back(std::make_unique<ThemeDataTypeController>(
-        error_callback, this, profile_));
+    if (base::FeatureList::IsEnabled(switches::kSyncPseudoUSSThemes)) {
+      controllers.push_back(std::make_unique<ExtensionModelTypeController>(
+          syncer::THEMES, GetModelTypeStoreService()->GetStoreFactory(),
+          base::BindOnce(&ChromeSyncClient::GetSyncableServiceForType,
+                         base::Unretained(this), syncer::THEMES),
+          profile_));
+    } else {
+      controllers.push_back(std::make_unique<ThemeDataTypeController>(
+          error_callback, this, profile_));
+    }
   }
 
   // Search Engine sync is enabled by default.  Register unless explicitly
@@ -338,17 +367,33 @@
 #endif  // !defined(OS_ANDROID)
 
 #if BUILDFLAG(ENABLE_APP_LIST)
-  controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
-      syncer::APP_LIST, error_callback, this, syncer::GROUP_UI,
-      base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})));
+  if (base::FeatureList::IsEnabled(switches::kSyncPseudoUSSAppList)) {
+    controllers.push_back(
+        std::make_unique<syncer::SyncableServiceBasedModelTypeController>(
+            syncer::APP_LIST, GetModelTypeStoreService()->GetStoreFactory(),
+            base::BindOnce(&ChromeSyncClient::GetSyncableServiceForType,
+                           base::Unretained(this), syncer::APP_LIST)));
+  } else {
+    controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+        syncer::APP_LIST, error_callback, this, syncer::GROUP_UI,
+        base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})));
+  }
 #endif  // BUILDFLAG(ENABLE_APP_LIST)
 
 #if defined(OS_LINUX) || defined(OS_WIN)
   // Dictionary sync is enabled by default.
   if (!disabled_types.Has(syncer::DICTIONARY)) {
-    controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
-        syncer::DICTIONARY, error_callback, this, syncer::GROUP_UI,
-        base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})));
+    if (base::FeatureList::IsEnabled(switches::kSyncPseudoUSSDictionary)) {
+      controllers.push_back(
+          std::make_unique<syncer::SyncableServiceBasedModelTypeController>(
+              syncer::DICTIONARY, GetModelTypeStoreService()->GetStoreFactory(),
+              base::BindOnce(&ChromeSyncClient::GetSyncableServiceForType,
+                             base::Unretained(this), syncer::DICTIONARY)));
+    } else {
+      controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+          syncer::DICTIONARY, error_callback, this, syncer::GROUP_UI,
+          base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI})));
+    }
   }
 #endif  // defined(OS_LINUX) || defined(OS_WIN)
 
diff --git a/chrome/browser/sync/glue/extension_model_type_controller.cc b/chrome/browser/sync/glue/extension_model_type_controller.cc
new file mode 100644
index 0000000..619d96d9
--- /dev/null
+++ b/chrome/browser/sync/glue/extension_model_type_controller.cc
@@ -0,0 +1,39 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/sync/glue/extension_model_type_controller.h"
+
+#include <utility>
+
+#include "chrome/browser/profiles/profile.h"
+#include "extensions/browser/extension_system.h"
+
+namespace browser_sync {
+
+ExtensionModelTypeController::ExtensionModelTypeController(
+    syncer::ModelType type,
+    syncer::OnceModelTypeStoreFactory store_factory,
+    SyncableServiceProvider syncable_service_provider,
+    Profile* profile)
+    : SyncableServiceBasedModelTypeController(
+          type,
+          std::move(store_factory),
+          std::move(syncable_service_provider)),
+      profile_(profile) {
+  DCHECK(type == syncer::EXTENSIONS || type == syncer::APPS ||
+         type == syncer::THEMES);
+}
+
+ExtensionModelTypeController::~ExtensionModelTypeController() {}
+
+void ExtensionModelTypeController::LoadModels(
+    const syncer::ConfigureContext& configure_context,
+    const ModelLoadCallback& model_load_callback) {
+  DCHECK(CalledOnValidThread());
+  extensions::ExtensionSystem::Get(profile_)->InitForRegularProfile(
+      /*extensions_enabled=*/true);
+  ModelTypeController::LoadModels(configure_context, model_load_callback);
+}
+
+}  // namespace browser_sync
diff --git a/chrome/browser/sync/glue/extension_model_type_controller.h b/chrome/browser/sync/glue/extension_model_type_controller.h
new file mode 100644
index 0000000..1e61396
--- /dev/null
+++ b/chrome/browser/sync/glue/extension_model_type_controller.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 CHROME_BROWSER_SYNC_GLUE_EXTENSION_MODEL_TYPE_CONTROLLER_H_
+#define CHROME_BROWSER_SYNC_GLUE_EXTENSION_MODEL_TYPE_CONTROLLER_H_
+
+#include "base/macros.h"
+#include "components/sync/driver/syncable_service_based_model_type_controller.h"
+
+class Profile;
+
+namespace browser_sync {
+
+// Controller with custom logic to start the extensions machinery when sync is
+// starting. Analogous to ExtensionDataTypeController and
+// ThemeDataTypeController.
+class ExtensionModelTypeController
+    : public syncer::SyncableServiceBasedModelTypeController {
+ public:
+  ExtensionModelTypeController(
+      syncer::ModelType type,
+      syncer::OnceModelTypeStoreFactory store_factory,
+      SyncableServiceProvider syncable_service_provider,
+      Profile* profile);
+  ~ExtensionModelTypeController() override;
+
+  // DataTypeController overrides.
+  void LoadModels(const syncer::ConfigureContext& configure_context,
+                  const ModelLoadCallback& model_load_callback) override;
+
+ private:
+  Profile* const profile_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExtensionModelTypeController);
+};
+
+}  // namespace browser_sync
+
+#endif  // CHROME_BROWSER_SYNC_GLUE_EXTENSION_MODEL_TYPE_CONTROLLER_H_
diff --git a/chrome/browser/sync/profile_sync_test_util.cc b/chrome/browser/sync/profile_sync_test_util.cc
index a5550f7..c064cf0 100644
--- a/chrome/browser/sync/profile_sync_test_util.cc
+++ b/chrome/browser/sync/profile_sync_test_util.cc
@@ -11,6 +11,7 @@
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "chrome/browser/invalidation/deprecated_profile_invalidation_provider_factory.h"
+#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
@@ -19,6 +20,7 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/browser_sync/profile_sync_test_util.h"
+#include "components/invalidation/impl/invalidation_switches.h"
 #include "components/invalidation/impl/profile_invalidation_provider.h"
 #include "components/signin/core/browser/signin_manager.h"
 #include "components/sync/driver/startup_controller.h"
@@ -54,10 +56,16 @@
   init_params.start_behavior = ProfileSyncService::MANUAL_START;
   init_params.sync_client = std::move(sync_client);
   init_params.network_time_update_callback = base::DoNothing();
+  bool fcm_invalidations_enabled =
+      base::FeatureList::IsEnabled(invalidation::switches::kFCMInvalidations);
   init_params.invalidations_identity_provider =
-      invalidation::DeprecatedProfileInvalidationProviderFactory::GetForProfile(
-          profile)
-          ->GetIdentityProvider();
+      fcm_invalidations_enabled
+          ? invalidation::ProfileInvalidationProviderFactory::GetForProfile(
+                profile)
+                ->GetIdentityProvider()
+          : invalidation::DeprecatedProfileInvalidationProviderFactory::
+                GetForProfile(profile)
+                    ->GetIdentityProvider();
   init_params.url_loader_factory =
       content::BrowserContext::GetDefaultStoragePartition(profile)
           ->GetURLLoaderFactoryForBrowserProcess();
diff --git a/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc
index 2d8b246..4e88fd5 100644
--- a/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_apps_sync_test.cc
@@ -3,16 +3,36 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace {
 
 using apps_helper::AllProfilesHaveSameApps;
 using apps_helper::InstallApp;
 using apps_helper::InstallPlatformApp;
 
-class SingleClientAppsSyncTest : public SyncTest {
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(switches::kSyncPseudoUSSApps);
+    } else {
+      override_features_.InitAndDisableFeature(switches::kSyncPseudoUSSApps);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
+class SingleClientAppsSyncTest : public UssSwitchToggler, public SyncTest {
  public:
   SingleClientAppsSyncTest() : SyncTest(SINGLE_CLIENT) {}
 
@@ -22,12 +42,12 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientAppsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithNoApps) {
+IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, StartWithNoApps) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithSomeLegacyApps) {
+IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, StartWithSomeLegacyApps) {
   ASSERT_TRUE(SetupClients());
 
   const int kNumApps = 5;
@@ -40,7 +60,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, StartWithSomePlatformApps) {
+IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, StartWithSomePlatformApps) {
   ASSERT_TRUE(SetupClients());
 
   const int kNumApps = 5;
@@ -53,7 +73,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, InstallSomeLegacyApps) {
+IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, InstallSomeLegacyApps) {
   ASSERT_TRUE(SetupSync());
 
   const int kNumApps = 5;
@@ -66,7 +86,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, InstallSomePlatformApps) {
+IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, InstallSomePlatformApps) {
   ASSERT_TRUE(SetupSync());
 
   const int kNumApps = 5;
@@ -79,7 +99,7 @@
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientAppsSyncTest, InstallSomeApps) {
+IN_PROC_BROWSER_TEST_P(SingleClientAppsSyncTest, InstallSomeApps) {
   ASSERT_TRUE(SetupSync());
 
   int i = 0;
@@ -99,3 +119,9 @@
   ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 }
+
+INSTANTIATE_TEST_CASE_P(USS,
+                        SingleClientAppsSyncTest,
+                        ::testing::Values(false, true));
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc b/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc
index 763e1a3..c4e96bc 100644
--- a/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_dictionary_sync_test.cc
@@ -3,12 +3,35 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sync/test/integration/dictionary_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
 
-class SingleClientDictionarySyncTest : public SyncTest {
+namespace {
+
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(
+          switches::kSyncPseudoUSSDictionary);
+    } else {
+      override_features_.InitAndDisableFeature(
+          switches::kSyncPseudoUSSDictionary);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
+class SingleClientDictionarySyncTest : public UssSwitchToggler,
+                                       public SyncTest {
  public:
   SingleClientDictionarySyncTest() : SyncTest(SINGLE_CLIENT) {}
   ~SingleClientDictionarySyncTest() override {}
@@ -17,7 +40,7 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientDictionarySyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(SingleClientDictionarySyncTest, Sanity) {
+IN_PROC_BROWSER_TEST_P(SingleClientDictionarySyncTest, Sanity) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
   ASSERT_TRUE(dictionary_helper::DictionariesMatch());
@@ -31,3 +54,9 @@
   ASSERT_TRUE(UpdatedProgressMarkerChecker(GetSyncService(0)).Wait());
   ASSERT_TRUE(dictionary_helper::DictionariesMatch());
 }
+
+INSTANTIATE_TEST_CASE_P(USS,
+                        SingleClientDictionarySyncTest,
+                        ::testing::Values(false, true));
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
index cda11f3..38c6f7a5e 100644
--- a/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_extensions_sync_test.cc
@@ -3,20 +3,43 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sync/test/integration/await_match_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/extensions_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/test/fake_server/fake_server.h"
 
+namespace {
+
 using extensions_helper::AllProfilesHaveSameExtensionsAsVerifier;
 using extensions_helper::DisableExtension;
 using extensions_helper::GetInstalledExtensions;
 using extensions_helper::InstallExtension;
 using extensions_helper::InstallExtensionForAllProfiles;
 
-class SingleClientExtensionsSyncTest : public SyncTest {
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(
+          switches::kSyncPseudoUSSExtensions);
+    } else {
+      override_features_.InitAndDisableFeature(
+          switches::kSyncPseudoUSSExtensions);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
+class SingleClientExtensionsSyncTest : public UssSwitchToggler,
+                                       public SyncTest {
  public:
   SingleClientExtensionsSyncTest() : SyncTest(SINGLE_CLIENT) {}
 
@@ -26,12 +49,12 @@
   DISALLOW_COPY_AND_ASSIGN(SingleClientExtensionsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, StartWithNoExtensions) {
+IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest, StartWithNoExtensions) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest,
                        StartWithSomeExtensions) {
   ASSERT_TRUE(SetupClients());
 
@@ -45,7 +68,7 @@
   ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, InstallSomeExtensions) {
+IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest, InstallSomeExtensions) {
   ASSERT_TRUE(SetupSync());
 
   const int kNumExtensions = 5;
@@ -66,7 +89,7 @@
 
 // Tests the case of an uninstall from the server conflicting with a local
 // modification, which we expect to be resolved in favor of the uninstall.
-IN_PROC_BROWSER_TEST_F(SingleClientExtensionsSyncTest, UninstallWinsConflicts) {
+IN_PROC_BROWSER_TEST_P(SingleClientExtensionsSyncTest, UninstallWinsConflicts) {
   ASSERT_TRUE(SetupClients());
 
   // Start with an extension installed, and setup sync.
@@ -102,3 +125,9 @@
   EXPECT_TRUE(checker.Wait());
   EXPECT_TRUE(GetInstalledExtensions(GetProfile(0)).empty());
 }
+
+INSTANTIATE_TEST_CASE_P(USS,
+                        SingleClientExtensionsSyncTest,
+                        ::testing::Values(false, true));
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
index fa5b140d..63f68eb 100644
--- a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
@@ -11,6 +12,7 @@
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "content/public/test/test_utils.h"
 
 using themes_helper::GetCustomTheme;
@@ -23,7 +25,23 @@
 
 namespace {
 
-class SingleClientThemesSyncTest : public SyncTest {
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(switches::kSyncPseudoUSSThemes);
+    } else {
+      override_features_.InitAndDisableFeature(switches::kSyncPseudoUSSThemes);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
+class SingleClientThemesSyncTest : public UssSwitchToggler, public SyncTest {
  public:
   SingleClientThemesSyncTest() : SyncTest(SINGLE_CLIENT) {}
   ~SingleClientThemesSyncTest() override {}
@@ -36,7 +54,7 @@
 // start with SetupClients(), change the theme state, then call
 // SetupSync()).
 
-IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, CustomTheme) {
+IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, CustomTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   EXPECT_FALSE(UsingCustomTheme(GetProfile(0)));
@@ -55,9 +73,9 @@
 
 // TODO(sync): Fails on Chrome OS. See http://crbug.com/84575.
 #if defined(OS_CHROMEOS)
-IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, DISABLED_NativeTheme) {
+IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, DISABLED_NativeTheme) {
 #else
-IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, NativeTheme) {
+IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, NativeTheme) {
 #endif  // OS_CHROMEOS
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
@@ -79,7 +97,7 @@
   EXPECT_TRUE(UsingSystemTheme(verifier()));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientThemesSyncTest, DefaultTheme) {
+IN_PROC_BROWSER_TEST_P(SingleClientThemesSyncTest, DefaultTheme) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   SetCustomTheme(GetProfile(0));
@@ -100,4 +118,8 @@
   EXPECT_TRUE(UsingDefaultTheme(verifier()));
 }
 
+INSTANTIATE_TEST_CASE_P(USS,
+                        SingleClientThemesSyncTest,
+                        ::testing::Values(false, true));
+
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index b30f6a80..b32e000 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/invalidation/deprecated_profile_invalidation_provider_factory.h"
+#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/net/system_network_context_manager.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -647,6 +648,8 @@
 }
 
 void SyncTest::SetUpInvalidations(int index) {
+  bool fcm_invalidations_enabled =
+      base::FeatureList::IsEnabled(invalidation::switches::kFCMInvalidations);
   switch (server_type_) {
     case EXTERNAL_LIVE_SERVER:
       // DO NOTHING. External live sync servers use GCM to notify profiles of
@@ -655,12 +658,22 @@
       break;
 
     case IN_PROCESS_FAKE_SERVER: {
-      KeyedService* test_factory =
-          invalidation::DeprecatedProfileInvalidationProviderFactory::
-              GetInstance()
-                  ->SetTestingFactoryAndUse(
-                      GetProfile(index),
-                      BuildFakeServerProfileInvalidationProvider);
+      KeyedService* test_factory;
+      if (fcm_invalidations_enabled) {
+        test_factory =
+            invalidation::ProfileInvalidationProviderFactory::GetInstance()
+                ->SetTestingFactoryAndUse(
+                    GetProfile(index),
+                    BuildFakeServerProfileInvalidationProvider);
+
+      } else {
+        test_factory =
+            invalidation::DeprecatedProfileInvalidationProviderFactory::
+                GetInstance()
+                    ->SetTestingFactoryAndUse(
+                        GetProfile(index),
+                        BuildFakeServerProfileInvalidationProvider);
+      }
       invalidation::InvalidationService* invalidation_service =
           static_cast<invalidation::ProfileInvalidationProvider*>(test_factory)
               ->GetInvalidationService();
@@ -683,8 +696,15 @@
               TestUsesSelfNotifications()
                   ? BuildSelfNotifyingP2PProfileInvalidationProvider
                   : BuildRealisticP2PProfileInvalidationProvider;
-      invalidation::DeprecatedProfileInvalidationProviderFactory::GetInstance()
-          ->SetTestingFactoryAndUse(GetProfile(index), invalidation_provider);
+      if (fcm_invalidations_enabled) {
+        invalidation::ProfileInvalidationProviderFactory::GetInstance()
+            ->SetTestingFactoryAndUse(GetProfile(index), invalidation_provider);
+      } else {
+        invalidation::DeprecatedProfileInvalidationProviderFactory::
+            GetInstance()
+                ->SetTestingFactoryAndUse(GetProfile(index),
+                                          invalidation_provider);
+      }
   }
 }
 
@@ -704,10 +724,20 @@
     }
     case SERVER_TYPE_UNDECIDED:
     case LOCAL_PYTHON_SERVER:
-      invalidation::InvalidationService* invalidation_service =
-          invalidation::DeprecatedProfileInvalidationProviderFactory::
-              GetForProfile(GetProfile(index))
-                  ->GetInvalidationService();
+      bool fcm_invalidations_enabled = base::FeatureList::IsEnabled(
+          invalidation::switches::kFCMInvalidations);
+      invalidation::InvalidationService* invalidation_service;
+      if (fcm_invalidations_enabled) {
+        invalidation_service =
+            invalidation::ProfileInvalidationProviderFactory::GetForProfile(
+                GetProfile(index))
+                ->GetInvalidationService();
+      } else {
+        invalidation_service =
+            invalidation::DeprecatedProfileInvalidationProviderFactory::
+                GetForProfile(GetProfile(index))
+                    ->GetInvalidationService();
+      }
       invalidation::P2PInvalidationService* p2p_invalidation_service =
           static_cast<invalidation::P2PInvalidationService*>(
               invalidation_service);
diff --git a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
index ce508b0..1f90ad4 100644
--- a/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_apps_sync_test.cc
@@ -6,6 +6,7 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/bookmark_app_helper.h"
@@ -22,6 +23,7 @@
 #include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
 #include "chrome/common/extensions/manifest_handlers/app_theme_color_info.h"
+#include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/model/string_ordinal.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
@@ -47,6 +49,22 @@
 
 namespace {
 
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(switches::kSyncPseudoUSSApps);
+    } else {
+      override_features_.InitAndDisableFeature(switches::kSyncPseudoUSSApps);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
 extensions::ExtensionRegistry* GetExtensionRegistry(Profile* profile) {
   return extensions::ExtensionRegistry::Get(profile);
 }
@@ -57,7 +75,7 @@
 
 }  // namespace
 
-class TwoClientAppsSyncTest : public SyncTest {
+class TwoClientAppsSyncTest : public UssSwitchToggler, public SyncTest {
  public:
   TwoClientAppsSyncTest() : SyncTest(TWO_CLIENT) {
     DisableVerifier();
@@ -71,12 +89,12 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientAppsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(StartWithNoApps)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(StartWithNoApps)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(StartWithSameApps)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(StartWithSameApps)) {
   ASSERT_TRUE(SetupClients());
 
   const int kNumApps = 5;
@@ -98,7 +116,7 @@
 #else
 #define MAYBE_StartWithDifferentApps StartWithDifferentApps
 #endif
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, MAYBE_StartWithDifferentApps) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, MAYBE_StartWithDifferentApps) {
   ASSERT_TRUE(SetupClients());
 
   int i = 0;
@@ -131,7 +149,7 @@
 // Install some apps on both clients, then sync.  Then install some apps on only
 // one client, some on only the other, and then sync again.  Both clients should
 // end up with all apps, and the app and page ordinals should be identical.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
                        E2E_ENABLED(InstallDifferentApps)) {
   ASSERT_TRUE(SetupClients());
 
@@ -158,7 +176,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Add)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(Add)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -167,7 +185,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Uninstall)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(Uninstall)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -182,7 +200,7 @@
 // client and sync again. Now install a new app on the first client and sync.
 // Both client should only have the second app, with identical app and page
 // ordinals.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
                        E2E_ENABLED(UninstallThenInstall)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -197,7 +215,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(Merge)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(Merge)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -215,7 +233,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
                        E2E_ENABLED(UpdateEnableDisableApp)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -230,7 +248,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
                        E2E_ENABLED(UpdateIncognitoEnableDisable)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -248,7 +266,7 @@
 // Install the same app on both clients, then sync. Change the page ordinal on
 // one client and sync. Both clients should have the updated page ordinal for
 // the app.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdatePageOrdinal)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(UpdatePageOrdinal)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -265,7 +283,7 @@
 // Install the same app on both clients, then sync. Change the app launch
 // ordinal on one client and sync. Both clients should have the updated app
 // launch ordinal for the app.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest,
                        E2E_ENABLED(UpdateAppLaunchOrdinal)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
@@ -284,7 +302,7 @@
 // Adjust the CWS location within a page on the first client and sync. Adjust
 // which page the CWS appears on and sync. Both clients should have the same
 // page and app launch ordinal values for the CWS.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdateCWSOrdinals)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(UpdateCWSOrdinals)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -313,7 +331,7 @@
 
 // Adjust the launch type on the first client and sync. Both clients should
 // have the same launch type values for the CWS.
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, E2E_ENABLED(UpdateLaunchType)) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, E2E_ENABLED(UpdateLaunchType)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AppsMatchChecker().Wait());
 
@@ -339,7 +357,7 @@
       extensions::LAUNCH_TYPE_REGULAR);
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, UnexpectedLaunchType) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, UnexpectedLaunchType) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -378,7 +396,7 @@
   ASSERT_TRUE(AppsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppBasic) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, BookmarkAppBasic) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -411,7 +429,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppMinimal) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, BookmarkAppMinimal) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -453,7 +471,7 @@
   return nullptr;
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, BookmarkAppThemeColor) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, BookmarkAppThemeColor) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -489,7 +507,7 @@
   EXPECT_EQ(SK_ColorBLUE, theme_color.value());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientAppsSyncTest, IsLocallyInstalled) {
+IN_PROC_BROWSER_TEST_P(TwoClientAppsSyncTest, IsLocallyInstalled) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameApps());
 
@@ -541,3 +559,7 @@
 // TODO(akalin): Add tests exercising:
 //   - Offline installation/uninstallation behavior
 //   - App-specific properties
+
+INSTANTIATE_TEST_CASE_P(USS,
+                        TwoClientAppsSyncTest,
+                        ::testing::Values(false, true));
diff --git a/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc b/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
index 45983cb..f4a97f05 100644
--- a/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
@@ -4,6 +4,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sync/test/integration/dictionary_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
@@ -11,10 +12,31 @@
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/spellcheck/common/spellcheck_common.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace {
 
 using spellcheck::kMaxSyncableDictionaryWords;
 
-class TwoClientDictionarySyncTest : public SyncTest {
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(
+          switches::kSyncPseudoUSSDictionary);
+    } else {
+      override_features_.InitAndDisableFeature(
+          switches::kSyncPseudoUSSDictionary);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
+class TwoClientDictionarySyncTest : public UssSwitchToggler, public SyncTest {
  public:
   TwoClientDictionarySyncTest() : SyncTest(TWO_CLIENT) {}
   ~TwoClientDictionarySyncTest() override {}
@@ -25,7 +47,7 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientDictionarySyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest, E2E_ENABLED(Sanity)) {
+IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest, E2E_ENABLED(Sanity)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
   ASSERT_TRUE(DictionaryMatchChecker().Wait());
@@ -54,7 +76,7 @@
   ASSERT_EQ(words.size(), dictionary_helper::GetDictionarySize(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
                        E2E_ENABLED(SimultaneousAdd)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -66,7 +88,7 @@
   ASSERT_EQ(1UL, dictionary_helper::GetDictionarySize(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
                        E2E_ENABLED(SimultaneousRemove)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -83,7 +105,7 @@
   ASSERT_EQ(0UL, dictionary_helper::GetDictionarySize(0));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
                        E2E_ENABLED(AddDifferentToEach)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -97,7 +119,7 @@
             static_cast<int>(dictionary_helper::GetDictionarySize(0)));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest,
                        E2E_ENABLED(RemoveOnAAddOnB)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
@@ -118,7 +140,7 @@
 
 // Tests the case where a client has more words added than the
 // kMaxSyncableDictionaryWords limit.
-IN_PROC_BROWSER_TEST_F(TwoClientDictionarySyncTest, Limit) {
+IN_PROC_BROWSER_TEST_P(TwoClientDictionarySyncTest, Limit) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
   dictionary_helper::LoadDictionaries();
   ASSERT_TRUE(DictionaryMatchChecker().Wait());
@@ -162,3 +184,9 @@
   ASSERT_TRUE(
       NumDictionaryEntriesChecker(0, kMaxSyncableDictionaryWords).Wait());
 }
+
+INSTANTIATE_TEST_CASE_P(USS,
+                        TwoClientDictionarySyncTest,
+                        ::testing::Values(false, true));
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc b/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc
index 7fcfe7d..130def9 100644
--- a/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_extensions_sync_test.cc
@@ -3,11 +3,15 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/extensions_helper.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace {
 
 using extensions_helper::AllProfilesHaveSameExtensions;
 using extensions_helper::DisableExtension;
@@ -19,7 +23,25 @@
 using extensions_helper::InstallExtension;
 using extensions_helper::UninstallExtension;
 
-class TwoClientExtensionsSyncTest : public SyncTest {
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(
+          switches::kSyncPseudoUSSExtensions);
+    } else {
+      override_features_.InitAndDisableFeature(
+          switches::kSyncPseudoUSSExtensions);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
+class TwoClientExtensionsSyncTest : public UssSwitchToggler, public SyncTest {
  public:
   TwoClientExtensionsSyncTest() : SyncTest(TWO_CLIENT) { DisableVerifier(); }
 
@@ -29,7 +51,7 @@
   DISALLOW_COPY_AND_ASSIGN(TwoClientExtensionsSyncTest);
 };
 
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(StartWithNoExtensions)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(ExtensionsMatchChecker().Wait());
@@ -48,7 +70,7 @@
 #else
 #define MAYBE_StartWithSameExtensions StartWithSameExtensions
 #endif
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(MAYBE_StartWithSameExtensions)) {
   ASSERT_TRUE(SetupClients());
 
@@ -70,7 +92,7 @@
 #else
 #define MAYBE_StartWithDifferentExtensions StartWithDifferentExtensions
 #endif
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
                        MAYBE_E2E(MAYBE_StartWithDifferentExtensions)) {
   ASSERT_TRUE(SetupClients());
 
@@ -99,7 +121,7 @@
       static_cast<int>(GetInstalledExtensions(GetProfile(0)).size()));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(InstallDifferentExtensions)) {
   ASSERT_TRUE(SetupClients());
 
@@ -130,7 +152,7 @@
       static_cast<int>(GetInstalledExtensions(GetProfile(0)).size()));
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, MAYBE_E2E(Add)) {
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest, MAYBE_E2E(Add)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
 
@@ -140,7 +162,7 @@
   EXPECT_EQ(1u, GetInstalledExtensions(GetProfile(0)).size());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest, MAYBE_E2E(Uninstall)) {
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest, MAYBE_E2E(Uninstall)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
 
@@ -152,7 +174,7 @@
   EXPECT_TRUE(GetInstalledExtensions(GetProfile(0)).empty());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
                        MAYBE_E2E(UpdateEnableDisableExtension)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
@@ -171,7 +193,7 @@
   ASSERT_TRUE(ExtensionsMatchChecker().Wait());
 }
 
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(UpdateIncognitoEnableDisable)) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
@@ -192,7 +214,7 @@
 
 // Regression test for bug 104399: ensure that an extension installed prior to
 // setting up sync, when uninstalled, is also uninstalled from sync.
-IN_PROC_BROWSER_TEST_F(TwoClientExtensionsSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientExtensionsSyncTest,
                        E2E_ENABLED(UninstallPreinstalledExtensions)) {
   ASSERT_TRUE(SetupClients());
   ASSERT_TRUE(AllProfilesHaveSameExtensions());
@@ -212,3 +234,9 @@
 
 // TODO(akalin): Add tests exercising:
 //   - Offline installation/uninstallation behavior
+
+INSTANTIATE_TEST_CASE_P(USS,
+                        TwoClientExtensionsSyncTest,
+                        ::testing::Values(false, true));
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
index 68beb142..a8d651cb 100644
--- a/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_themes_sync_test.cc
@@ -3,10 +3,14 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "chrome/browser/sync/test/integration/themes_helper.h"
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace {
 
 using themes_helper::GetCustomTheme;
 using themes_helper::GetThemeID;
@@ -17,7 +21,23 @@
 using themes_helper::UsingDefaultTheme;
 using themes_helper::UsingSystemTheme;
 
-class TwoClientThemesSyncTest : public SyncTest {
+// Class that enables or disables USS based on test parameter. Must be the first
+// base class of the test fixture.
+class UssSwitchToggler : public testing::WithParamInterface<bool> {
+ public:
+  UssSwitchToggler() {
+    if (GetParam()) {
+      override_features_.InitAndEnableFeature(switches::kSyncPseudoUSSThemes);
+    } else {
+      override_features_.InitAndDisableFeature(switches::kSyncPseudoUSSThemes);
+    }
+  }
+
+ private:
+  base::test::ScopedFeatureList override_features_;
+};
+
+class TwoClientThemesSyncTest : public UssSwitchToggler, public SyncTest {
  public:
   TwoClientThemesSyncTest() : SyncTest(TWO_CLIENT) {}
   ~TwoClientThemesSyncTest() override {}
@@ -31,7 +51,7 @@
 // Starts with default themes, then sets up sync and uses it to set all
 // profiles to use a custom theme.  Does not actually install any themes, but
 // instead verifies the custom theme is pending for install.
-IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest,
                        E2E_ENABLED(DefaultThenSyncCustom)) {
   ASSERT_TRUE(SetupSync());
 
@@ -53,7 +73,7 @@
 
 // Starts with custom themes, then sets up sync and uses it to set all profiles
 // to the system theme.
-IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest,
                        E2E_ENABLED(CustomThenSyncNative)) {
   ASSERT_TRUE(SetupClients());
 
@@ -73,7 +93,7 @@
 
 // Starts with custom themes, then sets up sync and uses it to set all profiles
 // to the default theme.
-IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest,
+IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest,
                        E2E_ENABLED(CustomThenSyncDefault)) {
   ASSERT_TRUE(SetupClients());
 
@@ -94,7 +114,7 @@
 //
 // Most other tests have significant coverage of model association.  This test
 // is intended to test steady-state scenarios.
-IN_PROC_BROWSER_TEST_F(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
+IN_PROC_BROWSER_TEST_P(TwoClientThemesSyncTest, E2E_ENABLED(CycleOptions)) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
   SetCustomTheme(GetProfile(0));
@@ -120,3 +140,9 @@
       ThemePendingInstallChecker(GetProfile(1), GetCustomTheme(1)).Wait());
   EXPECT_EQ(GetCustomTheme(1), GetThemeID(GetProfile(0)));
 }
+
+INSTANTIATE_TEST_CASE_P(USS,
+                        TwoClientThemesSyncTest,
+                        ::testing::Values(false, true));
+
+}  // namespace
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index ed643e0..aaf98f6 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/chromeos/arc/arc_session_manager.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/chromeos/arc/policy/arc_policy_util.h"
+#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
 #include "chrome/browser/image_decoder.h"
 #include "chrome/browser/profiles/profile.h"
@@ -1000,9 +1001,12 @@
                                         const bool launchable) {
   const std::string app_id = shortcut ? GetAppId(package_name, intent_uri)
                                       : GetAppId(package_name, activity);
-  // Do not add Play Store app for Public Session and Kiosk modes.
-  if (app_id == arc::kPlayStoreAppId && arc::IsRobotOrOfflineDemoAccountMode())
+  // TODO(khmel): Use show_in_launcher flag to hide the Play Store app.
+  if (app_id == arc::kPlayStoreAppId &&
+      arc::IsRobotOrOfflineDemoAccountMode() &&
+      !chromeos::DemoSession::IsDeviceInDemoMode()) {
     return;
+  }
 
   std::string updated_name = name;
   // Add "(beta)" string to Play Store. See crbug.com/644576 for details.
diff --git a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
index 891a0aa5..ea3d818 100644
--- a/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
+++ b/chrome/browser/ui/app_list/arc/arc_default_app_list.cc
@@ -1,3 +1,4 @@
+// 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.
 
@@ -9,13 +10,13 @@
 #include "base/json/json_file_value_serializer.h"
 #include "base/path_service.h"
 #include "base/task/post_task.h"
+#include "chrome/browser/chromeos/arc/arc_util.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_scoped_pref_update.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/common/chrome_paths.h"
-#include "components/arc/arc_util.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/browser/extension_system.h"
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.cc
index 772736f..cfaa529 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.cc
@@ -6,7 +6,9 @@
 
 #include <cmath>
 
+#include "ash/public/cpp/app_list/app_list_features.h"
 #include "base/logging.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/stl_util.h"
 
 namespace app_list {
@@ -16,12 +18,10 @@
 constexpr base::TimeDelta kSaveInternal = base::TimeDelta::FromHours(1);
 
 // A bin with index i has 5 adjacent bins as: i + 0, i + 1, i + 2, i + 22, and
-// i + 23. They each contributes to the final Rank score with different level:
-// 0.6 for i-th bin itself, 0.15 for i + 1 (one hour later) and i + 23 (
-// one hour earlier), 0.05 for i + 2 (two hours later) and i + 22 (two hours
-// earlier).
+// i + 23 which stand for the bin i itself, 1 hour later, 2 hours later,
+// 2 hours earlier and 1 hour earlier. Each adjacent bin contributes to the
+// final Rank score with weights from BinWeightsFromFlagOrDefault();
 constexpr int kAdjacentHourBin[] = {0, 1, 2, 22, 23};
-constexpr float kAdjacentHourWeight[] = {0.6, 0.15, 0.05, 0.05, 0.15};
 
 }  // namespace
 
@@ -157,6 +157,7 @@
   const auto& frequency_table_map =
       proto_.hour_app_launch_predictor().binned_frequency_table();
 
+  const std::vector<float> weights = BinWeightsFromFlagOrDefault();
   for (size_t i = 0; i < base::size(kAdjacentHourBin); ++i) {
     // Finds adjacent bin and weight.
     const int adj_bin =
@@ -166,7 +167,7 @@
       continue;
 
     const auto& frequency_table = find_frequency_table->second;
-    const float weight = kAdjacentHourWeight[i];
+    const float weight = weights[i];
 
     // Accumulates the frequency to the output.
     if (frequency_table.total_counts() > 0) {
@@ -256,6 +257,44 @@
   }
 }
 
+std::vector<float> HourAppLaunchPredictor::BinWeightsFromFlagOrDefault() {
+  const std::vector<float> default_weights = {0.6, 0.15, 0.05, 0.05, 0.15};
+  std::vector<float> weights(5);
+
+  // Get weights for adjacent bins. Every weight has to be within [0.0, 1.0]
+  // And the sum weights[1] + ..., + weights[4] also needs to be in [0.0, 1.0]
+  // so that the weight[0] is set to be 1.0 - (weights[1] + ..., + weights[4]).
+  weights[1] = static_cast<float>(base::GetFieldTrialParamByFeatureAsDouble(
+      app_list_features::kEnableAppSearchResultRanker,
+      "weight_1_hour_later_bin", -1.0));
+  if (weights[1] < 0.0 || weights[1] > 1.0)
+    return default_weights;
+
+  weights[2] = static_cast<float>(base::GetFieldTrialParamByFeatureAsDouble(
+      app_list_features::kEnableAppSearchResultRanker,
+      "weight_2_hour_later_bin", -1.0));
+  if (weights[2] < 0.0 || weights[2] > 1.0)
+    return default_weights;
+
+  weights[3] = static_cast<float>(base::GetFieldTrialParamByFeatureAsDouble(
+      app_list_features::kEnableAppSearchResultRanker,
+      "weight_2_hour_earlier_bin", -1.0));
+  if (weights[3] < 0.0 || weights[3] > 1.0)
+    return default_weights;
+
+  weights[4] = static_cast<float>(base::GetFieldTrialParamByFeatureAsDouble(
+      app_list_features::kEnableAppSearchResultRanker,
+      "weight_1_hour_earlier_bin", -1.0));
+  if (weights[4] < 0.0 || weights[4] > 1.0)
+    return default_weights;
+
+  weights[0] = 1.0 - weights[1] - weights[2] - weights[3] - weights[4];
+  if (weights[0] < 0.0 || weights[0] > 1.0)
+    return default_weights;
+
+  return weights;
+}
+
 void FakeAppLaunchPredictor::SetShouldSave(bool should_save) {
   should_save_ = should_save;
 }
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.h b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.h
index d62cdb1e..deb9c75 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.h
@@ -138,11 +138,17 @@
   FRIEND_TEST_ALL_PREFIXES(HourAppLaunchPredictorTest, GetTheRightBin);
   FRIEND_TEST_ALL_PREFIXES(HourAppLaunchPredictorTest, RankFromSingleBin);
   FRIEND_TEST_ALL_PREFIXES(HourAppLaunchPredictorTest, RankFromMultipleBin);
+  FRIEND_TEST_ALL_PREFIXES(HourAppLaunchPredictorTest, CheckDefaultWeights);
+  FRIEND_TEST_ALL_PREFIXES(HourAppLaunchPredictorTest, SetWeightsFromFlag);
   FRIEND_TEST_ALL_PREFIXES(HourAppLaunchPredictorTest, FromProtoDecay);
 
   // Returns current bin index of this predictor.
   int GetBin() const;
 
+  // Get weights of adjacent bins from flag which will be set using finch config
+  // for exploring possible options.
+  static std::vector<float> BinWeightsFromFlagOrDefault();
+
   // The proto for this predictor.
   AppLaunchPredictorProto proto_;
   // Last time the predictor was saved.
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc
index 22be4c8..dd16a86 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor_unittest.cc
@@ -4,11 +4,14 @@
 
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor.h"
 
+#include "ash/public/cpp/app_list/app_list_features.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_mock_clock_override.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_predictor_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::ElementsAre;
 using testing::UnorderedElementsAre;
 using testing::Pair;
 using testing::FloatEq;
@@ -153,6 +156,7 @@
 // Checks the apps are ranked based on frequency in a single bin.
 TEST_F(HourAppLaunchPredictorTest, RankFromSingleBin) {
   HourAppLaunchPredictor predictor;
+  const auto& weights = HourAppLaunchPredictor::BinWeightsFromFlagOrDefault();
 
   // Create a model that trained on kTarget1 3 times, and kTarget2 2 times.
   SetLocalTime(1, 10);
@@ -174,14 +178,16 @@
 
   SetLocalTime(1, 10);
   EXPECT_THAT(predictor.Rank(),
-              UnorderedElementsAre(Pair(kTarget1, FloatEq(0.6 * 0.6)),
-                                   Pair(kTarget2, FloatEq(0.6 * 0.4))));
+              UnorderedElementsAre(Pair(kTarget1, FloatEq(weights[0] * 0.6)),
+                                   Pair(kTarget2, FloatEq(weights[0] * 0.4))));
 }
 
 // Checks the apps are ranked based on linearly combined scores from adjacent
 // bins.
 TEST_F(HourAppLaunchPredictorTest, RankFromMultipleBin) {
   HourAppLaunchPredictor predictor;
+  const auto& weights = HourAppLaunchPredictor::BinWeightsFromFlagOrDefault();
+
   // For bin 10
   SetLocalTime(1, 10);
   predictor.Train(kTarget1);
@@ -210,15 +216,63 @@
   EXPECT_THAT(
       predictor.Rank(),
       UnorderedElementsAre(
-          Pair(kTarget1, FloatEq(0.6 * 2.0 / 3.0 + 0.15 * 0.5)),
-          Pair(kTarget2, FloatEq(0.6 * 1.0 / 3.0 + 0.15 * 0.5 + 0.05 * 1.0))));
+          Pair(kTarget1, FloatEq(weights[0] * 2.0 / 3.0 + weights[1] * 0.5)),
+          Pair(kTarget2, FloatEq(weights[0] * 1.0 / 3.0 + weights[1] * 0.5 +
+                                 weights[2] * 1.0))));
 
   // Check weekends.
   SetLocalTime(0, 9);
   EXPECT_THAT(
       predictor.Rank(),
-      UnorderedElementsAre(Pair(kTarget1, FloatEq(0.15 * 1.0 / 2.0)),
-                           Pair(kTarget2, FloatEq(0.15 * 1.0 / 2.0 + 0.05))));
+      UnorderedElementsAre(
+          Pair(kTarget1, FloatEq(weights[1] * 1.0 / 2.0)),
+          Pair(kTarget2, FloatEq(weights[1] * 1.0 / 2.0 + weights[2]))));
+}
+
+// Check the default weights are set correctly.
+TEST_F(HourAppLaunchPredictorTest, CheckDefaultWeights) {
+  base::test::ScopedFeatureList scoped_feature_list_;
+  scoped_feature_list_.InitAndEnableFeature(
+      app_list_features::kEnableAppSearchResultRanker);
+
+  EXPECT_THAT(HourAppLaunchPredictor::BinWeightsFromFlagOrDefault(),
+              ElementsAre(FloatEq(0.6), FloatEq(0.15), FloatEq(0.05),
+                          FloatEq(0.05), FloatEq(0.15)));
+}
+
+// Checks that the weights are set from flag correctly.
+TEST_F(HourAppLaunchPredictorTest, SetWeightsFromFlag) {
+  base::test::ScopedFeatureList scoped_feature_list_;
+  scoped_feature_list_.InitAndEnableFeatureWithParameters(
+      app_list_features::kEnableAppSearchResultRanker,
+      {{"weight_1_hour_later_bin", "0.1"},
+       {"weight_2_hour_later_bin", "0.2"},
+       {"weight_2_hour_earlier_bin", "0.22"},
+       {"weight_1_hour_earlier_bin", "0.23"}});
+
+  HourAppLaunchPredictor predictor;
+  const auto& weights = HourAppLaunchPredictor::BinWeightsFromFlagOrDefault();
+
+  EXPECT_THAT(weights, ElementsAre(FloatEq(0.25), FloatEq(0.1), FloatEq(0.2),
+                                   FloatEq(0.22), FloatEq(0.23)));
+
+  // For bin 0
+  SetLocalTime(1, 0);
+  predictor.Train(kTarget1);
+  predictor.Train(kTarget1);
+  predictor.Train(kTarget2);
+
+  // For bin 1
+  SetLocalTime(1, 1);
+  predictor.Train(kTarget1);
+  predictor.Train(kTarget2);
+
+  SetLocalTime(1, 0);
+  EXPECT_THAT(
+      predictor.Rank(),
+      UnorderedElementsAre(
+          Pair(kTarget1, FloatEq(weights[0] * 2.0 / 3.0 + weights[1] * 0.5)),
+          Pair(kTarget2, FloatEq(weights[0] * 1.0 / 3.0 + weights[1] * 0.5))));
 }
 
 // Checks FromProto applies decay correctly.
diff --git a/chrome/browser/ui/ash/chrome_accessibility_delegate.cc b/chrome/browser/ui/ash/chrome_accessibility_delegate.cc
index 8001a75..10072534 100644
--- a/chrome/browser/ui/ash/chrome_accessibility_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_accessibility_delegate.cc
@@ -8,6 +8,9 @@
 
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
+#include "chrome/browser/extensions/api/automation_internal/automation_event_router.h"
+#include "chrome/common/extensions/chrome_extension_messages.h"
+#include "ui/aura/env.h"
 
 using chromeos::AccessibilityManager;
 using chromeos::MagnificationManager;
@@ -42,3 +45,25 @@
 
   return std::numeric_limits<double>::min();
 }
+
+void ChromeAccessibilityDelegate::DispatchAccessibilityEvent(
+    const ui::AXTreeID& tree_id,
+    const std::vector<ui::AXTreeUpdate>& updates,
+    const ui::AXEvent& event) {
+  ExtensionMsg_AccessibilityEventBundleParams event_bundle;
+  event_bundle.tree_id = tree_id;
+  for (const ui::AXTreeUpdate& update : updates)
+    event_bundle.updates.push_back(update);
+  event_bundle.events.push_back(event);
+  event_bundle.mouse_location = aura::Env::GetInstance()->last_mouse_location();
+
+  // Forward the tree updates and the event to the accessibility extension.
+  extensions::AutomationEventRouter::GetInstance()->DispatchAccessibilityEvents(
+      event_bundle);
+}
+
+void ChromeAccessibilityDelegate::DispatchTreeDestroyedEvent(
+    const ui::AXTreeID& tree_id) {
+  extensions::AutomationEventRouter::GetInstance()->DispatchTreeDestroyedEvent(
+      tree_id, nullptr /* browser_context */);
+}
diff --git a/chrome/browser/ui/ash/chrome_accessibility_delegate.h b/chrome/browser/ui/ash/chrome_accessibility_delegate.h
index 015bd0f..773acc7 100644
--- a/chrome/browser/ui/ash/chrome_accessibility_delegate.h
+++ b/chrome/browser/ui/ash/chrome_accessibility_delegate.h
@@ -20,6 +20,10 @@
   bool ShouldShowAccessibilityMenu() const override;
   void SaveScreenMagnifierScale(double scale) override;
   double GetSavedScreenMagnifierScale() override;
+  void DispatchAccessibilityEvent(const ui::AXTreeID& tree_id,
+                                  const std::vector<ui::AXTreeUpdate>& updates,
+                                  const ui::AXEvent& event) override;
+  void DispatchTreeDestroyedEvent(const ui::AXTreeID& tree_id) override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(ChromeAccessibilityDelegate);
diff --git a/chrome/browser/ui/aura/accessibility/DEPS b/chrome/browser/ui/aura/accessibility/DEPS
index b2306413..fbced7a8 100644
--- a/chrome/browser/ui/aura/accessibility/DEPS
+++ b/chrome/browser/ui/aura/accessibility/DEPS
@@ -1,6 +1,7 @@
 specific_include_rules = {
   "automation_manager_aura\.cc": [
     # TODO(mash): Fix. https://crbug.com/756054
+    "+ash/accessibility/ax_host_service.h",
     "+ash/shell.h",
     "+ash/wm/window_util.h",
   ],
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
index df90b96..abda461 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -25,9 +25,9 @@
 #include "ui/views/widget/widget.h"
 
 #if defined(OS_CHROMEOS)
+#include "ash/accessibility/ax_host_service.h"
 #include "ash/shell.h"
 #include "ash/wm/window_util.h"
-#include "chrome/browser/chromeos/accessibility/ax_host_service.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/views/widget/widget_delegate.h"
 #endif
@@ -58,7 +58,8 @@
     }
   }
   // Gain access to out-of-process native windows.
-  AXHostService::SetAutomationEnabled(true);
+  // TODO(mash): Split AXHostService into chrome and ash parts.
+  ash::AXHostService::SetAutomationEnabled(true);
 #endif
 }
 
@@ -67,7 +68,7 @@
   Reset(true);
 
 #if defined(OS_CHROMEOS)
-  AXHostService::SetAutomationEnabled(false);
+  ash::AXHostService::SetAutomationEnabled(false);
 #endif
 }
 
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index c3807c3..189a2c7 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -246,18 +246,25 @@
   GetControlsScrimLayer()->SetColor(gfx::kGoogleGrey900);
   GetControlsScrimLayer()->SetOpacity(0.43f);
 
-  // views::View that toggles play/pause. -------------------------------------
-  play_pause_controls_view_->SetImageAlignment(
-      views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE);
-  play_pause_controls_view_->SetToggled(controller_->IsPlayerActive());
-  play_pause_controls_view_->set_owned_by_client();
+  // view::View that holds the controls. --------------------------------------
+  controls_parent_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
+  controls_parent_view_->SetSize(GetBounds().size());
+  controls_parent_view_->layer()->SetFillsBoundsOpaquely(false);
+  controls_parent_view_->set_owned_by_client();
 
   // views::View that closes the window. --------------------------------------
   close_controls_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
   close_controls_view_->layer()->SetFillsBoundsOpaquely(false);
   close_controls_view_->set_owned_by_client();
 
-  UpdatePlayPauseControlsSize();
+  // view::View that holds the video. -----------------------------------------
+  video_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
+
+  // views::View that toggles play/pause. -------------------------------------
+  play_pause_controls_view_->SetImageAlignment(
+      views::ImageButton::ALIGN_CENTER, views::ImageButton::ALIGN_MIDDLE);
+  play_pause_controls_view_->SetToggled(controller_->IsPlayerActive());
+  play_pause_controls_view_->set_owned_by_client();
 
   // Accessibility.
   play_pause_controls_view_->SetFocusForPlatform();  // Make button focusable.
@@ -274,21 +281,13 @@
   play_pause_controls_view_->SetToggledTooltipText(pause_button_label);
   play_pause_controls_view_->SetInstallFocusRingOnFocus(true);
 
-  // Add as child views to |controls_parent_view_|. --------------------------
-  controls_parent_view_->SetSize(GetBounds().size());
-  controls_parent_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
+  // Set up view::Views heirarchy. --------------------------------------------
   controls_parent_view_->AddChildView(play_pause_controls_view_.get());
-  controls_parent_view_->layer()->SetFillsBoundsOpaquely(false);
-  controls_parent_view_->set_owned_by_client();
-
-  // Add as child views to this widget. ---------------------------------------
   GetContentsView()->AddChildView(controls_scrim_view_.get());
   GetContentsView()->AddChildView(controls_parent_view_.get());
   GetContentsView()->AddChildView(close_controls_view_.get());
 
-  // Paint to ui::Layers. -----------------------------------------------------
-  video_view_->SetPaintToLayer(ui::LAYER_TEXTURED);
-
+  UpdatePlayPauseControlsSize();
   UpdateControlsVisibility(false);
 }
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 6f46b0d..6dbb63a 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -62,7 +62,6 @@
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/services/multidevice_setup/public/cpp/url_provider.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
-#include "components/arc/arc_util.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/chromeos/devicetype_utils.h"
 #include "ui/chromeos/events/keyboard_layout_util.h"
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
index 8e1ee50..fa06d6ad 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps.cc
@@ -4,13 +4,16 @@
 
 #include "chrome/browser/web_applications/bookmark_apps/external_web_apps.h"
 
+#include <map>
 #include <memory>
 #include <string>
 #include <utility>
 
 #include "base/callback.h"
+#include "base/feature_list.h"
 #include "base/files/file_enumerator.h"
 #include "base/json/json_file_value_serializer.h"
+#include "base/no_destructor.h"
 #include "base/path_service.h"
 #include "base/task/post_task.h"
 #include "base/threading/scoped_blocking_call.h"
@@ -35,6 +38,12 @@
 // The default value of kCreateShortcuts if false.
 constexpr char kCreateShortcuts[] = "create_shortcuts";
 
+// kFeatureName is an optional string parameter specifying a feature
+// associated with this app. If specified:
+//  - if the feature is enabled, the app will be installed
+//  - if the feature is not enabled, the app will be removed.
+constexpr char kFeatureName[] = "feature_name";
+
 // kLaunchContainer is a required string which can be "window" or "tab"
 // and controls what sort of container the web app is launched in.
 constexpr char kLaunchContainer[] = "launch_container";
@@ -48,6 +57,30 @@
     FILE_PATH_LITERAL("web_apps");
 #endif
 
+bool IsFeatureEnabled(const std::string& feature_name) {
+  // The feature system ensures there is only ever one Feature instance for each
+  // given feature name. To enable multiple apps to be gated by the same field
+  // trial this means there needs to be a global map of Features that is used.
+  static base::NoDestructor<
+      std::map<std::string, std::unique_ptr<base::Feature>>>
+      feature_map;
+  if (!feature_map->count(feature_name)) {
+    // To ensure the string used in the feature (which is a char*) is stable
+    // (i.e. is not freed later on), the key of the map is used. So, first
+    // insert a null Feature into the map, and then swap it with a real Feature
+    // constructed using the pointer from the key.
+    auto it = feature_map->insert(std::make_pair(feature_name, nullptr)).first;
+    it->second = std::make_unique<base::Feature>(
+        base::Feature{it->first.c_str(), base::FEATURE_DISABLED_BY_DEFAULT});
+  }
+
+  // Use the feature from the map, not the one in the pair above, as it has a
+  // stable address.
+  const auto it = feature_map->find(feature_name);
+  DCHECK(it != feature_map->end());
+  return base::FeatureList::IsEnabled(*it->second);
+}
+
 std::vector<web_app::PendingAppManager::AppInfo> ScanDir(base::FilePath dir) {
   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
   base::FilePath::StringType extension(FILE_PATH_LITERAL(".json"));
@@ -79,6 +112,12 @@
     std::unique_ptr<base::DictionaryValue> dict_value =
         base::DictionaryValue::From(std::move(value));
 
+    std::string feature_name;
+    if (dict_value->GetString(kFeatureName, &feature_name)) {
+      if (!IsFeatureEnabled(feature_name))
+        continue;
+    }
+
     std::string app_url_str;
     if (!dict_value->GetString(kAppUrl, &app_url_str) || app_url_str.empty()) {
       VLOG(2) << file.value() << " had an invalid " << kAppUrl;
diff --git a/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc b/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
index 3d924a32..18b8f6f 100644
--- a/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/external_web_apps_unittest.cc
@@ -7,8 +7,10 @@
 #include <algorithm>
 #include <vector>
 
+#include "base/feature_list.h"
 #include "base/path_service.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/common/chrome_paths.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -136,3 +138,26 @@
   // correct except for an invalid "launch_container" field.
   EXPECT_EQ(0u, app_infos.size());
 }
+
+TEST_F(ScanDirForExternalWebAppsTest, EnabledByFinch) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      base::Feature{"test_feature_name", base::FEATURE_DISABLED_BY_DEFAULT});
+  auto app_infos = web_app::ScanDirForExternalWebAppsForTesting(
+      test_dir("enabled_by_finch"));
+
+  // The enabled_by_finch directory contains two JSON file containing apps
+  // that have field trials. As the matching featureis enabled, they should be
+  // in our list of apps to install.
+  EXPECT_EQ(2u, app_infos.size());
+}
+
+TEST_F(ScanDirForExternalWebAppsTest, NotEnabledByFinch) {
+  auto app_infos = web_app::ScanDirForExternalWebAppsForTesting(
+      test_dir("enabled_by_finch"));
+
+  // The enabled_by_finch directory contains two JSON file containing apps
+  // that have field trials. As the matching featureis enabled, they should not
+  // be in our list of apps to install.
+  EXPECT_EQ(0u, app_infos.size());
+}
diff --git a/chrome/chrome_cleaner/chrome_utils/extensions_util.cc b/chrome/chrome_cleaner/chrome_utils/extensions_util.cc
index 1a4ae916..c2828733 100644
--- a/chrome/chrome_cleaner/chrome_utils/extensions_util.cc
+++ b/chrome/chrome_cleaner/chrome_utils/extensions_util.cc
@@ -207,11 +207,8 @@
 
   for (const auto& entry : *default_extensions) {
     base::string16 extension_id = base::UTF8ToUTF16(entry.first);
-    if (std::find(default_extension_whitelist.begin(),
-                  default_extension_whitelist.end(),
-                  extension_id) == default_extension_whitelist.end()) {
+    if (!base::ContainsValue(default_extension_whitelist, extension_id))
       policies->emplace_back(extension_id, extensions_file);
-    }
   }
 }
 
diff --git a/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc b/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc
index dec29d90..53dc811 100644
--- a/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc
+++ b/chrome/chrome_cleaner/scanner/urza_scanner_impl_unittest.cc
@@ -201,10 +201,8 @@
   void ExpectFoundPUPs(const std::set<UwSId>& pups) {
     EXPECT_EQ(pups.size(), found_pups_.size());
     std::set<UwSId>::const_iterator pup = pups.begin();
-    for (; pup != pups.end(); ++pup) {
-      EXPECT_NE(found_pups_.end(),
-                std::find(found_pups_.begin(), found_pups_.end(), *pup));
-    }
+    for (; pup != pups.end(); ++pup)
+      EXPECT_TRUE(base::ContainsValue(found_pups_, *pup));
     EXPECT_EQ(pups.size(), pups_seen_in_progress_callback_.size());
     EXPECT_EQ(pups, pups_seen_in_progress_callback_);
   }
diff --git a/chrome/common/chrome_content_client.cc b/chrome/common/chrome_content_client.cc
index d06d0cf..d6cb69e 100644
--- a/chrome/common/chrome_content_client.cc
+++ b/chrome/common/chrome_content_client.cc
@@ -96,16 +96,12 @@
 
 #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 #include "media/cdm/cdm_paths.h"  // nogncheck
-// The order is sensitive here as WIDEVINE_CDM_IS_COMPONENT is defined in
-// widevine_cdm_common.h.
-// TODO(xhwang): Also make WIDEVINE_CDM_IS_COMPONENT a buildflag to avoid this.
-#include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
 // Registers Widevine CDM if Widevine is enabled, the Widevine CDM is
 // bundled and not a component. When the Widevine CDM is a component, it is
 // registered in widevine_cdm_component_installer.cc.
-#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(SHOULD_BUNDLE_WIDEVINE_CDM) && \
-    !defined(WIDEVINE_CDM_IS_COMPONENT)
+#if BUILDFLAG(SHOULD_BUNDLE_WIDEVINE_CDM) && !BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
 #define REGISTER_BUNDLED_WIDEVINE_CDM
+#include "third_party/widevine/cdm/widevine_cdm_common.h"  // nogncheck
 // TODO(crbug.com/663554): Needed for WIDEVINE_CDM_VERSION_STRING. Support
 // component updated CDM on all desktop platforms and remove this.
 // This file is In SHARED_INTERMEDIATE_DIR.
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index b6e838a..e3ff3d8a 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -4,6 +4,8 @@
 
 // API for integration testing. To be used on test images with a test component
 // extension.
+[platforms=("chromeos"),
+ implemented_in="chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h"]
 namespace autotestPrivate {
 
   dictionary LoginStatusDict {
diff --git a/chrome/common/mac/app_shim.mojom b/chrome/common/mac/app_shim.mojom
index 4ad1dbd..7005ff32 100644
--- a/chrome/common/mac/app_shim.mojom
+++ b/chrome/common/mac/app_shim.mojom
@@ -4,6 +4,7 @@
 
 module chrome.mojom;
 
+import "content/public/common/ns_view_bridge_factory.mojom";
 import "mojo/public/mojom/base/file_path.mojom";
 import "ui/views_bridge_mac/mojo/bridge_factory.mojom";
 
@@ -26,6 +27,13 @@
   CreateViewsBridgeFactory(
       views_bridge_mac.mojom.BridgeFactory& views_bridge_factory);
 
+  // Create the interface through which a content structure
+  // (RenderWidgetHostView or WebContentsView) may create an NSView that exists
+  // in the app shim process.
+  CreateContentNSViewBridgeFactory(
+      associated content.mojom.NSViewBridgeFactory&
+          content_ns_views_bridge_factory);
+
   // Signals that a previous LaunchApp message has been processed, and lets the
   // app shim process know whether it was registered successfully.
   LaunchAppDone(AppShimLaunchResult launch_result);
diff --git a/chrome/renderer/BUILD.gn b/chrome/renderer/BUILD.gn
index b051404..5f1fed1 100644
--- a/chrome/renderer/BUILD.gn
+++ b/chrome/renderer/BUILD.gn
@@ -233,7 +233,7 @@
     }
   }
 
-  if (enable_library_cdms && enable_widevine) {
+  if (enable_widevine_cdm_component) {
     deps += [ "//third_party/widevine/cdm:headers" ]
   }
 
diff --git a/chrome/renderer/media/chrome_key_systems_provider.cc b/chrome/renderer/media/chrome_key_systems_provider.cc
index 48d53984..e1ba7562 100644
--- a/chrome/renderer/media/chrome_key_systems_provider.cc
+++ b/chrome/renderer/media/chrome_key_systems_provider.cc
@@ -6,10 +6,9 @@
 
 #include "base/time/default_tick_clock.h"
 #include "chrome/renderer/media/chrome_key_systems.h"
-#include "media/media_buildflags.h"
 #include "third_party/widevine/cdm/buildflags.h"
 
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS) && BUILDFLAG(ENABLE_WIDEVINE)
+#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
 #include "third_party/widevine/cdm/widevine_cdm_common.h"
 #endif
 
@@ -36,8 +35,7 @@
 
 // Check whether all potentially supported key systems are supported. If so,
 // no need to update again.
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS) && BUILDFLAG(ENABLE_WIDEVINE) && \
-    defined(WIDEVINE_CDM_IS_COMPONENT)
+#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
   for (const auto& properties : *key_systems) {
     if (properties->GetKeySystemName() == kWidevineKeySystem) {
       is_update_needed_ = false;
diff --git a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc b/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
index be8fa26..a57a4ab 100644
--- a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
+++ b/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
@@ -127,7 +127,7 @@
   EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
   tick_clock.Advance(base::TimeDelta::FromMilliseconds(10));
 
-#if BUILDFLAG(ENABLE_WIDEVINE) && defined(WIDEVINE_CDM_IS_COMPONENT)
+#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
   // Require update once enough time has passed for builds that install Widevine
   // as a component.
   EXPECT_TRUE(key_systems_provider.IsKeySystemsUpdateNeeded());
@@ -157,5 +157,5 @@
   // No update needed for builds that either don't offer Widevine or do so
   // as part of Chrome rather than component installer.
   EXPECT_FALSE(key_systems_provider.IsKeySystemsUpdateNeeded());
-#endif  // BUILDFLAG(ENABLE_WIDEVINE) && defined(WIDEVINE_CDM_IS_COMPONENT)
+#endif  // BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)
 }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ce204893..252ed9d 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1622,6 +1622,7 @@
         "../browser/chromeos/extensions/accessibility_features_apitest.cc",
         "../browser/chromeos/extensions/action_handlers/action_handlers_apitest.cc",
         "../browser/chromeos/extensions/arc_apps_private_apitest.cc",
+        "../browser/chromeos/extensions/autotest_private/autotest_private_apitest.cc",
         "../browser/chromeos/extensions/default_keyboard_extension_browser_test.cc",
         "../browser/chromeos/extensions/default_keyboard_extension_browser_test.h",
         "../browser/chromeos/extensions/echo_private_apitest.cc",
@@ -1766,7 +1767,6 @@
         "../browser/chromeos/system/device_disabling_browsertest.cc",
         "../browser/chromeos/system/tray_accessibility_browsertest.cc",
         "../browser/drive/drive_notification_manager_factory_browsertest.cc",
-        "../browser/extensions/api/autotest_private/autotest_private_apitest.cc",
         "../browser/extensions/api/certificate_provider/certificate_provider_apitest.cc",
         "../browser/extensions/api/networking_private/networking_private_apitest.cc",
         "../browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc",
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 639b140..b32a606e 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -1014,7 +1014,7 @@
     const base::FilePath& relative_partition_path) {
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
     network::mojom::NetworkContextPtr network_context;
-    mojo::MakeRequest(&network_context);
+    network_context_request_ = mojo::MakeRequest(&network_context);
     return network_context;
   }
   return nullptr;
diff --git a/chrome/test/base/testing_profile.h b/chrome/test/base/testing_profile.h
index b784b5e2..7c56cc1 100644
--- a/chrome/test/base/testing_profile.h
+++ b/chrome/test/base/testing_profile.h
@@ -19,6 +19,7 @@
 #include "components/domain_reliability/clear_mode.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 #include "extensions/buildflags/buildflags.h"
+#include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 
 #if defined(OS_CHROMEOS)
@@ -404,6 +405,10 @@
   // request context. Currently, only the CookieMonster is hooked up.
   scoped_refptr<net::URLRequestContextGetter> extensions_request_context_;
 
+  // Holds a dummy network context request to avoid triggering connection error
+  // handler.
+  network::mojom::NetworkContextRequest network_context_request_;
+
   bool force_incognito_;
   std::unique_ptr<Profile> incognito_profile_;
   TestingProfile* original_profile_;
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 97de8f4..e02ea9c 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -106,6 +106,8 @@
 ]
 
 _VERSION_SPECIFIC_FILTER['69'] = [
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945
+    'ChromeDriverTest.testWindowFullScreen',
     # https://bugs.chromium.org/p/chromedriver/issues/detail?id=2515
     'HeadlessInvalidCertificateTest.*',
     # Feature not yet supported in this version
@@ -113,6 +115,8 @@
 ]
 
 _VERSION_SPECIFIC_FILTER['68'] = [
+    # https://bugs.chromium.org/p/chromedriver/issues/detail?id=1945
+    'ChromeDriverTest.testWindowFullScreen',
     # Feature not yet supported in this version
     'ChromeDriverTest.testGenerateTestReport',
 ]
diff --git a/chrome/test/data/web_app_default_apps/enabled_by_finch/chrome_platform_status.json b/chrome/test/data/web_app_default_apps/enabled_by_finch/chrome_platform_status.json
new file mode 100644
index 0000000..27013c3
--- /dev/null
+++ b/chrome/test/data/web_app_default_apps/enabled_by_finch/chrome_platform_status.json
@@ -0,0 +1,6 @@
+{
+  "app_url": "https://www.chromestatus.com/features",
+  "create_shortcuts": true,
+  "feature_name": "test_feature_name",
+  "launch_container": "tab"
+}
diff --git a/chrome/test/data/web_app_default_apps/enabled_by_finch/google_search.json b/chrome/test/data/web_app_default_apps/enabled_by_finch/google_search.json
new file mode 100644
index 0000000..9fb169dc
--- /dev/null
+++ b/chrome/test/data/web_app_default_apps/enabled_by_finch/google_search.json
@@ -0,0 +1,6 @@
+{
+  "app_url": "https://google.com",
+  "create_shortcuts": true,
+  "feature_name": "test_feature_name",
+  "launch_container": "tab"
+}
diff --git a/chrome/test/data/webui/print_preview/pages_settings_test.js b/chrome/test/data/webui/print_preview/pages_settings_test.js
index 954fdc7..29f7ed15 100644
--- a/chrome/test/data/webui/print_preview/pages_settings_test.js
+++ b/chrome/test/data/webui/print_preview/pages_settings_test.js
@@ -66,23 +66,27 @@
       documentInfo.updatePageCount(pageCount);
       pagesSection.notifyPath('documentInfo.pageCount');
       Polymer.dom.flush();
+      let input = null;
+      return test_util.waitForRender(pagesSection)
+          .then(() => {
+            input = pagesSection.$.pageSettingsCustomInput.inputElement;
+            const readyForInput = pagesSection.$.customRadioButton.checked ?
+                Promise.resolve() :
+                test_util.eventToPromise('focus', input);
 
-      const input = pagesSection.$.pageSettingsCustomInput.inputElement;
-      const readyForInput = pagesSection.$$('#custom-radio-button').checked ?
-          Promise.resolve() :
-          test_util.eventToPromise('focus', input);
+            // Select custom
+            pagesSection.$.customRadioButton.click();
+            return readyForInput;
+          })
+          .then(() => {
+            // Set input string
+            input.value = inputString;
+            input.dispatchEvent(
+                new CustomEvent('input', {composed: true, bubbles: true}));
 
-      // Select custom
-      pagesSection.$$('#custom-radio-button').click();
-      return readyForInput.then(() => {
-        // Set input string
-        input.value = inputString;
-        input.dispatchEvent(
-            new CustomEvent('input', {composed: true, bubbles: true}));
-
-        // Validate results
-        return test_util.eventToPromise('input-change', pagesSection);
-      });
+            // Validate results
+            return test_util.eventToPromise('input-change', pagesSection);
+          });
     }
 
     /** @param {!Array<number>} expectedPages The expected pages value. */
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
index 99d676d..4670b74 100644
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -557,8 +557,8 @@
       assertFalse(pagesElement.hidden);
 
       // Default value is all pages. Print ticket expects this to be empty.
-      const allRadio = pagesElement.$$('#all-radio-button');
-      const customRadio = pagesElement.$$('#custom-radio-button');
+      const allRadio = pagesElement.$.allRadioButton;
+      const customRadio = pagesElement.$.customRadioButton;
       const pagesCrInput = pagesElement.$.pageSettingsCustomInput;
       const pagesInput = pagesCrInput.inputElement;
 
@@ -582,10 +582,10 @@
       // Set selection of pages 1 and 2.
       customRadio.click();
 
-      // Manually set |customSelected_| since focus may not work correctly on
+      // Manually set |optionSelected_| since focus may not work correctly on
       // MacOS. The PageSettingsTests verify this behavior is correct on all
       // platforms.
-      pagesElement.set('customSelected_', true);
+      pagesElement.set('optionSelected_', pagesElement.pagesValueEnum_.CUSTOM);
 
       triggerInputEvent(pagesInput, '1-2');
       return test_util.eventToPromise('input-change', pagesElement)
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 9b4a632..022b672b 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-11100.0.0
\ No newline at end of file
+11107.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc
index 0dc72e5..8660ec4 100644
--- a/chromeos/chromeos_switches.cc
+++ b/chromeos/chromeos_switches.cc
@@ -332,6 +332,9 @@
 // Enables animated transitions during first-run tutorial.
 const char kEnableFirstRunUITransitions[] = "enable-first-run-ui-transitions";
 
+// Enables the marketing opt-in screen in OOBE.
+const char kEnableMarketingOptInScreen[] = "enable-market-opt-in";
+
 // Enables notifications about captive portals in session.
 const char kEnableNetworkPortalNotification[] =
     "enable-network-portal-notification";
@@ -366,12 +369,6 @@
 // Enables the VoiceInteraction support.
 const char kEnableVoiceInteraction[] = "enable-voice-interaction";
 
-// Enables zip archiver - packer.
-const char kEnableZipArchiverPacker[] = "enable-zip-archiver-packer";
-
-// Disables zip archiver - packer.
-const char kDisableZipArchiverPacker[] = "disable-zip-archiver-packer";
-
 // Enables zip archiver - unpacker.
 const char kEnableZipArchiverUnpacker[] = "enable-zip-archiver-unpacker";
 
@@ -704,12 +701,6 @@
       kDisableZipArchiverUnpacker);
 }
 
-bool IsZipArchiverPackerEnabled() {
-  // Enabled by default.
-  return !base::CommandLine::ForCurrentProcess()->HasSwitch(
-      kDisableZipArchiverPacker);
-}
-
 bool IsSigninFrameClientCertsEnabled() {
   return !base::CommandLine::ForCurrentProcess()->HasSwitch(
       kDisableSigninFrameClientCerts);
diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h
index 89058ab..6f51a85 100644
--- a/chromeos/chromeos_switches.h
+++ b/chromeos/chromeos_switches.h
@@ -85,7 +85,6 @@
     kDisableSystemTimezoneAutomaticDetectionPolicy[];
 CHROMEOS_EXPORT extern const char kDisableVolumeAdjustSound[];
 CHROMEOS_EXPORT extern const char kDisableWakeOnWifi[];
-CHROMEOS_EXPORT extern const char kDisableZipArchiverPacker[];
 CHROMEOS_EXPORT extern const char kDisableZipArchiverUnpacker[];
 CHROMEOS_EXPORT extern const char kEnableArc[];
 CHROMEOS_EXPORT extern const char kEnableArcOobeOptinNoSkip[];
@@ -99,6 +98,7 @@
 CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[];
 CHROMEOS_EXPORT extern const char kEnableFileManagerTouchMode[];
 CHROMEOS_EXPORT extern const char kEnableFirstRunUITransitions[];
+CHROMEOS_EXPORT extern const char kEnableMarketingOptInScreen[];
 CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[];
 CHROMEOS_EXPORT extern const char kEnableOfflineDemoMode[];
 CHROMEOS_EXPORT extern const char kEnablePhysicalKeyboardAutocorrect[];
@@ -108,7 +108,6 @@
 CHROMEOS_EXPORT extern const char kEnableTouchpadThreeFingerClick[];
 CHROMEOS_EXPORT extern const char kEnableVideoPlayerChromecastSupport[];
 CHROMEOS_EXPORT extern const char kEnableVoiceInteraction[];
-CHROMEOS_EXPORT extern const char kEnableZipArchiverPacker[];
 CHROMEOS_EXPORT extern const char kEnableZipArchiverUnpacker[];
 CHROMEOS_EXPORT extern const char kEnterpriseDisableArc[];
 CHROMEOS_EXPORT extern const char kEnterpriseDisableLicenseTypeSelection[];
@@ -208,9 +207,6 @@
 // Returns true if Zip Archiver is enabled for unpacking files.
 CHROMEOS_EXPORT bool IsZipArchiverUnpackerEnabled();
 
-// Returns true if Zip Archiver is enabled for packing files.
-CHROMEOS_EXPORT bool IsZipArchiverPackerEnabled();
-
 // Returns true if client certificate authentication for the sign-in frame on
 // the Chrome OS sign-in screen is enabled.
 CHROMEOS_EXPORT bool IsSigninFrameClientCertsEnabled();
diff --git a/chromeos/components/drivefs/fake_drivefs.cc b/chromeos/components/drivefs/fake_drivefs.cc
index 1939d1f9..9e73ccb8 100644
--- a/chromeos/components/drivefs/fake_drivefs.cc
+++ b/chromeos/components/drivefs/fake_drivefs.cc
@@ -120,7 +120,9 @@
   static std::vector<drivefs::mojom::QueryItemPtr> SearchFiles(
       const base::FilePath& mount_path) {
     std::vector<drivefs::mojom::QueryItemPtr> results;
-    base::FileEnumerator walker(mount_path, true, base::FileEnumerator::FILES);
+    base::FileEnumerator walker(
+        mount_path, true,
+        base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
     for (auto file = walker.Next(); !file.empty(); file = walker.Next()) {
       auto item = drivefs::mojom::QueryItem::New();
       item->path = base::FilePath("/");
@@ -157,29 +159,26 @@
 
   void OnComplete() {
     if (--pending_callbacks_ == 0) {
-      if (!query_.empty() || available_offline_ || shared_with_me_) {
-        // Filter out non-matching results.
-        base::EraseIf(results_, [=](const auto& item_ptr) {
-          const base::FilePath path = item_ptr->path;
-          const drivefs::mojom::FileMetadata* metadata =
-              item_ptr->metadata.get();
-          if (!query_.empty()) {
-            return base::ToLowerASCII(path.BaseName().value()).find(query_) ==
-                   std::string::npos;
-          }
-          if (available_offline_) {
-            if (metadata && metadata->available_offline)
-              return false;
-            if (metadata &&
-                metadata->type == mojom::FileMetadata::Type::kHosted)
-              return false;
-          }
-          if (shared_with_me_ && metadata) {
-            return !metadata->shared;
-          }
+      // Filter out non-matching results.
+      base::EraseIf(results_, [=](const auto& item_ptr) {
+        if (!item_ptr->metadata) {
           return true;
-        });
-      }
+        }
+        const base::FilePath path = item_ptr->path;
+        const drivefs::mojom::FileMetadata* metadata = item_ptr->metadata.get();
+        if (!query_.empty()) {
+          return base::ToLowerASCII(path.BaseName().value()).find(query_) ==
+                 std::string::npos;
+        }
+        if (available_offline_) {
+          return !metadata->available_offline &&
+                 metadata->type != mojom::FileMetadata::Type::kHosted;
+        }
+        if (shared_with_me_) {
+          return !metadata->shared;
+        }
+        return false;
+      });
 
       std::move(callback_).Run(drive::FileError::FILE_ERROR_OK,
                                {std::move(results_)});
diff --git a/chromeos/services/ime/ime_service_unittest.cc b/chromeos/services/ime/ime_service_unittest.cc
index eceb940..08b5612 100644
--- a/chromeos/services/ime/ime_service_unittest.cc
+++ b/chromeos/services/ime/ime_service_unittest.cc
@@ -152,19 +152,20 @@
 
 TEST_F(ImeServiceTest, MultipleClients) {
   bool success = false;
-  TestClientChannel test_channel;
+  TestClientChannel test_channel1;
+  TestClientChannel test_channel2;
   mojom::InputChannelPtr to_engine_ptr1;
   mojom::InputChannelPtr to_engine_ptr2;
 
   ime_manager_->ConnectToImeEngine(
       "m17n:ar", mojo::MakeRequest(&to_engine_ptr1),
-      test_channel.CreateInterfacePtrAndBind(), extra,
+      test_channel1.CreateInterfacePtrAndBind(), extra,
       base::BindOnce(&ConnectCallback, &success));
   ime_manager_.FlushForTesting();
 
   ime_manager_->ConnectToImeEngine(
       "m17n:ar", mojo::MakeRequest(&to_engine_ptr2),
-      test_channel.CreateInterfacePtrAndBind(), extra,
+      test_channel2.CreateInterfacePtrAndBind(), extra,
       base::BindOnce(&ConnectCallback, &success));
   ime_manager_.FlushForTesting();
 
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc
index efc40632e..aa476967 100644
--- a/components/arc/arc_util.cc
+++ b/components/arc/arc_util.cc
@@ -75,18 +75,6 @@
   return false;
 }
 
-bool IsPlayStoreAvailable() {
-  if (IsRobotOrOfflineDemoAccountMode())
-    return false;
-  const auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
-    return true;
-
-  const std::string value =
-      command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode);
-  return value != kAlwaysStartWithNoPlayStore;
-}
-
 bool ShouldArcAlwaysStart() {
   const auto* command_line = base::CommandLine::ForCurrentProcess();
   if (!command_line->HasSwitch(chromeos::switches::kArcStartMode))
@@ -96,6 +84,11 @@
   return value == kAlwaysStartWithNoPlayStore || value == kAlwaysStart;
 }
 
+bool ShouldArcAlwaysStartWithNoPlayStore() {
+  return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+             chromeos::switches::kArcStartMode) == kAlwaysStartWithNoPlayStore;
+}
+
 bool ShouldShowOptInForTesting() {
   return base::CommandLine::ForCurrentProcess()->HasSwitch(
       chromeos::switches::kArcForceShowOptInUi);
diff --git a/components/arc/arc_util.h b/components/arc/arc_util.h
index cbf7656..bbfc2b8 100644
--- a/components/arc/arc_util.h
+++ b/components/arc/arc_util.h
@@ -40,14 +40,16 @@
 // to run ARC.
 bool IsWebstoreSearchEnabled();
 
-// Returns true if ARC image has Play Store package.
-bool IsPlayStoreAvailable();
-
 // Returns true if ARC should always start within the primary user session
 // (opted in user or not), and other supported mode such as guest and Kiosk
 // mode.
 bool ShouldArcAlwaysStart();
 
+// Returns true if ARC should always start with no Play Store availability
+// within the primary user session (opted in user or not), and other supported
+// mode such as guest and Kiosk mode.
+bool ShouldArcAlwaysStartWithNoPlayStore();
+
 // Returns true if ARC OptIn ui needs to be shown for testing.
 bool ShouldShowOptInForTesting();
 
diff --git a/components/arc/arc_util_unittest.cc b/components/arc/arc_util_unittest.cc
index 4192eae..0bc9e2a0 100644
--- a/components/arc/arc_util_unittest.cc
+++ b/components/arc/arc_util_unittest.cc
@@ -239,7 +239,15 @@
   auto* command_line = base::CommandLine::ForCurrentProcess();
   command_line->InitFromArgv({"", "--arc-availability=installed"});
   EXPECT_FALSE(ShouldArcAlwaysStart());
-  EXPECT_TRUE(IsPlayStoreAvailable());
+  EXPECT_FALSE(ShouldArcAlwaysStartWithNoPlayStore());
+}
+
+TEST_F(ArcUtilTest, ArcStartModeAlwaysStart) {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(
+      {"", "--arc-availability=installed", "--arc-start-mode=always-start"});
+  EXPECT_TRUE(ShouldArcAlwaysStart());
+  EXPECT_FALSE(ShouldArcAlwaysStartWithNoPlayStore());
 }
 
 TEST_F(ArcUtilTest, ArcStartModeWithoutPlayStore) {
@@ -248,7 +256,7 @@
       {"", "--arc-availability=installed",
        "--arc-start-mode=always-start-with-no-play-store"});
   EXPECT_TRUE(ShouldArcAlwaysStart());
-  EXPECT_FALSE(IsPlayStoreAvailable());
+  EXPECT_TRUE(ShouldArcAlwaysStartWithNoPlayStore());
 }
 
 TEST_F(ArcUtilTest, ScaleFactorToDensity) {
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 767c230e..50cdd5d 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -22,8 +22,14 @@
     "actions/click_action.h",
     "actions/focus_element_action.cc",
     "actions/focus_element_action.h",
+    "actions/navigate_action.cc",
+    "actions/navigate_action.h",
+    "actions/reset_action.cc",
+    "actions/reset_action.h",
     "actions/select_option_action.cc",
     "actions/select_option_action.h",
+    "actions/stop_action.cc",
+    "actions/stop_action.h",
     "actions/tell_action.cc",
     "actions/tell_action.h",
     "actions/upload_dom_action.cc",
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 54d9073e..bd52c55d 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -10,6 +10,8 @@
 
 #include "base/callback_forward.h"
 
+class GURL;
+
 namespace autofill {
 class AutofillProfile;
 }
@@ -90,6 +92,17 @@
                              NodeProto* node_tree_out,
                              base::OnceCallback<void(bool)> callback) = 0;
 
+  // Load |url| in the current tab. Returns immediately, before the new page has
+  // been loaded.
+  virtual void LoadURL(const GURL& url) = 0;
+
+  // Shut down Autofill Assistant at the end of the current script.
+  virtual void Shutdown() = 0;
+
+  // Restart Autofill Assistant at the end of the current script with a cleared
+  // state.
+  virtual void Restart() = 0;
+
   // Return the current ClientMemory.
   virtual ClientMemory* GetClientMemory() = 0;
 
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.cc b/components/autofill_assistant/browser/actions/mock_action_delegate.cc
index 3c4daf0..7036a53 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.cc
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.cc
@@ -4,6 +4,8 @@
 
 #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
 
+#include "url/gurl.h"
+
 namespace autofill_assistant {
 
 MockActionDelegate::MockActionDelegate() = default;
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 758e52a..2c6bee0 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_MOCK_ACTION_DELEGATE_H_
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_MOCK_ACTION_DELEGATE_H_
 
+#include <string>
+#include <vector>
+
 #include "base/callback.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -95,6 +98,9 @@
                void(const std::vector<std::string>& selectors,
                     NodeProto* node_tree_out,
                     base::OnceCallback<void(bool)> callback));
+  MOCK_METHOD1(LoadURL, void(const GURL& url));
+  MOCK_METHOD0(Shutdown, void());
+  MOCK_METHOD0(Restart, void());
   MOCK_METHOD0(GetClientMemory, ClientMemory*());
 };
 
diff --git a/components/autofill_assistant/browser/actions/navigate_action.cc b/components/autofill_assistant/browser/actions/navigate_action.cc
new file mode 100644
index 0000000..67e2c08
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/navigate_action.cc
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/actions/navigate_action.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+#include "url/gurl.h"
+
+namespace autofill_assistant {
+
+NavigateAction::NavigateAction(const ActionProto& proto) : Action(proto) {
+  DCHECK(proto_.has_navigate());
+}
+
+NavigateAction::~NavigateAction() {}
+
+void NavigateAction::ProcessAction(ActionDelegate* delegate,
+                                   ProcessActionCallback callback) {
+  GURL url(proto_.navigate().url());
+  delegate->LoadURL(url);
+  processed_action_proto_ = std::make_unique<ProcessedActionProto>();
+  UpdateProcessedAction(/* status= */ true);
+  std::move(callback).Run(std::move(processed_action_proto_));
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/navigate_action.h b/components/autofill_assistant/browser/actions/navigate_action.h
new file mode 100644
index 0000000..f42c9f784
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/navigate_action.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_NAVIGATE_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_NAVIGATE_ACTION_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+
+namespace autofill_assistant {
+// An action to display a message.
+class NavigateAction : public Action {
+ public:
+  explicit NavigateAction(const ActionProto& proto);
+  ~NavigateAction() override;
+
+  // Overrides Action:
+  void ProcessAction(ActionDelegate* delegate,
+                     ProcessActionCallback callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NavigateAction);
+};
+
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_NAVIGATE_ACTION_H_
diff --git a/components/autofill_assistant/browser/actions/reset_action.cc b/components/autofill_assistant/browser/actions/reset_action.cc
new file mode 100644
index 0000000..ea3b81f
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/reset_action.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/actions/reset_action.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+
+namespace autofill_assistant {
+
+ResetAction::ResetAction(const ActionProto& proto) : Action(proto) {
+  DCHECK(proto_.has_reset());
+}
+
+ResetAction::~ResetAction() {}
+
+void ResetAction::ProcessAction(ActionDelegate* delegate,
+                                ProcessActionCallback callback) {
+  delegate->Restart();
+  processed_action_proto_ = std::make_unique<ProcessedActionProto>();
+  UpdateProcessedAction(true);
+  std::move(callback).Run(std::move(processed_action_proto_));
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/reset_action.h b/components/autofill_assistant/browser/actions/reset_action.h
new file mode 100644
index 0000000..1fb19af
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/reset_action.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_RESET_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_RESET_ACTION_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+
+namespace autofill_assistant {
+
+class ResetAction : public Action {
+ public:
+  explicit ResetAction(const ActionProto& proto);
+  ~ResetAction() override;
+
+  // Overrides Action:
+  void ProcessAction(ActionDelegate* delegate,
+                     ProcessActionCallback callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ResetAction);
+};
+
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_RESET_ACTION_H_
diff --git a/components/autofill_assistant/browser/actions/stop_action.cc b/components/autofill_assistant/browser/actions/stop_action.cc
new file mode 100644
index 0000000..fba77a7
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/stop_action.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/actions/stop_action.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "components/autofill_assistant/browser/actions/action_delegate.h"
+
+namespace autofill_assistant {
+
+StopAction::StopAction(const ActionProto& proto) : Action(proto) {
+  DCHECK(proto_.has_stop());
+}
+
+StopAction::~StopAction() {}
+
+void StopAction::ProcessAction(ActionDelegate* delegate,
+                               ProcessActionCallback callback) {
+  delegate->Shutdown();
+  processed_action_proto_ = std::make_unique<ProcessedActionProto>();
+  UpdateProcessedAction(true);
+  std::move(callback).Run(std::move(processed_action_proto_));
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/stop_action.h b/components/autofill_assistant/browser/actions/stop_action.h
new file mode 100644
index 0000000..de62b30
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/stop_action.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_STOP_ACTION_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_STOP_ACTION_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/autofill_assistant/browser/actions/action.h"
+
+namespace autofill_assistant {
+
+class StopAction : public Action {
+ public:
+  explicit StopAction(const ActionProto& proto);
+  ~StopAction() override;
+
+  // Overrides Action:
+  void ProcessAction(ActionDelegate* delegate,
+                     ProcessActionCallback callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(StopAction);
+};
+
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_STOP_ACTION_H_
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 c69b659c..be7dd36 100644
--- a/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
+++ b/components/autofill_assistant/browser/actions/wait_for_dom_action.cc
@@ -32,8 +32,8 @@
                                      ProcessActionCallback callback) {
   processed_action_proto_ = std::make_unique<ProcessedActionProto>();
 
-  // Fail the action if the selector is empty.
-  if (proto_.wait_for_dom().element().selectors().empty()) {
+  // Fail the action if selectors is empty.
+  if (proto_.wait_for_dom().selectors().empty()) {
     UpdateProcessedAction(false);
     DLOG(ERROR) << "Empty selector, failing action.";
     std::move(callback).Run(std::move(processed_action_proto_));
@@ -54,7 +54,7 @@
                                           ProcessActionCallback callback) {
   DCHECK(rounds > 0);
   std::vector<std::string> selectors;
-  for (const auto& selector : proto_.wait_for_dom().element().selectors()) {
+  for (const auto& selector : proto_.wait_for_dom().selectors()) {
     selectors.emplace_back(selector);
   }
   delegate->ElementExists(
diff --git a/components/autofill_assistant/browser/client_memory.h b/components/autofill_assistant/browser/client_memory.h
index 128c473..49d7ab12 100644
--- a/components/autofill_assistant/browser/client_memory.h
+++ b/components/autofill_assistant/browser/client_memory.h
@@ -34,9 +34,6 @@
   virtual void set_selected_address(const std::string& name,
                                     const std::string& guid);
 
-  // TODO(crbug.com/806868): Add a clear() method that resets the memory and
-  // call it when necessary (TBD).
-
  private:
   base::Optional<std::string> selected_card_;
 
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 7d67ded..c7432cc 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -56,7 +56,8 @@
       client_(std::move(client)),
       web_controller_(std::move(web_controller)),
       service_(std::move(service)),
-      script_tracker_(std::make_unique<ScriptTracker>(this, this)),
+      script_tracker_(std::make_unique<ScriptTracker>(/* delegate= */ this,
+                                                      /* listener= */ this)),
       parameters_(std::move(parameters)),
       memory_(std::make_unique<ClientMemory>()),
       allow_autostart_(true) {
@@ -106,13 +107,32 @@
 }
 
 void Controller::OnScriptExecuted(const std::string& script_path,
-                                  bool success) {
+                                  ScriptExecutor::Result result) {
   GetUiController()->HideOverlay();
-  if (!success) {
+  if (!result.success) {
     LOG(ERROR) << "Failed to execute script " << script_path;
     // TODO(crbug.com/806868): Handle script execution failure.
   }
 
+  switch (result.at_end) {
+    case ScriptExecutor::SHUTDOWN:
+      GetUiController()->Shutdown();  // indirectly deletes this
+      return;
+
+    case ScriptExecutor::RESTART:
+      script_tracker_ = std::make_unique<ScriptTracker>(/* delegate= */ this,
+                                                        /* listener= */ this);
+      memory_ = std::make_unique<ClientMemory>();
+      script_domain_ = "";
+      break;
+
+    case ScriptExecutor::CONTINUE:
+      break;
+
+    default:
+      DLOG(ERROR) << "Unexpected value for at_end: " << result.at_end;
+      break;
+  }
   GetOrCheckScripts(web_contents()->GetLastCommittedURL());
 }
 
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index cd93b96..f9b93c4 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -62,7 +62,8 @@
   void GetOrCheckScripts(const GURL& url);
   void OnGetScripts(const GURL& url, bool result, const std::string& response);
   void OnScriptChosen(const std::string& script_path);
-  void OnScriptExecuted(const std::string& script_path, bool success);
+  void OnScriptExecuted(const std::string& script_path,
+                        ScriptExecutor::Result result);
 
   // Overrides content::UiDelegate:
   void OnClickOverlay() override;
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 1a788dc..b6e0b1f 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -23,6 +23,7 @@
 using ::testing::Contains;
 using ::testing::ElementsAre;
 using ::testing::Eq;
+using ::testing::InSequence;
 using ::testing::NiceMock;
 using ::testing::Pair;
 using ::testing::ReturnRef;
@@ -101,6 +102,7 @@
   // Updates the current url of the controller and forces a refresh, without
   // bothering with actually rendering any page content.
   void SimulateNavigateToUrl(const GURL& url) {
+    url_ = url;
     tester_->SetLastCommittedURL(url);
     controller_->DidFinishLoad(nullptr, url);
   }
@@ -163,6 +165,65 @@
   SimulateNavigateToUrl(GURL("http://a.example.com/path"));
 }
 
+TEST_F(ControllerTest, Stop) {
+  ActionsResponseProto actions_response;
+  actions_response.add_actions()->mutable_stop();
+  std::string actions_response_str;
+  actions_response.SerializeToString(&actions_response_str);
+  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("stop"), _, _))
+      .WillOnce(RunOnceCallback<2>(true, actions_response_str));
+  EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _))
+      .WillOnce(RunOnceCallback<2>(true, ""));
+
+  EXPECT_CALL(*mock_ui_controller_, Shutdown());
+  GetUiDelegate()->OnScriptSelected("stop");
+}
+
+TEST_F(ControllerTest, Reset) {
+  {
+    InSequence sequence;
+
+    // 1. Fetch scripts for URL, which in contains a single "reset" script.
+    SupportsScriptResponseProto script_response;
+    AddRunnableScript(&script_response, "reset");
+    std::string script_response_str;
+    script_response.SerializeToString(&script_response_str);
+    EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
+        .WillOnce(RunOnceCallback<2>(true, script_response_str));
+
+    EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(1)));
+
+    // 2. Execute the "reset" script, which contains a reset action.
+    ActionsResponseProto actions_response;
+    actions_response.add_actions()->mutable_reset();
+    std::string actions_response_str;
+    actions_response.SerializeToString(&actions_response_str);
+    EXPECT_CALL(*mock_service_, OnGetActions(StrEq("reset"), _, _))
+        .WillOnce(RunOnceCallback<2>(true, actions_response_str));
+
+    // 3. Report the result of running that action.
+    EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _))
+        .WillOnce(RunOnceCallback<2>(true, ""));
+
+    // 4. The reset action forces a reload of the scripts, even though the URL
+    // hasn't changed. The "reset" script is reported again to UpdateScripts.
+    EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
+        .WillOnce(RunOnceCallback<2>(true, script_response_str));
+
+    // Reset forces the controller to fetch the scripts twice, even though the
+    // URL doesn't change..
+    EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(1)));
+  }
+
+  // Resetting should clear the client memory
+  controller_->GetClientMemory()->set_selected_card("set");
+
+  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+  GetUiDelegate()->OnScriptSelected("reset");
+
+  EXPECT_FALSE(controller_->GetClientMemory()->selected_card());
+}
+
 TEST_F(ControllerTest, RefreshScriptWhenDomainChanges) {
   EXPECT_CALL(*mock_service_,
               OnGetScriptsForUrl(Eq(GURL("http://a.example.com/path1")), _, _))
diff --git a/components/autofill_assistant/browser/mock_ui_controller.h b/components/autofill_assistant/browser/mock_ui_controller.h
index ed0f7a6..d02b43c4 100644
--- a/components/autofill_assistant/browser/mock_ui_controller.h
+++ b/components/autofill_assistant/browser/mock_ui_controller.h
@@ -5,6 +5,9 @@
 #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_UI_CONTROLLER_H_
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_MOCK_UI_CONTROLLER_H_
 
+#include <string>
+#include <vector>
+
 #include "base/callback.h"
 #include "components/autofill_assistant/browser/script.h"
 #include "components/autofill_assistant/browser/ui_controller.h"
@@ -21,6 +24,7 @@
   MOCK_METHOD1(ShowStatusMessage, void(const std::string& message));
   MOCK_METHOD0(ShowOverlay, void());
   MOCK_METHOD0(HideOverlay, void());
+  MOCK_METHOD0(Shutdown, void());
   MOCK_METHOD1(UpdateScripts, void(const std::vector<ScriptHandle>& scripts));
 
   void ChooseAddress(
diff --git a/components/autofill_assistant/browser/mock_web_controller.h b/components/autofill_assistant/browser/mock_web_controller.h
index c243260..518e1e4 100644
--- a/components/autofill_assistant/browser/mock_web_controller.h
+++ b/components/autofill_assistant/browser/mock_web_controller.h
@@ -21,6 +21,8 @@
 
   MOCK_METHOD0(GetUrl, const GURL&());
 
+  MOCK_METHOD1(LoadURL, void(const GURL&));
+
   void ClickElement(const std::vector<std::string>& selectors,
                     base::OnceCallback<void(bool)> callback) override {
     // Transforming callback into a references allows using RunOnceCallback on
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 3ab4340..c44b2ff 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -10,7 +10,10 @@
 #include "components/autofill_assistant/browser/actions/autofill_action.h"
 #include "components/autofill_assistant/browser/actions/click_action.h"
 #include "components/autofill_assistant/browser/actions/focus_element_action.h"
+#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/stop_action.h"
 #include "components/autofill_assistant/browser/actions/tell_action.h"
 #include "components/autofill_assistant/browser/actions/upload_dom_action.h"
 #include "components/autofill_assistant/browser/actions/wait_for_dom_action.h"
@@ -170,16 +173,26 @@
         actions->emplace_back(std::make_unique<WaitForDomAction>(action));
         break;
       }
-      case ActionProto::ActionInfoCase::kUploadDom: {
-        actions->emplace_back(std::make_unique<UploadDomAction>(action));
-        break;
-      }
       case ActionProto::ActionInfoCase::kSelectOption: {
         actions->emplace_back(std::make_unique<SelectOptionAction>(action));
         break;
       }
+      case ActionProto::ActionInfoCase::kNavigate: {
+        actions->emplace_back(std::make_unique<NavigateAction>(action));
+        break;
+      }
+      case ActionProto::ActionInfoCase::kStop: {
+        actions->emplace_back(std::make_unique<StopAction>(action));
+        break;
+      }
+      case ActionProto::ActionInfoCase::kReset: {
+        actions->emplace_back(std::make_unique<ResetAction>(action));
+        break;
+      }
+      default:
       case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
-        LOG(ERROR) << "Unknown or unspported action.";
+        DLOG(ERROR) << "Unknown or unsupported action with action_case="
+                    << action.action_info_case();
         break;
       }
     }
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index a5c02f4..9e3f510 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -21,7 +21,10 @@
 
 ScriptExecutor::ScriptExecutor(const std::string& script_path,
                                ScriptExecutorDelegate* delegate)
-    : script_path_(script_path), delegate_(delegate), weak_ptr_factory_(this) {
+    : script_path_(script_path),
+      delegate_(delegate),
+      at_end_(CONTINUE),
+      weak_ptr_factory_(this) {
   DCHECK(delegate_);
 }
 ScriptExecutor::~ScriptExecutor() {}
@@ -112,13 +115,25 @@
                                                std::move(callback));
 }
 
+void ScriptExecutor::LoadURL(const GURL& url) {
+  delegate_->GetWebController()->LoadURL(url);
+}
+
+void ScriptExecutor::Shutdown() {
+  at_end_ = SHUTDOWN;
+}
+
+void ScriptExecutor::Restart() {
+  at_end_ = RESTART;
+}
+
 ClientMemory* ScriptExecutor::GetClientMemory() {
   return delegate_->GetClientMemory();
 }
 
 void ScriptExecutor::OnGetActions(bool result, const std::string& response) {
   if (!result) {
-    std::move(callback_).Run(false);
+    RunCallback(false);
     return;
   }
   processed_actions_.clear();
@@ -127,19 +142,28 @@
   bool parse_result =
       ProtocolUtils::ParseActions(response, &last_server_payload_, &actions_);
   if (!parse_result) {
-    std::move(callback_).Run(false);
+    RunCallback(false);
     return;
   }
 
   if (actions_.empty()) {
     // Finished executing the script if there are no more actions.
-    std::move(callback_).Run(true);
+    RunCallback(true);
     return;
   }
 
   ProcessNextAction();
 }
 
+void ScriptExecutor::RunCallback(bool success) {
+  DCHECK(callback_);
+
+  ScriptExecutor::Result result;
+  result.success = success;
+  result.at_end = at_end_;
+  std::move(callback_).Run(result);
+}
+
 void ScriptExecutor::ProcessNextAction() {
   // We could get into a strange situation if ProcessNextAction is called before
   // the action was reported as processed, which should not happen. In that case
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 59b8883..2bfb5cb5 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -27,7 +27,25 @@
                  ScriptExecutorDelegate* delegate);
   ~ScriptExecutor() override;
 
-  using RunScriptCallback = base::OnceCallback<void(bool)>;
+  // What should happen after the script has run.
+  enum AtEnd {
+    // Continue normally.
+    CONTINUE = 0,
+
+    // Shut down Autofill Assistant.
+    SHUTDOWN,
+
+    // Reset all state and restart.
+    RESTART
+  };
+
+  // Contains the result of the Run operation.
+  struct Result {
+    bool success = false;
+    AtEnd at_end = AtEnd::CONTINUE;
+  };
+
+  using RunScriptCallback = base::OnceCallback<void(Result)>;
   void Run(RunScriptCallback callback);
 
   // Override ActionDelegate:
@@ -62,10 +80,14 @@
   void BuildNodeTree(const std::vector<std::string>& selectors,
                      NodeProto* node_tree_out,
                      base::OnceCallback<void(bool)> callback) override;
+  void LoadURL(const GURL& url) override;
+  void Shutdown() override;
+  void Restart() override;
   ClientMemory* GetClientMemory() override;
 
  private:
   void OnGetActions(bool result, const std::string& response);
+  void RunCallback(bool success);
   void ProcessNextAction();
   void ProcessAction(Action* action);
   void GetNextActions();
@@ -78,6 +100,7 @@
   std::vector<std::unique_ptr<Action>> actions_;
   std::vector<ProcessedActionProto> processed_actions_;
   std::string last_server_payload_;
+  AtEnd at_end_;
 
   base::WeakPtrFactory<ScriptExecutor> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(ScriptExecutor);
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 462283df..8aa3cbb 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -23,6 +23,7 @@
 using ::testing::AllOf;
 using ::testing::Contains;
 using ::testing::DoAll;
+using ::testing::Field;
 using ::testing::NiceMock;
 using ::testing::Pair;
 using ::testing::SaveArg;
@@ -81,7 +82,10 @@
 TEST_F(ScriptExecutorTest, GetActionsFails) {
   EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
       .WillOnce(RunOnceCallback<2>(false, ""));
-  EXPECT_CALL(executor_callback_, Run(false));
+  EXPECT_CALL(executor_callback_,
+              Run(AllOf(Field(&ScriptExecutor::Result::success, false),
+                        Field(&ScriptExecutor::Result::at_end,
+                              ScriptExecutor::CONTINUE))));
   executor_->Run(executor_callback_.Get());
 }
 
@@ -95,11 +99,12 @@
                            _))
       .WillOnce(RunOnceCallback<2>(true, ""));
 
-  EXPECT_CALL(executor_callback_, Run(true));
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
   executor_->Run(executor_callback_.Get());
 }
 
-TEST_F(ScriptExecutorTest, RunOneActionReportFailureAndStop) {
+TEST_F(ScriptExecutorTest, RunOneActionReportAndReturn) {
   ActionsResponseProto actions_response;
   actions_response.set_server_payload("payload");
   actions_response.add_actions()
@@ -114,7 +119,10 @@
   EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
       .WillOnce(DoAll(SaveArg<1>(&processed_actions_capture),
                       RunOnceCallback<2>(true, "")));
-  EXPECT_CALL(executor_callback_, Run(true));
+  EXPECT_CALL(executor_callback_,
+              Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+                        Field(&ScriptExecutor::Result::at_end,
+                              ScriptExecutor::CONTINUE))));
   executor_->Run(executor_callback_.Get());
 
   ASSERT_EQ(1u, processed_actions_capture.size());
@@ -140,13 +148,48 @@
                 RunOnceCallback<2>(true, Serialize(next_actions_response))))
       .WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
                       RunOnceCallback<2>(true, "")));
-  EXPECT_CALL(executor_callback_, Run(true));
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
   executor_->Run(executor_callback_.Get());
 
   EXPECT_EQ(2u, processed_actions1_capture.size());
   EXPECT_EQ(1u, processed_actions2_capture.size());
 }
 
+TEST_F(ScriptExecutorTest, StopAfterEnd) {
+  ActionsResponseProto actions_response;
+  actions_response.set_server_payload("payload");
+  actions_response.add_actions()->mutable_stop();
+
+  EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
+      .WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
+
+  EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
+      .WillOnce(RunOnceCallback<2>(true, ""));
+  EXPECT_CALL(executor_callback_,
+              Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+                        Field(&ScriptExecutor::Result::at_end,
+                              ScriptExecutor::SHUTDOWN))));
+  executor_->Run(executor_callback_.Get());
+}
+
+TEST_F(ScriptExecutorTest, ResetAfterEnd) {
+  ActionsResponseProto actions_response;
+  actions_response.set_server_payload("payload");
+  actions_response.add_actions()->mutable_reset();
+
+  EXPECT_CALL(mock_service_, OnGetActions(_, _, _))
+      .WillOnce(RunOnceCallback<2>(true, Serialize(actions_response)));
+
+  EXPECT_CALL(mock_service_, OnGetNextActions(_, _, _))
+      .WillOnce(RunOnceCallback<2>(true, ""));
+  EXPECT_CALL(executor_callback_,
+              Run(AllOf(Field(&ScriptExecutor::Result::success, true),
+                        Field(&ScriptExecutor::Result::at_end,
+                              ScriptExecutor::RESTART))));
+  executor_->Run(executor_callback_.Get());
+}
+
 TEST_F(ScriptExecutorTest, InterruptActionListOnError) {
   ActionsResponseProto initial_actions_response;
   initial_actions_response.set_server_payload("payload");
@@ -174,7 +217,8 @@
                 RunOnceCallback<2>(true, Serialize(next_actions_response))))
       .WillOnce(DoAll(SaveArg<1>(&processed_actions2_capture),
                       RunOnceCallback<2>(true, "")));
-  EXPECT_CALL(executor_callback_, Run(true));
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
   executor_->Run(executor_callback_.Get());
 
   ASSERT_EQ(2u, processed_actions1_capture.size());
@@ -209,7 +253,8 @@
   EXPECT_TRUE(scoped_task_environment_.MainThreadHasPendingTask());
 
   // Moving forward in time triggers action execution.
-  EXPECT_CALL(executor_callback_, Run(true));
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
   scoped_task_environment_.FastForwardBy(
       base::TimeDelta::FromMilliseconds(1000));
   EXPECT_FALSE(scoped_task_environment_.MainThreadHasPendingTask());
diff --git a/components/autofill_assistant/browser/script_tracker.cc b/components/autofill_assistant/browser/script_tracker.cc
index 001cc84..d2cac1c 100644
--- a/components/autofill_assistant/browser/script_tracker.cc
+++ b/components/autofill_assistant/browser/script_tracker.cc
@@ -59,9 +59,11 @@
 }
 
 void ScriptTracker::ExecuteScript(const std::string& script_path,
-                                  base::OnceCallback<void(bool)> callback) {
+                                  ScriptExecutor::RunScriptCallback callback) {
   if (running()) {
-    std::move(callback).Run(false);
+    ScriptExecutor::Result result;
+    result.success = false;
+    std::move(callback).Run(result);
     return;
   }
 
@@ -74,12 +76,10 @@
 
 void ScriptTracker::OnScriptRun(
     const std::string& script_path,
-    base::OnceCallback<void(bool)> original_callback,
-    bool success) {
-  executed_scripts_[script_path] =
-      success ? SCRIPT_STATUS_SUCCESS : SCRIPT_STATUS_FAILURE;
+    ScriptExecutor::RunScriptCallback original_callback,
+    ScriptExecutor::Result result) {
   executor_.reset();
-  std::move(original_callback).Run(success);
+  std::move(original_callback).Run(result);
 }
 
 void ScriptTracker::UpdateRunnableScriptsIfNecessary() {
diff --git a/components/autofill_assistant/browser/script_tracker.h b/components/autofill_assistant/browser/script_tracker.h
index bd48e92..4e4d67e 100644
--- a/components/autofill_assistant/browser/script_tracker.h
+++ b/components/autofill_assistant/browser/script_tracker.h
@@ -59,7 +59,7 @@
   // Call CheckScripts to refresh the set of runnable script after script
   // execution.
   void ExecuteScript(const std::string& path,
-                     base::OnceCallback<void(bool)> callback);
+                     ScriptExecutor::RunScriptCallback callback);
 
   // Checks whether a script is currently running. There can be at most one
   // script running at a time.
@@ -67,8 +67,8 @@
 
  private:
   void OnScriptRun(const std::string& script_path,
-                   base::OnceCallback<void(bool)> original_callback,
-                   bool success);
+                   ScriptExecutor::RunScriptCallback original_callback,
+                   ScriptExecutor::Result result);
   void UpdateRunnableScriptsIfNecessary();
 
   // Returns true if |runnable_| should be updated.
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index d64d90cd..9acb108 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -21,11 +21,12 @@
 namespace autofill_assistant {
 using ::testing::_;
 using ::testing::ElementsAre;
-using ::testing::UnorderedElementsAre;
+using ::testing::Field;
 using ::testing::IsEmpty;
 using ::testing::NiceMock;
 using ::testing::ReturnRef;
 using ::testing::SizeIs;
+using ::testing::UnorderedElementsAre;
 
 class ScriptTrackerTest : public testing::Test,
                           public ScriptTracker::Listener,
@@ -227,8 +228,9 @@
               UnorderedElementsAre("script1", "script2"));
 
   // run 'script 1'
-  base::MockCallback<base::OnceCallback<void(bool)>> execute_callback;
-  EXPECT_CALL(execute_callback, Run(true));
+  base::MockCallback<ScriptExecutor::RunScriptCallback> execute_callback;
+  EXPECT_CALL(execute_callback,
+              Run(Field(&ScriptExecutor::Result::success, true)));
 
   tracker_.ExecuteScript("script1", execute_callback.Get());
   tracker_.CheckScripts();
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 385b3a0..de0cc72 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -187,12 +187,15 @@
   oneof action_info {
     ClickProto click = 5;
     SelectOptionProto select_option = 7;
+    NavigateProto navigate = 9;
     TellProto tell = 11;
     FocusElementProto focus_element = 12;
     WaitForDomProto wait_for_dom = 19;
     UseCreditCardProto use_card = 28;
     UseAddressProto use_address = 29;
     UploadDomProto upload_dom = 18;
+    ResetProto reset = 34;
+    StopProto stop = 35;
   }
 }
 
@@ -340,11 +343,11 @@
 // Ask Chrome to wait for an element in the DOM. This can be used to only
 // proceed to the next action once the page is ready.
 message WaitForDomProto {
-  // The element to wait.
-  optional ElementReferenceProto element = 1;
-
   // Fail after waiting this amount of time.
-  optional int32 timeout_ms = 2;
+  optional int32 timeout_ms = 1;
+
+  // The element to wait for.
+  repeated string selectors = 2;
 }
 
 // Volatile upload of a portion of the dom for backend analysis, does not store
@@ -354,3 +357,14 @@
   // page is returned.
   optional ElementReferenceProto tree_root = 1;
 }
+
+// Load the given URL in the current tab.
+message NavigateProto {
+  optional string url = 1;
+}
+
+// Resets Autofill Assistant: clears any state and server payload.
+message ResetProto {}
+
+// Stop Autofill Assistant.
+message StopProto {}
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index 58063f4..db7d91e2 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -5,13 +5,12 @@
 #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_CONTROLLER_H_
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_CONTROLLER_H_
 
-#include "components/autofill_assistant/browser/script.h"
-#include "components/autofill_assistant/browser/ui_delegate.h"
-
 #include <string>
 #include <vector>
 
 #include "base/callback_forward.h"
+#include "components/autofill_assistant/browser/script.h"
+#include "components/autofill_assistant/browser/ui_delegate.h"
 
 namespace autofill_assistant {
 struct ScriptHandle;
@@ -33,6 +32,11 @@
   // Hide the overlay.
   virtual void HideOverlay() = 0;
 
+  // Shuts down Autofill Assistant: hide the UI and frees any associated state.
+  //
+  // Warning: this indirectly deletes the caller.
+  virtual void Shutdown() = 0;
+
   // Update the list of scripts in the UI.
   virtual void UpdateScripts(const std::vector<ScriptHandle>& scripts) = 0;
 
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index d38b4185..3dfe1f9 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -4,6 +4,8 @@
 
 #include "components/autofill_assistant/browser/web_controller.h"
 
+#include <utility>
+
 #include "base/callback.h"
 #include "base/logging.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
@@ -83,6 +85,11 @@
   return web_contents_->GetLastCommittedURL();
 }
 
+void WebController::LoadURL(const GURL& url) {
+  web_contents_->GetController().LoadURLWithParams(
+      content::NavigationController::LoadURLParams(url));
+}
+
 void WebController::ClickElement(const std::vector<std::string>& selectors,
                                  base::OnceCallback<void(bool)> callback) {
   DCHECK(!selectors.empty());
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index 9c3e155..278798b3 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -45,6 +45,10 @@
   // Returns the last committed URL of the associated |web_contents_|.
   virtual const GURL& GetUrl();
 
+  // Load |url| in the current tab. Returns immediately, before the new page has
+  // been loaded.
+  virtual void LoadURL(const GURL& url);
+
   // Perform a mouse left button click on the element given by |selectors| and
   // return the result through callback.
   // CSS selectors in |selectors| are ordered from top frame to the frame
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index 0938619..0df28081 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -14,6 +14,8 @@
 
 namespace autofill_assistant {
 
+const char* kTargetWebsitePath = "/autofill_assistant_target_website.html";
+
 class WebControllerBrowserTest : public content::ContentBrowserTest {
  public:
   WebControllerBrowserTest() {}
@@ -26,9 +28,8 @@
     http_server_->ServeFilesFromSourceDirectory(
         "components/test/data/autofill_assistant");
     ASSERT_TRUE(http_server_->Start());
-    ASSERT_TRUE(NavigateToURL(
-        shell(),
-        http_server_->GetURL("/autofill_assistant_target_website.html")));
+    ASSERT_TRUE(
+        NavigateToURL(shell(), http_server_->GetURL(kTargetWebsitePath)));
     web_controller_ =
         WebController::CreateForWebContents(shell()->web_contents());
   }
@@ -200,9 +201,11 @@
     std::move(done_callback).Run();
   }
 
+ protected:
+  std::unique_ptr<WebController> web_controller_;
+
  private:
   std::unique_ptr<net::EmbeddedTestServer> http_server_;
-  std::unique_ptr<WebController> web_controller_;
 
   DISALLOW_COPY_AND_ASSIGN(WebControllerBrowserTest);
 };
@@ -398,4 +401,11 @@
   EXPECT_FALSE(SetFieldValue(selectors, "foobar"));
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, NavigateToUrl) {
+  EXPECT_EQ(kTargetWebsitePath, web_controller_->GetUrl().path());
+  web_controller_->LoadURL(GURL(url::kAboutBlankURL));
+  WaitForLoadStop(shell()->web_contents());
+  EXPECT_EQ(url::kAboutBlankURL, web_controller_->GetUrl().spec());
+}
+
 }  // namespace
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index a861e9c..5ed2abe 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -35,7 +35,9 @@
 #include "components/sync/driver/proxy_data_type_controller.h"
 #include "components/sync/driver/sync_client.h"
 #include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/driver/syncable_service_based_model_type_controller.h"
 #include "components/sync/engine/sync_engine.h"
+#include "components/sync/model/model_type_store_service.h"
 #include "components/sync/model_impl/forwarding_model_type_controller_delegate.h"
 #include "components/sync/model_impl/proxy_model_type_controller_delegate.h"
 #include "components/sync_bookmarks/bookmark_change_processor.h"
@@ -59,6 +61,7 @@
 using syncer::DataTypeManagerObserver;
 using syncer::ModelTypeController;
 using syncer::ProxyDataTypeController;
+using syncer::SyncableServiceBasedModelTypeController;
 
 namespace browser_sync {
 
@@ -266,13 +269,29 @@
     // Favicon sync is enabled by default. Register unless explicitly disabled.
     if (!disabled_types.Has(syncer::FAVICON_IMAGES) &&
         !disabled_types.Has(syncer::FAVICON_TRACKING)) {
-      // crbug/384552. We disable error uploading for this data types for now.
-      controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
-          syncer::FAVICON_IMAGES, base::RepeatingClosure(), sync_client_,
-          syncer::GROUP_UI, ui_thread_));
-      controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
-          syncer::FAVICON_TRACKING, base::RepeatingClosure(), sync_client_,
-          syncer::GROUP_UI, ui_thread_));
+      if (base::FeatureList::IsEnabled(switches::kSyncPseudoUSSFavicons)) {
+        controllers.push_back(
+            std::make_unique<SyncableServiceBasedModelTypeController>(
+                syncer::FAVICON_IMAGES,
+                sync_client_->GetModelTypeStoreService()->GetStoreFactory(),
+                base::BindOnce(&syncer::SyncClient::GetSyncableServiceForType,
+                               base::Unretained(sync_client_),
+                               syncer::FAVICON_IMAGES)));
+        controllers.push_back(
+            std::make_unique<SyncableServiceBasedModelTypeController>(
+                syncer::FAVICON_TRACKING,
+                sync_client_->GetModelTypeStoreService()->GetStoreFactory(),
+                base::BindOnce(&syncer::SyncClient::GetSyncableServiceForType,
+                               base::Unretained(sync_client_),
+                               syncer::FAVICON_TRACKING)));
+      } else {
+        controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+            syncer::FAVICON_IMAGES, base::RepeatingClosure(), sync_client_,
+            syncer::GROUP_UI, ui_thread_));
+        controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+            syncer::FAVICON_TRACKING, base::RepeatingClosure(), sync_client_,
+            syncer::GROUP_UI, ui_thread_));
+      }
     }
   }
 
@@ -285,20 +304,40 @@
   }
 
   if (!disabled_types.Has(syncer::PREFERENCES)) {
-    if (!override_prefs_controller_to_uss_for_test_) {
+    if (override_prefs_controller_to_uss_for_test_) {
+      controllers.push_back(CreateModelTypeControllerForModelRunningOnUIThread(
+          syncer::PREFERENCES));
+    } else if (base::FeatureList::IsEnabled(
+                   switches::kSyncPseudoUSSPreferences)) {
+      controllers.push_back(
+          std::make_unique<SyncableServiceBasedModelTypeController>(
+              syncer::PREFERENCES,
+              sync_client_->GetModelTypeStoreService()->GetStoreFactory(),
+              base::BindOnce(&syncer::SyncClient::GetSyncableServiceForType,
+                             base::Unretained(sync_client_),
+                             syncer::PREFERENCES)));
+    } else {
       controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
           syncer::PREFERENCES, error_callback, sync_client_, syncer::GROUP_UI,
           ui_thread_));
-    } else {
-      controllers.push_back(CreateModelTypeControllerForModelRunningOnUIThread(
-          syncer::PREFERENCES));
     }
   }
 
   if (!disabled_types.Has(syncer::PRIORITY_PREFERENCES)) {
-    controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
-        syncer::PRIORITY_PREFERENCES, error_callback, sync_client_,
-        syncer::GROUP_UI, ui_thread_));
+    if (base::FeatureList::IsEnabled(
+            switches::kSyncPseudoUSSPriorityPreferences)) {
+      controllers.push_back(
+          std::make_unique<SyncableServiceBasedModelTypeController>(
+              syncer::PRIORITY_PREFERENCES,
+              sync_client_->GetModelTypeStoreService()->GetStoreFactory(),
+              base::BindOnce(&syncer::SyncClient::GetSyncableServiceForType,
+                             base::Unretained(sync_client_),
+                             syncer::PRIORITY_PREFERENCES)));
+    } else {
+      controllers.push_back(std::make_unique<AsyncDirectoryTypeController>(
+          syncer::PRIORITY_PREFERENCES, error_callback, sync_client_,
+          syncer::GROUP_UI, ui_thread_));
+    }
   }
 
 #if defined(OS_CHROMEOS)
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 4b6efca..05d1160cb 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -677,9 +677,6 @@
   // is decremented back to zero, Sync setup is marked no longer in progress.
   int outstanding_setup_in_progress_handles_ = 0;
 
-  // List of available data type controllers.
-  syncer::DataTypeController::TypeMap data_type_controllers_;
-
   // Whether the SyncEngine has been initialized.
   bool engine_initialized_;
 
@@ -737,6 +734,9 @@
 
   std::unique_ptr<syncer::DeviceInfoSyncBridge> device_info_sync_bridge_;
 
+  // List of available data type controllers.
+  syncer::DataTypeController::TypeMap data_type_controllers_;
+
   std::unique_ptr<syncer::NetworkResources> network_resources_;
 
   const StartBehavior start_behavior_;
diff --git a/components/browser_sync/sync_auth_manager.cc b/components/browser_sync/sync_auth_manager.cc
index 1f98567..b559018 100644
--- a/components/browser_sync/sync_auth_manager.cc
+++ b/components/browser_sync/sync_auth_manager.cc
@@ -177,10 +177,7 @@
         // Drop any access token here, to maintain the invariant that only one
         // of a token OR a pending request OR a pending retry can exist at any
         // time.
-        if (!access_token_.empty()) {
-          access_token_.clear();
-          credentials_changed_callback_.Run();
-        }
+        InvalidateAccessToken();
         request_access_token_backoff_.InformOfRequest(false);
         ScheduleAccessTokenRequest();
       }
@@ -210,6 +207,20 @@
   }
 }
 
+void SyncAuthManager::InvalidateAccessToken() {
+  if (access_token_.empty()) {
+    return;
+  }
+
+  identity_manager_->RemoveAccessTokenFromCache(
+      sync_account_.account_info.account_id,
+      OAuth2TokenService::ScopeSet{GaiaConstants::kChromeSyncOAuth2Scope},
+      access_token_);
+
+  access_token_.clear();
+  credentials_changed_callback_.Run();
+}
+
 void SyncAuthManager::ClearAccessTokenAndRequest() {
   access_token_.clear();
   request_access_token_retry_timer_.Stop();
@@ -425,13 +436,7 @@
 
   // Invalidate any previous token, otherwise the token service will return the
   // same token again.
-  if (!access_token_.empty()) {
-    identity_manager_->RemoveAccessTokenFromCache(
-        sync_account_.account_info.account_id, kOAuth2ScopeSet, access_token_);
-
-    access_token_.clear();
-    credentials_changed_callback_.Run();
-  }
+  InvalidateAccessToken();
 
   // Finally, kick off a new access token fetch.
   token_status_.token_request_time = base::Time::Now();
diff --git a/components/browser_sync/sync_auth_manager.h b/components/browser_sync/sync_auth_manager.h
index 44f0578c..745919ad 100644
--- a/components/browser_sync/sync_auth_manager.h
+++ b/components/browser_sync/sync_auth_manager.h
@@ -120,6 +120,12 @@
   // account to another is exposed to observers as a sign-out + sign-in.
   bool UpdateSyncAccountIfNecessary();
 
+  // Invalidates any current access token, which means invalidating it with the
+  // IdentityManager and also dropping our own cached copy. Meant to be called
+  // when we know the current token is invalid (e.g. expired). Does not do
+  // anything about any scheduled or ongoing request.
+  void InvalidateAccessToken();
+
   // Clears any access token we have, and cancels any pending or scheduled
   // request for one.
   void ClearAccessTokenAndRequest();
diff --git a/components/drive/service/fake_drive_service.cc b/components/drive/service/fake_drive_service.cc
index 378a5074..414a268 100644
--- a/components/drive/service/fake_drive_service.cc
+++ b/components/drive/service/fake_drive_service.cc
@@ -1563,6 +1563,19 @@
   return HTTP_SUCCESS;
 }
 
+google_apis::DriveApiErrorCode FakeDriveService::SetFileAsSharedWithMe(
+    const std::string& resource_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+
+  EntryInfo* entry = FindEntryByResourceId(resource_id);
+  if (!entry)
+    return HTTP_NOT_FOUND;
+
+  entry->change_resource.mutable_file()->set_shared_with_me_date(
+      base::Time::Now());
+  return HTTP_SUCCESS;
+}
+
 void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) {
   change_observers_.AddObserver(change_observer);
 }
diff --git a/components/drive/service/fake_drive_service.h b/components/drive/service/fake_drive_service.h
index de3ab351..10bcc28 100644
--- a/components/drive/service/fake_drive_service.h
+++ b/components/drive/service/fake_drive_service.h
@@ -332,6 +332,9 @@
       const std::string& resource_id,
       google_apis::drive::FileVisibility* visibility);
 
+  google_apis::DriveApiErrorCode SetFileAsSharedWithMe(
+      const std::string& resource_id);
+
   void AddChangeObserver(ChangeObserver* observer);
   void RemoveChangeObserver(ChangeObserver* observer);
 
diff --git a/components/password_manager/core/browser/blacklisted_duplicates_cleaner_unittest.cc b/components/password_manager/core/browser/blacklisted_duplicates_cleaner_unittest.cc
index f65f18d..43e2625 100644
--- a/components/password_manager/core/browser/blacklisted_duplicates_cleaner_unittest.cc
+++ b/components/password_manager/core/browser/blacklisted_duplicates_cleaner_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/password_manager/core/browser/blacklisted_duplicates_cleaner.h"
 
+#include "base/bind_helpers.h"
 #include "base/stl_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
@@ -87,7 +88,8 @@
   prefs()->registry()->RegisterBooleanPref(
       prefs::kCredentialsWithWrongSignonRealmRemoved, true);
 
-  password_manager_util::RemoveUselessCredentials(store(), prefs(), 0);
+  password_manager_util::RemoveUselessCredentials(store(), prefs(), 0,
+                                                  base::NullCallback());
   scoped_task_environment.RunUntilIdle();
 
   // Check that one of the next two forms was removed.
@@ -99,7 +101,8 @@
   EXPECT_FALSE(
       prefs()->GetBoolean(prefs::kDuplicatedBlacklistedCredentialsRemoved));
 
-  password_manager_util::RemoveUselessCredentials(store(), prefs(), 0);
+  password_manager_util::RemoveUselessCredentials(store(), prefs(), 0,
+                                                  base::NullCallback());
   scoped_task_environment.RunUntilIdle();
   EXPECT_TRUE(
       prefs()->GetBoolean(prefs::kDuplicatedBlacklistedCredentialsRemoved));
diff --git a/components/password_manager/core/browser/form_saver_impl.cc b/components/password_manager/core/browser/form_saver_impl.cc
index 63835157..07d51de9 100644
--- a/components/password_manager/core/browser/form_saver_impl.cc
+++ b/components/password_manager/core/browser/form_saver_impl.cc
@@ -16,10 +16,32 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+using autofill::FormData;
+using autofill::FormFieldData;
 using autofill::PasswordForm;
 
 namespace password_manager {
 
+namespace {
+
+// Remove all information from |form| that is not required for signature
+// calculation.
+void SanitizeFormData(FormData* form) {
+  form->main_frame_origin = url::Origin();
+  for (FormFieldData& field : form->fields) {
+    field.label.clear();
+    field.value.clear();
+    field.autocomplete_attribute.clear();
+    field.option_values.clear();
+    field.option_contents.clear();
+    field.placeholder.clear();
+    field.css_classes.clear();
+    field.id.clear();
+  }
+}
+
+}  // namespace
+
 FormSaverImpl::FormSaverImpl(PasswordStore* store) : store_(store) {
   DCHECK(store);
 }
@@ -53,11 +75,13 @@
 }
 
 void FormSaverImpl::PresaveGeneratedPassword(const PasswordForm& generated) {
+  auto form = std::make_unique<PasswordForm>(generated);
+  SanitizeFormData(&form->form_data);
   if (presaved_)
-    store_->UpdateLoginWithPrimaryKey(generated, *presaved_);
+    store_->UpdateLoginWithPrimaryKey(*form, *presaved_);
   else
-    store_->AddLogin(generated);
-  presaved_.reset(new PasswordForm(generated));
+    store_->AddLogin(*form);
+  presaved_ = std::move(form);
 }
 
 void FormSaverImpl::RemovePresavedPassword() {
@@ -72,7 +96,7 @@
   auto result = std::make_unique<FormSaverImpl>(store_);
   if (presaved_)
     result->presaved_ = std::make_unique<PasswordForm>(*presaved_);
-  return std::move(result);
+  return result;
 }
 
 void FormSaverImpl::SaveImpl(
@@ -85,18 +109,20 @@
   DCHECK(!pending.blacklisted_by_user);
 
   UpdatePreferredLoginState(pending.username_value, best_matches);
+  PasswordForm sanitized_pending(pending);
+  SanitizeFormData(&sanitized_pending.form_data);
   if (presaved_) {
-    store_->UpdateLoginWithPrimaryKey(pending, *presaved_);
+    store_->UpdateLoginWithPrimaryKey(sanitized_pending, *presaved_);
     presaved_ = nullptr;
   } else if (is_new_login) {
-    store_->AddLogin(pending);
-    if (!pending.username_value.empty())
-      DeleteEmptyUsernameCredentials(pending, best_matches);
+    store_->AddLogin(sanitized_pending);
+    if (!sanitized_pending.username_value.empty())
+      DeleteEmptyUsernameCredentials(sanitized_pending, best_matches);
   } else {
     if (old_primary_key)
-      store_->UpdateLoginWithPrimaryKey(pending, *old_primary_key);
+      store_->UpdateLoginWithPrimaryKey(sanitized_pending, *old_primary_key);
     else
-      store_->UpdateLogin(pending);
+      store_->UpdateLogin(sanitized_pending);
   }
 
   if (credentials_to_update) {
@@ -115,6 +141,7 @@
         form.username_value != preferred_username) {
       // This wasn't the selected login but it used to be preferred.
       PasswordForm update(form);
+      SanitizeFormData(&update.form_data);
       update.preferred = false;
       store_->UpdateLogin(update);
     }
diff --git a/components/password_manager/core/browser/form_saver_impl_unittest.cc b/components/password_manager/core/browser/form_saver_impl_unittest.cc
index 9bccc89..758c90b3 100644
--- a/components/password_manager/core/browser/form_saver_impl_unittest.cc
+++ b/components/password_manager/core/browser/form_saver_impl_unittest.cc
@@ -20,6 +20,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+using autofill::FormFieldData;
 using autofill::PasswordForm;
 using base::ASCIIToUTF16;
 using base::StringPiece;
@@ -530,4 +531,37 @@
   clone->PresaveGeneratedPassword(generated);
 }
 
+// Check that on saving the pending form |form_data| is sanitized.
+TEST_F(FormSaverImplTest, FormDataSanitized) {
+  PasswordForm pending = CreatePending("nameofuser", "wordToP4a55");
+  FormFieldData field;
+  field.name = ASCIIToUTF16("name");
+  field.form_control_type = "password";
+  field.value = ASCIIToUTF16("value");
+  field.label = ASCIIToUTF16("label");
+  field.placeholder = ASCIIToUTF16("placeholder");
+  field.id = ASCIIToUTF16("id");
+  field.css_classes = ASCIIToUTF16("css_classes");
+  pending.form_data.fields.push_back(field);
+
+  for (bool presave : {false, true}) {
+    PasswordForm saved;
+    EXPECT_CALL(*mock_store_, AddLogin(_)).WillOnce(SaveArg<0>(&saved));
+    if (presave)
+      form_saver_.PresaveGeneratedPassword(pending);
+    else
+      form_saver_.Save(pending, {});
+
+    ASSERT_EQ(1u, saved.form_data.fields.size());
+    const FormFieldData& saved_field = saved.form_data.fields[0];
+    EXPECT_EQ(ASCIIToUTF16("name"), saved_field.name);
+    EXPECT_EQ("password", saved_field.form_control_type);
+    EXPECT_TRUE(saved_field.value.empty());
+    EXPECT_TRUE(saved_field.label.empty());
+    EXPECT_TRUE(saved_field.placeholder.empty());
+    EXPECT_TRUE(saved_field.id.empty());
+    EXPECT_TRUE(saved_field.css_classes.empty());
+  }
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/http_credentials_cleaner.cc b/components/password_manager/core/browser/http_credentials_cleaner.cc
index 072d27c..0119b85 100644
--- a/components/password_manager/core/browser/http_credentials_cleaner.cc
+++ b/components/password_manager/core/browser/http_credentials_cleaner.cc
@@ -16,11 +16,17 @@
         network_context_getter)
     : store_(std::move(store)),
       network_context_getter_(network_context_getter) {
-  store_->GetAutofillableLogins(this);
 }
 
 HttpCredentialCleaner::~HttpCredentialCleaner() = default;
 
+void HttpCredentialCleaner::StartCleaning(Observer* observer) {
+  DCHECK(observer);
+  DCHECK(!observer_);
+  observer_ = observer;
+  store_->GetAutofillableLogins(this);
+}
+
 void HttpCredentialCleaner::OnGetPasswordStoreResults(
     std::vector<std::unique_ptr<autofill::PasswordForm>> results) {
   // Non HTTP or HTTPS credentials are ignored.
@@ -103,7 +109,7 @@
         https_credential_not_found_[is_hsts_enabled]);
   }
 
-  delete this;
+  observer_->CleaningCompleted();
 }
 
 }  // namespace password_manager
\ No newline at end of file
diff --git a/components/password_manager/core/browser/http_credentials_cleaner.h b/components/password_manager/core/browser/http_credentials_cleaner.h
index 42a0767..ec01e306 100644
--- a/components/password_manager/core/browser/http_credentials_cleaner.h
+++ b/components/password_manager/core/browser/http_credentials_cleaner.h
@@ -15,6 +15,7 @@
 #include "base/containers/flat_set.h"
 #include "base/memory/ref_counted.h"
 #include "components/autofill/core/common/password_form.h"
+#include "components/password_manager/core/browser/credentials_cleaner.h"
 #include "components/password_manager/core/browser/hsts_query.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
 
@@ -29,14 +30,20 @@
 class PasswordStore;
 
 // This class is responsible for reporting metrics about HTTP to HTTPS
-// migration. Important note: The object will delete itself once metrics are
-// reported. Having a private destructor enforces this.
-class HttpCredentialCleaner : public PasswordStoreConsumer {
+// migration.
+class HttpCredentialCleaner : public PasswordStoreConsumer,
+                              public CredentialsCleaner {
  public:
+  // |network_context_getter| should return nullptr if it can't get the network
+  // context because whatever owns it is dead.
   HttpCredentialCleaner(
       scoped_refptr<PasswordStore> store,
       base::RepeatingCallback<network::mojom::NetworkContext*()>
           network_context_getter);
+  ~HttpCredentialCleaner() override;
+
+  // CredentialsCleaner:
+  void StartCleaning(Observer* observer) override;
 
  private:
   // This type define a subset of PasswordForm where first argument is the
@@ -46,8 +53,6 @@
   using FormKey =
       std::tuple<std::string, autofill::PasswordForm::Scheme, base::string16>;
 
-  ~HttpCredentialCleaner() override;
-
   // PasswordStoreConsumer:
   void OnGetPasswordStoreResults(
       std::vector<std::unique_ptr<autofill::PasswordForm>> results) override;
@@ -56,6 +61,8 @@
                          base::string16 password_value,
                          HSTSResult hsts_result);
 
+  // After metrics are reported, this function will inform the |observer_| about
+  // completion.
   void ReportMetrics();
 
   scoped_refptr<PasswordStore> store_;
@@ -68,6 +75,10 @@
   // tuples of HTTPS forms to a list of passwords for that pair.
   std::map<FormKey, base::flat_set<base::string16>> https_credentials_map_;
 
+  // Used to signal completion of the clean-up. It is null until
+  // StartCleaning is called.
+  Observer* observer_ = nullptr;
+
   // The number of HTTP credentials processed after HSTS query results are
   // received.
   size_t processed_results_ = 0;
diff --git a/components/password_manager/core/browser/http_credentials_cleaner_unittest.cc b/components/password_manager/core/browser/http_credentials_cleaner_unittest.cc
index 571e3ed..1720124 100644
--- a/components/password_manager/core/browser/http_credentials_cleaner_unittest.cc
+++ b/components/password_manager/core/browser/http_credentials_cleaner_unittest.cc
@@ -10,10 +10,13 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_task_environment.h"
-#include "components/password_manager/core/browser/password_manager_util.h"
 #include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
 #include "net/url_request/url_request_test_util.h"
 #include "services/network/network_context.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -90,6 +93,16 @@
 
 }  // namespace
 
+class MockCredentialsCleanerObserver : public CredentialsCleaner::Observer {
+ public:
+  MockCredentialsCleanerObserver() = default;
+  ~MockCredentialsCleanerObserver() override = default;
+  MOCK_METHOD0(CleaningCompleted, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockCredentialsCleanerObserver);
+};
+
 class HttpCredentialCleanerTest : public ::testing::TestWithParam<TestCase> {
  public:
   HttpCredentialCleanerTest() = default;
@@ -167,12 +180,10 @@
   }
   scoped_task_environment.RunUntilIdle();
 
-  const TestPasswordStore::PasswordMap passwords_before_cleaning =
-      store_->stored_passwords();
-
   base::HistogramTester histogram_tester;
 
-  password_manager_util::ReportHttpMigrationMetrics(
+  MockCredentialsCleanerObserver observer;
+  HttpCredentialCleaner cleaner(
       store_,
       base::BindLambdaForTesting([&]() -> network::mojom::NetworkContext* {
         // This needs to be network_context_pipe.get() and
@@ -183,6 +194,8 @@
         // even in the in-process case.
         return network_context_pipe.get();
       }));
+  EXPECT_CALL(observer, CleaningCompleted);
+  cleaner.StartCleaning(&observer);
   scoped_task_environment.RunUntilIdle();
 
   std::vector<Histogram> histograms_to_test;
diff --git a/components/password_manager/core/browser/invalid_realm_credential_cleaner_unittest.cc b/components/password_manager/core/browser/invalid_realm_credential_cleaner_unittest.cc
index 620f0762..2143c26 100644
--- a/components/password_manager/core/browser/invalid_realm_credential_cleaner_unittest.cc
+++ b/components/password_manager/core/browser/invalid_realm_credential_cleaner_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "components/password_manager/core/browser/invalid_realm_credential_cleaner.h"
 
+#include "base/bind_helpers.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_task_environment.h"
@@ -53,6 +54,11 @@
 const base::string16 kUsernames[] = {base::ASCIIToUTF16("user0"),
                                      base::ASCIIToUTF16("user1")};
 
+// TODO(http://crbug.com/889983): This callback is needed to be passed to
+// function that does the clean-up, but is not used. Remove it once the function
+// is skipped.
+auto null_callback = base::NullCallback();
+
 bool StoreContains(TestPasswordStore* store,
                    const autofill::PasswordForm& form) {
   const auto it = store->stored_passwords().find(form.signon_realm);
@@ -212,7 +218,8 @@
     prefs.registry()->RegisterBooleanPref(
         prefs::kCredentialsWithWrongSignonRealmRemoved, false);
 
-    password_manager_util::RemoveUselessCredentials(password_store, &prefs, 0);
+    password_manager_util::RemoveUselessCredentials(password_store, &prefs, 0,
+                                                    null_callback);
     scoped_task_environment.RunUntilIdle();
 
     EXPECT_EQ(StoreContains(password_store.get(), https_form),
@@ -351,7 +358,8 @@
     prefs.registry()->RegisterBooleanPref(
         prefs::kCredentialsWithWrongSignonRealmRemoved, false);
 
-    password_manager_util::RemoveUselessCredentials(password_store, &prefs, 0);
+    password_manager_util::RemoveUselessCredentials(password_store, &prefs, 0,
+                                                    null_callback);
     scoped_task_environment.RunUntilIdle();
 
     EXPECT_NE(StoreContains(password_store.get(), https_form),
@@ -390,7 +398,8 @@
   prefs.registry()->RegisterBooleanPref(
       prefs::kCredentialsWithWrongSignonRealmRemoved, false);
 
-  password_manager_util::RemoveUselessCredentials(password_store, &prefs, 0);
+  password_manager_util::RemoveUselessCredentials(password_store, &prefs, 0,
+                                                  null_callback);
   scoped_task_environment.RunUntilIdle();
 
   // Check that credentials were not deleted.
diff --git a/components/password_manager/core/browser/mock_password_store.cc b/components/password_manager/core/browser/mock_password_store.cc
index 8279307..c88b29a 100644
--- a/components/password_manager/core/browser/mock_password_store.cc
+++ b/components/password_manager/core/browser/mock_password_store.cc
@@ -18,7 +18,9 @@
   return base::SequencedTaskRunnerHandle::Get();
 }
 
-void MockPasswordStore::InitOnBackgroundSequence(
-    const syncer::SyncableService::StartSyncFlare& flare) {}
+bool MockPasswordStore::InitOnBackgroundSequence(
+    const syncer::SyncableService::StartSyncFlare& flare) {
+  return true;
+}
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/mock_password_store.h b/components/password_manager/core/browser/mock_password_store.h
index b8c714a..f2aca6a 100644
--- a/components/password_manager/core/browser/mock_password_store.h
+++ b/components/password_manager/core/browser/mock_password_store.h
@@ -97,7 +97,7 @@
   // PasswordStore:
   scoped_refptr<base::SequencedTaskRunner> CreateBackgroundTaskRunner()
       const override;
-  void InitOnBackgroundSequence(
+  bool InitOnBackgroundSequence(
       const syncer::SyncableService::StartSyncFlare& flare) override;
 };
 
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 698592a..580dd97 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -19,6 +19,8 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "build/build_config.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
 #include "components/password_manager/core/browser/form_fetcher_impl.h"
 #include "components/password_manager/core/browser/mock_password_store.h"
 #include "components/password_manager/core/browser/new_password_form_manager.h"
@@ -39,6 +41,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using autofill::FormData;
+using autofill::FormFieldData;
 using autofill::PasswordForm;
 using base::ASCIIToUTF16;
 using base::TestMockTimeTaskRunner;
@@ -149,6 +153,20 @@
 
 ACTION_P(SaveToScopedPtr, scoped) { scoped->reset(arg0); }
 
+void SanitizeFormData(FormData* form) {
+  form->main_frame_origin = url::Origin();
+  for (FormFieldData& field : form->fields) {
+    field.label.clear();
+    field.value.clear();
+    field.autocomplete_attribute.clear();
+    field.option_values.clear();
+    field.option_contents.clear();
+    field.placeholder.clear();
+    field.css_classes.clear();
+    field.id.clear();
+  }
+}
+
 }  // namespace
 
 class PasswordManagerTest : public testing::Test {
@@ -1781,20 +1799,26 @@
 
   // The user accepts a generated password.
   form.password_value = base::ASCIIToUTF16("password");
-  EXPECT_CALL(*store_, AddLogin(form)).WillOnce(Return());
+  PasswordForm sanitized_form(form);
+  SanitizeFormData(&sanitized_form.form_data);
+
+  EXPECT_CALL(*store_, AddLogin(sanitized_form)).WillOnce(Return());
   manager()->OnPresaveGeneratedPassword(form);
 
   // The user updates the generated password.
   PasswordForm updated_form(form);
   updated_form.password_value = base::ASCIIToUTF16("password_12345");
-  EXPECT_CALL(*store_, UpdateLoginWithPrimaryKey(updated_form, form))
+  PasswordForm sanitized_updated_form(updated_form);
+  SanitizeFormData(&sanitized_updated_form.form_data);
+  EXPECT_CALL(*store_,
+              UpdateLoginWithPrimaryKey(sanitized_updated_form, sanitized_form))
       .WillOnce(Return());
   manager()->OnPresaveGeneratedPassword(updated_form);
   histogram_tester.ExpectUniqueSample(
       "PasswordManager.GeneratedFormHasNoFormManager", false, 2);
 
   // The user removes the generated password.
-  EXPECT_CALL(*store_, RemoveLogin(updated_form)).WillOnce(Return());
+  EXPECT_CALL(*store_, RemoveLogin(sanitized_updated_form)).WillOnce(Return());
   manager()->OnPasswordNoLongerGenerated(updated_form);
 }
 
@@ -1830,6 +1854,7 @@
     SCOPED_TRACE(testing::Message("found_matched_logins_in_store = ")
                  << found_matched_logins_in_store);
     PasswordForm form(MakeFormWithOnlyNewPasswordField());
+    SanitizeFormData(&form.form_data);
     std::vector<PasswordForm> observed = {form};
     if (found_matched_logins_in_store) {
       EXPECT_CALL(*store_, GetLogins(_, _))
diff --git a/components/password_manager/core/browser/password_manager_util.cc b/components/password_manager/core/browser/password_manager_util.cc
index c0511d16..49314ba 100644
--- a/components/password_manager/core/browser/password_manager_util.cc
+++ b/components/password_manager/core/browser/password_manager_util.cc
@@ -48,17 +48,6 @@
 
 }  // namespace
 
-#if !defined(OS_IOS)
-void ReportHttpMigrationMetrics(
-    scoped_refptr<password_manager::PasswordStore> store,
-    base::RepeatingCallback<network::mojom::NetworkContext*()>
-        network_context_getter) {
-  // The object will delete itself once the metrics are recorded.
-  new password_manager::HttpCredentialCleaner(std::move(store),
-                                              network_context_getter);
-}
-#endif  // !defined(OS_IOS)
-
 // Update |credential| to reflect usage.
 void UpdateMetadataForUsage(PasswordForm* credential) {
   ++credential->times_used;
@@ -200,7 +189,9 @@
 void RemoveUselessCredentials(
     scoped_refptr<password_manager::PasswordStore> store,
     PrefService* prefs,
-    int delay_in_seconds) {
+    int delay_in_seconds,
+    base::RepeatingCallback<network::mojom::NetworkContext*()>
+        network_context_getter) {
   // TODO(https://crbug.com/887889): Remove the knowledge of the particular
   // preferences from this code.
 
@@ -236,6 +227,15 @@
             store, prefs));
   }
 
+#if !defined(OS_IOS)
+  // Can be null for some unittests.
+  if (!network_context_getter.is_null()) {
+    cleaning_tasks_runner->AddCleaningTask(
+        std::make_unique<password_manager::HttpCredentialCleaner>(
+            store, network_context_getter));
+  }
+#endif  // !defined(OS_IOS)
+
   base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&password_manager::CredentialsCleanerRunner::StartCleaning,
diff --git a/components/password_manager/core/browser/password_manager_util.h b/components/password_manager/core/browser/password_manager_util.h
index a855bab..e6b75d65 100644
--- a/components/password_manager/core/browser/password_manager_util.h
+++ b/components/password_manager/core/browser/password_manager_util.h
@@ -100,10 +100,16 @@
 // hence blacklisted duplicates need to be removed.
 // (2) Removing or fixing of HTTPS credentials with wrong signon_realm. See
 // https://crbug.com/881731 for details.
+// (3) Report metrics about HTTP to HTTPS migration process. This feature
+// is not available on iOS platform because the HSTS query is not supported.
+// |network_context_getter| is always null for iOS and it can also be null for
+// some unittests.
 void RemoveUselessCredentials(
     scoped_refptr<password_manager::PasswordStore> store,
     PrefService* prefs,
-    int delay_in_seconds);
+    int delay_in_seconds,
+    base::RepeatingCallback<network::mojom::NetworkContext*()>
+        network_context_getter);
 
 // Excluding protocol from a signon_realm means to remove from the signon_realm
 // what is before the web origin (with the protocol excluded as well). For
@@ -113,15 +119,6 @@
 base::StringPiece GetSignonRealmWithProtocolExcluded(
     const autofill::PasswordForm& form);
 
-// Report metrics about HTTP to HTTPS migration process. This function cannot be
-// used on iOS platform because the HSTS query is not supported.
-// |network_context_getter| should return nullptr if it can't get the network
-// context because whatever owns it is dead.
-void ReportHttpMigrationMetrics(
-    scoped_refptr<password_manager::PasswordStore> store,
-    base::RepeatingCallback<network::mojom::NetworkContext*()>
-        network_context_getter);
-
 // Given all non-blacklisted |matches|, finds and populates
 // |best_matches_|, |preferred_match_| and |non_best_matches_| accordingly.
 // For comparing credentials the following rule is used: non-psl match is better
diff --git a/components/password_manager/core/browser/password_manager_util_unittest.cc b/components/password_manager/core/browser/password_manager_util_unittest.cc
index 1577721..d6a7c3e 100644
--- a/components/password_manager/core/browser/password_manager_util_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_util_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind_helpers.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -176,7 +177,7 @@
         password_manager::prefs::kCredentialsWithWrongSignonRealmRemoved,
         false);
 
-    RemoveUselessCredentials(password_store, &prefs, 0);
+    RemoveUselessCredentials(password_store, &prefs, 0, base::NullCallback());
     scoped_task_environment.RunUntilIdle();
 
     // Check that invalid credentials were removed.
@@ -191,7 +192,7 @@
     EXPECT_NE(StoreContains(password_store.get(), https_blacklisted),
               StoreContains(password_store.get(), https_blacklisted_duplicate));
 
-    RemoveUselessCredentials(password_store, &prefs, 0);
+    RemoveUselessCredentials(password_store, &prefs, 0, base::NullCallback());
     scoped_task_environment.RunUntilIdle();
 
     // Nothing must be removed by a second call.
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index e543a24..69d3a8c 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -15,6 +15,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/task/post_task.h"
+#include "base/task_runner_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "build/build_config.h"
 #include "components/autofill/core/common/form_data.h"
@@ -122,7 +123,8 @@
 PasswordStore::PasswordStore()
     : observers_(new base::ObserverListThreadSafe<Observer>()),
       is_propagating_password_changes_to_web_credentials_enabled_(false),
-      shutdown_called_(false) {}
+      shutdown_called_(false),
+      init_status_(InitStatus::kUnknown) {}
 
 bool PasswordStore::Init(const syncer::SyncableService::StartSyncFlare& flare,
                          PrefService* prefs) {
@@ -134,8 +136,12 @@
   prefs_ = prefs;
   hash_password_manager_.set_prefs(prefs);
 #endif
-  ScheduleTask(base::BindRepeating(&PasswordStore::InitOnBackgroundSequence,
-                                   this, flare));
+  if (background_task_runner_) {
+    base::PostTaskAndReplyWithResult(
+        background_task_runner_.get(), FROM_HERE,
+        base::BindOnce(&PasswordStore::InitOnBackgroundSequence, this, flare),
+        base::BindOnce(&PasswordStore::OnInitCompleted, this));
+  }
   return true;
 }
 
@@ -331,6 +337,10 @@
   return false;
 }
 
+bool PasswordStore::IsAbleToSavePasswords() const {
+  return init_status_ == InitStatus::kSuccess;
+}
+
 void PasswordStore::ShutdownOnUIThread() {
   ScheduleTask(base::Bind(&PasswordStore::DestroyOnBackgroundSequence, this));
   // The AffiliationService must be destroyed from the main sequence.
@@ -468,7 +478,7 @@
       {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
 }
 
-void PasswordStore::InitOnBackgroundSequence(
+bool PasswordStore::InitOnBackgroundSequence(
     const syncer::SyncableService::StartSyncFlare& flare) {
   DCHECK(background_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(!syncable_service_);
@@ -480,6 +490,7 @@
   GetAutofillableLoginsImpl(
       std::make_unique<GetLoginsRequest>(reuse_detector_));
 #endif
+  return true;
 }
 
 void PasswordStore::GetLoginsImpl(const FormDigest& form,
@@ -604,6 +615,12 @@
 }
 #endif
 
+void PasswordStore::OnInitCompleted(bool success) {
+  DCHECK(main_task_runner_->RunsTasksInCurrentSequence());
+  init_status_ = success ? InitStatus::kSuccess : InitStatus::kFailure;
+  // TODO(tsabolcec): Add UMA histogram to record the success rate.
+}
+
 void PasswordStore::Schedule(
     void (PasswordStore::*func)(std::unique_ptr<GetLoginsRequest>),
     PasswordStoreConsumer* consumer) {
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index 8c17f18..f91e40d 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -252,6 +252,9 @@
   // Schedules the given |task| to be run on the PasswordStore's TaskRunner.
   bool ScheduleTask(const base::Closure& task);
 
+  // Returns true iff initialization was successful.
+  bool IsAbleToSavePasswords() const;
+
   base::WeakPtr<syncer::SyncableService> GetPasswordSyncableService();
 
 // TODO(crbug.com/706392): Fix password reuse detection for Android.
@@ -369,6 +372,19 @@
   };
 #endif
 
+  // Status of PasswordStore::Init().
+  enum class InitStatus {
+    // Initialization status is still not determined (init hasn't started or
+    // finished yet).
+    kUnknown,
+    // Initialization is successfully finished.
+    kSuccess,
+    // There was an error during initialization and PasswordStore is not ready
+    // to save or get passwords.
+    // Removing passwords may still work.
+    kFailure,
+  };
+
   ~PasswordStore() override;
 
   // Create a TaskRunner to be saved in |background_task_runner_|.
@@ -376,8 +392,9 @@
       const;
 
   // Creates PasswordSyncableService and PasswordReuseDetector instances on the
-  // background sequence. Subclasses can add more logic.
-  virtual void InitOnBackgroundSequence(
+  // background sequence. Subclasses can add more logic. Returns true on
+  // success.
+  virtual bool InitOnBackgroundSequence(
       const syncer::SyncableService::StartSyncFlare& flare);
 
   // Methods below will be run in PasswordStore's own sequence.
@@ -521,6 +538,11 @@
   FRIEND_TEST_ALL_PREFIXES(PasswordStoreTest,
                            UpdatePasswordsStoredForAffiliatedWebsites);
 
+  // Called on the main thread after initialization is completed.
+  // |success| is true if initialization was successful. Sets the
+  // |init_status_|.
+  void OnInitCompleted(bool success);
+
   // Schedule the given |func| to be run in the PasswordStore's own sequence
   // with responses delivered to |consumer| on the current sequence.
   void Schedule(void (PasswordStore::*func)(std::unique_ptr<GetLoginsRequest>),
@@ -676,6 +698,8 @@
 
   bool shutdown_called_;
 
+  InitStatus init_status_;
+
   DISALLOW_COPY_AND_ASSIGN(PasswordStore);
 };
 
diff --git a/components/password_manager/core/browser/password_store_default.cc b/components/password_manager/core/browser/password_store_default.cc
index 18a548e..a3be1aa2 100644
--- a/components/password_manager/core/browser/password_store_default.cc
+++ b/components/password_manager/core/browser/password_store_default.cc
@@ -28,15 +28,19 @@
   ScheduleTask(base::Bind(&PasswordStoreDefault::ResetLoginDB, this));
 }
 
-void PasswordStoreDefault::InitOnBackgroundSequence(
+bool PasswordStoreDefault::InitOnBackgroundSequence(
     const syncer::SyncableService::StartSyncFlare& flare) {
   DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
   DCHECK(login_db_);
+  bool success = true;
   if (!login_db_->Init()) {
     login_db_.reset();
+    // The initialization should be continued, because PasswordSyncableService
+    // has to be initialized even if database initialization failed.
+    success = false;
     LOG(ERROR) << "Could not create/open login database.";
   }
-  PasswordStore::InitOnBackgroundSequence(flare);
+  return PasswordStore::InitOnBackgroundSequence(flare) && success;
 }
 
 void PasswordStoreDefault::ReportMetricsImpl(
diff --git a/components/password_manager/core/browser/password_store_default.h b/components/password_manager/core/browser/password_store_default.h
index 3a203da1..c80e3dd 100644
--- a/components/password_manager/core/browser/password_store_default.h
+++ b/components/password_manager/core/browser/password_store_default.h
@@ -41,7 +41,7 @@
   ~PasswordStoreDefault() override;
 
   // Opens |login_db_| on the background sequence.
-  void InitOnBackgroundSequence(
+  bool InitOnBackgroundSequence(
       const syncer::SyncableService::StartSyncFlare& flare) override;
 
   // Implements PasswordStore interface.
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index e9a0a060..1ed14318 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -157,6 +157,8 @@
     "driver/sync_type_preference_provider.h",
     "driver/sync_util.cc",
     "driver/sync_util.h",
+    "driver/syncable_service_based_model_type_controller.cc",
+    "driver/syncable_service_based_model_type_controller.h",
     "driver/user_selectable_sync_type.h",
     "engine/commit_queue.cc",
     "engine/commit_queue.h",
@@ -450,6 +452,8 @@
     "model_impl/proxy_model_type_controller_delegate.h",
     "model_impl/sync_metadata_store_change_list.cc",
     "model_impl/sync_metadata_store_change_list.h",
+    "model_impl/syncable_service_based_bridge.cc",
+    "model_impl/syncable_service_based_bridge.h",
     "protocol/proto_enum_conversions.cc",
     "protocol/proto_enum_conversions.h",
     "protocol/proto_memory_estimations.cc",
@@ -902,6 +906,7 @@
     "model_impl/model_type_store_backend_unittest.cc",
     "model_impl/model_type_store_impl_unittest.cc",
     "model_impl/processor_entity_tracker_unittest.cc",
+    "model_impl/syncable_service_based_bridge_unittest.cc",
     "protocol/proto_enum_conversions_unittest.cc",
     "protocol/proto_value_conversions_unittest.cc",
     "syncable/change_record_unittest.cc",
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index 42a1429..020aa1b 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -46,6 +46,25 @@
 const base::Feature kSyncClearDataOnPassphraseEncryption{
     "ClearSyncDataOnPassphraseEncryption", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// For each below, if enabled, the SyncableService implementation of the
+// corresponding datatype(s) is wrapped within the USS architecture.
+const base::Feature kSyncPseudoUSSAppList{"SyncPseudoUSSAppList",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSyncPseudoUSSApps{"SyncPseudoUSSApps",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSyncPseudoUSSDictionary{"SyncPseudoUSSDictionary",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSyncPseudoUSSExtensions{"SyncPseudoUSSExtensions",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSyncPseudoUSSFavicons{"SyncPseudoUSSFavicons",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSyncPseudoUSSPreferences{
+    "SyncPseudoUSSPreferences", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSyncPseudoUSSPriorityPreferences{
+    "SyncPseudoUSSPriorityPreferences", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kSyncPseudoUSSThemes{"SyncPseudoUSSThemes",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
 // If enabled, allows the Sync machinery ("transport layer") to start
 // independently of Sync-the-feature.
 const base::Feature kSyncStandaloneTransport{"SyncStandaloneTransport",
diff --git a/components/sync/driver/sync_driver_switches.h b/components/sync/driver/sync_driver_switches.h
index f964a78..00b6099a 100644
--- a/components/sync/driver/sync_driver_switches.h
+++ b/components/sync/driver/sync_driver_switches.h
@@ -23,6 +23,14 @@
 extern const base::Feature
     kSyncAllowWalletDataInTransportModeWithCustomPassphrase;
 extern const base::Feature kSyncClearDataOnPassphraseEncryption;
+extern const base::Feature kSyncPseudoUSSAppList;
+extern const base::Feature kSyncPseudoUSSApps;
+extern const base::Feature kSyncPseudoUSSDictionary;
+extern const base::Feature kSyncPseudoUSSExtensions;
+extern const base::Feature kSyncPseudoUSSFavicons;
+extern const base::Feature kSyncPseudoUSSPreferences;
+extern const base::Feature kSyncPseudoUSSPriorityPreferences;
+extern const base::Feature kSyncPseudoUSSThemes;
 extern const base::Feature kSyncStandaloneTransport;
 extern const base::Feature kSyncSupportSecondaryAccount;
 extern const base::Feature kSyncUserEvents;
diff --git a/components/sync/driver/syncable_service_based_model_type_controller.cc b/components/sync/driver/syncable_service_based_model_type_controller.cc
new file mode 100644
index 0000000..6d1ca9e
--- /dev/null
+++ b/components/sync/driver/syncable_service_based_model_type_controller.cc
@@ -0,0 +1,98 @@
+// 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/syncable_service_based_model_type_controller.h"
+
+#include <utility>
+
+#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+#include "components/sync/model_impl/forwarding_model_type_controller_delegate.h"
+#include "components/sync/model_impl/syncable_service_based_bridge.h"
+
+namespace syncer {
+
+namespace {
+
+// Similar to ForwardingModelTypeControllerDelegate, but allows evaluating the
+// reference to SyncableService in a lazy way, which is convenient for tests.
+class ControllerDelegate : public ModelTypeControllerDelegate {
+ public:
+  using SyncableServiceProvider =
+      SyncableServiceBasedModelTypeController::SyncableServiceProvider;
+
+  ControllerDelegate(ModelType type,
+                     OnceModelTypeStoreFactory store_factory,
+                     SyncableServiceProvider syncable_service_provider)
+      : type_(type),
+        store_factory_(std::move(store_factory)),
+        syncable_service_provider_(std::move(syncable_service_provider)) {
+    DCHECK(store_factory_);
+    DCHECK(syncable_service_provider_);
+  }
+
+  ~ControllerDelegate() override {}
+
+  void OnSyncStarting(const DataTypeActivationRequest& request,
+                      StartCallback callback) override {
+    BuildOrGetBridgeDelegate()->OnSyncStarting(request, std::move(callback));
+  }
+
+  void OnSyncStopping(SyncStopMetadataFate metadata_fate) override {
+    BuildOrGetBridgeDelegate()->OnSyncStopping(metadata_fate);
+  }
+
+  void GetAllNodesForDebugging(AllNodesCallback callback) override {
+    BuildOrGetBridgeDelegate()->GetAllNodesForDebugging(std::move(callback));
+  }
+
+  void GetStatusCountersForDebugging(StatusCountersCallback callback) override {
+    BuildOrGetBridgeDelegate()->GetStatusCountersForDebugging(
+        std::move(callback));
+  }
+
+  void RecordMemoryUsageAndCountsHistograms() override {
+    BuildOrGetBridgeDelegate()->RecordMemoryUsageAndCountsHistograms();
+  }
+
+ private:
+  ModelTypeControllerDelegate* BuildOrGetBridgeDelegate() {
+    if (!bridge_) {
+      base::WeakPtr<SyncableService> syncable_service =
+          std::move(syncable_service_provider_).Run();
+      DCHECK(syncable_service);
+      bridge_ = std::make_unique<SyncableServiceBasedBridge>(
+          type_, std::move(store_factory_),
+          std::make_unique<ClientTagBasedModelTypeProcessor>(
+              type_,
+              /*dump_stack=*/base::RepeatingClosure()),
+          syncable_service.get());
+    }
+    return bridge_->change_processor()->GetControllerDelegate().get();
+  }
+
+  const ModelType type_;
+  OnceModelTypeStoreFactory store_factory_;
+  SyncableServiceProvider syncable_service_provider_;
+  std::unique_ptr<ModelTypeSyncBridge> bridge_;
+
+  DISALLOW_COPY_AND_ASSIGN(ControllerDelegate);
+};
+
+}  // namespace
+
+SyncableServiceBasedModelTypeController::
+    SyncableServiceBasedModelTypeController(
+        ModelType type,
+        OnceModelTypeStoreFactory store_factory,
+        SyncableServiceProvider syncable_service_provider)
+    : ModelTypeController(type,
+                          std::make_unique<ControllerDelegate>(
+                              type,
+                              std::move(store_factory),
+                              std::move(syncable_service_provider))) {}
+
+SyncableServiceBasedModelTypeController::
+    ~SyncableServiceBasedModelTypeController() {}
+
+}  // namespace syncer
diff --git a/components/sync/driver/syncable_service_based_model_type_controller.h b/components/sync/driver/syncable_service_based_model_type_controller.h
new file mode 100644
index 0000000..bdc38fd
--- /dev/null
+++ b/components/sync/driver/syncable_service_based_model_type_controller.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 COMPONENTS_SYNC_DRIVER_SYNCABLE_SERVICE_BASED_MODEL_TYPE_CONTROLLER_H_
+#define COMPONENTS_SYNC_DRIVER_SYNCABLE_SERVICE_BASED_MODEL_TYPE_CONTROLLER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/model_type_controller.h"
+#include "components/sync/model/model_type_store.h"
+
+namespace syncer {
+
+class SyncableService;
+
+// Controller responsible for integrating SyncableService implementations within
+// a non-blocking datatype (USS).
+class SyncableServiceBasedModelTypeController : public ModelTypeController {
+ public:
+  using SyncableServiceProvider =
+      base::OnceCallback<base::WeakPtr<syncer::SyncableService>()>;
+
+  SyncableServiceBasedModelTypeController(
+      ModelType type,
+      OnceModelTypeStoreFactory store_factory,
+      SyncableServiceProvider syncable_service_provider);
+  ~SyncableServiceBasedModelTypeController() override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SyncableServiceBasedModelTypeController);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_DRIVER_SYNCABLE_SERVICE_BASED_MODEL_TYPE_CONTROLLER_H_
diff --git a/components/sync/model/model_type_sync_bridge.h b/components/sync/model/model_type_sync_bridge.h
index 411fc44..a297134 100644
--- a/components/sync/model/model_type_sync_bridge.h
+++ b/components/sync/model/model_type_sync_bridge.h
@@ -108,10 +108,10 @@
   // Get or generate a client tag for |entity_data|. This must be the same tag
   // that was/would have been generated in the SyncableService/Directory world
   // for backward compatibility with pre-USS clients. The only time this
-  // theoretically needs to be called is on the creation of local data, however
-  // it is also used to verify the hash of remote data. If a model type was
-  // never launched pre-USS, then method does not need to be different from
-  // GetStorageKey(). Only the hash of this value is kept.
+  // theoretically needs to be called is on the creation of local data.
+  //
+  // If a model type was never launched pre-USS, then method does not need to be
+  // different from GetStorageKey(). Only the hash of this value is kept.
   virtual std::string GetClientTag(const EntityData& entity_data) = 0;
 
   // Must not be called unless SupportsGetStorageKey() returns true.
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index ccd425a1..e72601ba 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -1169,6 +1169,8 @@
   memory_usage += EstimateMemoryUsage(model_type_state_);
   memory_usage += EstimateMemoryUsage(entities_);
   memory_usage += EstimateMemoryUsage(storage_key_to_tag_hash_);
+  // TODO(crbug.com/870624): Let bridges provide custom additional memory
+  // overhead, which is important for SyncableServiceBasedBridge.
   return memory_usage;
 }
 
diff --git a/components/sync/model_impl/syncable_service_based_bridge.cc b/components/sync/model_impl/syncable_service_based_bridge.cc
new file mode 100644
index 0000000..49590a1
--- /dev/null
+++ b/components/sync/model_impl/syncable_service_based_bridge.cc
@@ -0,0 +1,593 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/model_impl/syncable_service_based_bridge.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "components/sync/base/hash_util.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/model/sync_change.h"
+#include "components/sync/model/sync_error_factory.h"
+#include "components/sync/model/syncable_service.h"
+#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+#include "components/sync/protocol/persisted_entity_data.pb.h"
+
+namespace syncer {
+namespace {
+
+// Same as kInvalidId in syncable/base_node.h.
+constexpr int64_t kInvalidNodeId = 0;
+
+std::unique_ptr<EntityData> ConvertPersistedToEntityData(
+    const std::string& client_tag_hash,
+    std::unique_ptr<sync_pb::PersistedEntityData> data) {
+  DCHECK(data);
+  DCHECK(!client_tag_hash.empty());
+  DCHECK(!data->non_unique_name().empty());
+  auto entity_data = std::make_unique<EntityData>();
+
+  entity_data->non_unique_name = std::move(*data->mutable_non_unique_name());
+  entity_data->specifics.Swap(data->mutable_specifics());
+  entity_data->client_tag_hash = client_tag_hash;
+  return entity_data;
+}
+
+sync_pb::PersistedEntityData CreatePersistedFromEntityData(
+    const EntityData& entity_data) {
+  DCHECK(!entity_data.non_unique_name.empty());
+
+  sync_pb::PersistedEntityData persisted;
+  persisted.set_non_unique_name(entity_data.non_unique_name);
+  *persisted.mutable_specifics() = entity_data.specifics;
+  return persisted;
+}
+
+std::unique_ptr<sync_pb::PersistedEntityData> CreatePersistedFromSyncData(
+    const SyncDataLocal& sync_data) {
+  DCHECK(!sync_data.GetTitle().empty());
+  auto persisted = std::make_unique<sync_pb::PersistedEntityData>();
+  persisted->set_non_unique_name(sync_data.GetTitle());
+  *persisted->mutable_specifics() = sync_data.GetSpecifics();
+  return persisted;
+}
+
+SyncChange::SyncChangeType ConvertToSyncChangeType(
+    EntityChange::ChangeType type) {
+  switch (type) {
+    case EntityChange::ACTION_DELETE:
+      return SyncChange::ACTION_DELETE;
+    case EntityChange::ACTION_ADD:
+      return SyncChange::ACTION_ADD;
+    case EntityChange::ACTION_UPDATE:
+      return SyncChange::ACTION_UPDATE;
+  }
+  NOTREACHED();
+  return SyncChange::ACTION_INVALID;
+}
+
+base::Optional<ModelError> ConvertToModelError(const SyncError& sync_error) {
+  if (sync_error.IsSet()) {
+    return ModelError(sync_error.location(), sync_error.message());
+  }
+  return base::nullopt;
+}
+
+// Object to propagate local changes to the bridge, which will ultimately
+// propagate them to the server.
+class ChangeProcessorImpl : public SyncChangeProcessor {
+ public:
+  ChangeProcessorImpl(
+      ModelType type,
+      const base::RepeatingCallback<void(const base::Optional<ModelError>&)>&
+          error_callback,
+      ModelTypeStore* store,
+      std::map<std::string, sync_pb::EntitySpecifics>* in_memory_store,
+      ModelTypeChangeProcessor* other)
+      : type_(type),
+        error_callback_(error_callback),
+        store_(store),
+        in_memory_store_(in_memory_store),
+        other_(other) {
+    DCHECK(store);
+    DCHECK(other);
+  }
+
+  ~ChangeProcessorImpl() override {}
+
+  SyncError ProcessSyncChanges(const base::Location& from_here,
+                               const SyncChangeList& change_list) override {
+    std::unique_ptr<ModelTypeStore::WriteBatch> batch =
+        store_->CreateWriteBatch();
+
+    for (const SyncChange& change : change_list) {
+      DCHECK(change.sync_data().IsLocal())
+          << " from " << change.location().ToString();
+      SyncDataLocal sync_data(change.sync_data());
+      DCHECK(sync_data.IsValid()) << " from " << change.location().ToString();
+      const std::string storage_key =
+          GenerateSyncableHash(type_, sync_data.GetTag());
+      DCHECK(!storage_key.empty());
+
+      switch (change.change_type()) {
+        case SyncChange::ACTION_INVALID:
+          NOTREACHED() << " from " << change.location().ToString();
+          break;
+        case SyncChange::ACTION_ADD:
+        case SyncChange::ACTION_UPDATE: {
+          (*in_memory_store_)[storage_key] = sync_data.GetSpecifics();
+          std::unique_ptr<sync_pb::PersistedEntityData> persisted_entity =
+              CreatePersistedFromSyncData(sync_data);
+          batch->WriteData(storage_key, persisted_entity->SerializeAsString());
+          other_->Put(
+              storage_key,
+              ConvertPersistedToEntityData(
+                  /*client_tag_hash=*/storage_key, std::move(persisted_entity)),
+              batch->GetMetadataChangeList());
+          break;
+        }
+        case SyncChange::ACTION_DELETE:
+          in_memory_store_->erase(storage_key);
+          other_->Delete(storage_key, batch->GetMetadataChangeList());
+          batch->DeleteData(storage_key);
+          break;
+      }
+    }
+
+    store_->CommitWriteBatch(std::move(batch), error_callback_);
+
+    return SyncError();
+  }
+
+  SyncDataList GetAllSyncData(ModelType type) const override {
+    // This function is not supported and not exercised by the relevant
+    // datatypes (that are integrated with this bridge).
+    NOTREACHED();
+    return SyncDataList();
+  }
+
+  SyncError UpdateDataTypeContext(ModelType type,
+                                  ContextRefreshStatus refresh_status,
+                                  const std::string& context) override {
+    // This function is not supported and not exercised by anyone, since
+    // the USS flow doesn't use SharedChangeProcessor.
+    // TODO(crbug.com/870624): Remove this function altogether when the
+    // directory codebase is removed.
+    NOTREACHED();
+    return SyncError();
+  }
+
+  void AddLocalChangeObserver(LocalChangeObserver* observer) override {
+    // This function is not supported and not exercised by the relevant
+    // datatypes (that are integrated with this bridge).
+    NOTREACHED();
+  }
+
+  void RemoveLocalChangeObserver(LocalChangeObserver* observer) override {
+    // This function is not supported and not exercised by the relevant
+    // datatypes (that are integrated with this bridge).
+    NOTREACHED();
+  }
+
+ private:
+  const ModelType type_;
+  const base::RepeatingCallback<void(const base::Optional<ModelError>&)>
+      error_callback_;
+  ModelTypeStore* const store_;
+  std::map<std::string, sync_pb::EntitySpecifics>* const in_memory_store_;
+  ModelTypeChangeProcessor* const other_;
+
+  DISALLOW_COPY_AND_ASSIGN(ChangeProcessorImpl);
+};
+
+class SyncErrorFactoryImpl : public SyncErrorFactory {
+ public:
+  explicit SyncErrorFactoryImpl(ModelType type) : type_(type) {}
+  ~SyncErrorFactoryImpl() override = default;
+
+  SyncError CreateAndUploadError(const base::Location& location,
+                                 const std::string& message) override {
+    // Uploading is not supported, we simply return the error.
+    return SyncError(location, SyncError::DATATYPE_ERROR, message, type_);
+  }
+
+ private:
+  const ModelType type_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncErrorFactoryImpl);
+};
+
+}  // namespace
+
+SyncableServiceBasedBridge::SyncableServiceBasedBridge(
+    ModelType type,
+    OnceModelTypeStoreFactory store_factory,
+    std::unique_ptr<ModelTypeChangeProcessor> change_processor,
+    SyncableService* syncable_service)
+    : ModelTypeSyncBridge(std::move(change_processor)),
+      type_(type),
+      syncable_service_(syncable_service),
+      store_factory_(std::move(store_factory)),
+      syncable_service_started_(false),
+      weak_ptr_factory_(this) {
+  DCHECK(store_factory_);
+  DCHECK(syncable_service_);
+}
+
+SyncableServiceBasedBridge::~SyncableServiceBasedBridge() {
+  // Stop the syncable service to make sure instances of ChangeProcessorImpl are
+  // not continued to be used.
+  if (syncable_service_started_) {
+    syncable_service_->StopSyncing(type_);
+  }
+}
+
+std::unique_ptr<MetadataChangeList>
+SyncableServiceBasedBridge::CreateMetadataChangeList() {
+  return ModelTypeStore::WriteBatch::CreateMetadataChangeList();
+}
+
+void SyncableServiceBasedBridge::OnSyncStarting(
+    const DataTypeActivationRequest& request) {
+  DCHECK(!syncable_service_started_);
+
+  if (!store_factory_) {
+    // Sync was have been started earlier, and |store_| is guaranteed to be
+    // initialized because stopping of the datatype cannot be completed before
+    // ModelReadyToSync().
+    DCHECK(store_);
+    MaybeStartSyncableService();
+    return;
+  }
+
+  std::move(store_factory_)
+      .Run(type_, base::BindOnce(&SyncableServiceBasedBridge::OnStoreCreated,
+                                 weak_ptr_factory_.GetWeakPtr()));
+  DCHECK(!store_factory_);
+}
+
+base::Optional<ModelError> SyncableServiceBasedBridge::MergeSyncData(
+    std::unique_ptr<MetadataChangeList> metadata_change_list,
+    EntityChangeList entity_change_list) {
+  DCHECK(store_);
+  DCHECK(change_processor()->IsTrackingMetadata());
+  DCHECK(!syncable_service_started_);
+  DCHECK(in_memory_store_.empty());
+
+  const SyncChangeList sync_change_list = StoreAndConvertRemoteChanges(
+      std::move(metadata_change_list), std::move(entity_change_list));
+
+  SyncDataList initial_sync_data;
+  initial_sync_data.reserve(sync_change_list.size());
+  for (const SyncChange& change : sync_change_list) {
+    initial_sync_data.push_back(change.sync_data());
+  }
+
+  auto error_callback =
+      base::BindRepeating(&SyncableServiceBasedBridge::ReportErrorIfSet,
+                          weak_ptr_factory_.GetWeakPtr());
+  auto processor_impl = std::make_unique<ChangeProcessorImpl>(
+      type_, error_callback, store_.get(), &in_memory_store_,
+      change_processor());
+
+  const base::Optional<ModelError> merge_error = ConvertToModelError(
+      syncable_service_
+          ->MergeDataAndStartSyncing(
+              type_, initial_sync_data, std::move(processor_impl),
+              std::make_unique<SyncErrorFactoryImpl>(type_))
+          .error());
+
+  if (!merge_error) {
+    syncable_service_started_ = true;
+  }
+
+  return merge_error;
+}
+
+base::Optional<ModelError> SyncableServiceBasedBridge::ApplySyncChanges(
+    std::unique_ptr<MetadataChangeList> metadata_change_list,
+    EntityChangeList entity_change_list) {
+  DCHECK(store_);
+  DCHECK(change_processor()->IsTrackingMetadata());
+  DCHECK(syncable_service_started_);
+
+  const SyncChangeList sync_change_list = StoreAndConvertRemoteChanges(
+      std::move(metadata_change_list), std::move(entity_change_list));
+
+  if (sync_change_list.empty()) {
+    return base::nullopt;
+  }
+
+  return ConvertToModelError(
+      syncable_service_->ProcessSyncChanges(FROM_HERE, sync_change_list));
+}
+
+void SyncableServiceBasedBridge::GetData(StorageKeyList storage_keys,
+                                         DataCallback callback) {
+  DCHECK(store_);
+  store_->ReadData(
+      storage_keys,
+      base::BindOnce(&SyncableServiceBasedBridge::OnReadDataForProcessor,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void SyncableServiceBasedBridge::GetAllDataForDebugging(DataCallback callback) {
+  DCHECK(store_);
+  store_->ReadAllData(
+      base::BindOnce(&SyncableServiceBasedBridge::OnReadAllDataForProcessor,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+std::string SyncableServiceBasedBridge::GetClientTag(
+    const EntityData& entity_data) {
+  NOTREACHED();
+  return std::string();
+}
+
+std::string SyncableServiceBasedBridge::GetStorageKey(
+    const EntityData& entity_data) {
+  // Not supported as per SupportsGetStorageKey().
+  NOTREACHED();
+  return std::string();
+}
+
+bool SyncableServiceBasedBridge::SupportsGetClientTag() const {
+  return false;
+}
+
+bool SyncableServiceBasedBridge::SupportsGetStorageKey() const {
+  return false;
+}
+
+ConflictResolution SyncableServiceBasedBridge::ResolveConflict(
+    const EntityData& local_data,
+    const EntityData& remote_data) const {
+  if (!remote_data.is_deleted()) {
+    return ConflictResolution::UseRemote();
+  }
+
+  DCHECK(!local_data.is_deleted());
+
+  // Ignore local changes for extensions/apps when server had a delete, to
+  // avoid unwanted reinstall of an uninstalled extension.
+  if (type_ == EXTENSIONS || type_ == APPS) {
+    DVLOG(1) << "Resolving conflict, ignoring local changes for extension/app";
+    return ConflictResolution::UseRemote();
+  }
+
+  return ConflictResolution::UseLocal();
+}
+
+ModelTypeSyncBridge::StopSyncResponse
+SyncableServiceBasedBridge::ApplyStopSyncChanges(
+    std::unique_ptr<MetadataChangeList> delete_metadata_change_list) {
+  DCHECK(store_);
+
+  if (delete_metadata_change_list) {
+    in_memory_store_.clear();
+    store_->DeleteAllDataAndMetadata(base::DoNothing());
+  }
+
+  if (syncable_service_started_) {
+    syncable_service_->StopSyncing(type_);
+    syncable_service_started_ = false;
+  }
+
+  return StopSyncResponse::kModelStillReadyToSync;
+}
+
+void SyncableServiceBasedBridge::OnStoreCreated(
+    const base::Optional<ModelError>& error,
+    std::unique_ptr<ModelTypeStore> store) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  DCHECK(store);
+  store_ = std::move(store);
+
+  store_->ReadAllData(
+      base::BindOnce(&SyncableServiceBasedBridge::OnReadAllDataForInit,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SyncableServiceBasedBridge::OnReadAllDataForInit(
+    const base::Optional<ModelError>& error,
+    std::unique_ptr<ModelTypeStore::RecordList> record_list) {
+  DCHECK(in_memory_store_.empty());
+
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  for (const ModelTypeStore::Record& record : *record_list) {
+    sync_pb::PersistedEntityData persisted_entity;
+    if (!persisted_entity.ParseFromString(record.value)) {
+      change_processor()->ReportError(
+          {FROM_HERE, "Failed deserializing data."});
+      return;
+    }
+
+    in_memory_store_[record.id] = persisted_entity.specifics();
+  }
+
+  store_->ReadAllMetadata(
+      base::BindOnce(&SyncableServiceBasedBridge::OnReadAllMetadataForInit,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void SyncableServiceBasedBridge::OnReadAllMetadataForInit(
+    const base::Optional<ModelError>& error,
+    std::unique_ptr<MetadataBatch> metadata_batch) {
+  DCHECK(!syncable_service_started_);
+
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  change_processor()->ModelReadyToSync(std::move(metadata_batch));
+  MaybeStartSyncableService();
+}
+
+void SyncableServiceBasedBridge::MaybeStartSyncableService() {
+  DCHECK(!syncable_service_started_);
+  DCHECK(store_);
+
+  // If sync wasn't enabled according to the loaded metadata, let's wait until
+  // MergeSyncData() is called before starting the SyncableService.
+  if (!change_processor()->IsTrackingMetadata()) {
+    return;
+  }
+
+  // Sync enabled, so exercise MergeDataAndStartSyncing() immediately, since
+  // this function is reached only if sync is starting already.
+  SyncDataList initial_sync_data;
+  initial_sync_data.reserve(in_memory_store_.size());
+  for (const std::pair<std::string, sync_pb::EntitySpecifics>& record :
+       in_memory_store_) {
+    initial_sync_data.push_back(SyncData::CreateRemoteData(
+        /*id=*/kInvalidNodeId, record.second,
+        /*last_modified_time=*/base::Time(),  // Used by legacy sessions only.
+        /*client_tag_hash=*/record.first));
+  }
+
+  auto error_callback =
+      base::BindRepeating(&SyncableServiceBasedBridge::ReportErrorIfSet,
+                          weak_ptr_factory_.GetWeakPtr());
+  auto processor_impl = std::make_unique<ChangeProcessorImpl>(
+      type_, error_callback, store_.get(), &in_memory_store_,
+      change_processor());
+
+  const base::Optional<ModelError> merge_error = ConvertToModelError(
+      syncable_service_
+          ->MergeDataAndStartSyncing(
+              type_, initial_sync_data, std::move(processor_impl),
+              std::make_unique<SyncErrorFactoryImpl>(type_))
+          .error());
+
+  if (merge_error) {
+    change_processor()->ReportError(*merge_error);
+  } else {
+    syncable_service_started_ = true;
+  }
+}
+
+SyncChangeList SyncableServiceBasedBridge::StoreAndConvertRemoteChanges(
+    std::unique_ptr<MetadataChangeList> metadata_change_list,
+    EntityChangeList entity_change_list) {
+  std::unique_ptr<ModelTypeStore::WriteBatch> batch =
+      store_->CreateWriteBatch();
+
+  SyncChangeList output_change_list;
+  output_change_list.reserve(entity_change_list.size());
+
+  for (const EntityChange& change : entity_change_list) {
+    switch (change.type()) {
+      case EntityChange::ACTION_DELETE: {
+        const std::string& storage_key = change.storage_key();
+        DCHECK_NE(0U, in_memory_store_.count(storage_key));
+        DVLOG(1) << ModelTypeToString(type_)
+                 << ": Processing deletion with storage key: " << storage_key;
+
+        output_change_list.emplace_back(
+            FROM_HERE, SyncChange::ACTION_DELETE,
+            SyncData::CreateRemoteData(
+                /*id=*/kInvalidNodeId, in_memory_store_[storage_key],
+                change.data().modification_time,
+                change.data().client_tag_hash));
+
+        // For tombstones, there is no actual data, which means no client tag
+        // hash either, but the processor provides the storage key.
+        DCHECK(!storage_key.empty());
+        batch->DeleteData(storage_key);
+        in_memory_store_.erase(storage_key);
+        break;
+      }
+
+      case EntityChange::ACTION_ADD:
+        // Because we use the client tag hash as storage key, let the processor
+        // know.
+        change_processor()->UpdateStorageKey(
+            change.data(), /*storage_key=*/change.data().client_tag_hash,
+            metadata_change_list.get());
+        FALLTHROUGH;
+
+      case EntityChange::ACTION_UPDATE: {
+        const std::string& storage_key = change.data().client_tag_hash;
+        DVLOG(1) << ModelTypeToString(type_)
+                 << ": Processing add/update with key: " << storage_key;
+
+        output_change_list.emplace_back(
+            FROM_HERE, ConvertToSyncChangeType(change.type()),
+            SyncData::CreateRemoteData(
+                /*id=*/kInvalidNodeId, change.data().specifics,
+                change.data().modification_time,
+                change.data().client_tag_hash));
+
+        batch->WriteData(
+            storage_key,
+            CreatePersistedFromEntityData(change.data()).SerializeAsString());
+        in_memory_store_[storage_key] = change.data().specifics;
+        break;
+      }
+    }
+  }
+
+  store_->CommitWriteBatch(
+      std::move(batch),
+      base::BindOnce(&SyncableServiceBasedBridge::ReportErrorIfSet,
+                     weak_ptr_factory_.GetWeakPtr()));
+
+  return output_change_list;
+}
+
+void SyncableServiceBasedBridge::OnReadDataForProcessor(
+    DataCallback callback,
+    const base::Optional<ModelError>& error,
+    std::unique_ptr<ModelTypeStore::RecordList> record_list,
+    std::unique_ptr<ModelTypeStore::IdList> missing_id_list) {
+  OnReadAllDataForProcessor(std::move(callback), error, std::move(record_list));
+}
+
+void SyncableServiceBasedBridge::OnReadAllDataForProcessor(
+    DataCallback callback,
+    const base::Optional<ModelError>& error,
+    std::unique_ptr<ModelTypeStore::RecordList> record_list) {
+  if (error) {
+    change_processor()->ReportError(*error);
+    return;
+  }
+
+  auto batch = std::make_unique<MutableDataBatch>();
+  for (const ModelTypeStore::Record& record : *record_list) {
+    auto persisted_entity = std::make_unique<sync_pb::PersistedEntityData>();
+    if (record.id.empty() || !persisted_entity->ParseFromString(record.value)) {
+      change_processor()->ReportError(
+          {FROM_HERE, "Failed deserializing data."});
+      return;
+    }
+
+    // Note that client tag hash is used as storage key too.
+    batch->Put(record.id,
+               ConvertPersistedToEntityData(
+                   /*client_tag_hash=*/record.id, std::move(persisted_entity)));
+  }
+  std::move(callback).Run(std::move(batch));
+}
+
+void SyncableServiceBasedBridge::ReportErrorIfSet(
+    const base::Optional<ModelError>& error) {
+  if (error) {
+    change_processor()->ReportError(*error);
+  }
+}
+
+}  // namespace syncer
diff --git a/components/sync/model_impl/syncable_service_based_bridge.h b/components/sync/model_impl/syncable_service_based_bridge.h
new file mode 100644
index 0000000..938d809
--- /dev/null
+++ b/components/sync/model_impl/syncable_service_based_bridge.h
@@ -0,0 +1,103 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_MODEL_IMPL_SYNCABLE_SERVICE_BASED_BRIDGE_H_
+#define COMPONENTS_SYNC_MODEL_IMPL_SYNCABLE_SERVICE_BASED_BRIDGE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_store.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+#include "components/sync/model/sync_change_processor.h"
+
+namespace syncer {
+
+class MetadataBatch;
+class ModelTypeChangeProcessor;
+class SyncableService;
+
+// Implementation of ModelTypeSyncBridge that allows integrating legacy
+// datatypes that implement SyncableService. Internally, it uses a database to
+// persist and mimic the legacy directory's behavior, but as opposed to the
+// legacy directory, it's not exposed anywhere outside this bridge, and is
+// considered an implementation detail.
+class SyncableServiceBasedBridge : public ModelTypeSyncBridge {
+ public:
+  // Pointers must not be null and |syncable_service| must outlive this object.
+  SyncableServiceBasedBridge(
+      ModelType type,
+      OnceModelTypeStoreFactory store_factory,
+      std::unique_ptr<ModelTypeChangeProcessor> change_processor,
+      SyncableService* syncable_service);
+  ~SyncableServiceBasedBridge() override;
+
+  // ModelTypeSyncBridge implementation.
+  void OnSyncStarting(const DataTypeActivationRequest& request) override;
+  std::unique_ptr<MetadataChangeList> CreateMetadataChangeList() override;
+  base::Optional<ModelError> MergeSyncData(
+      std::unique_ptr<MetadataChangeList> metadata_change_list,
+      EntityChangeList entity_change_list) override;
+  base::Optional<ModelError> ApplySyncChanges(
+      std::unique_ptr<MetadataChangeList> metadata_change_list,
+      EntityChangeList entity_change_list) override;
+  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetAllDataForDebugging(DataCallback callback) override;
+  std::string GetClientTag(const EntityData& entity_data) override;
+  std::string GetStorageKey(const EntityData& entity_data) override;
+  bool SupportsGetClientTag() const override;
+  bool SupportsGetStorageKey() const override;
+  ConflictResolution ResolveConflict(
+      const EntityData& local_data,
+      const EntityData& remote_data) const override;
+  StopSyncResponse ApplyStopSyncChanges(
+      std::unique_ptr<MetadataChangeList> delete_metadata_change_list) override;
+
+ private:
+  void OnStoreCreated(const base::Optional<ModelError>& error,
+                      std::unique_ptr<ModelTypeStore> store);
+  void OnReadAllDataForInit(
+      const base::Optional<ModelError>& error,
+      std::unique_ptr<ModelTypeStore::RecordList> record_list);
+  void OnReadAllMetadataForInit(const base::Optional<ModelError>& error,
+                                std::unique_ptr<MetadataBatch> metadata_batch);
+  void MaybeStartSyncableService();
+  SyncChangeList StoreAndConvertRemoteChanges(
+      std::unique_ptr<MetadataChangeList> metadata_change_list,
+      EntityChangeList entity_change_list);
+  void OnReadDataForProcessor(
+      DataCallback callback,
+      const base::Optional<ModelError>& error,
+      std::unique_ptr<ModelTypeStore::RecordList> record_list,
+      std::unique_ptr<ModelTypeStore::IdList> missing_id_list);
+  void OnReadAllDataForProcessor(
+      DataCallback callback,
+      const base::Optional<ModelError>& error,
+      std::unique_ptr<ModelTypeStore::RecordList> record_list);
+  void ReportErrorIfSet(const base::Optional<ModelError>& error);
+
+  const ModelType type_;
+  SyncableService* const syncable_service_;
+  OnceModelTypeStoreFactory store_factory_;
+
+  std::unique_ptr<ModelTypeStore> store_;
+  bool syncable_service_started_;
+
+  // In-memory copy of |store_|, needed for remote deletions, because we need to
+  // provide specifics of the deleted entity to the SyncableService.
+  std::map<std::string, sync_pb::EntitySpecifics> in_memory_store_;
+
+  base::WeakPtrFactory<SyncableServiceBasedBridge> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(SyncableServiceBasedBridge);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_MODEL_IMPL_SYNCABLE_SERVICE_BASED_BRIDGE_H_
diff --git a/components/sync/model_impl/syncable_service_based_bridge_unittest.cc b/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
new file mode 100644
index 0000000..4682028
--- /dev/null
+++ b/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
@@ -0,0 +1,432 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/model_impl/syncable_service_based_bridge.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/mock_callback.h"
+#include "components/sync/base/hash_util.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/model/model_type_store_test_util.h"
+#include "components/sync/model/sync_change.h"
+#include "components/sync/model/sync_error_factory.h"
+#include "components/sync/model/sync_merge_result.h"
+#include "components/sync/model/syncable_service.h"
+#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+#include "components/sync/protocol/sync.pb.h"
+#include "components/sync/test/engine/mock_model_type_worker.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+namespace {
+
+using testing::DoAll;
+using testing::ElementsAre;
+using testing::IsEmpty;
+using testing::NotNull;
+using testing::Pair;
+using testing::Return;
+using testing::SaveArg;
+using testing::_;
+
+const ModelType kModelType = PREFERENCES;
+
+sync_pb::EntitySpecifics GetTestSpecifics(const std::string& name = "name") {
+  sync_pb::EntitySpecifics specifics;
+  // Make specifics non empty, to avoid it being interpreted as a tombstone.
+  specifics.mutable_preference()->set_name(name);
+  return specifics;
+}
+
+MATCHER_P(SyncDataRemoteMatches, name, "") {
+  return arg.IsValid() && !arg.IsLocal() && arg.GetDataType() == kModelType &&
+         arg.GetSpecifics().preference().name() == name;
+}
+
+MATCHER_P2(SyncChangeMatches, change_type, name, "") {
+  return arg.IsValid() && change_type == arg.change_type() &&
+         arg.sync_data().GetDataType() == kModelType &&
+         arg.sync_data().GetSpecifics().preference().name() == name;
+}
+
+MATCHER_P(HasName, name, "") {
+  return arg && arg->specifics.preference().name() == name;
+}
+
+class MockSyncableService : public SyncableService {
+ public:
+  MOCK_METHOD4(
+      MergeDataAndStartSyncing,
+      SyncMergeResult(ModelType type,
+                      const SyncDataList& initial_sync_data,
+                      std::unique_ptr<SyncChangeProcessor> sync_processor,
+                      std::unique_ptr<SyncErrorFactory> sync_error_factory));
+  MOCK_METHOD1(StopSyncing, void(ModelType type));
+  MOCK_METHOD2(ProcessSyncChanges,
+               SyncError(const base::Location& from_here,
+                         const SyncChangeList& change_list));
+  MOCK_CONST_METHOD1(GetAllSyncData, SyncDataList(ModelType type));
+};
+
+class SyncableServiceBasedBridgeTest : public ::testing::Test {
+ protected:
+  SyncableServiceBasedBridgeTest() {
+    ON_CALL(syncable_service_, MergeDataAndStartSyncing(_, _, _, _))
+        .WillByDefault(
+            [&](ModelType type, const SyncDataList& initial_sync_data,
+                std::unique_ptr<SyncChangeProcessor> sync_processor,
+                std::unique_ptr<SyncErrorFactory> sync_error_factory) {
+              start_syncing_sync_processor_ = std::move(sync_processor);
+              return SyncMergeResult(kModelType);
+            });
+  }
+
+  ~SyncableServiceBasedBridgeTest() override {}
+
+  void InitializeBridge() {
+    real_processor_ =
+        std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+            kModelType, /*dump_stack=*/base::DoNothing(),
+            /*commit_only=*/false);
+    mock_processor_.DelegateCallsByDefaultTo(real_processor_.get());
+    bridge_ = std::make_unique<SyncableServiceBasedBridge>(
+        kModelType, ModelTypeStoreTestUtil::FactoryForInMemoryStoreForTest(),
+        mock_processor_.CreateForwardingProcessor(), &syncable_service_);
+  }
+
+  void ShutdownBridge() {
+    bridge_.reset();
+    // The mock is still delegating to |real_processor_|, so we reset it too.
+    ASSERT_TRUE(testing::Mock::VerifyAndClear(&mock_processor_));
+    real_processor_.reset();
+  }
+
+  void StartSyncing() {
+    syncer::DataTypeActivationRequest request;
+    request.error_handler = mock_error_handler_.Get();
+    request.cache_guid = "TestCacheGuid";
+    request.authenticated_account_id = "SomeAccountId";
+
+    base::RunLoop loop;
+    real_processor_->OnSyncStarting(
+        request,
+        base::BindLambdaForTesting(
+            [&](std::unique_ptr<syncer::DataTypeActivationResponse> response) {
+              worker_ = std::make_unique<MockModelTypeWorker>(
+                  response->model_type_state, real_processor_.get());
+              loop.Quit();
+            }));
+    loop.Run();
+  }
+
+  std::map<std::string, std::unique_ptr<EntityData>> GetAllData() {
+    base::RunLoop loop;
+    std::unique_ptr<DataBatch> batch;
+    bridge_->GetAllDataForDebugging(base::BindLambdaForTesting(
+        [&loop, &batch](std::unique_ptr<DataBatch> input_batch) {
+          batch = std::move(input_batch);
+          loop.Quit();
+        }));
+    loop.Run();
+    EXPECT_NE(nullptr, batch);
+
+    std::map<std::string, std::unique_ptr<EntityData>> storage_key_to_data;
+    while (batch && batch->HasNext()) {
+      storage_key_to_data.insert(batch->Next());
+    }
+    return storage_key_to_data;
+  }
+
+  const std::string kClientTag = "clienttag";
+  const std::string kClientTagHash =
+      GenerateSyncableHash(kModelType, kClientTag);
+
+  base::MessageLoop message_loop_;
+  testing::NiceMock<MockSyncableService> syncable_service_;
+  testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_;
+  base::MockCallback<ModelErrorHandler> mock_error_handler_;
+  std::unique_ptr<syncer::ClientTagBasedModelTypeProcessor> real_processor_;
+  std::unique_ptr<SyncableServiceBasedBridge> bridge_;
+  std::unique_ptr<MockModelTypeWorker> worker_;
+  // SyncChangeProcessor received via MergeDataAndStartSyncing(), or null if it
+  // hasn't been called.
+  std::unique_ptr<SyncChangeProcessor> start_syncing_sync_processor_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SyncableServiceBasedBridgeTest);
+};
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldStartSyncingWithEmptyInitialRemoteData) {
+  // Bridge initialization alone, without sync itself starting, should not
+  // issue calls to the syncable service.
+  EXPECT_CALL(syncable_service_, MergeDataAndStartSyncing(_, _, _, _)).Times(0);
+  InitializeBridge();
+
+  // Starting sync itself is also not sufficient, until initial remote data is
+  // received.
+  StartSyncing();
+
+  // Once the initial data is fetched from the server,
+  // MergeDataAndStartSyncing() should be exercised.
+  EXPECT_CALL(
+      syncable_service_,
+      MergeDataAndStartSyncing(kModelType, IsEmpty(), NotNull(), NotNull()));
+  worker_->UpdateFromServer();
+  EXPECT_THAT(GetAllData(), IsEmpty());
+}
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldStartSyncingWithNonEmptyInitialRemoteData) {
+  InitializeBridge();
+  StartSyncing();
+
+  // Once the initial data is fetched from the server,
+  // MergeDataAndStartSyncing() should be exercised.
+  EXPECT_CALL(syncable_service_,
+              MergeDataAndStartSyncing(
+                  kModelType, ElementsAre(SyncDataRemoteMatches("name1")),
+                  NotNull(), NotNull()));
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+}
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldStopSyncableServiceIfPreviouslyStarted) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer();
+
+  EXPECT_CALL(syncable_service_, StopSyncing(kModelType));
+  real_processor_->OnSyncStopping(KEEP_METADATA);
+
+  EXPECT_CALL(syncable_service_, StopSyncing(_)).Times(0);
+  ShutdownBridge();
+}
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldStopSyncableServiceDuringShutdownIfPreviouslyStarted) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer();
+
+  EXPECT_CALL(syncable_service_, StopSyncing(kModelType));
+  ShutdownBridge();
+}
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldNotStopSyncableServiceIfNotPreviouslyStarted) {
+  EXPECT_CALL(syncable_service_, StopSyncing(_)).Times(0);
+  InitializeBridge();
+  StartSyncing();
+  real_processor_->OnSyncStopping(KEEP_METADATA);
+}
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldNotStopSyncableServiceDuringShutdownIfNotPreviouslyStarted) {
+  EXPECT_CALL(syncable_service_, StopSyncing(_)).Times(0);
+  InitializeBridge();
+  StartSyncing();
+  ShutdownBridge();
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateErrorDuringStart) {
+  // Instrument MergeDataAndStartSyncing() to return an error.
+  SyncMergeResult merge_result(kModelType);
+  merge_result.set_error(SyncError(FROM_HERE, SyncError::PERSISTENCE_ERROR,
+                                   "Test error", kModelType));
+  ON_CALL(syncable_service_, MergeDataAndStartSyncing(_, _, _, _))
+      .WillByDefault(Return(merge_result));
+
+  EXPECT_CALL(mock_error_handler_, Run(_));
+
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer();
+
+  // Since the syncable service failed to start, it shouldn't be stopped.
+  EXPECT_CALL(syncable_service_, StopSyncing(_)).Times(0);
+  ShutdownBridge();
+}
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldStartSyncingWithPreviousDirectoryData) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
+  real_processor_->OnSyncStopping(KEEP_METADATA);
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+
+  EXPECT_CALL(syncable_service_,
+              MergeDataAndStartSyncing(
+                  kModelType, ElementsAre(SyncDataRemoteMatches("name1")),
+                  NotNull(), NotNull()));
+  StartSyncing();
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldSupportDisableReenableSequence) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics());
+  real_processor_->OnSyncStopping(CLEAR_METADATA);
+  EXPECT_THAT(GetAllData(), IsEmpty());
+
+  EXPECT_CALL(syncable_service_, MergeDataAndStartSyncing(_, _, _, _)).Times(0);
+  StartSyncing();
+  EXPECT_CALL(
+      syncable_service_,
+      MergeDataAndStartSyncing(kModelType, IsEmpty(), NotNull(), NotNull()));
+  worker_->UpdateFromServer();
+}
+
+TEST_F(SyncableServiceBasedBridgeTest,
+       ShouldPropagateLocalEntitiesDuringMerge) {
+  ON_CALL(syncable_service_, MergeDataAndStartSyncing(_, _, _, _))
+      .WillByDefault([&](ModelType type, const SyncDataList& initial_sync_data,
+                         std::unique_ptr<SyncChangeProcessor> sync_processor,
+                         std::unique_ptr<SyncErrorFactory> sync_error_factory) {
+        SyncChangeList change_list;
+        change_list.emplace_back(
+            FROM_HERE, SyncChange::ACTION_ADD,
+            SyncData::CreateLocalData(kClientTag, "title", GetTestSpecifics()));
+        const SyncError error =
+            sync_processor->ProcessSyncChanges(FROM_HERE, change_list);
+        EXPECT_FALSE(error.IsSet());
+        return SyncMergeResult(kModelType);
+      });
+
+  InitializeBridge();
+  StartSyncing();
+
+  EXPECT_CALL(mock_processor_, Put(kClientTagHash, NotNull(), NotNull()));
+  worker_->UpdateFromServer();
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateLocalCreation) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer();
+  ASSERT_THAT(start_syncing_sync_processor_, NotNull());
+  ASSERT_THAT(GetAllData(), IsEmpty());
+
+  EXPECT_CALL(mock_processor_, Put(kClientTagHash, NotNull(), NotNull()));
+
+  SyncChangeList change_list;
+  change_list.emplace_back(
+      FROM_HERE, SyncChange::ACTION_ADD,
+      SyncData::CreateLocalData(kClientTag, "title", GetTestSpecifics()));
+  const SyncError error =
+      start_syncing_sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
+  EXPECT_FALSE(error.IsSet());
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateLocalUpdate) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
+  ASSERT_THAT(start_syncing_sync_processor_, NotNull());
+  ASSERT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+
+  EXPECT_CALL(mock_processor_, Put(GenerateSyncableHash(kModelType, kClientTag),
+                                   NotNull(), NotNull()));
+
+  SyncChangeList change_list;
+  change_list.emplace_back(FROM_HERE, SyncChange::ACTION_UPDATE,
+                           SyncData::CreateLocalData(
+                               kClientTag, "title", GetTestSpecifics("name2")));
+  const SyncError error =
+      start_syncing_sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
+  EXPECT_FALSE(error.IsSet());
+  EXPECT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name2"))));
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateLocalDeletion) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
+  ASSERT_THAT(start_syncing_sync_processor_, NotNull());
+  ASSERT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+
+  EXPECT_CALL(mock_processor_,
+              Delete(GenerateSyncableHash(kModelType, kClientTag), NotNull()));
+
+  SyncChangeList change_list;
+  change_list.emplace_back(FROM_HERE, SyncChange::ACTION_DELETE,
+                           SyncData::CreateLocalDelete(kClientTag, kModelType));
+
+  const SyncError error =
+      start_syncing_sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
+  EXPECT_FALSE(error.IsSet());
+  EXPECT_THAT(GetAllData(), IsEmpty());
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateRemoteCreation) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer();
+  ASSERT_THAT(start_syncing_sync_processor_, NotNull());
+  ASSERT_THAT(GetAllData(), IsEmpty());
+
+  EXPECT_CALL(syncable_service_,
+              ProcessSyncChanges(_, ElementsAre(SyncChangeMatches(
+                                        SyncChange::ACTION_ADD, "name1"))));
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
+  EXPECT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateRemoteUpdates) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
+  ASSERT_THAT(start_syncing_sync_processor_, NotNull());
+  ASSERT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+
+  EXPECT_CALL(syncable_service_,
+              ProcessSyncChanges(_, ElementsAre(SyncChangeMatches(
+                                        SyncChange::ACTION_UPDATE, "name2"))));
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name2"));
+  EXPECT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name2"))));
+
+  // A second update for the same entity.
+  EXPECT_CALL(syncable_service_,
+              ProcessSyncChanges(_, ElementsAre(SyncChangeMatches(
+                                        SyncChange::ACTION_UPDATE, "name3"))));
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name3"));
+  EXPECT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name3"))));
+}
+
+TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateRemoteDeletion) {
+  InitializeBridge();
+  StartSyncing();
+  worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
+  ASSERT_THAT(start_syncing_sync_processor_, NotNull());
+  ASSERT_THAT(GetAllData(),
+              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+
+  EXPECT_CALL(syncable_service_,
+              ProcessSyncChanges(_, ElementsAre(SyncChangeMatches(
+                                        SyncChange::ACTION_DELETE, "name1"))));
+  worker_->TombstoneFromServer(kClientTagHash);
+  EXPECT_THAT(GetAllData(), IsEmpty());
+}
+
+}  // namespace
+}  // namespace syncer
diff --git a/components/sync/protocol/persisted_entity_data.proto b/components/sync/protocol/persisted_entity_data.proto
new file mode 100644
index 0000000..7661b29
--- /dev/null
+++ b/components/sync/protocol/persisted_entity_data.proto
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package sync_pb;
+
+import "sync.proto";
+
+// Sync proto to store entity data similar to what directory stores, used to
+// persist data locally and never sent through the wire.
+//
+// Because it's conceptually similar to SyncEntity (actual protocol) and it's
+// unclear how big this'll grow, we've kept compatibility with SyncEntity by
+// using the same field numbers.
+message PersistedEntityData {
+  // See corresponding fields in SyncEntity for details.
+  optional string non_unique_name = 8;
+  optional EntitySpecifics specifics = 21;
+}
diff --git a/components/sync/protocol/protocol_sources.gni b/components/sync/protocol/protocol_sources.gni
index 2b1b740..2e3c322 100644
--- a/components/sync/protocol/protocol_sources.gni
+++ b/components/sync/protocol/protocol_sources.gni
@@ -37,6 +37,7 @@
   "mountain_share_specifics",
   "nigori_specifics",
   "password_specifics",
+  "persisted_entity_data",
   "preference_specifics",
   "printer_specifics",
   "priority_preference_specifics",
diff --git a/components/variations/variations_crash_keys.cc b/components/variations/variations_crash_keys.cc
index 91c477c..d43be0ef 100644
--- a/components/variations/variations_crash_keys.cc
+++ b/components/variations/variations_crash_keys.cc
@@ -19,10 +19,10 @@
 
 namespace {
 
-// Size of the "num-experiments" crash key in bytes. 2048 bytes should be able
-// to hold about 113 entries, given each entry is 18 bytes long (due to being
+// Size of the "num-experiments" crash key in bytes. 4096 bytes should be able
+// to hold about 227 entries, given each entry is 18 bytes long (due to being
 // of the form "8e7abfb0-c16397b7,").
-constexpr size_t kVariationsKeySize = 2048;
+constexpr size_t kVariationsKeySize = 4096;
 
 // Crash key reporting the number of experiments. 8 is the size of the crash key
 // in bytes, which is used to hold an int as a string.
diff --git a/components/viz/service/display/gl_renderer.h b/components/viz/service/display/gl_renderer.h
index ac15162..11d1664 100644
--- a/components/viz/service/display/gl_renderer.h
+++ b/components/viz/service/display/gl_renderer.h
@@ -76,23 +76,7 @@
  protected:
   void DidChangeVisibility() override;
 
-  const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; }
-  const StaticGeometryBinding* SharedGeometry() const {
-    return shared_geometry_.get();
-  }
-
-  // Returns the format to use for storage if copying from the current
-  // framebuffer. If the root renderpass is current, it uses the best matching
-  // format from the OutputSurface, otherwise it uses the best matching format
-  // from the texture being drawn to as the backbuffer.
-  GLenum GetFramebufferCopyTextureFormat();
-  void ReleaseRenderPassTextures();
-  enum BoundGeometry { NO_BINDING, SHARED_BINDING, CLIPPED_BINDING };
-  void PrepareGeometry(BoundGeometry geometry_to_bind);
-  void SetStencilEnabled(bool enabled);
   bool stencil_enabled() const { return stencil_shadow_; }
-  void SetBlendEnabled(bool enabled);
-  bool blend_enabled() const { return blend_shadow_; }
 
   bool CanPartialSwap() override;
   void UpdateRenderPassTextures(
@@ -169,6 +153,18 @@
 
   struct DrawRenderPassDrawQuadParams;
 
+  // Returns the format to use for storage if copying from the current
+  // framebuffer. If the root renderpass is current, it uses the best matching
+  // format from the OutputSurface, otherwise it uses the best matching format
+  // from the texture being drawn to as the backbuffer.
+  GLenum GetFramebufferCopyTextureFormat();
+  void ReleaseRenderPassTextures();
+  enum BoundGeometry { NO_BINDING, SHARED_BINDING, CLIPPED_BINDING };
+  void PrepareGeometry(BoundGeometry geometry_to_bind);
+  void SetStencilEnabled(bool enabled);
+  void SetBlendEnabled(bool enabled);
+  bool blend_enabled() const { return blend_shadow_; }
+
   // If any of the following functions returns false, then it means that drawing
   // is not possible.
   bool InitializeRPDQParameters(DrawRenderPassDrawQuadParams* params);
@@ -259,6 +255,11 @@
                               gfx::QuadF* local_quad,
                               const gfx::Rect& tile_rect);
 
+  const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; }
+  const StaticGeometryBinding* SharedGeometry() const {
+    return shared_geometry_.get();
+  }
+
   // If |dst_color_space| is invalid, then no color conversion (apart from
   // YUV to RGB conversion) is performed. This explicit argument is available
   // so that video color conversion can be enabled separately from general color
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 587c10f1..cf8c28f 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -182,6 +182,7 @@
                            SkiaOutputSurface* skia_output_surface)
     : DirectRenderer(settings, output_surface, resource_provider),
       skia_output_surface_(skia_output_surface),
+      quad_vertex_skrect_(gfx::RectFToSkRect(QuadVertexRect())),
       lock_set_for_external_use_(resource_provider) {
   if (auto* context_provider = output_surface_->context_provider()) {
     const auto& context_caps = context_provider->ContextCapabilities();
@@ -562,7 +563,7 @@
 void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
   // We need to apply the matrix manually to have pixel-sized stroke width.
   SkPoint vertices[4];
-  gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
+  QuadVertexSkRect().toQuad(vertices);
   SkPoint transformed_vertices[4];
   current_canvas_->getTotalMatrix().mapPoints(transformed_vertices, vertices,
                                               4);
@@ -580,8 +581,7 @@
 void SkiaRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
   SkMatrix content_matrix;
   content_matrix.setRectToRect(gfx::RectFToSkRect(quad->tex_coord_rect),
-                               gfx::RectFToSkRect(QuadVertexRect()),
-                               SkMatrix::kFill_ScaleToFit);
+                               QuadVertexSkRect(), SkMatrix::kFill_ScaleToFit);
   current_canvas_->concat(content_matrix);
 
   const bool needs_transparency =
@@ -815,7 +815,6 @@
   if (!can_draw)
     return;
 
-  const auto dest_rect = gfx::RectFToSkRect(QuadVertexRect());
   SkRect content_rect;
   SkRect dest_visible_rect;
   if (params.filter_image) {
@@ -841,7 +840,7 @@
     SkRect mask_rect = gfx::RectFToSkRect(
         gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
                        quad->mask_texture_size.height()));
-    mask_to_dest_matrix.setRectToRect(mask_rect, dest_rect,
+    mask_to_dest_matrix.setRectToRect(mask_rect, QuadVertexSkRect(),
                                       SkMatrix::kFill_ScaleToFit);
     mask_filter =
         SkShaderMaskFilter::Make(mask_image->makeShader(&mask_to_dest_matrix));
@@ -865,7 +864,7 @@
     // Convert the content_image to a shader, and use drawRect() with the
     // shader.
     SkMatrix content_to_dest_matrix;
-    content_to_dest_matrix.setRectToRect(content_rect, dest_rect,
+    content_to_dest_matrix.setRectToRect(content_rect, QuadVertexSkRect(),
                                          SkMatrix::kFill_ScaleToFit);
     auto shader = content_image->makeShader(&content_to_dest_matrix);
     current_paint_.setShader(std::move(shader));
@@ -882,7 +881,7 @@
                               : nullptr;
   DCHECK(background_image_filter);
   SkMatrix content_to_dest_matrix;
-  content_to_dest_matrix.setRectToRect(content_rect, dest_rect,
+  content_to_dest_matrix.setRectToRect(content_rect, QuadVertexSkRect(),
                                        SkMatrix::kFill_ScaleToFit);
   SkMatrix local_matrix;
   local_matrix.setTranslate(quad->filters_origin.x(), quad->filters_origin.y());
@@ -892,7 +891,7 @@
       background_image_filter->makeWithLocalMatrix(local_matrix);
 
   SkAutoCanvasRestore auto_canvas_restore(current_canvas_, true /* do_save */);
-  current_canvas_->clipRect(dest_rect);
+  current_canvas_->clipRect(QuadVertexSkRect());
 
   SkPaint paint;
   paint.setMaskFilter(mask_filter);
@@ -919,8 +918,7 @@
   current_paint_.setColor(SK_ColorMAGENTA);
 #endif
   current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
-  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
-                            current_paint_);
+  current_canvas_->drawRect(QuadVertexSkRect(), current_paint_);
 }
 
 void SkiaRenderer::CopyDrawnRenderPass(
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h
index e2fa854..05318a8 100644
--- a/components/viz/service/display/skia_renderer.h
+++ b/components/viz/service/display/skia_renderer.h
@@ -104,6 +104,7 @@
   GrContext* GetGrContext();
   bool is_using_ddl() const { return !!skia_output_surface_; }
   const TileDrawQuad* CanPassBeDrawnDirectly(const RenderPass* pass) override;
+  const SkRect& QuadVertexSkRect() const { return quad_vertex_skrect_; }
 
   // A map from RenderPass id to the texture used to draw the RenderPass from.
   struct RenderPassBacking {
@@ -138,6 +139,7 @@
   SkCanvas* current_canvas_ = nullptr;
   SkSurface* current_surface_ = nullptr;
   SkPaint current_paint_;
+  const SkRect quad_vertex_skrect_;
 
   base::Optional<SyncQueryCollection> sync_queries_;
   bool use_swap_with_bounds_ = false;
diff --git a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
index 8a56959..fa78b89 100644
--- a/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
+++ b/components/viz/service/frame_sinks/direct_layer_tree_frame_sink.cc
@@ -132,6 +132,15 @@
         const SurfaceDrawQuad* surface_quad =
             SurfaceDrawQuad::MaterialCast(quad);
 
+        // Skip the quad if the FrameSinkId between fallback and primary is not
+        // the same, because we don't know which FrameSinkId would be used to
+        // draw this quad.
+        if (surface_quad->surface_range.start() &&
+            surface_quad->surface_range.start()->frame_sink_id() !=
+                surface_quad->surface_range.end().frame_sink_id()) {
+          continue;
+        }
+
         // Skip the quad if the transform is not invertible (i.e. it will not
         // be able to receive events).
         gfx::Transform target_to_quad_transform;
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 8ef41f3..7a7d21a 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -710,8 +710,7 @@
       client_id, client_tracing_id, is_gpu_host, cache_shaders_on_disk);
 
   mojo::MessagePipe pipe;
-  gpu_channel->Init(std::make_unique<gpu::SyncChannelFilteredSender>(
-      pipe.handle0.release(), gpu_channel, io_runner_, shutdown_event_));
+  gpu_channel->Init(pipe.handle0.release(), shutdown_event_);
 
   media_gpu_channel_manager_->AddChannel(client_id);
 
diff --git a/content/browser/background_fetch/storage/mark_request_complete_task.cc b/content/browser/background_fetch/storage/mark_request_complete_task.cc
index 78be237..7fcf47e 100644
--- a/content/browser/background_fetch/storage/mark_request_complete_task.cc
+++ b/content/browser/background_fetch/storage/mark_request_complete_task.cc
@@ -62,8 +62,6 @@
 void MarkRequestCompleteTask::StoreResponse(base::OnceClosure done_closure) {
   auto response = blink::mojom::FetchAPIResponse::New();
   response->url_list = request_info_->GetURLChain();
-  // TODO(crbug.com/838837): fill error and cors_exposed_header_names in
-  // response.
   response->response_type = network::mojom::FetchResponseType::kDefault;
   response->response_time = request_info_->GetResponseTime();
 
diff --git a/content/browser/devtools/devtools_video_consumer.cc b/content/browser/devtools/devtools_video_consumer.cc
index 1dd814c..f06866c3 100644
--- a/content/browser/devtools/devtools_video_consumer.cc
+++ b/content/browser/devtools/devtools_video_consumer.cc
@@ -57,7 +57,7 @@
   skbitmap.allocN32Pixels(frame->visible_rect().width(),
                           frame->visible_rect().height());
   cc::SkiaPaintCanvas canvas(skbitmap);
-  renderer.Copy(frame, &canvas, media::Context3D(), nullptr);
+  renderer.Copy(frame, &canvas, media::Context3D());
   return skbitmap;
 }
 
diff --git a/content/browser/loader/data_pipe_to_source_stream.cc b/content/browser/loader/data_pipe_to_source_stream.cc
index 4a3694f..87f422f3 100644
--- a/content/browser/loader/data_pipe_to_source_stream.cc
+++ b/content/browser/loader/data_pipe_to_source_stream.cc
@@ -33,7 +33,7 @@
 int DataPipeToSourceStream::Read(net::IOBuffer* buf,
                                  int buf_size,
                                  net::CompletionOnceCallback callback) {
-  base::AutoReset<bool>(&inside_read_, true);
+  base::AutoReset<bool> inside_read_checker(&inside_read_, true);
 
   if (!body_.get()) {
     // We have finished reading the pipe.
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 838bef6ca..a5ea7dd9 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -221,10 +221,8 @@
       request_info->begin_params->headers);
 
   std::string accept_value = network::kFrameAcceptHeader;
-  // TODO(https://crbug.com/840704): Decide whether the Accept header should
-  // advertise the state of kSignedHTTPExchangeOriginTrial before starting the
-  // Origin-Trial.
-  if (signed_exchange_utils::IsSignedExchangeHandlingEnabled()) {
+  if (signed_exchange_utils::ShouldAdvertiseAcceptHeader(
+          url::Origin::Create(request_info->common_params.url))) {
     DCHECK(!accept_value.empty());
     accept_value.append(kAcceptHeaderSignedExchangeSuffix);
   }
@@ -988,6 +986,27 @@
     // |resource_request_| during redirect.
     url_loader_modified_request_headers_ = modified_request_headers;
 
+    if (signed_exchange_utils::NeedToCheckRedirectedURLForAcceptHeader()) {
+      // Currently we send the SignedExchange accept header only for the limited
+      // origins when SignedHTTPExchangeOriginTrial feature is enabled without
+      // SignedHTTPExchange feature. We need to put the SignedExchange accept
+      // header on when redirecting to the origins in the OriginList of
+      // SignedHTTPExchangeAcceptHeader field trial, and need to remove it when
+      // redirecting to out of the OriginList.
+      if (!url_loader_modified_request_headers_)
+        url_loader_modified_request_headers_ = net::HttpRequestHeaders();
+      std::string accept_value = network::kFrameAcceptHeader;
+      if (signed_exchange_utils::ShouldAdvertiseAcceptHeader(
+              url::Origin::Create(resource_request_->url))) {
+        DCHECK(!accept_value.empty());
+        accept_value.append(kAcceptHeaderSignedExchangeSuffix);
+      }
+      url_loader_modified_request_headers_->SetHeader(network::kAcceptHeader,
+                                                      accept_value);
+      resource_request_->headers.SetHeader(network::kAcceptHeader,
+                                           accept_value);
+    }
+
     Restart();
   }
 
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index 58e1e1a..c0a78b1 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -1505,10 +1505,8 @@
   headers.AddHeadersFromString(info.begin_params->headers);
 
   std::string accept_value = network::kFrameAcceptHeader;
-  // TODO(https://crbug.com/840704): Decide whether the Accept header should
-  // advertise the state of kSignedHTTPExchangeOriginTrial before starting the
-  // Origin-Trial.
-  if (signed_exchange_utils::IsSignedExchangeHandlingEnabled()) {
+  if (signed_exchange_utils::ShouldAdvertiseAcceptHeader(
+          url::Origin::Create(info.common_params.url))) {
     DCHECK(!accept_value.empty());
     accept_value.append(kAcceptHeaderSignedExchangeSuffix);
   }
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 8ba5624e..769bc582 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -149,7 +149,7 @@
 }
 
 bool DelegatedFrameHost::CanCopyFromCompositingSurface() const {
-  return active_device_scale_factor_ != 0.f;
+  return HasFallbackSurface() && active_device_scale_factor_ != 0.f;
 }
 
 bool DelegatedFrameHost::TransformPointToLocalCoordSpaceLegacy(
@@ -330,6 +330,8 @@
     return;
   }
 
+  client_->DelegatedFrameHostGetLayer()->SetFallbackSurfaceId(
+      surface_info.id());
   active_local_surface_id_ = surface_info.id().local_surface_id();
   active_device_scale_factor_ = surface_info.device_scale_factor();
 
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 1c2a8390..95444b0 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1698,6 +1698,8 @@
   // In TransformPointToLocalCoordSpace() there is a Point-to-Pixel conversion,
   // but it is not necessary here because the final target view is responsible
   // for converting before computing the final transform.
+  if (!HasFallbackSurface())
+    return false;
   return target_view->TransformPointToLocalCoordSpace(
       point, GetCurrentSurfaceId(), transformed_point, source);
 }
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 26a8baaf..84876fe 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -3431,7 +3431,8 @@
   EXPECT_FALSE(view_->HasFallbackSurface());
 }
 
-// This test verifies that the primary SurfaceId is populated on resize.
+// This test verifies that the primary SurfaceId is populated on resize and
+// the fallback SurfaceId is populated in OnFirstSurfaceActivation.
 TEST_F(RenderWidgetHostViewAuraSurfaceSynchronizationTest, SurfaceChanges) {
   // Early out because DelegatedFrameHost is not used in mash.
   if (features::IsMultiProcessMash())
@@ -3447,8 +3448,25 @@
   view_->SetSize(gfx::Size(300, 300));
   ASSERT_TRUE(view_->HasPrimarySurface());
   EXPECT_EQ(gfx::Size(300, 300), view_->window_->layer()->size());
+  EXPECT_FALSE(view_->HasFallbackSurface());
   EXPECT_EQ(gfx::Size(300, 300),
             view_->delegated_frame_host_->CurrentFrameSizeInDipForTesting());
+
+  // Resizing should update the primary SurfaceId.
+  view_->SetSize(gfx::Size(400, 400));
+  EXPECT_EQ(gfx::Size(400, 400), view_->window_->layer()->size());
+  EXPECT_EQ(nullptr, view_->window_->layer()->GetFallbackSurfaceId());
+  EXPECT_EQ(gfx::Size(400, 400),
+            view_->delegated_frame_host_->CurrentFrameSizeInDipForTesting());
+
+  // Fallback SurfaceId should be updated in OnFirstSurfaceActivation.
+  // Submitting a CompositorFrame should update the fallback SurfaceId
+  viz::SurfaceId surface_id(view_->GetFrameSinkId(),
+                            view_->GetLocalSurfaceId());
+  view_->delegated_frame_host_->OnFirstSurfaceActivation(
+      viz::SurfaceInfo(surface_id, 1.f, gfx::Size(400, 400)));
+  EXPECT_EQ(gfx::Size(400, 400), view_->window_->layer()->size());
+  EXPECT_EQ(surface_id, *view_->window_->layer()->GetFallbackSurfaceId());
 }
 
 // This test verifies that the primary SurfaceId is updated on device scale
@@ -3645,8 +3663,7 @@
 
   // Make [1] hidden, resize it. It should drop its frame.
   views[1]->Hide();
-  // TODO(samans): Fix this expectation once https://crbug.com/878372 is fixed.
-  EXPECT_FALSE(views[1]->HasFallbackSurface());
+  EXPECT_TRUE(views[1]->HasFallbackSurface());
   gfx::Size size2(200, 200);
   views[1]->SetSize(size2);
   EXPECT_FALSE(views[1]->HasFallbackSurface());
@@ -5919,9 +5936,7 @@
   viz::LocalSurfaceId id1 = view_->GetLocalSurfaceId();
   view_->delegated_frame_host_->OnFirstSurfaceActivation(viz::SurfaceInfo(
       viz::SurfaceId(view_->GetFrameSinkId(), id1), 1, gfx::Size(20, 20)));
-  // TODO(samans): Fix these expectations once https://crbug.com/878372 is
-  // fixed.
-  EXPECT_FALSE(view_->window_->layer()->GetFallbackSurfaceId());
+  EXPECT_TRUE(view_->window_->layer()->GetFallbackSurfaceId()->is_valid());
   view_->Hide();
   view_->SetSize(gfx::Size(54, 32));
   view_->Show();
@@ -5944,12 +5959,10 @@
   viz::LocalSurfaceId id1 = view_->GetLocalSurfaceId();
   view_->delegated_frame_host_->OnFirstSurfaceActivation(viz::SurfaceInfo(
       viz::SurfaceId(view_->GetFrameSinkId(), id1), 1, gfx::Size(20, 20)));
-  // TODO(samans): Fix these expectations once https://crbug.com/878372 is
-  // fixed.
-  EXPECT_FALSE(view_->window_->layer()->GetFallbackSurfaceId());
+  EXPECT_TRUE(view_->window_->layer()->GetFallbackSurfaceId()->is_valid());
   view_->Hide();
   view_->Show();
-  EXPECT_FALSE(view_->window_->layer()->GetFallbackSurfaceId());
+  EXPECT_TRUE(view_->window_->layer()->GetFallbackSurfaceId()->is_valid());
 }
 
 // Check that TakeFallbackContentFrom() copies the fallback SurfaceId and
@@ -5974,10 +5987,16 @@
       view2->GetNativeView(), parent_view_->GetNativeView()->GetRootWindow(),
       gfx::Rect());
 
-  // Call TakeFallbackContentFrom(). The second view should obtain a fallback
-  // from the first view.
+  // Set fallback for the first view.
+  viz::LocalSurfaceId id = view_->GetLocalSurfaceId();
+  view_->delegated_frame_host_->OnFirstSurfaceActivation(viz::SurfaceInfo(
+      viz::SurfaceId(view_->GetFrameSinkId(), id), 1, gfx::Size(20, 20)));
+  EXPECT_TRUE(view_->window_->layer()->GetFallbackSurfaceId()->is_valid());
+
+  // Call TakeFallbackContentFrom(). The second view should now have the same
+  // fallback as the first view.
   view2->TakeFallbackContentFrom(view_);
-  EXPECT_EQ(view_->window_->layer()->GetPrimarySurfaceId()->ToSmallestId(),
+  EXPECT_EQ(*view_->window_->layer()->GetFallbackSurfaceId(),
             *view2->window_->layer()->GetFallbackSurfaceId());
 
   DestroyView(view2);
diff --git a/content/browser/renderer_host/render_widget_host_view_mac.mm b/content/browser/renderer_host/render_widget_host_view_mac.mm
index d5c21de..fc8e9dec 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac.mm
@@ -1311,6 +1311,8 @@
     return true;
   }
 
+  if (!HasFallbackSurface())
+    return false;
   return target_view->TransformPointToLocalCoordSpace(
       point, GetCurrentSurfaceId(), transformed_point, source);
 }
diff --git a/content/browser/service_worker/service_worker_cache_writer_unittest.cc b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
index 7c858bd1..d37f40a40 100644
--- a/content/browser/service_worker/service_worker_cache_writer_unittest.cc
+++ b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
@@ -19,306 +19,6 @@
 namespace content {
 namespace {
 
-// A test implementation of ServiceWorkerResponseReader.
-//
-// This class exposes the ability to expect reads (see ExpectRead*() below).
-// Each call to ReadInfo() or ReadData() consumes another expected read, in the
-// order those reads were expected, so:
-//    reader->ExpectReadInfoOk(5, false);
-//    reader->ExpectReadDataOk("abcdef", false);
-//    reader->ExpectReadDataOk("ghijkl", false);
-// Expects these calls, in this order:
-//    reader->ReadInfo(...);  // reader writes 5 into
-//                            // |info_buf->response_data_size|
-//    reader->ReadData(...);  // reader writes "abcdef" into |buf|
-//    reader->ReadData(...);  // reader writes "ghijkl" into |buf|
-// If an unexpected call happens, this class DCHECKs.
-// If an expected read is marked "async", it will not complete immediately, but
-// must be completed by the test using CompletePendingRead().
-// These is a convenience method AllExpectedReadsDone() which returns whether
-// there are any expected reads that have not yet happened.
-class MockServiceWorkerResponseReader : public ServiceWorkerResponseReader {
- public:
-  MockServiceWorkerResponseReader()
-      : ServiceWorkerResponseReader(
-            0,
-            base::WeakPtr<AppCacheDiskCacheInterface>()) {}
-  ~MockServiceWorkerResponseReader() override {}
-
-  // ServiceWorkerResponseReader overrides
-  void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
-                OnceCompletionCallback callback) override;
-  void ReadData(net::IOBuffer* buf,
-                int buf_len,
-                OnceCompletionCallback callback) override;
-
-  // Test helpers. ExpectReadInfo() and ExpectReadData() give precise control
-  // over both the data to be written and the result to return.
-  // ExpectReadInfoOk() and ExpectReadDataOk() are convenience functions for
-  // expecting successful reads, which always have their length as their result.
-
-  // Expect a call to ReadInfo() on this reader. For these functions, |len| will
-  // be used as |response_data_size|, not as the length of this particular read.
-  void ExpectReadInfo(size_t len, bool async, int result);
-  void ExpectReadInfoOk(size_t len, bool async);
-
-  // Expect a call to ReadData() on this reader. For these functions, |len| is
-  // the length of the data to be written back; in ExpectReadDataOk(), |len| is
-  // implicitly the length of |data|.
-  void ExpectReadData(const char* data, size_t len, bool async, int result);
-  void ExpectReadDataOk(const std::string& data, bool async);
-
-  // Complete a pending async read. It is an error to call this function without
-  // a pending async read (ie, a previous call to ReadInfo() or ReadData()
-  // having not run its callback yet).
-  void CompletePendingRead();
-
-  // Returns whether all expected reads have occurred.
-  bool AllExpectedReadsDone() { return expected_reads_.size() == 0; }
-
- private:
-  struct ExpectedRead {
-    ExpectedRead(size_t len, bool async, int result)
-        : data(nullptr), len(len), info(true), async(async), result(result) {}
-    ExpectedRead(const char* data, size_t len, bool async, int result)
-        : data(data), len(len), info(false), async(async), result(result) {}
-    const char* data;
-    size_t len;
-    bool info;
-    bool async;
-    int result;
-  };
-
-  base::queue<ExpectedRead> expected_reads_;
-  scoped_refptr<net::IOBuffer> pending_buffer_;
-  size_t pending_buffer_len_;
-  scoped_refptr<HttpResponseInfoIOBuffer> pending_info_;
-  OnceCompletionCallback pending_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockServiceWorkerResponseReader);
-};
-
-void MockServiceWorkerResponseReader::ReadInfo(
-    HttpResponseInfoIOBuffer* info_buf,
-    OnceCompletionCallback callback) {
-  DCHECK(!expected_reads_.empty());
-  ExpectedRead expected = expected_reads_.front();
-  EXPECT_TRUE(expected.info);
-  if (expected.async) {
-    pending_info_ = info_buf;
-    pending_callback_ = std::move(callback);
-  } else {
-    expected_reads_.pop();
-    info_buf->response_data_size = expected.len;
-    std::move(callback).Run(expected.result);
-  }
-}
-
-void MockServiceWorkerResponseReader::ReadData(
-    net::IOBuffer* buf,
-    int buf_len,
-    OnceCompletionCallback callback) {
-  DCHECK(!expected_reads_.empty());
-  ExpectedRead expected = expected_reads_.front();
-  EXPECT_FALSE(expected.info);
-  if (expected.async) {
-    pending_callback_ = std::move(callback);
-    pending_buffer_ = buf;
-    pending_buffer_len_ = static_cast<size_t>(buf_len);
-  } else {
-    expected_reads_.pop();
-    if (expected.len > 0) {
-      size_t to_read = std::min(static_cast<size_t>(buf_len), expected.len);
-      memcpy(buf->data(), expected.data, to_read);
-    }
-    std::move(callback).Run(expected.result);
-  }
-}
-
-void MockServiceWorkerResponseReader::ExpectReadInfo(size_t len,
-                                                     bool async,
-                                                     int result) {
-  expected_reads_.push(ExpectedRead(len, async, result));
-}
-
-void MockServiceWorkerResponseReader::ExpectReadInfoOk(size_t len, bool async) {
-  expected_reads_.push(ExpectedRead(len, async, len));
-}
-
-void MockServiceWorkerResponseReader::ExpectReadData(const char* data,
-                                                     size_t len,
-                                                     bool async,
-                                                     int result) {
-  expected_reads_.push(ExpectedRead(data, len, async, result));
-}
-
-void MockServiceWorkerResponseReader::ExpectReadDataOk(const std::string& data,
-                                                       bool async) {
-  expected_reads_.push(
-      ExpectedRead(data.data(), data.size(), async, data.size()));
-}
-
-void MockServiceWorkerResponseReader::CompletePendingRead() {
-  DCHECK(!expected_reads_.empty());
-  ExpectedRead expected = expected_reads_.front();
-  expected_reads_.pop();
-  EXPECT_TRUE(expected.async);
-  if (expected.info) {
-    pending_info_->response_data_size = expected.len;
-  } else {
-    size_t to_read = std::min(pending_buffer_len_, expected.len);
-    if (to_read > 0)
-      memcpy(pending_buffer_->data(), expected.data, to_read);
-  }
-  pending_info_ = nullptr;
-  pending_buffer_ = nullptr;
-  OnceCompletionCallback callback = std::move(pending_callback_);
-  pending_callback_.Reset();
-  std::move(callback).Run(expected.result);
-}
-
-// A test implementation of ServiceWorkerResponseWriter.
-//
-// This class exposes the ability to expect writes (see ExpectWrite*Ok() below).
-// Each write to this class via WriteInfo() or WriteData() consumes another
-// expected write, in the order they were added, so:
-//   writer->ExpectWriteInfoOk(5, false);
-//   writer->ExpectWriteDataOk(6, false);
-//   writer->ExpectWriteDataOk(6, false);
-// Expects these calls, in this order:
-//   writer->WriteInfo(...);  // checks that |buf->response_data_size| == 5
-//   writer->WriteData(...);  // checks that 6 bytes are being written
-//   writer->WriteData(...);  // checks that another 6 bytes are being written
-// If this class receives an unexpected call to WriteInfo() or WriteData(), it
-// DCHECKs.
-// Expected writes marked async do not complete synchronously, but rather return
-// without running their callback and need to be completed with
-// CompletePendingWrite().
-// A convenience method AllExpectedWritesDone() is exposed so tests can ensure
-// that all expected writes have been consumed by matching calls to WriteInfo()
-// or WriteData().
-class MockServiceWorkerResponseWriter : public ServiceWorkerResponseWriter {
- public:
-  MockServiceWorkerResponseWriter()
-      : ServiceWorkerResponseWriter(
-            0,
-            base::WeakPtr<AppCacheDiskCacheInterface>()),
-        info_written_(0),
-        data_written_(0) {}
-  ~MockServiceWorkerResponseWriter() override {}
-
-  // ServiceWorkerResponseWriter overrides
-  void WriteInfo(HttpResponseInfoIOBuffer* info_buf,
-                 OnceCompletionCallback callback) override;
-  void WriteData(net::IOBuffer* buf,
-                 int buf_len,
-                 OnceCompletionCallback callback) override;
-
-  // Enqueue expected writes.
-  void ExpectWriteInfoOk(size_t len, bool async);
-  void ExpectWriteInfo(size_t len, bool async, int result);
-  void ExpectWriteDataOk(size_t len, bool async);
-  void ExpectWriteData(size_t len, bool async, int result);
-
-  // Complete a pending asynchronous write. This method DCHECKs unless there is
-  // a pending write (a write for which WriteInfo() or WriteData() has been
-  // called but the callback has not yet been run).
-  void CompletePendingWrite();
-
-  // Returns whether all expected reads have been consumed.
-  bool AllExpectedWritesDone() { return expected_writes_.size() == 0; }
-
- private:
-  struct ExpectedWrite {
-    ExpectedWrite(bool is_info, size_t length, bool async, int result)
-        : is_info(is_info), length(length), async(async), result(result) {}
-    bool is_info;
-    size_t length;
-    bool async;
-    int result;
-  };
-
-  base::queue<ExpectedWrite> expected_writes_;
-
-  size_t info_written_;
-  size_t data_written_;
-
-  OnceCompletionCallback pending_callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(MockServiceWorkerResponseWriter);
-};
-
-void MockServiceWorkerResponseWriter::WriteInfo(
-    HttpResponseInfoIOBuffer* info_buf,
-    OnceCompletionCallback callback) {
-  DCHECK(!expected_writes_.empty());
-  ExpectedWrite write = expected_writes_.front();
-  EXPECT_TRUE(write.is_info);
-  if (write.result > 0) {
-    EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size));
-    info_written_ += info_buf->response_data_size;
-  }
-  if (!write.async) {
-    expected_writes_.pop();
-    std::move(callback).Run(write.result);
-  } else {
-    pending_callback_ = std::move(callback);
-  }
-}
-
-void MockServiceWorkerResponseWriter::WriteData(
-    net::IOBuffer* buf,
-    int buf_len,
-    OnceCompletionCallback callback) {
-  DCHECK(!expected_writes_.empty());
-  ExpectedWrite write = expected_writes_.front();
-  EXPECT_FALSE(write.is_info);
-  if (write.result > 0) {
-    EXPECT_EQ(write.length, static_cast<size_t>(buf_len));
-    data_written_ += buf_len;
-  }
-  if (!write.async) {
-    expected_writes_.pop();
-    std::move(callback).Run(write.result);
-  } else {
-    pending_callback_ = std::move(callback);
-  }
-}
-
-void MockServiceWorkerResponseWriter::ExpectWriteInfoOk(size_t length,
-                                                        bool async) {
-  ExpectWriteInfo(length, async, length);
-}
-
-void MockServiceWorkerResponseWriter::ExpectWriteDataOk(size_t length,
-                                                        bool async) {
-  ExpectWriteData(length, async, length);
-}
-
-void MockServiceWorkerResponseWriter::ExpectWriteInfo(size_t length,
-                                                      bool async,
-                                                      int result) {
-  DCHECK_NE(net::ERR_IO_PENDING, result);
-  ExpectedWrite expected(true, length, async, result);
-  expected_writes_.push(expected);
-}
-
-void MockServiceWorkerResponseWriter::ExpectWriteData(size_t length,
-                                                      bool async,
-                                                      int result) {
-  DCHECK_NE(net::ERR_IO_PENDING, result);
-  ExpectedWrite expected(false, length, async, result);
-  expected_writes_.push(expected);
-}
-
-void MockServiceWorkerResponseWriter::CompletePendingWrite() {
-  DCHECK(!expected_writes_.empty());
-  ExpectedWrite write = expected_writes_.front();
-  DCHECK(write.async);
-  expected_writes_.pop();
-  std::move(pending_callback_).Run(write.result);
-}
-
 class ServiceWorkerCacheWriterTest : public ::testing::Test {
  public:
   ServiceWorkerCacheWriterTest() {}
diff --git a/content/browser/service_worker/service_worker_database_unittest.cc b/content/browser/service_worker/service_worker_database_unittest.cc
index 8b0dca82d..74bc0ee 100644
--- a/content/browser/service_worker/service_worker_database_unittest.cc
+++ b/content/browser/service_worker/service_worker_database_unittest.cc
@@ -9,7 +9,6 @@
 
 #include <string>
 
-#include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/macros.h"
@@ -17,7 +16,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "content/browser/service_worker/service_worker_database.pb.h"
 #include "content/common/service_worker/service_worker_types.h"
-#include "content/public/common/content_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
@@ -548,11 +546,6 @@
 }
 
 TEST(ServiceWorkerDatabaseTest, GetAllRegistrations) {
-  // TODO(https://crbug.com/618076): Remove the following command line switch
-  // when update_via_cache is shipped to stable.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalWebPlatformFeatures);
-
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
 
   std::vector<RegistrationData> registrations;
@@ -768,11 +761,6 @@
 }
 
 TEST(ServiceWorkerDatabaseTest, Registration_Overwrite) {
-  // TODO(https://crbug.com/618076): Remove the following command line switch
-  // when update_via_cache is shipped to stable.
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(
-      switches::kEnableExperimentalWebPlatformFeatures);
-
   std::unique_ptr<ServiceWorkerDatabase> database(CreateDatabaseInMemory());
 
   GURL origin("http://example.com");
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc
index 3319999..5552d4a 100644
--- a/content/browser/service_worker/service_worker_navigation_loader.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -416,6 +416,7 @@
 
   if (fetch_result ==
       ServiceWorkerFetchDispatcher::FetchEventResult::kShouldFallback) {
+    TransitionToStatus(Status::kCompleted);
     // TODO(falken): Propagate the timing info to the renderer somehow, or else
     // Navigation Timing etc APIs won't know about service worker.
     std::move(fallback_callback_)
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index 969f6b0..c9e9f2f 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -19,6 +19,7 @@
 #include "content/browser/service_worker/service_worker_storage.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
+#include "content/browser/url_loader_factory_getter.h"
 #include "content/common/service_worker/service_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
@@ -299,7 +300,9 @@
         registration()->GetNewestVersion();
     std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
     version_to_update->script_cache_map()->GetResources(&resources);
-    update_checker_ = std::make_unique<ServiceWorkerUpdateChecker>(resources);
+    update_checker_ = std::make_unique<ServiceWorkerUpdateChecker>(
+        resources, version_to_update,
+        context_->loader_factory_getter()->GetNetworkFactory());
     update_checker_->Start(
         base::BindOnce(&ServiceWorkerRegisterJob::OnUpdateCheckFinished,
                        weak_factory_.GetWeakPtr()));
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.cc b/content/browser/service_worker/service_worker_single_script_update_checker.cc
index 20a63dea..a08ad96 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.cc
@@ -3,34 +3,142 @@
 // found in the LICENSE file.
 
 #include "content/browser/service_worker/service_worker_single_script_update_checker.h"
-#include "content/browser/service_worker/service_worker_update_checker.h"
+
+#include "content/browser/appcache/appcache_response.h"
+#include "content/browser/service_worker/service_worker_cache_writer.h"
+#include "content/public/common/resource_type.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "services/network/public/cpp/net_adapters.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "third_party/blink/public/common/mime_util/mime_util.h"
+
+// TODO(momohatt): Use ServiceWorkerMetrics for UMA.
+
+namespace {
+
+constexpr net::NetworkTrafficAnnotationTag kUpdateCheckTrafficAnnotation =
+    net::DefineNetworkTrafficAnnotation("service_worker_update_checker",
+                                        R"(
+    semantics {
+      sender: "ServiceWorker System"
+      description:
+        "This request is issued by an update check to fetch the content of "
+        "the new scripts."
+      trigger:
+        "ServiceWorker's update logic, which is triggered by a navigation to a "
+        "site controlled by a service worker."
+      data:
+        "No body. 'Service-Worker: script' header is attached when it's the "
+        "main worker script. Requests may include cookies and credentials."
+      destination: WEBSITE
+    }
+    policy {
+      cookies_allowed: YES
+      cookies_store: "user"
+      setting:
+        "Users can control this feature via the 'Cookies' setting under "
+        "'Privacy, Content settings'. If cookies are disabled for a single "
+        "site, serviceworkers are disabled for the site only. If they are "
+        "totally disabled, all serviceworker requests will be stopped."
+      chrome_policy {
+        URLBlacklist {
+          URLBlacklist: { entries: '*' }
+        }
+      }
+      chrome_policy {
+        URLWhitelist {
+          URLWhitelist { }
+        }
+      }
+    }
+    comments:
+      "Chrome would be unable to update service workers without this type of "
+      "request. Using either URLBlacklist or URLWhitelist policies (or a "
+      "combination of both) limits the scope of these requests."
+    )");
+
+}  // namespace
 
 namespace content {
 
 ServiceWorkerSingleScriptUpdateChecker::ServiceWorkerSingleScriptUpdateChecker(
-    const GURL script_url,
-    int64_t resource_id,
-    ServiceWorkerUpdateChecker* owner)
-    : owner_(owner) {
-  NOTIMPLEMENTED();
+    const GURL& url,
+    bool is_main_script,
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
+    std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
+    std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
+    std::unique_ptr<ServiceWorkerResponseWriter> writer,
+    ResultCallback callback)
+    : network_client_binding_(this),
+      network_watcher_(FROM_HERE,
+                       mojo::SimpleWatcher::ArmingPolicy::MANUAL,
+                       base::SequencedTaskRunnerHandle::Get()),
+      callback_(std::move(callback)),
+      weak_factory_(this) {
+  network::ResourceRequest resource_request;
+  resource_request.url = url;
+  resource_request.resource_type =
+      is_main_script ? RESOURCE_TYPE_SERVICE_WORKER : RESOURCE_TYPE_SCRIPT;
+  resource_request.do_not_prompt_for_login = true;
+  if (is_main_script)
+    resource_request.headers.SetHeader("Service-Worker", "script");
+
+  // TODO(momohatt): Handle cases where force_bypass_cache is enabled.
+
+  // |compare_reader| shouldn't be a nullptr, which forces
+  // ServiceWorkerCacheWriter to do the comparison.
+  DCHECK(compare_reader);
+  cache_writer_ = std::make_unique<ServiceWorkerCacheWriter>(
+      std::move(compare_reader), std::move(copy_reader), std::move(writer),
+      true /* pause_when_not_identical */);
+
+  network::mojom::URLLoaderClientPtr network_client;
+  network_client_binding_.Bind(mojo::MakeRequest(&network_client));
+
+  loader_factory->CreateLoaderAndStart(
+      mojo::MakeRequest(&network_loader_), -1 /* routing_id */,
+      -1 /* request_id */, network::mojom::kURLLoadOptionNone, resource_request,
+      std::move(network_client),
+      net::MutableNetworkTrafficAnnotationTag(kUpdateCheckTrafficAnnotation));
+  DCHECK_EQ(NetworkLoaderState::kNotStarted, network_loader_state_);
+  network_loader_state_ = NetworkLoaderState::kLoadingHeader;
 }
 
 ServiceWorkerSingleScriptUpdateChecker::
     ~ServiceWorkerSingleScriptUpdateChecker() = default;
 
-void ServiceWorkerSingleScriptUpdateChecker::Start() {
-  CommitCompleted(true /* is_script_changed */);
-}
-
 // URLLoaderClient override ----------------------------------------------------
+
 void ServiceWorkerSingleScriptUpdateChecker::OnReceiveResponse(
     const network::ResourceResponseHead& response_head) {
-  NOTIMPLEMENTED();
+  DCHECK_EQ(NetworkLoaderState::kLoadingHeader, network_loader_state_);
+
+  // We don't have complete info here, but fill in what we have now.
+  // At least we need headers and SSL info.
+  auto response_info = std::make_unique<net::HttpResponseInfo>();
+  response_info->headers = response_head.headers;
+  if (response_head.ssl_info.has_value())
+    response_info->ssl_info = *response_head.ssl_info;
+  response_info->was_fetched_via_spdy = response_head.was_fetched_via_spdy;
+  response_info->was_alpn_negotiated = response_head.was_alpn_negotiated;
+  response_info->alpn_negotiated_protocol =
+      response_head.alpn_negotiated_protocol;
+  response_info->connection_info = response_head.connection_info;
+  response_info->socket_address = response_head.socket_address;
+
+  // TODO(momohatt): Check for header errors.
+
+  network_loader_state_ = NetworkLoaderState::kWaitingForBody;
+
+  WriteHeaders(
+      base::MakeRefCounted<HttpResponseInfoIOBuffer>(std::move(response_info)));
 }
 
 void ServiceWorkerSingleScriptUpdateChecker::OnReceiveRedirect(
     const net::RedirectInfo& redirect_info,
     const network::ResourceResponseHead& response_head) {
+  // TODO(momohatt): Raise error and terminate the update check here, like
+  // ServiceWorkerNewScriptLoader does.
   NOTIMPLEMENTED();
 }
 
@@ -38,13 +146,12 @@
     int64_t current_position,
     int64_t total_size,
     OnUploadProgressCallback ack_callback) {
-  NOTIMPLEMENTED();
+  // The network request for update checking shouldn't have upload data.
+  NOTREACHED();
 }
 
 void ServiceWorkerSingleScriptUpdateChecker::OnReceiveCachedMetadata(
-    const std::vector<uint8_t>& data) {
-  NOTIMPLEMENTED();
-}
+    const std::vector<uint8_t>& data) {}
 
 void ServiceWorkerSingleScriptUpdateChecker::OnTransferSizeUpdated(
     int32_t transfer_size_diff) {
@@ -53,18 +160,226 @@
 
 void ServiceWorkerSingleScriptUpdateChecker::OnStartLoadingResponseBody(
     mojo::ScopedDataPipeConsumerHandle consumer) {
-  NOTIMPLEMENTED();
+  DCHECK_EQ(NetworkLoaderState::kWaitingForBody, network_loader_state_);
+
+  network_consumer_ = std::move(consumer);
+  network_loader_state_ = NetworkLoaderState::kLoadingBody;
+  MaybeStartNetworkConsumerHandleWatcher();
 }
 
 void ServiceWorkerSingleScriptUpdateChecker::OnComplete(
     const network::URLLoaderCompletionStatus& status) {
-  NOTIMPLEMENTED();
+  NetworkLoaderState previous_loader_state = network_loader_state_;
+  network_loader_state_ = NetworkLoaderState::kCompleted;
+  if (status.error_code != net::OK) {
+    Finish(false /* is_script_changed */);
+    return;
+  }
+
+  DCHECK(previous_loader_state == NetworkLoaderState::kWaitingForBody ||
+         previous_loader_state == NetworkLoaderState::kLoadingBody);
+
+  // Response body is empty.
+  if (previous_loader_state == NetworkLoaderState::kWaitingForBody) {
+    DCHECK_EQ(CacheWriterState::kNotStarted, body_writer_state_);
+    body_writer_state_ = CacheWriterState::kCompleted;
+    switch (header_writer_state_) {
+      case CacheWriterState::kNotStarted:
+        NOTREACHED()
+            << "Response header should be received before OnComplete()";
+        break;
+      case CacheWriterState::kWriting:
+        // Wait until it's written. OnWriteHeadersComplete() will call
+        // Finish().
+        return;
+      case CacheWriterState::kCompleted:
+        DCHECK(!network_consumer_.is_valid());
+        // Compare the cached data with an empty data to notify |cache_writer_|
+        // of the end of the comparison.
+        CompareData(nullptr /* pending_buffer */, 0 /* bytes_available */);
+        break;
+    }
+  }
+
+  // Response body exists.
+  if (previous_loader_state == NetworkLoaderState::kLoadingBody) {
+    switch (body_writer_state_) {
+      case CacheWriterState::kNotStarted:
+        DCHECK_EQ(CacheWriterState::kWriting, header_writer_state_);
+        return;
+      case CacheWriterState::kWriting:
+        DCHECK_EQ(CacheWriterState::kCompleted, header_writer_state_);
+        return;
+      case CacheWriterState::kCompleted:
+        DCHECK_EQ(CacheWriterState::kCompleted, header_writer_state_);
+        Finish(false /* is_script_changed */);
+        return;
+    }
+  }
 }
+
 //------------------------------------------------------------------------------
 
-void ServiceWorkerSingleScriptUpdateChecker::CommitCompleted(
-    bool is_script_changed) {
-  owner_->OnOneUpdateCheckFinished(is_script_changed);
+void ServiceWorkerSingleScriptUpdateChecker::WriteHeaders(
+    scoped_refptr<HttpResponseInfoIOBuffer> info_buffer) {
+  DCHECK_EQ(CacheWriterState::kNotStarted, header_writer_state_);
+  header_writer_state_ = CacheWriterState::kWriting;
+
+  // Pass the header to the cache_writer_. This is written to the storage when
+  // the body had changes.
+  net::Error error = cache_writer_->MaybeWriteHeaders(
+      info_buffer.get(),
+      base::BindOnce(
+          &ServiceWorkerSingleScriptUpdateChecker::OnWriteHeadersComplete,
+          weak_factory_.GetWeakPtr()));
+  if (error == net::ERR_IO_PENDING) {
+    // OnWriteHeadersComplete() will be called asynchronously.
+    return;
+  }
+  // MaybeWriteHeaders() doesn't run the callback if it finishes synchronously,
+  // so explicitly call it here.
+  OnWriteHeadersComplete(error);
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::OnWriteHeadersComplete(
+    net::Error error) {
+  DCHECK_EQ(CacheWriterState::kWriting, header_writer_state_);
+  DCHECK_NE(net::ERR_IO_PENDING, error);
+  header_writer_state_ = CacheWriterState::kCompleted;
+
+  if (error != net::OK) {
+    Finish(false /* is_script_changed */);
+    return;
+  }
+
+  // Response body is empty.
+  if (network_loader_state_ == NetworkLoaderState::kCompleted &&
+      body_writer_state_ == CacheWriterState::kCompleted) {
+    // Compare the cached data with an empty data to notify |cache_writer_|
+    // the end of the comparison.
+    CompareData(nullptr /* pending_buffer */, 0 /* bytes_available */);
+    return;
+  }
+
+  MaybeStartNetworkConsumerHandleWatcher();
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::
+    MaybeStartNetworkConsumerHandleWatcher() {
+  if (network_loader_state_ == NetworkLoaderState::kWaitingForBody) {
+    // OnStartLoadingResponseBody() or OnComplete() will continue the sequence.
+    return;
+  }
+  if (header_writer_state_ != CacheWriterState::kCompleted) {
+    DCHECK_EQ(CacheWriterState::kWriting, header_writer_state_);
+    // OnWriteHeadersComplete() will continue the sequence.
+    return;
+  }
+
+  DCHECK_EQ(CacheWriterState::kNotStarted, body_writer_state_);
+  body_writer_state_ = CacheWriterState::kWriting;
+
+  network_watcher_.Watch(
+      network_consumer_.get(),
+      MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
+      base::BindRepeating(
+          &ServiceWorkerSingleScriptUpdateChecker::OnNetworkDataAvailable,
+          weak_factory_.GetWeakPtr()));
+  network_watcher_.ArmOrNotify();
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::OnNetworkDataAvailable(
+    MojoResult,
+    const mojo::HandleSignalsState& state) {
+  DCHECK_EQ(CacheWriterState::kCompleted, header_writer_state_);
+  DCHECK(network_consumer_.is_valid());
+  scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer;
+  uint32_t bytes_available = 0;
+  MojoResult result = network::MojoToNetPendingBuffer::BeginRead(
+      &network_consumer_, &pending_buffer, &bytes_available);
+  switch (result) {
+    case MOJO_RESULT_OK:
+      CompareData(std::move(pending_buffer), bytes_available);
+      return;
+    case MOJO_RESULT_FAILED_PRECONDITION:
+      // Closed by peer. This indicates all the data from the network service
+      // are read or there is an error. In the error case, the reason is
+      // notified via OnComplete().
+      if (network_loader_state_ == NetworkLoaderState::kCompleted) {
+        // Compare the cached data with an empty data to notify |cache_writer_|
+        // the end of the comparison.
+        CompareData(nullptr /* pending_buffer */, 0 /* bytes_available */);
+      }
+      return;
+    case MOJO_RESULT_SHOULD_WAIT:
+      network_watcher_.ArmOrNotify();
+      return;
+  }
+  NOTREACHED() << static_cast<int>(result);
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::CompareData(
+    scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
+    uint32_t bytes_to_compare) {
+  auto buffer = base::MakeRefCounted<net::WrappedIOBuffer>(
+      pending_buffer ? pending_buffer->buffer() : nullptr);
+
+  // Compare the network data and the stored data.
+  net::Error error = cache_writer_->MaybeWriteData(
+      buffer.get(), bytes_to_compare,
+      base::BindOnce(
+          &ServiceWorkerSingleScriptUpdateChecker::OnCompareDataComplete,
+          weak_factory_.GetWeakPtr(),
+          base::WrapRefCounted(pending_buffer.get()), bytes_to_compare));
+
+  if (pending_buffer) {
+    pending_buffer->CompleteRead(bytes_to_compare);
+    network_consumer_ = pending_buffer->ReleaseHandle();
+  }
+
+  if (error == net::ERR_IO_PENDING && !cache_writer_->is_pausing()) {
+    // OnCompareDataComplete() will be called asynchronously.
+    return;
+  }
+  // MaybeWriteData() doesn't run the callback if it finishes synchronously, so
+  // explicitly call it here.
+  OnCompareDataComplete(std::move(pending_buffer), bytes_to_compare, error);
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::OnCompareDataComplete(
+    scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
+    uint32_t bytes_written,
+    net::Error error) {
+  if (cache_writer_->is_pausing()) {
+    // |cache_writer_| can be pausing only when it finds difference between
+    // stored body and network body.
+    DCHECK_EQ(net::ERR_IO_PENDING, error);
+    Finish(true /* is_script_changed */);
+    return;
+  }
+  if (!pending_buffer || error != net::OK) {
+    Finish(false /* is_script_changed */);
+    return;
+  }
+  DCHECK(pending_buffer);
+  network_watcher_.ArmOrNotify();
+}
+
+void ServiceWorkerSingleScriptUpdateChecker::Finish(bool is_script_changed) {
+  if (is_script_changed) {
+    // TODO(momohatt): pass the necessary information to the version to update.
+  } else {
+    network_loader_.reset();
+    network_client_binding_.Close();
+    network_consumer_.reset();
+  }
+  network_watcher_.Cancel();
+  network_loader_state_ = NetworkLoaderState::kCompleted;
+  header_writer_state_ = CacheWriterState::kCompleted;
+  body_writer_state_ = CacheWriterState::kCompleted;
+
+  std::move(callback_).Run(is_script_changed);
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.h b/content/browser/service_worker/service_worker_single_script_update_checker.h
index 570b1eb3..d006ae26 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.h
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.h
@@ -5,21 +5,45 @@
 #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_SINGLE_SCRIPT_UPDATE_CHECKER_H_
 #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_SINGLE_SCRIPT_UPDATE_CHECKER_H_
 
+#include "content/browser/service_worker/service_worker_disk_cache.h"
+#include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
+namespace network {
+class MojoToNetPendingBuffer;
+class SharedURLLoaderFactory;
+}  // namespace network
+
 namespace content {
 
-class ServiceWorkerUpdateChecker;
+struct HttpResponseInfoIOBuffer;
+class ServiceWorkerCacheWriter;
 
-class ServiceWorkerSingleScriptUpdateChecker
+// Executes byte-for-byte update check of one script. This loads the script from
+// the network and compares it with the stored counterpart read from
+// |compare_reader|. The result will be passed as an argument of |callback|:
+// true when they are identical and false otherwise. When |callback| is
+// triggered, |cache_writer_| owned by |this| should be paused if the scripts
+// were not identical.
+class CONTENT_EXPORT ServiceWorkerSingleScriptUpdateChecker
     : public network::mojom::URLLoaderClient {
  public:
-  ServiceWorkerSingleScriptUpdateChecker(const GURL script_url,
-                                         int64_t resource_id,
-                                         ServiceWorkerUpdateChecker* owner);
-  ~ServiceWorkerSingleScriptUpdateChecker() override;
+  using ResultCallback = base::OnceCallback<void(bool)>;
 
-  void Start();
+  // Both |compare_reader| and |copy_reader| should be created from the same
+  // resource ID, and this ID should locate where the script specified with
+  // |url| is stored. |writer| should be created with a new resource ID.
+  ServiceWorkerSingleScriptUpdateChecker(
+      const GURL& url,
+      bool is_main_script,
+      scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
+      std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
+      std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
+      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      ResultCallback callback);
+
+  ~ServiceWorkerSingleScriptUpdateChecker() override;
 
   // network::mojom::URLLoaderClient override:
   void OnReceiveResponse(
@@ -37,9 +61,79 @@
   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
 
  private:
-  void CommitCompleted(bool is_script_changed);
+  enum class NetworkLoaderState {
+    kNotStarted,
+    kLoadingHeader,
+    kWaitingForBody,
+    kLoadingBody,
+    kCompleted
+  };
 
-  ServiceWorkerUpdateChecker* owner_;
+  enum class CacheWriterState { kNotStarted, kWriting, kCompleted };
+
+  void WriteHeaders(scoped_refptr<HttpResponseInfoIOBuffer> info_buffer);
+  void OnWriteHeadersComplete(net::Error error);
+
+  void MaybeStartNetworkConsumerHandleWatcher();
+  void OnNetworkDataAvailable(MojoResult,
+                              const mojo::HandleSignalsState& state);
+  void CompareData(
+      scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
+      uint32_t bytes_available);
+  void OnCompareDataComplete(
+      scoped_refptr<network::MojoToNetPendingBuffer> pending_buffer,
+      uint32_t bytes_written,
+      net::Error error);
+  void Finish(bool is_script_changed);
+
+  network::mojom::URLLoaderPtr network_loader_;
+  mojo::Binding<network::mojom::URLLoaderClient> network_client_binding_;
+  mojo::ScopedDataPipeConsumerHandle network_consumer_;
+  mojo::SimpleWatcher network_watcher_;
+
+  std::unique_ptr<ServiceWorkerCacheWriter> cache_writer_;
+  ResultCallback callback_;
+
+  // Represents the state of |network_loader_|.
+  // Corresponds to the steps described in the class comments.
+  //
+  // When response body exists:
+  // CreateLoaderAndStart(): kNotStarted -> kLoadingHeader
+  // OnReceiveResponse(): kLoadingHeader -> kWaitingForBody
+  // OnStartLoadingResponseBody(): kWaitingForBody -> kLoadingBody
+  // OnComplete(): kLoadingBody -> kCompleted
+  //
+  // When response body is empty:
+  // CreateLoaderAndStart(): kNotStarted -> kLoadingHeader
+  // OnReceiveResponse(): kLoadingHeader -> kWaitingForBody
+  // OnComplete(): kWaitingForBody -> kCompleted
+  NetworkLoaderState network_loader_state_ = NetworkLoaderState::kNotStarted;
+
+  // Represents the state of |cache_writer_|.
+  // Set to kWriting when it starts to send the header to |cache_writer_|, and
+  // set to kCompleted when the header has been sent.
+  //
+  // OnReceiveResponse(): kNotStarted -> kWriting (in WriteHeaders())
+  // OnWriteHeadersComplete(): kWriting -> kCompleted
+  CacheWriterState header_writer_state_ = CacheWriterState::kNotStarted;
+
+  // Represents the state of |cache_writer_| and |network_consumer_|.
+  // Set to kWriting when |this| starts watching |network_consumer_|, and set to
+  // kCompleted when |cache_writer_| reports any difference between the stored
+  // body and the network body, or the entire body is compared without any
+  // difference.
+  //
+  // When response body exists:
+  // OnStartLoadingResponseBody() && OnWriteHeadersComplete():
+  //     kNotStarted -> kWriting
+  // OnNetworkDataAvailable() && MOJO_RESULT_FAILED_PRECONDITION:
+  //     kWriting -> kCompleted
+  //
+  // When response body is empty:
+  // OnComplete(): kNotStarted -> kCompleted
+  CacheWriterState body_writer_state_ = CacheWriterState::kNotStarted;
+
+  base::WeakPtrFactory<ServiceWorkerSingleScriptUpdateChecker> weak_factory_;
 };
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
new file mode 100644
index 0000000..cc2188cd
--- /dev/null
+++ b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
@@ -0,0 +1,288 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_single_script_update_checker.h"
+
+#include <vector>
+#include "base/containers/queue.h"
+#include "base/run_loop.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_test_utils.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/http/http_util.h"
+#include "services/network/test/test_url_loader_factory.h"
+
+namespace content {
+namespace {
+
+constexpr char kScriptURL[] = "https://example.com/script.js";
+constexpr char kSuccessHeader[] =
+    "HTTP/1.1 200 OK\n"
+    "Content-Type: text/javascript\n\n";
+
+class ServiceWorkerSingleScriptUpdateCheckerTest : public testing::Test {
+ public:
+  ServiceWorkerSingleScriptUpdateCheckerTest()
+      : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+  ~ServiceWorkerSingleScriptUpdateCheckerTest() override = default;
+
+  ServiceWorkerStorage* storage() { return helper_->context()->storage(); }
+
+  void SetUp() override {
+    helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
+    base::RunLoop run_loop;
+    storage()->LazyInitializeForTest(run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
+  size_t TotalBytes(const std::vector<std::string>& data_chunks) {
+    size_t bytes = 0;
+    for (const auto& data : data_chunks)
+      bytes += data.size();
+    return bytes;
+  }
+
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker>
+  CreateSingleScriptUpdateChecker(
+      const char* url,
+      std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
+      std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
+      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      network::TestURLLoaderFactory* loader_factory,
+      base::Optional<bool>* out_script_changed) {
+    helper_->SetNetworkFactory(loader_factory);
+    return std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
+        GURL(url), true /* is_main_script */,
+        helper_->url_loader_factory_getter()->GetNetworkFactory(),
+        std::move(compare_reader), std::move(copy_reader), std::move(writer),
+        base::BindOnce(
+            [](base::Optional<bool>* out_script_changed, bool script_changed) {
+              *out_script_changed = script_changed;
+            },
+            out_script_changed));
+  }
+
+  std::unique_ptr<network::TestURLLoaderFactory> CreateLoaderFactoryWithRespone(
+      const GURL& url,
+      std::string header,
+      std::string body,
+      net::Error error) {
+    auto loader_factory = std::make_unique<network::TestURLLoaderFactory>();
+    network::ResourceResponseHead head;
+    head.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
+        net::HttpUtil::AssembleRawHeaders(header.c_str(), header.size()));
+    network::URLLoaderCompletionStatus status(error);
+    status.decoded_body_length = body.size();
+    loader_factory->AddResponse(url, head, body, status);
+    return loader_factory;
+  }
+
+ protected:
+  TestBrowserThreadBundle thread_bundle_;
+  std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerSingleScriptUpdateCheckerTest);
+};
+
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, Identical_SingleSyncRead) {
+  // Response body from the network.
+  const std::string body_from_net("abcdef");
+
+  // Stored data for |kScriptURL|.
+  const std::vector<std::string> body_from_storage{body_from_net};
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kScriptURL), kSuccessHeader,
+                                     body_from_net, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+  MockServiceWorkerResponseReader* compare_reader_rawptr = compare_reader.get();
+  compare_reader->ExpectReadOk(body_from_storage, TotalBytes(body_from_storage),
+                               false /* async */);
+
+  base::Optional<bool> script_changed;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(kScriptURL, std::move(compare_reader),
+                                      std::move(copy_reader), std::move(writer),
+                                      loader_factory.get(), &script_changed);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(script_changed.has_value());
+  EXPECT_FALSE(script_changed.value());
+  EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
+}
+
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, Different_SingleSyncRead) {
+  // Response body from the network.
+  const std::string body_from_net("abcdef");
+
+  // Stored data for |kScriptURL|.
+  const std::vector<std::string> body_from_storage{"abxx"};
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kScriptURL), kSuccessHeader,
+                                     body_from_net, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+  MockServiceWorkerResponseReader* compare_reader_rawptr = compare_reader.get();
+  compare_reader->ExpectReadOk(body_from_storage, TotalBytes(body_from_storage),
+                               false /* async */);
+
+  base::Optional<bool> script_changed;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(kScriptURL, std::move(compare_reader),
+                                      std::move(copy_reader), std::move(writer),
+                                      loader_factory.get(), &script_changed);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(script_changed.has_value());
+  EXPECT_TRUE(script_changed.value());
+  EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
+}
+
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, Different_MultipleSyncRead) {
+  // Response body from the network.
+  const std::string body_from_net("abcdef");
+
+  // Stored data for |kScriptURL| (the data for compare reader).
+  // The comparison should stop in the second block of data.
+  const std::vector<std::string> body_from_storage{"ab", "cx"};
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kScriptURL), kSuccessHeader,
+                                     body_from_net, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+  MockServiceWorkerResponseReader* compare_reader_rawptr = compare_reader.get();
+  compare_reader->ExpectReadOk(body_from_storage, TotalBytes(body_from_storage),
+                               false /* async */);
+
+  base::Optional<bool> script_changed;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(kScriptURL, std::move(compare_reader),
+                                      std::move(copy_reader), std::move(writer),
+                                      loader_factory.get(), &script_changed);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(script_changed.has_value());
+  EXPECT_TRUE(script_changed.value());
+  EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
+}
+
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, NetworkDataLong_SyncRead) {
+  // Response body from the network.
+  const std::string body_from_net("abcdef");
+
+  // Stored data for |kScriptURL| (the data for compare reader).
+  const std::vector<std::string> body_from_storage{"ab", "cd", ""};
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kScriptURL), kSuccessHeader,
+                                     body_from_net, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+  MockServiceWorkerResponseReader* compare_reader_rawptr = compare_reader.get();
+  compare_reader->ExpectReadOk(body_from_storage, TotalBytes(body_from_storage),
+                               false /* async */);
+
+  base::Optional<bool> script_changed;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(kScriptURL, std::move(compare_reader),
+                                      std::move(copy_reader), std::move(writer),
+                                      loader_factory.get(), &script_changed);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(script_changed.has_value());
+  EXPECT_TRUE(script_changed.value());
+  EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
+}
+
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, NetworkDataShort_SyncRead) {
+  // Response body from the network.
+  const std::string body_from_net("abcdef");
+
+  // Stored data for |kScriptURL| (the data for compare reader).
+  const std::vector<std::string> body_in_storage{"ab", "cd", "ef", "gh"};
+
+  // Stored data that will actually be read from the compare reader.
+  // The last 2 bytes of |body_in_storage| won't be read.
+  const std::vector<std::string> body_read_from_storage{"ab", "cd", "ef"};
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kScriptURL), kSuccessHeader,
+                                     body_from_net, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+  MockServiceWorkerResponseReader* compare_reader_rawptr = compare_reader.get();
+  compare_reader->ExpectReadOk(body_read_from_storage,
+                               TotalBytes(body_in_storage), false /* async */);
+
+  base::Optional<bool> script_changed;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(kScriptURL, std::move(compare_reader),
+                                      std::move(copy_reader), std::move(writer),
+                                      loader_factory.get(), &script_changed);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(script_changed.has_value());
+  EXPECT_TRUE(script_changed.value());
+  EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
+}
+
+TEST_F(ServiceWorkerSingleScriptUpdateCheckerTest, Identical_SingleAsyncRead) {
+  // Response body from the network.
+  const std::string body_from_net("abcdef");
+
+  // Stored data for |kScriptURL| (the data for compare reader).
+  const std::vector<std::string> body_from_storage{body_from_net};
+
+  std::unique_ptr<network::TestURLLoaderFactory> loader_factory =
+      CreateLoaderFactoryWithRespone(GURL(kScriptURL), kSuccessHeader,
+                                     body_from_net, net::OK);
+
+  auto compare_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto copy_reader = std::make_unique<MockServiceWorkerResponseReader>();
+  auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
+  MockServiceWorkerResponseReader* compare_reader_rawptr = compare_reader.get();
+  compare_reader->ExpectReadOk(body_from_storage, TotalBytes(body_from_storage),
+                               true /* async */);
+
+  base::Optional<bool> script_changed;
+  std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> checker =
+      CreateSingleScriptUpdateChecker(kScriptURL, std::move(compare_reader),
+                                      std::move(copy_reader), std::move(writer),
+                                      loader_factory.get(), &script_changed);
+
+  // Update check stops in WriteHeader() due to the asynchronous read of the
+  // |compare_reader|.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(script_changed.has_value());
+
+  // Continue the update check and trigger OnWriteHeadersComplete(). The resumed
+  // update check stops again at CompareData().
+  compare_reader_rawptr->CompletePendingRead();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(script_changed.has_value());
+
+  // Continue the update check and trigger OnCompareDataComplete(). This will
+  // finish the entire update check.
+  compare_reader_rawptr->CompletePendingRead();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(script_changed.has_value());
+  EXPECT_FALSE(script_changed.value());
+  EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
+}
+
+}  // namespace
+}  // namespace content
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
index 05e9cb7..344734b 100644
--- a/content/browser/service_worker/service_worker_test_utils.cc
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -281,4 +281,178 @@
                                                body.size());
 }
 
+MockServiceWorkerResponseReader::MockServiceWorkerResponseReader()
+    : ServiceWorkerResponseReader(0 /* resource_id */,
+                                  base::WeakPtr<AppCacheDiskCacheInterface>()) {
+}
+
+MockServiceWorkerResponseReader::~MockServiceWorkerResponseReader() {}
+
+void MockServiceWorkerResponseReader::ReadInfo(
+    HttpResponseInfoIOBuffer* info_buf,
+    OnceCompletionCallback callback) {
+  DCHECK(!expected_reads_.empty());
+  ExpectedRead expected = expected_reads_.front();
+  EXPECT_TRUE(expected.info);
+  if (expected.async) {
+    pending_info_ = info_buf;
+    pending_callback_ = std::move(callback);
+  } else {
+    expected_reads_.pop();
+    info_buf->response_data_size = expected.len;
+    std::move(callback).Run(expected.result);
+  }
+}
+
+void MockServiceWorkerResponseReader::ReadData(
+    net::IOBuffer* buf,
+    int buf_len,
+    OnceCompletionCallback callback) {
+  DCHECK(!expected_reads_.empty());
+  ExpectedRead expected = expected_reads_.front();
+  EXPECT_FALSE(expected.info);
+  if (expected.async) {
+    pending_callback_ = std::move(callback);
+    pending_buffer_ = buf;
+    pending_buffer_len_ = static_cast<size_t>(buf_len);
+  } else {
+    expected_reads_.pop();
+    if (expected.len > 0) {
+      size_t to_read = std::min(static_cast<size_t>(buf_len), expected.len);
+      memcpy(buf->data(), expected.data, to_read);
+    }
+    std::move(callback).Run(expected.result);
+  }
+}
+
+void MockServiceWorkerResponseReader::ExpectReadInfo(size_t len,
+                                                     bool async,
+                                                     int result) {
+  expected_reads_.push(ExpectedRead(len, async, result));
+}
+
+void MockServiceWorkerResponseReader::ExpectReadInfoOk(size_t len, bool async) {
+  expected_reads_.push(ExpectedRead(len, async, len));
+}
+
+void MockServiceWorkerResponseReader::ExpectReadData(const char* data,
+                                                     size_t len,
+                                                     bool async,
+                                                     int result) {
+  expected_reads_.push(ExpectedRead(data, len, async, result));
+}
+
+void MockServiceWorkerResponseReader::ExpectReadDataOk(const std::string& data,
+                                                       bool async) {
+  expected_reads_.push(
+      ExpectedRead(data.data(), data.size(), async, data.size()));
+}
+
+void MockServiceWorkerResponseReader::ExpectReadOk(
+    const std::vector<std::string>& stored_data,
+    const size_t bytes_stored,
+    const bool async) {
+  ExpectReadInfoOk(bytes_stored, async);
+  for (const auto& data : stored_data)
+    ExpectReadDataOk(data, async);
+}
+
+void MockServiceWorkerResponseReader::CompletePendingRead() {
+  DCHECK(!expected_reads_.empty());
+  ExpectedRead expected = expected_reads_.front();
+  expected_reads_.pop();
+  EXPECT_TRUE(expected.async);
+  if (expected.info) {
+    pending_info_->response_data_size = expected.len;
+  } else {
+    size_t to_read = std::min(pending_buffer_len_, expected.len);
+    if (to_read > 0)
+      memcpy(pending_buffer_->data(), expected.data, to_read);
+  }
+  pending_info_ = nullptr;
+  pending_buffer_ = nullptr;
+  OnceCompletionCallback callback = std::move(pending_callback_);
+  pending_callback_.Reset();
+  std::move(callback).Run(expected.result);
+}
+
+MockServiceWorkerResponseWriter::MockServiceWorkerResponseWriter()
+    : ServiceWorkerResponseWriter(0 /* resource_id */,
+                                  base::WeakPtr<AppCacheDiskCacheInterface>()),
+      info_written_(0),
+      data_written_(0) {}
+
+MockServiceWorkerResponseWriter::~MockServiceWorkerResponseWriter() = default;
+
+void MockServiceWorkerResponseWriter::WriteInfo(
+    HttpResponseInfoIOBuffer* info_buf,
+    OnceCompletionCallback callback) {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  EXPECT_TRUE(write.is_info);
+  if (write.result > 0) {
+    EXPECT_EQ(write.length, static_cast<size_t>(info_buf->response_data_size));
+    info_written_ += info_buf->response_data_size;
+  }
+  if (!write.async) {
+    expected_writes_.pop();
+    std::move(callback).Run(write.result);
+  } else {
+    pending_callback_ = std::move(callback);
+  }
+}
+
+void MockServiceWorkerResponseWriter::WriteData(
+    net::IOBuffer* buf,
+    int buf_len,
+    OnceCompletionCallback callback) {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  EXPECT_FALSE(write.is_info);
+  if (write.result > 0) {
+    EXPECT_EQ(write.length, static_cast<size_t>(buf_len));
+    data_written_ += buf_len;
+  }
+  if (!write.async) {
+    expected_writes_.pop();
+    std::move(callback).Run(write.result);
+  } else {
+    pending_callback_ = std::move(callback);
+  }
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteInfoOk(size_t length,
+                                                        bool async) {
+  ExpectWriteInfo(length, async, length);
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteDataOk(size_t length,
+                                                        bool async) {
+  ExpectWriteData(length, async, length);
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteInfo(size_t length,
+                                                      bool async,
+                                                      int result) {
+  DCHECK_NE(net::ERR_IO_PENDING, result);
+  ExpectedWrite expected(true, length, async, result);
+  expected_writes_.push(expected);
+}
+
+void MockServiceWorkerResponseWriter::ExpectWriteData(size_t length,
+                                                      bool async,
+                                                      int result) {
+  DCHECK_NE(net::ERR_IO_PENDING, result);
+  ExpectedWrite expected(false, length, async, result);
+  expected_writes_.push(expected);
+}
+
+void MockServiceWorkerResponseWriter::CompletePendingWrite() {
+  DCHECK(!expected_writes_.empty());
+  ExpectedWrite write = expected_writes_.front();
+  DCHECK(write.async);
+  expected_writes_.pop();
+  std::move(pending_callback_).Run(write.result);
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_test_utils.h b/content/browser/service_worker/service_worker_test_utils.h
index dba5946..aa81bbf 100644
--- a/content/browser/service_worker/service_worker_test_utils.h
+++ b/content/browser/service_worker/service_worker_test_utils.h
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/task/post_task.h"
 #include "content/browser/service_worker/service_worker_database.h"
+#include "content/browser/service_worker/service_worker_disk_cache.h"
 #include "content/common/service_worker/service_worker_provider.mojom.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -165,6 +166,154 @@
     const std::string& meta_data,
     base::OnceClosure callback);
 
+// A test implementation of ServiceWorkerResponseReader.
+//
+// This class exposes the ability to expect reads (see ExpectRead*() below).
+// Each call to ReadInfo() or ReadData() consumes another expected read, in the
+// order those reads were expected, so:
+//    reader->ExpectReadInfoOk(5, false);
+//    reader->ExpectReadDataOk("abcdef", false);
+//    reader->ExpectReadDataOk("ghijkl", false);
+// Expects these calls, in this order:
+//    reader->ReadInfo(...);  // reader writes 5 into
+//                            // |info_buf->response_data_size|
+//    reader->ReadData(...);  // reader writes "abcdef" into |buf|
+//    reader->ReadData(...);  // reader writes "ghijkl" into |buf|
+// If an unexpected call happens, this class DCHECKs.
+// If an expected read is marked "async", it will not complete immediately, but
+// must be completed by the test using CompletePendingRead().
+// These is a convenience method AllExpectedReadsDone() which returns whether
+// there are any expected reads that have not yet happened.
+class MockServiceWorkerResponseReader : public ServiceWorkerResponseReader {
+ public:
+  MockServiceWorkerResponseReader();
+  ~MockServiceWorkerResponseReader() override;
+
+  // ServiceWorkerResponseReader overrides
+  void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
+                OnceCompletionCallback callback) override;
+  void ReadData(net::IOBuffer* buf,
+                int buf_len,
+                OnceCompletionCallback callback) override;
+
+  // Test helpers. ExpectReadInfo() and ExpectReadData() give precise control
+  // over both the data to be written and the result to return.
+  // ExpectReadInfoOk() and ExpectReadDataOk() are convenience functions for
+  // expecting successful reads, which always have their length as their result.
+
+  // Expect a call to ReadInfo() on this reader. For these functions, |len| will
+  // be used as |response_data_size|, not as the length of this particular read.
+  void ExpectReadInfo(size_t len, bool async, int result);
+  void ExpectReadInfoOk(size_t len, bool async);
+
+  // Expect a call to ReadData() on this reader. For these functions, |len| is
+  // the length of the data to be written back; in ExpectReadDataOk(), |len| is
+  // implicitly the length of |data|.
+  void ExpectReadData(const char* data, size_t len, bool async, int result);
+  void ExpectReadDataOk(const std::string& data, bool async);
+
+  // Convenient method for calling ExpectReadInfoOk() with the length being
+  // |bytes_stored|, and ExpectReadDataOk() for each element of |stored_data|.
+  void ExpectReadOk(const std::vector<std::string>& stored_data,
+                    const size_t bytes_stored,
+                    const bool async);
+
+  // Complete a pending async read. It is an error to call this function without
+  // a pending async read (ie, a previous call to ReadInfo() or ReadData()
+  // having not run its callback yet).
+  void CompletePendingRead();
+
+  // Returns whether all expected reads have occurred.
+  bool AllExpectedReadsDone() { return expected_reads_.size() == 0; }
+
+ private:
+  struct ExpectedRead {
+    ExpectedRead(size_t len, bool async, int result)
+        : data(nullptr), len(len), info(true), async(async), result(result) {}
+    ExpectedRead(const char* data, size_t len, bool async, int result)
+        : data(data), len(len), info(false), async(async), result(result) {}
+    const char* data;
+    size_t len;
+    bool info;
+    bool async;
+    int result;
+  };
+
+  base::queue<ExpectedRead> expected_reads_;
+  scoped_refptr<net::IOBuffer> pending_buffer_;
+  size_t pending_buffer_len_;
+  scoped_refptr<HttpResponseInfoIOBuffer> pending_info_;
+  OnceCompletionCallback pending_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockServiceWorkerResponseReader);
+};
+
+// A test implementation of ServiceWorkerResponseWriter.
+//
+// This class exposes the ability to expect writes (see ExpectWrite*Ok() below).
+// Each write to this class via WriteInfo() or WriteData() consumes another
+// expected write, in the order they were added, so:
+//   writer->ExpectWriteInfoOk(5, false);
+//   writer->ExpectWriteDataOk(6, false);
+//   writer->ExpectWriteDataOk(6, false);
+// Expects these calls, in this order:
+//   writer->WriteInfo(...);  // checks that |buf->response_data_size| == 5
+//   writer->WriteData(...);  // checks that 6 bytes are being written
+//   writer->WriteData(...);  // checks that another 6 bytes are being written
+// If this class receives an unexpected call to WriteInfo() or WriteData(), it
+// DCHECKs.
+// Expected writes marked async do not complete synchronously, but rather return
+// without running their callback and need to be completed with
+// CompletePendingWrite().
+// A convenience method AllExpectedWritesDone() is exposed so tests can ensure
+// that all expected writes have been consumed by matching calls to WriteInfo()
+// or WriteData().
+class MockServiceWorkerResponseWriter : public ServiceWorkerResponseWriter {
+ public:
+  MockServiceWorkerResponseWriter();
+  ~MockServiceWorkerResponseWriter() override;
+
+  // ServiceWorkerResponseWriter overrides
+  void WriteInfo(HttpResponseInfoIOBuffer* info_buf,
+                 OnceCompletionCallback callback) override;
+  void WriteData(net::IOBuffer* buf,
+                 int buf_len,
+                 OnceCompletionCallback callback) override;
+
+  // Enqueue expected writes.
+  void ExpectWriteInfoOk(size_t len, bool async);
+  void ExpectWriteInfo(size_t len, bool async, int result);
+  void ExpectWriteDataOk(size_t len, bool async);
+  void ExpectWriteData(size_t len, bool async, int result);
+
+  // Complete a pending asynchronous write. This method DCHECKs unless there is
+  // a pending write (a write for which WriteInfo() or WriteData() has been
+  // called but the callback has not yet been run).
+  void CompletePendingWrite();
+
+  // Returns whether all expected reads have been consumed.
+  bool AllExpectedWritesDone() { return expected_writes_.size() == 0; }
+
+ private:
+  struct ExpectedWrite {
+    ExpectedWrite(bool is_info, size_t length, bool async, int result)
+        : is_info(is_info), length(length), async(async), result(result) {}
+    bool is_info;
+    size_t length;
+    bool async;
+    int result;
+  };
+
+  base::queue<ExpectedWrite> expected_writes_;
+
+  size_t info_written_;
+  size_t data_written_;
+
+  OnceCompletionCallback pending_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockServiceWorkerResponseWriter);
+};
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_TEST_UTILS_H_
diff --git a/content/browser/service_worker/service_worker_update_checker.cc b/content/browser/service_worker/service_worker_update_checker.cc
index 752e7343..1156b0e4 100644
--- a/content/browser/service_worker/service_worker_update_checker.cc
+++ b/content/browser/service_worker/service_worker_update_checker.cc
@@ -4,11 +4,20 @@
 
 #include "content/browser/service_worker/service_worker_update_checker.h"
 
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_storage.h"
+#include "content/browser/service_worker/service_worker_version.h"
+
 namespace content {
 
 ServiceWorkerUpdateChecker::ServiceWorkerUpdateChecker(
-    std::vector<ServiceWorkerDatabase::ResourceRecord> scripts_to_compare)
-    : scripts_to_compare_(std::move(scripts_to_compare)), weak_factory_(this) {}
+    std::vector<ServiceWorkerDatabase::ResourceRecord> scripts_to_compare,
+    scoped_refptr<ServiceWorkerVersion> version_to_update,
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory)
+    : scripts_to_compare_(std::move(scripts_to_compare)),
+      version_to_update_(std::move(version_to_update)),
+      loader_factory_(std::move(loader_factory)),
+      weak_factory_(this) {}
 
 ServiceWorkerUpdateChecker::~ServiceWorkerUpdateChecker() = default;
 
@@ -45,9 +54,21 @@
   DCHECK_NE(kInvalidServiceWorkerResourceId, script.resource_id)
       << "All the target scripts should be stored in the storage.";
 
+  bool is_main_script = script.url == version_to_update_->script_url();
+  ServiceWorkerStorage* storage = version_to_update_->context()->storage();
+
+  // We need two identical readers for comparing and reading the resource for
+  // |script.resource_id| from the storage.
+  auto compare_reader = storage->CreateResponseReader(script.resource_id);
+  auto copy_reader = storage->CreateResponseReader(script.resource_id);
+
+  auto writer = storage->CreateResponseWriter(storage->NewResourceId());
+
   running_checker_ = std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
-      script.url, script.resource_id, this);
-  running_checker_->Start();
+      script.url, is_main_script, loader_factory_, std::move(compare_reader),
+      std::move(copy_reader), std::move(writer),
+      base::BindOnce(&ServiceWorkerUpdateChecker::OnOneUpdateCheckFinished,
+                     weak_factory_.GetWeakPtr()));
 }
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_update_checker.h b/content/browser/service_worker/service_worker_update_checker.h
index 4d7a6877..2bd0b08 100644
--- a/content/browser/service_worker/service_worker_update_checker.h
+++ b/content/browser/service_worker/service_worker_update_checker.h
@@ -9,14 +9,22 @@
 #include "content/browser/service_worker/service_worker_database.h"
 #include "content/browser/service_worker/service_worker_single_script_update_checker.h"
 
+namespace network {
+class SharedURLLoaderFactory;
+}
+
 namespace content {
 
+class ServiceWorkerVersion;
+
 class ServiceWorkerUpdateChecker {
  public:
   using UpdateStatusCallback = base::OnceCallback<void(bool)>;
 
   ServiceWorkerUpdateChecker(
-      std::vector<ServiceWorkerDatabase::ResourceRecord> scripts_to_compare);
+      std::vector<ServiceWorkerDatabase::ResourceRecord> scripts_to_compare,
+      scoped_refptr<ServiceWorkerVersion> version_to_update,
+      scoped_refptr<network::SharedURLLoaderFactory> loader_factory);
   ~ServiceWorkerUpdateChecker();
 
   // |callback| is always triggered when Start() finishes. If the scripts are
@@ -32,10 +40,14 @@
   std::vector<ServiceWorkerDatabase::ResourceRecord> scripts_to_compare_;
   size_t scripts_compared_ = 0;
 
+  // The version which triggered this update.
+  scoped_refptr<ServiceWorkerVersion> version_to_update_;
+
   std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker> running_checker_;
 
   UpdateStatusCallback callback_;
 
+  scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
   base::WeakPtrFactory<ServiceWorkerUpdateChecker> weak_factory_;
 };
 
diff --git a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
index bf516f3c4..fd28255 100644
--- a/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
+++ b/content/browser/web_package/signed_exchange_request_handler_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/time/time.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
 #include "content/browser/web_package/signed_exchange_handler.h"
+#include "content/browser/web_package/signed_exchange_utils.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
@@ -32,13 +33,16 @@
 #include "content/shell/browser/shell.h"
 #include "net/cert/cert_verify_result.h"
 #include "net/cert/mock_cert_verifier.h"
+#include "net/dns/mock_host_resolver.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
 #include "net/test/test_data_directory.h"
 #include "net/test/url_request/url_request_mock_http_job.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_interceptor.h"
+#include "services/network/loader_util.h"
 #include "services/network/public/cpp/features.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 
@@ -183,14 +187,6 @@
   mock_cert_verifier()->AddResultForCertAndHost(
       original_cert, "test.example.org", dummy_result, net::OK);
 
-  embedded_test_server()->RegisterRequestMonitor(
-      base::BindRepeating([](const net::test_server::HttpRequest& request) {
-        if (request.relative_url == "/sxg/test.example.org_test.sxg") {
-          const auto& accept_value = request.headers.find("accept")->second;
-          EXPECT_THAT(accept_value,
-                      ::testing::HasSubstr("application/signed-exchange;v=b2"));
-        }
-      }));
   embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");
@@ -330,14 +326,6 @@
   InstallUrlInterceptor(GURL("https://test.example.org/test/"),
                         "content/test/data/sxg/fallback.html");
 
-  embedded_test_server()->RegisterRequestMonitor(
-      base::BindRepeating([](const net::test_server::HttpRequest& request) {
-        if (request.relative_url == "/sxg/test.example.org_test.sxg") {
-          const auto& accept_value = request.headers.find("accept")->second;
-          EXPECT_THAT(accept_value,
-                      ::testing::HasSubstr("application/signed-exchange;v=b2"));
-        }
-      }));
   embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url = embedded_test_server()->GetURL("/sxg/test.example.org_test.sxg");
@@ -357,4 +345,190 @@
                                        SignedExchangeLoadResult::kOCSPError, 1);
 }
 
+struct SignedExchangeAcceptHeaderBrowserTestParam {
+  SignedExchangeAcceptHeaderBrowserTestParam(bool sxg_enabled,
+                                             bool sxg_origin_trial_enabled,
+                                             bool sxg_accept_header_enabled)
+      : sxg_enabled(sxg_enabled),
+        sxg_origin_trial_enabled(sxg_origin_trial_enabled),
+        sxg_accept_header_enabled(sxg_accept_header_enabled) {}
+  const bool sxg_enabled;
+  const bool sxg_origin_trial_enabled;
+  const bool sxg_accept_header_enabled;
+};
+
+class SignedExchangeAcceptHeaderBrowserTest
+    : public ContentBrowserTest,
+      public testing::WithParamInterface<
+          SignedExchangeAcceptHeaderBrowserTestParam> {
+ public:
+  using self = SignedExchangeAcceptHeaderBrowserTest;
+  SignedExchangeAcceptHeaderBrowserTest()
+      : enabled_https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
+        disabled_https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+  ~SignedExchangeAcceptHeaderBrowserTest() override = default;
+
+ protected:
+  void SetUp() override {
+    std::vector<base::Feature> enable_features;
+    if (GetParam().sxg_enabled)
+      enable_features.push_back(features::kSignedHTTPExchange);
+    if (GetParam().sxg_origin_trial_enabled)
+      enable_features.push_back(features::kSignedHTTPExchangeOriginTrial);
+    feature_list_.InitWithFeatures(enable_features, {});
+
+    enabled_https_server_.ServeFilesFromSourceDirectory("content/test/data");
+    enabled_https_server_.RegisterRequestHandler(
+        base::BindRepeating(&self::RedirectResponseHandler));
+    enabled_https_server_.RegisterRequestMonitor(
+        base::BindRepeating(&self::MonitorRequest, base::Unretained(this)));
+    ASSERT_TRUE(enabled_https_server_.Start());
+
+    disabled_https_server_.ServeFilesFromSourceDirectory("content/test/data");
+    disabled_https_server_.RegisterRequestHandler(
+        base::BindRepeating(&self::RedirectResponseHandler));
+    disabled_https_server_.RegisterRequestMonitor(
+        base::BindRepeating(&self::MonitorRequest, base::Unretained(this)));
+    ASSERT_TRUE(disabled_https_server_.Start());
+
+    if (GetParam().sxg_accept_header_enabled) {
+      std::map<std::string, std::string> feature_parameters;
+      feature_parameters["OriginsList"] =
+          base::StringPrintf("127.0.0.1:%u", enabled_https_server_.port());
+      feature_list_for_accept_header_.InitAndEnableFeatureWithParameters(
+          features::kSignedHTTPExchangeAcceptHeader, feature_parameters);
+    }
+    ContentBrowserTest::SetUp();
+  }
+
+  void NavigateAndWaitForTitle(const GURL& url, const std::string title) {
+    base::string16 expected_title = base::ASCIIToUTF16(title);
+    TitleWatcher title_watcher(shell()->web_contents(), expected_title);
+    NavigateToURL(shell(), url);
+    EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
+  }
+
+  bool ShouldHaveSXGAcceptHeaderInEnabledOrigin() {
+    return GetParam().sxg_enabled || (GetParam().sxg_origin_trial_enabled &&
+                                      GetParam().sxg_accept_header_enabled);
+  }
+
+  void CheckNavigationAcceptHeader(const GURL& url, bool should_have_sxg) {
+    if (should_have_sxg) {
+      EXPECT_EQ(GetInterceptedAcceptHeader(url),
+                std::string(network::kFrameAcceptHeader) +
+                    std::string(kAcceptHeaderSignedExchangeSuffix));
+    } else {
+      EXPECT_EQ(GetInterceptedAcceptHeader(url),
+                std::string(network::kFrameAcceptHeader));
+    }
+  }
+
+  net::EmbeddedTestServer enabled_https_server_;
+  net::EmbeddedTestServer disabled_https_server_;
+
+ private:
+  static std::unique_ptr<net::test_server::HttpResponse>
+  RedirectResponseHandler(const net::test_server::HttpRequest& request) {
+    if (!base::StartsWith(request.relative_url, "/r?",
+                          base::CompareCase::SENSITIVE)) {
+      return std::unique_ptr<net::test_server::HttpResponse>();
+    }
+    std::unique_ptr<net::test_server::BasicHttpResponse> http_response(
+        new net::test_server::BasicHttpResponse);
+    http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
+    http_response->AddCustomHeader("Location", request.relative_url.substr(3));
+    return std::move(http_response);
+  }
+
+  void MonitorRequest(const net::test_server::HttpRequest& request) {
+    const auto it = request.headers.find(std::string(network::kAcceptHeader));
+    if (it == request.headers.end())
+      return;
+    url_accept_header_map_[request.base_url.Resolve(request.relative_url)] =
+        it->second;
+  }
+
+  std::string GetInterceptedAcceptHeader(const GURL& url) {
+    return url_accept_header_map_[url];
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  base::test::ScopedFeatureList feature_list_for_accept_header_;
+
+  std::map<GURL, std::string> url_accept_header_map_;
+};
+
+IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest, EnabledOrigin) {
+  const GURL enabled_test_url = enabled_https_server_.GetURL("/sxg/test.html");
+  EXPECT_EQ(ShouldHaveSXGAcceptHeaderInEnabledOrigin(),
+            signed_exchange_utils::ShouldAdvertiseAcceptHeader(
+                url::Origin::Create(enabled_test_url)));
+  NavigateAndWaitForTitle(enabled_test_url, enabled_test_url.spec());
+  CheckNavigationAcceptHeader(enabled_test_url,
+                              ShouldHaveSXGAcceptHeaderInEnabledOrigin());
+}
+
+IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest, DisabledOrigin) {
+  const GURL disabled_test_url =
+      disabled_https_server_.GetURL("/sxg/test.html");
+  EXPECT_EQ(GetParam().sxg_enabled,
+            signed_exchange_utils::ShouldAdvertiseAcceptHeader(
+                url::Origin::Create(disabled_test_url)));
+
+  NavigateAndWaitForTitle(disabled_test_url, disabled_test_url.spec());
+  CheckNavigationAcceptHeader(disabled_test_url, GetParam().sxg_enabled);
+}
+
+IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
+                       RedirectEnabledToDisabledToEnabled) {
+  const GURL enabled_test_url = enabled_https_server_.GetURL("/sxg/test.html");
+  const GURL redirect_disabled_to_enabled_url =
+      disabled_https_server_.GetURL("/r?" + enabled_test_url.spec());
+  const GURL redirect_enabled_to_disabled_to_enabled_url =
+      enabled_https_server_.GetURL("/r?" +
+                                   redirect_disabled_to_enabled_url.spec());
+  NavigateAndWaitForTitle(redirect_enabled_to_disabled_to_enabled_url,
+                          enabled_test_url.spec());
+
+  CheckNavigationAcceptHeader(redirect_enabled_to_disabled_to_enabled_url,
+                              ShouldHaveSXGAcceptHeaderInEnabledOrigin());
+  CheckNavigationAcceptHeader(redirect_disabled_to_enabled_url,
+                              GetParam().sxg_enabled);
+  CheckNavigationAcceptHeader(enabled_test_url,
+                              ShouldHaveSXGAcceptHeaderInEnabledOrigin());
+}
+
+IN_PROC_BROWSER_TEST_P(SignedExchangeAcceptHeaderBrowserTest,
+                       RedirectDisabledToEnabledToDisabled) {
+  const GURL disabled_test_url =
+      disabled_https_server_.GetURL("/sxg/test.html");
+  const GURL redirect_enabled_to_disabled_url =
+      enabled_https_server_.GetURL("/r?" + disabled_test_url.spec());
+  const GURL redirect_disabled_to_enabled_to_disabled_url =
+      disabled_https_server_.GetURL("/r?" +
+                                    redirect_enabled_to_disabled_url.spec());
+  NavigateAndWaitForTitle(redirect_disabled_to_enabled_to_disabled_url,
+                          disabled_test_url.spec());
+
+  CheckNavigationAcceptHeader(redirect_disabled_to_enabled_to_disabled_url,
+                              GetParam().sxg_enabled);
+  CheckNavigationAcceptHeader(redirect_enabled_to_disabled_url,
+                              ShouldHaveSXGAcceptHeaderInEnabledOrigin());
+  CheckNavigationAcceptHeader(disabled_test_url, GetParam().sxg_enabled);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    SignedExchangeAcceptHeaderBrowserTest,
+    SignedExchangeAcceptHeaderBrowserTest,
+    testing::Values(
+        SignedExchangeAcceptHeaderBrowserTestParam(false, false, false),
+        SignedExchangeAcceptHeaderBrowserTestParam(false, false, true),
+        SignedExchangeAcceptHeaderBrowserTestParam(false, true, false),
+        SignedExchangeAcceptHeaderBrowserTestParam(false, true, true),
+        SignedExchangeAcceptHeaderBrowserTestParam(true, false, false),
+        SignedExchangeAcceptHeaderBrowserTestParam(true, false, true),
+        SignedExchangeAcceptHeaderBrowserTestParam(true, true, false),
+        SignedExchangeAcceptHeaderBrowserTestParam(true, true, true)));
+
 }  // namespace content
diff --git a/content/browser/web_package/signed_exchange_utils.cc b/content/browser/web_package/signed_exchange_utils.cc
index ed020c6d..a52b7b0 100644
--- a/content/browser/web_package/signed_exchange_utils.cc
+++ b/content/browser/web_package/signed_exchange_utils.cc
@@ -48,9 +48,29 @@
 
 }  //  namespace
 
+bool NeedToCheckRedirectedURLForAcceptHeader() {
+  // When SignedHTTPExchange is enabled, the SignedExchange accept header must
+  // be sent to all origins. So we don't need to check the redirected URL.
+  return !base::FeatureList::IsEnabled(features::kSignedHTTPExchange) &&
+         base::FeatureList::IsEnabled(
+             features::kSignedHTTPExchangeOriginTrial) &&
+         base::FeatureList::IsEnabled(
+             features::kSignedHTTPExchangeAcceptHeader);
+}
+
 bool ShouldAdvertiseAcceptHeader(const url::Origin& origin) {
-  if (!base::FeatureList::IsEnabled(features::kSignedHTTPExchangeAcceptHeader))
+  // When SignedHTTPExchange is enabled, we must send the SignedExchange accept
+  // header to all origins.
+  if (base::FeatureList::IsEnabled(features::kSignedHTTPExchange))
+    return true;
+  // When SignedHTTPExchangeOriginTrial is not enabled or
+  // SignedHTTPExchangeAcceptHeader is not enabled, we must not send the
+  // SignedExchange accept header.
+  if (!base::FeatureList::IsEnabled(features::kSignedHTTPExchangeOriginTrial) ||
+      !base::FeatureList::IsEnabled(
+          features::kSignedHTTPExchangeAcceptHeader)) {
     return false;
+  }
 
   // |origins_list| is initialized in a thread-safe manner.
   // Querying OriginsList::Match() should be safe since it's read-only access.
diff --git a/content/browser/web_package/signed_exchange_utils.h b/content/browser/web_package/signed_exchange_utils.h
index 80c5171..bbcad0a 100644
--- a/content/browser/web_package/signed_exchange_utils.h
+++ b/content/browser/web_package/signed_exchange_utils.h
@@ -38,9 +38,14 @@
     base::Optional<SignedExchangeError::FieldIndexPair> error_field =
         base::nullopt);
 
+// Returns true when SignedHTTPExchange feature is NOT enabled and
+// SignedHTTPExchangeOriginTrial and SignedHTTPExchangeAcceptHeader features are
+// enabled.
+bool NeedToCheckRedirectedURLForAcceptHeader();
+
 // Returns true if Accept headers should be sent with
 // "application/signed-exchange".
-bool ShouldAdvertiseAcceptHeader(const url::Origin& origin);
+CONTENT_EXPORT bool ShouldAdvertiseAcceptHeader(const url::Origin& origin);
 
 // Returns true when SignedHTTPExchange feature or SignedHTTPExchangeOriginTrial
 // feature is enabled.
diff --git a/content/browser/webrtc/webrtc_audio_browsertest.cc b/content/browser/webrtc/webrtc_audio_browsertest.cc
index aacf3404..538577a8b 100644
--- a/content/browser/webrtc/webrtc_audio_browsertest.cc
+++ b/content/browser/webrtc/webrtc_audio_browsertest.cc
@@ -4,7 +4,6 @@
 
 #include "base/command_line.h"
 #include "base/files/file_util.h"
-#include "base/metrics/persistent_histogram_allocator.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/platform_thread.h"
 #include "build/build_config.h"
@@ -39,15 +38,6 @@
       // Force audio service out of process to disabled.
       audio_service_features_.InitWithFeatures({}, audio_service_oop_features);
     }
-#if defined(OS_WIN)
-    // TODO(https://crbug.com/867827) remove histogram allocator creation when
-    // removing output controller checks.
-    if (!base::GlobalHistogramAllocator::Get()) {
-      const int32_t kAllocatorMemorySize = 8 << 20;
-      base::GlobalHistogramAllocator::CreateWithLocalMemory(
-          kAllocatorMemorySize, 0, "HistogramAllocatorTest");
-    }
-#endif
   }
   ~WebRtcAudioBrowserTest() override {}
 
diff --git a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
index 48785fa9..bb6e4cbf2 100644
--- a/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
+++ b/content/browser/webrtc/webrtc_getusermedia_browsertest.cc
@@ -7,7 +7,6 @@
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/json/json_reader.h"
-#include "base/metrics/persistent_histogram_allocator.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
@@ -119,15 +118,6 @@
       // Force audio service out of process to disabled.
       audio_service_features_.InitWithFeatures({}, audio_service_oop_features);
     }
-#if defined(OS_WIN)
-    // TODO(https://crbug.com/867827) remove histogram allocator creation when
-    // removing output controller checks.
-    if (!base::GlobalHistogramAllocator::Get()) {
-      const int32_t kAllocatorMemorySize = 8 << 20;
-      base::GlobalHistogramAllocator::CreateWithLocalMemory(
-          kAllocatorMemorySize, 0, "HistogramAllocatorTest");
-    }
-#endif
   }
   ~WebRtcGetUserMediaBrowserTest() override {}
 
diff --git a/content/public/browser/browser_task_traits.h b/content/public/browser/browser_task_traits.h
index 8ccee9d9..c370cec 100644
--- a/content/public/browser/browser_task_traits.h
+++ b/content/public/browser/browser_task_traits.h
@@ -32,6 +32,10 @@
 // To obtain a TaskRunner for the UI thread (analogous for the IO thread):
 //     base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::UI});
 //
+// Tasks posted to the same BrowserThread with the same traits will be executed
+// in the order they were posted, regardless of the TaskRunners they were
+// posted via.
+//
 // See //base/task/post_task.h for more detailed documentation.
 //
 // Posting to a BrowserThread must only be done after it was initialized (ref.
diff --git a/content/renderer/media/stream/media_stream_audio_processor_unittest.cc b/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
index b560026..7dc5ad67 100644
--- a/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
+++ b/content/renderer/media/stream/media_stream_audio_processor_unittest.cc
@@ -159,6 +159,9 @@
   void VerifyDefaultComponents(MediaStreamAudioProcessor* audio_processor) {
     webrtc::AudioProcessing* audio_processing =
         audio_processor->audio_processing_.get();
+    const webrtc::AudioProcessing::Config apm_config =
+        audio_processing->GetConfig();
+    EXPECT_TRUE(apm_config.high_pass_filter.enabled);
 #if defined(OS_ANDROID)
     EXPECT_TRUE(audio_processing->echo_control_mobile()->is_enabled());
     EXPECT_TRUE(audio_processing->echo_control_mobile()->routing_mode() ==
@@ -176,7 +179,6 @@
     EXPECT_TRUE(audio_processing->noise_suppression()->is_enabled());
     EXPECT_TRUE(audio_processing->noise_suppression()->level() ==
         webrtc::NoiseSuppression::kHigh);
-    EXPECT_TRUE(audio_processing->high_pass_filter()->is_enabled());
     EXPECT_TRUE(audio_processing->gain_control()->is_enabled());
 #if defined(OS_ANDROID)
     EXPECT_TRUE(audio_processing->gain_control()->mode() ==
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc
index 8d5a6ab..d2b6ef2 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -829,7 +829,6 @@
       compositor_->GetCurrentFrameWithoutUpdatingStatistics();
 
   media::Context3D context_3d;
-  gpu::ContextSupport* context_support = nullptr;
   if (frame && frame->HasTextures()) {
     auto* provider =
         RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
@@ -837,11 +836,11 @@
     if (!provider)
       return;
     context_3d = media::Context3D(provider->ContextGL(), provider->GrContext());
-    context_support = provider->ContextSupport();
+    DCHECK(context_3d.gl);
   }
   const gfx::RectF dest_rect(rect.x, rect.y, rect.width, rect.height);
   video_renderer_.Paint(frame, canvas, dest_rect, flags, video_rotation_,
-                        context_3d, context_support);
+                        context_3d);
 }
 
 bool WebMediaPlayerMS::DidGetOpaqueResponseFromServiceWorker() const {
@@ -1017,8 +1016,8 @@
   DCHECK(context_3d.gl);
 
   return video_renderer_.CopyVideoFrameTexturesToGLTexture(
-      context_3d, provider->ContextSupport(), gl, video_frame.get(), target,
-      texture, internal_format, format, type, level, premultiply_alpha, flip_y);
+      context_3d, gl, video_frame.get(), target, texture, internal_format,
+      format, type, level, premultiply_alpha, flip_y);
 }
 
 bool WebMediaPlayerMS::CopyVideoYUVDataToPlatformTexture(
diff --git a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
index 92b6b60..c53c0be2 100644
--- a/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms_compositor.cc
@@ -68,8 +68,7 @@
     DCHECK(provider->ContextGL());
     video_renderer->Copy(
         frame.get(), &paint_canvas,
-        media::Context3D(provider->ContextGL(), provider->GrContext()),
-        provider->ContextSupport());
+        media::Context3D(provider->ContextGL(), provider->GrContext()));
 
     SkPixmap pixmap;
     const bool result = bitmap.peekPixels(&pixmap);
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index cfd1e8d..76bffec 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -433,8 +433,9 @@
 
 class InternalStatsObserver : public webrtc::StatsObserver {
  public:
-  explicit InternalStatsObserver(int lid)
-      : lid_(lid), main_thread_(base::ThreadTaskRunnerHandle::Get()) {}
+  InternalStatsObserver(int lid,
+                        scoped_refptr<base::SingleThreadTaskRunner> main_thread)
+      : lid_(lid), main_thread_(std::move(main_thread)) {}
 
   void OnComplete(const StatsReports& reports) override {
     std::unique_ptr<base::ListValue> list(new base::ListValue());
@@ -473,14 +474,19 @@
   const scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
 };
 
-PeerConnectionTracker::PeerConnectionTracker()
-    : next_local_id_(1), send_target_for_test_(nullptr) {}
-
 PeerConnectionTracker::PeerConnectionTracker(
-    mojom::PeerConnectionTrackerHostAssociatedPtr host)
+    scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
     : next_local_id_(1),
       send_target_for_test_(nullptr),
-      peer_connection_tracker_host_ptr_(std::move(host)) {}
+      main_thread_task_runner_(std::move(main_thread_task_runner)) {}
+
+PeerConnectionTracker::PeerConnectionTracker(
+    mojom::PeerConnectionTrackerHostAssociatedPtr host,
+    scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
+    : next_local_id_(1),
+      send_target_for_test_(nullptr),
+      peer_connection_tracker_host_ptr_(std::move(host)),
+      main_thread_task_runner_(std::move(main_thread_task_runner)) {}
 
 PeerConnectionTracker::~PeerConnectionTracker() {
 }
@@ -508,7 +514,8 @@
   for (PeerConnectionIdMap::iterator it = peer_connection_id_map_.begin();
        it != peer_connection_id_map_.end(); ++it) {
     rtc::scoped_refptr<InternalStatsObserver> observer(
-        new rtc::RefCountedObject<InternalStatsObserver>(it->second));
+        new rtc::RefCountedObject<InternalStatsObserver>(
+            it->second, main_thread_task_runner_));
 
     it->first->GetStats(observer,
                         webrtc::PeerConnectionInterface::kStatsOutputLevelDebug,
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.h b/content/renderer/media/webrtc/peer_connection_tracker.h
index 24e0fd85..7f95e5df 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.h
+++ b/content/renderer/media/webrtc/peer_connection_tracker.h
@@ -45,8 +45,11 @@
     : public RenderThreadObserver,
       public base::SupportsWeakPtr<PeerConnectionTracker> {
  public:
-  PeerConnectionTracker();
-  PeerConnectionTracker(mojom::PeerConnectionTrackerHostAssociatedPtr host);
+  explicit PeerConnectionTracker(
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
+  PeerConnectionTracker(
+      mojom::PeerConnectionTrackerHostAssociatedPtr host,
+      scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
   ~PeerConnectionTracker() override;
 
   enum Source {
@@ -262,6 +265,8 @@
   mojom::PeerConnectionTrackerHostAssociatedPtr
       peer_connection_tracker_host_ptr_;
 
+  scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
+
   DISALLOW_COPY_AND_ASSIGN(PeerConnectionTracker);
 };
 
diff --git a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
index 8486597..2bda94c 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
@@ -151,8 +151,9 @@
  public:
   void CreateTrackerWithMocks() {
     mock_host_.reset(new MockPeerConnectionTrackerHost());
-    tracker_.reset(
-        new PeerConnectionTracker(mock_host_->CreateInterfacePtrAndBind()));
+    tracker_.reset(new PeerConnectionTracker(
+        mock_host_->CreateInterfacePtrAndBind(),
+        blink::scheduler::GetSingleThreadTaskRunnerForTesting()));
     target_thread_.reset(new MockSendTargetThread());
     tracker_->OverrideSendTargetForTesting(target_thread_.get());
   }
@@ -177,7 +178,8 @@
 }  // namespace
 
 TEST_F(PeerConnectionTrackerTest, CreatingObject) {
-  PeerConnectionTracker tracker;
+  PeerConnectionTracker tracker(
+      blink::scheduler::GetSingleThreadTaskRunnerForTesting());
 }
 
 TEST_F(PeerConnectionTrackerTest, TrackCreateOffer) {
diff --git a/content/renderer/media/webrtc/rtc_dtmf_sender_handler.cc b/content/renderer/media/webrtc/rtc_dtmf_sender_handler.cc
index 82ffa649..fa3c5767 100644
--- a/content/renderer/media/webrtc/rtc_dtmf_sender_handler.cc
+++ b/content/renderer/media/webrtc/rtc_dtmf_sender_handler.cc
@@ -22,8 +22,9 @@
     public base::RefCountedThreadSafe<Observer>,
     public webrtc::DtmfSenderObserverInterface {
  public:
-  explicit Observer(const base::WeakPtr<RtcDtmfSenderHandler>& handler)
-      : main_thread_(base::ThreadTaskRunnerHandle::Get()), handler_(handler) {}
+  explicit Observer(scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+                    const base::WeakPtr<RtcDtmfSenderHandler>& handler)
+      : main_thread_(std::move(main_thread)), handler_(handler) {}
 
  private:
   friend class base::RefCountedThreadSafe<Observer>;
@@ -51,10 +52,12 @@
   base::WeakPtr<RtcDtmfSenderHandler> handler_;
 };
 
-RtcDtmfSenderHandler::RtcDtmfSenderHandler(DtmfSenderInterface* dtmf_sender)
+RtcDtmfSenderHandler::RtcDtmfSenderHandler(
+    scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+    DtmfSenderInterface* dtmf_sender)
     : dtmf_sender_(dtmf_sender), webkit_client_(nullptr), weak_factory_(this) {
   DVLOG(1) << "::ctor";
-  observer_ = new Observer(weak_factory_.GetWeakPtr());
+  observer_ = new Observer(std::move(main_thread), weak_factory_.GetWeakPtr());
   dtmf_sender_->RegisterObserver(observer_.get());
 }
 
diff --git a/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h b/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h
index 193bc77..a314ecc1 100644
--- a/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h
+++ b/content/renderer/media/webrtc/rtc_dtmf_sender_handler.h
@@ -16,6 +16,10 @@
 #include "third_party/blink/public/platform/web_rtc_dtmf_sender_handler_client.h"
 #include "third_party/webrtc/api/dtmfsenderinterface.h"
 
+namespace base {
+class SingleThreadTaskRunner;
+}
+
 namespace content {
 
 // RtcDtmfSenderHandler is a delegate for the RTC DTMF Sender API messages
@@ -27,7 +31,8 @@
 class CONTENT_EXPORT RtcDtmfSenderHandler
     : public blink::WebRTCDTMFSenderHandler {
  public:
-  explicit RtcDtmfSenderHandler(webrtc::DtmfSenderInterface* dtmf_sender);
+  RtcDtmfSenderHandler(scoped_refptr<base::SingleThreadTaskRunner> main_thread,
+                       webrtc::DtmfSenderInterface* dtmf_sender);
   ~RtcDtmfSenderHandler() override;
 
   // blink::WebRTCDTMFSenderHandler implementation.
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index e281ec9..fa770cf 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -148,6 +148,10 @@
 
 class MockPeerConnectionTracker : public PeerConnectionTracker {
  public:
+  MockPeerConnectionTracker()
+      : PeerConnectionTracker(
+            blink::scheduler::GetSingleThreadTaskRunnerForTesting()) {}
+
   MOCK_METHOD1(UnregisterPeerConnection,
                void(RTCPeerConnectionHandler* pc_handler));
   // TODO(jiayl): add coverage for the following methods
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender.cc b/content/renderer/media/webrtc/rtc_rtp_sender.cc
index a2d16ec..14d7a5f5 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_sender.cc
@@ -185,7 +185,8 @@
     // webrtc signalling thread.
     DCHECK(main_task_runner_->BelongsToCurrentThread());
     auto dtmf_sender = webrtc_sender_->GetDtmfSender();
-    return std::make_unique<RtcDtmfSenderHandler>(dtmf_sender);
+    return std::make_unique<RtcDtmfSenderHandler>(main_task_runner_,
+                                                  dtmf_sender);
   }
 
   std::unique_ptr<webrtc::RtpParameters> GetParameters() {
diff --git a/content/renderer/media/webrtc/webrtc_audio_sink.cc b/content/renderer/media/webrtc/webrtc_audio_sink.cc
index 81becdc4..521f8ec 100644
--- a/content/renderer/media/webrtc/webrtc_audio_sink.cc
+++ b/content/renderer/media/webrtc/webrtc_audio_sink.cc
@@ -18,9 +18,13 @@
 WebRtcAudioSink::WebRtcAudioSink(
     const std::string& label,
     scoped_refptr<webrtc::AudioSourceInterface> track_source,
-    scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner)
-    : adapter_(new rtc::RefCountedObject<Adapter>(
-          label, std::move(track_source), std::move(signaling_task_runner))),
+    scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
+    : adapter_(
+          new rtc::RefCountedObject<Adapter>(label,
+                                             std::move(track_source),
+                                             std::move(signaling_task_runner),
+                                             std::move(main_task_runner))),
       fifo_(base::Bind(&WebRtcAudioSink::DeliverRebufferedAudio,
                        base::Unretained(this))) {
   DVLOG(1) << "WebRtcAudioSink::WebRtcAudioSink()";
@@ -100,12 +104,14 @@
 WebRtcAudioSink::Adapter::Adapter(
     const std::string& label,
     scoped_refptr<webrtc::AudioSourceInterface> source,
-    scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
     : webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>(label),
       source_(std::move(source)),
       signaling_task_runner_(std::move(signaling_task_runner)),
-      main_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+      main_task_runner_(std::move(main_task_runner)) {
   DCHECK(signaling_task_runner_);
+  DCHECK(main_task_runner_);
 }
 
 WebRtcAudioSink::Adapter::~Adapter() {
diff --git a/content/renderer/media/webrtc/webrtc_audio_sink.h b/content/renderer/media/webrtc/webrtc_audio_sink.h
index a5667cc3..c2a6c381 100644
--- a/content/renderer/media/webrtc/webrtc_audio_sink.h
+++ b/content/renderer/media/webrtc/webrtc_audio_sink.h
@@ -40,7 +40,8 @@
   WebRtcAudioSink(
       const std::string& label,
       scoped_refptr<webrtc::AudioSourceInterface> track_source,
-      scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
 
   ~WebRtcAudioSink() override;
 
@@ -72,7 +73,8 @@
    public:
     Adapter(const std::string& label,
             scoped_refptr<webrtc::AudioSourceInterface> source,
-            scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner);
+            scoped_refptr<base::SingleThreadTaskRunner> signaling_task_runner,
+            scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
 
     base::SingleThreadTaskRunner* signaling_task_runner() const {
       return signaling_task_runner_.get();
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc
index 92d4065c2..caa1974 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter.cc
@@ -149,7 +149,7 @@
   webrtc::AudioSourceInterface* source_interface = nullptr;
   local_track_audio_sink_.reset(
       new WebRtcAudioSink(web_track_.Id().Utf8(), source_interface,
-                          factory_->GetWebRtcSignalingThread()));
+                          factory_->GetWebRtcSignalingThread(), main_thread_));
 
   if (auto* media_stream_source = ProcessedLocalAudioSource::From(
           MediaStreamAudioSource::From(web_track_.Source()))) {
diff --git a/content/renderer/media_recorder/video_track_recorder.cc b/content/renderer/media_recorder/video_track_recorder.cc
index ef28d3170..c7fb3465 100644
--- a/content/renderer/media_recorder/video_track_recorder.cc
+++ b/content/renderer/media_recorder/video_track_recorder.cc
@@ -313,8 +313,7 @@
     DCHECK(context_provider->ContextGL());
     video_renderer_->Copy(video_frame.get(), canvas_.get(),
                           media::Context3D(context_provider->ContextGL(),
-                                           context_provider->GrContext()),
-                          context_provider->ContextSupport());
+                                           context_provider->GrContext()));
 
     SkPixmap pixmap;
     if (!bitmap_.peekPixels(&pixmap)) {
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 5723681..f6b7f9f 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -775,7 +775,8 @@
   browser_plugin_manager_.reset(new BrowserPluginManager());
   AddObserver(browser_plugin_manager_.get());
 
-  peer_connection_tracker_.reset(new PeerConnectionTracker());
+  peer_connection_tracker_.reset(
+      new PeerConnectionTracker(main_thread_runner()));
   AddObserver(peer_connection_tracker_.get());
 
   p2p_socket_dispatcher_ = new P2PSocketDispatcher();
@@ -1166,25 +1167,14 @@
 }
 
 void RenderThreadImpl::InitializeCompositorThread() {
-  blink::WebThreadCreationParams params(
-      blink::WebThreadType::kCompositorThread);
-#if defined(OS_ANDROID)
-  params.thread_options.priority = base::ThreadPriority::DISPLAY;
-#endif
-  blink_platform_impl_->InitializeCompositorThread(params);
-  blink::WebThread* compositor_thread =
-      blink_platform_impl_->CompositorThread();
-  compositor_task_runner_ = compositor_thread->GetTaskRunner();
+  blink_platform_impl_->InitializeCompositorThread();
+  compositor_task_runner_ = blink_platform_impl_->CompositorThreadTaskRunner();
   compositor_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(base::IgnoreResult(&ThreadRestrictions::SetIOAllowed),
                      false));
   GetContentClient()->renderer()->PostCompositorThreadCreated(
       compositor_task_runner_.get());
-#if defined(OS_LINUX)
-  render_message_filter()->SetThreadPriority(compositor_thread->ThreadId(),
-                                             base::ThreadPriority::DISPLAY);
-#endif
 }
 
 scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 1e87b767..807e75b 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -380,6 +380,16 @@
   return url_loader_factory;
 }
 
+void RendererBlinkPlatformImpl::SetDisplayThreadPriority(
+    base::PlatformThreadId thread_id) {
+#if defined(OS_LINUX)
+  if (RenderThreadImpl* render_thread = RenderThreadImpl::current()) {
+    render_thread->render_message_filter()->SetThreadPriority(
+        thread_id, base::ThreadPriority::DISPLAY);
+  }
+#endif
+}
+
 blink::BlameContext* RendererBlinkPlatformImpl::GetTopLevelBlameContext() {
   return &top_level_blame_context_;
 }
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 960ed3a..b74b59a 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -196,6 +196,7 @@
   blink::WebString ConvertIDNToUnicode(const blink::WebString& host) override;
   service_manager::Connector* GetConnector() override;
   blink::InterfaceProvider* GetInterfaceProvider() override;
+  void SetDisplayThreadPriority(base::PlatformThreadId thread_id) override;
   blink::BlameContext* GetTopLevelBlameContext() override;
   void RecordRappor(const char* metric,
                     const blink::WebString& sample) override;
diff --git a/content/shell/renderer/layout_test/blink_test_runner.cc b/content/shell/renderer/layout_test/blink_test_runner.cc
index 2366d29..0058ebb4 100644
--- a/content/shell/renderer/layout_test/blink_test_runner.cc
+++ b/content/shell/renderer/layout_test/blink_test_runner.cc
@@ -107,7 +107,6 @@
 using blink::WebURLError;
 using blink::WebURLRequest;
 using blink::WebTestingSupport;
-using blink::WebThread;
 using blink::WebVector;
 using blink::WebView;
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index f99c8790..e42988d 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1578,6 +1578,7 @@
     "../browser/service_worker/service_worker_registration_unittest.cc",
     "../browser/service_worker/service_worker_request_handler_unittest.cc",
     "../browser/service_worker/service_worker_script_loader_factory_unittest.cc",
+    "../browser/service_worker/service_worker_single_script_update_checker_unittest.cc",
     "../browser/service_worker/service_worker_storage_unittest.cc",
     "../browser/service_worker/service_worker_url_request_job_unittest.cc",
     "../browser/service_worker/service_worker_version_unittest.cc",
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index 5737483f6..a721305 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -179,7 +179,7 @@
     v8::Local<v8::Value> chrome(global->Get(chrome_string));
     if (chrome->IsUndefined()) {
       chrome = v8::Object::New(context()->isolate());
-      global->Set(chrome_string, chrome);
+      global->Set(context()->v8_context(), chrome_string, chrome).ToChecked();
     }
     args.GetReturnValue().Set(chrome);
   }
diff --git a/extensions/renderer/js_extension_bindings_system.cc b/extensions/renderer/js_extension_bindings_system.cc
index 783fa0c..beb355c 100644
--- a/extensions/renderer/js_extension_bindings_system.cc
+++ b/extensions/renderer/js_extension_bindings_system.cc
@@ -36,9 +36,9 @@
 // exist.
 v8::Local<v8::Object> GetOrCreateObject(const v8::Local<v8::Object>& object,
                                         const std::string& field,
-                                        v8::Isolate* isolate) {
+                                        ScriptContext* context) {
   v8::Local<v8::String> key =
-      v8::String::NewFromUtf8(isolate, field.c_str(),
+      v8::String::NewFromUtf8(context->isolate(), field.c_str(),
                               v8::NewStringType::kInternalized)
           .ToLocalChecked();
   // If the object has a callback property, it is assumed it is an unavailable
@@ -52,8 +52,8 @@
     return v8::Local<v8::Object>::Cast(value);
   }
 
-  v8::Local<v8::Object> new_object = v8::Object::New(isolate);
-  object->Set(key, new_object);
+  v8::Local<v8::Object> new_object = v8::Object::New(context->isolate());
+  object->Set(context->v8_context(), key, new_object).ToChecked();
   return new_object;
 }
 
@@ -70,7 +70,7 @@
   v8::Local<v8::Value> chrome(global->Get(chrome_string));
   if (chrome->IsUndefined()) {
     chrome = v8::Object::New(context->isolate());
-    global->Set(chrome_string, chrome);
+    global->Set(context->v8_context(), chrome_string, chrome).ToChecked();
   }
   return chrome->IsObject() ? chrome.As<v8::Object>() : v8::Local<v8::Object>();
 }
@@ -113,7 +113,7 @@
       if (bind_object.IsEmpty())
         return v8::Local<v8::Object>();
     }
-    bind_object = GetOrCreateObject(bind_object, split[i], context->isolate());
+    bind_object = GetOrCreateObject(bind_object, split[i], context);
   }
 
   if (only_ancestor_available)
diff --git a/gin/interceptor_unittest.cc b/gin/interceptor_unittest.cc
index 4af449f96..58f209a 100644
--- a/gin/interceptor_unittest.cc
+++ b/gin/interceptor_unittest.cc
@@ -39,7 +39,10 @@
     if (property == "value") {
       return ConvertToV8(isolate, value_);
     } else if (property == "func") {
-      return GetFunctionTemplate(isolate, "func")->GetFunction();
+      v8::Local<v8::Context> context = isolate->GetCurrentContext();
+      return GetFunctionTemplate(isolate, "func")
+          ->GetFunction(context)
+          .ToLocalChecked();
     } else {
       return v8::Local<v8::Value>();
     }
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 3ae6d87..a96a369 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -304,39 +304,6 @@
   return true;
 }
 
-// Definitions for constructor and destructor of this interface are needed to
-// avoid MSVC LNK2019.
-FilteredSender::FilteredSender() = default;
-
-FilteredSender::~FilteredSender() = default;
-
-SyncChannelFilteredSender::SyncChannelFilteredSender(
-    IPC::ChannelHandle channel_handle,
-    IPC::Listener* listener,
-    scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
-    base::WaitableEvent* shutdown_event)
-    : channel_(IPC::SyncChannel::Create(channel_handle,
-                                        IPC::Channel::MODE_SERVER,
-                                        listener,
-                                        ipc_task_runner,
-                                        base::ThreadTaskRunnerHandle::Get(),
-                                        false,
-                                        shutdown_event)) {}
-
-SyncChannelFilteredSender::~SyncChannelFilteredSender() = default;
-
-bool SyncChannelFilteredSender::Send(IPC::Message* msg) {
-  return channel_->Send(msg);
-}
-
-void SyncChannelFilteredSender::AddFilter(IPC::MessageFilter* filter) {
-  channel_->AddFilter(filter);
-}
-
-void SyncChannelFilteredSender::RemoveFilter(IPC::MessageFilter* filter) {
-  channel_->RemoveFilter(filter);
-}
-
 GpuChannel::GpuChannel(
     GpuChannelManager* gpu_channel_manager,
     Scheduler* scheduler,
@@ -361,29 +328,42 @@
   DCHECK(gpu_channel_manager_);
   DCHECK(client_id_);
   filter_ = new GpuChannelMessageFilter(this, scheduler, task_runner);
+  // SharedImageInterfaceProxy/Stub is a singleton per channel, using a reserved
+  // route.
+  const int32_t shared_image_route_id =
+      static_cast<int32_t>(GpuChannelReservedRoutes::kSharedImageInterface);
+  shared_image_stub_ =
+      std::make_unique<SharedImageStub>(this, shared_image_route_id);
+  filter_->AddRoute(shared_image_route_id, shared_image_stub_->sequence());
+  router_.AddRoute(shared_image_route_id, shared_image_stub_.get());
 }
 
 GpuChannel::~GpuChannel() {
   // Clear stubs first because of dependencies.
   stubs_.clear();
 
-  // Destroy filter first so that scheduler gets no more messages.
+  // Destroy filter first to stop posting tasks to scheduler.
   filter_->Destroy();
 
   for (const auto& kv : stream_sequences_)
     scheduler_->DestroySequence(kv.second);
 }
 
-void GpuChannel::Init(std::unique_ptr<FilteredSender> channel) {
-  channel_ = std::move(channel);
-  channel_->AddFilter(filter_.get());
-  // SharedImageInterfaceProxy/Stub is a singleton per channel, using a reserved
-  // route.
-  const int32_t route_id =
-      static_cast<int32_t>(GpuChannelReservedRoutes::kSharedImageInterface);
-  shared_image_stub_ = std::make_unique<SharedImageStub>(this, route_id);
-  filter_->AddRoute(route_id, shared_image_stub_->sequence());
-  router_.AddRoute(route_id, shared_image_stub_.get());
+void GpuChannel::Init(IPC::ChannelHandle channel_handle,
+                      base::WaitableEvent* shutdown_event) {
+  sync_channel_ = IPC::SyncChannel::Create(
+      channel_handle, IPC::Channel::MODE_SERVER, this, io_task_runner_.get(),
+      task_runner_.get(), false, shutdown_event);
+  sync_channel_->AddFilter(filter_.get());
+  channel_ = sync_channel_.get();
+}
+
+void GpuChannel::InitForTesting(IPC::Channel* channel) {
+  channel_ = channel;
+  // |channel| is an IPC::TestSink in tests, so don't add the filter to it
+  // because it will forward sent messages back to the filter.
+  // Call OnFilterAdded() to prevent DCHECK failures.
+  filter_->OnFilterAdded(channel);
 }
 
 void GpuChannel::SetUnhandledMessageListener(IPC::Listener* listener) {
diff --git a/gpu/ipc/service/gpu_channel.h b/gpu/ipc/service/gpu_channel.h
index 3e8c1c6..4ea6463 100644
--- a/gpu/ipc/service/gpu_channel.h
+++ b/gpu/ipc/service/gpu_channel.h
@@ -47,38 +47,10 @@
 class SharedImageStub;
 class SyncPointManager;
 
-class GPU_IPC_SERVICE_EXPORT FilteredSender : public IPC::Sender {
- public:
-  FilteredSender();
-  ~FilteredSender() override;
-
-  virtual void AddFilter(IPC::MessageFilter* filter) = 0;
-  virtual void RemoveFilter(IPC::MessageFilter* filter) = 0;
-};
-
-class GPU_IPC_SERVICE_EXPORT SyncChannelFilteredSender : public FilteredSender {
- public:
-  SyncChannelFilteredSender(
-      IPC::ChannelHandle channel_handle,
-      IPC::Listener* listener,
-      scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
-      base::WaitableEvent* shutdown_event);
-  ~SyncChannelFilteredSender() override;
-
-  bool Send(IPC::Message* msg) override;
-  void AddFilter(IPC::MessageFilter* filter) override;
-  void RemoveFilter(IPC::MessageFilter* filter) override;
-
- private:
-  std::unique_ptr<IPC::SyncChannel> channel_;
-
-  DISALLOW_COPY_AND_ASSIGN(SyncChannelFilteredSender);
-};
-
 // Encapsulates an IPC channel between the GPU process and one renderer
 // process. On the renderer side there's a corresponding GpuChannelHost.
 class GPU_IPC_SERVICE_EXPORT GpuChannel : public IPC::Listener,
-                                          public FilteredSender {
+                                          public IPC::Sender {
  public:
   // Takes ownership of the renderer process handle.
   GpuChannel(GpuChannelManager* gpu_channel_manager,
@@ -92,9 +64,12 @@
              bool is_gpu_host);
   ~GpuChannel() override;
 
-  // The IPC channel cannot be passed in the constructor because it needs a
-  // listener. The listener is the GpuChannel and must be constructed first.
-  void Init(std::unique_ptr<FilteredSender> channel);
+  // Init() sets up the underlying IPC channel.  Use a separate method because
+  // we don't want to do that in tests.
+  void Init(IPC::ChannelHandle channel_handle,
+            base::WaitableEvent* shutdown_event);
+
+  void InitForTesting(IPC::Channel* channel);
 
   base::WeakPtr<GpuChannel> AsWeakPtr();
 
@@ -126,17 +101,16 @@
     return io_task_runner_;
   }
 
-  FilteredSender* channel_for_testing() const { return channel_.get(); }
-
   // IPC::Listener implementation:
   bool OnMessageReceived(const IPC::Message& msg) override;
   void OnChannelConnected(int32_t peer_pid) override;
   void OnChannelError() override;
 
-  // FilteredSender implementation:
+  // IPC::Sender implementation:
   bool Send(IPC::Message* msg) override;
-  void AddFilter(IPC::MessageFilter* filter) override;
-  void RemoveFilter(IPC::MessageFilter* filter) override;
+
+  void AddFilter(IPC::MessageFilter* filter);
+  void RemoveFilter(IPC::MessageFilter* filter);
 
   void OnCommandBufferScheduled(CommandBufferStub* stub);
   void OnCommandBufferDescheduled(CommandBufferStub* stub);
@@ -196,7 +170,8 @@
   void OnDestroyCommandBuffer(int32_t route_id);
   void OnCrashForTesting();
 
-  std::unique_ptr<FilteredSender> channel_;
+  std::unique_ptr<IPC::SyncChannel> sync_channel_;  // nullptr in tests.
+  IPC::Sender* channel_;  // Same as sync_channel_.get() except in tests.
 
   base::ProcessId peer_pid_ = base::kNullProcessId;
 
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index a388a48a..787b918 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -13,7 +13,6 @@
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/gpu_channel_manager_delegate.h"
-#include "ipc/ipc_test_sink.h"
 #include "ui/gl/init/gl_factory.h"
 #include "ui/gl/test/gl_surface_test_support.h"
 #include "url/gurl.h"
@@ -47,30 +46,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestGpuChannelManagerDelegate);
 };
 
-class TestSinkFilteredSender : public FilteredSender {
- public:
-  TestSinkFilteredSender() : sink_(std::make_unique<IPC::TestSink>()) {}
-  ~TestSinkFilteredSender() override = default;
-
-  IPC::TestSink* sink() const { return sink_.get(); }
-
-  bool Send(IPC::Message* msg) override { return sink_->Send(msg); }
-
-  void AddFilter(IPC::MessageFilter* filter) override {
-    // Needed to appease DCHECKs.
-    filter->OnFilterAdded(sink_.get());
-  }
-
-  void RemoveFilter(IPC::MessageFilter* filter) override {
-    filter->OnFilterRemoved();
-  }
-
- private:
-  std::unique_ptr<IPC::TestSink> sink_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestSinkFilteredSender);
-};
-
 GpuChannelTestCommon::GpuChannelTestCommon()
     : task_runner_(new base::TestSimpleTaskRunner),
       io_task_runner_(new base::TestSimpleTaskRunner),
@@ -104,7 +79,7 @@
   uint64_t kClientTracingId = 1;
   GpuChannel* channel = channel_manager()->EstablishChannel(
       client_id, kClientTracingId, is_gpu_host, true);
-  channel->Init(std::make_unique<TestSinkFilteredSender>());
+  channel->InitForTesting(&sink_);
   base::ProcessId kProcessId = 1;
   channel->OnChannelConnected(kProcessId);
   return channel;
@@ -112,13 +87,9 @@
 
 void GpuChannelTestCommon::HandleMessage(GpuChannel* channel,
                                          IPC::Message* msg) {
-  IPC::TestSink* sink =
-      static_cast<TestSinkFilteredSender*>(channel->channel_for_testing())
-          ->sink();
-
   // Some IPCs (such as GpuCommandBufferMsg_Initialize) will generate more
   // delayed responses, drop those if they exist.
-  sink->ClearMessages();
+  sink_.ClearMessages();
 
   // Needed to appease DCHECKs.
   msg->set_unblock(false);
@@ -131,7 +102,7 @@
 
   // Replies are sent to the sink.
   if (msg->is_sync()) {
-    const IPC::Message* reply_msg = sink->GetMessageAt(0);
+    const IPC::Message* reply_msg = sink_.GetMessageAt(0);
     ASSERT_TRUE(reply_msg);
     EXPECT_TRUE(!reply_msg->is_reply_error());
 
@@ -146,7 +117,7 @@
     delete deserializer;
   }
 
-  sink->ClearMessages();
+  sink_.ClearMessages();
 
   delete msg;
 }
diff --git a/gpu/ipc/service/gpu_channel_test_common.h b/gpu/ipc/service/gpu_channel_test_common.h
index 11a96d23..cd3aae11 100644
--- a/gpu/ipc/service/gpu_channel_test_common.h
+++ b/gpu/ipc/service/gpu_channel_test_common.h
@@ -9,6 +9,7 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/unsafe_shared_memory_region.h"
+#include "ipc/ipc_test_sink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -42,6 +43,7 @@
   base::UnsafeSharedMemoryRegion GetSharedMemoryRegion();
 
  private:
+  IPC::TestSink sink_;
   scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
   scoped_refptr<base::TestSimpleTaskRunner> io_task_runner_;
   std::unique_ptr<SyncPointManager> sync_point_manager_;
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 5fbc0bd..2389884d 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -3252,9 +3252,7 @@
       recipe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        # TODO(phoglund): change to chromium.webrtc.fyi once done rewriting the
-        # configs to be src-side
-        properties: "mastername:chromium.webrtc.fyi.experimental"
+        properties: "mastername:chromium.webrtc.fyi"
         name: "chromium"
       }
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index ec8e043..749eb4b 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -3869,109 +3869,109 @@
   refs: "refs/heads/master"
   manifest_name: "REVISION"
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Android Builder (dbg)"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder (dbg)"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder (dbg)"
     category: "android|debug|builder"
     short_name: "32"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Android Builder ARM64 (dbg)"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 (dbg)"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 (dbg)"
     category: "android|debug|builder"
     short_name: "64"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Android Tests (dbg) (K Nexus5)"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Android Tests (dbg) (K Nexus5)"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Tests (dbg) (K Nexus5)"
     category: "android|debug|tester"
     short_name: "K"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Android Tests (dbg) (M Nexus5X)"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)"
     category: "android|debug|tester"
     short_name: "M"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Android Builder"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Android Builder"
     category: "android|release"
     short_name: "32"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Linux Builder (dbg)"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Linux Builder (dbg)"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Linux Builder (dbg)"
     category: "linux|debug"
     short_name: "bld"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Linux Builder"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Linux Builder"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Linux Builder"
     category: "linux|release"
     short_name: "bld"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Linux Tester"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Linux Tester"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Linux Tester"
     category: "linux|release"
     short_name: "tst"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Mac Builder (dbg)"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Mac Builder (dbg)"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Mac Builder (dbg)"
     category: "mac|debug"
     short_name: "bld"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Mac Builder"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Mac Builder"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Mac Builder"
     category: "mac|release"
     short_name: "bld"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Mac Tester"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Mac Tester"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Mac Tester"
     category: "mac|release"
     short_name: "tst"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Win Builder (dbg)"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Win Builder (dbg)"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win Builder (dbg)"
     category: "win|debug"
     short_name: "bld"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Win Builder"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Win Builder"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win Builder"
     category: "win|release|builder"
     short_name: "32"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Win10 Tester"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Win10 Tester"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win10 Tester"
     category: "win|release|tester"
     short_name: "10"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Win7 Tester"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Win7 Tester"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win7 Tester"
     category: "win|release|tester"
     short_name: "7"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/Win8 Tester"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI Win8 Tester"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI Win8 Tester"
     category: "win|release|tester"
     short_name: "8"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/ios-device"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI ios-device"
     category: "ios"
     short_name: "dev"
   }
   builders {
-    name: "buildbot/chromium.webrtc.fyi/ios-simulator"
+    name: "buildbot/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator"
     name: "buildbucket/luci.chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator"
     category: "ios"
     short_name: "sim"
diff --git a/ios/build/bots/chromium.webrtc.fyi.experimental/WebRTC Chromium FYI ios-device.json b/ios/build/bots/chromium.webrtc.fyi.experimental/WebRTC Chromium FYI ios-device.json
deleted file mode 100644
index a46d34d..0000000
--- a/ios/build/bots/chromium.webrtc.fyi.experimental/WebRTC Chromium FYI ios-device.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "comments": [
-    "Builder for 32-bit devices.",
-    "Build is performed with gn+ninja.",
-    "TODO(crbug.com/877018): graduate to chromium.webrtc.fyi when it works."
-  ],
-  "xcode build version": "10a254a",
-  "gn_args": [
-    "additional_target_cpus=[ \"arm64\" ]",
-    "goma_dir=\"$(goma_dir)\"",
-    "ios_enable_code_signing=false",
-    "is_component_build=false",
-    "is_debug=false",
-    "target_cpu=\"arm\"",
-    "target_os=\"ios\"",
-    "use_goma=true"
-  ],
-  "additional_compile_targets": [
-    "all"
-  ],
-  "tests": [
-  ]
-}
diff --git a/ios/build/bots/chromium.webrtc.fyi.experimental/WebRTC Chromium FYI ios-simulator.json b/ios/build/bots/chromium.webrtc.fyi.experimental/WebRTC Chromium FYI ios-simulator.json
deleted file mode 100644
index 8aaa591..0000000
--- a/ios/build/bots/chromium.webrtc.fyi.experimental/WebRTC Chromium FYI ios-simulator.json
+++ /dev/null
@@ -1,65 +0,0 @@
-{
-  "comments": [
-    "Runs tests on @3x, @2x, 64-bit, 32-bit, phone, tablet, iOS 10, and iOS 9.",
-    "TODO(crbug.com/877018): graduate to chromium.webrtc.fyi when it works."
-  ],
-  "xcode build version": "10a254a",
-  "gn_args": [
-    "additional_target_cpus=[\"x86\"]",
-    "goma_dir=\"$(goma_dir)\"",
-    "ios_enable_code_signing=false",
-    "is_component_build=false",
-    "is_debug=true",
-    "symbol_level=1",
-    "target_cpu=\"x64\"",
-    "target_os=\"ios\"",
-    "use_goma=true"
-  ],
-  "additional_compile_targets": [
-    "all"
-  ],
-  "configuration": "Debug",
-  "sdk": "iphonesimulator10.0",
-  "tests": [
-    {
-      "include": "common_tests.json",
-      "device type": "iPhone 6s Plus",
-      "os": "10.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPhone 6s",
-      "os": "10.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPhone 5",
-      "os": "10.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPad Air 2",
-      "os": "10.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPad Retina",
-      "os": "10.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPhone 5",
-      "comments": [
-        "This still relies on system iOS 9.0 legacy simulators,",
-        "since Xcode 8.0 doesn't provide it. But our test runner",
-        "doesn't support Xcode 7.0 anymore..."
-      ],
-      "os": "9.0"
-    },
-    {
-      "include": "common_tests.json",
-      "device type": "iPad Air 2",
-      "os": "9.0"
-    }
-  ]
-}
diff --git a/ios/build/bots/chromium.webrtc.fyi/ios-device.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json
similarity index 100%
rename from ios/build/bots/chromium.webrtc.fyi/ios-device.json
rename to ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json
diff --git a/ios/build/bots/chromium.webrtc.fyi/ios-simulator.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
similarity index 100%
rename from ios/build/bots/chromium.webrtc.fyi/ios-simulator.json
rename to ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
diff --git a/ios/chrome/app/startup/setup_debugging.mm b/ios/chrome/app/startup/setup_debugging.mm
index 5a49e40..ffc0220 100644
--- a/ios/chrome/app/startup/setup_debugging.mm
+++ b/ios/chrome/app/startup/setup_debugging.mm
@@ -34,7 +34,6 @@
   [whiteList addObject:@"glif-google-to-dots_28"];
   // TODO(crbug.com/721338): Add missing image.
   [whiteList addObject:@"voice_icon_keyboard_accessory"];
-  [whiteList addObject:@"voice_icon"];
   // TODO(crbug.com/754032): Add missing image.
   [whiteList addObject:@"ios_default_avatar"];
 
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 581981e5..2f0c71b 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1722,22 +1722,22 @@
       <message name="IDS_IOS_TOOLS_MENU_BOOKMARKS" desc="The iOS menu item for opening the bookmarks manager [Length: 15em] [iOS only]">
         Bookmarks
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="The iOS menu item for closing all incognito tabs [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_CLOSE_ALL_INCOGNITO_TABS" desc="The iOS menu item for closing all incognito tabs [iOS only]" meaning="[Length: unlimited]">
         Close All Incognito Tabs
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_CLOSE_ALL_TABS" desc="The iOS menu item for closing all tabs [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_CLOSE_ALL_TABS" desc="The iOS menu item for closing all tabs [iOS only]" meaning="[Length: unlimited]">
         Close All Tabs
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_CLOSE_TAB" desc="The iOS menu item for closing a tab [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_CLOSE_TAB" desc="The iOS menu item for closing a tab [iOS only]" meaning="[Length: unlimited]">
         Close Tab
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_EDIT_BOOKMARK" desc="The iOS menu item for editing a bookmark [Length: 25em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_EDIT_BOOKMARK" desc="The iOS menu item for editing a bookmark [iOS only]" meaning="[Length: unlimited]">
         Edit Bookmark
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_FIND_IN_PAGE" desc="The iOS menu item for searching in the current page [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_FIND_IN_PAGE" desc="The iOS menu item for searching in the current page [iOS only]" meaning="Open the Find In Page menu [Length: unlimited]">
         Find in Page...
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_HELP_MOBILE" desc="The Tools menu item for bringing up online help page. [Length: 15em. Width of Tools menu.]">
+      <message name="IDS_IOS_TOOLS_MENU_HELP_MOBILE" desc="The Tools menu item for bringing up online help page." meaning="Open the help menu [Length: unlimited]">
         Help
       </message>
       <message name="IDS_IOS_TOOLS_MENU_HELP_URL" translateable="false">
@@ -1746,10 +1746,10 @@
       <message name="IDS_IOS_TOOLS_MENU_HISTORY" desc="The Tools menu item for opening the navigation history menu">
         History
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB" desc="The iOS menu item for opening a new incognito tab [Length: 15em] [iOS only]" meaning="Tools menu entry to create a new Incognito tab.">
+      <message name="IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB" desc="The iOS menu item for opening a new incognito tab [iOS only]" meaning="Tools menu entry to create a new Incognito tab. [Length: Unlimited]">
         New Incognito Tab
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_NEW_TAB" desc="The iOS menu item for opening a new tab [Length: 15em] [iOS only]" meaning="Tools menu entry to create a new tab.">
+      <message name="IDS_IOS_TOOLS_MENU_NEW_TAB" desc="The iOS menu item for opening a new tab [iOS only]" meaning="Tools menu entry to create a new tab. [Length: Unlimited]">
         New Tab
       </message>
       <message name="IDS_IOS_TOOLS_MENU_PASTE_AND_GO" desc="The iOS menu item for pasting a URL or text in the omnibox and navigating to the page or searching for the text">
@@ -1758,22 +1758,22 @@
       <message name="IDS_IOS_TOOLS_MENU_QR_SCANNER" desc="The iOS menu item for opening the QR Code Scanner screen, allowing the user to scan QR code.">
         Scan QR Code
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_READING_LIST" desc="The iOS menu item for opening the reading list [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_READING_LIST" desc="The iOS menu item for opening the reading list [iOS only]" meaning="Open the Reading List menu. [Length: Unlimited]">
         Reading List
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_RECENT_TABS" desc="The iOS menu item for opening the panel showing the recently closed tabs and the tabs from other devices. [Length: 15em]" meaning="[In Title case] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_RECENT_TABS" desc="The iOS menu item for opening the panel showing the recently closed tabs and the tabs from other devices." meaning="[In Title case] [iOS only] [Length: Unlimited]">
         Recent Tabs
       </message>
       <message name="IDS_IOS_TOOLS_MENU_RELOAD" desc="The title of the button in the menu allowing the user to reload the page.">
         Reload
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE" desc="The iOS menu item for requesting the desktop site [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE" desc="The iOS menu item for requesting the desktop site [iOS only]" meaning="Request the desktop version of the current web site. [Length: Unlimited]">
         Request Desktop Site
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_REQUEST_MOBILE_SITE" desc="The iOS menu item for requesting the mobile site [Length: 25em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_REQUEST_MOBILE_SITE" desc="The iOS menu item for requesting the mobile site [iOS only]" meaning="Request the mobile version of the current web site. [Length: Unlimited]">
         Request Mobile Site
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_SETTINGS" desc="The iOS menu item for opening the settings [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_SETTINGS" desc="The iOS menu item for opening the settings [iOS only]" meaning="Open the settings menu. [Length:Unlimited]">
         Settings
       </message>
       <message name="IDS_IOS_TOOLS_MENU_SHARE" desc="The iOS menu item for sharing the current URL [Length: 15em] [iOS only]">
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.h b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
index 0630cb7e..71b231c 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.h
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.h
@@ -103,17 +103,6 @@
         RefcountedBrowserStateKeyedServiceFactory::TestingFactory
             testing_factory);
 
-    // Deprecated, do not use. This is kept for compatibility with downstream
-    // code and will be removed as soon as the downstream code has been fixed
-    // to use the new interface.
-    void AddTestingFactory(
-        BrowserStateKeyedServiceFactory* service_factory,
-        std::unique_ptr<KeyedService> (*testing_factory)(web::BrowserState*));
-    void AddTestingFactory(
-        RefcountedBrowserStateKeyedServiceFactory* service_factory,
-        scoped_refptr<RefcountedKeyedService> (*testing_factory)(
-            web::BrowserState*));
-
     // Sets the path to the directory to be used to hold ChromeBrowserState
     // data.
     void SetPath(const base::FilePath& path);
diff --git a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
index dee2a25..9f028ca 100644
--- a/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
+++ b/ios/chrome/browser/browser_state/test_chrome_browser_state.mm
@@ -377,25 +377,6 @@
                                              std::move(testing_factory));
 }
 
-void TestChromeBrowserState::Builder::AddTestingFactory(
-    BrowserStateKeyedServiceFactory* service_factory,
-    std::unique_ptr<KeyedService> (*testing_factory)(web::BrowserState*)) {
-  BrowserStateKeyedServiceFactory::TestingFactory wrapped_factory;
-  if (testing_factory)
-    wrapped_factory = base::BindRepeating(testing_factory);
-  AddTestingFactory(service_factory, std::move(wrapped_factory));
-}
-
-void TestChromeBrowserState::Builder::AddTestingFactory(
-    RefcountedBrowserStateKeyedServiceFactory* service_factory,
-    scoped_refptr<RefcountedKeyedService> (*testing_factory)(
-        web::BrowserState*)) {
-  RefcountedBrowserStateKeyedServiceFactory::TestingFactory wrapped_factory;
-  if (testing_factory)
-    wrapped_factory = base::BindRepeating(testing_factory);
-  AddTestingFactory(service_factory, std::move(wrapped_factory));
-}
-
 void TestChromeBrowserState::Builder::SetPath(const base::FilePath& path) {
   DCHECK(!build_called_);
   state_path_ = path;
diff --git a/ios/chrome/browser/google/google_logo_service.mm b/ios/chrome/browser/google/google_logo_service.mm
index 6566328..a32eb82 100644
--- a/ios/chrome/browser/google/google_logo_service.mm
+++ b/ios/chrome/browser/google/google_logo_service.mm
@@ -37,17 +37,17 @@
 GoogleLogoService::GoogleLogoService(
     TemplateURLService* template_url_service,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : LogoServiceImpl(DoodleDirectory(),
-                      // Personalized Doodles aren't supported on iOS (see
-                      // https://crbug.com/711314), so no need to pass a
-                      // GaiaCookieManagerService.
-                      /*cookie_service=*/nullptr,
-                      template_url_service,
-                      image_fetcher::CreateIOSImageDecoder(),
-                      std::move(url_loader_factory),
-                      /*want_gray_logo_getter=*/base::BindRepeating([] {
-                        return !IsUIRefreshPhase1Enabled();
-                      })) {}
+    : LogoServiceImpl(
+          DoodleDirectory(),
+          // Personalized Doodles aren't supported on iOS (see
+          // https://crbug.com/711314), so no need to pass a
+          // GaiaCookieManagerService.
+          /*cookie_service=*/nullptr,
+          template_url_service,
+          image_fetcher::CreateIOSImageDecoder(),
+          std::move(url_loader_factory),
+          /*want_gray_logo_getter=*/base::BindRepeating([] { return false; })) {
+}
 
 GoogleLogoService::~GoogleLogoService() {}
 
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
index 7509e87..204dbc5 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
@@ -29,7 +29,6 @@
 #include "components/ntp_snippets/features.h"
 #include "components/ntp_snippets/logger.h"
 #include "components/ntp_snippets/ntp_snippets_constants.h"
-#include "components/ntp_snippets/reading_list/reading_list_suggestions_provider.h"
 #include "components/ntp_snippets/remote/persistent_scheduler.h"
 #include "components/ntp_snippets/remote/remote_suggestions_database.h"
 #include "components/ntp_snippets/remote/remote_suggestions_fetcher_impl.h"
@@ -37,7 +36,6 @@
 #include "components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h"
 #include "components/ntp_snippets/remote/remote_suggestions_status_service_impl.h"
 #include "components/ntp_snippets/user_classifier.h"
-#include "components/reading_list/core/reading_list_model.h"
 #include "components/version_info/version_info.h"
 #include "google_apis/google_api_keys.h"
 #include "ios/chrome/browser/application_context.h"
@@ -45,7 +43,6 @@
 #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 #include "ios/chrome/browser/history/history_service_factory.h"
 #include "ios/chrome/browser/pref_names.h"
-#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/common/channel_info.h"
@@ -95,7 +92,6 @@
   ContentSuggestionsService* suggestions_service =
       static_cast<ContentSuggestionsService*>(service.get());
 
-  ntp_snippets::RegisterReadingListProvider(suggestions_service, browser_state);
   if (base::FeatureList::IsEnabled(ntp_snippets::kArticleSuggestionsFeature)) {
     ntp_snippets::RegisterRemoteSuggestionsProvider(suggestions_service,
                                                     browser_state);
@@ -142,24 +138,6 @@
       std::move(scheduler), std::move(debug_logger));
 }
 
-void RegisterReadingListProvider(ContentSuggestionsService* service,
-                                 web::BrowserState* browser_state) {
-  // Prevent loading any reading list items for refresh.
-  if (IsUIRefreshPhase1Enabled())
-    return;
-
-  ios::ChromeBrowserState* chrome_browser_state =
-      ios::ChromeBrowserState::FromBrowserState(browser_state);
-
-  ReadingListModel* reading_list_model =
-      ReadingListModelFactory::GetForBrowserState(chrome_browser_state);
-  std::unique_ptr<ntp_snippets::ReadingListSuggestionsProvider>
-      reading_list_suggestions_provider =
-          std::make_unique<ntp_snippets::ReadingListSuggestionsProvider>(
-              service, reading_list_model);
-  service->RegisterProvider(std::move(reading_list_suggestions_provider));
-}
-
 void RegisterRemoteSuggestionsProvider(ContentSuggestionsService* service,
                                        web::BrowserState* browser_state) {
   ios::ChromeBrowserState* chrome_browser_state =
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h
index 39fb7250..f6ab9fc 100644
--- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h
+++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h
@@ -25,10 +25,6 @@
 std::unique_ptr<KeyedService> CreateChromeContentSuggestionsService(
     web::BrowserState* browser_state);
 
-// Registers the Reading List provider.
-void RegisterReadingListProvider(ContentSuggestionsService* service,
-                                 web::BrowserState* browser_state);
-
 // Registers the RemoteSuggestionsProvider (articles provider).
 void RegisterRemoteSuggestionsProvider(ContentSuggestionsService* service,
                                        web::BrowserState* browser_state);
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc b/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
index f324428..67394d8 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
+++ b/ios/chrome/browser/passwords/ios_chrome_password_store_factory.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind_helpers.h"
 #include "base/command_line.h"
 #include "base/memory/singleton.h"
 #include "base/sequenced_task_runner.h"
@@ -102,8 +103,8 @@
     return nullptr;
   }
   password_manager_util::RemoveUselessCredentials(
-      store, ios::ChromeBrowserState::FromBrowserState(context)->GetPrefs(),
-      60);
+      store, ios::ChromeBrowserState::FromBrowserState(context)->GetPrefs(), 60,
+      base::NullCallback());
   return store;
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
index bfc2199..f19f6b7f 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.mm
@@ -4,12 +4,10 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_cell.h"
 
-#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/util/i18n_string.h"
 #import "ios/chrome/common/favicon/favicon_view.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
-#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
 #import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -18,11 +16,8 @@
 
 namespace {
 const CGFloat kImageSize = 80;
-const CGFloat kImageSizeLegacy = 62;
 const CGFloat kStandardSpacing = 13;
-const CGFloat kStandardSpacingLegacy = 16;
 const CGFloat kSmallSpacing = 7;
-const CGFloat kSmallSpacingLegacy = 8;
 
 // Size of the favicon view.
 const CGFloat kFaviconSize = 16;
@@ -59,10 +54,6 @@
 // Applies the constraints on the elements. Called in the init.
 - (void)applyConstraints;
 
-// Helpers to account for legacy sizes
-+ (CGFloat)imageSize;
-+ (CGFloat)smallSpacing;
-
 @end
 
 @implementation ContentSuggestionsCell
@@ -82,10 +73,8 @@
   if (self) {
     _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
     _imageContainer = [[UIView alloc] initWithFrame:CGRectZero];
-    if (IsUIRefreshPhase1Enabled()) {
-      _imageContainer.layer.cornerRadius = 11;
-      _imageContainer.layer.masksToBounds = YES;
-    }
+    _imageContainer.layer.cornerRadius = 11;
+    _imageContainer.layer.masksToBounds = YES;
     _noImageIcon = [[UIImageView alloc] initWithFrame:CGRectZero];
     _additionalInformationLabel = [[UILabel alloc] initWithFrame:CGRectZero];
     _contentImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
@@ -120,13 +109,8 @@
     [[self class] configureTitleLabel:_titleLabel];
     _additionalInformationLabel.font = [[self class] additionalInformationFont];
     _faviconView.font = [[MDCTypography fontLoader] mediumFontOfSize:10];
-    if (IsUIRefreshPhase1Enabled()) {
-      _additionalInformationLabel.textColor =
-          [UIColor colorWithWhite:0 alpha:0.54];
-    } else {
-      _additionalInformationLabel.textColor =
-          [[MDCPalette greyPalette] tint700];
-    }
+    _additionalInformationLabel.textColor =
+        [UIColor colorWithWhite:0 alpha:0.54];
 
     [self applyConstraints];
   }
@@ -168,8 +152,8 @@
 
 - (void)setDisplayImage:(BOOL)displayImage {
   if (displayImage) {
-    self.imageTitleSpacing.constant = [[self class] standardSpacing];
-    self.imageSizeConstraint.constant = [[self class] imageSize];
+    self.imageTitleSpacing.constant = kStandardSpacing;
+    self.imageSizeConstraint.constant = kImageSize;
     self.imageContainer.hidden = NO;
   } else {
     self.imageTitleSpacing.constant = 0;
@@ -196,28 +180,15 @@
     CGSize sizeForLabels =
         CGSizeMake(width - [self labelMarginWithImage:hasImage], 500);
 
-    if (IsUIRefreshPhase1Enabled()) {
-      CGFloat minimalHeight =
-          [[self class] imageSize] + [[self class] standardSpacing];
+    CGFloat minimalHeight = kImageSize + kStandardSpacing;
 
-      CGFloat labelHeight = [[self class] standardSpacing];
-      labelHeight += [titleLabel sizeThatFits:sizeForLabels].height;
-      labelHeight += [[self class] smallSpacing];
-      CGFloat additionalInfoHeight =
-          [additionalInfoLabel sizeThatFits:sizeForLabels].height;
-      labelHeight += MAX(additionalInfoHeight, kFaviconSize);
-      return MAX(minimalHeight, labelHeight);
-    } else {
-      CGFloat labelHeight = 3 * [[self class] standardSpacing];
-      labelHeight += [titleLabel sizeThatFits:sizeForLabels].height;
-      CGFloat additionalInfoHeight =
-          [additionalInfoLabel sizeThatFits:sizeForLabels].height;
-      labelHeight += MAX(additionalInfoHeight, kFaviconSize);
-
-      CGFloat minimalHeight = hasImage ? [[self class] imageSize] : 0;
-      minimalHeight += 2 * [[self class] standardSpacing];
-      return MAX(minimalHeight, labelHeight);
-  }
+    CGFloat labelHeight = kStandardSpacing;
+    labelHeight += [titleLabel sizeThatFits:sizeForLabels].height;
+    labelHeight += kSmallSpacing;
+    CGFloat additionalInfoHeight =
+        [additionalInfoLabel sizeThatFits:sizeForLabels].height;
+    labelHeight += MAX(additionalInfoHeight, kFaviconSize);
+    return MAX(minimalHeight, labelHeight);
 }
 
 #pragma mark - UICollectionViewCell
@@ -232,9 +203,8 @@
 
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
   [super traitCollectionDidChange:previousTraitCollection];
-  if (IsUIRefreshPhase1Enabled() &&
-      self.traitCollection.preferredContentSizeCategory !=
-          previousTraitCollection.preferredContentSizeCategory) {
+  if (self.traitCollection.preferredContentSizeCategory !=
+      previousTraitCollection.preferredContentSizeCategory) {
     [[self class] configureTitleLabel:_titleLabel];
     _additionalInformationLabel.font = [[self class] additionalInformationFont];
   }
@@ -252,8 +222,7 @@
   self.titleLabel.preferredMaxLayoutWidth =
       parentWidth - [[self class] labelMarginWithImage:self.displayImage];
   self.additionalInformationLabel.preferredMaxLayoutWidth =
-      parentWidth - kFaviconSize - [[self class] smallSpacing] -
-      2 * [[self class] standardSpacing];
+      parentWidth - kFaviconSize - kSmallSpacing - 2 * kStandardSpacing;
 
   // Re-layout with the new preferred width to allow the label to adjust its
   // height.
@@ -263,43 +232,17 @@
 #pragma mark - Private
 
 - (void)applyConstraints {
-  _imageSizeConstraint = [_imageContainer.heightAnchor
-      constraintEqualToConstant:[[self class] imageSize]];
+  _imageSizeConstraint =
+      [_imageContainer.heightAnchor constraintEqualToConstant:kImageSize];
   _imageTitleSpacing = [_titleLabel.leadingAnchor
       constraintEqualToAnchor:_imageContainer.trailingAnchor
-                     constant:[[self class] standardSpacing]];
-
-  if (IsUIRefreshPhase1Enabled()) {
-    [NSLayoutConstraint activateConstraints:@[
-      [_imageContainer.bottomAnchor
-          constraintLessThanOrEqualToAnchor:_faviconView.bottomAnchor],
-      [_faviconView.topAnchor
-          constraintGreaterThanOrEqualToAnchor:self.titleLabel.bottomAnchor
-                                      constant:[[self class] smallSpacing]]
-    ]];
-  } else {
-    [NSLayoutConstraint activateConstraints:@[
-      [_imageContainer.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
-                                   constant:-[[self class] standardSpacing]],
-      [_additionalInformationLabel.topAnchor
-          constraintGreaterThanOrEqualToAnchor:_titleLabel.bottomAnchor
-                                      constant:[[self class] standardSpacing]],
-      [_additionalInformationLabel.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
-                                   constant:-[[self class] standardSpacing]],
-      [_faviconView.topAnchor
-          constraintGreaterThanOrEqualToAnchor:_titleLabel.bottomAnchor
-                                      constant:[[self class] standardSpacing]],
-      [_faviconView.bottomAnchor
-          constraintLessThanOrEqualToAnchor:self.contentView.bottomAnchor
-                                   constant:-[[self class] standardSpacing]],
-    ]];
-  }
+                     constant:kStandardSpacing];
 
   [NSLayoutConstraint activateConstraints:@[
     // Image.
     _imageSizeConstraint,
+    [_imageContainer.bottomAnchor
+        constraintLessThanOrEqualToAnchor:_faviconView.bottomAnchor],
     [_imageContainer.widthAnchor
         constraintEqualToAnchor:_imageContainer.heightAnchor],
     [_imageContainer.topAnchor constraintEqualToAnchor:_titleLabel.topAnchor],
@@ -319,6 +262,9 @@
     [_faviconView.heightAnchor constraintEqualToConstant:kFaviconSize],
     [_faviconView.widthAnchor
         constraintEqualToAnchor:_faviconView.heightAnchor],
+    [_faviconView.topAnchor
+        constraintGreaterThanOrEqualToAnchor:self.titleLabel.bottomAnchor
+                                    constant:kSmallSpacing],
 
     // No image icon.
     [_noImageIcon.centerXAnchor
@@ -344,60 +290,39 @@
         @"additional" : _additionalInformationLabel,
         @"favicon" : _faviconView,
       },
-      @{
-        @"space" : @([[self class] standardSpacing]),
-        @"small" : @([[self class] smallSpacing])
-      });
-}
-
-+ (CGFloat)imageSize {
-  return IsUIRefreshPhase1Enabled() ? kImageSize : kImageSizeLegacy;
+      @{ @"space" : @(kStandardSpacing),
+         @"small" : @(kSmallSpacing) });
 }
 
 + (CGFloat)standardSpacing {
-  return IsUIRefreshPhase1Enabled() ? kStandardSpacing : kStandardSpacingLegacy;
-}
-
-+ (CGFloat)smallSpacing {
-  return IsUIRefreshPhase1Enabled() ? kSmallSpacing : kSmallSpacingLegacy;
+  return kStandardSpacing;
 }
 
 // Configures the |titleLabel|.
 + (void)configureTitleLabel:(UILabel*)titleLabel {
-  if (IsUIRefreshPhase1Enabled()) {
-    titleLabel.textColor = [UIColor colorWithWhite:0 alpha:0.8];
-    UIFontDescriptor* descriptor = [[UIFontDescriptor
-        preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline]
-        fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
-    titleLabel.font = [UIFont fontWithDescriptor:descriptor size:0];
-    titleLabel.numberOfLines = 3;
-  } else {
-    titleLabel.font = [MDCTypography subheadFont];
-    titleLabel.numberOfLines = 2;
-  }
+  titleLabel.textColor = [UIColor colorWithWhite:0 alpha:0.8];
+  UIFontDescriptor* descriptor = [[UIFontDescriptor
+      preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline]
+      fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
+  titleLabel.font = [UIFont fontWithDescriptor:descriptor size:0];
+  titleLabel.numberOfLines = 3;
 }
 
 // Returns the font used to display the additional informations.
 + (UIFont*)additionalInformationFont {
-  if (IsUIRefreshPhase1Enabled()) {
-    return [UIFont preferredFontForTextStyle:UIFontTextStyleCaption2];
-  } else {
-    return [MDCTypography captionFont];
-  }
+  return [UIFont preferredFontForTextStyle:UIFontTextStyleCaption2];
 }
 
 // Returns the margin for the labels, depending if the cell |hasImage|.
 + (CGFloat)labelMarginWithImage:(BOOL)hasImage {
-  CGFloat offset =
-      hasImage ? [[self class] imageSize] + [[self class] standardSpacing] : 0;
-  return 2 * [[self class] standardSpacing] + offset;
+  CGFloat offset = hasImage ? kImageSize + kStandardSpacing : 0;
+  return 2 * kStandardSpacing + offset;
 }
 
 // Returns the attributed string to be displayed.
 + (NSString*)stringForPublisher:(NSString*)publisherName date:(NSString*)date {
-  NSString* sep = IsUIRefreshPhase1Enabled() ? @"•" : @"-";
   return AdjustStringForLocaleDirection(
-      [NSString stringWithFormat:@"%@ %@ %@ ", publisherName, sep, date]);
+      [NSString stringWithFormat:@"%@ • %@ ", publisherName, date]);
 }
 
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_footer_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_footer_item.mm
index b2e7062..c2e5ade 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_footer_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_footer_item.mm
@@ -14,7 +14,6 @@
 #endif
 
 namespace {
-const CGFloat kButtonMargin = 2;
 const CGFloat kButtonPadding = 16;
 }
 
@@ -89,10 +88,8 @@
     _activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
     _button = [UIButton buttonWithType:UIButtonTypeSystem];
     _button.translatesAutoresizingMaskIntoConstraints = NO;
-    if (IsUIRefreshPhase1Enabled()) {
-      _button.titleLabel.font =
-          [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
-    }
+    _button.titleLabel.font =
+        [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
     _button.contentEdgeInsets =
         UIEdgeInsetsMake(0, kButtonPadding, 0, kButtonPadding);
     [_button addTarget:self
@@ -103,14 +100,7 @@
     [self.contentView addSubview:_activityIndicator];
 
     AddSameConstraints(self.contentView, _activityIndicator);
-    if (IsUIRefreshPhase1Enabled()) {
-      AddSameConstraints(self.contentView, _button);
-    } else {
-      ApplyVisualConstraintsWithMetrics(
-          @[ @"V:|-(margin)-[button]-(margin)-|", @"H:|-(margin)-[button]" ],
-          @{@"button" : _button},
-          @{ @"margin" : @(kButtonMargin) });
-    }
+    AddSameConstraints(self.contentView, _button);
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm
index 440a16a..ffda5aea0 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm
@@ -112,11 +112,7 @@
 + (void)configureLabel:(UILabel*)label withText:(NSString*)text {
   label.numberOfLines = 0;
   label.textColor = [[MDCPalette greyPalette] tint700];
-  if (IsUIRefreshPhase1Enabled()) {
-    label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
-  } else {
-    label.font = [MDCTypography italicFontFromFont:[MDCTypography captionFont]];
-  }
+  label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
   NSRange linkRange;
   NSString* strippedText = ParseStringWithLink(text, &linkRange);
   DCHECK_NE(NSNotFound, static_cast<NSInteger>(linkRange.location));
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
index 14b67ee..c0c11286 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_cell.mm
@@ -9,18 +9,11 @@
 #import "ios/chrome/common/favicon/favicon_view.h"
 #import "ios/chrome/common/material_timing.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-namespace {
-
-const CGFloat kIconSizeLegacy = 48;
-
-}  // namespace
-
 @implementation ContentSuggestionsMostVisitedCell : MDCCollectionViewCell
 
 @synthesize faviconView = _faviconView;
@@ -32,62 +25,39 @@
   self = [super initWithFrame:frame];
   if (self) {
     _titleLabel = [[UILabel alloc] init];
-    if (IsUIRefreshPhase1Enabled()) {
-      _titleLabel.textColor = [UIColor colorWithWhite:0 alpha:kTitleAlpha];
-      _titleLabel.font = [UIFont systemFontOfSize:12];
-    } else {
-      _titleLabel.textColor =
-          [UIColor colorWithWhite:kLabelTextColor alpha:1.0];
-      _titleLabel.font = [MDCTypography captionFont];
-    }
+    _titleLabel.textColor = [UIColor colorWithWhite:0 alpha:kTitleAlpha];
+    _titleLabel.font = [UIFont systemFontOfSize:12];
     _titleLabel.textAlignment = NSTextAlignmentCenter;
     _titleLabel.preferredMaxLayoutWidth = [[self class] defaultSize].width;
     _titleLabel.numberOfLines = kLabelNumLines;
 
     _faviconView = [[FaviconViewNew alloc] init];
-    if (IsUIRefreshPhase1Enabled()) {
-      _faviconView.font = [UIFont systemFontOfSize:22];
-    } else {
-      _faviconView.font = [MDCTypography headlineFont];
-    }
+    _faviconView.font = [UIFont systemFontOfSize:22];
     _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
     _faviconView.translatesAutoresizingMaskIntoConstraints = NO;
 
     [self.contentView addSubview:_titleLabel];
 
     UIView* containerView = nil;
-    if (IsUIRefreshPhase1Enabled()) {
-      UIImageView* faviconContainer =
-          [[UIImageView alloc] initWithFrame:self.bounds];
-      faviconContainer.translatesAutoresizingMaskIntoConstraints = NO;
-      faviconContainer.image = [UIImage imageNamed:@"ntp_most_visited_tile"];
-      [self.contentView addSubview:faviconContainer];
-      [faviconContainer addSubview:_faviconView];
+    UIImageView* faviconContainer =
+        [[UIImageView alloc] initWithFrame:self.bounds];
+    faviconContainer.translatesAutoresizingMaskIntoConstraints = NO;
+    faviconContainer.image = [UIImage imageNamed:@"ntp_most_visited_tile"];
+    [self.contentView addSubview:faviconContainer];
+    [faviconContainer addSubview:_faviconView];
 
-      [NSLayoutConstraint activateConstraints:@[
-        [faviconContainer.widthAnchor constraintEqualToConstant:kIconSize],
-        [faviconContainer.heightAnchor
-            constraintEqualToAnchor:faviconContainer.widthAnchor],
-        [faviconContainer.centerXAnchor
-            constraintEqualToAnchor:_titleLabel.centerXAnchor],
-        [_faviconView.heightAnchor constraintEqualToConstant:32],
-        [_faviconView.widthAnchor
-            constraintEqualToAnchor:_faviconView.heightAnchor],
-      ]];
-      AddSameCenterConstraints(_faviconView, faviconContainer);
-      containerView = faviconContainer;
-    } else {
-      [self.contentView addSubview:_faviconView];
-
-      [NSLayoutConstraint activateConstraints:@[
-        [_faviconView.widthAnchor constraintEqualToConstant:kIconSizeLegacy],
-        [_faviconView.heightAnchor
-            constraintEqualToAnchor:_faviconView.widthAnchor],
-        [_faviconView.centerXAnchor
-            constraintEqualToAnchor:_titleLabel.centerXAnchor],
-      ]];
-      containerView = _faviconView;
-    }
+    [NSLayoutConstraint activateConstraints:@[
+      [faviconContainer.widthAnchor constraintEqualToConstant:kIconSize],
+      [faviconContainer.heightAnchor
+          constraintEqualToAnchor:faviconContainer.widthAnchor],
+      [faviconContainer.centerXAnchor
+          constraintEqualToAnchor:_titleLabel.centerXAnchor],
+      [_faviconView.heightAnchor constraintEqualToConstant:32],
+      [_faviconView.widthAnchor
+          constraintEqualToAnchor:_faviconView.heightAnchor],
+    ]];
+    AddSameCenterConstraints(_faviconView, faviconContainer);
+    containerView = faviconContainer;
 
     ApplyVisualConstraintsWithMetrics(
         @[ @"V:|[container]-(space)-[title]", @"H:|[title]|" ],
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
index 45fa7bd..55af203 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -663,59 +663,31 @@
 
   if (![model headerForSectionWithIdentifier:sectionIdentifier] &&
       sectionInfo.title) {
-    BOOL addHeader = YES;
-
-    if (IsFromContentSuggestionsService(sectionIdentifier)) {
-      addHeader = IsUIRefreshPhase1Enabled();
-
-      if ([self.sectionIdentifiersFromContentSuggestions
-              containsObject:@(sectionIdentifier)]) {
-        return;
-      }
-
-      if ([self.sectionIdentifiersFromContentSuggestions count] == 1) {
-        NSNumber* existingSectionIdentifier =
-            [self.sectionIdentifiersFromContentSuggestions anyObject];
-        ContentSuggestionsSectionInformation* existingSectionInfo =
-            self.sectionInfoBySectionIdentifier[existingSectionIdentifier];
-        [model setHeader:[self headerForSectionInfo:existingSectionInfo]
-            forSectionWithIdentifier:[existingSectionIdentifier integerValue]];
-        addHeader = YES;
-      } else if ([self.sectionIdentifiersFromContentSuggestions count] > 1) {
-        addHeader = YES;
-      }
-
-      [self.sectionIdentifiersFromContentSuggestions
-          addObject:@(sectionIdentifier)];
+    DCHECK(IsFromContentSuggestionsService(sectionIdentifier));
+    if ([self.sectionIdentifiersFromContentSuggestions
+            containsObject:@(sectionIdentifier)]) {
+      return;
     }
-
-    if (addHeader) {
-      [model setHeader:[self headerForSectionInfo:sectionInfo]
-          forSectionWithIdentifier:sectionIdentifier];
-    }
+    [self.sectionIdentifiersFromContentSuggestions
+        addObject:@(sectionIdentifier)];
+    [model setHeader:[self headerForSectionInfo:sectionInfo]
+        forSectionWithIdentifier:sectionIdentifier];
   }
 }
 
 // Returns the header for this |sectionInfo|.
 - (CollectionViewItem*)headerForSectionInfo:
     (ContentSuggestionsSectionInformation*)sectionInfo {
-  if (IsUIRefreshPhase1Enabled()) {
-    DCHECK(SectionIdentifierForInfo(sectionInfo) == SectionIdentifierArticles);
-    __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
-    ContentSuggestionsArticlesHeaderItem* header =
-        [[ContentSuggestionsArticlesHeaderItem alloc]
-            initWithType:ItemTypeHeader
-                   title:sectionInfo.title
-                callback:^{
-                  [weakSelf.dataSource toggleArticlesVisibility];
-                }];
-    header.expanded = sectionInfo.expanded;
-    return header;
-  }
-  CollectionViewTextItem* header =
-      [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader];
-  header.text = sectionInfo.title;
-  header.textColor = [[MDCPalette greyPalette] tint500];
+  DCHECK(SectionIdentifierForInfo(sectionInfo) == SectionIdentifierArticles);
+  __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
+  ContentSuggestionsArticlesHeaderItem* header =
+      [[ContentSuggestionsArticlesHeaderItem alloc]
+          initWithType:ItemTypeHeader
+                 title:sectionInfo.title
+              callback:^{
+                [weakSelf.dataSource toggleArticlesVisibility];
+              }];
+  header.expanded = sectionInfo.expanded;
   return header;
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
index 2c1cfd8..f40b75f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h
@@ -59,10 +59,6 @@
 // Returns the nearest ancestor of |view| that is kind of |aClass|.
 UIView* nearestAncestor(UIView* view, Class aClass);
 
-// Helper methods to support RxR for UIRefreshPhase1 and IpadIdiom pre-Refresh.
-BOOL IsRegularXRegularSizeClass(id<UITraitEnvironment> environment);
-BOOL IsRegularXRegularSizeClass();
-
 }  // namespace content_suggestions
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COLLECTION_UTILS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
index 562adf2..f40579f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.mm
@@ -13,7 +13,6 @@
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.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)
@@ -26,32 +25,21 @@
 const CGFloat kHorizontalSpacingRegularXRegular = 19;
 const CGFloat kHorizontalSpacingOther = 9;
 const CGFloat kVerticalSpacing = 16;
-const CGFloat kSpacingIPhone = 16;
-const CGFloat kSpacingIPad = 24;
 
 // Width of search field.
 const CGFloat kSearchFieldLarge = 432;
 const CGFloat kSearchFieldSmall = 343;
 const CGFloat kSearchFieldMinMargin = 8;
-const CGFloat kMinSearchFieldWidthLegacy = 50;
 
 // Veritcla margin of search hint text.
 const CGFloat kSearchHintMargin = 3;
 
-// Offset to align the hint of the fake omnibox with the one in the toolbar.
-const CGFloat kSearchHintVerticalOffset = 0.5;
-// Maximum margin for the search field.
-const CGFloat kMaxSearchFieldFrameMargin = 200;
-
 // Top margin for the doodle.
 const CGFloat kDoodleTopMarginRegularXRegular = 162;
 const CGFloat kDoodleTopMarginOther = 58;
-const CGFloat kDoodleTopMarginIPadLegacy = 82;
 
 // Top margin for the search field
 const CGFloat kSearchFieldTopMargin = 32;
-const CGFloat kSearchFieldTopMarginIPhoneLegacy = 32;
-const CGFloat kSearchFieldTopMarginIPadLegacy = 82;
 
 // Bottom margin for the search field.
 const CGFloat kNTPSearchFieldBottomPadding = 16;
@@ -73,13 +61,6 @@
 // engine.
 const CGFloat kNonGoogleSearchHeaderHeightIPad = 10;
 
-// Returns the width necessary to fit |numberOfItem| items, with no padding on
-// the side.
-CGFloat widthForNumberOfItem(NSUInteger numberOfItem) {
-  return (numberOfItem - 1) *
-             content_suggestions::horizontalSpacingBetweenTiles() +
-         numberOfItem * [ContentSuggestionsMostVisitedCell defaultSize].width;
-}
 }
 
 namespace content_suggestions {
@@ -91,36 +72,17 @@
 const NSUInteger kMostVisitedItemsPerLine = 4;
 
 NSUInteger numberOfTilesForWidth(CGFloat availableWidth) {
-  if (IsUIRefreshPhase1Enabled()) {
-    return kMostVisitedItemsPerLine;
-  }
-
-  if (availableWidth > widthForNumberOfItem(4))
-    return 4;
-  if (availableWidth > widthForNumberOfItem(3))
-    return 3;
-  if (availableWidth > widthForNumberOfItem(2))
-    return 2;
-
-  return 1;
+  return kMostVisitedItemsPerLine;
 }
 
 CGFloat horizontalSpacingBetweenTiles() {
-  if (IsUIRefreshPhase1Enabled()) {
-    return (!IsCompactWidth() && !IsCompactHeight())
-               ? kHorizontalSpacingRegularXRegular
-               : kHorizontalSpacingOther;
-  } else {
-    return IsIPadIdiom() ? kSpacingIPad : kSpacingIPhone;
-  }
+  return (!IsCompactWidth() && !IsCompactHeight())
+             ? kHorizontalSpacingRegularXRegular
+             : kHorizontalSpacingOther;
 }
 
 CGFloat verticalSpacingBetweenTiles() {
-  if (IsUIRefreshPhase1Enabled()) {
-    return kVerticalSpacing;
-  } else {
-    return horizontalSpacingBetweenTiles();
-  }
+  return kVerticalSpacing;
 }
 
 CGFloat centeredTilesMarginForWidth(CGFloat width) {
@@ -131,14 +93,10 @@
       (columns * [ContentSuggestionsMostVisitedCell defaultSize].width) -
       ((columns - 1) * horizontalSpace);
   CGFloat margin = AlignValueToPixel(whitespace / 2);
-  if (IsUIRefreshPhase1Enabled()) {
-    // Allow for less spacing as an edge case on smaller devices.
-    if (margin < horizontalSpace) {
-      DCHECK(width < 400);  // For now this is only expected on small widths.
-      return fmaxf(margin, 0);
-    }
-  } else {
-    DCHECK(margin > horizontalSpace);
+  // Allow for less spacing as an edge case on smaller devices.
+  if (margin < horizontalSpace) {
+    DCHECK(width < 400);  // For now this is only expected on small widths.
+    return fmaxf(margin, 0);
   }
   return margin;
 }
@@ -151,37 +109,21 @@
 }
 
 CGFloat doodleTopMargin(BOOL toolbarPresent) {
-  if (IsUIRefreshPhase1Enabled()) {
-    if (!IsCompactWidth() && !IsCompactHeight())
-      return kDoodleTopMarginRegularXRegular;
-    return StatusBarHeight() + kDoodleTopMarginOther;
-  }
-  if (IsIPadIdiom())
-    return kDoodleTopMarginIPadLegacy;
-  return toolbarPresent ? ntp_header::ToolbarHeight() : 0;
+  if (!IsCompactWidth() && !IsCompactHeight())
+    return kDoodleTopMarginRegularXRegular;
+  return StatusBarHeight() + kDoodleTopMarginOther;
 }
 
 CGFloat searchFieldTopMargin() {
-  if (IsUIRefreshPhase1Enabled()) {
-    return kSearchFieldTopMargin;
-  }
-  if (IsIPadIdiom())
-    return kSearchFieldTopMarginIPadLegacy;
-  return kSearchFieldTopMarginIPhoneLegacy;
+  return kSearchFieldTopMargin;
 }
 
 CGFloat searchFieldWidth(CGFloat superviewWidth) {
-  if (IsUIRefreshPhase1Enabled()) {
-    if (!IsCompactWidth() && !IsCompactHeight())
-      return kSearchFieldLarge;
+  if (!IsCompactWidth() && !IsCompactHeight())
+    return kSearchFieldLarge;
 
-    // Special case for narrow sizes.
-    return MIN(kSearchFieldSmall, superviewWidth - kSearchFieldMinMargin * 2);
-  }
-  CGFloat margin = centeredTilesMarginForWidth(superviewWidth);
-  if (margin > kMaxSearchFieldFrameMargin)
-    margin = kMaxSearchFieldFrameMargin;
-  return fmax(superviewWidth - 2 * margin, kMinSearchFieldWidthLegacy);
+  // Special case for narrow sizes.
+  return MIN(kSearchFieldSmall, superviewWidth - kSearchFieldMinMargin * 2);
 }
 
 CGFloat heightForLogoHeader(BOOL logoIsShowing,
@@ -208,37 +150,24 @@
   [searchHintLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
   [searchTapTarget addSubview:searchHintLabel];
 
-  CGFloat centerYOffsetConstant =
-      IsUIRefreshPhase1Enabled() ? 0 : kSearchHintVerticalOffset;
   [NSLayoutConstraint activateConstraints:@[
     [searchHintLabel.centerYAnchor
-        constraintEqualToAnchor:searchTapTarget.centerYAnchor
-                       constant:centerYOffsetConstant],
+        constraintEqualToAnchor:searchTapTarget.centerYAnchor],
     [searchHintLabel.heightAnchor
         constraintEqualToConstant:kSearchFieldHeight - 2 * kSearchHintMargin],
   ]];
 
-  if (IsUIRefreshPhase1Enabled()) {
-    [searchHintLabel.centerXAnchor
-        constraintEqualToAnchor:searchTapTarget.centerXAnchor]
-        .active = YES;
-  }
+  [searchHintLabel.centerXAnchor
+      constraintEqualToAnchor:searchTapTarget.centerXAnchor]
+      .active = YES;
 
   [searchHintLabel setText:l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT)];
   if (base::i18n::IsRTL()) {
     [searchHintLabel setTextAlignment:NSTextAlignmentRight];
   }
-  if (IsUIRefreshPhase1Enabled()) {
-    [searchHintLabel setTextColor:[UIColor colorWithWhite:0 alpha:kHintAlpha]];
-    searchHintLabel.font = [UIFont systemFontOfSize:17];
-    searchHintLabel.textAlignment = NSTextAlignmentCenter;
-  } else {
-    [searchHintLabel
-        setTextColor:
-            [UIColor colorWithWhite:kiPhoneLocationBarPlaceholderColorBrightness
-                              alpha:1.0]];
-    [searchHintLabel setFont:[MDCTypography subheadFont]];
-  }
+  [searchHintLabel setTextColor:[UIColor colorWithWhite:0 alpha:kHintAlpha]];
+  searchHintLabel.font = [UIFont systemFontOfSize:17];
+  searchHintLabel.textAlignment = NSTextAlignmentCenter;
 }
 
 void configureVoiceSearchButton(UIButton* voiceSearchButton,
@@ -257,15 +186,10 @@
 
   [voiceSearchButton setAdjustsImageWhenHighlighted:NO];
 
-  UIImage* micImage =
-      IsUIRefreshPhase1Enabled()
-          ? [[UIImage imageNamed:@"location_bar_voice"]
-                imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]
-          : [UIImage imageNamed:@"voice_icon"];
+  UIImage* micImage = [[UIImage imageNamed:@"location_bar_voice"]
+      imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
   [voiceSearchButton setImage:micImage forState:UIControlStateNormal];
-  if (IsUIRefreshPhase1Enabled()) {
-    voiceSearchButton.tintColor = [UIColor colorWithWhite:0 alpha:0.7];
-  }
+  voiceSearchButton.tintColor = [UIColor colorWithWhite:0 alpha:0.7];
   [voiceSearchButton setAccessibilityLabel:l10n_util::GetNSString(
                                                IDS_IOS_ACCNAME_VOICE_SEARCH)];
   [voiceSearchButton setAccessibilityIdentifier:@"Voice Search"];
@@ -281,17 +205,4 @@
   return nearestAncestor([view superview], aClass);
 }
 
-// Content suggestion dupliations of uikit_ui_util to allow wrapping behind
-// the refresh flag.  Post refresh remove these helpers and just check
-// IsRxRSC instead.
-BOOL IsRegularXRegularSizeClass(id<UITraitEnvironment> environment) {
-  return IsUIRefreshPhase1Enabled() ? ::IsRegularXRegularSizeClass(environment)
-                                    : IsIPadIdiom();
-}
-
-BOOL IsRegularXRegularSizeClass() {
-  return IsUIRefreshPhase1Enabled() ? ::IsRegularXRegularSizeClass()
-                                    : IsIPadIdiom();
-}
-
 }  // namespace content_suggestions
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
index 6b1becd..da5bf73 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils_unittest.mm
@@ -89,26 +89,16 @@
   // Setup.
   SetAsIPhone();
 
-  if (IsUIRefreshPhase1Enabled()) {
-    CGFloat result = centeredTilesMarginForWidth(375);
-    EXPECT_EQ(28, result);
-  } else {
-    CGFloat result = centeredTilesMarginForWidth(374);
-    EXPECT_EQ(17, result);
-  }
+  CGFloat result = centeredTilesMarginForWidth(375);
+  EXPECT_EQ(28, result);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, centeredTilesMarginIPad) {
   // Setup.
   SetAsIPad();
 
-  if (IsUIRefreshPhase1Enabled()) {
-    CGFloat result = centeredTilesMarginForWidth(767);
-    EXPECT_EQ(209, result);
-  } else {
-    CGFloat result = centeredTilesMarginForWidth(700);
-    EXPECT_EQ(168, result);
-  }
+  CGFloat result = centeredTilesMarginForWidth(767);
+  EXPECT_EQ(209, result);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPad) {
@@ -118,17 +108,10 @@
   // Action.
   CGFloat height = doodleHeight(YES);
   CGFloat topMargin = doodleTopMargin(YES);
-  CGFloat topMarginNoToolbar = doodleTopMargin(NO);
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(120, height);
-    EXPECT_EQ(162, topMargin);
-  } else {
-    EXPECT_EQ(120, height);
-    EXPECT_EQ(82, topMargin);
-    EXPECT_EQ(82, topMarginNoToolbar);
-  }
+  EXPECT_EQ(120, height);
+  EXPECT_EQ(162, topMargin);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPhonePortrait) {
@@ -140,19 +123,11 @@
   CGFloat heightLogo = doodleHeight(YES);
   CGFloat heightNoLogo = doodleHeight(NO);
   CGFloat topMargin = doodleTopMargin(YES);
-  CGFloat topMarginNoToolbar = doodleTopMargin(NO);
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(120, heightLogo);
-    EXPECT_EQ(60, heightNoLogo);
-    EXPECT_EQ(58, topMargin);
-  } else {
-    EXPECT_EQ(120, heightLogo);
-    EXPECT_EQ(60, heightNoLogo);
-    EXPECT_EQ(56, topMargin);
-    EXPECT_EQ(0, topMarginNoToolbar);
-  }
+  EXPECT_EQ(120, heightLogo);
+  EXPECT_EQ(60, heightNoLogo);
+  EXPECT_EQ(58, topMargin);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, doodleFrameIPhoneLandscape) {
@@ -164,19 +139,11 @@
   CGFloat heightLogo = doodleHeight(YES);
   CGFloat heightNoLogo = doodleHeight(NO);
   CGFloat topMargin = doodleTopMargin(YES);
-  CGFloat topMarginNoToolbar = doodleTopMargin(NO);
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(120, heightLogo);
-    EXPECT_EQ(60, heightNoLogo);
-    EXPECT_EQ(58, topMargin);
-  } else {
-    EXPECT_EQ(120, heightLogo);
-    EXPECT_EQ(60, heightNoLogo);
-    EXPECT_EQ(56, topMargin);
-    EXPECT_EQ(0, topMarginNoToolbar);
-  }
+  EXPECT_EQ(120, heightLogo);
+  EXPECT_EQ(60, heightNoLogo);
+  EXPECT_EQ(58, topMargin);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPad) {
@@ -184,7 +151,6 @@
   SetAsIPad();
   CGFloat width = 500;
   CGFloat largeIPadWidth = 1366;
-  CGFloat margin = centeredTilesMarginForWidth(width);
 
   // Action.
   CGFloat resultWidth = searchFieldWidth(width);
@@ -192,15 +158,9 @@
   CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(32, topMargin);
-    EXPECT_EQ(432, resultWidth);
-    EXPECT_EQ(432, resultWidthLargeIPad);
-  } else {
-    EXPECT_EQ(82, topMargin);
-    EXPECT_EQ(width - 2 * margin, resultWidth);
-    EXPECT_EQ(largeIPadWidth - 400, resultWidthLargeIPad);
-  }
+  EXPECT_EQ(32, topMargin);
+  EXPECT_EQ(432, resultWidth);
+  EXPECT_EQ(432, resultWidthLargeIPad);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPhonePortrait) {
@@ -208,20 +168,14 @@
   SetAsIPhone();
   SetAsPortrait();
   CGFloat width = 500;
-  CGFloat margin = centeredTilesMarginForWidth(width);
 
   // Action.
   CGFloat resultWidth = searchFieldWidth(width);
   CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(32, topMargin);
-    EXPECT_EQ(343, resultWidth);
-  } else {
-    EXPECT_EQ(32, topMargin);
-    EXPECT_EQ(width - 2 * margin, resultWidth);
-  }
+  EXPECT_EQ(32, topMargin);
+  EXPECT_EQ(343, resultWidth);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, searchFieldFrameIPhoneLandscape) {
@@ -229,20 +183,14 @@
   SetAsIPhone();
   SetAsLandscape();
   CGFloat width = 500;
-  CGFloat margin = centeredTilesMarginForWidth(width);
 
   // Action.
   CGFloat resultWidth = searchFieldWidth(width);
   CGFloat topMargin = searchFieldTopMargin();
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(32, topMargin);
-    EXPECT_EQ(343, resultWidth);
-  } else {
-    EXPECT_EQ(32, topMargin);
-    EXPECT_EQ(width - 2 * margin, resultWidth);
-  }
+  EXPECT_EQ(32, topMargin);
+  EXPECT_EQ(343, resultWidth);
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPad) {
@@ -250,17 +198,10 @@
   SetAsIPad();
 
   // Action, tests.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(380, heightForLogoHeader(YES, YES, YES));
-    EXPECT_EQ(404, heightForLogoHeader(YES, NO, YES));
-    EXPECT_EQ(380, heightForLogoHeader(YES, YES, NO));
-    EXPECT_EQ(404, heightForLogoHeader(YES, NO, NO));
-  } else {
-    EXPECT_EQ(350, heightForLogoHeader(YES, YES, YES));
-    EXPECT_EQ(374, heightForLogoHeader(YES, NO, YES));
-    EXPECT_EQ(350, heightForLogoHeader(YES, YES, NO));
-    EXPECT_EQ(374, heightForLogoHeader(YES, NO, NO));
-  }
+  EXPECT_EQ(380, heightForLogoHeader(YES, YES, YES));
+  EXPECT_EQ(404, heightForLogoHeader(YES, NO, YES));
+  EXPECT_EQ(380, heightForLogoHeader(YES, YES, NO));
+  EXPECT_EQ(404, heightForLogoHeader(YES, NO, NO));
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, heightForLogoHeaderIPhone) {
@@ -268,17 +209,10 @@
   SetAsIPhone();
 
   // Action, tests.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(276, heightForLogoHeader(YES, YES, YES));
-    EXPECT_EQ(276, heightForLogoHeader(YES, NO, YES));
-    EXPECT_EQ(276, heightForLogoHeader(YES, YES, NO));
-    EXPECT_EQ(276, heightForLogoHeader(YES, NO, NO));
-  } else {
-    EXPECT_EQ(274, heightForLogoHeader(YES, YES, YES));
-    EXPECT_EQ(274, heightForLogoHeader(YES, NO, YES));
-    EXPECT_EQ(218, heightForLogoHeader(YES, YES, NO));
-    EXPECT_EQ(218, heightForLogoHeader(YES, NO, NO));
-  }
+  EXPECT_EQ(276, heightForLogoHeader(YES, YES, YES));
+  EXPECT_EQ(276, heightForLogoHeader(YES, NO, YES));
+  EXPECT_EQ(276, heightForLogoHeader(YES, YES, NO));
+  EXPECT_EQ(276, heightForLogoHeader(YES, NO, NO));
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, SizeIPhone6) {
@@ -294,11 +228,7 @@
   SetAsIPhone();
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(4U, numberOfTilesForWidth(320));
-  } else {
-    EXPECT_EQ(3U, numberOfTilesForWidth(320));
-  }
+  EXPECT_EQ(4U, numberOfTilesForWidth(320));
 }
 
 // Test for iPad portrait and iPhone landscape.
@@ -312,11 +242,7 @@
   SetAsIPad();
 
   // Test.
-  if (IsUIRefreshPhase1Enabled()) {
-    EXPECT_EQ(4U, numberOfTilesForWidth(360));
-  } else {
-    EXPECT_EQ(3U, numberOfTilesForWidth(360));
-  }
+  EXPECT_EQ(4U, numberOfTilesForWidth(360));
 }
 
 TEST_F(ContentSuggestionsCollectionUtilsTest, NearestAncestor) {
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 853a196..d8248dd 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -39,7 +39,6 @@
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/voice/voice_search_provider.h"
-#include "ios/web/public/features.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -259,9 +258,7 @@
   CGFloat topInset = 0.0;
   if (@available(iOS 11, *)) {
     topInset = self.suggestionsViewController.view.safeAreaInsets.top;
-  } else if (IsUIRefreshPhase1Enabled() ||
-             base::FeatureList::IsEnabled(
-                 web::features::kBrowserContainerFullscreen)) {
+  } else {
     // TODO(crbug.com/826369) Replace this when the NTP is contained by the
     // BVC with |self.suggestionsViewController.topLayoutGuide.length|.
     topInset = StatusBarHeight();
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
index 58b60b0..cd9ef9a7 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
@@ -18,14 +18,11 @@
 #include "components/ntp_snippets/content_suggestion.h"
 #include "components/ntp_snippets/content_suggestions_service.h"
 #include "components/ntp_snippets/mock_content_suggestions_provider.h"
-#include "components/reading_list/core/reading_list_entry.h"
-#include "components/reading_list/core/reading_list_model.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/chrome_switches.h"
 #include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory.h"
 #include "ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.h"
-#include "ios/chrome/browser/reading_list/reading_list_model_factory.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_header_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.h"
 #include "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
@@ -33,6 +30,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_provider_test_singleton.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h"
 #include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/app/history_test_util.h"
@@ -149,7 +147,6 @@
   ContentSuggestionsService* service =
       IOSChromeContentSuggestionsServiceFactory::GetForBrowserState(
           browserState);
-  RegisterReadingListProvider(service, browserState);
   [[ContentSuggestionsTestSingleton sharedInstance]
       registerArticleProvider:service];
 }
@@ -158,7 +155,6 @@
   [self closeAllTabs];
   ios::ChromeBrowserState* browserState =
       chrome_test_util::GetOriginalBrowserState();
-  ReadingListModelFactory::GetForBrowserState(browserState)->DeleteAllEntries();
 
   // Resets the Service associated with this browserState to a service with
   // default providers. The previous service is deleted.
@@ -181,10 +177,6 @@
 - (void)setUp {
   self.provider->FireCategoryStatusChanged(self.category,
                                            CategoryStatus::AVAILABLE);
-
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  readingListModel->DeleteAllEntries();
   [super setUp];
 }
 
@@ -244,173 +236,6 @@
       assertWithMatcher:grey_notNil()];
 }
 
-// Tests that after dismissing a ReadingList item, it is not displayed on the
-// NTP. But it is still unread in the Reading List surface.
-- (void)testSwipeToDismissReadingListItem {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-
-  // Add two items to Reading List.
-  std::string stdTitle1{"test title1"};
-  std::string stdTitle2{"test title2"};
-  NSString* title1 = base::SysUTF8ToNSString(stdTitle1);
-  NSString* title2 = base::SysUTF8ToNSString(stdTitle2);
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  readingListModel->AddEntry(GURL("http://chromium.org/2"), stdTitle2,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-  readingListModel->AddEntry(GURL("http://chromium.org/1"), stdTitle1,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-
-  // Check that the two items are present in a new tab.
-  [ChromeEarlGreyUI openNewTab];
-  [CellWithMatcher(grey_accessibilityID(title1))
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [CellWithMatcher(grey_accessibilityID(title2))
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // Swipe to dismiss the first one.
-  [CellWithMatcher(grey_accessibilityID(title1))
-      performAction:[GREYActions
-                        actionForSwipeFastInDirection:kGREYDirectionLeft
-                               xOriginStartPercentage:0.9
-                               yOriginStartPercentage:0.5]];
-
-  // Check the swiped item is dismissed.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(grey_accessibilityID(title1),
-                                          grey_sufficientlyVisible(), nil)]
-      assertWithMatcher:grey_nil()];
-
-  // Check the dismissed item is not present when opening a new NTP.
-  ScrollUp();
-  [ChromeEarlGreyUI openNewTab];
-  [CellWithMatcher(grey_accessibilityID(title2))
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(title1)]
-      assertWithMatcher:grey_nil()];
-
-  // Open the Reading List surface.
-  ScrollUp();
-  [ChromeEarlGreyUI openToolsMenu];
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_TOOLS_MENU_READING_LIST)]
-      performAction:grey_tap()];
-
-  // Check that both entries are unread in the ReadingList surface.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          StaticTextWithAccessibilityLabelId(
-                                              IDS_IOS_READING_LIST_READ_HEADER)]
-      assertWithMatcher:grey_notVisible()];
-
-  [[EarlGrey selectElementWithMatcher:
-                 chrome_test_util::StaticTextWithAccessibilityLabelId(
-                     IDS_IOS_READING_LIST_UNREAD_HEADER)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  [[EarlGrey selectElementWithMatcher:
-                 chrome_test_util::StaticTextWithAccessibilityLabel(title1)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // On iPad two Reading List items are displayed as the Reading List view is
-  // displayed modally, the NTP is still visible.
-  [[EarlGrey
-      selectElementWithMatcher:
-          grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabel(title2),
-                     grey_not(grey_ancestor(
-                         chrome_test_util::ContentSuggestionCollectionView())),
-                     nil)] assertWithMatcher:grey_sufficientlyVisible()];
-
-  // Close Reading List.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_READING_LIST_DONE_BUTTON)]
-      performAction:grey_tap()];
-}
-
-// Tests that only the 3 most recent Reading List items are displayed.
-- (void)testReadingListItem {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-
-  // Create entry titles for 4 unread entries and 1 read entry.
-  std::string stdTitle1{"test unread title1"};
-  std::string stdTitle2{"test unread title2"};
-  std::string stdTitle3{"test unread title3"};
-  std::string stdTitle4{"test unread title4"};
-  std::string stdReadTitle{"test read title"};
-  NSString* title1 = base::SysUTF8ToNSString(stdTitle1);
-  NSString* title2 = base::SysUTF8ToNSString(stdTitle2);
-  NSString* title3 = base::SysUTF8ToNSString(stdTitle3);
-  NSString* title4 = base::SysUTF8ToNSString(stdTitle4);
-  NSString* readTitle = base::SysUTF8ToNSString(stdReadTitle);
-
-  // Adds the entries: title1 is the oldest, title4 is the latest.
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  readingListModel->AddEntry(GURL("http://chromium.org/1"), stdTitle1,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-  readingListModel->AddEntry(GURL("http://chromium.org/2"), stdTitle2,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-  readingListModel->AddEntry(GURL("http://chromium.org/3"), stdTitle3,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-  readingListModel->AddEntry(GURL("http://chromium.org/5"), stdReadTitle,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-  readingListModel->SetReadStatus(GURL("http://chromium.org/5"), true);
-  readingListModel->AddEntry(GURL("http://chromium.org/4"), stdTitle4,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-
-  // Check that only the first 3 unread items are displayed.
-  [ChromeEarlGreyUI openNewTab];
-  [CellWithMatcher(grey_accessibilityID(title4))
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [CellWithMatcher(grey_accessibilityID(title3))
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [CellWithMatcher(grey_accessibilityID(title2))
-      assertWithMatcher:grey_sufficientlyVisible()];
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(readTitle)]
-      assertWithMatcher:grey_nil()];
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(title1)]
-      assertWithMatcher:grey_nil()];
-}
-
-// Tests that tapping "More" on the Reading List section opens the Reading List
-// surface.
-- (void)testMoreReadingListSection {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-  // Add an entry to make sure the Reading List section is displayed.
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  readingListModel->AddEntry(GURL("http://chromium.org/2"), "test title",
-                             reading_list::ADDED_VIA_CURRENT_APP);
-
-  // Tap More.
-  [CellWithMatcher(chrome_test_util::StaticTextWithAccessibilityLabelId(
-      IDS_IOS_CONTENT_SUGGESTIONS_FOOTER_TITLE)) performAction:grey_tap()];
-
-  // Check the Reading List surface is opened.
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                          StaticTextWithAccessibilityLabelId(
-                                              IDS_IOS_TOOLS_MENU_READING_LIST)]
-      assertWithMatcher:grey_sufficientlyVisible()];
-
-  // Close Reading List.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_READING_LIST_DONE_BUTTON)]
-      performAction:grey_tap()];
-}
 
 // Tests that a switch for the ContentSuggestions exists in the settings. The
 // behavior depends on having a real remote provider, so it cannot be tested
@@ -425,33 +250,6 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-// Tests that the section titles are displayed only if there are two sections.
-- (void)testSectionTitle {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  readingListModel->AddEntry(GURL("http://chromium.org"), "test title",
-                             reading_list::ADDED_VIA_CURRENT_APP);
-
-  [CellWithMatcher(chrome_test_util::StaticTextWithAccessibilityLabelId(
-      IDS_NTP_READING_LIST_SUGGESTIONS_SECTION_HEADER))
-      assertWithMatcher:grey_nil()];
-
-  std::vector<ContentSuggestion> suggestions;
-  suggestions.emplace_back(
-      Suggestion(self.category, "chromium", GURL("http://chromium.org")));
-  self.provider->FireSuggestionsChanged(self.category, std::move(suggestions));
-
-  [CellWithMatcher(chrome_test_util::StaticTextWithAccessibilityLabelId(
-      IDS_NTP_READING_LIST_SUGGESTIONS_SECTION_HEADER))
-      assertWithMatcher:grey_sufficientlyVisible()];
-}
-
 // Tests that when tapping a suggestion, it is opened. When going back, the
 // disposition of the collection takes into account the previous scroll, even
 // when more is tapped.
@@ -504,21 +302,10 @@
 
   // Test that the omnibox is visible and taking full width, before any scroll
   // happen on iPhone.
-  if (!content_suggestions::IsRegularXRegularSizeClass()) {
-    if (!IsUIRefreshPhase1Enabled()) {
-      CGFloat collectionWidth = ntp_home::CollectionView().bounds.size.width;
-      [[EarlGrey
-          selectElementWithMatcher:grey_accessibilityID(
-                                       ntp_home::FakeOmniboxAccessibilityID())]
-          assertWithMatcher:grey_allOf(grey_sufficientlyVisible(),
-                                       ntp_home::OmniboxWidthBetween(
-                                           collectionWidth + 1, 1),
-                                       nil)];
-    }
-
+  if (!IsRegularXRegularSizeClass()) {
     // Test that the omnibox is still pinned to the top of the screen and
     // under the safe area.
-    CGFloat safeAreaTop = IsUIRefreshPhase1Enabled() ? StatusBarHeight() : 0;
+    CGFloat safeAreaTop = StatusBarHeight();
     CGFloat contentOffset = ntp_home::CollectionView().contentOffset.y;
     CGFloat fakeOmniboxOrigin = ntp_home::FakeOmnibox().frame.origin.y;
     CGFloat pinnedOffset = contentOffset - (fakeOmniboxOrigin - safeAreaTop);
@@ -557,149 +344,6 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
-// Tests that when long pressing a Reading List entry, a context menu is shown.
-- (void)testReadingListLongPress {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-
-  NSString* title = @"ReadingList test title";
-  std::string sTitle{"ReadingList test title"};
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  readingListModel->AddEntry(GURL("http://chromium.org"), sTitle,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-
-  [CellWithMatcher(grey_accessibilityID(title)) performAction:grey_longPress()];
-
-  if (!content_suggestions::IsRegularXRegularSizeClass()) {
-    [[EarlGrey selectElementWithMatcher:
-                   chrome_test_util::ButtonWithAccessibilityLabelId(
-                       IDS_APP_CANCEL)] assertWithMatcher:grey_interactable()];
-  }
-
-  // No read later as it is already in the Reading List section.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST)]
-      assertWithMatcher:grey_nil()];
-}
-
-// Tests that "Open in New Tab" in context menu opens in a new tab.
-- (void)testReadingListOpenNewTab {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-
-  // Setup.
-  [self setupReadingListContextMenu];
-  const GURL pageURL = self.testServer->GetURL(kPageURL);
-
-  // Open in new tab.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB)]
-      performAction:grey_tap()];
-
-  // Check a new page in normal model is opened.
-  [ChromeEarlGrey waitForMainTabCount:2];
-  [ChromeEarlGrey waitForIncognitoTabCount:0];
-
-  // Wait for the end of the new tab opening in background. This is needed as
-  // the iOS 11 devices cannot complete this animations while checking if the
-  // collection is present.
-  base::test::ios::SpinRunLoopWithMinDelay(base::TimeDelta::FromSecondsD(1));
-
-  // Check that the tab has been opened in background.
-  ConditionBlock condition = ^{
-    NSError* error = nil;
-    [[EarlGrey selectElementWithMatcher:chrome_test_util::
-                                            ContentSuggestionCollectionView()]
-        assertWithMatcher:grey_sufficientlyVisible()
-                    error:&error];
-    return error == nil;
-  };
-  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
-                 base::test::ios::kWaitForUIElementTimeout, condition),
-             @"Collection view not visible");
-
-  // Check the page has been correctly opened.
-  chrome_test_util::SelectTabAtIndexInCurrentMode(1);
-  [ChromeEarlGrey waitForWebViewContainingText:kPageLoadedString];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
-                                          pageURL.GetContent())]
-      assertWithMatcher:grey_notNil()];
-}
-
-// Tests that "Open in New Incognito Tab" in context menu opens in a new
-// incognito tab.
-- (void)testReadingListOpenNewIncognitoTab {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-
-  // Setup.
-  [self setupReadingListContextMenu];
-  const GURL pageURL = self.testServer->GetURL(kPageURL);
-
-  // Open in new incognito tab.
-  [[EarlGrey selectElementWithMatcher:
-                 chrome_test_util::ButtonWithAccessibilityLabelId(
-                     IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWINCOGNITOTAB)]
-      performAction:grey_tap()];
-
-  // Check that the tab has been opened in foreground.
-  [ChromeEarlGrey waitForWebViewContainingText:kPageLoadedString];
-  [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText(
-                                          pageURL.GetContent())]
-      assertWithMatcher:grey_notNil()];
-
-  GREYAssertTrue(chrome_test_util::IsIncognitoMode(),
-                 @"Test did not switch to incognito");
-
-  // Check only one incognito tab has been opened.
-  [ChromeEarlGrey waitForIncognitoTabCount:1];
-  [ChromeEarlGrey waitForMainTabCount:1];
-}
-
-// Tests that "Remove" in context menu removes the entry.
-- (void)testReadingListRemove {
-  // TODO(crbug.com/807330): The collection view reading list section is not
-  // used in ui refresh.
-  if (IsUIRefreshPhase1Enabled()) {
-    EARL_GREY_TEST_SKIPPED(@"ReadingList section does not exist in UI Refresh");
-  }
-
-  // Setup.
-  NSString* title = @"ReadingList test title";
-  [self setupReadingListContextMenu];
-  const GURL pageURL = self.testServer->GetURL(kPageURL);
-
-  // Remove the element.
-  [[EarlGrey
-      selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
-                                   IDS_IOS_CONTENT_SUGGESTIONS_REMOVE)]
-      performAction:grey_tap()];
-
-  // Check the entry has been removed.
-  [[EarlGrey
-      selectElementWithMatcher:grey_allOf(grey_accessibilityID(title),
-                                          grey_sufficientlyVisible(), nil)]
-      assertWithMatcher:grey_nil()];
-
-  // Check the entry is still unread in the Reading List model.
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  GREYAssertEqual(1, readingListModel->unread_size(),
-                  @"The number of unread entry has been changed.");
-}
-
 // Tests the "Open in New Tab" action of the Most Visited context menu.
 - (void)testMostVisitedNewTab {
   [self setupMostVisitedTileLongPress];
@@ -806,7 +450,7 @@
 - (void)testMostVisitedLongPress {
   [self setupMostVisitedTileLongPress];
 
-  if (!content_suggestions::IsRegularXRegularSizeClass()) {
+  if (!IsRegularXRegularSizeClass()) {
     [[EarlGrey selectElementWithMatcher:
                    chrome_test_util::ButtonWithAccessibilityLabelId(
                        IDS_APP_CANCEL)] assertWithMatcher:grey_interactable()];
@@ -835,24 +479,6 @@
 
 #pragma mark - Test utils
 
-// Setup a Reading List item and long press it to open the context menu.
-- (void)setupReadingListContextMenu {
-  self.testServer->RegisterRequestHandler(base::Bind(&StandardResponse));
-  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
-  const GURL pageURL = self.testServer->GetURL(kPageURL);
-  std::string sTitle{"ReadingList test title"};
-  NSString* title = @"ReadingList test title";
-  ReadingListModel* readingListModel =
-      ReadingListModelFactory::GetForBrowserState(self.browserState);
-  readingListModel->AddEntry(pageURL, sTitle,
-                             reading_list::ADDED_VIA_CURRENT_APP);
-  [CellWithMatcher(grey_accessibilityID(title)) performAction:grey_longPress()];
-  [ChromeEarlGrey waitForMainTabCount:1];
-  [ChromeEarlGrey waitForIncognitoTabCount:0];
-  GREYAssertEqual(1, readingListModel->unread_size(),
-                  @"There should be only one unread entry.");
-}
-
 // Setup a most visited tile, and open the context menu by long pressing on it.
 - (void)setupMostVisitedTileLongPress {
   self.testServer->RegisterRequestHandler(base::Bind(&StandardResponse));
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer.mm
index 84a63d2..9984eb97 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer.mm
@@ -14,7 +14,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_controlling.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
-#include "ios/web/public/features.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -26,10 +25,7 @@
 
 UIEdgeInsets SafeAreaInsetsForViewWithinNTP(UIView* view) {
   UIEdgeInsets insets = SafeAreaInsetsForView(view);
-  if ((IsUIRefreshPhase1Enabled() ||
-       base::FeatureList::IsEnabled(
-           web::features::kBrowserContainerFullscreen)) &&
-      !base::ios::IsRunningOnIOS11OrLater()) {
+  if (!base::ios::IsRunningOnIOS11OrLater()) {
     // TODO(crbug.com/826369) Replace this when the NTP is contained by the
     // BVC with |self.collectionController.topLayoutGuide.length|.
     insets = UIEdgeInsetsMake(StatusBarHeight(), 0, 0, 0);
@@ -180,10 +176,6 @@
     [self.headerController unfocusOmnibox];
   }
 
-  if (IsIPadIdiom() && !IsUIRefreshPhase1Enabled()) {
-    return;
-  }
-
   if (self.shouldAnimateHeader) {
     UIEdgeInsets insets = SafeAreaInsetsForViewWithinNTP(self.collectionView);
     [self.headerController
@@ -194,8 +186,7 @@
 }
 
 - (void)updateFakeOmniboxOnNewWidth:(CGFloat)width {
-  if (self.shouldAnimateHeader &&
-      (IsUIRefreshPhase1Enabled() || !IsIPadIdiom())) {
+  if (self.shouldAnimateHeader) {
     // We check -superview here because in certain scenarios (such as when the
     // VC is rotated underneath another presented VC), in a
     // UICollectionViewController -viewSafeAreaInsetsDidChange the VC.view has
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
index 2f34a3e..dff5fc3 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
@@ -229,10 +229,7 @@
 
   // Adjust the position of the search field's subviews by adjusting their
   // constraint constant value.
-  CGFloat constantDiff = IsUIRefreshPhase1Enabled()
-                             ? -maxXInset * percent
-                             : percent * (ntp_header::kMaxHorizontalMarginDiff +
-                                          inset + safeAreaInsets.left);
+  CGFloat constantDiff = -maxXInset * percent;
   for (NSLayoutConstraint* constraint in constraints) {
     if (constraint.constant > 0)
       constraint.constant = constantDiff + ntp_header::kHintLabelSidePadding;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
index 9a0d468..da92accf 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
@@ -37,10 +37,6 @@
 
 using base::UserMetricsAction;
 
-namespace {
-const UIEdgeInsets kSearchBoxStretchInsets = {3, 3, 3, 3};
-}  // namespace
-
 @interface ContentSuggestionsHeaderViewController ()
 
 // |YES| when notifications indicate the omnibox is focused.
@@ -128,9 +124,6 @@
                   (id<UIViewControllerTransitionCoordinator>)coordinator {
   [super willTransitionToTraitCollection:newCollection
                withTransitionCoordinator:coordinator];
-  if (!IsUIRefreshPhase1Enabled())
-    return;
-
   void (^transition)(id<UIViewControllerTransitionCoordinatorContext>) =
       ^(id<UIViewControllerTransitionCoordinatorContext> context) {
         // Ensure omnibox is reset when not a regular tablet.
@@ -152,9 +145,9 @@
 - (void)updateFakeOmniboxForOffset:(CGFloat)offset
                        screenWidth:(CGFloat)screenWidth
                     safeAreaInsets:(UIEdgeInsets)safeAreaInsets {
-  if (self.isShowing && IsUIRefreshPhase1Enabled()) {
+  if (self.isShowing) {
     CGFloat progress =
-        self.logoIsShowing || !content_suggestions::IsRegularXRegularSizeClass()
+        self.logoIsShowing || !IsRegularXRegularSizeClass()
             ? [self.headerView searchFieldProgressForOffset:offset
                                              safeAreaInsets:safeAreaInsets]
             // RxR with no logo hides the fakebox, so always show the omnibox.
@@ -209,11 +202,11 @@
 
   CGFloat offsetY =
       headerHeight - ntp_header::kScrolledToTopOmniboxBottomMargin;
-  if (!content_suggestions::IsRegularXRegularSizeClass(self)) {
+  if (!IsRegularXRegularSizeClass(self)) {
     CGFloat top = 0;
     if (@available(iOS 11, *)) {
       top = self.parentViewController.view.safeAreaInsets.top;
-    } else if (IsUIRefreshPhase1Enabled()) {
+    } else {
       // TODO(crbug.com/826369) Replace this when the NTP is contained by the
       // BVC with |self.parentViewController.topLayoutGuide.length|.
       top = StatusBarHeight();
@@ -274,12 +267,6 @@
 // Initialize and add a search field tap target and a voice search button.
 - (void)addFakeOmnibox {
   self.fakeOmnibox = [[UIButton alloc] init];
-  if (IsIPadIdiom() && !IsUIRefreshPhase1Enabled()) {
-    UIImage* searchBoxImage = [[UIImage imageNamed:@"ntp_google_search_box"]
-        resizableImageWithCapInsets:kSearchBoxStretchInsets];
-    [self.fakeOmnibox setBackgroundImage:searchBoxImage
-                                forState:UIControlStateNormal];
-  }
   [self.fakeOmnibox setAdjustsImageWhenHighlighted:NO];
 
   // Set isAccessibilityElement to NO so that Voice Search button is accessible.
@@ -292,15 +279,9 @@
   content_suggestions::configureSearchHintLabel(self.searchHintLabel,
                                                 self.fakeOmnibox);
 
-  if (IsUIRefreshPhase1Enabled()) {
-    self.hintLabelLeadingConstraint = [self.searchHintLabel.leadingAnchor
-        constraintGreaterThanOrEqualToAnchor:[self.fakeOmnibox leadingAnchor]
-                                    constant:ntp_header::kHintLabelSidePadding];
-  } else {
-    self.hintLabelLeadingConstraint = [self.searchHintLabel.leadingAnchor
-        constraintEqualToAnchor:[self.fakeOmnibox leadingAnchor]
-                       constant:ntp_header::kHintLabelSidePaddingLegacy];
-  }
+  self.hintLabelLeadingConstraint = [self.searchHintLabel.leadingAnchor
+      constraintGreaterThanOrEqualToAnchor:[self.fakeOmnibox leadingAnchor]
+                                  constant:ntp_header::kHintLabelSidePadding];
   self.hintLabelLeadingConstraint.active = YES;
 
   // Set a button the same size as the fake omnibox as the accessibility
@@ -394,11 +375,7 @@
 }
 
 - (void)focusFakebox {
-  if (IsUIRefreshPhase1Enabled()) {
-    [self shiftTilesUp];
-  } else {
-    [self.dispatcher fakeboxFocused];
-  }
+  [self shiftTilesUp];
 }
 
 // TODO(crbug.com/807330) The fakebox is currently a collection of views spread
@@ -420,7 +397,7 @@
     self.logoVendor.showingLogo = self.logoIsShowing;
     [self.doodleHeightConstraint
         setConstant:content_suggestions::doodleHeight(self.logoIsShowing)];
-    if (content_suggestions::IsRegularXRegularSizeClass(self))
+    if (IsRegularXRegularSizeClass(self))
       [self.fakeOmnibox setHidden:!self.logoIsShowing];
     [self.collectionSynchronizer invalidateLayout];
   }
@@ -456,9 +433,7 @@
 }
 
 - (void)shiftTilesDown {
-  if ((IsUIRefreshPhase1Enabled() && IsSplitToolbarMode()) ||
-      (!IsUIRefreshPhase1Enabled() &&
-       !content_suggestions::IsRegularXRegularSizeClass(self))) {
+  if (IsSplitToolbarMode()) {
     [self.dispatcher onFakeboxBlur];
   }
   [self.collectionSynchronizer shiftTilesDown];
@@ -468,12 +443,8 @@
 
 - (void)shiftTilesUp {
   void (^completionBlock)() = ^{
-    if (IsUIRefreshPhase1Enabled()) {
-      [self.dispatcher fakeboxFocused];
-    }
-    if ((IsUIRefreshPhase1Enabled() && IsSplitToolbarMode()) ||
-        (!IsUIRefreshPhase1Enabled() &&
-         !content_suggestions::IsRegularXRegularSizeClass(self))) {
+    [self.dispatcher fakeboxFocused];
+    if (IsSplitToolbarMode()) {
       [self.dispatcher onFakeboxAnimationComplete];
     }
   };
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
index 2f19695..0c5fba6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.mm
@@ -7,6 +7,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -25,14 +26,12 @@
   CGFloat minimumHeight = collectionViewHeight + headerHeight -
                           ntp_header::kScrolledToTopOmniboxBottomMargin;
   CGFloat topSafeArea = 0;
-  if (IsUIRefreshPhase1Enabled()) {
-    if (@available(iOS 11, *)) {
-      topSafeArea = self.collectionView.safeAreaInsets.top;
-    } else {
-      topSafeArea = StatusBarHeight();
-    }
+  if (@available(iOS 11, *)) {
+    topSafeArea = self.collectionView.safeAreaInsets.top;
+  } else {
+    topSafeArea = StatusBarHeight();
   }
-  if (!content_suggestions::IsRegularXRegularSizeClass(self.collectionView))
+  if (!IsRegularXRegularSizeClass(self.collectionView))
     minimumHeight -= ntp_header::ToolbarHeight() + topSafeArea +
                      self.collectionView.contentInset.bottom;
 
@@ -44,7 +43,7 @@
 }
 
 - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
-  if (content_suggestions::IsRegularXRegularSizeClass())
+  if (IsRegularXRegularSizeClass())
     return [super layoutAttributesForElementsInRect:rect];
 
   NSMutableArray* layoutAttributes =
@@ -86,7 +85,7 @@
   UICollectionViewLayoutAttributes* attributes =
       [super layoutAttributesForSupplementaryViewOfKind:kind
                                             atIndexPath:indexPath];
-  if (content_suggestions::IsRegularXRegularSizeClass())
+  if (IsRegularXRegularSizeClass())
     return attributes;
 
   if ([kind isEqualToString:UICollectionElementKindSectionHeader] &&
@@ -101,12 +100,10 @@
 
     // Prevent the fake omnibox from scrolling up off of the screen.
     CGFloat topSafeArea = 0;
-    if (IsUIRefreshPhase1Enabled()) {
-      if (@available(iOS 11, *)) {
-        topSafeArea = self.collectionView.safeAreaInsets.top;
-      } else {
-        topSafeArea = StatusBarHeight();
-      }
+    if (@available(iOS 11, *)) {
+      topSafeArea = self.collectionView.safeAreaInsets.top;
+    } else {
+      topSafeArea = StatusBarHeight();
     }
     CGFloat minY = headerHeight - ntp_header::kMinHeaderHeight - topSafeArea;
     if (contentOffset.y > minY)
@@ -117,7 +114,7 @@
 }
 
 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound {
-  if (content_suggestions::IsRegularXRegularSizeClass())
+  if (IsRegularXRegularSizeClass())
     return [super shouldInvalidateLayoutForBoundsChange:newBound];
   return YES;
 }
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index 40ca0cf..961aea0 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -49,7 +49,6 @@
 using CSCollectionViewItem = CollectionViewItem<SuggestedContent>;
 
 // Maximum number of most visited tiles fetched.
-const NSInteger kMaxNumMostVisitedTilesLegacy = 8;
 const NSInteger kMaxNumMostVisitedTiles = 4;
 
 }  // namespace
@@ -169,15 +168,10 @@
     _mostVisitedSites = std::move(mostVisitedSites);
     _mostVisitedBridge =
         std::make_unique<ntp_tiles::MostVisitedSitesObserverBridge>(self);
-    NSInteger maxNumMostVisitedTiles = IsUIRefreshPhase1Enabled()
-                                           ? kMaxNumMostVisitedTiles
-                                           : kMaxNumMostVisitedTilesLegacy;
     _mostVisitedSites->SetMostVisitedURLsObserver(_mostVisitedBridge.get(),
-                                                  maxNumMostVisitedTiles);
-    if (IsUIRefreshPhase1Enabled()) {
-      _readingListModelBridge =
-          std::make_unique<ReadingListModelBridge>(self, readingListModel);
-    }
+                                                  kMaxNumMostVisitedTiles);
+    _readingListModelBridge =
+        std::make_unique<ReadingListModelBridge>(self, readingListModel);
   }
   return self;
 }
@@ -202,8 +196,7 @@
 }
 
 + (NSUInteger)maxSitesShown {
-  return IsUIRefreshPhase1Enabled() ? kMaxNumMostVisitedTiles
-                                    : kMaxNumMostVisitedTilesLegacy;
+  return kMaxNumMostVisitedTiles;
 }
 
 #pragma mark - ContentSuggestionsDataSource
@@ -218,11 +211,7 @@
     [sectionsInfo addObject:self.promoSectionInfo];
   }
 
-  // Since action items are always visible in UI Refresh, always add
-  // |mostVisitedSectionInfo| in UI Refresh.
-  if (self.mostVisitedItems.count > 0 || IsUIRefreshPhase1Enabled()) {
-    [sectionsInfo addObject:self.mostVisitedSectionInfo];
-  }
+  [sectionsInfo addObject:self.mostVisitedSectionInfo];
 
   std::vector<ntp_snippets::Category> categories =
       self.contentService->GetCategories();
@@ -263,9 +252,7 @@
     }
   } else if (sectionInfo == self.mostVisitedSectionInfo) {
     [convertedSuggestions addObjectsFromArray:self.mostVisitedItems];
-    if (IsUIRefreshPhase1Enabled()) {
-      [convertedSuggestions addObjectsFromArray:self.actionButtonItems];
-    }
+    [convertedSuggestions addObjectsFromArray:self.actionButtonItems];
   } else if (sectionInfo == self.learnMoreSectionInfo) {
     [convertedSuggestions addObject:self.learnMoreItem];
   } else {
@@ -624,8 +611,7 @@
 - (BOOL)isCategoryInitOrAvailable:(ntp_snippets::Category)category {
   ntp_snippets::CategoryStatus status =
       self.contentService->GetCategoryStatus(category);
-  if (IsUIRefreshPhase1Enabled() &&
-      category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES) &&
+  if (category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES) &&
       status == ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED)
     return [self.contentArticlesEnabled value];
   else
@@ -639,8 +625,7 @@
 - (BOOL)isCategoryAvailable:(ntp_snippets::Category)category {
   ntp_snippets::CategoryStatus status =
       self.contentService->GetCategoryStatus(category);
-  if (IsUIRefreshPhase1Enabled() &&
-      category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES) &&
+  if (category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES) &&
       status == ntp_snippets::CategoryStatus::CATEGORY_EXPLICITLY_DISABLED) {
     return [self.contentArticlesEnabled value];
   } else {
@@ -652,8 +637,7 @@
 // Returns whether the Articles category pref indicates it should be expanded,
 // otherwise returns YES.
 - (BOOL)isCategoryExpanded:(ntp_snippets::Category)category {
-  if (IsUIRefreshPhase1Enabled() &&
-      category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES))
+  if (category.IsKnownCategory(ntp_snippets::KnownCategories::ARTICLES))
     return [self.contentArticlesExpanded value];
   else
     return YES;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 4705870..8656ee4 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -33,18 +33,9 @@
 
 namespace {
 using CSCollectionViewItem = CollectionViewItem<SuggestedContent>;
-const CGFloat kMaxCardWidth = 416;
-const CGFloat kStandardSpacing = 8;
 const CGFloat kMostVisitedBottomMargin = 13;
 const CGFloat kCardBorderRadius = 11;
 
-// Returns whether the cells should be displayed using the full width.
-BOOL ShouldCellsBeFullWidth(UITraitCollection* collection) {
-  if (IsUIRefreshPhase1Enabled())
-    return NO;
-  return collection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
-         collection.verticalSizeClass != UIUserInterfaceSizeClassCompact;
-}
 }
 
 @interface ContentSuggestionsViewController ()<UIGestureRecognizerDelegate>
@@ -187,12 +178,7 @@
       updateFakeOmniboxOnNewWidth:self.collectionView.bounds.size.width];
   [self.headerSynchronizer updateConstraints];
   [self.collectionView reloadData];
-  if (ShouldCellsBeFullWidth(
-          [UIApplication sharedApplication].keyWindow.traitCollection)) {
-    self.styler.cellStyle = MDCCollectionViewCellStyleGrouped;
-  } else {
-    self.styler.cellStyle = MDCCollectionViewCellStyleCard;
-  }
+  self.styler.cellStyle = MDCCollectionViewCellStyleCard;
 }
 
 - (void)clearOverscroll {
@@ -213,9 +199,7 @@
     // Overscroll action does not work well with content offset, so set this
     // to never and internally offset the UI to account for safe area insets.
     self.collectionView.contentInsetAdjustmentBehavior =
-        IsUIRefreshPhase1Enabled()
-            ? UIScrollViewContentInsetAdjustmentNever
-            : UIScrollViewContentInsetAdjustmentAutomatic;
+        UIScrollViewContentInsetAdjustmentNever;
   }
   self.collectionView.accessibilityIdentifier =
       [[self class] collectionAccessibilityIdentifier];
@@ -223,44 +207,13 @@
 
   self.collectionView.delegate = self;
   self.collectionView.backgroundColor = ntp_home::kNTPBackgroundColor();
-  if (ShouldCellsBeFullWidth(
-          [UIApplication sharedApplication].keyWindow.traitCollection)) {
-    self.styler.cellStyle = MDCCollectionViewCellStyleGrouped;
-  } else {
-    self.styler.cellStyle = MDCCollectionViewCellStyleCard;
-    if (IsUIRefreshPhase1Enabled())
-      self.styler.cardBorderRadius = kCardBorderRadius;
-  }
+  self.styler.cellStyle = MDCCollectionViewCellStyleCard;
+  self.styler.cardBorderRadius = kCardBorderRadius;
   self.automaticallyAdjustsScrollViewInsets = NO;
   self.collectionView.translatesAutoresizingMaskIntoConstraints = NO;
 
-  if (base::FeatureList::IsEnabled(
-          web::features::kBrowserContainerFullscreen) &&
-      !IsUIRefreshPhase1Enabled()) {
-    // Add a fake status bar at the top.
-    UIView* fakeStatusBar = [[UIView alloc] init];
-    fakeStatusBar.backgroundColor = ntp_home::kNTPBackgroundColor();
-    fakeStatusBar.translatesAutoresizingMaskIntoConstraints = NO;
-    [self.view addSubview:fakeStatusBar];
-    AddSameConstraintsToSides(
-        self.view, fakeStatusBar,
-        LayoutSides::kTop | LayoutSides::kTrailing | LayoutSides::kLeading);
-    if (@available(iOS 11.0, *)) {
-      [fakeStatusBar.bottomAnchor
-          constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor]
-          .active = YES;
-    } else {
-      [fakeStatusBar.bottomAnchor
-          constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor]
-          .active = YES;
-    }
-    ApplyVisualConstraints(
-        @[ @"V:|[statusBar][collection]|", @"H:|[collection]|" ],
-        @{@"collection" : self.collectionView, @"statusBar" : fakeStatusBar});
-  } else {
-    ApplyVisualConstraints(@[ @"V:|[collection]|", @"H:|[collection]|" ],
-                           @{@"collection" : self.collectionView});
-  }
+  ApplyVisualConstraints(@[ @"V:|[collection]|", @"H:|[collection]|" ],
+                         @{@"collection" : self.collectionView});
 
   UILongPressGestureRecognizer* longPressRecognizer =
       [[UILongPressGestureRecognizer alloc]
@@ -278,8 +231,7 @@
 }
 
 - (void)updateOverscrollActionsState {
-  if (IsSplitToolbarMode(self) ||
-      (!IsUIRefreshPhase1Enabled() && !IsRegularXRegularSizeClass(self))) {
+  if (IsSplitToolbarMode(self)) {
     [self.overscrollActionsController enableOverscrollActions];
   } else {
     [self.overscrollActionsController disableOverscrollActions];
@@ -329,11 +281,7 @@
   // Invalidating the layout after changing the cellStyle results in the layout
   // not being updated. Do it before to have it taken into account.
   [self.collectionView.collectionViewLayout invalidateLayout];
-  if (ShouldCellsBeFullWidth(newCollection)) {
-    self.styler.cellStyle = MDCCollectionViewCellStyleGrouped;
-  } else {
-    self.styler.cellStyle = MDCCollectionViewCellStyleCard;
-  }
+  self.styler.cellStyle = MDCCollectionViewCellStyleCard;
 }
 
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
@@ -441,58 +389,12 @@
     parentInset.left = margin;
     parentInset.right = margin;
     if ([self.collectionUpdater isMostVisitedSection:section]) {
-      if (IsUIRefreshPhase1Enabled()) {
-        parentInset.bottom = kMostVisitedBottomMargin;
-      } else {
-        // Make sure the Content Suggestions is displayed at a reasonnable
-        // distance from the Most Visited tiles.
-        CGFloat maximumMargin = IsIPadIdiom()
-                                    ? ntp_home::kMostVisitedBottomMarginIPad
-                                    : ntp_home::kMostVisitedBottomMarginIPhone;
-
-        NSInteger promoSection = -1;
-        for (NSInteger i = 0; i < [self.collectionViewModel numberOfSections];
-             i++) {
-          if ([self.collectionUpdater isPromoSection:i]) {
-            promoSection = i;
-          }
-        }
-
-        // Height for the displayed content.
-        CGFloat contentHeight =
-            [self collectionView:collectionView
-                                         layout:collectionViewLayout
-                referenceSizeForHeaderInSection:0]
-                .height;
-        contentHeight += kStandardSpacing;
-        if (promoSection >= 0) {
-          contentHeight +=
-              [self collectionView:collectionView
-                  cellHeightAtIndexPath:[NSIndexPath
-                                            indexPathForItem:0
-                                                   inSection:promoSection]];
-          contentHeight += kStandardSpacing;
-        }
-        contentHeight +=
-            2 * [ContentSuggestionsMostVisitedCell defaultSize].height;
-        contentHeight += content_suggestions::verticalSpacingBetweenTiles();
-
-        // The Content Suggestions should idealy be displayed such as only part
-        // of the first suggestion is displayed. The distance should be capped
-        // to not push the suggestions too far.
-        CGFloat collectionHeight = collectionView.bounds.size.height;
-        CGFloat idealMargin = collectionHeight - contentHeight -
-                              ntp_home::kSuggestionPeekingHeight;
-        CGFloat margin = MIN(MAX(kStandardSpacing, idealMargin), maximumMargin);
-        parentInset.bottom = margin;
-      }
+      parentInset.bottom = kMostVisitedBottomMargin;
     }
   } else if (self.styler.cellStyle == MDCCollectionViewCellStyleCard) {
     CGFloat collectionWidth = collectionView.bounds.size.width;
     CGFloat maxCardWidth =
-        IsUIRefreshPhase1Enabled()
-            ? content_suggestions::searchFieldWidth(collectionWidth)
-            : kMaxCardWidth;
+        content_suggestions::searchFieldWidth(collectionWidth);
     CGFloat margin =
         MAX(0, (collectionView.frame.size.width - maxCardWidth) / 2);
     parentInset.left = margin;
@@ -517,13 +419,7 @@
 
 - (BOOL)collectionView:(UICollectionView*)collectionView
     hidesInkViewAtIndexPath:(NSIndexPath*)indexPath {
-  if (IsUIRefreshPhase1Enabled())
-    return YES;
-  ContentSuggestionType itemType = [self.collectionUpdater
-      contentSuggestionTypeForItem:[self.collectionViewModel
-                                       itemAtIndexPath:indexPath]];
-  return [self.collectionUpdater isMostVisitedSection:indexPath.section] ||
-         itemType == ContentSuggestionTypeEmpty;
+  return YES;
 }
 
 - (UIColor*)collectionView:(nonnull UICollectionView*)collectionView
@@ -555,10 +451,7 @@
 
 - (BOOL)collectionView:(UICollectionView*)collectionView
     shouldHideHeaderBackgroundForSection:(NSInteger)section {
-  if (IsUIRefreshPhase1Enabled()) {
-    return [self.collectionUpdater shouldUseCustomStyleForSection:section];
-  }
-  return YES;
+  return [self.collectionUpdater shouldUseCustomStyleForSection:section];
 }
 
 - (CGFloat)collectionView:(UICollectionView*)collectionView
@@ -576,30 +469,20 @@
 
 - (BOOL)collectionView:(UICollectionView*)collectionView
     shouldHideItemSeparatorAtIndexPath:(NSIndexPath*)indexPath {
-  if (IsUIRefreshPhase1Enabled()) {
-    // Special case, show a seperator between the last regular item and the
-    // footer.
-    if (![self.collectionUpdater
-            shouldUseCustomStyleForSection:indexPath.section] &&
-        indexPath.row ==
-            [self.collectionView numberOfItemsInSection:indexPath.section] -
-                1) {
-      return NO;
-    }
-    return YES;
-  } else {
-    return [self collectionView:collectionView
-        shouldHideItemBackgroundAtIndexPath:indexPath];
+  // Special case, show a seperator between the last regular item and the
+  // footer.
+  if (![self.collectionUpdater
+          shouldUseCustomStyleForSection:indexPath.section] &&
+      indexPath.row ==
+          [self.collectionView numberOfItemsInSection:indexPath.section] - 1) {
+    return NO;
   }
+  return YES;
 }
 
 - (BOOL)collectionView:(UICollectionView*)collectionView
     shouldHideHeaderSeparatorForSection:(NSInteger)section {
-  if (IsUIRefreshPhase1Enabled()) {
-    return [self.collectionUpdater shouldUseCustomStyleForSection:section];
-  } else {
-    return YES;
-  }
+  return [self.collectionUpdater shouldUseCustomStyleForSection:section];
 }
 
 #pragma mark - MDCCollectionViewEditingDelegate
@@ -708,16 +591,14 @@
 // though content suggestions appear under the top safe area, they are blocked
 // by the browser container view controller.
 - (void)correctMissingSafeArea {
-  if (IsUIRefreshPhase1Enabled()) {
-    if (@available(iOS 11, *)) {
-      UIEdgeInsets missingTop = UIEdgeInsetsZero;
-      // During the new tab animation the browser container view controller
-      // actually matches the browser view controller frame, so safe area does
-      // work, so be sure to check the parent view controller offset.
-      if (self.parentViewController.view.frame.origin.y == StatusBarHeight())
-        missingTop = UIEdgeInsetsMake(StatusBarHeight(), 0, 0, 0);
-      self.additionalSafeAreaInsets = missingTop;
-    }
+  if (@available(iOS 11, *)) {
+    UIEdgeInsets missingTop = UIEdgeInsetsZero;
+    // During the new tab animation the browser container view controller
+    // actually matches the browser view controller frame, so safe area does
+    // work, so be sure to check the parent view controller offset.
+    if (self.parentViewController.view.frame.origin.y == StatusBarHeight())
+      missingTop = UIEdgeInsetsMake(StatusBarHeight(), 0, 0, 0);
+    self.additionalSafeAreaInsets = missingTop;
   }
 }
 
@@ -766,7 +647,7 @@
       break;
   }
 
-  if (content_suggestions::IsRegularXRegularSizeClass(self))
+  if (IsRegularXRegularSizeClass(self))
     [self.headerSynchronizer unfocusOmnibox];
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_constant.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_constant.mm
index d0d8e7b..54baf49 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_constant.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_constant.mm
@@ -19,11 +19,7 @@
 const CGFloat kSuggestionPeekingHeight = 60;
 
 UIColor* kNTPBackgroundColor() {
-  if (IsUIRefreshPhase1Enabled()) {
-    return [UIColor colorWithWhite:0.98 alpha:1.0];
-  } else {
-    return [UIColor whiteColor];
-  }
+  return [UIColor colorWithWhite:0.98 alpha:1.0];
 }
 
 }  // namespace ntp_home
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
index 75f6678..8776372 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -109,7 +109,6 @@
   ContentSuggestionsService* service =
       IOSChromeContentSuggestionsServiceFactory::GetForBrowserState(
           browserState);
-  RegisterReadingListProvider(service, browserState);
   [[ContentSuggestionsTestSingleton sharedInstance]
       registerArticleProvider:service];
 }
@@ -206,7 +205,7 @@
 - (void)testOmniboxWidthRotationBehindSettings {
   // TODO(crbug.com/652465): Enable the test for iPad when rotation bug is
   // fixed.
-  if (content_suggestions::IsRegularXRegularSizeClass()) {
+  if (IsRegularXRegularSizeClass()) {
     EARL_GREY_TEST_DISABLED(@"Disabled for iPad due to device rotation bug.");
   }
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
@@ -246,7 +245,7 @@
 - (void)testOmniboxPinnedWidthRotation {
   // TODO(crbug.com/652465): Enable the test for iPad when rotation bug is
   // fixed.
-  if (content_suggestions::IsRegularXRegularSizeClass()) {
+  if (IsRegularXRegularSizeClass()) {
     EARL_GREY_TEST_DISABLED(@"Disabled for iPad due to device rotation bug.");
   }
 
@@ -272,17 +271,9 @@
   GREYAssertNotEqual(collectionWidth, collectionWidthAfterRotation,
                      @"The collection width has not changed.");
 
-  if (IsUIRefreshPhase1Enabled()) {
-    // In UI refresh, scrolled, landscape, the fake omnibox is hidden.
-    [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                            FakeOmniboxAccessibilityID())]
-        assertWithMatcher:grey_not(grey_sufficientlyVisible())];
-  } else {
-    [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                            FakeOmniboxAccessibilityID())]
-        assertWithMatcher:OmniboxWidthBetween(collectionWidthAfterRotation + 1,
-                                              2)];
-  }
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                          FakeOmniboxAccessibilityID())]
+      assertWithMatcher:grey_not(grey_sufficientlyVisible())];
 }
 
 // Tests that the promo is correctly displayed and removed once tapped.
@@ -395,8 +386,7 @@
 - (void)testTapFakeOmnibox {
   // TODO(crbug.com/753098): Re-enable this test on iOS 11 iPad once
   // grey_typeText works on iOS 11.
-  if (content_suggestions::IsRegularXRegularSizeClass() &&
-      base::ios::IsRunningOnIOS11OrLater()) {
+  if (IsRegularXRegularSizeClass() && base::ios::IsRunningOnIOS11OrLater()) {
     EARL_GREY_TEST_DISABLED(@"Test disabled on iOS 11.");
   }
   // Setup the server.
@@ -419,41 +409,11 @@
   [ChromeEarlGrey waitForWebViewContainingText:kPageLoadedString];
 }
 
-// Tests that tapping the fake omnibox logs correctly.
-// It is important for ranking algorithm of omnibox that requests from fakebox
-// and real omnibox are marked appropriately.
-- (void)testTapFakeOmniboxLogsCorrectly {
-  if (!IsIPadIdiom() || IsUIRefreshPhase1Enabled()) {
-    // This logging only happens on iPad pre-UIRefresh, since on iPhone there is
-    // no real omnibox on NTP, only fakebox, and post-UIRefresh the NTP never
-    // shows multiple omniboxes.
-    return;
-  }
-
-  // Swizzle the method that needs to be called for correct logging.
-  __block BOOL tapped = NO;
-  ScopedBlockSwizzler swizzler([LocationBarLegacyCoordinator class],
-                               @selector(focusOmniboxFromFakebox), ^{
-                                 tapped = YES;
-                               });
-
-  // Tap the fake omnibox.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
-                                          FakeOmniboxAccessibilityID())]
-      performAction:grey_tap()];
-  [ChromeEarlGrey
-      waitForElementWithMatcherSufficientlyVisible:chrome_test_util::Omnibox()];
-
-  // Check that the page is loaded.
-  GREYAssertTrue(tapped, @"The tap on the fakebox was not correctly logged.");
-}
-
 // Tests that tapping the omnibox search button logs correctly.
 // It is important for ranking algorithm of omnibox that requests from the
 // search button and real omnibox are marked appropriately.
 - (void)testTapOmniboxSearchButtonLogsCorrectly {
-  if (!IsUIRefreshPhase1Enabled() || !IsRefreshLocationBarEnabled() ||
-      content_suggestions::IsRegularXRegularSizeClass()) {
+  if (IsRegularXRegularSizeClass()) {
     // This logging only happens on iPhone, since on iPad there's no secondary
     // toolbar.
     return;
@@ -515,7 +475,7 @@
   // TODO(crbug.com/826369) This should use collectionView.safeAreaInsets.top
   // instead of -StatusBarHeight once iOS10 is dropped and the NTP is out of
   // native content.
-  CGFloat top = IsUIRefreshPhase1Enabled() ? StatusBarHeight() : 0;
+  CGFloat top = StatusBarHeight();
   GREYAssertTrue(offsetAfterTap.y >= origin.y + headerHeight - (60 + top),
                  @"The collection has not moved.");
 
@@ -578,8 +538,7 @@
 
 // Tests tapping the search button when the fake omnibox is scrolled.
 - (void)testTapSearchButtonFakeOmniboxScrolled {
-  if (!IsUIRefreshPhase1Enabled() ||
-      content_suggestions::IsRegularXRegularSizeClass()) {
+  if (IsRegularXRegularSizeClass()) {
     // This only happens on iPhone, since on iPad there's no secondary toolbar.
     return;
   }
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_controller.h
index 5691fa8..685d2a9 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller.h
@@ -27,13 +27,8 @@
 @class TabModel;
 @protocol UrlLoader;
 
-// A controller for the New Tab Page user interface. Supports multiple "panels",
-// each with its own controller. The panels are created lazily.
-//
-// The strongly retained instance variables |*Controller_| are instances of
-// subclasses of CRWNativeContent that are created lazily.
-// Each Panel is its own controller with the accessible views are added to the
-// |ntpView_|.
+// A controller for the New Tab Page user interface. Supports content
+// suggestions and incognito, each with its own controller.
 //
 // The currently visible CRWNativeContent is accessible through
 // |currentController_|.
@@ -47,7 +42,7 @@
     swipeRecognizerProvider;
 
 // Exposes content inset of contentSuggestions collectionView to ensure all of
-// content is visible under the bottom toolbar in ui refresh.
+// content is visible under the bottom toolbar.
 @property(nonatomic) UIEdgeInsets contentInset;
 
 // Init with the given url (presumably "chrome://newtab") and loader object.
@@ -75,8 +70,6 @@
 
 #pragma mark - Testing
 
-@class NewTabPageView;
-
 @interface NewTabPageController (TestSupport)
 - (id<CRWNativeContent>)currentController;
 - (id<CRWNativeContent>)incognitoController;
diff --git a/ios/chrome/browser/ui/settings/google_services_settings_coordinator.mm b/ios/chrome/browser/ui/settings/google_services_settings_coordinator.mm
index a0f8349..f9092982 100644
--- a/ios/chrome/browser/ui/settings/google_services_settings_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/google_services_settings_coordinator.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/ui/settings/google_services_settings_coordinator.h"
 
+#include "base/mac/foundation_util.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "components/browser_sync/profile_sync_service.h"
@@ -15,6 +16,7 @@
 #import "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/sync/profile_sync_service_factory.h"
 #include "ios/chrome/browser/sync/sync_setup_service_factory.h"
+#import "ios/chrome/browser/ui/authentication/authentication_flow.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/commands/show_signin_command.h"
@@ -43,6 +45,11 @@
 @property(nonatomic, strong) GoogleServicesSettingsMediator* mediator;
 // Returns the authentication service.
 @property(nonatomic, assign, readonly) AuthenticationService* authService;
+// Manages the authentication flow for a given identity.
+@property(nonatomic, strong) AuthenticationFlow* authenticationFlow;
+// View controller presented by this coordinator.
+@property(nonatomic, strong, readonly)
+    GoogleServicesSettingsViewController* googleServicesSettingsViewController;
 
 @end
 
@@ -52,6 +59,7 @@
 @synthesize delegate = _delegate;
 @synthesize dispatcher = _dispatcher;
 @synthesize mediator = _mediator;
+@synthesize authenticationFlow = _authenticationFlow;
 
 - (void)start {
   UICollectionViewLayout* layout = [[MDCCollectionViewFlowLayout alloc] init];
@@ -95,10 +103,31 @@
   return AuthenticationServiceFactory::GetForBrowserState(self.browserState);
 }
 
+- (GoogleServicesSettingsViewController*)googleServicesSettingsViewController {
+  return base::mac::ObjCCast<GoogleServicesSettingsViewController>(
+      self.viewController);
+}
+
 #pragma mark - GoogleServicesSettingsLocalCommands
 
 - (void)restartAuthenticationFlow {
-  // TODO(crbug.com/849754): Restart the authentication flow.
+  ChromeIdentity* authenticatedIdentity =
+      AuthenticationServiceFactory::GetForBrowserState(self.browserState)
+          ->GetAuthenticatedIdentity();
+  [self.googleServicesSettingsViewController preventUserInteraction];
+  DCHECK(!self.authenticationFlow);
+  self.authenticationFlow = [[AuthenticationFlow alloc]
+          initWithBrowserState:self.browserState
+                      identity:authenticatedIdentity
+               shouldClearData:SHOULD_CLEAR_DATA_USER_CHOICE
+              postSignInAction:POST_SIGNIN_ACTION_START_SYNC
+      presentingViewController:self.viewController];
+  self.authenticationFlow.dispatcher = self.dispatcher;
+  __weak GoogleServicesSettingsCoordinator* weakSelf = self;
+  [self.authenticationFlow startSignInWithCompletion:^(BOOL success) {
+    // TODO(crbug.com/889919): Needs to add histogram for |success|.
+    [weakSelf.googleServicesSettingsViewController allowUserInteraction];
+  }];
 }
 
 - (void)openReauthDialogAsSyncIsInAuthError {
@@ -113,7 +142,8 @@
 }
 
 - (void)openPassphraseDialog {
-  // TODO(crbug.com/849754): open the passphrase dialog.
+  [self.dispatcher
+      showSyncPassphraseSettingsFromViewController:self.viewController];
 }
 
 - (void)openGoogleActivityControlsDialog {
diff --git a/ios/web/web_state/web_frame_util.mm b/ios/web/web_state/web_frame_util.mm
index 3550c57..8821a52f 100644
--- a/ios/web/web_state/web_frame_util.mm
+++ b/ios/web/web_state/web_frame_util.mm
@@ -29,6 +29,8 @@
 }
 
 WebFrame* GetWebFrameWithId(WebState* web_state, const std::string& frame_id) {
+  if (frame_id.size() == 0)
+    return nullptr;
   WebFramesManager* manager = WebFramesManager::FromWebState(web_state);
   DCHECK(manager);
   return manager->GetFrameWithId(frame_id);
diff --git a/ios/web_view/BUILD.gn b/ios/web_view/BUILD.gn
index 64f0c40..591d5ee 100644
--- a/ios/web_view/BUILD.gn
+++ b/ios/web_view/BUILD.gn
@@ -213,7 +213,11 @@
 ]
 ios_web_view_sources += ios_web_view_public_headers
 if (ios_web_view_enable_sync) {
-  ios_web_view_sources += [ "internal/signin/cwv_identity.mm" ]
+  ios_web_view_sources += [
+    "internal/signin/cwv_identity.mm",
+    "internal/sync/cwv_sync_controller.mm",
+    "internal/sync/cwv_sync_controller_internal.h",
+  ]
 }
 if (ios_web_view_enable_autofill) {
   ios_web_view_sources += [
@@ -378,6 +382,7 @@
     "internal/cwv_html_element_unittest.mm",
     "internal/cwv_preferences_unittest.mm",
     "internal/cwv_preview_element_info_unittest.mm",
+    "internal/cwv_scroll_view_unittest.mm",
     "internal/signin/cwv_identity_unittest.mm",
     "internal/translate/cwv_translation_controller_unittest.mm",
     "internal/translate/cwv_translation_language_unittest.mm",
diff --git a/ios/web_view/internal/cwv_scroll_view.mm b/ios/web_view/internal/cwv_scroll_view.mm
index f688dd0..2e4a541 100644
--- a/ios/web_view/internal/cwv_scroll_view.mm
+++ b/ios/web_view/internal/cwv_scroll_view.mm
@@ -168,6 +168,14 @@
   }
 }
 
+- (BOOL)webViewScrollViewShouldScrollToTop:
+    (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
+  if ([_delegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) {
+    return [_delegate scrollViewShouldScrollToTop:self];
+  }
+  return YES;
+}
+
 - (void)webViewScrollViewDidResetContentSize:
     (CRWWebViewScrollViewProxy*)webViewScrollViewProxy {
   self.contentSize = _proxy.contentSize;
diff --git a/ios/web_view/internal/cwv_scroll_view_unittest.mm b/ios/web_view/internal/cwv_scroll_view_unittest.mm
new file mode 100644
index 0000000..4ac915c
--- /dev/null
+++ b/ios/web_view/internal/cwv_scroll_view_unittest.mm
@@ -0,0 +1,70 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web_view/internal/cwv_scroll_view_internal.h"
+
+#import <UIKit/UIKit.h>
+
+#import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
+#import "ios/web_view/public/cwv_scroll_view_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios_web_view {
+
+class CWVScrollViewTest : public PlatformTest {
+ protected:
+  CWVScrollViewTest() {
+    scroll_view_ = [[CWVScrollView alloc] init];
+
+    scroll_view_proxy_ = [[CRWWebViewScrollViewProxy alloc] init];
+    scroll_view_.proxy = scroll_view_proxy_;
+
+    ui_scroll_view_ = [[UIScrollView alloc] init];
+    [scroll_view_proxy_ setScrollView:ui_scroll_view_];
+  }
+
+  CWVScrollView* scroll_view_;
+  UIScrollView* ui_scroll_view_;
+  CRWWebViewScrollViewProxy* scroll_view_proxy_;
+};
+
+// Tests CWVScrollView delegate callbacks.
+TEST_F(CWVScrollViewTest, DelegateCallbacks) {
+  id delegate = OCMProtocolMock(@protocol(CWVScrollViewDelegate));
+  scroll_view_.delegate = delegate;
+
+  [[delegate expect] scrollViewWillBeginDragging:scroll_view_];
+  [scroll_view_proxy_ scrollViewWillBeginDragging:ui_scroll_view_];
+
+  CGPoint targetContentOffset;
+  [[delegate expect] scrollViewWillEndDragging:scroll_view_
+                                  withVelocity:CGPointZero
+                           targetContentOffset:&targetContentOffset];
+  [scroll_view_proxy_ scrollViewWillEndDragging:ui_scroll_view_
+                                   withVelocity:CGPointZero
+                            targetContentOffset:&targetContentOffset];
+
+  [[delegate expect] scrollViewDidScroll:scroll_view_];
+  [scroll_view_proxy_ scrollViewDidScroll:ui_scroll_view_];
+
+  [[delegate expect] scrollViewDidEndDecelerating:scroll_view_];
+  [scroll_view_proxy_ scrollViewDidEndDecelerating:ui_scroll_view_];
+
+  [[delegate expect] scrollViewShouldScrollToTop:scroll_view_];
+  [scroll_view_proxy_ scrollViewShouldScrollToTop:ui_scroll_view_];
+
+  [[delegate expect] scrollViewWillBeginZooming:scroll_view_];
+  [scroll_view_proxy_ scrollViewWillBeginZooming:ui_scroll_view_ withView:nil];
+
+  [delegate verify];
+}
+
+}  // namespace ios_web_view
diff --git a/ios/web_view/internal/cwv_web_view_configuration.mm b/ios/web_view/internal/cwv_web_view_configuration.mm
index 37fe6c1c6..217d557 100644
--- a/ios/web_view/internal/cwv_web_view_configuration.mm
+++ b/ios/web_view/internal/cwv_web_view_configuration.mm
@@ -8,6 +8,7 @@
 
 #include "base/logging.h"
 #include "base/threading/thread_restrictions.h"
+#include "components/browser_sync/profile_sync_service.h"
 #include "ios/web_view/cwv_web_view_features.h"
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_data_manager_internal.h"
@@ -15,6 +16,13 @@
 #import "ios/web_view/internal/cwv_preferences_internal.h"
 #import "ios/web_view/internal/cwv_user_content_controller_internal.h"
 #import "ios/web_view/internal/cwv_web_view_internal.h"
+#include "ios/web_view/internal/signin/ios_web_view_signin_client.h"
+#include "ios/web_view/internal/signin/web_view_account_tracker_service_factory.h"
+#include "ios/web_view/internal/signin/web_view_oauth2_token_service_factory.h"
+#include "ios/web_view/internal/signin/web_view_signin_client_factory.h"
+#include "ios/web_view/internal/signin/web_view_signin_manager_factory.h"
+#import "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
+#import "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
 #include "ios/web_view/internal/web_view_global_state_util.h"
 
@@ -40,6 +48,12 @@
     CWVAutofillDataManager* autofillDataManager;
 #endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 
+#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
+// This web view configuration's sync controller.
+// nil if CWVWebViewConfiguration is created with +incognitoConfiguration.
+@property(nonatomic, readonly, nullable) CWVSyncController* syncController;
+#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
+
 // Initializes configuration with the specified browser state mode.
 - (instancetype)initWithBrowserState:
     (std::unique_ptr<ios_web_view::WebViewBrowserState>)browserState;
@@ -52,6 +66,9 @@
 @synthesize autofillDataManager = _autofillDataManager;
 #endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 @synthesize preferences = _preferences;
+#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
+@synthesize syncController = _syncController;
+#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
 @synthesize userContentController = _userContentController;
 
 namespace {
@@ -118,7 +135,6 @@
 
 #if BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 #pragma mark - Autofill
-
 - (CWVAutofillDataManager*)autofillDataManager {
   if (!_autofillDataManager && self.persistent) {
     autofill::PersonalDataManager* personalDataManager =
@@ -129,9 +145,42 @@
   }
   return _autofillDataManager;
 }
-
 #endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_AUTOFILL)
 
+#if BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
+#pragma mark - Sync
+- (CWVSyncController*)syncController {
+  if (!_syncController && self.persistent) {
+    browser_sync::ProfileSyncService* profileSyncService =
+        ios_web_view::WebViewProfileSyncServiceFactory::GetForBrowserState(
+            self.browserState);
+    AccountTrackerService* accountTrackerService =
+        ios_web_view::WebViewAccountTrackerServiceFactory::GetForBrowserState(
+            self.browserState);
+    SigninManager* signinManager =
+        ios_web_view::WebViewSigninManagerFactory::GetForBrowserState(
+            self.browserState);
+    ProfileOAuth2TokenService* tokenService =
+        ios_web_view::WebViewOAuth2TokenServiceFactory::GetForBrowserState(
+            self.browserState);
+
+    _syncController = [[CWVSyncController alloc]
+        initWithProfileSyncService:profileSyncService
+             accountTrackerService:accountTrackerService
+                     signinManager:signinManager
+                      tokenService:tokenService];
+
+    // Set the newly created CWVSyncController on IOSWebViewSigninClient to
+    // so access tokens can be fetched.
+    IOSWebViewSigninClient* signinClient =
+        ios_web_view::WebViewSigninClientFactory::GetForBrowserState(
+            self.browserState);
+    signinClient->SetSyncController(_syncController);
+  }
+  return _syncController;
+}
+#endif  // BUILDFLAG(IOS_WEB_VIEW_ENABLE_SYNC)
+
 #pragma mark - Public Methods
 
 - (BOOL)isPersistent {
diff --git a/ios/web_view/internal/signin/ios_web_view_signin_client.h b/ios/web_view/internal/signin/ios_web_view_signin_client.h
index d7db2c1..f5fc8bb 100644
--- a/ios/web_view/internal/signin/ios_web_view_signin_client.h
+++ b/ios/web_view/internal/signin/ios_web_view_signin_client.h
@@ -16,6 +16,8 @@
 #include "net/cookies/cookie_change_dispatcher.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
+@class CWVSyncController;
+
 // iOS WebView specific signin client.
 class IOSWebViewSigninClient : public SigninClient,
                                public SigninErrorController::Observer {
@@ -46,6 +48,9 @@
       content_settings::Observer* observer) override;
   void RemoveContentSettingsObserver(
       content_settings::Observer* observer) override;
+  void PreSignOut(
+      const base::RepeatingClosure& sign_out,
+      signin_metrics::ProfileSignout signout_source_metric) override;
   void DelayNetworkCall(const base::Closure& callback) override;
   std::unique_ptr<GaiaAuthFetcher> CreateGaiaAuthFetcher(
       GaiaAuthConsumer* consumer,
@@ -57,6 +62,10 @@
   // SigninErrorController::Observer implementation.
   void OnErrorChanged() override;
 
+  // CWVSyncController setter/getter.
+  void SetSyncController(CWVSyncController* sync_controller);
+  CWVSyncController* GetSyncController() const;
+
  private:
   // Helper to delay callbacks until connection becomes online again.
   std::unique_ptr<WaitForNetworkCallbackHelper> network_callback_helper_;
@@ -70,6 +79,10 @@
   scoped_refptr<content_settings::CookieSettings> cookie_settings_;
   // Used to add and remove content settings observers.
   scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
+  // Used by WebViewProfileOAuth2TokenServiceIOSProviderImpl to fetch access
+  // tokens. Also used to notify of signout events. Held weak so this class
+  // does not determine |sync_controller_|'s lifetime.
+  __weak CWVSyncController* sync_controller_ = nil;
 
   DISALLOW_COPY_AND_ASSIGN(IOSWebViewSigninClient);
 };
diff --git a/ios/web_view/internal/signin/ios_web_view_signin_client.mm b/ios/web_view/internal/signin/ios_web_view_signin_client.mm
index b2101e65..b0ee3092 100644
--- a/ios/web_view/internal/signin/ios_web_view_signin_client.mm
+++ b/ios/web_view/internal/signin/ios_web_view_signin_client.mm
@@ -7,6 +7,7 @@
 #include "components/signin/core/browser/cookie_settings_util.h"
 #include "components/signin/core/browser/device_id_helper.h"
 #include "google_apis/gaia/gaia_auth_fetcher.h"
+#import "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -84,6 +85,13 @@
   host_content_settings_map_->RemoveObserver(observer);
 }
 
+void IOSWebViewSigninClient::PreSignOut(
+    const base::RepeatingClosure& sign_out,
+    signin_metrics::ProfileSignout signout_source_metric) {
+  sign_out.Run();
+  [sync_controller_ didSignoutWithSourceMetric:signout_source_metric];
+}
+
 void IOSWebViewSigninClient::DelayNetworkCall(const base::Closure& callback) {
   network_callback_helper_->HandleCallback(callback);
 }
@@ -97,3 +105,12 @@
 }
 
 void IOSWebViewSigninClient::OnErrorChanged() {}
+
+void IOSWebViewSigninClient::SetSyncController(
+    CWVSyncController* sync_controller) {
+  sync_controller_ = sync_controller;
+}
+
+CWVSyncController* IOSWebViewSigninClient::GetSyncController() const {
+  return sync_controller_;
+}
diff --git a/ios/web_view/internal/signin/web_view_oauth2_token_service_factory.mm b/ios/web_view/internal/signin/web_view_oauth2_token_service_factory.mm
index e0b86f61..4af229e 100644
--- a/ios/web_view/internal/signin/web_view_oauth2_token_service_factory.mm
+++ b/ios/web_view/internal/signin/web_view_oauth2_token_service_factory.mm
@@ -56,7 +56,8 @@
   IOSWebViewSigninClient* signin_client =
       WebViewSigninClientFactory::GetForBrowserState(browser_state);
   auto token_service_provider =
-      std::make_unique<WebViewProfileOAuth2TokenServiceIOSProviderImpl>();
+      std::make_unique<WebViewProfileOAuth2TokenServiceIOSProviderImpl>(
+          signin_client);
   auto delegate = std::make_unique<ProfileOAuth2TokenServiceIOSDelegate>(
       signin_client, std::move(token_service_provider),
       WebViewAccountTrackerServiceFactory::GetForBrowserState(browser_state),
diff --git a/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.h b/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.h
index c5dc1d2..26a69a7 100644
--- a/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.h
+++ b/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.h
@@ -11,11 +11,15 @@
 #include "base/macros.h"
 #include "components/signin/ios/browser/profile_oauth2_token_service_ios_provider.h"
 
+class IOSWebViewSigninClient;
+
 // Implementation of ProfileOAuth2TokenServiceIOSProvider.
 class WebViewProfileOAuth2TokenServiceIOSProviderImpl
     : public ProfileOAuth2TokenServiceIOSProvider {
  public:
-  WebViewProfileOAuth2TokenServiceIOSProviderImpl();
+  // |signin_client| used to fetch access tokens.
+  explicit WebViewProfileOAuth2TokenServiceIOSProviderImpl(
+      IOSWebViewSigninClient* signin_client);
   ~WebViewProfileOAuth2TokenServiceIOSProviderImpl() override;
 
   // ios::ProfileOAuth2TokenServiceIOSProvider
@@ -29,6 +33,9 @@
       NSError* error) const override;
 
  private:
+  // Used to obtain access tokens in |GetAccessToken|.
+  IOSWebViewSigninClient* const signin_client_ = nullptr;
+
   DISALLOW_COPY_AND_ASSIGN(WebViewProfileOAuth2TokenServiceIOSProviderImpl);
 };
 
diff --git a/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.mm b/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.mm
index ab391de1..a0b4684a 100644
--- a/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.mm
+++ b/ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.mm
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/web_view/internal/signin/ios_web_view_signin_client.h"
+#import "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
 #import "ios/web_view/public/cwv_identity.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -14,7 +15,9 @@
 #endif
 
 WebViewProfileOAuth2TokenServiceIOSProviderImpl::
-    WebViewProfileOAuth2TokenServiceIOSProviderImpl() {}
+    WebViewProfileOAuth2TokenServiceIOSProviderImpl(
+        IOSWebViewSigninClient* signin_client)
+    : signin_client_(signin_client) {}
 
 WebViewProfileOAuth2TokenServiceIOSProviderImpl::
     ~WebViewProfileOAuth2TokenServiceIOSProviderImpl() = default;
@@ -24,10 +27,25 @@
     const std::string& client_id,
     const std::set<std::string>& scopes,
     const AccessTokenCallback& callback) {
+  // |sync_controller| may still be nil if this is called too early so
+  // |callback| will not be invoked. That's OK because this will be called again
+  // after |sync_controller| has been set.
+  CWVSyncController* sync_controller = signin_client_->GetSyncController();
+  [sync_controller fetchAccessTokenForScopes:scopes callback:callback];
 }
 
 std::vector<ProfileOAuth2TokenServiceIOSProvider::AccountInfo>
 WebViewProfileOAuth2TokenServiceIOSProviderImpl::GetAllAccounts() const {
+  // |sync_controller| may still be nil if this is called too early. That's OK
+  // because this will be called again after it has been set.
+  CWVSyncController* sync_controller = signin_client_->GetSyncController();
+  CWVIdentity* current_identity = sync_controller.currentIdentity;
+  if (current_identity) {
+    AccountInfo account_info;
+    account_info.email = base::SysNSStringToUTF8(current_identity.email);
+    account_info.gaia = base::SysNSStringToUTF8(current_identity.gaiaID);
+    return {account_info};
+  }
   return {};
 }
 
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm
new file mode 100644
index 0000000..bb6619ec
--- /dev/null
+++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -0,0 +1,228 @@
+// 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 "ios/web_view/internal/sync/cwv_sync_controller_internal.h"
+
+#import <UIKit/UIKit.h>
+#include <memory>
+
+#include "base/strings/sys_string_conversions.h"
+#include "components/browser_sync/profile_sync_service.h"
+#include "components/signin/core/browser/account_info.h"
+#include "components/signin/core/browser/account_tracker_service.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "components/signin/core/browser/signin_manager.h"
+#include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
+#include "ios/web/public/web_thread.h"
+#import "ios/web_view/public/cwv_identity.h"
+#import "ios/web_view/public/cwv_sync_controller_data_source.h"
+#import "ios/web_view/public/cwv_sync_controller_delegate.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface CWVSyncController ()
+
+// Called by WebViewSyncServiceObserverBridge's |OnSyncConfigurationCompleted|.
+- (void)didCompleteSyncConfiguration;
+// Called by WebViewSyncServiceObserverBridge's |OnSyncShutdown|.
+- (void)didShutdownSync;
+
+// Call to refresh access tokens for |currentIdentity|.
+- (void)reloadCredentials;
+
+@end
+
+namespace ios_web_view {
+
+// Bridge that observes browser_sync::ProfileSyncService and calls analagous
+// methods on CWVSyncController.
+class WebViewSyncServiceObserverBridge : public syncer::SyncServiceObserver {
+ public:
+  explicit WebViewSyncServiceObserverBridge(CWVSyncController* sync_controller)
+      : sync_controller_(sync_controller) {}
+  void OnStateChanged(syncer::SyncService* sync) override {
+    // No op.
+  }
+
+  void OnSyncCycleCompleted(syncer::SyncService* sync) override {
+    // No op.
+  }
+
+  void OnSyncConfigurationCompleted(syncer::SyncService* sync) override {
+    [sync_controller_ didCompleteSyncConfiguration];
+  }
+
+  void OnForeignSessionUpdated(syncer::SyncService* sync) override {
+    // No op.
+  }
+
+  void OnSyncShutdown(syncer::SyncService* sync) override {
+    [sync_controller_ didShutdownSync];
+  }
+
+ private:
+  __weak CWVSyncController* sync_controller_;
+};
+
+}  // namespace ios_web_view
+
+@implementation CWVSyncController {
+  browser_sync::ProfileSyncService* _profileSyncService;
+  AccountTrackerService* _accountTrackerService;
+  SigninManager* _signinManager;
+  IOSWebViewSigninClient* _signinClient;
+  ProfileOAuth2TokenService* _tokenService;
+  std::unique_ptr<ios_web_view::WebViewSyncServiceObserverBridge> _observer;
+
+  // Data source that can provide access tokens.
+  __weak id<CWVSyncControllerDataSource> _dataSource;
+}
+
+@synthesize delegate = _delegate;
+
+- (instancetype)
+initWithProfileSyncService:(browser_sync::ProfileSyncService*)profileSyncService
+     accountTrackerService:(AccountTrackerService*)accountTrackerService
+             signinManager:(SigninManager*)signinManager
+              tokenService:(ProfileOAuth2TokenService*)tokenService {
+  self = [super init];
+  if (self) {
+    _profileSyncService = profileSyncService;
+    _accountTrackerService = accountTrackerService;
+    _signinManager = signinManager;
+    _tokenService = tokenService;
+    _observer =
+        std::make_unique<ios_web_view::WebViewSyncServiceObserverBridge>(self);
+    _profileSyncService->AddObserver(_observer.get());
+
+    // Refresh access tokens on foreground to extend expiration dates.
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(reloadCredentials)
+               name:UIApplicationWillEnterForegroundNotification
+             object:nil];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  _profileSyncService->RemoveObserver(_observer.get());
+}
+
+#pragma mark - Public Methods
+
+- (CWVIdentity*)currentIdentity {
+  std::string authenticatedID = _signinManager->GetAuthenticatedAccountId();
+  if (authenticatedID.empty()) {
+    return nil;
+  }
+  AccountInfo accountInfo =
+      _accountTrackerService->GetAccountInfo(authenticatedID);
+  NSString* email = base::SysUTF8ToNSString(accountInfo.email);
+  NSString* fullName = base::SysUTF8ToNSString(accountInfo.full_name);
+  NSString* gaiaID = base::SysUTF8ToNSString(accountInfo.gaia);
+  return
+      [[CWVIdentity alloc] initWithEmail:email fullName:fullName gaiaID:gaiaID];
+}
+
+- (BOOL)isPassphraseNeeded {
+  return _profileSyncService->IsPassphraseRequiredForDecryption();
+}
+
+- (void)startSyncWithIdentity:(CWVIdentity*)identity
+                   dataSource:
+                       (__weak id<CWVSyncControllerDataSource>)dataSource {
+  DCHECK(!_dataSource);
+
+  _dataSource = dataSource;
+
+  AccountInfo info;
+  info.gaia = base::SysNSStringToUTF8(identity.gaiaID);
+  info.email = base::SysNSStringToUTF8(identity.email);
+  info.full_name = base::SysNSStringToUTF8(identity.fullName);
+  std::string newAuthenticatedAccountID =
+      _accountTrackerService->SeedAccountInfo(info);
+  _signinManager->OnExternalSigninCompleted(info.email);
+
+  [self reloadCredentials];
+  _profileSyncService->RequestStart();
+  _profileSyncService->SetFirstSetupComplete();
+}
+
+- (void)stopSyncAndClearIdentity {
+  _profileSyncService->RequestStop(syncer::SyncService::CLEAR_DATA);
+  _signinManager->SignOut(
+      signin_metrics::ProfileSignout::USER_CLICKED_SIGNOUT_SETTINGS,
+      signin_metrics::SignoutDelete::IGNORE_METRIC);
+  _dataSource = nil;
+}
+
+- (BOOL)unlockWithPassphrase:(NSString*)passphrase {
+  return _profileSyncService->SetDecryptionPassphrase(
+      base::SysNSStringToUTF8(passphrase));
+}
+
+#pragma mark - Private Methods
+
+- (void)didCompleteSyncConfiguration {
+  if ([_delegate respondsToSelector:@selector(syncControllerDidStartSync:)]) {
+    [_delegate syncControllerDidStartSync:self];
+  }
+}
+
+- (void)didShutdownSync {
+  _profileSyncService->RemoveObserver(_observer.get());
+}
+
+- (void)reloadCredentials {
+  std::string authenticatedID = _signinManager->GetAuthenticatedAccountId();
+  if (!authenticatedID.empty()) {
+    ProfileOAuth2TokenServiceIOSDelegate* tokenDelegate =
+        static_cast<ProfileOAuth2TokenServiceIOSDelegate*>(
+            _tokenService->GetDelegate());
+    tokenDelegate->LoadCredentials(authenticatedID);
+  }
+}
+
+#pragma mark - Internal Methods
+
+- (void)fetchAccessTokenForScopes:(const std::set<std::string>&)scopes
+                         callback:(const ProfileOAuth2TokenServiceIOSProvider::
+                                       AccessTokenCallback&)callback {
+  NSMutableArray<NSString*>* scopesArray = [NSMutableArray array];
+  for (const auto& scope : scopes) {
+    [scopesArray addObject:base::SysUTF8ToNSString(scope)];
+  }
+  ProfileOAuth2TokenServiceIOSProvider::AccessTokenCallback scopedCallback =
+      callback;
+  [_dataSource syncController:self
+      getAccessTokenForScopes:[scopesArray copy]
+            completionHandler:^(NSString* accessToken, NSDate* expirationDate,
+                                NSError* error) {
+              if (!scopedCallback.is_null()) {
+                scopedCallback.Run(accessToken, expirationDate, error);
+              }
+            }];
+}
+
+- (void)didSignoutWithSourceMetric:(signin_metrics::ProfileSignout)metric {
+  if (![_delegate respondsToSelector:@selector
+                  (syncController:didStopSyncWithReason:)]) {
+    return;
+  }
+  CWVStopSyncReason reason;
+  if (metric == signin_metrics::ProfileSignout::USER_CLICKED_SIGNOUT_SETTINGS) {
+    reason = CWVStopSyncReasonClient;
+  } else if (metric == signin_metrics::ProfileSignout::SERVER_FORCED_DISABLE) {
+    reason = CWVStopSyncReasonServer;
+  } else {
+    NOTREACHED();
+    return;
+  }
+  [_delegate syncController:self didStopSyncWithReason:reason];
+}
+
+@end
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_internal.h b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
new file mode 100644
index 0000000..1d0ee27
--- /dev/null
+++ b/ios/web_view/internal/sync/cwv_sync_controller_internal.h
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_VIEW_INTERNAL_SYNC_CWV_SYNC_CONTROLLER_INTERNAL_H_
+#define IOS_WEB_VIEW_INTERNAL_SYNC_CWV_SYNC_CONTROLLER_INTERNAL_H_
+
+#include <set>
+
+#include "components/signin/core/browser/signin_metrics.h"
+#include "ios/web_view/internal/signin/web_view_profile_oauth2_token_service_ios_provider_impl.h"
+#import "ios/web_view/public/cwv_sync_controller.h"
+
+namespace browser_sync {
+class ProfileSyncService;
+}  // namespace browser_sync
+
+class AccountTrackerService;
+class SigninManager;
+class ProfileOAuth2TokenService;
+
+@interface CWVSyncController ()
+
+// All dependencies must out live this class.
+- (instancetype)
+initWithProfileSyncService:(browser_sync::ProfileSyncService*)profileSyncService
+     accountTrackerService:(AccountTrackerService*)accountTrackerService
+             signinManager:(SigninManager*)signinManager
+              tokenService:(ProfileOAuth2TokenService*)tokenService
+    NS_DESIGNATED_INITIALIZER;
+
+// Called by WebViewProfileOAuth2TokenServiceIOSProviderImpl to obtain
+// access tokens for |scopes| to be passed back in |callback|.
+- (void)fetchAccessTokenForScopes:(const std::set<std::string>&)scopes
+                         callback:(const ProfileOAuth2TokenServiceIOSProvider::
+                                       AccessTokenCallback&)callback;
+
+// Called by IOSWebViewSigninClient when signing out.
+- (void)didSignoutWithSourceMetric:(signin_metrics::ProfileSignout)metric;
+
+@end
+
+#endif  // IOS_WEB_VIEW_INTERNAL_SYNC_CWV_SYNC_CONTROLLER_INTERNAL_H_
diff --git a/ios/web_view/internal/web_view_global_state_util.mm b/ios/web_view/internal/web_view_global_state_util.mm
index fcf62ef..185244ae 100644
--- a/ios/web_view/internal/web_view_global_state_util.mm
+++ b/ios/web_view/internal/web_view_global_state_util.mm
@@ -29,6 +29,13 @@
     web_main_delegate =
         std::make_unique<ios_web_view::WebViewWebMainDelegate>();
     web::WebMainParams params(web_main_delegate.get());
+    params.argc = 2;
+    const char* executable = "ios-web-view";
+    // This is used in SigninManagerBase to clear the tokens on startup. This
+    // greatly simplifies the management of ChromeWebView's signin state.
+    const char* clear_token = "--clear-token-service";
+    const char* argv[] = {executable, clear_token};
+    params.argv = argv;
     web_main = std::make_unique<web::WebMain>(std::move(params));
   });
 }
diff --git a/ios/web_view/public/cwv_scroll_view_delegate.h b/ios/web_view/public/cwv_scroll_view_delegate.h
index 794333a6..31122b1 100644
--- a/ios/web_view/public/cwv_scroll_view_delegate.h
+++ b/ios/web_view/public/cwv_scroll_view_delegate.h
@@ -24,6 +24,7 @@
               targetContentOffset:(inout CGPoint*)targetContentOffset;
 - (void)scrollViewDidScroll:(CWVScrollView*)scrollView;
 - (void)scrollViewDidEndDecelerating:(CWVScrollView*)scrollView;
+- (BOOL)scrollViewShouldScrollToTop:(CWVScrollView*)scrollView;
 
 // The equivalent in UIScrollViewDelegate also takes a parameter (UIView*)view,
 // but CWVScrollViewDelegate doesn't expose it for flexibility of future
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 0496c2d..253f3fd 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1152,8 +1152,7 @@
   }
   video_renderer_.Paint(
       video_frame, canvas, gfx::RectF(gfx_rect), flags,
-      pipeline_metadata_.video_decoder_config.video_rotation(), context_3d,
-      context_provider_->ContextSupport());
+      pipeline_metadata_.video_decoder_config.video_rotation(), context_3d);
 }
 
 bool WebMediaPlayerImpl::DidGetOpaqueResponseFromServiceWorker() const {
@@ -1244,9 +1243,8 @@
                            context_provider_->GrContext());
   }
   return video_renderer_.CopyVideoFrameTexturesToGLTexture(
-      context_3d, context_provider_->ContextSupport(), gl, video_frame.get(),
-      target, texture, internal_format, format, type, level, premultiply_alpha,
-      flip_y);
+      context_3d, gl, video_frame.get(), target, texture, internal_format,
+      format, type, level, premultiply_alpha, flip_y);
 }
 
 // static
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 83843ce0..024570f 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -7,11 +7,14 @@
 #include <libdrm/drm_fourcc.h>
 #include <linux/videodev2.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sstream>
 
+#include "base/bind.h"
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "build/build_config.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/video_types.h"
 #include "media/gpu/v4l2/generic_v4l2_device.h"
 #if defined(ARCH_CPU_ARMEL)
@@ -20,13 +23,774 @@
 
 #define DVLOGF(level) DVLOG(level) << __func__ << "(): "
 #define VLOGF(level) VLOG(level) << __func__ << "(): "
+#define VPLOGF(level) VPLOG(level) << __func__ << "(): "
 
 namespace media {
 
+// Class used to store the state of a buffer that should persist between
+// reference creations. This includes:
+// * Result of initial VIDIOC_QUERYBUF ioctl,
+// * Plane mappings.
+//
+// Also provides helper functions.
+class V4L2Buffer {
+ public:
+  static std::unique_ptr<V4L2Buffer> Create(scoped_refptr<V4L2Device> device,
+                                            enum v4l2_buf_type type,
+                                            enum v4l2_memory memory,
+                                            size_t buffer_id);
+  ~V4L2Buffer();
+
+  void* GetPlaneMapping(const size_t plane);
+  const struct v4l2_buffer* v4l2_buffer() const { return &v4l2_buffer_; }
+
+ private:
+  V4L2Buffer(scoped_refptr<V4L2Device> device,
+             enum v4l2_buf_type type,
+             enum v4l2_memory memory,
+             size_t buffer_id);
+  bool Query();
+
+  scoped_refptr<V4L2Device> device_;
+  std::vector<void*> plane_mappings_;
+
+  // V4L2 data as queried by QUERYBUF.
+  struct v4l2_buffer v4l2_buffer_ = {};
+  struct v4l2_plane v4l2_planes_[VIDEO_MAX_PLANES] = {{}};
+
+  DISALLOW_COPY_AND_ASSIGN(V4L2Buffer);
+};
+
+std::unique_ptr<V4L2Buffer> V4L2Buffer::Create(scoped_refptr<V4L2Device> device,
+                                               enum v4l2_buf_type type,
+                                               enum v4l2_memory memory,
+                                               size_t buffer_id) {
+  // Not using std::make_unique because constructor is private.
+  std::unique_ptr<V4L2Buffer> buffer(
+      new V4L2Buffer(device, type, memory, buffer_id));
+
+  if (!buffer->Query())
+    return nullptr;
+
+  return buffer;
+}
+
+V4L2Buffer::V4L2Buffer(scoped_refptr<V4L2Device> device,
+                       enum v4l2_buf_type type,
+                       enum v4l2_memory memory,
+                       size_t buffer_id)
+    : device_(device), plane_mappings_(VIDEO_MAX_PLANES) {
+  DCHECK(V4L2_TYPE_IS_MULTIPLANAR(type));
+  v4l2_buffer_.m.planes = v4l2_planes_;
+  v4l2_buffer_.length = VIDEO_MAX_PLANES;
+  v4l2_buffer_.index = buffer_id;
+  v4l2_buffer_.type = type;
+  v4l2_buffer_.memory = memory;
+}
+
+V4L2Buffer::~V4L2Buffer() {
+  if (v4l2_buffer_.memory == V4L2_MEMORY_MMAP) {
+    for (size_t i = 0; i < plane_mappings_.size(); i++)
+      if (plane_mappings_[i] != nullptr)
+        device_->Munmap(plane_mappings_[i], v4l2_buffer_.m.planes[i].length);
+  }
+}
+
+bool V4L2Buffer::Query() {
+  int ret = device_->Ioctl(VIDIOC_QUERYBUF, &v4l2_buffer_);
+  if (ret) {
+    VPLOGF(1) << "VIDIOC_QUERYBUF failed: ";
+    return false;
+  }
+
+  plane_mappings_.resize(v4l2_buffer_.length);
+
+  return true;
+}
+
+void* V4L2Buffer::GetPlaneMapping(const size_t plane) {
+  if (plane >= plane_mappings_.size()) {
+    VLOGF(1) << "Invalid plane " << plane << " requested.";
+    return nullptr;
+  }
+
+  void* p = plane_mappings_[plane];
+  if (p)
+    return p;
+
+  // Do this check here to avoid repeating it after a buffer has been
+  // successfully mapped (we know we are of MMAP type by then).
+  if (v4l2_buffer_.memory != V4L2_MEMORY_MMAP) {
+    VLOGF(1) << "Cannot create mapping on non-MMAP buffer";
+    return nullptr;
+  }
+
+  p = device_->Mmap(NULL, v4l2_buffer_.m.planes[plane].length,
+                    PROT_READ | PROT_WRITE, MAP_SHARED,
+                    v4l2_buffer_.m.planes[plane].m.mem_offset);
+  if (p == MAP_FAILED) {
+    VPLOGF(1) << "mmap() failed: ";
+    return nullptr;
+  }
+
+  plane_mappings_[plane] = p;
+  return p;
+}
+
+// Module-private class that let users query/write V4L2 buffer information.
+// It also makes some private V4L2Queue methods available to this module only.
+class V4L2BufferQueueProxy {
+ public:
+  V4L2BufferQueueProxy(const struct v4l2_buffer* v4l2_buffer,
+                       scoped_refptr<V4L2Queue> queue);
+
+  void ReturnBuffer() { queue_->ReturnBuffer(BufferId()); }
+
+  bool QueueBuffer();
+
+  void* GetPlaneMapping(const size_t plane) {
+    return queue_->buffers_[BufferId()]->GetPlaneMapping(plane);
+  }
+
+  // Data from the buffer, that users can query and/or write.
+  struct v4l2_buffer v4l2_buffer_;
+  struct v4l2_plane v4l2_planes_[VIDEO_MAX_PLANES];
+
+ private:
+  size_t BufferId() const { return v4l2_buffer_.index; }
+
+  // The queue must be kept alive as long as the reference to the buffer exists.
+  scoped_refptr<V4L2Queue> queue_;
+
+  DISALLOW_COPY_AND_ASSIGN(V4L2BufferQueueProxy);
+};
+
+V4L2BufferQueueProxy::V4L2BufferQueueProxy(
+    const struct v4l2_buffer* v4l2_buffer,
+    scoped_refptr<V4L2Queue> queue)
+    : queue_(std::move(queue)) {
+  DCHECK(V4L2_TYPE_IS_MULTIPLANAR(v4l2_buffer->type));
+  DCHECK_LE(v4l2_buffer->length, static_cast<uint32_t>(VIDEO_MAX_PLANES));
+
+  memcpy(&v4l2_buffer_, v4l2_buffer, sizeof(v4l2_buffer_));
+  memcpy(v4l2_planes_, v4l2_buffer->m.planes,
+         sizeof(struct v4l2_plane) * v4l2_buffer->length);
+  v4l2_buffer_.m.planes = v4l2_planes_;
+}
+
+bool V4L2BufferQueueProxy::QueueBuffer() {
+  bool queued = queue_->QueueBuffer(&v4l2_buffer_);
+
+  // If an error occurred during queueing, then the buffer must be made
+  // available again.
+  if (!queued)
+    ReturnBuffer();
+
+  return queued;
+}
+
+V4L2WritableBufferRef::V4L2WritableBufferRef() {
+  // Invalid buffers can be created from any thread.
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+V4L2WritableBufferRef::V4L2WritableBufferRef(
+    const struct v4l2_buffer* v4l2_buffer,
+    scoped_refptr<V4L2Queue> queue)
+    : buffer_data_(std::make_unique<V4L2BufferQueueProxy>(v4l2_buffer,
+                                                          std::move(queue))) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+V4L2WritableBufferRef::V4L2WritableBufferRef(V4L2WritableBufferRef&& other)
+    : buffer_data_(std::move(other.buffer_data_)) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(other.sequence_checker_);
+}
+
+V4L2WritableBufferRef::~V4L2WritableBufferRef() {
+  // Only valid references should be sequence-checked
+  if (buffer_data_) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    buffer_data_->ReturnBuffer();
+  }
+}
+
+V4L2WritableBufferRef& V4L2WritableBufferRef::operator=(
+    V4L2WritableBufferRef&& other) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_CALLED_ON_VALID_SEQUENCE(other.sequence_checker_);
+
+  if (this == &other)
+    return *this;
+
+  if (IsValid())
+    buffer_data_->ReturnBuffer();
+  buffer_data_ = std::move(other.buffer_data_);
+
+  return *this;
+}
+
+bool V4L2WritableBufferRef::IsValid() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return buffer_data_ != nullptr;
+}
+
+enum v4l2_memory V4L2WritableBufferRef::Memory() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  return static_cast<enum v4l2_memory>(buffer_data_->v4l2_buffer_.memory);
+}
+
+bool V4L2WritableBufferRef::DoQueue() && {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  bool queued = buffer_data_->QueueBuffer();
+
+  // Clear our own reference.
+  buffer_data_.reset();
+
+  return queued;
+}
+
+bool V4L2WritableBufferRef::QueueMMap() && {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  // Move ourselves so our data gets freed no matter when we return
+  V4L2WritableBufferRef self(std::move(*this));
+
+  if (self.Memory() != V4L2_MEMORY_MMAP) {
+    VLOGF(1) << "Called on invalid buffer type!";
+    return false;
+  }
+
+  return std::move(self).DoQueue();
+}
+
+bool V4L2WritableBufferRef::QueueUserPtr(const std::vector<void*>& ptrs) && {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  // Move ourselves so our data gets freed no matter when we return
+  V4L2WritableBufferRef self(std::move(*this));
+
+  if (self.Memory() != V4L2_MEMORY_USERPTR) {
+    VLOGF(1) << "Called on invalid buffer type!";
+    return false;
+  }
+
+  if (ptrs.size() != self.PlanesCount()) {
+    VLOGF(1) << "Provided " << ptrs.size() << " pointers while we require "
+             << self.buffer_data_->v4l2_buffer_.length << ".";
+    return false;
+  }
+
+  for (size_t i = 0; i < ptrs.size(); i++)
+    self.buffer_data_->v4l2_buffer_.m.planes[i].m.userptr =
+        reinterpret_cast<unsigned long>(ptrs[i]);
+
+  return std::move(self).DoQueue();
+}
+
+bool V4L2WritableBufferRef::QueueDMABuf(
+    const std::vector<base::ScopedFD>& fds) && {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  // Move ourselves so our data gets freed no matter when we return
+  V4L2WritableBufferRef self(std::move(*this));
+
+  if (self.Memory() != V4L2_MEMORY_DMABUF) {
+    VLOGF(1) << "Called on invalid buffer type!";
+    return false;
+  }
+
+  if (fds.size() != self.PlanesCount()) {
+    VLOGF(1) << "Provided " << fds.size() << " FDs while we require "
+             << self.buffer_data_->v4l2_buffer_.length << ".";
+    return false;
+  }
+
+  for (size_t i = 0; i < fds.size(); i++)
+    self.buffer_data_->v4l2_buffer_.m.planes[i].m.fd = fds[i].get();
+
+  return std::move(self).DoQueue();
+}
+
+size_t V4L2WritableBufferRef::PlanesCount() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  return buffer_data_->v4l2_buffer_.length;
+}
+
+size_t V4L2WritableBufferRef::GetPlaneSize(const size_t plane) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  if (plane >= PlanesCount()) {
+    VLOGF(1) << "Invalid plane " << plane << " requested.";
+    return 0;
+  }
+
+  return buffer_data_->v4l2_buffer_.m.planes[plane].length;
+}
+
+void* V4L2WritableBufferRef::GetPlaneMapping(const size_t plane) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  return buffer_data_->GetPlaneMapping(plane);
+}
+
+void V4L2WritableBufferRef::SetTimeStamp(const struct timeval& timestamp) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  buffer_data_->v4l2_buffer_.timestamp = timestamp;
+}
+
+const struct timeval& V4L2WritableBufferRef::GetTimeStamp() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  return buffer_data_->v4l2_buffer_.timestamp;
+}
+
+void V4L2WritableBufferRef::SetPlaneBytesUsed(const size_t plane,
+                                              const size_t bytes_used) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  if (plane >= PlanesCount()) {
+    VLOGF(1) << "Invalid plane " << plane << " requested.";
+    return;
+  }
+
+  if (bytes_used >= GetPlaneSize(plane)) {
+    VLOGF(1) << "Set bytes used " << bytes_used << " larger than plane size "
+             << GetPlaneSize(plane) << ".";
+    return;
+  }
+
+  buffer_data_->v4l2_buffer_.m.planes[plane].bytesused = bytes_used;
+}
+
+size_t V4L2WritableBufferRef::GetPlaneBytesUsed(const size_t plane) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  if (plane >= PlanesCount()) {
+    VLOGF(1) << "Invalid plane " << plane << " requested.";
+    return 0;
+  }
+
+  return buffer_data_->v4l2_buffer_.m.planes[plane].bytesused;
+}
+
+size_t V4L2WritableBufferRef::BufferId() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(IsValid());
+
+  return buffer_data_->v4l2_buffer_.index;
+}
+
+V4L2ReadableBuffer::V4L2ReadableBuffer(const struct v4l2_buffer* v4l2_buffer,
+                                       scoped_refptr<V4L2Queue> queue)
+    : buffer_data_(std::make_unique<V4L2BufferQueueProxy>(v4l2_buffer,
+                                                          std::move(queue))) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+V4L2ReadableBuffer::~V4L2ReadableBuffer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(buffer_data_);
+
+  buffer_data_->ReturnBuffer();
+}
+
+bool V4L2ReadableBuffer::IsLast() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(buffer_data_);
+
+  return buffer_data_->v4l2_buffer_.flags & V4L2_BUF_FLAG_LAST;
+}
+
+struct timeval V4L2ReadableBuffer::GetTimeStamp() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(buffer_data_);
+
+  return buffer_data_->v4l2_buffer_.timestamp;
+}
+
+size_t V4L2ReadableBuffer::PlanesCount() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(buffer_data_);
+
+  return buffer_data_->v4l2_buffer_.length;
+}
+
+size_t V4L2ReadableBuffer::GetPlaneBytesUsed(const size_t plane) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(buffer_data_);
+
+  if (plane >= PlanesCount()) {
+    VLOGF(1) << "Invalid plane " << plane << " requested.";
+    return 0;
+  }
+
+  return buffer_data_->v4l2_planes_[plane].bytesused;
+}
+
+size_t V4L2ReadableBuffer::BufferId() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(buffer_data_);
+
+  return buffer_data_->v4l2_buffer_.index;
+}
+
+// This class is used to expose buffer reference classes constructors to
+// this module. This is to ensure that nobody else can create buffer references.
+class V4L2BufferRefFactory {
+ public:
+  static V4L2WritableBufferRef CreateWritableRef(
+      const struct v4l2_buffer* v4l2_buffer,
+      scoped_refptr<V4L2Queue> queue) {
+    return V4L2WritableBufferRef(v4l2_buffer, std::move(queue));
+  }
+
+  static V4L2ReadableBufferRef CreateReadableRef(
+      const struct v4l2_buffer* v4l2_buffer,
+      scoped_refptr<V4L2Queue> queue) {
+    return new V4L2ReadableBuffer(v4l2_buffer, std::move(queue));
+  }
+};
+
+V4L2Queue::V4L2Queue(scoped_refptr<V4L2Device> dev,
+                     enum v4l2_buf_type type,
+                     base::OnceClosure destroy_cb)
+    : type_(type), device_(dev), destroy_cb_(std::move(destroy_cb)) {
+  // TODO(acourbot): fix clients - the constructor should be called on the same
+  // sequence as the rest.
+  DETACH_FROM_SEQUENCE(sequence_checker_);
+}
+
+V4L2Queue::~V4L2Queue() {
+  // TODO(acourbot): we do this prior to checking the sequence because we
+  // tolerate queues to be destroyed in the wrong thread if they are properly
+  // cleaned up. But ultimately clients should be fixed.
+  if (!is_streaming_ && buffers_.empty()) {
+    std::move(destroy_cb_).Run();
+    return;
+  }
+
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (is_streaming_) {
+    VLOGF(1) << "Queue is still streaming, trying to stop it...";
+    Streamoff();
+  }
+
+  DCHECK(queued_buffers_.empty());
+  DCHECK(free_buffers_.empty());
+
+  if (!buffers_.empty()) {
+    VLOGF(1) << "Buffers are still allocated, trying to deallocate them...";
+    DeallocateBuffers();
+  }
+
+  std::move(destroy_cb_).Run();
+}
+
+size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(free_buffers_.size(), 0u);
+  DCHECK_EQ(queued_buffers_.size(), 0u);
+
+  if (IsStreaming()) {
+    VLOGF(1) << "Cannot allocate buffers while streaming.";
+    return 0;
+  }
+
+  if (buffers_.size() != 0) {
+    VLOGF(1) << "Cannot allocate new buffers while others are still allocated.";
+    return 0;
+  }
+
+  if (count == 0) {
+    VLOGF(1) << "Attempting to allocate 0 buffers.";
+    return 0;
+  }
+
+  struct v4l2_requestbuffers reqbufs = {};
+  reqbufs.count = count;
+  reqbufs.type = type_;
+  reqbufs.memory = memory;
+  DVLOGF(3) << "queue " << type_ << ": requesting " << count << " buffers.";
+
+  int ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
+  if (ret) {
+    VPLOGF(1) << "VIDIOC_REQBUFS failed: ";
+    return 0;
+  }
+  DVLOGF(3) << "queue " << type_ << ": got " << reqbufs.count << " buffers.";
+
+  memory_ = memory;
+
+  // Now query all buffer information.
+  for (size_t i = 0; i < reqbufs.count; i++) {
+    auto buffer = V4L2Buffer::Create(device_, type_, memory_, i);
+
+    if (!buffer) {
+      DeallocateBuffers();
+
+      return 0;
+    }
+
+    buffers_.emplace_back(std::move(buffer));
+    ReturnBuffer(i);
+  }
+
+  DCHECK_EQ(free_buffers_.size(), buffers_.size());
+  DCHECK_EQ(queued_buffers_.size(), 0u);
+
+  return buffers_.size();
+}
+
+bool V4L2Queue::DeallocateBuffers() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (IsStreaming()) {
+    VLOGF(1) << "Cannot deallocate buffers while streaming.";
+    return false;
+  }
+
+  if (buffers_.size() != free_buffers_.size()) {
+    VPLOGF(1) << "Trying to deallocate buffers while some are still in use!";
+    return false;
+  }
+
+  if (buffers_.size() == 0)
+    return true;
+
+  // Free all buffers.
+  struct v4l2_requestbuffers reqbufs = {};
+  reqbufs.count = 0;
+  reqbufs.type = type_;
+  reqbufs.memory = memory_;
+
+  int ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
+  if (ret) {
+    VPLOGF(1) << "VIDIOC_REQBUFS failed: ";
+    return false;
+  }
+
+  buffers_.clear();
+  free_buffers_.clear();
+
+  DCHECK_EQ(free_buffers_.size(), 0u);
+  DCHECK_EQ(queued_buffers_.size(), 0u);
+
+  return true;
+}
+
+V4L2WritableBufferRef V4L2Queue::GetFreeBuffer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  auto iter = free_buffers_.begin();
+
+  if (iter == free_buffers_.end()) {
+    VLOGF(3) << "No free buffer available!";
+    return V4L2WritableBufferRef();
+  }
+
+  size_t buffer_id = *iter;
+  free_buffers_.erase(buffer_id);
+
+  return V4L2BufferRefFactory::CreateWritableRef(
+      buffers_[buffer_id]->v4l2_buffer(), this);
+}
+
+void V4L2Queue::ReturnBuffer(size_t buffer_id) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  auto inserted = free_buffers_.emplace(buffer_id);
+  DCHECK_EQ(inserted.second, true);
+}
+
+bool V4L2Queue::QueueBuffer(struct v4l2_buffer* v4l2_buffer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  int ret = device_->Ioctl(VIDIOC_QBUF, v4l2_buffer);
+  if (ret) {
+    VPLOGF(1) << "VIDIOC_QBUF failed: ";
+    return false;
+  }
+
+  auto inserted = queued_buffers_.emplace(v4l2_buffer->index);
+  DCHECK_EQ(inserted.second, true);
+
+  return true;
+}
+
+std::pair<bool, V4L2ReadableBufferRef> V4L2Queue::DequeueBuffer() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // No need to dequeue if no buffers queued.
+  if (QueuedBuffersCount() == 0)
+    return std::make_pair(true, nullptr);
+
+  if (!IsStreaming()) {
+    VLOGF(1) << "Attempting to dequeue a buffer while not streaming.";
+    return std::make_pair(true, nullptr);
+  }
+
+  struct v4l2_buffer v4l2_buffer = {};
+  struct v4l2_plane planes[VIDEO_MAX_PLANES] = {{}};
+  v4l2_buffer.type = type_;
+  v4l2_buffer.memory = memory_;
+  v4l2_buffer.m.planes = planes;
+  // TODO(acourbot): use actual number of planes.
+  v4l2_buffer.length = VIDEO_MAX_PLANES;
+  int ret = device_->Ioctl(VIDIOC_DQBUF, &v4l2_buffer);
+  if (ret) {
+    // TODO(acourbot): we should not have to check for EPIPE as codec clients
+    // should not call this method after the last buffer is dequeued.
+    switch (errno) {
+      case EAGAIN:
+      case EPIPE:
+        // This is not an error but won't provide a buffer either.
+        return std::make_pair(true, nullptr);
+      default:
+        VPLOGF(1) << "VIDIOC_DQBUF failed: ";
+        return std::make_pair(false, nullptr);
+    }
+  }
+
+  auto it = queued_buffers_.find(v4l2_buffer.index);
+  DCHECK(it != queued_buffers_.end());
+  queued_buffers_.erase(*it);
+
+  return std::make_pair(
+      true, V4L2BufferRefFactory::CreateReadableRef(&v4l2_buffer, this));
+}
+
+bool V4L2Queue::IsStreaming() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return is_streaming_;
+}
+
+bool V4L2Queue::Streamon() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (is_streaming_)
+    return true;
+
+  int arg = static_cast<int>(type_);
+  int ret = device_->Ioctl(VIDIOC_STREAMON, &arg);
+  if (ret) {
+    VPLOGF(1) << "VIDIOC_STREAMON failed: ";
+    return false;
+  }
+
+  is_streaming_ = true;
+
+  return true;
+}
+
+bool V4L2Queue::Streamoff() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  // We do not check the value of IsStreaming(), because we may have queued
+  // buffers to the queue and wish to get them back - in such as case, we may
+  // need to do a VIDIOC_STREAMOFF on a stopped queue.
+
+  int arg = static_cast<int>(type_);
+  int ret = device_->Ioctl(VIDIOC_STREAMOFF, &arg);
+  if (ret) {
+    VPLOGF(1) << "VIDIOC_STREAMOFF failed: ";
+    return false;
+  }
+
+  for (const auto& buffer_id : queued_buffers_)
+    ReturnBuffer(buffer_id);
+
+  queued_buffers_.clear();
+
+  is_streaming_ = false;
+
+  return true;
+}
+
+size_t V4L2Queue::AllocatedBuffersCount() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return buffers_.size();
+}
+
+size_t V4L2Queue::FreeBuffersCount() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return free_buffers_.size();
+}
+
+size_t V4L2Queue::QueuedBuffersCount() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  return queued_buffers_.size();
+}
+
+// This class is used to expose V4L2Queue's constructor to this module. This is
+// to ensure that nobody else can create instances of it.
+class V4L2QueueFactory {
+ public:
+  static scoped_refptr<V4L2Queue> CreateQueue(scoped_refptr<V4L2Device> dev,
+                                              enum v4l2_buf_type type,
+                                              base::OnceClosure destroy_cb) {
+    return new V4L2Queue(std::move(dev), type, std::move(destroy_cb));
+  }
+};
+
 V4L2Device::V4L2Device() {}
 
 V4L2Device::~V4L2Device() {}
 
+scoped_refptr<V4L2Queue> V4L2Device::GetQueue(enum v4l2_buf_type type) {
+  switch (type) {
+    // Supported queue types.
+    case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+    case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+      break;
+    default:
+      VLOGF(1) << "Unsupported V4L2 queue type: " << type;
+      return nullptr;
+  }
+
+  // TODO(acourbot): we should instead query the device for available queues,
+  // and allocate them accordingly. This will do for now though.
+  auto it = queues_.find(type);
+  if (it != queues_.end())
+    return scoped_refptr<V4L2Queue>(it->second);
+
+  scoped_refptr<V4L2Queue> queue = V4L2QueueFactory::CreateQueue(
+      this, type,
+      media::BindToCurrentLoop(
+          base::Bind(&V4L2Device::OnQueueDestroyed, this, type)));
+
+  queues_[type] = queue.get();
+  return queue;
+}
+
+void V4L2Device::OnQueueDestroyed(v4l2_buf_type buf_type) {
+  auto it = queues_.find(buf_type);
+  DCHECK(it != queues_.end());
+  queues_.erase(it);
+}
+
 // static
 scoped_refptr<V4L2Device> V4L2Device::Create() {
   VLOGF(2);
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index db9514c3..d17657c9 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -12,8 +12,11 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <vector>
+
 #include <linux/videodev2.h>
 
+#include "base/containers/flat_map.h"
 #include "base/files/scoped_file.h"
 #include "base/memory/ref_counted.h"
 #include "media/base/video_decoder_config.h"
@@ -34,6 +37,244 @@
 
 namespace media {
 
+class V4L2Queue;
+class V4L2BufferQueueProxy;
+
+// A unique reference to a buffer for clients to prepare and submit.
+//
+// Clients can prepare a buffer for queuing using the methods of this class, and
+// then either queue it using the Queue() method corresponding to the memory
+// type of the buffer, or drop the reference to make the buffer available again.
+class MEDIA_GPU_EXPORT V4L2WritableBufferRef {
+ public:
+  // Default constructor, creates invalid buffer reference.
+  V4L2WritableBufferRef();
+  V4L2WritableBufferRef(V4L2WritableBufferRef&& other);
+  V4L2WritableBufferRef& operator=(V4L2WritableBufferRef&& other);
+
+  // Returns true if the reference points to a valid buffer.
+  bool IsValid() const;
+
+  // Return the memory type of the buffer. Useful to e.g. decide which Queue()
+  // method to use.
+  enum v4l2_memory Memory() const;
+
+  // Queue a MMAP buffer.
+  // If successful, true is returned and the reference to the buffer is dropped
+  // so this reference becomes invalid.
+  // In case of error, false is returned and the buffer is returned to the free
+  // list.
+  bool QueueMMap() &&;
+  // Queue a USERPTR buffer, assigning |ptrs| as pointer for each plane.
+  // The size of |ptrs| must be equal to the number of planes of this buffer.
+  // If successful, true is returned and the reference to the buffer is dropped
+  // so this reference becomes invalid.
+  // In case of error, false is returned and the buffer is returned to the free
+  // list.
+  bool QueueUserPtr(const std::vector<void*>& ptrs) &&;
+  // Queue a DMABUF buffer, assigning |fds| as file descriptors for each plane.
+  // The size of |fds| must be equal to the number of planes of this buffer.
+  // If successful, true is returned and the reference to the buffer is dropped
+  // so this reference becomes invalid.
+  // In case of error, false is returned and the buffer is returned to the free
+  // list.
+  bool QueueDMABuf(const std::vector<base::ScopedFD>& fds) &&;
+
+  // Returns the number of planes in this buffer.
+  size_t PlanesCount() const;
+  // Returns the size of the requested |plane|, in bytes.
+  size_t GetPlaneSize(const size_t plane) const;
+  // This method can only be used with MMAP buffers.
+  // It will return a pointer to the data of the |plane|th plane.
+  // In case of error (invalid plane index or mapping failed), a nullptr is
+  // returned.
+  void* GetPlaneMapping(const size_t plane);
+  // Set the timestamp field for this buffer.
+  void SetTimeStamp(const struct timeval& timestamp);
+  // Return the previously-set timestamp field for this buffer.
+  const struct timeval& GetTimeStamp() const;
+  // Set the number of bytes used for |plane|.
+  void SetPlaneBytesUsed(const size_t plane, const size_t bytes_used);
+  // Returns the previously-set number of bytes used for |plane|.
+  size_t GetPlaneBytesUsed(const size_t plane) const;
+
+  // Return the V4L2 buffer ID of the underlying buffer.
+  // TODO(acourbot) This is used for legacy clients but should be ultimately
+  // removed. See crbug/879971
+  size_t BufferId() const;
+
+  ~V4L2WritableBufferRef();
+
+ private:
+  // Do the actual queue operation once the v4l2_buffer structure is properly
+  // filled.
+  bool DoQueue() &&;
+
+  V4L2WritableBufferRef(const struct v4l2_buffer* v4l2_buffer,
+                        scoped_refptr<V4L2Queue> queue);
+  friend class V4L2BufferRefFactory;
+
+  std::unique_ptr<V4L2BufferQueueProxy> buffer_data_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  DISALLOW_COPY_AND_ASSIGN(V4L2WritableBufferRef);
+};
+
+// A reference to a read-only, dequeued buffer.
+//
+// Clients use this class to query the buffer state and content, and are
+// guaranteed that the buffer will not be reused until all references are
+// destroyed.
+class MEDIA_GPU_EXPORT V4L2ReadableBuffer
+    : public base::RefCounted<V4L2ReadableBuffer> {
+ public:
+  // Returns whether the V4L2_BUF_FLAG_LAST flag is set for this buffer.
+  bool IsLast() const;
+  // Return the timestamp set by the driver on this buffer.
+  struct timeval GetTimeStamp() const;
+  // Returns the number of planes in this buffer.
+  size_t PlanesCount() const;
+  // Returns the number of bytes used for |plane|.
+  size_t GetPlaneBytesUsed(size_t plane) const;
+
+  // Return the V4L2 buffer ID of the underlying buffer.
+  // TODO(acourbot) This is used for legacy clients but should be ultimately
+  // removed. See crbug/879971
+  size_t BufferId() const;
+
+ private:
+  ~V4L2ReadableBuffer();
+
+  V4L2ReadableBuffer(const struct v4l2_buffer* v4l2_buffer,
+                     scoped_refptr<V4L2Queue> queue);
+  friend class V4L2BufferRefFactory;
+
+  std::unique_ptr<V4L2BufferQueueProxy> buffer_data_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  friend class base::RefCounted<V4L2ReadableBuffer>;
+  DISALLOW_COPY_AND_ASSIGN(V4L2ReadableBuffer);
+};
+
+// Shortcut for naming consistency.
+using V4L2ReadableBufferRef = scoped_refptr<V4L2ReadableBuffer>;
+
+class V4L2Device;
+class V4L2Buffer;
+
+// Interface representing a specific queue of a |V4L2Device|. It provides free
+// and queued buffer management that is commonly required by clients.
+//
+// Buffers managed by this class undergo the following cycle:
+// 1) Allocated buffers are put into a free buffers pool, indicating that they
+//    are used neither by the client nor the hardware.
+// 2) The client obtains a unique, writable reference to one of the free
+//    buffers in order to set its content and other parameters.
+// 3) The client then queues the buffer obtained in 2), which invalidates its
+//    reference. The buffer is now prepared to be processed by the hardware.
+// 4) Once the hardware is done with the buffer, it is ready to be dequeued by
+//    the client. The client obtains a read-only, counted reference to the
+//    buffer and can read its content and metadata, as well as making other
+//    references to it. The buffer will not be reused until all the references
+//    are dropped. Once this happens, the buffer goes back to the free list
+//    described in 1).
+class MEDIA_GPU_EXPORT V4L2Queue
+    : public base::RefCountedThreadSafe<V4L2Queue> {
+ public:
+  // Allocate |count| buffers for the current format of this queue, with a
+  // specific |memory| allocation, and returns the number of buffers allocated
+  // or zero if an error occurred, or if references to any previously allocated
+  // buffers are still held by any clients.
+  //
+  // The number of allocated buffers may be larger than the number requested, so
+  // callers must always check the return value.
+  //
+  // Calling this method while buffers are still allocated results in an error.
+  size_t AllocateBuffers(size_t count,
+                         enum v4l2_memory memory) WARN_UNUSED_RESULT;
+
+  // Deallocate all buffers previously allocated by |AllocateBuffers|. Any
+  // references to buffers previously allocated held by the client must be
+  // released, or this call will fail.
+  bool DeallocateBuffers();
+
+  // Return a unique pointer to a free buffer for the caller to prepare and
+  // submit, or an empty pointer if no buffer is currently free.
+  //
+  // If the caller discards the returned reference, the underlying buffer is
+  // made available to clients again.
+  V4L2WritableBufferRef GetFreeBuffer();
+
+  // Attempt to dequeue a buffer, and return a reference to it if one was
+  // available.
+  //
+  // The first element of the returned pair will be false if an error occurred,
+  // in which case the second element will be nullptr. If no error occurred,
+  // then the first element will be true and the second element will contain a
+  // reference to the dequeued buffer if one was available, or nullptr
+  // otherwise.
+  // Dequeued buffers will not be reused by the driver until all references to
+  // them are dropped.
+  std::pair<bool, V4L2ReadableBufferRef> DequeueBuffer();
+
+  // Returns true if this queue is currently streaming.
+  bool IsStreaming() const;
+  // If not currently streaming, starts streaming. Returns true if we started
+  // streaming, or were already streaming, or false if we were not streaming
+  // and an error occurred when attempting to start the stream. On failure, any
+  // previously-queued buffers will be dequeued without processing and made
+  // available to the client, while any buffers held by the client will remain
+  // unchanged and their ownership will remain with the client.
+  bool Streamon();
+  // If currently streaming, stops streaming. Also make all queued buffers
+  // available to the client again regardless of the streaming state.
+  // If an error occurred while attempting to stop streaming, then false is
+  // returned and queued buffers are left untouched since the V4L2 queue may
+  // still be using them.
+  bool Streamoff();
+
+  // Returns the number of buffers currently allocated for this queue.
+  size_t AllocatedBuffersCount() const;
+  // Returns the number of currently free buffers on this queue.
+  size_t FreeBuffersCount() const;
+  // Returns the number of buffers currently queued on this queue.
+  size_t QueuedBuffersCount() const;
+
+ private:
+  ~V4L2Queue();
+
+  // Called when clients lose their reference to a buffer.
+  void ReturnBuffer(size_t buffer_id);
+  // Called when clients request a buffer to be queued.
+  bool QueueBuffer(struct v4l2_buffer* v4l2_buffer);
+
+  const enum v4l2_buf_type type_;
+  enum v4l2_memory memory_ = V4L2_MEMORY_MMAP;
+  bool is_streaming_ = false;
+
+  std::vector<std::unique_ptr<V4L2Buffer>> buffers_;
+
+  // Buffers that are available for client to get and submit.
+  // Buffers in this list are not referenced by anyone else than ourselves.
+  std::set<size_t> free_buffers_;
+  // Buffers that have been queued by the client, and not dequeued yet.
+  std::set<size_t> queued_buffers_;
+
+  scoped_refptr<V4L2Device> device_;
+  // Callback to call in this queue's destructor.
+  base::OnceClosure destroy_cb_;
+
+  V4L2Queue(scoped_refptr<V4L2Device> dev,
+            enum v4l2_buf_type type,
+            base::OnceClosure destroy_cb);
+  friend class V4L2QueueFactory;
+  friend class V4L2BufferQueueProxy;
+  friend class base::RefCountedThreadSafe<V4L2Queue>;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  DISALLOW_COPY_AND_ASSIGN(V4L2Queue);
+};
+
 class MEDIA_GPU_EXPORT V4L2Device
     : public base::RefCountedThreadSafe<V4L2Device> {
  public:
@@ -79,6 +320,10 @@
   // The device will be closed in the destructor.
   virtual bool Open(Type type, uint32_t v4l2_pixfmt) = 0;
 
+  // Returns the V4L2Queue corresponding to the requested |type|, or nullptr
+  // if the requested queue type is not supported.
+  scoped_refptr<V4L2Queue> GetQueue(enum v4l2_buf_type type);
+
   // Parameters and return value are the same as for the standard ioctl() system
   // call.
   virtual int Ioctl(int request, void* arg) = 0;
@@ -208,6 +453,13 @@
   // Return true on success, false on error or if the particular implementation
   // is not available.
   virtual bool Initialize() = 0;
+
+  // Associates a v4l2_buf_type to its queue.
+  base::flat_map<enum v4l2_buf_type, V4L2Queue*> queues_;
+
+  // Callback that is called upon a queue's destruction, to cleanup its pointer
+  // in queues_.
+  void OnQueueDestroyed(v4l2_buf_type buf_type);
 };
 
 }  //  namespace media
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index afbb91f..8d12b5f 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -14,7 +14,6 @@
 #include "cc/paint/paint_image.h"
 #include "cc/paint/paint_image_builder.h"
 #include "gpu/GLES2/gl2extchromium.h"
-#include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
@@ -276,35 +275,6 @@
   }
 }
 
-void OnQueryDone(scoped_refptr<VideoFrame> video_frame,
-                 gpu::gles2::GLES2Interface* gl,
-                 unsigned query_id) {
-  gl->DeleteQueriesEXT(1, &query_id);
-  // |video_frame| is dropped here.
-}
-
-void SynchronizeVideoFrameRead(scoped_refptr<VideoFrame> video_frame,
-                               gpu::gles2::GLES2Interface* gl,
-                               gpu::ContextSupport* context_support) {
-  DCHECK(gl);
-  DCHECK(context_support);
-
-  SyncTokenClientImpl client(gl);
-  video_frame->UpdateReleaseSyncToken(&client);
-
-  if (video_frame->metadata()->IsTrue(
-          VideoFrameMetadata::READ_LOCK_FENCES_ENABLED)) {
-    // |video_frame| must be kept alive during read operations.
-    unsigned query_id = 0;
-    gl->GenQueriesEXT(1, &query_id);
-    DCHECK(query_id);
-    gl->BeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, query_id);
-    gl->EndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
-    context_support->SignalQuery(
-        query_id, base::BindOnce(&OnQueryDone, video_frame, gl, query_id));
-  }
-}
-
 }  // anonymous namespace
 
 // Generates an RGB image from a VideoFrame. Convert YUV to RGB plain on GPU.
@@ -446,8 +416,7 @@
     const gfx::RectF& dest_rect,
     cc::PaintFlags& flags,
     VideoRotation video_rotation,
-    const Context3D& context_3d,
-    gpu::ContextSupport* context_support) {
+    const Context3D& context_3d) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if (flags.getAlpha() == 0) {
     return;
@@ -540,23 +509,22 @@
   canvas->flush();
 
   if (video_frame->HasTextures()) {
-    // Synchronize |video_frame| with the read operations in UpdateLastImage(),
-    // which are triggered by canvas->flush().
-    SynchronizeVideoFrameRead(video_frame, gl, context_support);
+    DCHECK(gl);
+    SyncTokenClientImpl client(gl);
+    video_frame->UpdateReleaseSyncToken(&client);
   }
 }
 
 void PaintCanvasVideoRenderer::Copy(
     const scoped_refptr<VideoFrame>& video_frame,
     cc::PaintCanvas* canvas,
-    const Context3D& context_3d,
-    gpu::ContextSupport* context_support) {
+    const Context3D& context_3d) {
   cc::PaintFlags flags;
   flags.setBlendMode(SkBlendMode::kSrc);
   flags.setFilterQuality(kLow_SkFilterQuality);
   Paint(video_frame, canvas,
         gfx::RectF(gfx::SizeF(video_frame->visible_rect().size())), flags,
-        media::VIDEO_ROTATION_0, context_3d, context_support);
+        media::VIDEO_ROTATION_0, context_3d);
 }
 
 namespace {
@@ -933,14 +901,14 @@
                                     target, texture, internal_format, format,
                                     type, level, premultiply_alpha, flip_y);
   gl->DeleteTextures(1, &source_texture);
-  gl->ShallowFlushCHROMIUM();
-  // The caller must call SynchronizeVideoFrameRead() after this operation, but
-  // we can't do that because we don't have the ContextSupport.
+  gl->Flush();
+
+  SyncTokenClientImpl client(gl);
+  video_frame->UpdateReleaseSyncToken(&client);
 }
 
 bool PaintCanvasVideoRenderer::CopyVideoFrameTexturesToGLTexture(
     const Context3D& context_3d,
-    gpu::ContextSupport* context_support,
     gpu::gles2::GLES2Interface* destination_gl,
     const scoped_refptr<VideoFrame>& video_frame,
     unsigned int target,
@@ -968,11 +936,7 @@
     if (!backend_texture.getGLTextureInfo(&texture_info))
       return false;
 
-    // Synchronize |video_frame| with the read operations in UpdateLastImage(),
-    // which are triggered by getBackendTexture().
     gpu::gles2::GLES2Interface* canvas_gl = context_3d.gl;
-    SynchronizeVideoFrameRead(video_frame, canvas_gl, context_support);
-
     gpu::MailboxHolder mailbox_holder;
     mailbox_holder.texture_target = texture_info.fTarget;
     canvas_gl->ProduceTextureDirectCHROMIUM(texture_info.fID,
@@ -1000,30 +964,13 @@
     gpu::SyncToken dest_sync_token;
     destination_gl->GenUnverifiedSyncTokenCHROMIUM(dest_sync_token.GetData());
     canvas_gl->WaitSyncTokenCHROMIUM(dest_sync_token.GetConstData());
+
+    SyncTokenClientImpl client(canvas_gl);
+    video_frame->UpdateReleaseSyncToken(&client);
   } else {
-    gpu::gles2::GLES2Interface* canvas_gl = context_3d.gl;
-
-    // Create a mailbox for the target texture.
-    gpu::MailboxHolder mailbox_holder;
-    mailbox_holder.texture_target = target;
-    destination_gl->ProduceTextureDirectCHROMIUM(texture,
-                                                 mailbox_holder.mailbox.name);
-    destination_gl->GenUnverifiedSyncTokenCHROMIUM(
-        mailbox_holder.sync_token.GetData());
-
-    // Make the target texture available to |canvas_gl|.
-    canvas_gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
-    uint32_t intermediate_texture =
-        canvas_gl->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name);
-
-    // Copy the texture contents and discard the intermediate texture.
     CopyVideoFrameSingleTextureToGLTexture(
-        canvas_gl, video_frame.get(), target, intermediate_texture,
-        internal_format, format, type, level, premultiply_alpha, flip_y);
-    canvas_gl->DeleteTextures(1, &intermediate_texture);
-    canvas_gl->ShallowFlushCHROMIUM();
-
-    SynchronizeVideoFrameRead(video_frame, canvas_gl, context_support);
+        destination_gl, video_frame.get(), target, texture, internal_format,
+        format, type, level, premultiply_alpha, flip_y);
   }
 
   return true;
@@ -1261,9 +1208,8 @@
       return false;
     last_id_ = video_frame->unique_id();
   }
-
-  DCHECK(last_image_);
   last_image_deleting_timer_.Reset();
+  DCHECK(!!last_image_);
   return true;
 }
 
diff --git a/media/renderers/paint_canvas_video_renderer.h b/media/renderers/paint_canvas_video_renderer.h
index d4752f3..d732111 100644
--- a/media/renderers/paint_canvas_video_renderer.h
+++ b/media/renderers/paint_canvas_video_renderer.h
@@ -29,7 +29,6 @@
 
 namespace gpu {
 struct Capabilities;
-class ContextSupport;
 }
 
 namespace media {
@@ -40,29 +39,25 @@
   PaintCanvasVideoRenderer();
   ~PaintCanvasVideoRenderer();
 
-  // Paints |video_frame| translated and scaled to |dest_rect| on |canvas|.
-  //
+  // Paints |video_frame| on |canvas|, scaling and rotating the result to fit
+  // dimensions specified by |dest_rect|.
   // If the format of |video_frame| is PIXEL_FORMAT_NATIVE_TEXTURE, |context_3d|
-  // and |context_support| must be provided.
+  // must be provided.
   //
-  // If |video_frame| is nullptr or an unsupported format, |dest_rect| will be
-  // painted black.
+  // Black will be painted on |canvas| if |video_frame| is null.
   void Paint(const scoped_refptr<VideoFrame>& video_frame,
              cc::PaintCanvas* canvas,
              const gfx::RectF& dest_rect,
              cc::PaintFlags& flags,
              VideoRotation video_rotation,
-             const Context3D& context_3d,
-             gpu::ContextSupport* context_support);
+             const Context3D& context_3d);
 
-  // Paints |video_frame| scaled to its visible size on |canvas|.
-  //
+  // Copy |video_frame| on |canvas|.
   // If the format of |video_frame| is PIXEL_FORMAT_NATIVE_TEXTURE, |context_3d|
-  // and |context_support| must be provided.
+  // must be provided.
   void Copy(const scoped_refptr<VideoFrame>& video_frame,
             cc::PaintCanvas* canvas,
-            const Context3D& context_3d,
-            gpu::ContextSupport* context_support);
+            const Context3D& context_3d);
 
   // Convert the contents of |video_frame| to raw RGB pixels. |rgb_pixels|
   // should point into a buffer large enough to hold as many 32 bit RGBA pixels
@@ -87,12 +82,15 @@
       bool premultiply_alpha,
       bool flip_y);
 
-  // Copy the contents of |video_frame| to |texture| of |destination_gl|.
-  //
+  // Copy the contents of texture of |video_frame| to texture |texture| in
+  // context |destination_gl|.
+  // |level|, |internal_format|, |type| specify target texture |texture|.
   // The format of |video_frame| must be VideoFrame::NATIVE_TEXTURE.
+  // |context_3d| has a GrContext that may be used during the copy.
+  // CorrectLastImageDimensions() ensures that the source texture will be
+  // cropped to |visible_rect|. Returns true on success.
   bool CopyVideoFrameTexturesToGLTexture(
       const Context3D& context_3d,
-      gpu::ContextSupport* context_support,
       gpu::gles2::GLES2Interface* destination_gl,
       const scoped_refptr<VideoFrame>& video_frame,
       unsigned int target,
@@ -170,6 +168,8 @@
   // never be painted again, so we can release the resource.
   void ResetCache();
 
+  void CorrectLastImageDimensions(const SkIRect& visible_rect);
+
   // Used for unit test.
   SkISize LastImageDimensionsForTesting();
 
@@ -179,8 +179,6 @@
   bool UpdateLastImage(const scoped_refptr<VideoFrame>& video_frame,
                        const Context3D& context_3d);
 
-  void CorrectLastImageDimensions(const SkIRect& visible_rect);
-
   // Last image used to draw to the canvas.
   cc::PaintImage last_image_;
 
diff --git a/media/renderers/paint_canvas_video_renderer_unittest.cc b/media/renderers/paint_canvas_video_renderer_unittest.cc
index bd321305..3bca993 100644
--- a/media/renderers/paint_canvas_video_renderer_unittest.cc
+++ b/media/renderers/paint_canvas_video_renderer_unittest.cc
@@ -225,7 +225,7 @@
   cc::PaintFlags flags;
   flags.setFilterQuality(kLow_SkFilterQuality);
   renderer_.Paint(nullptr, canvas, kNaturalRect, flags, VIDEO_ROTATION_0,
-                  Context3D(), nullptr);
+                  Context3D());
 }
 
 void PaintCanvasVideoRendererTest::Paint(
@@ -260,13 +260,13 @@
   flags.setBlendMode(mode);
   flags.setFilterQuality(kLow_SkFilterQuality);
   renderer_.Paint(video_frame, canvas, dest_rect, flags, video_rotation,
-                  Context3D(), nullptr);
+                  Context3D());
 }
 
 void PaintCanvasVideoRendererTest::Copy(
     const scoped_refptr<VideoFrame>& video_frame,
     cc::PaintCanvas* canvas) {
-  renderer_.Copy(video_frame, canvas, Context3D(), nullptr);
+  renderer_.Copy(video_frame, canvas, Context3D());
 }
 
 TEST_F(PaintCanvasVideoRendererTest, NoFrame) {
@@ -564,7 +564,7 @@
   flags.setFilterQuality(kNone_SkFilterQuality);
   renderer_.Paint(video_frame, &canvas,
                   gfx::RectF(bitmap.width(), bitmap.height()), flags,
-                  VIDEO_ROTATION_0, Context3D(), nullptr);
+                  VIDEO_ROTATION_0, Context3D());
   for (int j = 0; j < bitmap.height(); j++) {
     for (int i = 0; i < bitmap.width(); i++) {
       const int value = i + j * bitmap.width();
@@ -657,32 +657,43 @@
   cc::PaintFlags flags;
   flags.setFilterQuality(kLow_SkFilterQuality);
   renderer_.Paint(video_frame, &canvas, kNaturalRect, flags, VIDEO_ROTATION_90,
-                  context_3d, nullptr);
+                  context_3d);
 }
 
 void EmptyCallback(const gpu::SyncToken& sync_token) {}
 
 TEST_F(PaintCanvasVideoRendererTest, CorrectFrameSizeToVisibleRect) {
-  constexpr int fWidth{16}, fHeight{16};
+  int fWidth{16}, fHeight{16};
   SkImageInfo imInfo =
       SkImageInfo::MakeN32(fWidth, fHeight, kOpaque_SkAlphaType);
 
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
+  sk_sp<const GrGLInterface> glInterface(GrGLCreateNullInterface());
+  sk_sp<GrContext> grContext = GrContext::MakeGL(std::move(glInterface));
 
+  sk_sp<SkSurface> surface =
+      SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, imInfo);
+  cc::SkiaPaintCanvas canvas(surface->getCanvas());
+
+  TestGLES2Interface gles2;
+  Context3D context_3d(&gles2, grContext.get());
   gfx::Size coded_size(fWidth, fHeight);
   gfx::Size visible_size(fWidth / 2, fHeight / 2);
 
-  uint8_t memory[fWidth * fHeight * 2] = {0};
+  gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
+  for (size_t i = 0; i < VideoFrame::kMaxPlanes; i++) {
+    mailbox_holders[i] = gpu::MailboxHolder(
+        gpu::Mailbox::Generate(), gpu::SyncToken(), GL_TEXTURE_RECTANGLE_ARB);
+  }
 
-  auto video_frame = media::VideoFrame::WrapExternalData(
-      media::PIXEL_FORMAT_Y16, coded_size, gfx::Rect(visible_size),
-      visible_size, &memory[0], fWidth * fHeight * 2,
+  auto video_frame = VideoFrame::WrapNativeTextures(
+      PIXEL_FORMAT_I420, mailbox_holders, base::Bind(EmptyCallback), coded_size,
+      gfx::Rect(visible_size), visible_size,
       base::TimeDelta::FromMilliseconds(4));
 
   gfx::RectF visible_rect(visible_size.width(), visible_size.height());
   cc::PaintFlags flags;
   renderer_.Paint(video_frame, &canvas, visible_rect, flags, VIDEO_ROTATION_0,
-                  Context3D(), nullptr);
+                  context_3d);
 
   EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().width());
   EXPECT_EQ(fWidth / 2, renderer_.LastImageDimensionsForTesting().height());
diff --git a/media/renderers/video_resource_updater.cc b/media/renderers/video_resource_updater.cc
index 5b4abce..952c9a7 100644
--- a/media/renderers/video_resource_updater.cc
+++ b/media/renderers/video_resource_updater.cc
@@ -866,7 +866,7 @@
 
         // This is software path, so canvas and video_frame are always backed
         // by software.
-        video_renderer_->Copy(video_frame, &canvas, Context3D(), nullptr);
+        video_renderer_->Copy(video_frame, &canvas, Context3D());
       } else {
         HardwarePlaneResource* hardware_resource = plane_resource->AsHardware();
         size_t bytes_per_row = viz::ResourceSizes::CheckedWidthInBytes<size_t>(
diff --git a/services/audio/output_controller.cc b/services/audio/output_controller.cc
index 420ecfda..00e503a 100644
--- a/services/audio/output_controller.cc
+++ b/services/audio/output_controller.cc
@@ -12,14 +12,12 @@
 #include "base/bind_helpers.h"
 #include "base/compiler_specific.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/metrics/persistent_histogram_allocator.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task_runner_util.h"
 #include "base/threading/platform_thread.h"
 #include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "services/audio/stream_monitor.h"
 
@@ -45,10 +43,6 @@
 
 void LogStreamCreationResult(bool for_device_change,
                              StreamCreationResult result) {
-#if defined(OS_WIN)
-  // TODO(https://crbug.com/867827) remove histogram allocator check.
-  CHECK(base::GlobalHistogramAllocator::Get());
-#endif
   if (for_device_change) {
     UMA_HISTOGRAM_ENUMERATION(
         "Media.AudioOutputController.ProxyStreamCreationResultForDeviceChange",
@@ -97,10 +91,6 @@
 }
 
 OutputController::ErrorStatisticsTracker::~ErrorStatisticsTracker() {
-#if defined(OS_WIN)
-  // TODO(https://crbug.com/867827) remove histogram allocator check.
-  CHECK(base::GlobalHistogramAllocator::Get());
-#endif
   UMA_HISTOGRAM_LONG_TIMES("Media.OutputStreamDuration",
                            base::TimeTicks::Now() - start_time_);
   UMA_HISTOGRAM_BOOLEAN("Media.AudioOutputController.CallbackError",
@@ -166,10 +156,6 @@
 }
 
 bool OutputController::Create(bool is_for_device_change) {
-#if defined(OS_WIN)
-  // TODO(https://crbug.com/867827) remove histogram allocator check.
-  CHECK(base::GlobalHistogramAllocator::Get());
-#endif
   DCHECK(task_runner_->BelongsToCurrentThread());
   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime");
   TRACE_EVENT0("audio", "OutputController::Create");
@@ -240,10 +226,6 @@
 }
 
 void OutputController::Play() {
-#if defined(OS_WIN)
-  // TODO(https://crbug.com/867827) remove histogram allocator check.
-  CHECK(base::GlobalHistogramAllocator::Get());
-#endif
   DCHECK(task_runner_->BelongsToCurrentThread());
   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime");
   TRACE_EVENT0("audio", "OutputController::Play");
@@ -289,10 +271,6 @@
 }
 
 void OutputController::Pause() {
-#if defined(OS_WIN)
-  // TODO(https://crbug.com/867827) remove histogram allocator check.
-  CHECK(base::GlobalHistogramAllocator::Get());
-#endif
   DCHECK(task_runner_->BelongsToCurrentThread());
   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime");
   TRACE_EVENT0("audio", "OutputController::Pause");
@@ -312,10 +290,6 @@
 }
 
 void OutputController::Close() {
-#if defined(OS_WIN)
-  // TODO(https://crbug.com/867827) remove histogram allocator check.
-  CHECK(base::GlobalHistogramAllocator::Get());
-#endif
   DCHECK(task_runner_->BelongsToCurrentThread());
   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime");
   TRACE_EVENT0("audio", "OutputController::Close");
@@ -567,10 +541,6 @@
 }
 
 void OutputController::OnDeviceChange() {
-#if defined(OS_WIN)
-  // TODO(https://crbug.com/867827) remove histogram allocator check.
-  CHECK(base::GlobalHistogramAllocator::Get());
-#endif
   DCHECK(task_runner_->BelongsToCurrentThread());
   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime");
   TRACE_EVENT0("audio", "OutputController::OnDeviceChange");
diff --git a/services/audio/output_controller_unittest.cc b/services/audio/output_controller_unittest.cc
index 51786f91..8b0ffee 100644
--- a/services/audio/output_controller_unittest.cc
+++ b/services/audio/output_controller_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
-#include "base/metrics/persistent_histogram_allocator.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
@@ -27,7 +26,6 @@
 #include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "base/unguessable_token.h"
-#include "build/build_config.h"
 #include "media/audio/audio_device_description.h"
 #include "media/audio/fake_audio_log_factory.h"
 #include "media/audio/fake_audio_manager.h"
@@ -39,13 +37,13 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::testing::_;
 using ::testing::AtLeast;
 using ::testing::Invoke;
 using ::testing::Mock;
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::StrictMock;
-using ::testing::_;
 
 using media::AudioBus;
 using media::AudioManager;
@@ -332,15 +330,6 @@
                         std::string(), &mock_sync_reader_,
                         &stream_monitor_coordinator_, processing_id_);
     controller_->SetVolume(kTestVolume);
-#if defined(OS_WIN)
-    // TODO(https://crbug.com/867827) remove histogram allocator creation when
-    // removing output controller checks.
-    if (!base::GlobalHistogramAllocator::Get()) {
-      const int32_t kAllocatorMemorySize = 8 << 20;
-      base::GlobalHistogramAllocator::CreateWithLocalMemory(
-          kAllocatorMemorySize, 0, "HistogramAllocatorTest");
-    }
-#endif
   }
 
   void TearDown() override { controller_ = base::nullopt; }
diff --git a/services/audio/output_stream_unittest.cc b/services/audio/output_stream_unittest.cc
index 191e7c70..f5348fd1 100644
--- a/services/audio/output_stream_unittest.cc
+++ b/services/audio/output_stream_unittest.cc
@@ -6,11 +6,9 @@
 
 #include <utility>
 
-#include "base/metrics/persistent_histogram_allocator.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/unguessable_token.h"
-#include "build/build_config.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/mock_audio_manager.h"
 #include "media/audio/test_audio_thread.h"
@@ -118,15 +116,6 @@
         stream_factory_binding_(&stream_factory_,
                                 mojo::MakeRequest(&stream_factory_ptr_)) {
     mojo::core::SetDefaultProcessErrorCallback(bad_message_callback_.Get());
-#if defined(OS_WIN)
-    // TODO(https://crbug.com/867827) remove histogram allocator creation when
-    // removing output controller checks.
-    if (!base::GlobalHistogramAllocator::Get()) {
-      const int32_t kAllocatorMemorySize = 8 << 20;
-      base::GlobalHistogramAllocator::CreateWithLocalMemory(
-          kAllocatorMemorySize, 0, "HistogramAllocatorTest");
-    }
-#endif
   }
 
   ~TestEnvironment() {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 7fe9e730..cfc72399 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -1604,6 +1604,17 @@
       },
       {
         "args": [
+          "--enable-features=SingleProcessMash",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter"
+        ],
+        "name": "single_process_mash_interactive_ui_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true
+        },
+        "test": "interactive_ui_tests"
+      },
+      {
+        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_interactive_ui_tests",
diff --git a/testing/buildbot/chromium.webrtc.fyi.experimental.json b/testing/buildbot/chromium.webrtc.fyi.experimental.json
deleted file mode 100644
index 4951a90d..0000000
--- a/testing/buildbot/chromium.webrtc.fyi.experimental.json
+++ /dev/null
@@ -1,655 +0,0 @@
-{
-  "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
-  "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "WebRTC Chromium FYI Android Builder": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "jingle_unittests",
-      "remoting_unittests"
-    ]
-  },
-  "WebRTC Chromium FYI Android Builder (dbg)": {},
-  "WebRTC Chromium FYI Android Builder ARM64 (dbg)": {},
-  "WebRTC Chromium FYI Android Tests (dbg) (K Nexus5)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "K",
-              "device_type": "hammerhead",
-              "os": "Android"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      }
-    ]
-  },
-  "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      }
-    ]
-  },
-  "WebRTC Chromium FYI Linux Builder": {
-    "additional_compile_targets": [
-      "frame_analyzer",
-      "remoting/webapp:webapp"
-    ]
-  },
-  "WebRTC Chromium FYI Linux Builder (dbg)": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "frame_analyzer",
-      "jingle_unittests",
-      "remoting_unittests",
-      "remoting/webapp:webapp"
-    ]
-  },
-  "WebRTC Chromium FYI Linux Tester": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=WebRtcApprtcBrowserTest.*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_apprtc",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_functional",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=120000"
-        ],
-        "name": "content_browsertests_stress",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=Webrtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      }
-    ]
-  },
-  "WebRTC Chromium FYI Mac Builder": {
-    "additional_compile_targets": [
-      "frame_analyzer",
-      "remoting/webapp:webapp"
-    ]
-  },
-  "WebRTC Chromium FYI Mac Builder (dbg)": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "frame_analyzer",
-      "jingle_unittests",
-      "remoting_unittests",
-      "remoting/webapp:webapp"
-    ]
-  },
-  "WebRTC Chromium FYI Mac Tester": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=WebRtcApprtcBrowserTest.*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_apprtc",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_functional",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=120000"
-        ],
-        "name": "content_browsertests_stress",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=Webrtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:0a2e",
-              "os": "Mac-10.12.6"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      }
-    ]
-  },
-  "WebRTC Chromium FYI Win Builder": {
-    "additional_compile_targets": [
-      "frame_analyzer",
-      "remoting/webapp:webapp"
-    ]
-  },
-  "WebRTC Chromium FYI Win Builder (dbg)": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "frame_analyzer",
-      "jingle_unittests",
-      "remoting_unittests",
-      "remoting/webapp:webapp"
-    ]
-  },
-  "WebRTC Chromium FYI Win10 Tester": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=WebRtcApprtcBrowserTest.*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_apprtc",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_functional",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=120000"
-        ],
-        "name": "content_browsertests_stress",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=Webrtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-10-15063"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      }
-    ]
-  },
-  "WebRTC Chromium FYI Win7 Tester": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=WebRtcApprtcBrowserTest.*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_apprtc",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_functional",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-7-SP1"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-7-SP1"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=120000"
-        ],
-        "name": "content_browsertests_stress",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-7-SP1"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-7-SP1"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-7-SP1"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=Webrtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-7-SP1"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      }
-    ]
-  },
-  "WebRTC Chromium FYI Win8 Tester": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gtest_filter=WebRtcApprtcBrowserTest.*",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_apprtc",
-        "swarming": {
-          "can_use_on_swarming_builders": false
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
-          "--run-manual",
-          "--test-launcher-jobs=1"
-        ],
-        "name": "browser_tests_functional",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
-          "--run-manual",
-          "--ui-test-action-max-timeout=120000"
-        ],
-        "name": "content_browsertests_stress",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ]
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=Webrtc*"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Windows-8.1-SP0"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      }
-    ]
-  }
-}
diff --git a/testing/buildbot/chromium.webrtc.fyi.json b/testing/buildbot/chromium.webrtc.fyi.json
index fc9268f..4951a90d 100644
--- a/testing/buildbot/chromium.webrtc.fyi.json
+++ b/testing/buildbot/chromium.webrtc.fyi.json
@@ -1,7 +1,7 @@
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
   "AAAAA2 See generate_buildbot_json.py to make changes": {},
-  "Android Builder": {
+  "WebRTC Chromium FYI Android Builder": {
     "additional_compile_targets": [
       "browser_tests",
       "capture_unittests",
@@ -11,27 +11,55 @@
       "remoting_unittests"
     ]
   },
-  "Android Builder (dbg)": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "jingle_unittests",
-      "remoting_unittests"
+  "WebRTC Chromium FYI Android Builder (dbg)": {},
+  "WebRTC Chromium FYI Android Builder ARM64 (dbg)": {},
+  "WebRTC Chromium FYI Android Tests (dbg) (K Nexus5)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=WebRtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "K",
+              "device_type": "hammerhead",
+              "os": "Android"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      }
     ]
   },
-  "Android Builder ARM64 (dbg)": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "jingle_unittests",
-      "remoting_unittests"
+  "WebRTC Chromium FYI Android Tests (dbg) (M Nexus5X)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=WebRtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "device_os": "MMB29Q",
+              "device_type": "bullhead",
+              "os": "Android"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      }
     ]
   },
-  "Linux Builder": {
+  "WebRTC Chromium FYI Linux Builder": {
+    "additional_compile_targets": [
+      "frame_analyzer",
+      "remoting/webapp:webapp"
+    ]
+  },
+  "WebRTC Chromium FYI Linux Builder (dbg)": {
     "additional_compile_targets": [
       "browser_tests",
       "capture_unittests",
@@ -43,7 +71,122 @@
       "remoting/webapp:webapp"
     ]
   },
-  "Linux Builder (dbg)": {
+  "WebRTC Chromium FYI Linux Tester": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=WebRtcApprtcBrowserTest.*",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_apprtc",
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_functional",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
+          "--run-manual",
+          "--ui-test-action-max-timeout=120000"
+        ],
+        "name": "content_browsertests_stress",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=Webrtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      }
+    ]
+  },
+  "WebRTC Chromium FYI Mac Builder": {
+    "additional_compile_targets": [
+      "frame_analyzer",
+      "remoting/webapp:webapp"
+    ]
+  },
+  "WebRTC Chromium FYI Mac Builder (dbg)": {
     "additional_compile_targets": [
       "browser_tests",
       "capture_unittests",
@@ -55,18 +198,122 @@
       "remoting/webapp:webapp"
     ]
   },
-  "Mac Builder": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "frame_analyzer",
-      "jingle_unittests",
-      "remoting_unittests"
+  "WebRTC Chromium FYI Mac Tester": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=WebRtcApprtcBrowserTest.*",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_apprtc",
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_functional",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
+          "--run-manual",
+          "--ui-test-action-max-timeout=120000"
+        ],
+        "name": "content_browsertests_stress",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=Webrtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.12.6"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      }
     ]
   },
-  "Mac Builder (dbg)": {
+  "WebRTC Chromium FYI Win Builder": {
+    "additional_compile_targets": [
+      "frame_analyzer",
+      "remoting/webapp:webapp"
+    ]
+  },
+  "WebRTC Chromium FYI Win Builder (dbg)": {
     "additional_compile_targets": [
       "browser_tests",
       "capture_unittests",
@@ -78,28 +325,331 @@
       "remoting/webapp:webapp"
     ]
   },
-  "Win Builder": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "frame_analyzer",
-      "jingle_unittests",
-      "remoting_unittests",
-      "remoting/webapp:webapp"
+  "WebRTC Chromium FYI Win10 Tester": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=WebRtcApprtcBrowserTest.*",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_apprtc",
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_functional",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
+          "--run-manual",
+          "--ui-test-action-max-timeout=120000"
+        ],
+        "name": "content_browsertests_stress",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=Webrtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-10-15063"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      }
     ]
   },
-  "Win Builder (dbg)": {
-    "additional_compile_targets": [
-      "browser_tests",
-      "capture_unittests",
-      "content_browsertests",
-      "content_unittests",
-      "frame_analyzer",
-      "jingle_unittests",
-      "remoting_unittests",
-      "remoting/webapp:webapp"
+  "WebRTC Chromium FYI Win7 Tester": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=WebRtcApprtcBrowserTest.*",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_apprtc",
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_functional",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-7-SP1"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-7-SP1"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
+          "--run-manual",
+          "--ui-test-action-max-timeout=120000"
+        ],
+        "name": "content_browsertests_stress",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-7-SP1"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-7-SP1"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-7-SP1"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=Webrtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-7-SP1"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      }
+    ]
+  },
+  "WebRTC Chromium FYI Win8 Tester": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--gtest_filter=WebRtcApprtcBrowserTest.*",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_apprtc",
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc_functional.browser_tests.filter",
+          "--run-manual",
+          "--test-launcher-jobs=1"
+        ],
+        "name": "browser_tests_functional",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-8.1-SP0"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-8.1-SP0"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebRtc*MANUAL*:-UsingRealWebcam*",
+          "--run-manual",
+          "--ui-test-action-max-timeout=120000"
+        ],
+        "name": "content_browsertests_stress",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-8.1-SP0"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--test-launcher-filter-file=../../testing/buildbot/filters/webrtc.content_unittests.filter"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-8.1-SP0"
+            }
+          ]
+        },
+        "test": "content_unittests"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-8.1-SP0"
+            }
+          ]
+        },
+        "test": "jingle_unittests"
+      },
+      {
+        "args": [
+          "--gtest_filter=Webrtc*"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Windows-8.1-SP0"
+            }
+          ]
+        },
+        "test": "remoting_unittests"
+      }
     ]
   }
 }
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index b51942c..f0770e2 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -87,6 +87,7 @@
   testonly = true
 
   data = [
+    "//testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter",
     "//testing/buildbot/filters/webui_polymer2_interactive_ui_tests.filter",
   ]
 }
diff --git a/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
new file mode 100644
index 0000000..9231452
--- /dev/null
+++ b/testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter
@@ -0,0 +1,91 @@
+# These tests currently fail with SingleProcessMash enabled.
+# Bug: crbug.com/883523
+
+# Keyboard event related. See crbug.com/889101
+-BrowserCommandControllerInteractiveTest.KeyEventsShouldBeConsumedByWebPageInBrowserFullscreen
+-BrowserCommandControllerInteractiveTest.KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForEsc
+-BrowserCommandControllerInteractiveTest.KeyEventsShouldBeConsumedByWebPageInJsFullscreenExceptForF11
+-BrowserCommandControllerInteractiveTest.ShortcutsShouldTakeEffectInBrowserFullscreen
+-BrowserKeyEventsTest.CtrlKeyEvents
+-BrowserKeyEventsTest.NormalKeyEvents
+-KeyboardLockInteractiveBrowserTest.ActiveWithAllKeysLocked
+-KeyboardLockInteractiveBrowserTest.ActiveWithSomeKeysLocked
+-KeyboardLockInteractiveBrowserTest.CancelActiveKeyboardLockInFullscreen
+-KeyboardLockInteractiveBrowserTest.RequestedButNotActive
+-KeyboardLockInteractiveBrowserTest.SubsequentLockCallSupersedesPreviousCall
+-SitePerProcessTextInputManagerTest.CorrectlyShowVirtualKeyboardIfEnabled
+-TextInput_SurroundingTextChangedTest.FocusToTextContainingTextAreaByClickingCase
+-TextInput_SurroundingTextChangedTest.SurroundingTextChangedWithComposition
+-TextInput_SurroundingTextChangedTest.SurroundingTextChangedWithInsertText
+
+# misc.
+-DevToolsManagerDelegateTest.ShowMinimizedWindow
+-OmniboxViewViewsTest.DeactivateTouchEditingOnExecuteCommand
+-OmniboxViewViewsTest.SelectAllOnTap
+-WindowActivityWatcherTest.Basic
+-WindowActivityWatcherTest.MultipleWindows
+-WindowActivityWatcherTest.WindowActivation
+
+# Broken tests related to accessibility. crbug.com/888750 and crbug.com/889093
+-GuestSpokenFeedbackTest.FocusToolbar
+-SelectToSpeakTest.ActivatesWithTapOnSelectToSpeakTray
+-SelectToSpeakTest.BreaksAtParagraphBounds
+-SelectToSpeakTest.ContinuesReadingDuringResize
+-SelectToSpeakTest.FocusRingMovesWithMouse
+-SelectToSpeakTest.ReadsStaticTextWithoutInlineTextChildren
+-SelectToSpeakTest.SelectToSpeakTrayNotSpoken
+-SelectToSpeakTest.SmoothlyReadsAcrossFormattedText
+-SelectToSpeakTest.SmoothlyReadsAcrossInlineUrl
+-SelectToSpeakTest.SmoothlyReadsAcrossMultipleLines
+-SelectToSpeakTest.SpeakStatusTray
+-SelectToSpeakTest.WorksWithStickyKeys
+-StickyKeysBrowserTest.CtrlClickHomeButton
+-StickyKeysBrowserTest.OpenNewTabs
+-StickyKeysBrowserTest.OpenTrayMenu
+-StickyKeysBrowserTest.OverlayShown
+-StickyKeysBrowserTest.SearchLeftOmnibox
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.ChromeVoxNextTabRecovery/0
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.ChromeVoxNextTabRecovery/1
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.ChromeVoxShiftSearch/0
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.ChromeVoxShiftSearch/1
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.FocusToolbar/0
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.FocusToolbar/1
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.OpenStatusTray/0
+-TestAsNormalAndGuestUser/SpokenFeedbackTest.OpenStatusTray/1
+
+# TabDragging: crbug.com/890071
+-DetachToBrowserInSeparateDisplayAndCancelTabDragControllerTest.CancelDragTabToWindowIn1stDisplay
+-TabDragging/DetachToBrowserInSeparateDisplayTabDragControllerTest.DragBrowserWindowWhenMajorityOfBoundsInSecondDisplay/0
+-TabDragging/DetachToBrowserInSeparateDisplayTabDragControllerTest.DragSingleTabToSeparateWindowInSecondDisplay/0
+-TabDragging/DetachToBrowserInSeparateDisplayTabDragControllerTest.DragTabToWindowInSeparateDisplay/0
+-TabDragging/DetachToBrowserInSeparateDisplayTabDragControllerTest.DragTabToWindowOnSecondDisplay/0
+-TabDragging/DetachToBrowserTabDragControllerTest.DeferredTargetTabStripTest/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DetachToOwnWindowFromMaximizedWindow/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DetachToOwnWindowWhileInImmersiveFullscreenMode/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DoNotAttachToOtherWindowTest/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DoNotObserveDraggedWidgetAfterDragEnds/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DragToOverviewNewWindowItem/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DragToOverviewWindow/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DragToSeparateWindow/1
+-TabDragging/DetachToBrowserTabDragControllerTest.DragWithMaskedWindows/0
+-TabDragging/DetachToBrowserTabDragControllerTest.DragWithMaskedWindows/1
+-TabDragging/DetachToBrowserTabDragControllerTestTouch.PressSecondFingerWhileDetached/0
+-TabDragging/DetachToBrowserTabDragControllerTestTouch.SecondFingerPressTest/0
+
+# TabScrubber: crbug.com/889097
+-TabScrubberTest.Bounds
+-TabScrubberTest.CloseBrowser
+-TabScrubberTest.DeleteBeforeHighlighted
+-TabScrubberTest.DeleteHighlighted
+-TabScrubberTest.FullScreenBrowser
+-TabScrubberTest.MoveAfter
+-TabScrubberTest.MoveBefore
+-TabScrubberTest.MoveHighlighted
+-TabScrubberTest.Multi
+-TabScrubberTest.MultiBrowser
+-TabScrubberTest.Repeated
+-TabScrubberTest.RTLMoveBefore
+-TabScrubberTest.RTLMulti
+-TabScrubberTest.RTLSkipped
+-TabScrubberTest.Single
+-TabScrubberTest.Skipped
diff --git a/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter b/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
index fb0d5f6..65dd820 100644
--- a/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
+++ b/testing/buildbot/filters/mojo.fyi.chromeos.network_browser_tests.filter
@@ -14,12 +14,6 @@
 -ArcSessionManagerTest.ManagedAndroidAccount
 -ArcSessionManagerTest.ManagedChromeAccount
 -ArcSessionManagerTest.WellKnownConsumerAccount
--BackgroundFetchBrowserTest.FetchesRunToCompletionAndUpdateTitle_Failed
--BackgroundFetchBrowserTest.FetchesRunToCompletionAndUpdateTitle_Fetched
--BackgroundFetchBrowserTest.FetchFromChildFrameWithPermissions
--BackgroundFetchBrowserTest.FetchFromServiceWorker
--BackgroundFetchBrowserTest.OfflineItemCollection_VerifyResourceDownloadedWhenDownloadTotalLargerThanActualSize
--BackgroundFetchBrowserTest.OfflineItemCollection_VerifyResourceDownloadedWhenDownloadTotalSmallerThanActualSize
 -CheckSystemTokenAvailability/EnterprisePlatformKeysTest.Basic/0
 -CheckSystemTokenAvailability/EnterprisePlatformKeysTest.Basic/1
 -CheckSystemTokenAvailability/EnterprisePlatformKeysTest.Basic/2
@@ -31,9 +25,6 @@
 -DeviceIDTest.Migration
 -DeviceIDTest.NewUsers
 -ExtensionWebRequestApiTest.WebRequestApiDoesNotCrashOnErrorAfterProfileDestroyed
--FileManagerPrivateApiTest.OnFileChanged
--FileManagerPrivateApiTest.Permissions
--FileManagerUITest.QuickView
 -ForceMaximizeOnFirstRunTest.TwoRuns
 -ForceMaximizePolicyFalseTest.GeneralFirstRun
 -HostedAppNonClientFrameViewAshTest.FocusableViews/material
@@ -134,11 +125,12 @@
 # https://crbug.com/882610
 -ChromeBrowserMainBrowserTest.VariationsServiceStartsRequestOnNetworkChange
 -NetworkConnectionTrackerBrowserTest.SimulateNetworkServiceCrash
-
-# Time out in StoragePartitionImpl::InitNetworkContext(). Might be related to
-# https://crbug.com/882610
--ArcAuthServiceChildAccountTest.ChildTransition
--FileManagerPrivateApiTest.OnFileChanged
+-BackgroundFetchBrowserTest.FetchesRunToCompletionAndUpdateTitle_Failed
+-BackgroundFetchBrowserTest.FetchesRunToCompletionAndUpdateTitle_Fetched
+-BackgroundFetchBrowserTest.FetchFromChildFrameWithPermissions
+-BackgroundFetchBrowserTest.FetchFromServiceWorker
+-BackgroundFetchBrowserTest.OfflineItemCollection_VerifyResourceDownloadedWhenDownloadTotalLargerThanActualSize
+-BackgroundFetchBrowserTest.OfflineItemCollection_VerifyResourceDownloadedWhenDownloadTotalSmallerThanActualSize
 
 # Relies on net::URLRequestInterceptor.
 # https://crbug.com/884782
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 2eb8976..e215548 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -1815,6 +1815,13 @@
           '--enable-features=Mash',
         ],
       },
+      'single_process_mash_interactive_ui_tests': {
+        'test': 'interactive_ui_tests',
+        'args': [
+          '--enable-features=SingleProcessMash',
+          '--test-launcher-filter-file=../../testing/buildbot/filters/chromeos.single_process_mash.interactive_ui_tests.filter',
+        ],
+      },
     },
 
     'memory_infra_isolated_scripts': {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 93b43a7..5c357eab 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3428,112 +3428,6 @@
   {
     'name': 'chromium.webrtc.fyi',
     'machines': {
-      'Android Builder': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'jingle_unittests',
-          'remoting_unittests',
-        ],
-      },
-      'Android Builder (dbg)': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'jingle_unittests',
-          'remoting_unittests',
-        ],
-      },
-      'Android Builder ARM64 (dbg)': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'jingle_unittests',
-          'remoting_unittests',
-        ],
-      },
-      'Linux Builder': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'frame_analyzer',
-          'jingle_unittests',
-          'remoting_unittests',
-          'remoting/webapp:webapp',
-        ],
-      },
-      'Linux Builder (dbg)': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'frame_analyzer',
-          'jingle_unittests',
-          'remoting_unittests',
-          'remoting/webapp:webapp',
-        ],
-      },
-      'Mac Builder': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'frame_analyzer',
-          'jingle_unittests',
-          'remoting_unittests',
-        ],
-      },
-      'Mac Builder (dbg)': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'frame_analyzer',
-          'jingle_unittests',
-          'remoting_unittests',
-          'remoting/webapp:webapp',
-        ],
-      },
-      'Win Builder': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'frame_analyzer',
-          'jingle_unittests',
-          'remoting_unittests',
-          'remoting/webapp:webapp',
-        ],
-      },
-      'Win Builder (dbg)': {
-        'additional_compile_targets': [
-          'browser_tests',
-          'capture_unittests',
-          'content_browsertests',
-          'content_unittests',
-          'frame_analyzer',
-          'jingle_unittests',
-          'remoting_unittests',
-          'remoting/webapp:webapp',
-        ],
-      },
-    },
-  },
-  {
-    'name': 'chromium.webrtc.fyi.experimental',
-    'machines': {
       # For builders, specify targets if the builder has no associated
       # tester (if it does, it will build what the tester needs).
       'WebRTC Chromium FYI Android Builder': {
diff --git a/testing/scripts/monochrome_apk_checker_wrapper.py b/testing/scripts/monochrome_apk_checker_wrapper.py
index 4a1e8e5..a05f8bd 100755
--- a/testing/scripts/monochrome_apk_checker_wrapper.py
+++ b/testing/scripts/monochrome_apk_checker_wrapper.py
@@ -50,7 +50,7 @@
       'actual': 'PASS' if success else 'FAIL',
     }
     if not success:
-      test['unexpected'] = True
+      test['is_unexpected'] = True
 
     json.dump({
       'version': 3,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 35757c1..ce496d42 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1797,6 +1797,23 @@
             ]
         }
     ],
+    "FCMInvalidationsExperiment": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "FCMInvalidationsEnabled",
+                    "enable_features": [
+                        "FCMInvalidations"
+                    ]
+                }
+            ]
+        }
+    ],
     "FrameTypePriorityExperiment": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
index 07df34b..50e48e24 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-blink-features=LayoutNG
@@ -339,8 +339,6 @@
 crbug.com/591099 external/wpt/xhr/send-entity-body-document.htm [ Pass ]
 crbug.com/591099 fast/backgrounds/quirks-mode-line-box-backgrounds.html [ Failure ]
 crbug.com/591099 fast/block/float-avoids-padding-inline-ancestors.html [ Crash Failure ]
-crbug.com/591099 fast/block/float/nopaint-after-layer-destruction.html [ Failure ]
-crbug.com/591099 fast/block/float/nopaint-after-layer-destruction2.html [ Failure ]
 crbug.com/591099 fast/block/float/overlapping-floats-paint-hittest-order-1.html [ Failure ]
 crbug.com/591099 fast/block/positioning/positioned-child-inside-relative-positioned-anonymous-block.html [ Failure ]
 crbug.com/591099 fast/borders/bidi-002.html [ Failure ]
@@ -353,7 +351,6 @@
 crbug.com/591099 fast/css/absolute-inline-alignment-2.html [ Pass ]
 crbug.com/591099 fast/css/case-transform.html [ Failure ]
 crbug.com/835484 fast/css/focus-ring-recursive-continuations.html [ Failure ]
-crbug.com/835484 fast/css/focus-ring-recursive-inlines.html [ Failure ]
 crbug.com/591099 fast/css/getComputedStyle/computed-style-percentage-top-with-position-inline.html [ Failure ]
 crbug.com/835484 fast/css/outline-auto-empty-rects.html [ Failure ]
 crbug.com/835484 fast/css/outline-narrowLine.html [ Failure ]
@@ -362,7 +359,7 @@
 crbug.com/591099 fast/events/wheel/mainthread-touchpad-fling-latching.html [ Pass ]
 crbug.com/591099 fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Pass ]
 crbug.com/591099 fast/forms/placeholder-position.html [ Failure ]
-crbug.com/591099 fast/forms/select/menulist-appearance-rtl.html [ Failure ]
+crbug.com/890135 fast/forms/select/menulist-appearance-rtl.html [ Failure ]
 crbug.com/591099 fast/frames/crash-frameset-CSS-content-property.html [ Crash Pass ]
 crbug.com/835484 fast/inline/continuation-outlines-with-layers.html [ Failure ]
 crbug.com/835484 fast/inline/continuation-outlines.html [ Failure ]
@@ -396,7 +393,7 @@
 crbug.com/591099 fast/writing-mode/fieldsets.html [ Failure ]
 crbug.com/591099 fast/writing-mode/percentage-height-orthogonal-writing-modes.html [ Failure ]
 crbug.com/591099 fast/writing-mode/table-percent-width-quirk.html [ Pass ]
-crbug.com/591099 fragmentation/transformed-clip-before-second-column.html [ Failure ]
+crbug.com/890135 fragmentation/transformed-clip-before-second-column.html [ Failure ]
 crbug.com/591099 hittesting/inline-with-clip-path.html [ Failure ]
 crbug.com/855039 html/details_summary/details-writing-mode-align-center.html [ Failure ]
 crbug.com/855039 html/details_summary/details-writing-mode-align-left.html [ Failure ]
@@ -418,17 +415,13 @@
 crbug.com/591099 http/tests/security/setDomainRelaxationForbiddenForURLScheme.html [ Crash ]
 crbug.com/591099 idle-callback/test-runner-run-idle-tasks.html [ Pass ]
 crbug.com/591099 images/color-profile-image-filter-all.html [ Failure ]
-crbug.com/591099 images/rendering-broken-block-flow-images.html [ Failure ]
-crbug.com/591099 images/rendering-broken-images.html [ Failure ]
+crbug.com/890135 images/rendering-broken-block-flow-images.html [ Failure ]
+crbug.com/890135 images/rendering-broken-images.html [ Failure ]
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-pseudo-element.js [ Failure ]
 crbug.com/591099 inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot.js [ Failure ]
 crbug.com/714962 inspector-protocol/layout-fonts/languages-emoji-rare-glyphs.js [ Failure ]
 crbug.com/591099 inspector-protocol/timeline/page-frames.js [ Failure ]
 crbug.com/591099 intersection-observer/v2/text-shadow.html [ Failure ]
-crbug.com/591099 media/audio-controls-rendering.html [ Failure ]
-crbug.com/591099 media/media-document-audio-repaint.html [ Failure ]
-crbug.com/591099 media/video-display-toggle.html [ Failure ]
-crbug.com/591099 media/video-layer-crash.html [ Failure ]
 crbug.com/591099 paint/float/float-under-inline-self-painting-change.html [ Failure ]
 crbug.com/835484 paint/inline/focus-ring-under-absolute-with-relative-continuation.html [ Failure ]
 crbug.com/591099 paint/invalidation/clip/control-clip.html [ Failure ]
@@ -492,20 +485,14 @@
 crbug.com/591099 tables/mozilla/bugs/bug2973.html [ Failure ]
 crbug.com/591099 tables/mozilla/bugs/bug50695-2.html [ Failure ]
 crbug.com/591099 tables/mozilla/bugs/bug55527.html [ Failure ]
-crbug.com/591099 transforms/3d/hit-testing/backface-no-transform-hit-test.html [ Failure ]
-crbug.com/591099 transforms/3d/point-mapping/3d-point-mapping-deep.html [ Failure ]
-crbug.com/591099 transforms/3d/point-mapping/3d-point-mapping-origins.html [ Failure ]
-crbug.com/591099 transforms/3d/point-mapping/3d-point-mapping-overlapping.html [ Failure ]
-crbug.com/591099 transforms/3d/point-mapping/3d-point-mapping-preserve-3d.html [ Failure ]
-crbug.com/591099 transforms/3d/point-mapping/3d-point-mapping.html [ Failure ]
 crbug.com/591099 transforms/svg-vs-css.xhtml [ Failure ]
 crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document-iframe.html [ Failure ]
 crbug.com/870008 virtual/android/rootscroller/position-fixed-in-unscrollable-document.html [ Failure ]
 crbug.com/591099 virtual/exotic-color-space/ [ Skip ]
 crbug.com/591099 virtual/feature-policy-vibrate/ [ Skip ]
 crbug.com/591099 virtual/gpu-rasterization/images/color-profile-image-filter-all.html [ Failure ]
-crbug.com/591099 virtual/gpu-rasterization/images/rendering-broken-block-flow-images.html [ Failure ]
-crbug.com/591099 virtual/gpu-rasterization/images/rendering-broken-images.html [ Failure ]
+crbug.com/890135 virtual/gpu-rasterization/images/rendering-broken-block-flow-images.html [ Failure ]
+crbug.com/890135 virtual/gpu-rasterization/images/rendering-broken-images.html [ Failure ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-arc-circumference.html [ Failure Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-color-over-image.html [ Pass ]
 crbug.com/591099 virtual/gpu/fast/canvas/canvas-blending-gradient-over-pattern.html [ Pass Timeout ]
@@ -514,7 +501,6 @@
 crbug.com/591099 virtual/layout_ng/ [ Skip ]
 crbug.com/824918 virtual/layout_ng_experimental/ [ Skip ]
 crbug.com/591099 virtual/mojo-blob-urls/external/wpt/FileAPI/url/sandboxed-iframe.html [ Pass ]
-crbug.com/591099 virtual/mouseevent_fractional/fast/events/popup-allowed-from-gesture-initiated-event.html [ Failure ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/wheel/mainthread-touchpad-fling-latching.html [ Pass ]
 crbug.com/591099 virtual/mouseevent_fractional/fast/events/wheel/wheel-scroll-latching-on-scrollbar.html [ Pass ]
@@ -545,11 +531,6 @@
 crbug.com/591099 virtual/stable/ [ Skip ]
 crbug.com/591099 virtual/threaded/ [ Skip ]
 crbug.com/591099 virtual/user-activation-v2/fast/events/mouse-cursor.html [ Failure ]
-crbug.com/591099 virtual/user-activation-v2/fast/events/popup-allowed-from-gesture-initiated-event.html [ Failure ]
 crbug.com/591099 virtual/user-activation-v2/fast/events/touch/compositor-touch-hit-rects.html [ Failure ]
-crbug.com/591099 virtual/video-surface-layer/media/audio-controls-rendering.html [ Failure ]
-crbug.com/591099 virtual/video-surface-layer/media/media-document-audio-repaint.html [ Failure ]
-crbug.com/591099 virtual/video-surface-layer/media/video-aspect-ratio.html [ Failure ]
-crbug.com/591099 virtual/video-surface-layer/media/video-display-toggle.html [ Failure ]
 crbug.com/591099 virtual/video-surface-layer/media/video-layer-crash.html [ Crash Failure ]
 crbug.com/591099 virtual/webrtc-wpt-unified-plan/external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Pass ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index 19dfed3..7fa152d 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -202,8 +202,6 @@
 
 crbug.com/528419 http/tests/devtools/elements/styles-2/pseudo-elements.js [ Slow ]
 
-crbug.com/529345 [ Win10 ] paint/masks/fieldset-mask.html [ Slow ]
-
 crbug.com/802029 fast/dom/shadow/focus-controller-recursion-crash.html [ Slow ]
 
 crbug.com/592183 external/wpt/webusb/usbDevice.https.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index e9641cb..f58124e 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1396,6 +1396,7 @@
 crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/multiline.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/negative-flex-rounding-assert.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/negative-overflow.html [ Skip ]
+crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/nested-flexbox-min-size-auto.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/nested-orthogonal-flexbox-relayout.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/nested-stretch.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/css3/flexbox/order-painting.html [ Failure ]
@@ -1515,7 +1516,6 @@
 crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-006.xht [ Skip ]
 crbug.com/249112 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-007.xht [ Skip ]
 crbug.com/467127 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-008.xht [ Skip ]
-crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-009.html [ Skip ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-002.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-flexbox/flex-minimum-width-flex-items-003.xht [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/css3/flexbox/nested-flexbox-min-size-auto.html b/third_party/WebKit/LayoutTests/css3/flexbox/nested-flexbox-min-size-auto.html
new file mode 100644
index 0000000..1c9297d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/flexbox/nested-flexbox-min-size-auto.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<link href="resources/flexbox.css" rel="stylesheet">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/check-layout-th.js"></script>
+<style>
+#container {
+  height: 300px;
+  outline: 2px solid black;
+}
+
+.inner
+{
+  width: 400px;
+  flex: 1;
+  background-color: green;
+}
+</style>
+<script>
+function change() {
+  var container = document.getElementById('container');
+  container.offsetHeight;
+  container.style.height = '80px';
+  checkLayout('#container');
+}
+</script>
+<body onload="change()">
+<p>Green rectangle should be entirely within the black rectangle</p>
+<div id="log"></div>
+<div id="container">
+  <div class="flexbox column" style="height: 100%;">
+    <div class="flexbox flex-one">
+        <div class="flexbox column">
+          <div class="flexbox column flex-one">
+            <div class="inner" data-expected-height="80">
+            </div>
+          </div>
+        </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-009.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-009.html
deleted file mode 100644
index 718386a..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-009.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-<title>Tests correct handling of min-height: auto with dynamic changes</title>
-<link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#min-size-auto" title="4.5. Implied Minimum Size of Flex Items" />
-<link rel="author" title="Google Inc." href="http://www.google.com/">
-<link href="support/flexbox.css" rel="stylesheet">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/check-layout-th.js"></script>
-<style>
-.container {
-  height: 300px;
-  outline: 2px solid black;
-}
-
-.inner
-{
-  width: 400px;
-  flex: 1;
-  background-color: green;
-}
-#container2 .flexbox > * { flex-basis: 0; }
-#container2 .column > * { flex-basis: auto; }
-</style>
-<script>
-function change() {
-  var container = document.getElementById('container');
-  container.offsetHeight;
-  container.style.height = '80px';
-  container = document.getElementById('container2');
-  container.offsetHeight;
-  container.style.height = '80px';
-  checkLayout('.container');
-}
-</script>
-<body onload="change()">
-<p>Green rectangle should be entirely within the black rectangle</p>
-<div id="log"></div>
-<div id="container" class="container">
-  <div class="flexbox column" style="height: 100%;">
-    <div class="flexbox flex-one">
-        <div class="flexbox column">
-          <div class="flexbox column flex-one">
-            <div class="inner" data-expected-height="80">
-            </div>
-          </div>
-        </div>
-    </div>
-  </div>
-</div>
-
-<div id="container2" class="container">
-  <div class="flexbox column" style="height: 100%;">
-    <div class="flexbox flex-one">
-        <div class="flexbox column">
-          <div class="flexbox column flex-one">
-            <div class="inner" data-expected-height="80">
-            </div>
-          </div>
-        </div>
-    </div>
-  </div>
-</div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid/alignment/grid-gutters-013.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid/alignment/grid-gutters-013.html
new file mode 100644
index 0000000..e7281472
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid/alignment/grid-gutters-013.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: Gutters adjacent to collapsed tracks also collapse</title>
+<link rel="help" href="https://www.w3.org/TR/css-grid-1/#gutters">
+<link rel="help" href="https://www.w3.org/TR/css-align-3/#gap-shorthand">
+<link rel="help" href="https://www.w3.org/TR/css-align-3/#gap-legacy">
+<link rel="help" href="https://www.w3.org/TR/css-grid-1/#repeat-notation">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<meta name="assert" content="This test checks that gutters adjacent to collapsed tracks don't reduce the space available for aligning adjacent grid items." />
+<style>
+    #grid {
+        display: grid;
+        margin-top: -50px;
+        margin-left: -50px;
+        width: 500px;
+        height: 500px;
+        grid-gap: 100px;
+        grid-template-rows: repeat(auto-fit, 200px);
+        grid-template-columns: repeat(auto-fit, 200px);
+        align-items: center;
+        justify-items: center;
+        background: linear-gradient(red, red) no-repeat 50px 50px / 100px 100px;
+    }
+
+    #grid > div {
+        background-color: green;
+        width: 50%;
+        height: 50%;
+    }
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div id="grid">
+    <div></div>
+</div>
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/defined-pseudo.html b/third_party/WebKit/LayoutTests/fast/css/invalidation/defined-pseudo.html
new file mode 100644
index 0000000..a01bcf30
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/defined-pseudo.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<style>
+#sibling, #child, #child3 { background-color: green }
+custom-one:not(:defined) #child,
+custom-two:not(:defined) + #sibling,
+custom-three:not(:defined) #child3 {
+  background-color: red
+}
+</style>
+
+<custom-one>
+    <div></div>
+    <div id="child"></div>
+</custom-one>
+
+<custom-two>
+    <div></div>
+    <div></div>
+</custom-two>
+<div id="sibling"></div>
+
+<custom-three>
+    <div></div>
+    <div id="child3"></div>
+</custom-three>
+
+<script>
+const RED = "rgb(255, 0, 0)";
+const GREEN = "rgb(0, 128, 0)";
+
+test(() => {
+  // Initially :not(:defined).
+  assert_equals(getComputedStyle(child).backgroundColor, RED);
+  assert_equals(getComputedStyle(sibling).backgroundColor, RED);
+  assert_equals(getComputedStyle(child3).backgroundColor, RED);
+
+  document.body.offsetTop; // force recalc
+
+  customElements.define("custom-one", class extends HTMLElement {});
+  if (window.internals)
+    assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 1);
+  assert_equals(getComputedStyle(child).backgroundColor, GREEN);
+
+  document.body.offsetTop; // force recalc
+
+  customElements.define("custom-two", class extends HTMLElement {});
+  if (window.internals)
+    assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 1);
+  assert_equals(getComputedStyle(sibling).backgroundColor, GREEN);
+
+  document.body.offsetTop; // force recalc
+
+  document.registerElement("custom-three", { prototype: Object.create(HTMLElement.prototype) });
+  if (window.internals)
+    assert_equals(internals.updateStyleAndReturnAffectedElementCount(), 1);
+  assert_equals(getComputedStyle(child3).backgroundColor, GREEN);
+
+}, "Use invalidation sets for :defined pseudo class.")
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo-expected.txt b/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo-expected.txt
index 10538499..3f70e82 100644
--- a/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo-expected.txt
@@ -1,4 +1,5 @@
-CONSOLE WARNING: line 31: document.registerElement is deprecated and will be removed in M73, around March 2019. Please use window.customElements.define instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
+CONSOLE WARNING: :unresolved pseudo selector is deprecated and will be removed in M73, around March 2019. Please use :not(:defined) instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
+CONSOLE WARNING: line 41: document.registerElement is deprecated and will be removed in M73, around March 2019. Please use window.customElements.define instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
 Use invalidation sets for :unresolved pseudo class.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -6,10 +7,13 @@
 
 PASS getComputedStyle(child).backgroundColor is red
 PASS getComputedStyle(sibling).backgroundColor is red
+PASS getComputedStyle(child3).backgroundColor is red
 PASS internals.updateStyleAndReturnAffectedElementCount() is 1
 PASS getComputedStyle(child).backgroundColor is green
 PASS internals.updateStyleAndReturnAffectedElementCount() is 1
 PASS getComputedStyle(sibling).backgroundColor is green
+PASS internals.updateStyleAndReturnAffectedElementCount() is 1
+PASS getComputedStyle(child3).backgroundColor is green
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo.html b/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo.html
index 3fb26f92..6dbf472 100644
--- a/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo.html
+++ b/third_party/WebKit/LayoutTests/fast/css/invalidation/unresolved-pseudo.html
@@ -1,8 +1,12 @@
 <!DOCTYPE html>
 <script src="../../../resources/js-test.js"></script>
 <style>
-#sibling, #child { background-color: green }
-custom-one:unresolved #child, custom-two:unresolved + #sibling { background-color: red }
+#sibling, #child, #child3 { background-color: green }
+custom-one:unresolved #child,
+custom-two:unresolved + #sibling,
+custom-three:unresolved #child3 {
+  background-color: red
+}
 </style>
 
 <custom-one>
@@ -16,6 +20,11 @@
 </custom-two>
 <div id="sibling"></div>
 
+<custom-three>
+    <div></div>
+    <div id="child3"></div>
+</custom-three>
+
 <script>
 description("Use invalidation sets for :unresolved pseudo class.")
 
@@ -25,6 +34,7 @@
 // Initially :unresolved.
 shouldBe("getComputedStyle(child).backgroundColor", "red");
 shouldBe("getComputedStyle(sibling).backgroundColor", "red");
+shouldBe("getComputedStyle(child3).backgroundColor", "red");
 
 document.body.offsetTop; // force recalc
 
@@ -44,4 +54,11 @@
     shouldBe("internals.updateStyleAndReturnAffectedElementCount()", "1");
 shouldBe("getComputedStyle(sibling).backgroundColor", "green");
 
+document.body.offsetTop; // force recalc
+
+customElements.define("custom-three", class extends HTMLElement {});
+if (window.internals)
+    shouldBe("internals.updateStyleAndReturnAffectedElementCount()", "1");
+shouldBe("getComputedStyle(child3).backgroundColor", "green");
+
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/dom/custom/element-upgrade-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/custom/element-upgrade-expected.txt
index f98ba21f..7497a6e 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/custom/element-upgrade-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/custom/element-upgrade-expected.txt
@@ -1,4 +1,5 @@
 CONSOLE WARNING: line 17: document.registerElement is deprecated and will be removed in M73, around March 2019. Please use window.customElements.define instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
+CONSOLE WARNING: line 1: :unresolved pseudo selector is deprecated and will be removed in M73, around March 2019. Please use :not(:defined) instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
 Tests the element upgrade algorithm.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/fast/dom/custom/unresolved-pseudoclass-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/custom/unresolved-pseudoclass-expected.txt
index 600a1983..d0fa0fe 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/custom/unresolved-pseudoclass-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/custom/unresolved-pseudoclass-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: :unresolved pseudo selector is deprecated and will be removed in M73, around March 2019. Please use :not(:defined) instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
 CONSOLE WARNING: line 34: document.registerElement is deprecated and will be removed in M73, around March 2019. Please use window.customElements.define instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
 Tests the :unresolved pseudoclass.
 
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/nopaint-after-layer-destruction2-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/nopaint-after-layer-destruction2-expected.png
new file mode 100644
index 0000000..2368ff77
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/nopaint-after-layer-destruction2-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/nopaint-after-layer-destruction2-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/nopaint-after-layer-destruction2-expected.txt
new file mode 100644
index 0000000..7272bec
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/fast/block/float/nopaint-after-layer-destruction2-expected.txt
@@ -0,0 +1,35 @@
+layer at (0,0) size 800x600
+  LayoutView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  LayoutNGBlockFlow {HTML} at (0,0) size 800x600
+    LayoutNGBlockFlow {BODY} at (8,8) size 784x576
+      LayoutNGBlockFlow {P} at (0,0) size 784x40
+        LayoutText {#text} at (0,0) size 139x19
+          text run at (0,0) width 139: "This is a pixel test for "
+        LayoutInline {I} at (0,0) size 762x39
+          LayoutInline {A} at (0,0) size 347x19 [color=#0000EE]
+            LayoutText {#text} at (139,0) size 347x19
+              text run at (139,0) width 347: "http://bugzilla.opendarwin.org/show_bug.cgi?id=4334"
+          LayoutText {#text} at (486,0) size 762x39
+            text run at (486,0) width 276: " REGRESSION: Flickering when css-hover"
+            text run at (0,20) width 273: "should change opacity on floating elements"
+        LayoutText {#text} at (273,20) size 4x19
+          text run at (273,20) width 4: "."
+      LayoutNGBlockFlow {P} at (0,56) size 784x20
+        LayoutText {#text} at (0,0) size 491x19
+          text run at (0,0) width 491: "There should be a solid green square below mixed in with a paragraph of text."
+      LayoutNGBlockFlow {DIV} at (0,112.72) size 784x141.72
+        LayoutNGBlockFlow {DIV} at (0,0) size 784x141.72
+          LayoutNGBlockFlow (floating) {DIV} at (3,3) size 75x75 [bgcolor=#008000]
+          LayoutNGBlockFlow {H3} at (0,0) size 784x23
+            LayoutText {#text} at (81,0) size 163x22
+              text run at (81,0) width 163: "Stanley Park Forest"
+          LayoutNGBlockFlow {P} at (0,41.72) size 784x100
+            LayoutText {#text} at (81,0) size 784x99
+              text run at (81,0) width 703: "The forest is primarily second and third growth. The area was saved from development because of its status as a"
+              text run at (81,20) width 664: "federal military reserve; it occupied a strategic location for defending the former provincial capital of New"
+              text run at (0,40) width 760: "Westminster in the case of an American naval invasion. Nevertheless, the federal government allowed logging operations"
+              text run at (0,60) width 763: "there in the mid-nineteenth century. Large swathes of the park were also deforested by natural causes on two occasions in"
+              text run at (0,80) width 105: "the 20th century."
+layer at (8,100) size 784x2 clip at (0,0) size 0x0
+  LayoutNGBlockFlow {HR} at (0,92) size 784x2 [border: (1px inset #EEEEEE)]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/audio-controls-rendering-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/audio-controls-rendering-expected.txt
index 610087a..d480650 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/audio-controls-rendering-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/audio-controls-rendering-expected.txt
@@ -44,6 +44,18 @@
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
 layer at (139,69) size 111x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 111x4 [bgcolor=#0000008A]
+layer at (266,47) size 0x48 transparent
+  LayoutSlider {INPUT} at (258,3) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (266,69) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+layer at (266,69) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (266,69) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+layer at (266,69) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
 layer at (8,118) size 320x54
   LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x54
 layer at (8,118) size 320x54
@@ -72,6 +84,18 @@
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
 layer at (139,143) size 131x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 131x4 [bgcolor=#0000008A]
+layer at (286,121) size 0x48 transparent
+  LayoutSlider {INPUT} at (278,3) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (286,143) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+layer at (286,143) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (286,143) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+layer at (286,143) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
 layer at (8,192) size 320x100
   LayoutMedia (positioned) {AUDIO} at (8,192) size 320x100 [bgcolor=#0000FF]
 layer at (8,192) size 320x100
@@ -102,3 +126,15 @@
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
 layer at (139,263) size 131x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 131x4 [bgcolor=#0000008A]
+layer at (286,241) size 0x48 transparent
+  LayoutSlider {INPUT} at (278,3) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (286,263) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+layer at (286,263) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (286,263) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+layer at (286,263) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/media-document-audio-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/media-document-audio-repaint-expected.txt
index c48d0b8..f014079 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/media-document-audio-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/media-document-audio-repaint-expected.txt
@@ -46,3 +46,15 @@
       LayoutBlockFlow (positioned) {DIV} at (0,0) size 55x4 [bgcolor=#000000DE]
     layer at (226,211) size 55x4
       LayoutBlockFlow (positioned) {DIV} at (55,0) size 55x4 [bgcolor=#0000008A]
+    layer at (298,189) size 0x48 transparent
+      LayoutSlider {INPUT} at (258,3) size 0x48 [color=#9D968E]
+        LayoutFlexibleBox {DIV} at (0,22) size 0x4
+    layer at (298,211) size 12x4
+      LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+        LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+    layer at (298,211) size 12x4
+      LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+    layer at (298,211) size 0x4
+      LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+    layer at (298,211) size 12x4
+      LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-display-toggle-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-display-toggle-expected.txt
index a0e0e37..85686e2 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-display-toggle-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-display-toggle-expected.txt
@@ -16,31 +16,43 @@
   LayoutNGBlockFlow (positioned) {DIV} at (0,0) size 320x240
 layer at (8,28) size 320x240
   LayoutFlexibleBox {DIV} at (0,0) size 320x240
-layer at (8,28) size 320x224 clip at (10,30) size 316x220
-  LayoutButton (relative positioned) {INPUT} at (0,0) size 320x224 [border: (2px outset #C0C0C0)]
-    LayoutFlexibleBox (anonymous) at (8,82) size 304x60
-      LayoutBlockFlow {DIV} at (122,0) size 60x60 [bgcolor=#FFFFFFE6]
-layer at (8,204) size 320x48
-  LayoutFlexibleBox (relative positioned) {DIV} at (0,176) size 320x48
-    LayoutNGBlockFlow {DIV} at (16,0) size 31x48 [color=#FFFFFF]
-      LayoutText {#text} at (0,15) size 31x18
-        text run at (0,15) width 31: "0:00"
-    LayoutNGBlockFlow {DIV} at (51,0) size 39x48 [color=#FFFFFF]
-      LayoutText {#text} at (0,15) size 39x18
-        text run at (0,15) width 39: "/ 0:06"
-    LayoutNGBlockFlow {DIV} at (90,48) size 134x0
+layer at (118,86) size 100x100
+  LayoutButton (positioned) {INPUT} at (110,58) size 100x100
+    LayoutBlockFlow (anonymous) at (20,20) size 60x60
+      LayoutBlockFlow {DIV} at (0,0) size 60x60 [bgcolor=#FFFFFFE6]
+layer at (8,196) size 320x48
+  LayoutFlexibleBox (relative positioned) {DIV} at (0,168) size 320x48
+    LayoutNGBlockFlow {DIV} at (16,4) size 28x44 [color=#FFFFFF]
+      LayoutText {#text} at (0,14) size 28x16
+        text run at (0,14) width 28: "0:00"
+    LayoutNGBlockFlow {DIV} at (48,4) size 36x44 [color=#FFFFFF]
+      LayoutText {#text} at (0,14) size 36x16
+        text run at (0,14) width 36: "/ 0:06"
+    LayoutNGBlockFlow {DIV} at (84,48) size 140x0
     LayoutButton {INPUT} at (224,0) size 48x48
     LayoutButton {INPUT} at (272,0) size 48x48
-layer at (8,252) size 320x16
-  LayoutSlider {INPUT} at (0,224) size 320x16 [color=#9D968E]
-    LayoutFlexibleBox {DIV} at (16,0) size 288x4
-layer at (24,252) size 288x4
-  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 288x4 [bgcolor=#FFFFFF4D]
-layer at (24,248) size 12x12
-  LayoutBlockFlow (relative positioned) {DIV} at (0,-4) size 12x12 [bgcolor=#FFFFFF]
-layer at (24,252) size 288x4
-  LayoutBlockFlow (positioned) {DIV} at (0,0) size 288x4
-layer at (24,252) size 0x4
+layer at (232,196) size 0x48 transparent
+  LayoutSlider {INPUT} at (224,0) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (232,218) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#FFFFFF4D]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#FFFFFF]
+layer at (232,218) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (232,218) size 0x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#FFFFFF]
-layer at (24,252) size 288x4
+layer at (232,218) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#FFFFFF8A]
+layer at (8,244) size 320x24
+  LayoutSlider {INPUT} at (0,216) size 320x24 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (16,0) size 288x4
+layer at (24,244) size 288x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 288x4 [bgcolor=#FFFFFF4D]
+layer at (24,240) size 12x12
+  LayoutBlockFlow (relative positioned) {DIV} at (0,-4) size 12x12 [bgcolor=#FFFFFF]
+layer at (24,244) size 288x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 288x4
+layer at (24,244) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#FFFFFF]
+layer at (24,244) size 288x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 288x4 [bgcolor=#FFFFFF8A]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-layer-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-layer-crash-expected.txt
index c8e1f04a..20665a7 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-layer-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/media/video-layer-crash-expected.txt
@@ -18,15 +18,15 @@
         LayoutBR {BR} at (210,322) size 0x19
 layer at (12,64) size 206x156
   LayoutVideo {VIDEO} at (4,20) size 206x156 [border: (3px solid #FF0000)]
-layer at (15,67) size 200x150 backgroundClip at (65,105) size 100x75 clip at (65,105) size 100x75
+layer at (65,105) size 100x75
   LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150
     LayoutFlexibleBox {DIV} at (0,0) size 200x150
-layer at (15,67) size 200x150 backgroundClip at (65,105) size 100x75 clip at (65,105) size 100x75
+layer at (65,105) size 100x75
   LayoutNGBlockFlow (positioned) {DIV} at (0,0) size 200x150
 layer at (12,225) size 206x156
   LayoutVideo {VIDEO} at (4,181) size 206x156 [border: (3px solid #FF0000)]
-layer at (15,228) size 200x150
+layer at (-12,228) size 254x150 backgroundClip at (0,228) size 242x150 clip at (0,228) size 242x150
   LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150
     LayoutFlexibleBox {DIV} at (0,0) size 200x150
-layer at (15,228) size 200x150
+layer at (-12,228) size 254x150 backgroundClip at (0,228) size 242x150 clip at (0,228) size 242x150
   LayoutNGBlockFlow (positioned) {DIV} at (0,0) size 200x150
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/hit-testing/backface-no-transform-hit-test-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/hit-testing/backface-no-transform-hit-test-expected.txt
index 8178bf2..698f6001 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/hit-testing/backface-no-transform-hit-test-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/hit-testing/backface-no-transform-hit-test-expected.txt
@@ -18,11 +18,11 @@
     LayoutText {#text} at (197,32) size 4x19
       text run at (197,32) width 4: " "
     LayoutText {#text} at (0,0) size 0x0
-layer at (27,63) size 160x160
+layer at (231,63) size 160x160
   LayoutNGBlockFlow (relative positioned) {DIV} at (19,19) size 160x160 [bgcolor=#808080]
     LayoutText {#text} at (48,0) size 64x36
       text run at (48,0) width 64: "box1"
-layer at (227,63) size 160x160
+layer at (31,63) size 160x160
   LayoutNGBlockFlow (relative positioned) {DIV} at (219,19) size 160x160 [bgcolor=#808080]
     LayoutText {#text} at (48,0) size 64x36
       text run at (48,0) width 64: "box2"
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-deep-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-deep-expected.txt
deleted file mode 100644
index 324feb6..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-deep-expected.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 810
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 785x600
-  LayoutNGBlockFlow {HTML} at (0,0) size 785x600
-    LayoutNGBlockFlow {BODY} at (0,0) size 785x600 [border: (1px solid #000000)]
-      LayoutNGBlockFlow {DIV} at (21,21) size 402x402 [border: (1px solid #000000)]
-      LayoutText {#text} at (0,0) size 0x0
-layer at (42,42) size 340x340
-  LayoutNGBlockFlow {DIV} at (21,21) size 340x340 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (63,63) size 300x300
-  LayoutNGBlockFlow {DIV} at (21,21) size 300x300 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-layer at (104,104) size 300x300
-  LayoutNGBlockFlow {DIV} at (41,41) size 300x300 [border: (1px solid #000000)]
-layer at (145,145) size 300x300
-  LayoutNGBlockFlow {DIV} at (41,41) size 300x300 [bgcolor=#AA7994] [border: (1px solid #000000)]
-layer at (186,186) size 300x300
-  LayoutNGBlockFlow {DIV} at (41,41) size 300x300 [border: (1px solid #000000)]
-layer at (227,227) size 300x300
-  LayoutNGBlockFlow {DIV} at (41,41) size 300x300 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-layer at (268,268) size 300x300
-  LayoutNGBlockFlow {DIV} at (41,41) size 300x300 [border: (1px solid #000000)]
-    LayoutNGBlockFlow {DIV} at (61,61) size 90x90 [bgcolor=#0000FF]
-layer at (30,650) size 345x160 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
-  LayoutNGBlockFlow (positioned) {DIV} at (30,650) size 345x160
-    LayoutInline {SPAN} at (0,0) size 297x19 [color=#008000]
-      LayoutText {#text} at (0,0) size 297x19
-        text run at (0,0) width 297: "PASS: event at (45, 45) hit box1 at offset (2, 2)"
-    LayoutBR {BR} at (297,15) size 0x0
-    LayoutInline {SPAN} at (0,0) size 297x19 [color=#008000]
-      LayoutText {#text} at (0,20) size 297x19
-        text run at (0,20) width 297: "PASS: event at (54, 44) hit box2 at offset (1, 1)"
-    LayoutBR {BR} at (297,35) size 0x0
-    LayoutInline {SPAN} at (0,0) size 305x19 [color=#008000]
-      LayoutText {#text} at (0,40) size 305x19
-        text run at (0,40) width 305: "PASS: event at (104, 93) hit box3 at offset (1, 1)"
-    LayoutBR {BR} at (305,55) size 0x0
-    LayoutInline {SPAN} at (0,0) size 313x19 [color=#008000]
-      LayoutText {#text} at (0,60) size 313x19
-        text run at (0,60) width 313: "PASS: event at (175, 137) hit box4 at offset (1, 1)"
-    LayoutBR {BR} at (313,75) size 0x0
-    LayoutInline {SPAN} at (0,0) size 329x19 [color=#008000]
-      LayoutText {#text} at (0,80) size 329x19
-        text run at (0,80) width 329: "PASS: event at (167, 528) hit box4 at offset (1, 295)"
-    LayoutBR {BR} at (329,95) size 0x0
-    LayoutInline {SPAN} at (0,0) size 313x19 [color=#008000]
-      LayoutText {#text} at (0,100) size 313x19
-        text run at (0,100) width 313: "PASS: event at (227, 197) hit box5 at offset (1, 1)"
-    LayoutBR {BR} at (313,115) size 0x0
-    LayoutInline {SPAN} at (0,0) size 345x19 [color=#008000]
-      LayoutText {#text} at (0,120) size 345x19
-        text run at (0,120) width 345: "PASS: event at (539, 569) hit box7 at offset (295, 295)"
-    LayoutBR {BR} at (345,135) size 0x0
-    LayoutInline {SPAN} at (0,0) size 329x19 [color=#008000]
-      LayoutText {#text} at (0,140) size 329x19
-        text run at (0,140) width 329: "PASS: event at (431, 441) hit box8 at offset (85, 85)"
-    LayoutBR {BR} at (329,155) size 0x0
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-expected.txt
deleted file mode 100644
index 156fa48..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-expected.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 680
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 785x600
-  LayoutNGBlockFlow {HTML} at (0,0) size 785x600
-    LayoutNGBlockFlow {BODY} at (0,0) size 785x600 [border: (1px solid #000000)]
-layer at (30,500) size 337x180 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
-  LayoutNGBlockFlow (positioned) {DIV} at (30,500) size 337x180
-    LayoutInline {SPAN} at (0,0) size 297x19 [color=#008000]
-      LayoutText {#text} at (0,0) size 297x19
-        text run at (0,0) width 297: "PASS: event at (44, 44) hit box1 at offset (1, 1)"
-    LayoutBR {BR} at (297,15) size 0x0
-    LayoutInline {SPAN} at (0,0) size 297x19 [color=#008000]
-      LayoutText {#text} at (0,20) size 297x19
-        text run at (0,20) width 297: "PASS: event at (69, 55) hit box2 at offset (1, 1)"
-    LayoutBR {BR} at (297,35) size 0x0
-    LayoutInline {SPAN} at (0,0) size 329x19 [color=#008000]
-      LayoutText {#text} at (0,40) size 329x19
-        text run at (0,40) width 329: "PASS: event at (165, 182) hit box2 at offset (95, 95)"
-    LayoutBR {BR} at (329,55) size 0x0
-    LayoutInline {SPAN} at (0,0) size 305x19 [color=#008000]
-      LayoutText {#text} at (0,60) size 305x19
-        text run at (0,60) width 305: "PASS: event at (333, 79) hit box7 at offset (1, 1)"
-    LayoutBR {BR} at (305,75) size 0x0
-    LayoutInline {SPAN} at (0,0) size 313x19 [color=#008000]
-      LayoutText {#text} at (0,80) size 313x19
-        text run at (0,80) width 313: "PASS: event at (87, 325) hit box10 at offset (1, 1)"
-    LayoutBR {BR} at (313,95) size 0x0
-    LayoutInline {SPAN} at (0,0) size 337x19 [color=#008000]
-      LayoutText {#text} at (0,100) size 337x19
-        text run at (0,100) width 337: "PASS: event at (196, 467) hit box10 at offset (97, 97)"
-    LayoutBR {BR} at (337,115) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,120) size 321x19
-        text run at (0,120) width 321: "PASS: event at (333, 325) hit box13 at offset (1, 1)"
-    LayoutBR {BR} at (321,135) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,140) size 321x19
-        text run at (0,140) width 321: "PASS: event at (353, 352) hit box14 at offset (1, 1)"
-    LayoutBR {BR} at (321,155) size 0x0
-    LayoutInline {SPAN} at (0,0) size 337x19 [color=#008000]
-      LayoutText {#text} at (0,160) size 337x19
-        text run at (0,160) width 337: "PASS: event at (472, 507) hit box14 at offset (96, 96)"
-    LayoutBR {BR} at (337,175) size 0x0
-layer at (21,21) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (21,21) size 202x202 [border: (1px solid #000000)]
-layer at (42,42) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (63,63) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#AAAAAA] [border: (1px solid #000000)]
-layer at (267,21) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (267,21) size 202x202 [border: (1px solid #000000)]
-layer at (288,42) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (309,63) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#AAAAAA] [border: (1px solid #000000)]
-    LayoutNGBlockFlow {DIV} at (21,21) size 100x100 [bgcolor=#0000FF] [border: (1px solid #000000)]
-layer at (21,267) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (21,267) size 202x202 [border: (1px solid #000000)]
-layer at (42,288) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (63,309) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#AAAAAA] [border: (1px solid #000000)]
-layer at (84,330) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#0000FF] [border: (1px solid #000000)]
-layer at (267,267) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (267,267) size 202x202 [border: (1px solid #000000)]
-layer at (288,288) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (309,309) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#AAAAAA] [border: (1px solid #000000)]
-    LayoutNGBlockFlow {DIV} at (21,21) size 100x100 [bgcolor=#C0D69E] [border: (1px solid #000000)]
-layer at (351,351) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#0000FF] [border: (1px solid #000000)]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-origins-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-origins-expected.txt
index ede809ef..568deda 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-origins-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-origins-expected.txt
@@ -65,12 +65,12 @@
   LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
 layer at (63,310) size 100x100
   LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#AAAAAA] [border: (1px solid #000000)]
-layer at (84,331) size 100x100
+layer at (88,282) size 136x170
   LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#0000FF] [border: (1px solid #000000)]
 layer at (288,289) size 140x140
   LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
 layer at (309,310) size 100x100
   LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#AAAAAA] [border: (1px solid #000000)]
     LayoutNGBlockFlow {DIV} at (21,21) size 100x100 [bgcolor=#C0D69E] [border: (1px solid #000000)]
-layer at (351,352) size 100x100
+layer at (358,313) size 150x180
   LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#0000FF] [border: (1px solid #000000)]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-overlapping-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-overlapping-expected.txt
deleted file mode 100644
index 0abc557a..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-overlapping-expected.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutNGBlockFlow {HTML} at (0,0) size 800x600
-    LayoutNGBlockFlow {BODY} at (0,0) size 800x600 [border: (1px solid #000000)]
-      LayoutNGBlockFlow (anonymous) at (1,1) size 798x342
-        LayoutText {#text} at (0,0) size 0x0
-      LayoutNGBlockFlow {P} at (1,359) size 798x20
-        LayoutText {#text} at (0,0) size 602x19
-          text run at (0,0) width 602: "The green overlay is translated in Z by 100px, so should hit test in front relative to the blue box."
-layer at (30,400) size 343x80
-  LayoutNGBlockFlow (positioned) {DIV} at (30,400) size 343x80
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,0) size 321x19
-        text run at (0,0) width 321: "PASS: event at (285, 50) hit box2 at offset (197, 1)"
-    LayoutBR {BR} at (321,15) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,20) size 321x19
-        text run at (0,20) width 321: "PASS: event at (174, 108) hit box3 at offset (50, 2)"
-    LayoutBR {BR} at (321,35) size 0x0
-    LayoutInline {SPAN} at (0,0) size 328x19 [color=#008000]
-      LayoutText {#text} at (0,40) size 328x19
-        text run at (0,40) width 328: "PASS: event at (61, 50) hit overlay at offset (39, 28)"
-    LayoutBR {BR} at (328,55) size 0x0
-    LayoutInline {SPAN} at (0,0) size 343x19 [color=#008000]
-      LayoutText {#text} at (0,60) size 343x19
-        text run at (0,60) width 343: "PASS: event at (119, 108) hit overlay at offset (97, 86)"
-    LayoutBR {BR} at (343,75) size 0x0
-layer at (21,21) size 302x302
-  LayoutNGBlockFlow (relative positioned) {DIV} at (20,20) size 302x302 [border: (1px solid #000000)]
-layer at (42,42) size 260x260
-  LayoutNGBlockFlow (positioned) {DIV} at (21,21) size 260x260 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (73,63) size 200x200
-  LayoutNGBlockFlow (positioned) {DIV} at (31,21) size 200x200 [bgcolor=#AAAAAA] [border: (1px solid #000000)]
-layer at (124,114) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (51,51) size 100x100 [bgcolor=#0000FF]
-layer at (22,22) size 150x300
-  LayoutNGBlockFlow (positioned) {DIV} at (1,1) size 150x300 [bgcolor=#0080004D]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.txt
deleted file mode 100644
index d0e61d0..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/3d/point-mapping/3d-point-mapping-preserve-3d-expected.txt
+++ /dev/null
@@ -1,124 +0,0 @@
-layer at (0,0) size 800x600 clip at (0,0) size 785x600 scrollHeight 840
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 785x600
-  LayoutNGBlockFlow {HTML} at (0,0) size 785x600
-    LayoutNGBlockFlow {BODY} at (0,0) size 785x600 [border: (1px solid #000000)]
-layer at (30,500) size 337x340 backgroundClip at (0,0) size 785x600 clip at (0,0) size 785x600
-  LayoutNGBlockFlow (positioned) {DIV} at (30,500) size 337x340
-    LayoutInline {SPAN} at (0,0) size 297x19 [color=#008000]
-      LayoutText {#text} at (0,0) size 297x19
-        text run at (0,0) width 297: "PASS: event at (44, 44) hit box1 at offset (1, 1)"
-    LayoutBR {BR} at (297,15) size 0x0
-    LayoutInline {SPAN} at (0,0) size 297x19 [color=#008000]
-      LayoutText {#text} at (0,20) size 297x19
-        text run at (0,20) width 297: "PASS: event at (74, 68) hit box2 at offset (1, 1)"
-    LayoutBR {BR} at (297,35) size 0x0
-    LayoutInline {SPAN} at (0,0) size 329x19 [color=#008000]
-      LayoutText {#text} at (0,40) size 329x19
-        text run at (0,40) width 329: "PASS: event at (157, 164) hit box2 at offset (97, 97)"
-    LayoutBR {BR} at (329,55) size 0x0
-    LayoutInline {SPAN} at (0,0) size 305x19 [color=#008000]
-      LayoutText {#text} at (0,60) size 305x19
-        text run at (0,60) width 305: "PASS: event at (320, 68) hit box4 at offset (1, 1)"
-    LayoutBR {BR} at (305,75) size 0x0
-    LayoutInline {SPAN} at (0,0) size 305x19 [color=#008000]
-      LayoutText {#text} at (0,80) size 305x19
-        text run at (0,80) width 305: "PASS: event at (336, 87) hit box5 at offset (1, 1)"
-    LayoutBR {BR} at (305,95) size 0x0
-    LayoutInline {SPAN} at (0,0) size 305x19 [color=#008000]
-      LayoutText {#text} at (0,100) size 305x19
-        text run at (0,100) width 305: "PASS: event at (582, 87) hit box8 at offset (1, 1)"
-    LayoutBR {BR} at (305,115) size 0x0
-    LayoutInline {SPAN} at (0,0) size 329x19 [color=#008000]
-      LayoutText {#text} at (0,120) size 329x19
-        text run at (0,120) width 329: "PASS: event at (658, 174) hit box8 at offset (85, 85)"
-    LayoutBR {BR} at (329,135) size 0x0
-    LayoutInline {SPAN} at (0,0) size 313x19 [color=#008000]
-      LayoutText {#text} at (0,140) size 313x19
-        text run at (0,140) width 313: "PASS: event at (74, 314) hit box10 at offset (1, 1)"
-    LayoutBR {BR} at (313,155) size 0x0
-    LayoutInline {SPAN} at (0,0) size 312x19 [color=#008000]
-      LayoutText {#text} at (0,160) size 312x19
-        text run at (0,160) width 312: "PASS: event at (91, 351) hit box11 at offset (1, 1)"
-    LayoutBR {BR} at (312,175) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,180) size 321x19
-        text run at (0,180) width 321: "PASS: event at (320, 314) hit box13 at offset (1, 1)"
-    LayoutBR {BR} at (321,195) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,200) size 321x19
-        text run at (0,200) width 321: "PASS: event at (343, 351) hit box14 at offset (1, 1)"
-    LayoutBR {BR} at (321,215) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,220) size 321x19
-        text run at (0,220) width 321: "PASS: event at (365, 375) hit box15 at offset (1, 1)"
-    LayoutBR {BR} at (321,235) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,240) size 321x19
-        text run at (0,240) width 321: "PASS: event at (566, 314) hit box17 at offset (1, 1)"
-    LayoutBR {BR} at (321,255) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,260) size 321x19
-        text run at (0,260) width 321: "PASS: event at (587, 352) hit box18 at offset (1, 1)"
-    LayoutBR {BR} at (321,275) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,280) size 321x19
-        text run at (0,280) width 321: "PASS: event at (629, 401) hit box19 at offset (1, 1)"
-    LayoutBR {BR} at (321,295) size 0x0
-    LayoutInline {SPAN} at (0,0) size 321x19 [color=#008000]
-      LayoutText {#text} at (0,300) size 321x19
-        text run at (0,300) width 321: "PASS: event at (653, 422) hit box20 at offset (1, 1)"
-    LayoutBR {BR} at (321,315) size 0x0
-    LayoutInline {SPAN} at (0,0) size 337x19 [color=#008000]
-      LayoutText {#text} at (0,320) size 337x19
-        text run at (0,320) width 337: "PASS: event at (745, 505) hit box20 at offset (85, 86)"
-    LayoutBR {BR} at (337,335) size 0x0
-layer at (21,21) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (21,21) size 202x202 [border: (1px solid #000000)]
-layer at (42,42) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (63,63) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-layer at (267,21) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (267,21) size 202x202 [border: (1px solid #000000)]
-layer at (288,42) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (309,63) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-    LayoutNGBlockFlow {DIV} at (21,21) size 90x90 [bgcolor=#0000FF] [border: (1px solid #000000)]
-layer at (513,21) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (513,21) size 202x202 [border: (1px solid #000000)]
-layer at (534,42) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (555,63) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-layer at (576,84) size 90x90
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 90x90 [bgcolor=#0000FF] [border: (1px solid #000000)]
-layer at (21,267) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (21,267) size 202x202 [border: (1px solid #000000)]
-layer at (42,288) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (63,309) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-layer at (104,350) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (41,41) size 100x100 [border: (1px solid #000000)]
-layer at (267,267) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (267,267) size 202x202 [border: (1px solid #000000)]
-layer at (288,288) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (309,309) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-layer at (350,350) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (41,41) size 100x100 [border: (1px solid #000000)]
-    LayoutNGBlockFlow {DIV} at (21,21) size 90x90 [bgcolor=#0000FF] [border: (1px solid #000000)]
-layer at (513,267) size 202x202
-  LayoutNGBlockFlow (positioned) {DIV} at (513,267) size 202x202 [border: (1px solid #000000)]
-layer at (534,288) size 140x140
-  LayoutNGBlockFlow {DIV} at (21,21) size 140x140 [bgcolor=#DDDDDD] [border: (1px solid #000000)]
-layer at (555,309) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (21,21) size 100x100 [bgcolor=#81AA8A] [border: (1px solid #000000)]
-layer at (596,350) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (41,41) size 100x100 [border: (1px solid #000000)]
-layer at (637,391) size 100x100
-  LayoutNGBlockFlow (relative positioned) {DIV} at (41,41) size 100x100 [bgcolor=#AA7994] [border: (1px solid #000000)]
-    LayoutNGBlockFlow {DIV} at (21,21) size 90x90 [bgcolor=#0000FF] [border: (1px solid #000000)]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/svg-vs-css-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/svg-vs-css-expected.txt
deleted file mode 100644
index ec949727..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/transforms/svg-vs-css-expected.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x580
-  LayoutNGBlockFlow {html} at (0,0) size 800x579.63
-    LayoutNGBlockFlow {body} at (8,8) size 784x563.63
-      LayoutNGBlockFlow {div} at (10,10) size 220x543.63
-        LayoutNGBlockFlow {h2} at (0,19.91) size 220x27
-          LayoutText {#text} at (0,0) size 120x26
-            text run at (0,0) width 120: "SVG nested"
-        LayoutNGBlockFlow {h2} at (0,286.72) size 220x27
-          LayoutText {#text} at (0,0) size 114x26
-            text run at (0,0) width 114: "CSS nested"
-      LayoutText {#text} at (240,0) size 4x19
-        text run at (240,0) width 4: " "
-      LayoutNGBlockFlow {div} at (254,10) size 220x543.63
-        LayoutNGBlockFlow {h2} at (0,19.91) size 220x27
-          LayoutText {#text} at (0,0) size 162x26
-            text run at (0,0) width 162: "SVG compound"
-        LayoutNGBlockFlow {h2} at (0,286.72) size 220x27
-          LayoutText {#text} at (0,0) size 156x26
-            text run at (0,0) width 156: "CSS compound"
-      LayoutText {#text} at (484,0) size 4x19
-        text run at (484,0) width 4: " "
-      LayoutNGBlockFlow {div} at (498,10) size 220x543.63
-        LayoutNGBlockFlow {h2} at (0,19.91) size 220x27
-          LayoutText {#text} at (0,0) size 128x26
-            text run at (0,0) width 128: "SVG Matrix"
-        LayoutNGBlockFlow {h2} at (0,286.72) size 220x27
-          LayoutText {#text} at (0,0) size 116x26
-            text run at (0,0) width 116: "CSSMatrix"
-      LayoutText {#text} at (0,0) size 0x0
-layer at (28,85) size 200x200
-  LayoutNGBlockFlow (relative positioned) {div} at (10,66.81) size 200x200 [bgcolor=#C0C0C0] [border: (1px solid #000000)]
-    LayoutSVGRoot {svg} at (1,1) size 200x200
-      LayoutSVGContainer {g} at (-84.85,0) size 204.85x169.71 [transform={m=((1.00,0.00)(0.00,1.00)) t=(75.00,25.00)}]
-        LayoutSVGRect {rect} at (0,0) size 60x60 [stroke={[type=SOLID] [color=#000000] [dash array=[1.00, 1.00]]}] [x=0.00] [y=0.00] [width=60.00] [height=60.00]
-        LayoutSVGContainer {g} at (-42.43,0) size 102.43x84.85 [transform={m=((2.00,0.00)(0.00,2.00)) t=(0.00,0.00)}]
-          LayoutSVGRect {rect} at (0,0) size 60x60 [stroke={[type=SOLID] [color=#000000] [dash array=[1.00, 1.00]]}] [x=0.00] [y=0.00] [width=60.00] [height=60.00]
-          LayoutSVGRect {rect} at (0,0) size 60x60 [transform={m=((0.71,0.71)(-0.71,0.71)) t=(0.00,0.00)}] [stroke={[type=SOLID] [color=#0000FF]}] [x=0.00] [y=0.00] [width=60.00] [height=60.00]
-    LayoutText {#text} at (0,0) size 0x0
-layer at (28,352) size 200x200
-  LayoutNGBlockFlow (relative positioned) {div} at (10,333.63) size 200x200 [bgcolor=#C0C0C0] [border: (1px solid #000000)]
-layer at (29,353) size 60x60
-  LayoutNGBlockFlow (positioned) {div} at (1,1) size 60x60 [border: (1px dotted #000000)]
-layer at (105,379) size 60x60
-  LayoutNGBlockFlow (positioned) {div} at (1,1) size 60x60 [border: (1px dotted #000000)]
-layer at (31,355) size 60x60
-  LayoutNGBlockFlow (positioned) {div} at (1,1) size 60x60 [border: (1px solid #0000FF)]
-layer at (272,85) size 200x200
-  LayoutNGBlockFlow (relative positioned) {div} at (10,66.81) size 200x200 [bgcolor=#C0C0C0] [border: (1px solid #000000)]
-    LayoutSVGRoot {svg} at (1,1) size 200x200
-      LayoutSVGRect {rect} at (0,0) size 60x60 [transform={m=((1.41,1.41)(-1.41,1.41)) t=(75.00,25.00)}] [stroke={[type=SOLID] [color=#0000FF]}] [x=0.00] [y=0.00] [width=60.00] [height=60.00]
-    LayoutText {#text} at (0,0) size 0x0
-layer at (272,352) size 200x200
-  LayoutNGBlockFlow (relative positioned) {div} at (10,333.63) size 200x200 [bgcolor=#C0C0C0] [border: (1px solid #000000)]
-layer at (273,353) size 60x60
-  LayoutNGBlockFlow (positioned) {div} at (1,1) size 60x60 [border: (1px solid #0000FF)]
-layer at (516,85) size 200x200
-  LayoutNGBlockFlow (relative positioned) {div} at (10,66.81) size 200x200 [bgcolor=#C0C0C0] [border: (1px solid #000000)]
-    LayoutSVGRoot {svg} at (1,1) size 200x200
-      LayoutSVGRect {rect} at (0,0) size 60x60 [transform={m=((1.41,1.41)(-1.41,1.41)) t=(75.00,25.00)}] [stroke={[type=SOLID] [color=#0000FF]}] [x=0.00] [y=0.00] [width=60.00] [height=60.00]
-    LayoutText {#text} at (0,0) size 0x0
-layer at (516,352) size 200x200
-  LayoutNGBlockFlow (relative positioned) {div} at (10,333.63) size 200x200 [bgcolor=#C0C0C0] [border: (1px solid #000000)]
-layer at (517,353) size 60x60
-  LayoutNGBlockFlow (positioned) {div} at (1,1) size 60x60 [border: (1px solid #0000FF)]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/mouseevent_fractional/fast/events/popup-allowed-from-gesture-initiated-event-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/mouseevent_fractional/fast/events/popup-allowed-from-gesture-initiated-event-expected.txt
deleted file mode 100644
index 50f1454..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/mouseevent_fractional/fast/events/popup-allowed-from-gesture-initiated-event-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Click Here Click Here Too
-PASS win is non-null.
-PASS successfullyParsed is true
-
-TEST COMPLETE
-PASS win is non-null.
-
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/audio-controls-rendering-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/audio-controls-rendering-expected.txt
index 610087a..d480650 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/audio-controls-rendering-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/audio-controls-rendering-expected.txt
@@ -44,6 +44,18 @@
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
 layer at (139,69) size 111x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 111x4 [bgcolor=#0000008A]
+layer at (266,47) size 0x48 transparent
+  LayoutSlider {INPUT} at (258,3) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (266,69) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+layer at (266,69) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (266,69) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+layer at (266,69) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
 layer at (8,118) size 320x54
   LayoutFlexibleBox (relative positioned) {DIV} at (0,0) size 320x54
 layer at (8,118) size 320x54
@@ -72,6 +84,18 @@
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
 layer at (139,143) size 131x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 131x4 [bgcolor=#0000008A]
+layer at (286,121) size 0x48 transparent
+  LayoutSlider {INPUT} at (278,3) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (286,143) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+layer at (286,143) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (286,143) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+layer at (286,143) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
 layer at (8,192) size 320x100
   LayoutMedia (positioned) {AUDIO} at (8,192) size 320x100 [bgcolor=#0000FF]
 layer at (8,192) size 320x100
@@ -102,3 +126,15 @@
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
 layer at (139,263) size 131x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 131x4 [bgcolor=#0000008A]
+layer at (286,241) size 0x48 transparent
+  LayoutSlider {INPUT} at (278,3) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (286,263) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+layer at (286,263) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (286,263) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+layer at (286,263) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/media-document-audio-repaint-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/media-document-audio-repaint-expected.txt
index c48d0b8..f014079 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/media-document-audio-repaint-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/media-document-audio-repaint-expected.txt
@@ -46,3 +46,15 @@
       LayoutBlockFlow (positioned) {DIV} at (0,0) size 55x4 [bgcolor=#000000DE]
     layer at (226,211) size 55x4
       LayoutBlockFlow (positioned) {DIV} at (55,0) size 55x4 [bgcolor=#0000008A]
+    layer at (298,189) size 0x48 transparent
+      LayoutSlider {INPUT} at (258,3) size 0x48 [color=#9D968E]
+        LayoutFlexibleBox {DIV} at (0,22) size 0x4
+    layer at (298,211) size 12x4
+      LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#00000033]
+        LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#000000DE]
+    layer at (298,211) size 12x4
+      LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+    layer at (298,211) size 0x4
+      LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#000000DE]
+    layer at (298,211) size 12x4
+      LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#0000008A]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-aspect-ratio-expected.png b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-aspect-ratio-expected.png
index b007d07..2d641c3 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-aspect-ratio-expected.png
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-aspect-ratio-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-display-toggle-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-display-toggle-expected.txt
index d2234a46..8f97261 100644
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-display-toggle-expected.txt
+++ b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-display-toggle-expected.txt
@@ -16,32 +16,44 @@
   LayoutNGBlockFlow (positioned) {DIV} at (0,0) size 320x240
 layer at (8,28) size 320x240
   LayoutFlexibleBox {DIV} at (0,0) size 320x240
-layer at (8,28) size 320x224 clip at (10,30) size 316x220
-  LayoutButton (relative positioned) {INPUT} at (0,0) size 320x224 [border: (2px outset #C0C0C0)]
-    LayoutFlexibleBox (anonymous) at (8,82) size 304x60
-      LayoutBlockFlow {DIV} at (122,0) size 60x60 [bgcolor=#FFFFFFE6]
-layer at (8,204) size 320x48
-  LayoutFlexibleBox (relative positioned) {DIV} at (0,176) size 320x48
-    LayoutNGBlockFlow {DIV} at (16,0) size 31x48 [color=#FFFFFF]
-      LayoutText {#text} at (0,15) size 31x18
-        text run at (0,15) width 31: "0:00"
-    LayoutNGBlockFlow {DIV} at (51,0) size 39x48 [color=#FFFFFF]
-      LayoutText {#text} at (0,15) size 39x18
-        text run at (0,15) width 39: "/ 0:06"
-    LayoutNGBlockFlow {DIV} at (90,48) size 86x0
+layer at (118,86) size 100x100
+  LayoutButton (positioned) {INPUT} at (110,58) size 100x100
+    LayoutBlockFlow (anonymous) at (20,20) size 60x60
+      LayoutBlockFlow {DIV} at (0,0) size 60x60 [bgcolor=#FFFFFFE6]
+layer at (8,196) size 320x48
+  LayoutFlexibleBox (relative positioned) {DIV} at (0,168) size 320x48
+    LayoutNGBlockFlow {DIV} at (16,4) size 28x44 [color=#FFFFFF]
+      LayoutText {#text} at (0,14) size 28x16
+        text run at (0,14) width 28: "0:00"
+    LayoutNGBlockFlow {DIV} at (48,4) size 36x44 [color=#FFFFFF]
+      LayoutText {#text} at (0,14) size 36x16
+        text run at (0,14) width 36: "/ 0:06"
+    LayoutNGBlockFlow {DIV} at (84,48) size 92x0
     LayoutButton {INPUT} at (176,0) size 48x48
     LayoutButton {INPUT} at (224,0) size 48x48
     LayoutButton {INPUT} at (272,0) size 48x48
-layer at (8,252) size 320x16
-  LayoutSlider {INPUT} at (0,224) size 320x16 [color=#9D968E]
-    LayoutFlexibleBox {DIV} at (16,0) size 288x4
-layer at (24,252) size 288x4
-  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 288x4 [bgcolor=#FFFFFF4D]
-layer at (24,248) size 12x12
-  LayoutBlockFlow (relative positioned) {DIV} at (0,-4) size 12x12 [bgcolor=#FFFFFF]
-layer at (24,252) size 288x4
-  LayoutBlockFlow (positioned) {DIV} at (0,0) size 288x4
-layer at (24,252) size 0x4
+layer at (184,196) size 0x48 transparent
+  LayoutSlider {INPUT} at (176,0) size 0x48 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (0,22) size 0x4
+layer at (184,218) size 12x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 12x4 [bgcolor=#FFFFFF4D]
+    LayoutBlockFlow {DIV} at (0,-4) size 12x12 [bgcolor=#FFFFFF]
+layer at (184,218) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4
+layer at (184,218) size 0x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#FFFFFF]
-layer at (24,252) size 288x4
+layer at (184,218) size 12x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 12x4 [bgcolor=#FFFFFF8A]
+layer at (8,244) size 320x24
+  LayoutSlider {INPUT} at (0,216) size 320x24 [color=#9D968E]
+    LayoutFlexibleBox {DIV} at (16,0) size 288x4
+layer at (24,244) size 288x4
+  LayoutBlockFlow (relative positioned) {DIV} at (0,0) size 288x4 [bgcolor=#FFFFFF4D]
+layer at (24,240) size 12x12
+  LayoutBlockFlow (relative positioned) {DIV} at (0,-4) size 12x12 [bgcolor=#FFFFFF]
+layer at (24,244) size 288x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 288x4
+layer at (24,244) size 0x4
+  LayoutBlockFlow (positioned) {DIV} at (0,0) size 0x4 [bgcolor=#FFFFFF]
+layer at (24,244) size 288x4
   LayoutBlockFlow (positioned) {DIV} at (0,0) size 288x4 [bgcolor=#FFFFFF8A]
diff --git a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-layer-crash-expected.txt b/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-layer-crash-expected.txt
deleted file mode 100644
index c8e1f04a..0000000
--- a/third_party/WebKit/LayoutTests/flag-specific/enable-blink-features=LayoutNG/virtual/video-surface-layer/media/video-layer-crash-expected.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-layer at (0,0) size 800x600
-  LayoutView at (0,0) size 800x600
-layer at (0,0) size 800x600
-  LayoutNGBlockFlow {HTML} at (0,0) size 800x600
-    LayoutNGBlockFlow {BODY} at (8,8) size 784x584
-      LayoutNGBlockFlow {P} at (0,0) size 784x20
-        LayoutText {#text} at (0,0) size 359x19
-          text run at (0,0) width 359: "Test dynamic removal of transformed and reflected video"
-      LayoutNGBlockFlow (anonymous) at (0,36) size 784x342
-        LayoutText {#text} at (0,0) size 4x19
-          text run at (0,0) width 4: " "
-        LayoutBR {BR} at (4,0) size 0x19
-        LayoutText {#text} at (0,161) size 4x19
-          text run at (0,161) width 4: " "
-        LayoutBR {BR} at (210,161) size 0x19
-        LayoutText {#text} at (0,322) size 4x19
-          text run at (0,322) width 4: " "
-        LayoutBR {BR} at (210,322) size 0x19
-layer at (12,64) size 206x156
-  LayoutVideo {VIDEO} at (4,20) size 206x156 [border: (3px solid #FF0000)]
-layer at (15,67) size 200x150 backgroundClip at (65,105) size 100x75 clip at (65,105) size 100x75
-  LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150
-    LayoutFlexibleBox {DIV} at (0,0) size 200x150
-layer at (15,67) size 200x150 backgroundClip at (65,105) size 100x75 clip at (65,105) size 100x75
-  LayoutNGBlockFlow (positioned) {DIV} at (0,0) size 200x150
-layer at (12,225) size 206x156
-  LayoutVideo {VIDEO} at (4,181) size 206x156 [border: (3px solid #FF0000)]
-layer at (15,228) size 200x150
-  LayoutFlexibleBox (relative positioned) {DIV} at (3,3) size 200x150
-    LayoutFlexibleBox {DIV} at (0,0) size 200x150
-layer at (15,228) size 200x150
-  LayoutNGBlockFlow (positioned) {DIV} at (0,0) size 200x150
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/sandbox-iframe-blocks-top-navigation-to-javascript.html b/third_party/WebKit/LayoutTests/http/tests/security/sandbox-iframe-blocks-top-navigation-to-javascript.html
index 9cc1ea2..46672e782 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/sandbox-iframe-blocks-top-navigation-to-javascript.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/sandbox-iframe-blocks-top-navigation-to-javascript.html
@@ -12,10 +12,12 @@
                    "<a href='javascript:top.location=\"/security/frameNavigation/resources/fail.html\";'>click</a>";
 
         window.onmessage = t.step_func_done(e => {
-            // It was a SecurityError, but the error event got sanitized to
-            // "Script error." because the frames are cross-origin.
-            // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=363897
-            assert_equals(e.data, "Script error.", "The 'javascript:' navigation threw.");
+            const expected =
+                 "Uncaught SecurityError: Failed to set the 'href' " +
+                 "property on 'Location': The current window does not " +
+                 "have permission to navigate the target frame to " +
+                 "'/security/frameNavigation/resources/fail.html'.";
+            assert_equals(e.data, expected, "The 'javascript:' navigation threw.");
             assert_equals(i.contentDocument.body.innerText, "click", "The page contents did not change.");
         });
 
diff --git a/third_party/WebKit/LayoutTests/webexposed/custom-elements-expected.txt b/third_party/WebKit/LayoutTests/webexposed/custom-elements-expected.txt
index 0c67a68..5c0642b 100644
--- a/third_party/WebKit/LayoutTests/webexposed/custom-elements-expected.txt
+++ b/third_party/WebKit/LayoutTests/webexposed/custom-elements-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: line 1: :unresolved pseudo selector is deprecated and will be removed in M73, around March 2019. Please use :not(:defined) instead. See https://www.chromestatus.com/features/4642138092470272 for more details.
 Tests basic web-exposure of Custom Elements
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index ead1da6..8dfa718 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -447,7 +447,7 @@
 
   // Create and initialize the compositor thread. The thread is saved in
   // Platform, and will be accessible through CompositorThread().
-  void InitializeCompositorThread(const WebThreadCreationParams&);
+  void InitializeCompositorThread();
 
   // Returns an interface to the current thread.
   WebThread* CurrentThread();
@@ -459,6 +459,17 @@
   // renderer was created with threaded rendering disabled.
   WebThread* CompositorThread();
 
+  // Returns the task runner of the compositor thread. This is available
+  // once InitializeCompositorThread() is called.
+  scoped_refptr<base::SingleThreadTaskRunner> CompositorThreadTaskRunner();
+
+  // This is called after the compositor thread is created, so the embedder
+  // can initiate an IPC to change its thread priority (on Linux we can't
+  // increase the nice value, so we need to ask the browser process). This
+  // function is only called from the main thread (where InitializeCompositor-
+  // Thread() is called).
+  virtual void SetDisplayThreadPriority(base::PlatformThreadId) {}
+
   // Returns a blame context for attributing top-level work which does not
   // belong to a particular frame scope.
   virtual BlameContext* GetTopLevelBlameContext() { return nullptr; }
diff --git a/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc b/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
index 7489233f..a20c1a6 100644
--- a/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/activity_logger_test.cc
@@ -96,8 +96,7 @@
   void ExecuteScriptInIsolatedWorld(const String& script) const {
     v8::HandleScope scope(v8::Isolate::GetCurrent());
     script_controller_->ExecuteScriptInIsolatedWorld(
-        kIsolatedWorldId, ScriptSourceCode(script), KURL(),
-        kNotSharableCrossOrigin);
+        kIsolatedWorldId, ScriptSourceCode(script), KURL(), kOpaqueResource);
     PumpPendingRequestsForFrameToLoad(web_view_helper_.LocalMainFrame());
   }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
index 45e783b..bb38f953 100644
--- a/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
+++ b/third_party/blink/renderer/bindings/core/v8/scheduled_action.cc
@@ -170,11 +170,15 @@
   } else {
     DVLOG(1) << "ScheduledAction::execute " << this
              << ": executing from source";
+    // We're using |kSharableCrossOrigin| to keep the existing behavior, but
+    // this causes failures on
+    // wpt/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html
+    // and friends.
     frame->GetScriptController().ExecuteScriptAndReturnValue(
         script_state_->GetContext(),
         ScriptSourceCode(code_,
                          ScriptSourceLocationType::kEvalForScheduledAction),
-        KURL(), kNotSharableCrossOrigin);
+        KURL(), kSharableCrossOrigin);
   }
 
   // The frame might be invalid at this point because JavaScript could have
@@ -206,10 +210,14 @@
         function, worker, script_state_->GetContext()->Global(), info.size(),
         info.data(), script_state_->GetIsolate());
   } else {
+    // We're using |kSharableCrossOrigin| to keep the existing behavior, but
+    // this causes failures on
+    // wpt/html/webappapis/scripting/processing-model-2/compile-error-cross-origin-setTimeout.html
+    // and friends.
     worker->ScriptController()->Evaluate(
         ScriptSourceCode(code_,
                          ScriptSourceLocationType::kEvalForScheduledAction),
-        kNotSharableCrossOrigin);
+        kSharableCrossOrigin);
   }
 }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.cc b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
index 8a0bb55b..9132487 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
@@ -251,9 +251,10 @@
   // Step 12.9 "Let script be result of creating a classic script given script
   // source, settings, base URL, and the default classic script fetch options."
   // [spec text]
+  // We pass |kSharableCrossOrigin| because |muted errors| is false by default.
   v8::Local<v8::Value> result = EvaluateScriptInMainWorld(
       ScriptSourceCode(script_source, ScriptSourceLocationType::kJavascriptUrl),
-      base_url, kNotSharableCrossOrigin, ScriptFetchOptions(),
+      base_url, kSharableCrossOrigin, ScriptFetchOptions(),
       kDoNotExecuteScriptWhenScriptsDisabled);
 
   // If executing script caused this frame to be removed from the page, we
@@ -285,8 +286,8 @@
     ExecuteScriptPolicy policy) {
   v8::HandleScope handle_scope(GetIsolate());
   EvaluateScriptInMainWorld(ScriptSourceCode(script, source_location_type),
-                            KURL(), kNotSharableCrossOrigin,
-                            ScriptFetchOptions(), policy);
+                            KURL(), kOpaqueResource, ScriptFetchOptions(),
+                            policy);
 }
 
 void ScriptController::ExecuteScriptInMainWorld(
diff --git a/third_party/blink/renderer/bindings/core/v8/script_module_test.cc b/third_party/blink/renderer/bindings/core/v8/script_module_test.cc
index 6fe37339..2390b8d8 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_module_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_module_test.cc
@@ -282,7 +282,7 @@
       scope.GetFrame()
           .GetScriptController()
           .ExecuteScriptInMainWorldAndReturnValue(
-              ScriptSourceCode("window.foo"), KURL(), kNotSharableCrossOrigin);
+              ScriptSourceCode("window.foo"), KURL(), kOpaqueResource);
   ASSERT_TRUE(value->IsString());
   EXPECT_EQ("bar", ToCoreString(v8::Local<v8::String>::Cast(value)));
 
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
index 15fa392..8e3f5480 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer_test.cc
@@ -106,8 +106,7 @@
 v8::Local<v8::Value> Eval(const String& source, V8TestingScope& scope) {
   return scope.GetFrame()
       .GetScriptController()
-      .ExecuteScriptInMainWorldAndReturnValue(source, KURL(),
-                                              kNotSharableCrossOrigin);
+      .ExecuteScriptInMainWorldAndReturnValue(source, KURL(), kOpaqueResource);
 }
 
 String ToJSON(v8::Local<v8::Object> object, const V8TestingScope& scope) {
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
index 4dbd097..716d8437 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_initializer.cc
@@ -262,11 +262,8 @@
     return;
   }
 
-  AccessControlStatus access_control_status = kNotSharableCrossOrigin;
-  if (message->IsOpaque())
-    access_control_status = kOpaqueResource;
-  else if (message->IsSharedCrossOrigin())
-    access_control_status = kSharableCrossOrigin;
+  AccessControlStatus access_control_status =
+      message->IsSharedCrossOrigin() ? kSharableCrossOrigin : kOpaqueResource;
 
   ErrorEvent* event = ErrorEvent::Create(
       ToCoreStringWithNullCheck(message->Get()), std::move(location),
@@ -313,15 +310,14 @@
       ToCoreStringWithNullCheck(message->Get()), std::move(location),
       ScriptValue::From(script_state, data), &script_state->World());
 
-  AccessControlStatus cors_status = message->IsSharedCrossOrigin()
-                                        ? kSharableCrossOrigin
-                                        : kNotSharableCrossOrigin;
+  AccessControlStatus access_control_status =
+      message->IsSharedCrossOrigin() ? kSharableCrossOrigin : kOpaqueResource;
 
   // If execution termination has been triggered as part of constructing
   // the error event from the v8::Message, quietly leave.
   if (!isolate->IsExecutionTerminating()) {
     ExecutionContext::From(script_state)
-        ->DispatchErrorEvent(event, cors_status);
+        ->DispatchErrorEvent(event, access_control_status);
   }
 
   per_isolate_data->SetReportingException(false);
@@ -374,7 +370,7 @@
   }
 
   String error_message;
-  AccessControlStatus cors_status = kNotSharableCrossOrigin;
+  AccessControlStatus cors_status = kOpaqueResource;
   std::unique_ptr<SourceLocation> location;
 
   v8::Local<v8::Message> message =
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc
index 5f00ae29..2ff1bde 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.cc
@@ -218,11 +218,13 @@
       SourceLocation::FromMessage(GetIsolate(), message, execution_context),
       &World());
 
-  AccessControlStatus access_control_status = kNotSharableCrossOrigin;
+  AccessControlStatus access_control_status = kOpaqueResource;
   if (message->IsOpaque())
     access_control_status = kOpaqueResource;
   else if (message->IsSharedCrossOrigin())
     access_control_status = kSharableCrossOrigin;
+  else
+    NOTREACHED();
 
   execution_context->DispatchErrorEvent(event, access_control_status);
 }
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
index 0f989bb0..edbdba2 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner_test.cc
@@ -63,7 +63,7 @@
     std::tie(compile_options, produce_cache_options, no_cache_reason) =
         V8CodeCache::GetCompileOptions(cache_options, source_code);
     v8::MaybeLocal<v8::Script> compiled_script = V8ScriptRunner::CompileScript(
-        script_state, source_code, kNotSharableCrossOrigin, compile_options,
+        script_state, source_code, kOpaqueResource, compile_options,
         no_cache_reason, ReferrerScriptInfo());
     if (compiled_script.IsEmpty()) {
       return false;
@@ -81,7 +81,7 @@
                      v8::ScriptCompiler::NoCacheReason no_cache_reason,
                      V8CodeCache::ProduceCacheOptions produce_cache_options) {
     v8::MaybeLocal<v8::Script> compiled_script = V8ScriptRunner::CompileScript(
-        script_state, source_code, kNotSharableCrossOrigin, compile_options,
+        script_state, source_code, kOpaqueResource, compile_options,
         no_cache_reason, ReferrerScriptInfo());
     if (compiled_script.IsEmpty()) {
       return false;
diff --git a/third_party/blink/renderer/core/core_export.h b/third_party/blink/renderer/core/core_export.h
index 24af246f..0ccb042 100644
--- a/third_party/blink/renderer/core/core_export.h
+++ b/third_party/blink/renderer/core/core_export.h
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // This header defines macros to export component's symbols.
-// See "platform/PlatformExport.h" for details.
+// See "platform/platform_export.h" for details.
 
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CORE_EXPORT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CORE_EXPORT_H_
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 2a21dc8..f67c313 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -848,6 +848,8 @@
     "resolver/css_property_priority.h",
     "resolver/css_to_style_map.cc",
     "resolver/css_to_style_map.h",
+    "resolver/css_variable_animator.cc",
+    "resolver/css_variable_animator.h",
     "resolver/css_variable_resolver.cc",
     "resolver/css_variable_resolver.h",
     "resolver/element_resolve_context.cc",
diff --git a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
index c6994ea..5431ec0 100644
--- a/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_selector_parser.cc
@@ -1160,10 +1160,8 @@
           break;
       }
       if (feature != WebFeature::kNumberOfFeatures) {
-        if (!Deprecation::DeprecationMessage(feature).IsEmpty() &&
-            style_sheet_ && style_sheet_->AnyOwnerDocument()) {
-          Deprecation::CountDeprecation(*style_sheet_->AnyOwnerDocument(),
-                                        feature);
+        if (!Deprecation::DeprecationMessage(feature).IsEmpty()) {
+          context_->CountDeprecation(feature);
         } else {
           context_->Count(feature);
         }
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_animator.cc b/third_party/blink/renderer/core/css/resolver/css_variable_animator.cc
new file mode 100644
index 0000000..04f05610
--- /dev/null
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_animator.cc
@@ -0,0 +1,84 @@
+// 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/resolver/css_variable_animator.h"
+
+#include "third_party/blink/renderer/core/animation/css_interpolation_environment.h"
+#include "third_party/blink/renderer/core/animation/css_interpolation_types_map.h"
+#include "third_party/blink/renderer/core/animation/invalidatable_interpolation.h"
+#include "third_party/blink/renderer/core/animation/transition_interpolation.h"
+
+namespace blink {
+
+namespace {
+
+HashSet<PropertyHandle> CollectPending(const CSSAnimationUpdate& update) {
+  HashSet<PropertyHandle> pending;
+  for (const auto& entry : update.ActiveInterpolationsForCustomAnimations())
+    pending.insert(entry.key);
+  for (const auto& entry : update.ActiveInterpolationsForCustomTransitions())
+    pending.insert(entry.key);
+  return pending;
+}
+
+const ActiveInterpolations& ActiveInterpolationsForCustomProperty(
+    const CSSAnimationUpdate& update,
+    const PropertyHandle& property) {
+  // Interpolations will never be found in both animations_map and
+  // transitions_map. This condition is ensured by
+  // CSSAnimations::CalculateTransitionUpdateForProperty().
+  const ActiveInterpolationsMap& animations_map =
+      update.ActiveInterpolationsForCustomAnimations();
+  const ActiveInterpolationsMap& transitions_map =
+      update.ActiveInterpolationsForCustomTransitions();
+  const auto& animation = animations_map.find(property);
+  if (animation != animations_map.end()) {
+    DCHECK_EQ(transitions_map.find(property), transitions_map.end());
+    return animation->value;
+  }
+  const auto& transition = transitions_map.find(property);
+  DCHECK_NE(transition, transitions_map.end());
+  return transition->value;
+}
+
+}  // namespace
+
+CSSVariableAnimator::CSSVariableAnimator(StyleResolverState& state)
+    : CSSVariableResolver(state),
+      state_(state),
+      update_(state.AnimationUpdate()),
+      pending_properties_(CollectPending(update_)) {}
+
+void CSSVariableAnimator::ApplyAll() {
+  while (!pending_properties_.IsEmpty()) {
+    PropertyHandle property = *pending_properties_.begin();
+    Apply(property);
+    DCHECK_EQ(pending_properties_.find(property), pending_properties_.end());
+  }
+}
+
+void CSSVariableAnimator::ApplyAnimation(const AtomicString& name) {
+  PropertyHandle property(name);
+  if (pending_properties_.Contains(property))
+    Apply(property);
+}
+
+void CSSVariableAnimator::Apply(const PropertyHandle& property) {
+  DCHECK(property.IsCSSCustomProperty());
+  DCHECK(pending_properties_.Contains(property));
+  const ActiveInterpolations& interpolations =
+      ActiveInterpolationsForCustomProperty(update_, property);
+  const Interpolation& interpolation = *interpolations.front();
+  if (interpolation.IsInvalidatableInterpolation()) {
+    CSSInterpolationTypesMap map(state_.GetDocument().GetPropertyRegistry(),
+                                 state_.GetDocument());
+    CSSInterpolationEnvironment environment(map, state_, this);
+    InvalidatableInterpolation::ApplyStack(interpolations, environment);
+  } else {
+    ToTransitionInterpolation(interpolation).Apply(state_);
+  }
+  pending_properties_.erase(property);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_animator.h b/third_party/blink/renderer/core/css/resolver/css_variable_animator.h
new file mode 100644
index 0000000..6a284da
--- /dev/null
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_animator.h
@@ -0,0 +1,49 @@
+// 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_RESOLVER_CSS_VARIABLE_ANIMATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_CSS_VARIABLE_ANIMATOR_H_
+
+#include "third_party/blink/renderer/core/animation/interpolation.h"
+#include "third_party/blink/renderer/core/animation/property_handle.h"
+#include "third_party/blink/renderer/core/css/resolver/css_variable_resolver.h"
+
+namespace blink {
+
+class StyleResolverState;
+class CSSAnimationUpdate;
+
+// CSSVariableAnimator is a special CSSVariableResolver which can apply
+// animated values during var()-resolution. In other words, it makes sure that
+// if a var()-reference to a currently animating custom property is encountered,
+// we will first apply the animated value for that property before resolving it.
+class CORE_EXPORT CSSVariableAnimator : public CSSVariableResolver {
+  STACK_ALLOCATED();
+
+ public:
+  explicit CSSVariableAnimator(StyleResolverState&);
+
+  // Apply all custom property animations. After calling this, the set of
+  // pending properties will be empty and further calls to ApplyAll will have
+  // no effect.
+  void ApplyAll();
+
+ protected:
+  void ApplyAnimation(const AtomicString&) override;
+
+ private:
+  // Apply the animated value of a single property. The property must exist
+  // in 'pending_properties_'.
+  void Apply(const PropertyHandle&);
+
+  StyleResolverState& state_;
+  const CSSAnimationUpdate& update_;
+  // Set of custom properties with pending animations. We will apply these
+  // one by one until the set is empty.
+  HashSet<PropertyHandle> pending_properties_;
+};
+
+}  // namespace blink
+
+#endif  // CSSVariableAnimator
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
index 769dc7d..57172354 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
@@ -329,20 +329,14 @@
       range.ConsumeIncludingWhitespace().Value().ToAtomicString();
   DCHECK(range.AtEnd() || (range.Peek().GetType() == kCommaToken));
 
-  PropertyHandle property(variable_name);
-  if (state_.AnimationPendingCustomProperties().Contains(property) &&
-      !variables_seen_.Contains(variable_name)) {
-    // We make the StyleResolverState mutable for animated custom properties as
-    // an optimisation. Without this we would need to compute animated values on
-    // the stack without saving the result or perform an expensive and complex
-    // value dependency graph analysis to compute them in the required order.
-    StyleResolver::ApplyAnimatedCustomProperty(
-        const_cast<StyleResolverState&>(state_), *this, property);
+  if (!variables_seen_.Contains(variable_name)) {
+    ApplyAnimation(variable_name);
     // Null custom property storage may become non-null after application, we
     // must refresh these cached values.
     inherited_variables_ = state_.Style()->InheritedVariables();
     non_inherited_variables_ = state_.Style()->NonInheritedVariables();
   }
+
   scoped_refptr<CSSVariableData> variable_data =
       is_env_variable ? ValueForEnvironmentVariable(variable_name)
                       : ValueForCustomProperty(variable_name, options);
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
index 8815821..d748138 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
@@ -33,7 +33,7 @@
   STACK_ALLOCATED();
 
  public:
-  CSSVariableResolver(const StyleResolverState&);
+  explicit CSSVariableResolver(const StyleResolverState&);
 
   scoped_refptr<CSSVariableData> ResolveCustomPropertyAnimationKeyframe(
       const CSSCustomPropertyDeclaration& keyframe,
@@ -48,6 +48,11 @@
 
   void ComputeRegisteredVariables();
 
+ protected:
+  // Called before looking up the value of some var()-reference to make it
+  // possible to apply animated properties during variable resolution.
+  virtual void ApplyAnimation(const AtomicString& name) {}
+
  private:
   struct Options {
     STACK_ALLOCATED();
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index 183a581..86bdf2b 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -61,6 +61,7 @@
 #include "third_party/blink/renderer/core/css/part_names.h"
 #include "third_party/blink/renderer/core/css/properties/css_property.h"
 #include "third_party/blink/renderer/core/css/resolver/animated_style_builder.h"
+#include "third_party/blink/renderer/core/css/resolver/css_variable_animator.h"
 #include "third_party/blink/renderer/core/css/resolver/css_variable_resolver.h"
 #include "third_party/blink/renderer/core/css/resolver/match_result.h"
 #include "third_party/blink/renderer/core/css/resolver/media_query_result.h"
@@ -1084,70 +1085,6 @@
   }
 }
 
-static void ApplyAnimatedCustomProperties(StyleResolverState& state) {
-  if (!state.IsAnimatingCustomProperties()) {
-    return;
-  }
-  CSSAnimationUpdate& update = state.AnimationUpdate();
-  HashSet<PropertyHandle>& pending = state.AnimationPendingCustomProperties();
-  DCHECK(pending.IsEmpty());
-  for (const auto& interpolations :
-       {update.ActiveInterpolationsForCustomAnimations(),
-        update.ActiveInterpolationsForCustomTransitions()}) {
-    for (const auto& entry : interpolations) {
-      pending.insert(entry.key);
-    }
-  }
-  while (!pending.IsEmpty()) {
-    PropertyHandle property = *pending.begin();
-    CSSVariableResolver variable_resolver(state);
-    StyleResolver::ApplyAnimatedCustomProperty(state, variable_resolver,
-                                               property);
-    // The property must no longer be pending after applying it.
-    DCHECK_EQ(pending.find(property), pending.end());
-  }
-}
-
-static const ActiveInterpolations& ActiveInterpolationsForCustomProperty(
-    const StyleResolverState& state,
-    const PropertyHandle& property) {
-  // Interpolations will never be found in both animations_map and
-  // transitions_map. This condition is ensured by
-  // CSSAnimations::CalculateTransitionUpdateForProperty().
-  const ActiveInterpolationsMap& animations_map =
-      state.AnimationUpdate().ActiveInterpolationsForCustomAnimations();
-  const ActiveInterpolationsMap& transitions_map =
-      state.AnimationUpdate().ActiveInterpolationsForCustomTransitions();
-  const auto& animation = animations_map.find(property);
-  if (animation != animations_map.end()) {
-    DCHECK_EQ(transitions_map.find(property), transitions_map.end());
-    return animation->value;
-  }
-  const auto& transition = transitions_map.find(property);
-  DCHECK_NE(transition, transitions_map.end());
-  return transition->value;
-}
-
-void StyleResolver::ApplyAnimatedCustomProperty(
-    StyleResolverState& state,
-    CSSVariableResolver& variable_resolver,
-    const PropertyHandle& property) {
-  DCHECK(property.IsCSSCustomProperty());
-  DCHECK(state.AnimationPendingCustomProperties().Contains(property));
-  const ActiveInterpolations& interpolations =
-      ActiveInterpolationsForCustomProperty(state, property);
-  const Interpolation& interpolation = *interpolations.front();
-  if (interpolation.IsInvalidatableInterpolation()) {
-    CSSInterpolationTypesMap map(state.GetDocument().GetPropertyRegistry(),
-                                 state.GetDocument());
-    CSSInterpolationEnvironment environment(map, state, &variable_resolver);
-    InvalidatableInterpolation::ApplyStack(interpolations, environment);
-  } else {
-    ToTransitionInterpolation(interpolation).Apply(state);
-  }
-  state.AnimationPendingCustomProperties().erase(property);
-}
-
 bool StyleResolver::ApplyAnimatedStandardProperties(
     StyleResolverState& state,
     const Element* animating_element) {
@@ -1238,9 +1175,8 @@
 void StyleResolver::ApplyAnimatedStandardProperties(
     StyleResolverState& state,
     const ActiveInterpolationsMap& active_interpolations_map) {
-  static_assert(
-      priority != kResolveVariables,
-      "Use applyAnimatedCustomProperty() for custom property animations");
+  static_assert(priority != kResolveVariables,
+                "Use CSSVariableAnimator for custom property animations");
   // TODO(alancutter): Don't apply presentation attribute animations here,
   // they should instead apply in
   // SVGElement::CollectStyleForPresentationAttribute().
@@ -1744,8 +1680,9 @@
 
   CSSVariableResolver(state).ComputeRegisteredVariables();
 
-  if (apply_animations == kIncludeAnimations) {
-    ApplyAnimatedCustomProperties(state);
+  if (apply_animations == kIncludeAnimations &&
+      state.IsAnimatingCustomProperties()) {
+    CSSVariableAnimator(state).ApplyAll();
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h
index b3e6ff84..f300fa31 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -52,7 +52,6 @@
 class RuleSet;
 class CSSPropertyValueSet;
 class StyleRuleUsageTracker;
-class CSSVariableResolver;
 
 enum RuleMatchingBehavior { kMatchAllRules, kMatchAllRulesExcludingSMIL };
 
@@ -136,10 +135,6 @@
   void SetRuleUsageTracker(StyleRuleUsageTracker*);
   void UpdateMediaType();
 
-  static void ApplyAnimatedCustomProperty(StyleResolverState&,
-                                          CSSVariableResolver&,
-                                          const PropertyHandle&);
-
   static bool HasAuthorBackground(const StyleResolverState&);
 
   void Trace(blink::Visitor*);
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
index 3433438..fc9664a 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
@@ -125,14 +125,6 @@
     is_animating_custom_properties_ = value;
   }
 
-  HashSet<PropertyHandle>& AnimationPendingCustomProperties() {
-    return animation_pending_custom_properties_;
-  }
-
-  const HashSet<PropertyHandle>& AnimationPendingCustomProperties() const {
-    return animation_pending_custom_properties_;
-  }
-
   void SetParentStyle(scoped_refptr<const ComputedStyle>);
   const ComputedStyle* ParentStyle() const { return parent_style_.get(); }
 
@@ -220,7 +212,6 @@
   CSSAnimationUpdate animation_update_;
   bool is_animation_interpolation_map_ready_;
   bool is_animating_custom_properties_;
-  HashSet<PropertyHandle> animation_pending_custom_properties_;
 
   bool apply_property_to_regular_style_;
   bool apply_property_to_visited_link_style_;
diff --git a/third_party/blink/renderer/core/dom/BUILD.gn b/third_party/blink/renderer/core/dom/BUILD.gn
index 7ffeb62..d1bf2dc 100644
--- a/third_party/blink/renderer/core/dom/BUILD.gn
+++ b/third_party/blink/renderer/core/dom/BUILD.gn
@@ -127,6 +127,8 @@
     "events/event_target_impl.h",
     "events/node_event_context.cc",
     "events/node_event_context.h",
+    "events/registered_event_listener.cc",
+    "events/registered_event_listener.h",
     "events/scoped_event_queue.cc",
     "events/scoped_event_queue.h",
     "events/simulated_click_options.h",
diff --git a/third_party/blink/renderer/core/dom/abort_controller.h b/third_party/blink/renderer/core/dom/abort_controller.h
index 32e53cf..1cbd684 100644
--- a/third_party/blink/renderer/core/dom/abort_controller.h
+++ b/third_party/blink/renderer/core/dom/abort_controller.h
@@ -23,7 +23,7 @@
   static AbortController* Create(ExecutionContext*);
   ~AbortController() override;
 
-  // AbortController.idl
+  // abort_controller.idl
 
   // https://dom.spec.whatwg.org/#dom-abortcontroller-signal
   AbortSignal* signal() const { return signal_.Get(); }
diff --git a/third_party/blink/renderer/core/dom/abort_signal.h b/third_party/blink/renderer/core/dom/abort_signal.h
index 3a257ac..23cadde 100644
--- a/third_party/blink/renderer/core/dom/abort_signal.h
+++ b/third_party/blink/renderer/core/dom/abort_signal.h
@@ -24,7 +24,7 @@
   explicit AbortSignal(ExecutionContext*);
   ~AbortSignal() override;
 
-  // AbortSignal.idl
+  // abort_signal.idl
   bool aborted() const { return aborted_flag_; }
   DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
 
diff --git a/third_party/blink/renderer/core/dom/attr.h b/third_party/blink/renderer/core/dom/attr.h
index 09bc9491..cf12ab1b 100644
--- a/third_party/blink/renderer/core/dom/attr.h
+++ b/third_party/blink/renderer/core/dom/attr.h
@@ -79,13 +79,13 @@
 
   // Attr wraps either an element/name, or a name/value pair (when it's a
   // standalone Node.)
-  // Note that m_name is always set, but m_element/m_standaloneValue may be
-  // null.
+  // Note that name_ is always set, but element_ /
+  // standalone_value_or_attached_local_name_ may be null.
   TraceWrapperMember<Element> element_;
   QualifiedName name_;
   // Holds the value if it is a standalone Node, or the local name of the
   // attribute it is attached to on an Element. The latter may (letter case)
-  // differ from m_name's local name. As these two modes are non-overlapping,
+  // differ from name_'s local name. As these two modes are non-overlapping,
   // use a single field.
   AtomicString standalone_value_or_attached_local_name_;
 };
diff --git a/third_party/blink/renderer/core/dom/class_collection.h b/third_party/blink/renderer/core/dom/class_collection.h
index ff0d74a..b4c7d6b 100644
--- a/third_party/blink/renderer/core/dom/class_collection.h
+++ b/third_party/blink/renderer/core/dom/class_collection.h
@@ -38,9 +38,9 @@
 
 class ClassCollection final : public HTMLCollection {
  public:
-  // classNames argument is an AtomicString because it is common for Elements to
-  // share the same class names.  It is also used to construct a
-  // SpaceSplitString (m_classNames) and its constructor requires an
+  // class_names argument is an AtomicString because it is common for Elements
+  // to share the same class names.  It is also used to construct a
+  // SpaceSplitString (class_names_) and its constructor requires an
   // AtomicString.
   static ClassCollection* Create(ContainerNode& root_node,
                                  CollectionType type,
diff --git a/third_party/blink/renderer/core/dom/container_node.h b/third_party/blink/renderer/core/dom/container_node.h
index 578665e..ebb81ac 100644
--- a/third_party/blink/renderer/core/dom/container_node.h
+++ b/third_party/blink/renderer/core/dom/container_node.h
@@ -294,7 +294,7 @@
   void RebuildNonDistributedChildren();
 
   // -----------------------------------------------------------------------------
-  // Notification of document structure changes (see core/dom/Node.h for more
+  // Notification of document structure changes (see core/dom/node.h for more
   // notification methods)
 
   enum ChildrenChangeType {
diff --git a/third_party/blink/renderer/core/dom/context_features.h b/third_party/blink/renderer/core/dom/context_features.h
index 2fa26426..bf995fb 100644
--- a/third_party/blink/renderer/core/dom/context_features.h
+++ b/third_party/blink/renderer/core/dom/context_features.h
@@ -103,12 +103,12 @@
 inline void ContextFeatures::UrlDidChange(Document* document) {
   // FIXME: The original code, commented out below, is obviously
   // wrong, but the seemingly correct fix of negating the test to
-  // the more logical 'if (!m_client)' crashes the renderer.
+  // the more logical 'if (!client_)' crashes the renderer.
   // See issue 294180
   //
-  // if (m_client)
-  //     return;
-  // m_client->urlDidChange(document);
+  // if (client_)
+  //   return;
+  // client_->UrlDidChange(document);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 4caf2160..cdd6968 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -747,9 +747,9 @@
 
   lifecycle_.AdvanceTo(DocumentLifecycle::kInactive);
 
-  // Since CSSFontSelector requires Document::m_fetcher and StyleEngine owns
-  // CSSFontSelector, need to initialize m_styleEngine after initializing
-  // m_fetcher.
+  // Since CSSFontSelector requires Document::fetcher_ and StyleEngine owns
+  // CSSFontSelector, need to initialize style_engine_ after initializing
+  // fetcher_.
   style_engine_ = StyleEngine::Create(*this);
 
   // The parent's parser should be suspended together with all the other
@@ -2984,8 +2984,8 @@
 AXObjectCache* Document::ExistingAXObjectCache() const {
   auto& cache_owner = AXObjectCacheOwner();
 
-  // If the layoutObject is gone then we are in the process of destruction.
-  // This method will be called before m_frame = nullptr.
+  // If the LayoutView is gone then we are in the process of destruction.
+  // This method will be called before frame_ = nullptr.
   if (!cache_owner.GetLayoutView())
     return nullptr;
 
@@ -4005,8 +4005,7 @@
   // DOM 3 Core: When the Document supports the feature "HTML" [DOM Level 2
   // HTML], the base URI is computed using first the value of the href attribute
   // of the HTML BASE element if any, and the value of the documentURI attribute
-  // from the Document interface otherwise (which we store, preparsed, in
-  // m_url).
+  // from the Document interface otherwise (which we store, preparsed, in url_).
   if (!base_element_url_.IsEmpty())
     base_url_ = base_element_url_;
   else if (!base_url_override_.IsEmpty())
@@ -6575,7 +6574,7 @@
 }
 
 void Document::DetachRange(Range* range) {
-  // We don't ASSERT m_ranges.contains(range) to allow us to call this
+  // We don't DCHECK ranges_.contains(range) to allow us to call this
   // unconditionally to fix: https://bugs.webkit.org/show_bug.cgi?id=26044
   ranges_.erase(range);
 }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index bf0c2368..1cefa93f 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -985,7 +985,7 @@
   DocumentMarkerController& Markers() const { return *markers_; }
 
   // Support for Javascript execCommand, and related methods
-  // See "core/editing/commands/DocumentExecCommand.cpp" for implementations.
+  // See "core/editing/commands/document_exec_command.cc" for implementations.
   bool execCommand(const String& command,
                    bool show_ui,
                    const String& value,
@@ -1259,8 +1259,8 @@
   }
   HTMLDialogElement* ActiveModalDialog() const;
 
-  // A non-null m_templateDocumentHost implies that |this| was created by
-  // ensureTemplateDocument().
+  // A non-null template_document_host_ implies that |this| was created by
+  // EnsureTemplateDocument().
   bool IsTemplateDocument() const { return !!template_document_host_; }
   Document& EnsureTemplateDocument();
   Document* TemplateDocumentHost() { return template_document_host_; }
@@ -1642,9 +1642,9 @@
   // Document URLs.
   KURL url_;  // Document.URL: The URL from which this document was retrieved.
   KURL base_url_;  // Node.baseURI: The URL to use when resolving relative URLs.
-  KURL
-      base_url_override_;  // An alternative base URL that takes precedence over
-                           // m_baseURL (but not m_baseElementURL).
+  // An alternative base URL that takes precedence over base_url_ (but
+  // not base_element_url_).
+  KURL base_url_override_;
   KURL base_element_url_;  // The URL set by the <base> element.
   KURL cookie_url_;        // The URL to use for cookie access.
   std::unique_ptr<OriginAccessEntry> access_entry_from_url_;
@@ -1792,7 +1792,7 @@
   // For early return in Fullscreen::fromIfExists()
   bool has_fullscreen_supplement_;
 
-  // The last element in |m_topLayerElements| is topmost in the top layer
+  // The last element in |top_layer_elements_| is topmost in the top layer
   // stack and is thus the one that will be visually on top.
   HeapVector<Member<Element>> top_layer_elements_;
 
diff --git a/third_party/blink/renderer/core/dom/document.idl b/third_party/blink/renderer/core/dom/document.idl
index 072e5a8..5f6ff88 100644
--- a/third_party/blink/renderer/core/dom/document.idl
+++ b/third_party/blink/renderer/core/dom/document.idl
@@ -104,7 +104,7 @@
 
     // DOM tree accessors
     // Named getter is implemented without IDL code generation for better
-    // performance. See LocalWindowProxy.cpp.
+    // performance. See local_window_proxy.cc.
     // getter object (DOMString name);
     [Affects=Nothing, CEReactions, CustomElementCallbacks] attribute DOMString title;
     [CEReactions, CustomElementCallbacks] attribute DOMString dir;
diff --git a/third_party/blink/renderer/core/dom/document_parser.h b/third_party/blink/renderer/core/dom/document_parser.h
index 157f3b7..9ffd54d4 100644
--- a/third_party/blink/renderer/core/dom/document_parser.h
+++ b/third_party/blink/renderer/core/dom/document_parser.h
@@ -90,9 +90,9 @@
   virtual void StopParsing();
 
   // Document is expected to detach the parser before releasing its ref.
-  // After detach, m_document is cleared.  The parser will unwind its
+  // After detach, document_ is cleared.  The parser will unwind its
   // callstacks, but not produce any more nodes.
-  // It is impossible for the parser to touch the rest of WebCore after
+  // It is impossible for the parser to touch the rest of Blink Core after
   // detach is called.
   // Oilpan: We don't need to call detach when a Document is destructed.
   virtual void Detach();
@@ -129,7 +129,7 @@
   bool document_was_loaded_as_part_of_navigation_;
 
   // Every DocumentParser needs a pointer back to the document.
-  // m_document will be 0 after the parser is stopped.
+  // document_ will be 0 after the parser is stopped.
   Member<Document> document_;
 
   HeapHashSet<WeakMember<DocumentParserClient>> clients_;
diff --git a/third_party/blink/renderer/core/dom/document_parser_timing.h b/third_party/blink/renderer/core/dom/document_parser_timing.h
index f0c93090..4cea8dc 100644
--- a/third_party/blink/renderer/core/dom/document_parser_timing.h
+++ b/third_party/blink/renderer/core/dom/document_parser_timing.h
@@ -63,7 +63,7 @@
 
   // The getters below return monotonically-increasing time, or zero if the
   // given parser event has not yet occurred.  See the comments for
-  // monotonicallyIncreasingTime in wtf/Time.h for additional details.
+  // MonotonicallyIncreasingTime in platform/wtf/time.h for additional details.
 
   TimeTicks ParserStart() const { return parser_start_; }
   TimeTicks ParserStop() const { return parser_stop_; }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index f4839c8..494a938 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -395,9 +395,9 @@
 void Element::SynchronizeAllAttributes() const {
   if (!GetElementData())
     return;
-  // NOTE: anyAttributeMatches in SelectorChecker.cpp
-  // currently assumes that all lazy attributes have a null namespace.
-  // If that ever changes we'll need to fix that code.
+  // NOTE: AnyAttributeMatches in selector_checker.cc currently assumes that all
+  // lazy attributes have a null namespace.  If that ever changes we'll need to
+  // fix that code.
   if (GetElementData()->style_attribute_is_dirty_) {
     DCHECK(IsStyledElement());
     SynchronizeStyleAttributeInternal();
@@ -441,10 +441,10 @@
     // animated SVG Attribute. It would seem we should only call this method
     // if SVGElement::isAnimatableAttribute is true, but the list of
     // animatable attributes in isAnimatableAttribute does not suffice to
-    // pass all layout tests. Also, m_animatedSVGAttributesAreDirty stays
-    // dirty unless synchronizeAnimatedSVGAttribute is called with
-    // anyQName(). This means that even if Element::synchronizeAttribute()
-    // is called on all attributes, m_animatedSVGAttributesAreDirty remains
+    // pass all layout tests. Also, animated_svg_attributes_are_dirty_ stays
+    // dirty unless SynchronizeAnimatedSVGAttribute is called with
+    // AnyQName(). This means that even if Element::SynchronizeAttribute()
+    // is called on all attributes, animated_svg_attributes_are_dirty_ remains
     // true.
     ToSVGElement(this)->SynchronizeAnimatedSVGAttribute(
         QualifiedName(g_null_atom, local_name, g_null_atom));
@@ -1972,8 +1972,8 @@
 
   ParserDidSetAttributes();
 
-  // Use attributeVector instead of m_elementData because attributeChanged might
-  // modify m_elementData.
+  // Use attribute_vector instead of element_data_ because AttributeChanged
+  // might modify element_data_.
   for (const auto& attribute : attribute_vector) {
     AttributeChanged(AttributeModificationParams(
         attribute.GetName(), g_null_atom, attribute.Value(),
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 83959960..be54612 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -382,7 +382,7 @@
     return ContainerNode::HasTagName(tag_name);
   }
 
-  // Should be called only by Document::createElementNS to fix up m_tagName
+  // Should be called only by Document::createElementNS to fix up tag_name_
   // immediately after construction.
   void SetTagNameForCreateElementNS(const QualifiedName&);
 
diff --git a/third_party/blink/renderer/core/dom/element_data.h b/third_party/blink/renderer/core/dom/element_data.h
index 8cdcc95..7ee0db9a 100644
--- a/third_party/blink/renderer/core/dom/element_data.h
+++ b/third_party/blink/renderer/core/dom/element_data.h
@@ -158,7 +158,7 @@
 // UniqueElementData is created when an element needs to mutate its attributes
 // or gains presentation attribute style (ex. width="10"). It does not need to
 // be created to fill in values in the ElementData that are derived from
-// attributes. For example populating the m_inlineStyle from the style attribute
+// attributes. For example populating the inline_style_ from the style attribute
 // doesn't require a UniqueElementData as all elements with the same style
 // attribute will have the same inline style.
 class UniqueElementData final : public ElementData {
diff --git a/third_party/blink/renderer/core/dom/events/event.cc b/third_party/blink/renderer/core/dom/events/event.cc
index b9fa5584..0239dfb 100644
--- a/third_party/blink/renderer/core/dom/events/event.cc
+++ b/third_party/blink/renderer/core/dom/events/event.cc
@@ -239,6 +239,10 @@
   return false;
 }
 
+bool Event::IsErrorEvent() const {
+  return false;
+}
+
 void Event::preventDefault() {
   if (handling_passive_ != PassiveMode::kNotPassive &&
       handling_passive_ != PassiveMode::kNotPassiveDefault) {
diff --git a/third_party/blink/renderer/core/dom/events/event.h b/third_party/blink/renderer/core/dom/events/event.h
index 4b1a53b..b122900 100644
--- a/third_party/blink/renderer/core/dom/events/event.h
+++ b/third_party/blink/renderer/core/dom/events/event.h
@@ -163,7 +163,7 @@
   bool IsScopedInV0() const;
 
   // Event creation timestamp in milliseconds. It returns a DOMHighResTimeStamp
-  // using the platform timestamp (see |m_platformTimeStamp|).
+  // using the platform timestamp (see |platform_time_stamp_|).
   // For more info see http://crbug.com/160524
   double timeStamp(ScriptState*) const;
   TimeTicks PlatformTimeStamp() const { return platform_time_stamp_; }
@@ -208,6 +208,7 @@
   virtual bool IsBeforeTextInsertedEvent() const;
 
   virtual bool IsBeforeUnloadEvent() const;
+  virtual bool IsErrorEvent() const;
 
   virtual bool IsActivateInvisibleEvent() const;
 
@@ -341,8 +342,8 @@
   // does Event Timing report it.
   unsigned executed_listener_or_default_action_ : 1;
 
-  // Whether preventDefault was called when |m_handlingPassive| is
-  // true. This field is reset on each call to setHandlingPassive.
+  // Whether preventDefault was called when |handling_passive_| is
+  // true. This field is reset on each call to SetHandlingPassive.
   unsigned prevent_default_called_during_passive_ : 1;
   // Whether preventDefault was called on uncancelable event.
   unsigned prevent_default_called_on_uncancelable_event_ : 1;
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index 5b4781f..a6710b3 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -305,7 +305,7 @@
                   ToMouseEvent(*event_).type() == EventTypeNames::click;
   if (is_click) {
     // Fire an accessibility event indicating a node was clicked on.  This is
-    // safe if m_event->target()->toNode() returns null.
+    // safe if event_->target()->ToNode() returns null.
     if (AXObjectCache* cache = node_->GetDocument().ExistingAXObjectCache())
       cache->HandleClicked(event_->target()->ToNode());
 
diff --git a/third_party/blink/renderer/core/dom/events/event_listener_map.h b/third_party/blink/renderer/core/dom/events/event_listener_map.h
index 35e4fa57..d2ee92d 100644
--- a/third_party/blink/renderer/core/dom/events/event_listener_map.h
+++ b/third_party/blink/renderer/core/dom/events/event_listener_map.h
@@ -37,7 +37,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener_options.h"
-#include "third_party/blink/renderer/core/events/registered_event_listener.h"
+#include "third_party/blink/renderer/core/dom/events/registered_event_listener.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable_visitor.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
diff --git a/third_party/blink/renderer/core/dom/events/event_path.cc b/third_party/blink/renderer/core/dom/events/event_path.cc
index 7aa7fc8..b5f52b1 100644
--- a/third_party/blink/renderer/core/dom/events/event_path.cc
+++ b/third_party/blink/renderer/core/dom/events/event_path.cc
@@ -96,7 +96,7 @@
   // For performance and memory usage reasons we want to store the
   // path using as few bytes as possible and with as few allocations
   // as possible which is why we gather the data on the stack before
-  // storing it in a perfectly sized m_nodeEventContexts Vector.
+  // storing it in a perfectly sized node_event_contexts_ Vector.
   HeapVector<Member<Node>, 64> nodes_in_path;
   Node* current = node_;
 
@@ -141,8 +141,8 @@
 
 void EventPath::CalculateTreeOrderAndSetNearestAncestorClosedTree() {
   // Precondition:
-  //   - TreeScopes in m_treeScopeEventContexts must be *connected* in the same
-  //     composed tree.
+  //   - TreeScopes in tree_scope_event_contexts_ must be *connected* in the
+  //     same composed tree.
   //   - The root tree must be included.
   TreeScopeEventContext* root_tree = nullptr;
   for (const auto& tree_scope_event_context : tree_scope_event_contexts_) {
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc
index 6c34fb3..c82f6aa9 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -199,7 +199,7 @@
 // because it will increase the size of EventTarget and all of its
 // subclasses with code that are mostly unnecessary for them,
 // resulting in a performance decrease.
-// We also don't use ImplementedAs=EventTargetImpl in EventTarget.idl
+// We also don't use ImplementedAs=EventTargetImpl in event_target.idl
 // because it will result in some complications with classes that are
 // currently derived from EventTarget.
 // Spec: https://dom.spec.whatwg.org/#dom-eventtarget-eventtarget
diff --git a/third_party/blink/renderer/core/events/registered_event_listener.cc b/third_party/blink/renderer/core/dom/events/registered_event_listener.cc
similarity index 97%
rename from third_party/blink/renderer/core/events/registered_event_listener.cc
rename to third_party/blink/renderer/core/dom/events/registered_event_listener.cc
index b281c84d..91efc19 100644
--- a/third_party/blink/renderer/core/events/registered_event_listener.cc
+++ b/third_party/blink/renderer/core/dom/events/registered_event_listener.cc
@@ -22,7 +22,7 @@
  *
  */
 
-#include "third_party/blink/renderer/core/events/registered_event_listener.h"
+#include "third_party/blink/renderer/core/dom/events/registered_event_listener.h"
 
 #include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
diff --git a/third_party/blink/renderer/core/events/registered_event_listener.h b/third_party/blink/renderer/core/dom/events/registered_event_listener.h
similarity index 92%
rename from third_party/blink/renderer/core/events/registered_event_listener.h
rename to third_party/blink/renderer/core/dom/events/registered_event_listener.h
index 9af2c759..ac6bb8c 100644
--- a/third_party/blink/renderer/core/events/registered_event_listener.h
+++ b/third_party/blink/renderer/core/dom/events/registered_event_listener.h
@@ -22,8 +22,8 @@
  *
  */
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_REGISTERED_EVENT_LISTENER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_REGISTERED_EVENT_LISTENER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_EVENTS_REGISTERED_EVENT_LISTENER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_EVENTS_REGISTERED_EVENT_LISTENER_H_
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
@@ -98,4 +98,4 @@
 
 WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::RegisteredEventListener);
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_REGISTERED_EVENT_LISTENER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_EVENTS_REGISTERED_EVENT_LISTENER_H_
diff --git a/third_party/blink/renderer/core/dom/mutation_observer.cc b/third_party/blink/renderer/core/dom/mutation_observer.cc
index 85793581..ac2ea235 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer.cc
+++ b/third_party/blink/renderer/core/dom/mutation_observer.cc
@@ -300,7 +300,7 @@
 void MutationObserver::Deliver() {
   DCHECK(!ShouldBeSuspended());
 
-  // Calling clearTransientRegistrations() can modify m_registrations, so it's
+  // Calling ClearTransientRegistrations() can modify registrations_, so it's
   // necessary to make a copy of the transient registrations before operating on
   // them.
   HeapVector<Member<MutationObserverRegistration>, 1> transient_registrations;
diff --git a/third_party/blink/renderer/core/dom/mutation_observer_registration.cc b/third_party/blink/renderer/core/dom/mutation_observer_registration.cc
index 836bb34..8f4cc21 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer_registration.cc
+++ b/third_party/blink/renderer/core/dom/mutation_observer_registration.cc
@@ -107,7 +107,7 @@
 }
 
 void MutationObserverRegistration::Unregister() {
-  // |this| can outlives m_registrationNode.
+  // |this| can outlives registration_node_.
   if (registration_node_)
     registration_node_->UnregisterMutationObserver(this);
   else
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 43b367d..cb642b0 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -2620,8 +2620,10 @@
                 static_cast<NodeFlags>(new_state);
   DCHECK(new_state == GetCustomElementState());
 
-  if (element->IsDefined() != was_defined)
+  if (element->IsDefined() != was_defined) {
     element->PseudoStateChanged(CSSSelector::kPseudoDefined);
+    element->PseudoStateChanged(CSSSelector::kPseudoUnresolved);
+  }
 }
 
 void Node::SetV0CustomElementState(V0CustomElementState new_state) {
@@ -2646,8 +2648,10 @@
   SetFlag(kV0CustomElementFlag);
   SetFlag(new_state == kV0Upgraded, kV0CustomElementUpgradedFlag);
 
-  if (old_state == kV0NotCustomElement || new_state == kV0Upgraded)
+  if (old_state == kV0NotCustomElement || new_state == kV0Upgraded) {
     ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoUnresolved);
+    ToElement(this)->PseudoStateChanged(CSSSelector::kPseudoDefined);
+  }
 }
 
 void Node::CheckSlotChange(SlotChangeType slot_change_type) {
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index 8acd2ce..20acc9c 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -38,7 +38,7 @@
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
 
-// This needs to be here because Element.cpp also depends on it.
+// This needs to be here because element.cc also depends on it.
 #define DUMP_NODE_STATISTICS 0
 
 namespace blink {
@@ -680,7 +680,7 @@
   }
 
   // -----------------------------------------------------------------------------
-  // Notification of document structure changes (see ContainerNode.h for more
+  // Notification of document structure changes (see container_node.h for more
   // notification methods)
   //
   // At first, Blinkt notifies the node that it has been inserted into the
diff --git a/third_party/blink/renderer/core/dom/node_rare_data.cc b/third_party/blink/renderer/core/dom/node_rare_data.cc
index db3b785..f340e29 100644
--- a/third_party/blink/renderer/core/dom/node_rare_data.cc
+++ b/third_party/blink/renderer/core/dom/node_rare_data.cc
@@ -114,7 +114,7 @@
   return *node_lists_;
 }
 
-// Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow
+// Ensure the 10 bits reserved for the connected_frame_count_ cannot overflow.
 static_assert(Page::kMaxNumberOfFrames <
                   (1 << NodeRareData::kConnectedFrameCountBits),
               "Frame limit should fit in rare data count");
diff --git a/third_party/blink/renderer/core/dom/node_rare_data.h b/third_party/blink/renderer/core/dom/node_rare_data.h
index dd4f747..394632d8 100644
--- a/third_party/blink/renderer/core/dom/node_rare_data.h
+++ b/third_party/blink/renderer/core/dom/node_rare_data.h
@@ -124,9 +124,9 @@
 
   void ClearNodeLists() { node_lists_.Clear(); }
   NodeListsNodeData* NodeLists() const { return node_lists_.Get(); }
-  // ensureNodeLists() and a following NodeListsNodeData functions must be
+  // EnsureNodeLists() and a following NodeListsNodeData functions must be
   // wrapped with a ThreadState::GCForbiddenScope in order to avoid an
-  // initialized m_nodeLists is cleared by NodeRareData::traceAfterDispatch().
+  // initialized node_lists_ is cleared by NodeRareData::TraceAfterDispatch().
   NodeListsNodeData& EnsureNodeLists() {
     DCHECK(ThreadState::Current()->IsGCForbidden());
     if (!node_lists_)
diff --git a/third_party/blink/renderer/core/dom/processing_instruction.cc b/third_party/blink/renderer/core/dom/processing_instruction.cc
index c20295e..bfe8e34 100644
--- a/third_party/blink/renderer/core/dom/processing_instruction.cc
+++ b/third_party/blink/renderer/core/dom/processing_instruction.cc
@@ -81,7 +81,7 @@
 }
 
 Node* ProcessingInstruction::Clone(Document& factory, CloneChildrenFlag) const {
-  // FIXME: Is it a problem that this does not copy m_localHref?
+  // FIXME: Is it a problem that this does not copy local_href_?
   // What about other data members?
   return Create(factory, target_, data_);
 }
diff --git a/third_party/blink/renderer/core/dom/qualified_name.h b/third_party/blink/renderer/core/dom/qualified_name.h
index 3b91481..ea0348d 100644
--- a/third_party/blink/renderer/core/dom/qualified_name.h
+++ b/third_party/blink/renderer/core/dom/qualified_name.h
@@ -79,8 +79,8 @@
       RefCounted<QualifiedNameImpl>::Release();
     }
 
-    // We rely on StringHasher's hashMemory clearing out the top 8 bits when
-    // doing hashing and use one of the bits for the m_isStatic value.
+    // We rely on StringHasher's HashMemory clearing out the top 8 bits when
+    // doing hashing and use one of the bits for the is_static_ value.
     mutable unsigned existing_hash_ : 24;
     unsigned is_static_ : 1;
     const AtomicString prefix_;
diff --git a/third_party/blink/renderer/core/dom/range.cc b/third_party/blink/renderer/core/dom/range.cc
index 2835ccb..7832ffd 100644
--- a/third_party/blink/renderer/core/dom/range.cc
+++ b/third_party/blink/renderer/core/dom/range.cc
@@ -603,9 +603,8 @@
   // delete all children of commonRoot between the start and end container
   Node* process_start = ChildOfCommonRootBeforeOffset(
       &original_start.Container(), original_start.Offset(), common_root);
-  if (process_start &&
-      original_start.Container() !=
-          common_root)  // processStart contains nodes before m_start.
+  // process_start contains nodes before start_.
+  if (process_start && original_start.Container() != common_root)
     process_start = process_start->nextSibling();
   Node* process_end = ChildOfCommonRootBeforeOffset(
       &original_end.Container(), original_end.Offset(), common_root);
diff --git a/third_party/blink/renderer/core/dom/range_boundary_point.h b/third_party/blink/renderer/core/dom/range_boundary_point.h
index 8c3a071c..1329ced 100644
--- a/third_party/blink/renderer/core/dom/range_boundary_point.h
+++ b/third_party/blink/renderer/core/dom/range_boundary_point.h
@@ -128,8 +128,8 @@
 
 inline const Position RangeBoundaryPoint::ToPosition() const {
   EnsureOffsetIsValid();
-  // TODO(yosin): We should return |Position::beforeAnchor| when
-  // |m_containerNode| isn't |Text| node.
+  // TODO(yosin): We should return |Position::BeforeAnchor| when
+  // |container_node_| isn't a |Text| node.
   return Position(container_node_.Get(), offset_in_container_);
 }
 
diff --git a/third_party/blink/renderer/core/dom/scripted_animation_controller.cc b/third_party/blink/renderer/core/dom/scripted_animation_controller.cc
index 5bdc1ac0..685a7871 100644
--- a/third_party/blink/renderer/core/dom/scripted_animation_controller.cc
+++ b/third_party/blink/renderer/core/dom/scripted_animation_controller.cc
@@ -57,7 +57,7 @@
 }
 
 void ScriptedAnimationController::Unpause() {
-  // It would be nice to put an DCHECK(m_suspendCount > 0) here, but in WK1
+  // It would be nice to put an DCHECK_GT(suspend_count_, 0) here, but in WK1
   // resume() can be called even when suspend hasn't (if a tab was created in
   // the background).
   if (suspend_count_ > 0)
diff --git a/third_party/blink/renderer/core/dom/shadow_root.cc b/third_party/blink/renderer/core/dom/shadow_root.cc
index 89ce359b..f2d5200 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root.cc
@@ -188,10 +188,10 @@
 
   GetDocument().GetSlotAssignmentEngine().Connected(*this);
 
-  // FIXME: When parsing <video controls>, insertedInto() is called many times
-  // without invoking removedFrom.  For now, we check
-  // m_registeredWithParentShadowroot. We would like to
-  // DCHECK(!m_registeredShadowRoot) here.
+  // FIXME: When parsing <video controls>, InsertedInto() is called many times
+  // without invoking RemovedFrom().  For now, we check
+  // registered_with_parent_shadow_root. We would like to
+  // DCHECK(!registered_with_parent_shadow_root) here.
   // https://bugs.webkit.org/show_bug.cig?id=101316
   if (registered_with_parent_shadow_root_)
     return kInsertionDone;
diff --git a/third_party/blink/renderer/core/dom/space_split_string.cc b/third_party/blink/renderer/core/dom/space_split_string.cc
index 38730b3..cfe0bdbc 100644
--- a/third_party/blink/renderer/core/dom/space_split_string.cc
+++ b/third_party/blink/renderer/core/dom/space_split_string.cc
@@ -207,8 +207,8 @@
 
 SpaceSplitString::Data::Data(const SpaceSplitString::Data& other)
     : RefCounted<Data>(), vector_(other.vector_) {
-  // Note that we don't copy m_keyString to indicate to the destructor that
-  // there's nothing to be removed from the sharedDataMap().
+  // Note that we don't copy key_string_ to indicate to the destructor that
+  // there's nothing to be removed from the SharedDataMap().
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/whitespace_attacher.cc b/third_party/blink/renderer/core/dom/whitespace_attacher.cc
index b3029b4f..cbaf455 100644
--- a/third_party/blink/renderer/core/dom/whitespace_attacher.cc
+++ b/third_party/blink/renderer/core/dom/whitespace_attacher.cc
@@ -20,7 +20,7 @@
 void WhitespaceAttacher::DidReattach(Node* node, LayoutObject* prev_in_flow) {
   DCHECK(node);
   DCHECK(node->IsTextNode() || node->IsElementNode());
-  // See Invariants in WhitespaceAttacher.h
+  // See Invariants in whitespace_attacher.h
   DCHECK(!last_display_contents_ || !last_text_node_needs_reattach_);
 
   ForceLastTextNodeNeedsReattach();
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.cc b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
index b4ba2f9..44e3d3cf 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
@@ -132,11 +132,11 @@
 void CaretDisplayItemClient::UpdateStyleAndLayoutIfNeeded(
     const PositionWithAffinity& caret_position) {
   // This method may be called multiple times (e.g. in partial lifecycle
-  // updates) before a paint invalidation. We should save m_previousLayoutBlock
-  // and m_visualRectInPreviousLayoutBlock only if they have not been saved
+  // updates) before a paint invalidation. We should save previous_layout_block_
+  // and visual_rect_in_previous_layout_block only if they have not been saved
   // since the last paint invalidation to ensure the caret painted in the
   // previous paint invalidated block will be invalidated. We don't care about
-  // intermediate changes of layoutBlock because they are not painted.
+  // intermediate changes of LayoutBlock because they are not painted.
   if (!previous_layout_block_) {
     previous_layout_block_ = layout_block_;
     visual_rect_in_previous_layout_block_ = visual_rect_;
@@ -152,7 +152,7 @@
       needs_paint_invalidation_ = true;
       if (new_layout_block == previous_layout_block_) {
         // The caret has disappeared and is reappearing in the same block,
-        // since the last paint invalidation. Set m_visualRect as if the caret
+        // since the last paint invalidation. Set visual_rect_ as if the caret
         // has always been there as paint invalidation doesn't care about the
         // intermediate changes.
         visual_rect_ = visual_rect_in_previous_layout_block_;
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.h b/third_party/blink/renderer/core/editing/caret_display_item_client.h
index 04dda7f..1fe23287 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.h
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.h
@@ -93,13 +93,13 @@
   LayoutRect local_rect_;
   LayoutBlock* layout_block_ = nullptr;
 
-  // Visual rect of the caret in m_layoutBlock. This is updated by
-  // invalidatePaintIfNeeded().
+  // Visual rect of the caret in layout_block_. This is updated by
+  // InvalidatePaintIfNeeded().
   LayoutRect visual_rect_;
 
-  // These are set to the previous value of m_layoutBlock and m_visualRect
-  // during updateStyleAndLayoutIfNeeded() if they haven't been set since the
-  // last paint invalidation. They can only be used in invalidatePaintIfNeeded()
+  // These are set to the previous value of layout_bloc_k and visual_rect_
+  // during UpdateStyleAndLayoutIfNeeded() if they haven't been set since the
+  // last paint invalidation. They can only be used in InvalidatePaintIfNeeded()
   // to invalidate the caret in the previous layout block.
   const LayoutBlock* previous_layout_block_ = nullptr;
   LayoutRect visual_rect_in_previous_layout_block_;
diff --git a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
index c02f371..e3f5775 100644
--- a/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/apply_style_command.cc
@@ -1088,8 +1088,8 @@
        node = NodeTraversal::Next(*node)) {
     if (node->hasChildren())
       continue;
-    // We don't consider m_isInlineElementToRemoveFunction here because we never
-    // apply style when m_isInlineElementToRemoveFunction is specified
+    // We don't consider is_inline_element_to_remove_function_ here because we
+    // never apply style when is_inline_element_to_remove_function_ is specified
     if (!style->StyleIsPresentInComputedStyleOfNode(node))
       return true;
     if (styled_inline_element_ &&
diff --git a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
index 5b58183..be93026 100644
--- a/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/delete_selection_command.cc
@@ -739,12 +739,12 @@
           if (downstream_end_.ComputeEditingOffset() > 0) {
             DeleteTextFromNode(text, 0, downstream_end_.ComputeEditingOffset());
           }
-          // Remove children of m_downstreamEnd.anchorNode() that come after
-          // m_upstreamStart. Don't try to remove children if m_upstreamStart
-          // was inside m_downstreamEnd.anchorNode() and m_upstreamStart has
+          // Remove children of downstream_end_.AnchorNode() that come after
+          // upstream_start_. Don't try to remove children if upstream_start_
+          // was inside downstream_end_.AnchorNode() and upstream_start_ has
           // been removed from the document, because then we don't know how many
           // children to remove.
-          // FIXME: Make m_upstreamStart a position we update as we remove
+          // FIXME: Make upstream_start_ a position we update as we remove
           // content, then we can always know which children to remove.
         } else if (!(start_node_was_descendant_of_end_node &&
                      !upstream_start_.IsConnected())) {
@@ -836,7 +836,7 @@
       CreateVisiblePosition(downstream_end_);
   VisiblePosition merge_destination = CreateVisiblePosition(upstream_start_);
 
-  // m_downstreamEnd's block has been emptied out by deletion.  There is no
+  // downstream_end_'s block has been emptied out by deletion.  There is no
   // content inside of it to move, so just remove it.
   Element* end_block = EnclosingBlock(downstream_end_.AnchorNode());
   if (!end_block ||
@@ -850,7 +850,7 @@
   RelocatablePosition relocatable_start(
       start_of_paragraph_to_move.DeepEquivalent());
 
-  // We need to merge into m_upstreamStart's block, but it's been emptied out
+  // We need to merge into upstream_start_'s block, but it's been emptied out
   // and collapsed by deletion.
   if (!merge_destination.DeepEquivalent().AnchorNode() ||
       (!merge_destination.DeepEquivalent().AnchorNode()->IsDescendantOf(
@@ -987,14 +987,14 @@
   if (end_table_row_ && end_table_row_->isConnected() &&
       end_table_row_ != start_table_row_) {
     if (IsTableRowEmpty(end_table_row_.Get())) {
-      // Don't remove m_endTableRow if it's where we're putting the ending
+      // Don't remove end_table_row_ if it's where we're putting the ending
       // selection.
       if (!ending_position_.AnchorNode()->IsDescendantOf(
               end_table_row_.Get())) {
-        // FIXME: We probably shouldn't remove m_endTableRow unless it's
+        // FIXME: We probably shouldn't remove end_table_row_ unless it's
         // fully selected, even if it is empty. We'll need to start
         // adjusting the selection endpoints during deletion to know
-        // whether or not m_endTableRow was fully selected here.
+        // whether or not end_table_row_ was fully selected here.
         CompositeEditCommand::RemoveNode(end_table_row_.Get(), editing_state);
         if (editing_state->IsAborted())
           return;
@@ -1187,7 +1187,7 @@
       if (editing_state->IsAborted())
         return;
     }
-    // handleGeneralDelete cause DOM mutation events so |m_endingPosition|
+    // HandleGeneralDelete cause DOM mutation events so |ending_position_|
     // can be out of document.
     if (ending_position_.IsConnected()) {
       InsertNodeAt(placeholder, ending_position_, editing_state);
@@ -1238,9 +1238,9 @@
 InputEvent::InputType DeleteSelectionCommand::GetInputType() const {
   // |DeleteSelectionCommand| could be used with Cut, Menu Bar deletion and
   // |TypingCommand|.
-  // 1. Cut and Menu Bar deletion should rely on correct |m_inputType|.
-  // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could
-  // default to |InputType::None|.
+  // 1. Cut and Menu Bar deletion should rely on correct |input_type_|.
+  // 2. |TypingCommand| will supply the |GetInputType()|, so |input_type_| could
+  // default to |InputType::kNone|.
   return input_type_;
 }
 
diff --git a/third_party/blink/renderer/core/editing/commands/edit_command.h b/third_party/blink/renderer/core/editing/commands/edit_command.h
index d5456510..e6bff029 100644
--- a/third_party/blink/renderer/core/editing/commands/edit_command.h
+++ b/third_party/blink/renderer/core/editing/commands/edit_command.h
@@ -51,7 +51,7 @@
   // The |EditingState*| argument must not be nullptr.
   virtual void DoApply(EditingState*) = 0;
 
-  // |TypingCommand| will return the text of the last |m_commands|.
+  // |TypingCommand| will return the text of the last |commands_|.
   virtual String TextDataForInputEvent() const;
 
   virtual void Trace(blink::Visitor*);
diff --git a/third_party/blink/renderer/core/editing/commands/editor_command.cc b/third_party/blink/renderer/core/editing/commands/editor_command.cc
index 9411126..eb76d97 100644
--- a/third_party/blink/renderer/core/editing/commands/editor_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/editor_command.cc
@@ -1281,7 +1281,7 @@
   static const EditorInternalCommand kEditorCommands[] = {
       // Lists all commands in blink::WebEditingCommandType.
       // Must be ordered by |commandType| for index lookup.
-      // Covered by unit tests in EditingCommandTest.cpp
+      // Covered by unit tests in editing_command_test.cc
       {WebEditingCommandType::kAlignJustified, ExecuteJustifyFull,
        SupportedFromMenuOrKeyBinding, EnabledInRichlyEditableText, StateNone,
        ValueStateOrNull, kNotTextInsertion, CanNotExecuteWhenDisabled},
diff --git a/third_party/blink/renderer/core/editing/commands/editor_command_names.h b/third_party/blink/renderer/core/editing/commands/editor_command_names.h
index 6be74053..95e822f 100644
--- a/third_party/blink/renderer/core/editing/commands/editor_command_names.h
+++ b/third_party/blink/renderer/core/editing/commands/editor_command_names.h
@@ -8,7 +8,7 @@
 namespace blink {
 
 // Must be ordered in a case-folding manner for binary search. Covered by unit
-// tests in EditingCommandTest.cpp (not able to use static_assert)
+// tests in editing_command_test.cc (not able to use static_assert)
 #define FOR_EACH_BLINK_EDITING_COMMAND_NAME(V)    \
   V(AlignCenter)                                  \
   V(AlignJustified)                               \
diff --git a/third_party/blink/renderer/core/editing/commands/insert_list_command.cc b/third_party/blink/renderer/core/editing/commands/insert_list_command.cc
index 0213285..625716e 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_list_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_list_command.cc
@@ -363,8 +363,8 @@
     DCHECK(HasEditableStyle(*list_element));
     DCHECK(HasEditableStyle(*list_element->parentNode()));
     if (!list_element->HasTagName(list_tag)) {
-      // |listChildNode| will be removed from the list and a list of type
-      // |m_type| will be created.
+      // |list_child_node| will be removed from the list and a list of type
+      // |type_| will be created.
       switch_list_type = true;
     }
 
diff --git a/third_party/blink/renderer/core/editing/commands/insert_text_command.cc b/third_party/blink/renderer/core/editing/commands/insert_text_command.cc
index 3f47650..c843bc3b 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_text_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_text_command.cc
@@ -248,7 +248,7 @@
     if (placeholder.IsNotNull())
       RemovePlaceholderAt(placeholder);
   } else {
-    // Make sure the document is set up to receive m_text
+    // Make sure the document is set up to receive text_
     start_position = PositionInsideTextNode(start_position, editing_state);
     if (editing_state->IsAborted())
       return;
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
index 3b4d2fb..d39ff1b 100644
--- a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
@@ -804,7 +804,7 @@
     const {
   // TODO(editing-dev): Hoist the call and change it into a DCHECK.
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
-  // TODO(yosin): We should set |m_endOfInsertedContent| not in SELECT
+  // TODO(yosin): We should set |end_of_inserted_content_| not in SELECT
   // element, since contents of SELECT elements, e.g. OPTION, OPTGROUP, are
   // not editable, or SELECT element is an atomic on editing.
   HTMLSelectElement* enclosing_select = ToHTMLSelectElement(
@@ -956,7 +956,7 @@
 
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
 
-  // Merging forward will remove m_endOfInsertedContent from the document.
+  // Merging forward will remove end_of_inserted_content from the document.
   if (merge_forward) {
     const VisibleSelection& visible_selection = EndingVisibleSelection();
     if (start_of_inserted_content_.IsOrphan()) {
@@ -964,8 +964,8 @@
           visible_selection.VisibleStart().DeepEquivalent();
     }
     end_of_inserted_content_ = visible_selection.VisibleEnd().DeepEquivalent();
-    // If we merged text nodes, m_endOfInsertedContent could be null. If
-    // this is the case, we use m_startOfInsertedContent.
+    // If we merged text nodes, end_of_inserted_content_ could be null. If
+    // this is the case, we use start_of_inserted_content_.
     if (end_of_inserted_content_.IsNull())
       end_of_inserted_content_ = start_of_inserted_content_;
   }
@@ -1723,8 +1723,8 @@
       InsertNodeAfter(node, end_node, editing_state);
       if (editing_state->IsAborted())
         return;
-      // Make sure that |updateNodesInserted| does not change
-      // |m_startOfInsertedContent|.
+      // Make sure that |UpdateNodesInserted| does not change
+      // |start_of_inserted_content|.
       DCHECK(start_of_inserted_content_.IsNotNull());
       UpdateNodesInserted(node);
     }
@@ -1761,9 +1761,9 @@
     } else {
       Text* node = GetDocument().CreateEditingTextNode(
           collapse_white_space ? NonBreakingSpaceString() : " ");
-      // Don't updateNodesInserted. Doing so would set m_endOfInsertedContent to
-      // be the node containing the leading space, but m_endOfInsertedContent is
-      // supposed to mark the end of pasted content.
+      // Don't UpdateNodesInserted. Doing so would set end_of_inserted_content_
+      // to be the node containing the leading space, but
+      // end_of_inserted_content_ issupposed to mark the end of pasted content.
       InsertNodeBefore(node, start_node, editing_state);
       if (editing_state->IsAborted())
         return;
@@ -1921,9 +1921,9 @@
 InputEvent::InputType ReplaceSelectionCommand::GetInputType() const {
   // |ReplaceSelectionCommand| could be used with Paste, Drag&Drop,
   // InsertFragment and |TypingCommand|.
-  // 1. Paste, Drag&Drop, InsertFragment should rely on correct |m_inputType|.
-  // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could
-  //    default to |InputType::None|.
+  // 1. Paste, Drag&Drop, InsertFragment should rely on correct |input_type_|.
+  // 2. |TypingCommand| will supply the |GetInputType()|, so |input_type_| could
+  //    default to |InputType::kNone|.
   return input_type_;
 }
 
diff --git a/third_party/blink/renderer/core/editing/commands/smart_replace_icu.cc b/third_party/blink/renderer/core/editing/commands/smart_replace_icu.cc
index 759a1c2..496d5a9 100644
--- a/third_party/blink/renderer/core/editing/commands/smart_replace_icu.cc
+++ b/third_party/blink/renderer/core/editing/commands/smart_replace_icu.cc
@@ -43,8 +43,9 @@
     uset_add(smart_set, string[i]);
 }
 
-// This is mostly a port of the code in WebCore/editing/SmartReplaceCF.cpp
-// except we use icu in place of CoreFoundations character classes.
+// This is mostly a port of the code in
+// core/editing/commands/smart_replace_cf.cc except we use icu in place of
+// CoreFoundations character classes.
 static USet* GetSmartSet(bool is_previous_character) {
   static USet* pre_smart_set = nullptr;
   static USet* post_smart_set = nullptr;
diff --git a/third_party/blink/renderer/core/editing/dom_selection.h b/third_party/blink/renderer/core/editing/dom_selection.h
index 2b68830..430adb0 100644
--- a/third_party/blink/renderer/core/editing/dom_selection.h
+++ b/third_party/blink/renderer/core/editing/dom_selection.h
@@ -112,7 +112,7 @@
   void UpdateFrameSelection(const SelectionInDOMTree&,
                             Range*,
                             const SetSelectionOptions&) const;
-  // Convenience methods for accessors, does not check m_frame present.
+  // Convenience methods for accessors, does not check owner Frame presence.
   VisibleSelection GetVisibleSelection() const;
   bool IsBaseFirstInSelection() const;
   const Position& AnchorPosition() const;
diff --git a/third_party/blink/renderer/core/editing/editing_strategy.cc b/third_party/blink/renderer/core/editing/editing_strategy.cc
index 7a7cca6..2995371 100644
--- a/third_party/blink/renderer/core/editing/editing_strategy.cc
+++ b/third_party/blink/renderer/core/editing/editing_strategy.cc
@@ -40,7 +40,7 @@
     return 0;
 
   // editingIgnoresContent uses the same logic in
-  // isEmptyNonEditableNodeInEditable (EditingUtilities.cpp). We don't
+  // IsEmptyNonEditableNodeInEditable (editing_utilities.cc). We don't
   // understand why this function returns 1 even when the node doesn't have
   // children.
   return 1;
diff --git a/third_party/blink/renderer/core/editing/editing_style.cc b/third_party/blink/renderer/core/editing/editing_style.cc
index 75fba860..44dfbd757 100644
--- a/third_party/blink/renderer/core/editing/editing_style.cc
+++ b/third_party/blink/renderer/core/editing/editing_style.cc
@@ -271,7 +271,7 @@
     : HTMLElementEquivalent(CSSPropertyTextDecorationLine,
                             primitive_value,
                             tag_name)
-// m_propertyID is used in HTMLElementEquivalent::addToStyle
+// CSSPropertyTextDecorationLine is used in HTMLElementEquivalent::AddToStyle
 {}
 
 bool HTMLTextDecorationEquivalent::PropertyExistsInStyle(
diff --git a/third_party/blink/renderer/core/editing/ephemeral_range.h b/third_party/blink/renderer/core/editing/ephemeral_range.h
index d51b650..c247e84e 100644
--- a/third_party/blink/renderer/core/editing/ephemeral_range.h
+++ b/third_party/blink/renderer/core/editing/ephemeral_range.h
@@ -33,7 +33,7 @@
 };
 
 // This class acts like |TraversalNextIterator| but in addition
-// it allows to set current position and checks |m_current| pointer before
+// it allows to set current position and checks |current_| pointer before
 // dereferencing.
 template <class TraversalNext>
 class CheckedTraversalNextIterator
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder.cc b/third_party/blink/renderer/core/editing/finder/text_finder.cc
index 95c4e1d1..408dab3 100644
--- a/third_party/blink/renderer/core/editing/finder/text_finder.cc
+++ b/third_party/blink/renderer/core/editing/finder/text_finder.cc
@@ -497,7 +497,7 @@
     }
 
     // If the Find function found a match it will have stored where the
-    // match was found in m_activeSelectionRect on the current frame. If we
+    // match was found in active_selection_rect_ on the current frame. If we
     // find this rect during scoping it means we have found the active
     // tickmark.
     bool found_active_match = false;
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder.h b/third_party/blink/renderer/core/editing/finder/text_finder.h
index 65c7c6b..da148edf 100644
--- a/third_party/blink/renderer/core/editing/finder/text_finder.h
+++ b/third_party/blink/renderer/core/editing/finder/text_finder.h
@@ -242,7 +242,7 @@
 
   // Keeps track of how many matches this frame has found so far, so that we
   // don't lose count between scoping efforts, and is also used (in conjunction
-  // with m_lastSearchString) to figure out if we need to search the frame
+  // with last_search_string_) to figure out if we need to search the frame
   // again.
   int last_match_count_;
 
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index ed727836..ea47d4f 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -242,7 +242,7 @@
   if (!GetSelectionInDOMTree().IsNone() && !options.DoNotSetFocus()) {
     SetFocusedNodeIfNeeded();
     // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and
-    // "FocusIn", |m_frame| may associate to another document.
+    // "FocusIn", |frame_| may associate to another document.
     if (!IsAvailable() || GetDocument() != current_document) {
       // Once we get test case to reach here, we should change this
       // if-statement to |DCHECK()|.
@@ -1123,7 +1123,7 @@
 }
 
 GranularityStrategy* FrameSelection::GetGranularityStrategy() {
-  // We do lazy initalization for m_granularityStrategy, because if we
+  // We do lazy initialization for granularity_strategy_, because if we
   // initialize it right in the constructor - the correct settings may not be
   // set yet.
   SelectionStrategy strategy_type = SelectionStrategy::kCharacter;
diff --git a/third_party/blink/renderer/core/editing/granularity_strategy_test.cc b/third_party/blink/renderer/core/editing/granularity_strategy_test.cc
index d743e32..6542a2d 100644
--- a/third_party/blink/renderer/core/editing/granularity_strategy_test.cc
+++ b/third_party/blink/renderer/core/editing/granularity_strategy_test.cc
@@ -43,7 +43,7 @@
 
   Text* AppendTextNode(const String& data);
   void SetInnerHTML(const char*);
-  // Parses the text node, appending the info to m_letterPos and m_wordMiddles.
+  // Parses the text node, appending the info to letter_pos_ and word_middles_.
   void ParseText(Text*);
   void ParseText(const TextNodeVector&);
 
@@ -74,7 +74,7 @@
   // tested.
   Vector<IntPoint> letter_pos_;
   // Pixel coordinates of the middles of the words in the text being tested.
-  // (y coordinate is based on y coordinates of m_letterPos)
+  // (y coordinate is based on y coordinates of letter_pos_)
   Vector<IntPoint> word_middles_;
 };
 
diff --git a/third_party/blink/renderer/core/editing/ime/input_method_controller.cc b/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
index efd30e68..a4f43ae 100644
--- a/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
+++ b/third_party/blink/renderer/core/editing/ime/input_method_controller.cc
@@ -181,7 +181,7 @@
     case TypingCommand::TextCompositionType::kTextCompositionCancel:
       // TODO(chongz): Use TypingCommand::insertText after TextEvent was
       // removed. (Removed from spec since 2012)
-      // See TextEvent.idl.
+      // See text_event.idl.
       frame.GetEventHandler().HandleTextInputEvent(text, nullptr,
                                                    kTextEventInputComposition);
       break;
diff --git a/third_party/blink/renderer/core/editing/inline_box_position.h b/third_party/blink/renderer/core/editing/inline_box_position.h
index a95fd02..464ff16 100644
--- a/third_party/blink/renderer/core/editing/inline_box_position.h
+++ b/third_party/blink/renderer/core/editing/inline_box_position.h
@@ -83,7 +83,7 @@
 
 // The print for |InlineBoxPosition| is available only for testing
 // in "webkit_unit_tests", and implemented in
-// "core/editing/InlineBoxPositionTest.cpp".
+// "core/editing/inline_box_position_test.cc".
 std::ostream& operator<<(std::ostream&, const InlineBoxPosition&);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/iterators/character_iterator.cc b/third_party/blink/renderer/core/editing/iterators/character_iterator.cc
index 4bccec3..cfff0d204 100644
--- a/third_party/blink/renderer/core/editing/iterators/character_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/character_iterator.cc
@@ -138,7 +138,7 @@
 
   at_break_ = false;
 
-  // easy if there is enough left in the current m_textIterator run
+  // easy if there is enough left in the current text_iterator_ run
   int remaining = text_iterator_.length() - run_offset_;
   if (count < remaining) {
     run_offset_ += count;
@@ -146,30 +146,30 @@
     return;
   }
 
-  // exhaust the current m_textIterator run
+  // exhaust the current text_iterator_ run
   count -= remaining;
   offset_ += remaining;
 
-  // move to a subsequent m_textIterator run
+  // move to a subsequent text_iterator_ run
   for (text_iterator_.Advance(); !AtEnd(); text_iterator_.Advance()) {
     int run_length = text_iterator_.length();
     if (!run_length) {
       at_break_ = text_iterator_.BreaksAtReplacedElement();
     } else {
-      // see whether this is m_textIterator to use
+      // see whether this is text_iterator_ to use
       if (count < run_length) {
         run_offset_ = count;
         offset_ += count;
         return;
       }
 
-      // exhaust this m_textIterator run
+      // exhaust this text_iterator_ run
       count -= run_length;
       offset_ += run_length;
     }
   }
 
-  // ran to the end of the m_textIterator... no more runs left
+  // ran to the end of the text_iterator_... no more runs left
   at_break_ = true;
   run_offset_ = 0;
 }
diff --git a/third_party/blink/renderer/core/editing/iterators/fully_clipped_state_stack.cc b/third_party/blink/renderer/core/editing/iterators/fully_clipped_state_stack.cc
index 4955e7f..d19897a 100644
--- a/third_party/blink/renderer/core/editing/iterators/fully_clipped_state_stack.cc
+++ b/third_party/blink/renderer/core/editing/iterators/fully_clipped_state_stack.cc
@@ -53,10 +53,10 @@
     const Node* node) {
   DCHECK_EQ(size(), DepthCrossingShadowBoundaries<Strategy>(*node));
 
-  // FIXME: m_fullyClippedStack was added in response to
+  // FIXME: fully_clipped_stack_ was added in response to
   // <https://bugs.webkit.org/show_bug.cgi?id=26364> ("Search can find text
   // that's hidden by overflow:hidden"), but the logic here will not work
-  // correctly if a shadow tree redistributes nodes. m_fullyClippedStack relies
+  // correctly if a shadow tree redistributes nodes. fully_clipped_stack_ relies
   // on the assumption that DOM node hierarchy matches the layout tree, which is
   // not necessarily true if there happens to be shadow DOM distribution or
   // other mechanics that shuffle around the layout objects regardless of node
diff --git a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h
index 203fa7b..8653c5c 100644
--- a/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h
+++ b/third_party/blink/renderer/core/editing/iterators/simplified_backwards_text_iterator.h
@@ -127,14 +127,14 @@
   Member<const Node> end_node_;
   int end_offset_;
 
-  // Whether m_node has advanced beyond the iteration range (i.e. m_startNode).
+  // Whether m_node has advanced beyond the iteration range (i.e. start_node_).
   bool have_passed_start_node_;
 
   // Should handle first-letter layoutObject in the next call to handleTextNode.
   bool should_handle_first_letter_;
 
-  // Used when m_stopsOnFormControls is set to determine if the iterator should
-  // keep advancing.
+  // Used when behavior_.StopOnFormControls() is true to determine if the
+  // iterator should keep advancing.
   bool should_stop_;
 };
 
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
index 04e4ee88..a81a347 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
@@ -132,7 +132,7 @@
   return nullptr;
 }
 
-// Figure out the initial value of m_shadowDepth: the depth of startContainer's
+// Figure out the initial value of shadow_depth_: the depth of start_container's
 // tree scope from the common ancestor tree scope.
 template <typename Strategy>
 unsigned ShadowDepthOf(const Node& start_container, const Node& end_container);
@@ -241,12 +241,12 @@
 bool TextIteratorAlgorithm<Strategy>::HandleRememberedProgress() {
   // Handle remembered node that needed a newline after the text node's newline
   if (needs_another_newline_) {
-    // Emit the extra newline, and position it *inside* m_node, after m_node's
+    // Emit the extra newline, and position it *inside* node_, after node_'s
     // contents, in case it's a block, in the same way that we position the
     // first newline. The range for the emitted newline should start where the
     // line break begins.
     // FIXME: It would be cleaner if we emitted two newlines during the last
-    // iteration, instead of using m_needsAnotherNewline.
+    // iteration, instead of using needs_another_newline_.
     Node* last_child = Strategy::LastChild(*node_);
     const Node* base_node = last_child ? last_child : node_.Get();
     EmitChar16AfterNode('\n', *base_node);
@@ -645,7 +645,7 @@
   return node->HasTagName(pTag);
 }
 
-// Whether or not we should emit a character as we enter m_node (if it's a
+// Whether or not we should emit a character as we enter node_ (if it's a
 // container) or as we hit it (if it's atomic).
 template <typename Strategy>
 bool TextIteratorAlgorithm<Strategy>::ShouldRepresentNodeOffsetZero() {
@@ -676,15 +676,15 @@
     return false;
 
   // If we are outside the start container's subtree, assume we need to emit.
-  // FIXME: m_startContainer could be an inline block
+  // FIXME: start_container_ could be an inline block
   if (!Strategy::IsDescendantOf(*node_, *start_container_))
     return true;
 
-  // If we started as m_startContainer offset 0 and the current node is a
+  // If we started as start_container_ offset 0 and the current node is a
   // descendant of the start container, we already had enough context to
   // correctly decide whether to emit after a preceding block. We chose not to
-  // emit (m_hasEmitted is false), so don't second guess that now.
-  // NOTE: Is this really correct when m_node is not a leftmost descendant?
+  // emit (has_emitted_ is false), so don't second guess that now.
+  // NOTE: Is this really correct when node_ is not a leftmost descendant?
   // Probably immaterial since we likely would have already emitted something by
   // now.
   if (!start_offset_)
@@ -727,15 +727,15 @@
 
 template <typename Strategy>
 void TextIteratorAlgorithm<Strategy>::RepresentNodeOffsetZero() {
-  // Emit a character to show the positioning of m_node.
+  // Emit a character to show the positioning of node_.
 
   // TODO(editing-dev): We should rewrite this below code fragment to utilize
   // early-return style.
   // When we haven't been emitting any characters,
-  // shouldRepresentNodeOffsetZero() can create VisiblePositions, which is
+  // ShouldRepresentNodeOffsetZero() can create VisiblePositions, which is
   // expensive. So, we perform the inexpensive checks on m_node to see if it
   // necessitates emitting a character first and will early return before
-  // encountering shouldRepresentNodeOffsetZero()s worse case behavior.
+  // encountering ShouldRepresentNodeOffsetZero()s worse case behavior.
   if (ShouldEmitTabBeforeNode(*node_)) {
     if (ShouldRepresentNodeOffsetZero())
       EmitChar16BeforeNode('\t', *node_);
@@ -763,18 +763,18 @@
 void TextIteratorAlgorithm<Strategy>::ExitNode() {
   // prevent emitting a newline when exiting a collapsed block at beginning of
   // the range
-  // FIXME: !m_hasEmitted does not necessarily mean there was a collapsed
+  // FIXME: !has_emitted_ does not necessarily mean there was a collapsed
   // block... it could have been an hr (e.g.). Also, a collapsed block could
   // have height (e.g. a table) and therefore look like a blank line.
   if (!text_state_.HasEmitted())
     return;
 
-  // Emit with a position *inside* m_node, after m_node's contents, in
+  // Emit with a position *inside* node_, after node_'s contents, in
   // case it is a block, because the run should start where the
   // emitted character is positioned visually.
   Node* last_child = Strategy::LastChild(*node_);
   const Node* base_node = last_child ? last_child : node_.Get();
-  // FIXME: This shouldn't require the m_lastTextNode to be true, but we can't
+  // FIXME: This shouldn't require the last_text_node to be true, but we can't
   // change that without making the logic in _web_attributedStringFromRange
   // match. We'll get that for free when we switch to use TextIterator in
   // _web_attributedStringFromRange. See <rdar://problem/5428427> for an example
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.h b/third_party/blink/renderer/core/editing/iterators/text_iterator.h
index 51fd761..b2c2091ec 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.h
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.h
@@ -215,7 +215,7 @@
   const unsigned start_offset_;
   const Member<const Node> end_container_;
   const unsigned end_offset_;
-  // |m_endNode| stores |Strategy::childAt(*m_endContainer, m_endOffset - 1)|,
+  // |end_node_| stores |Strategy::ChildAt(*end_container_, end_offfset_ - 1)|,
   // if it exists, or |nullptr| otherwise.
   const Member<const Node> end_node_;
   const Member<const Node> past_end_node_;
diff --git a/third_party/blink/renderer/core/editing/local_caret_rect.h b/third_party/blink/renderer/core/editing/local_caret_rect.h
index 3a9c15a..e8e8dc7 100644
--- a/third_party/blink/renderer/core/editing/local_caret_rect.h
+++ b/third_party/blink/renderer/core/editing/local_caret_rect.h
@@ -49,7 +49,7 @@
 CORE_EXPORT IntRect AbsoluteSelectionBoundsOf(const VisiblePosition&);
 CORE_EXPORT IntRect AbsoluteSelectionBoundsOf(const VisiblePositionInFlatTree&);
 
-// Exposed to tests only. Implemented in LocalCaretRectTest.cpp.
+// Exposed to tests only. Implemented in local_caret_rect_test.cc.
 bool operator==(const LocalCaretRect&, const LocalCaretRect&);
 std::ostream& operator<<(std::ostream&, const LocalCaretRect&);
 
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
index cfad8dd..8c5ce7f5 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
@@ -265,19 +265,19 @@
 
     DocumentMarker* const new_marker = create_marker_from_offsets(
         start_offset_in_current_container, end_offset_in_current_container);
-    AddMarkerToNode(node, new_marker);
+    AddMarkerToNode(ToText(node), new_marker);
   }
 }
 
-void DocumentMarkerController::AddMarkerToNode(const Node& node,
+void DocumentMarkerController::AddMarkerToNode(const Text& text,
                                                DocumentMarker* new_marker) {
-  DCHECK_GE(ToText(node).length(), new_marker->EndOffset());
+  DCHECK_GE(text.length(), new_marker->EndOffset());
   possibly_existing_marker_types_ = possibly_existing_marker_types_.Add(
       DocumentMarker::MarkerTypes(new_marker->GetType()));
   SetContext(document_);
 
   Member<MarkerLists>& markers =
-      markers_.insert(&node, nullptr).stored_value->value;
+      markers_.insert(&text, nullptr).stored_value->value;
   if (!markers) {
     markers = new MarkerLists;
     markers->Grow(DocumentMarker::kMarkerTypeIndexesCount);
@@ -290,7 +290,7 @@
   DocumentMarkerList* const list = ListForType(markers, new_marker_type);
   list->Add(new_marker);
 
-  InvalidatePaintForNode(node);
+  InvalidatePaintForNode(text);
 }
 
 // Moves markers from src_node to dst_node. Markers are moved if their start
@@ -340,6 +340,9 @@
     unsigned start_offset,
     int length,
     DocumentMarker::MarkerTypes marker_types) {
+  // TODO(yoichio): Make this function to take Text instead of Node.
+  if (!node.IsTextNode())
+    return;
   if (length <= 0)
     return;
 
@@ -347,7 +350,7 @@
     return;
   DCHECK(!(markers_.IsEmpty()));
 
-  MarkerLists* const markers = markers_.at(&node);
+  MarkerLists* const markers = markers_.at(&ToText(node));
   if (!markers)
     return;
 
@@ -374,7 +377,7 @@
   }
 
   if (empty_lists_count == DocumentMarker::kMarkerTypeIndexesCount) {
-    markers_.erase(&node);
+    markers_.erase(&ToText(node));
     if (markers_.IsEmpty()) {
       possibly_existing_marker_types_ = DocumentMarker::MarkerTypes();
       SetContext(nullptr);
@@ -439,7 +442,9 @@
       range.EndPosition().ComputeOffsetInContainerNode();
 
   for (Node& node : range.Nodes()) {
-    MarkerLists* const markers = markers_.at(&node);
+    if (!node.IsTextNode())
+      continue;
+    MarkerLists* const markers = markers_.at(&ToText(node));
     if (!markers)
       continue;
 
@@ -628,7 +633,10 @@
 
 void DocumentMarkerController::InvalidateRectsForTextMatchMarkersInNode(
     const Node& node) {
-  MarkerLists* markers = markers_.at(&node);
+  // TODO(yoichio): Make this function to take Text instead of Node.
+  if (!node.IsTextNode())
+    return;
+  MarkerLists* markers = markers_.at(&ToText(node));
 
   const DocumentMarkerList* const marker_list =
       ListForType(markers, DocumentMarker::kTextMatch);
@@ -645,7 +653,7 @@
 
 void DocumentMarkerController::InvalidateRectsForAllTextMatchMarkers() {
   for (auto& node_markers : markers_) {
-    const Node& node = *node_markers.key;
+    const Text& node = *node_markers.key;
     InvalidateRectsForTextMatchMarkersInNode(node);
   }
 }
@@ -669,11 +677,14 @@
 void DocumentMarkerController::RemoveMarkersForNode(
     const Node* node,
     DocumentMarker::MarkerTypes marker_types) {
+  // TODO(yoichio): Make this function to take Text instead of Node.
+  if (!node->IsTextNode())
+    return;
   if (!PossiblyHasMarkers(marker_types))
     return;
   DCHECK(!markers_.IsEmpty());
 
-  MarkerMap::iterator iterator = markers_.find(node);
+  MarkerMap::iterator iterator = markers_.find(ToText(node));
   if (iterator != markers_.end())
     RemoveMarkersFromList(iterator, marker_types);
 }
@@ -681,18 +692,16 @@
 void DocumentMarkerController::RemoveSpellingMarkersUnderWords(
     const Vector<String>& words) {
   for (auto& node_markers : markers_) {
-    const Node& node = *node_markers.key;
-    if (!node.IsTextNode())
-      continue;
+    const Text& text = *node_markers.key;
     MarkerLists* markers = node_markers.value;
     for (DocumentMarker::MarkerType type :
          DocumentMarker::MarkerTypes::Misspelling()) {
       DocumentMarkerList* const list = ListForType(markers, type);
       if (!list)
         continue;
-      if (ToSpellCheckMarkerListImpl(list)->RemoveMarkersUnderWords(
-              ToText(node).data(), words)) {
-        InvalidatePaintForNode(node);
+      if (ToSpellCheckMarkerListImpl(list)->RemoveMarkersUnderWords(text.data(),
+                                                                    words)) {
+        InvalidatePaintForNode(text);
       }
     }
   }
@@ -700,7 +709,10 @@
 
 void DocumentMarkerController::RemoveSuggestionMarkerByTag(const Node* node,
                                                            int32_t marker_tag) {
-  MarkerLists* markers = markers_.at(node);
+  // TODO(yoichio): Make this function to take Text instead of Node.
+  if (!node->IsTextNode())
+    return;
+  MarkerLists* markers = markers_.at(ToText(node));
   SuggestionMarkerListImpl* const list = ToSuggestionMarkerListImpl(
       ListForType(markers, DocumentMarker::kSuggestion));
   if (!list->RemoveMarkerByTag(marker_tag))
@@ -714,7 +726,7 @@
     return;
   DCHECK(!markers_.IsEmpty());
 
-  HeapVector<Member<const Node>> nodes_with_markers;
+  HeapVector<Member<const Text>> nodes_with_markers;
   CopyKeysToVector(markers_, nodes_with_markers);
   unsigned size = nodes_with_markers.size();
   for (unsigned i = 0; i < size; ++i) {
@@ -782,19 +794,16 @@
     return;
   DCHECK(!markers_.IsEmpty());
 
-  // outer loop: process each markered node in the document
-  MarkerMap::iterator end = markers_.end();
-  for (MarkerMap::iterator i = markers_.begin(); i != end; ++i) {
-    const Node* node = i->key;
-
-    // inner loop: process each marker in the current node
-    MarkerLists* markers = i->value.Get();
+  // outer loop: process each markered Text in the document
+  for (auto& iterator : markers_) {
+    // inner loop: process each marker in the current Text
+    MarkerLists* markers = iterator.value.Get();
     for (DocumentMarker::MarkerType type : DocumentMarker::MarkerTypes::All()) {
       DocumentMarkerList* const list = ListForType(markers, type);
       if (!list || list->IsEmpty() || !marker_types.Contains(type))
         continue;
 
-      InvalidatePaintForNode(*node);
+      InvalidatePaintForNode(*iterator.key);
     }
   }
 }
@@ -832,7 +841,10 @@
                                                          unsigned start_offset,
                                                          unsigned end_offset,
                                                          bool active) {
-  MarkerLists* markers = markers_.at(node);
+  // TODO(yoichio): Make this function to take Text instead of Node.
+  if (!node->IsTextNode())
+    return false;
+  MarkerLists* markers = markers_.at(ToText(node));
   if (!markers)
     return false;
 
@@ -853,10 +865,8 @@
 #ifndef NDEBUG
 void DocumentMarkerController::ShowMarkers() const {
   StringBuilder builder;
-  MarkerMap::const_iterator end = markers_.end();
-  for (MarkerMap::const_iterator node_iterator = markers_.begin();
-       node_iterator != end; ++node_iterator) {
-    const Node* node = node_iterator->key;
+  for (auto& node_iterator : markers_) {
+    const Text* node = node_iterator.key;
     builder.Append(String::Format("%p", node));
     MarkerLists* markers = markers_.at(node);
     for (DocumentMarker::MarkerType type : DocumentMarker::MarkerTypes::All()) {
@@ -895,8 +905,9 @@
   if (!PossiblyHasMarkers(DocumentMarker::MarkerTypes::All()))
     return;
   DCHECK(!markers_.IsEmpty());
-
-  MarkerLists* markers = markers_.at(node);
+  if (!node->IsTextNode())
+    return;
+  MarkerLists* markers = markers_.at(ToText(node));
   if (!markers)
     return;
 
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.h b/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
index 47c3339..5496355 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
@@ -143,11 +143,11 @@
   void AddMarkerInternal(
       const EphemeralRange&,
       std::function<DocumentMarker*(int, int)> create_marker_from_offsets);
-  void AddMarkerToNode(const Node&, DocumentMarker*);
+  void AddMarkerToNode(const Text&, DocumentMarker*);
 
   using MarkerLists = HeapVector<Member<DocumentMarkerList>,
                                  DocumentMarker::kMarkerTypeIndexesCount>;
-  using MarkerMap = HeapHashMap<WeakMember<const Node>, Member<MarkerLists>>;
+  using MarkerMap = HeapHashMap<WeakMember<const Text>, Member<MarkerLists>>;
   static Member<DocumentMarkerList>& ListForType(MarkerLists*,
                                                  DocumentMarker::MarkerType);
   bool PossiblyHasMarkers(DocumentMarker::MarkerTypes) const;
diff --git a/third_party/blink/renderer/core/editing/markers/grammar_marker_list_impl_test.cc b/third_party/blink/renderer/core/editing/markers/grammar_marker_list_impl_test.cc
index d4ad416f..29192ae 100644
--- a/third_party/blink/renderer/core/editing/markers/grammar_marker_list_impl_test.cc
+++ b/third_party/blink/renderer/core/editing/markers/grammar_marker_list_impl_test.cc
@@ -11,7 +11,7 @@
 namespace blink {
 
 // Functionality implemented in SpellCheckMarkerListImpl is tested in
-// SpellingMarkerListImplTest.cpp.
+// spelling_marker_list_impl_test.cc.
 
 class GrammarMarkerListImplTest : public testing::Test {
  protected:
diff --git a/third_party/blink/renderer/core/editing/position.h b/third_party/blink/renderer/core/editing/position.h
index f19cca9..f36799c 100644
--- a/third_party/blink/renderer/core/editing/position.h
+++ b/third_party/blink/renderer/core/editing/position.h
@@ -133,12 +133,12 @@
   }
 
   // Returns an offset for editing based on anchor type for using with
-  // |anchorNode()| function:
-  //   - OffsetInAnchor  m_offset
-  //   - BeforeChildren  0
-  //   - BeforeAnchor    0
-  //   - AfterChildren   last editing offset in anchor node
-  //   - AfterAnchor     last editing offset in anchor node
+  // |AnchorNode()| function:
+  //   - kOffsetInAnchor  offset_
+  //   - kBeforeChildren  0
+  //   - kBeforeAnchor    0
+  //   - kAfterChildren   last editing offset in anchor node
+  //   - kAfterAnchor     last editing offset in anchor node
   // Editing operations will change in anchor node rather than nodes around
   // anchor node.
   int ComputeEditingOffset() const;
@@ -231,10 +231,10 @@
   // TODO(editing-dev): Since we should consider |Position| is constant in
   // tree, we should use |Member<const Node>|. see http://crbug.com/735327
   Member<Node> anchor_node_;
-  // m_offset can be the offset inside m_anchorNode, or if
-  // editingIgnoresContent(m_anchorNode) returns true, then other places in
-  // editing will treat m_offset == 0 as "before the anchor" and m_offset > 0 as
-  // "after the anchor node".  See parentAnchoredEquivalent for more info.
+  // offset_ can be the offset inside anchor_node_, or if
+  // EditingIgnoresContent(anchor_node_) returns true, then other places in
+  // editing will treat offset_ == 0 as "before the anchor" and offset_ > 0 as
+  // "after the anchor node".  See ParentAnchoredEquivalent for more info.
   int offset_;
   PositionAnchorType anchor_type_;
 };
diff --git a/third_party/blink/renderer/core/editing/position_iterator.cc b/third_party/blink/renderer/core/editing/position_iterator.cc
index 8cce67e..d5a5c9e 100644
--- a/third_party/blink/renderer/core/editing/position_iterator.cc
+++ b/third_party/blink/renderer/core/editing/position_iterator.cc
@@ -63,7 +63,7 @@
       dom_tree_version_(anchor_node->GetDocument().DomTreeVersion()) {
   for (Node* node = SelectableParentOf<Strategy>(*anchor_node); node;
        node = SelectableParentOf<Strategy>(*node)) {
-    // Each m_offsetsInAnchorNode[offset] should be an index of node in
+    // Each offsets_in_anchor_node_[offset] should be an index of node in
     // parent, but delay to calculate the index until it is needed for
     // performance.
     offsets_in_anchor_node_.push_back(kInvalidOffset);
@@ -126,9 +126,8 @@
     // For example, position is before E, F.
     DCHECK_EQ(Strategy::Parent(*node_after_position_in_anchor_), anchor_node_);
     DCHECK_NE(offsets_in_anchor_node_[depth_to_anchor_node_], kInvalidOffset);
-    // TODO(yoichio): This should be equivalent to
-    // PositionTemplate<Strategy>(m_anchorNode,
-    // PositionAnchorType::BeforeAnchor);
+    // TODO(yoichio): This should be equivalent to PositionTemplate<Strategy>(
+    // anchor_node_, PositionAnchorType::kBeforeAnchor).
     return PositionTemplate<Strategy>(
         anchor_node_, offsets_in_anchor_node_[depth_to_anchor_node_]);
   }
@@ -163,11 +162,11 @@
   // +-D
   //   |-G
   //   +-H
-  // Let |anchor| as |m_anchorNode| and
-  // |child| as |m_nodeAfterPositionInAnchor|.
+  // Let |anchor| as |anchor_node_| and
+  // |child| as |node_after_position_in_anchor_|.
   if (node_after_position_in_anchor_) {
     // Case #1: Move to position before the first child of
-    // |m_nodeAfterPositionInAnchor|.
+    // |node_after_position_in_anchor_|.
     // This is a point just before |child|.
     // Let |anchor| is A and |child| is B,
     // then next |anchor| is B and |child| is E.
@@ -190,10 +189,10 @@
       !ShouldTraverseChildren<Strategy>(*anchor_node_) &&
       offset_in_anchor_ < Strategy::LastOffsetForEditing(anchor_node_)) {
     // Case #2. This is the next of Case #1 or #2 itself.
-    // Position is (|anchor|, |m_offsetInAchor|).
+    // Position is (|anchor|, |offset_in_anchor_|).
     // In this case |anchor| is a leaf(E,F,C,G or H) and
-    // |m_offsetInAnchor| is not on the end of |anchor|.
-    // Then just increment |m_offsetInAnchor|.
+    // |offset_in_anchor_| is not on the end of |anchor|.
+    // Then just increment |offset_in_anchor_|.
     offset_in_anchor_ =
         NextGraphemeBoundaryOf(*anchor_node_, offset_in_anchor_);
   } else {
@@ -239,13 +238,13 @@
   // +-D
   //   |-G
   //   +-H
-  // Let |anchor| as |m_anchorNode| and
-  // |child| as |m_nodeAfterPositionInAnchor|.
-  // decrement() is complex but logically reverse of increment(), of course:)
+  // Let |anchor| as |anchor_node_| and
+  // |child| as |node_after_position_in_anchor_|.
+  // Decrement() is complex but logically reverse of Increment(), of course:)
   if (node_after_position_in_anchor_) {
     anchor_node_ = Strategy::PreviousSibling(*node_after_position_in_anchor_);
     if (anchor_node_) {
-      // Case #1-a. This is a revese of increment()::Case#3-a.
+      // Case #1-a. This is a revese of Increment()::Case#3-a.
       // |child| has a previous sibling.
       // Let |anchor| is B and |child| is F,
       // next |anchor| is E and |child| is null.
@@ -269,7 +268,7 @@
         offsets_in_anchor_node_[depth_to_anchor_node_] = offset_in_anchor_;
       return;
     } else {
-      // Case #1-b. This is a revese of increment()::Case#1.
+      // Case #1-b. This is a revese of Increment()::Case#1.
       // |child| doesn't have a previous sibling.
       // Let |anchor| is B and |child| is E,
       // next |anchor| is A and |child| is B.
@@ -298,7 +297,7 @@
                             ? 0
                             : Strategy::LastOffsetForEditing(anchor_node_);
     // Decrement depth initializing with -1 because
-    // |m_nodeAfterPositionInAnchor| is null so still unneeded.
+    // |node_after_position_in_anchor_| is null so still unneeded.
     if (depth_to_anchor_node_ >= offsets_in_anchor_node_.size())
       offsets_in_anchor_node_.push_back(kInvalidOffset);
     else
@@ -307,17 +306,17 @@
     return;
   }
   if (offset_in_anchor_ && anchor_node_->GetLayoutObject()) {
-    // Case #3-a. This is a reverse of increment()::Case#2.
+    // Case #3-a. This is a reverse of Increment()::Case#2.
     // In this case |anchor| is a leaf(E,F,C,G or H) and
-    // |m_offsetInAnchor| is not on the beginning of |anchor|.
-    // Then just decrement |m_offsetInAnchor|.
+    // |offset_in_anchor_| is not on the beginning of |anchor|.
+    // Then just decrement |offset_in_anchor_|.
     offset_in_anchor_ =
         PreviousGraphemeBoundaryOf(*anchor_node_, offset_in_anchor_);
     return;
   }
-  // Case #3-b. This is a reverse of increment()::Case#1.
+  // Case #3-b. This is a reverse of Increment()::Case#1.
   // In this case |anchor| is a leaf(E,F,C,G or H) and
-  // |m_offsetInAnchor| is on the beginning of |anchor|.
+  // |offset_in_anchor_| is on the beginning of |anchor|.
   // Let |anchor| is E,
   // next |anchor| is B and |child| is E.
   node_after_position_in_anchor_ = anchor_node_;
diff --git a/third_party/blink/renderer/core/editing/position_iterator.h b/third_party/blink/renderer/core/editing/position_iterator.h
index 43284e02..7a43559 100644
--- a/third_party/blink/renderer/core/editing/position_iterator.h
+++ b/third_party/blink/renderer/core/editing/position_iterator.h
@@ -78,14 +78,14 @@
   }
 
   Member<Node> anchor_node_;
-  // If this is non-null, Strategy::parent(*m_nodeAfterPositionInAnchor) ==
-  // m_anchorNode;
+  // If this is non-null, Strategy::Parent(*node_after_position_in_anchor_) ==
+  // anchor_node_;
   Member<Node> node_after_position_in_anchor_;
   int offset_in_anchor_;
   wtf_size_t depth_to_anchor_node_;
-  // If |m_nodeAfterPositionInAnchor| is not null,
-  // m_offsetsInAnchorNode[m_depthToAnchorNode] ==
-  //    Strategy::index(m_nodeAfterPositionInAnchor).
+  // If |node_after_position_in_anchor_| is not null,
+  // offsets_in_anchor_node_[depth_to_anchor_node_] ==
+  //    Strategy::Index(node_after_position_in_anchor_).
   Vector<int> offsets_in_anchor_node_;
   uint64_t dom_tree_version_;
 };
diff --git a/third_party/blink/renderer/core/editing/position_with_affinity.h b/third_party/blink/renderer/core/editing/position_with_affinity.h
index 008ffbc..132de6af 100644
--- a/third_party/blink/renderer/core/editing/position_with_affinity.h
+++ b/third_party/blink/renderer/core/editing/position_with_affinity.h
@@ -28,8 +28,8 @@
   TextAffinity Affinity() const { return affinity_; }
   const PositionTemplate<Strategy>& GetPosition() const { return position_; }
 
-  // Returns true if both |this| and |other| is null or both |m_position|
-  // and |m_affinity| equal.
+  // Returns true if both |this| and |other| is null or both |position_|
+  // and |affinity_| equal.
   bool operator==(const PositionWithAffinityTemplate& other) const;
   bool operator!=(const PositionWithAffinityTemplate& other) const {
     return !operator==(other);
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index e7e6162..7a774f5b 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -557,7 +557,7 @@
       return false;
   }
 
-  // |dispatchSelectStart()| can change document hosted by |m_frame|.
+  // |DispatchSelectStart()| can change document hosted by |frame_|.
   if (!this->Selection().IsAvailable())
     return false;
 
@@ -888,8 +888,8 @@
   if (Selection().ComputeVisibleSelectionInDOMTreeDeprecated().IsRange()) {
     // A double-click when range is already selected
     // should not change the selection.  So, do not call
-    // selectClosestWordFromMouseEvent, but do set
-    // m_beganSelectingText to prevent handleMouseReleaseEvent
+    // SelectClosestWordFromMouseEvent, but do set
+    // began_selecting_text_ to prevent HandleMouseReleaseEvent
     // from setting caret selection.
     selection_state_ = SelectionState::kExtendedSelection;
     return true;
diff --git a/third_party/blink/renderer/core/editing/selection_controller.h b/third_party/blink/renderer/core/editing/selection_controller.h
index e16e007..636332b5 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.h
+++ b/third_party/blink/renderer/core/editing/selection_controller.h
@@ -119,7 +119,7 @@
   FrameSelection& Selection() const;
 
   // Implements |DocumentShutdownObserver|.
-  // TODO(yosin): We should relocate |m_originalBaseInFlatTree| when DOM tree
+  // TODO(yosin): We should relocate |original_base_in_flat_tree_| when DOM tree
   // changed.
   void ContextDestroyed(Document*) final;
 
diff --git a/third_party/blink/renderer/core/editing/selection_editor.cc b/third_party/blink/renderer/core/editing/selection_editor.cc
index 83922c86..7fc23596 100644
--- a/third_party/blink/renderer/core/editing/selection_editor.cc
+++ b/third_party/blink/renderer/core/editing/selection_editor.cc
@@ -44,7 +44,7 @@
 void SelectionEditor::AssertSelectionValid() const {
 #if DCHECK_IS_ON()
   // Since We don't track dom tree version during attribute changes, we can't
-  // use it for validity of |m_selection|.
+  // use it for validity of |selection_|.
   const_cast<SelectionEditor*>(this)->selection_.dom_tree_version_ =
       GetDocument().DomTreeVersion();
 #endif
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
index 09e14ca..a56a84e 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
@@ -206,7 +206,7 @@
     }
   }
 
-  // If there is no the highest node in the selected nodes, |m_lastClosed| can
+  // If there is no the highest node in the selected nodes, |last_closed_| can
   // be #text when its parent is a formatting tag. In this case, #text is
   // wrapped by <span> tag, but this text should be wrapped by the formatting
   // tag. See http://crbug.com/634482
diff --git a/third_party/blink/renderer/core/editing/state_machines/backward_grapheme_boundary_state_machine.h b/third_party/blink/renderer/core/editing/state_machines/backward_grapheme_boundary_state_machine.h
index ff523a0a..efac1ee2 100644
--- a/third_party/blink/renderer/core/editing/state_machines/backward_grapheme_boundary_state_machine.h
+++ b/third_party/blink/renderer/core/editing/state_machines/backward_grapheme_boundary_state_machine.h
@@ -55,7 +55,7 @@
   // Used for composing supplementary code point with surrogate pairs.
   UChar trail_surrogate_ = 0;
 
-  // The code point immediately after the m_BoundaryOffset.
+  // The code point immediately after the boundary_offset_.
   UChar32 next_code_point_;
 
   // The relative offset from the begging of this state machine.
diff --git a/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.cc b/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.cc
index 9b40786..9293ebf 100644
--- a/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.cc
+++ b/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.cc
@@ -163,8 +163,7 @@
         if (Character::IsRegionalIndicator(prev_code_point_) &&
             Character::IsRegionalIndicator(code_point)) {
           if (preceding_ris_count_ % 2 == 0) {
-            // Odd numbered RI case, note that m_prevCodePoint is also
-            // RI.
+            // Odd numbered RI case, note that prev_code_point_ is also RI.
             boundary_offset_ += 2;
           }
           return Finish();
diff --git a/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.h b/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.h
index 8c0a54f5..9e612dfd 100644
--- a/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.h
+++ b/third_party/blink/renderer/core/editing/state_machines/forward_grapheme_boundary_state_machine.h
@@ -58,7 +58,7 @@
   // Used for composing supplementary code point with surrogate pairs.
   UChar pending_code_unit_ = 0;
 
-  // The code point immediately before the m_BoundaryOffset.
+  // The code point immediately before the boundary_offset_.
   UChar32 prev_code_point_;
 
   // The relative offset from the begging of this state machine.
diff --git a/third_party/blink/renderer/core/editing/visible_units.cc b/third_party/blink/renderer/core/editing/visible_units.cc
index efc60394..d3c3e77 100644
--- a/third_party/blink/renderer/core/editing/visible_units.cc
+++ b/third_party/blink/renderer/core/editing/visible_units.cc
@@ -581,7 +581,7 @@
   return VisiblePosition();
 }
 
-// TODO(yosin): We should use |associatedLayoutObjectOf()| in "VisibleUnits.cpp"
+// TODO(yosin): We should use |AssociatedLayoutObjectOf()| in "visible_units.cc"
 // where it takes |LayoutObject| from |Position|.
 
 int CaretMinOffset(const Node* node) {
@@ -990,7 +990,7 @@
 
   if (layout_object->IsBR()) {
     // TODO(leviw) The condition should be
-    // m_anchorType == PositionAnchorType::BeforeAnchor, but for now we
+    // anchor_type_ == PositionAnchorType::kBeforeAnchor, but for now we
     // still need to support legacy positions.
     if (position.IsAfterAnchor())
       return false;
diff --git a/third_party/blink/renderer/core/events/BUILD.gn b/third_party/blink/renderer/core/events/BUILD.gn
index a88f31af..f7facc6 100644
--- a/third_party/blink/renderer/core/events/BUILD.gn
+++ b/third_party/blink/renderer/core/events/BUILD.gn
@@ -60,8 +60,6 @@
     "progress_event.h",
     "promise_rejection_event.cc",
     "promise_rejection_event.h",
-    "registered_event_listener.cc",
-    "registered_event_listener.h",
     "resource_progress_event.cc",
     "resource_progress_event.h",
     "security_policy_violation_event.cc",
diff --git a/third_party/blink/renderer/core/events/error_event.cc b/third_party/blink/renderer/core/events/error_event.cc
index 46fce0c0..ba74dbe 100644
--- a/third_party/blink/renderer/core/events/error_event.cc
+++ b/third_party/blink/renderer/core/events/error_event.cc
@@ -82,6 +82,10 @@
   return EventNames::ErrorEvent;
 }
 
+bool ErrorEvent::IsErrorEvent() const {
+  return true;
+}
+
 bool ErrorEvent::CanBeDispatchedInWorld(const DOMWrapperWorld& world) const {
   return !world_ || world_ == &world;
 }
diff --git a/third_party/blink/renderer/core/events/error_event.h b/third_party/blink/renderer/core/events/error_event.h
index e6a8d72..c4eca9c6 100644
--- a/third_party/blink/renderer/core/events/error_event.h
+++ b/third_party/blink/renderer/core/events/error_event.h
@@ -89,6 +89,7 @@
 
   const AtomicString& InterfaceName() const override;
   bool CanBeDispatchedInWorld(const DOMWrapperWorld&) const override;
+  bool IsErrorEvent() const override;
 
   DOMWrapperWorld* World() const { return world_.get(); }
 
@@ -112,6 +113,8 @@
   scoped_refptr<DOMWrapperWorld> world_;
 };
 
+DEFINE_EVENT_TYPE_CASTS(ErrorEvent);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_EVENTS_ERROR_EVENT_H_
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index 3f485a3d..228e92e 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -564,7 +564,7 @@
   v8::Local<v8::Value> result =
       frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
           ScriptSourceCode(script, ScriptSourceLocationType::kJavascriptUrl),
-          KURL(), kNotSharableCrossOrigin);
+          KURL(), kOpaqueResource);
 
   // Failure is reported as a null string.
   if (result.IsEmpty() || !result->IsString())
diff --git a/third_party/blink/renderer/core/frame/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation.cc
index aa7481e..5b6b3dd 100644
--- a/third_party/blink/renderer/core/frame/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation.cc
@@ -473,6 +473,11 @@
               ReplacedWillBeRemoved("document.registerElement",
                                     "window.customElements.define", kM73,
                                     "4642138092470272")};
+    case WebFeature::kCSSSelectorPseudoUnresolved:
+      return {
+          "CSSSelectorPseudoUnresolved", kM73,
+          ReplacedWillBeRemoved(":unresolved pseudo selector", ":not(:defined)",
+                                kM73, "4642138092470272")};
 
     case WebFeature::
         kEncryptedMediaDisallowedByFeaturePolicyInCrossOriginIframe:
diff --git a/third_party/blink/renderer/core/frame/dom_timer_test.cc b/third_party/blink/renderer/core/frame/dom_timer_test.cc
index 1f50fb5..5398e2d 100644
--- a/third_party/blink/renderer/core/frame/dom_timer_test.cc
+++ b/third_party/blink/renderer/core/frame/dom_timer_test.cc
@@ -57,7 +57,7 @@
         .GetFrame()
         ->GetScriptController()
         .ExecuteScriptInMainWorldAndReturnValue(ScriptSourceCode(expr), KURL(),
-                                                kNotSharableCrossOrigin);
+                                                kOpaqueResource);
   }
 
   Vector<double> ToDoubleArray(v8::Local<v8::Value> value,
@@ -75,7 +75,7 @@
   void ExecuteScriptAndWaitUntilIdle(const char* script_text) {
     ScriptSourceCode script(script_text);
     GetDocument().GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
-        script, KURL(), kNotSharableCrossOrigin);
+        script, KURL(), kOpaqueResource);
     platform_->RunUntilIdle();
   }
 };
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 360cfa7..883ea60 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2307,7 +2307,9 @@
     ErrorEvent* error = ErrorEvent::Create(
         "ResizeObserver loop limit exceeded",
         SourceLocation::Capture(frame_->GetDocument()), nullptr);
-    frame_->GetDocument()->DispatchErrorEvent(error, kNotSharableCrossOrigin);
+    // We're using |kSharableCrossOrigin| as the error is made by blink itself.
+    // TODO(yhirano): Reconsider this.
+    frame_->GetDocument()->DispatchErrorEvent(error, kSharableCrossOrigin);
     // Ensure notifications will get delivered in next cycle.
     ScheduleAnimation();
   }
diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.cc b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
index 1e7e867a..980585a 100644
--- a/third_party/blink/renderer/core/frame/pausable_script_executor.cc
+++ b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
@@ -59,12 +59,14 @@
 
   Vector<v8::Local<v8::Value>> results;
   for (const auto& source : sources_) {
+    // Note: An error event in an isolated world will never be dispatched to
+    // a foreign world.
     v8::Local<v8::Value> script_value =
         world_id_ ? frame->GetScriptController().ExecuteScriptInIsolatedWorld(
-                        world_id_, source, KURL(), kNotSharableCrossOrigin)
+                        world_id_, source, KURL(), kSharableCrossOrigin)
                   : frame->GetScriptController()
                         .ExecuteScriptInMainWorldAndReturnValue(
-                            source, KURL(), kNotSharableCrossOrigin);
+                            source, KURL(), kSharableCrossOrigin);
     results.push_back(script_value);
   }
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 0d021c6..00a4cd8 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -680,8 +680,8 @@
 void WebLocalFrameImpl::ExecuteScript(const WebScriptSource& source) {
   DCHECK(GetFrame());
   v8::HandleScope handle_scope(ToIsolate(GetFrame()));
-  GetFrame()->GetScriptController().ExecuteScriptInMainWorld(
-      source, KURL(), kNotSharableCrossOrigin);
+  GetFrame()->GetScriptController().ExecuteScriptInMainWorld(source, KURL(),
+                                                             kOpaqueResource);
 }
 
 void WebLocalFrameImpl::ExecuteScriptInIsolatedWorld(
@@ -691,9 +691,11 @@
   CHECK_GT(world_id, 0);
   CHECK_LT(world_id, DOMWrapperWorld::kEmbedderWorldIdLimit);
 
+  // Note: An error event in an isolated world will never be dispatched to
+  // a foreign world.
   v8::HandleScope handle_scope(ToIsolate(GetFrame()));
   GetFrame()->GetScriptController().ExecuteScriptInIsolatedWorld(
-      world_id, source_in, KURL(), kNotSharableCrossOrigin);
+      world_id, source_in, KURL(), kSharableCrossOrigin);
 }
 
 v8::Local<v8::Value>
@@ -704,8 +706,10 @@
   CHECK_GT(world_id, 0);
   CHECK_LT(world_id, DOMWrapperWorld::kEmbedderWorldIdLimit);
 
+  // Note: An error event in an isolated world will never be dispatched to
+  // a foreign world.
   return GetFrame()->GetScriptController().ExecuteScriptInIsolatedWorld(
-      world_id, source_in, KURL(), kNotSharableCrossOrigin);
+      world_id, source_in, KURL(), kSharableCrossOrigin);
 }
 
 void WebLocalFrameImpl::SetIsolatedWorldSecurityOrigin(
@@ -801,8 +805,7 @@
 
   return GetFrame()
       ->GetScriptController()
-      .ExecuteScriptInMainWorldAndReturnValue(source, KURL(),
-                                              kNotSharableCrossOrigin);
+      .ExecuteScriptInMainWorldAndReturnValue(source, KURL(), kOpaqueResource);
 }
 
 void WebLocalFrameImpl::RequestExecuteScriptAndReturnValue(
@@ -2111,7 +2114,7 @@
   v8::Local<v8::Value> result =
       GetFrame()->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
           ScriptSourceCode(script, ScriptSourceLocationType::kJavascriptUrl),
-          KURL(), kNotSharableCrossOrigin);
+          KURL(), kOpaqueResource);
   if (result.IsEmpty() || !result->IsString())
     return;
   String script_result = ToCoreString(v8::Local<v8::String>::Cast(result));
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
index bbb5f2f..afb709b 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_context_creation_attributes_core.h
@@ -32,7 +32,7 @@
   bool preserve_drawing_buffer = false;
   bool stencil = false;
 
-  // This attribute is of type XRDevice, defined in modules/xr/XRDevice.h
+  // This attribute is of type XRDevice, defined in modules/xr/xr_device.h
   Member<ScriptWrappable> compatible_xr_device;
 
   void Trace(blink::Visitor*);
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
index bcde1d0e..3ee397d3 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -61,8 +61,8 @@
   if (!OriginTrials::LowLatencyCanvasEnabled(host->GetTopExecutionContext()))
     creation_attributes_.low_latency = false;
 
-  // Make m_creationAttributes reflect the effective colorSpace and pixelFormat
-  // rather than the requested one.
+  // Make creation_attributes_ reflect the effective color_space and
+  // pixel_format rather than the requested one.
   creation_attributes_.color_space = ColorSpaceAsString();
   creation_attributes_.pixel_format = PixelFormatAsString();
 }
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.idl b/third_party/blink/renderer/core/html/canvas/html_canvas_element.idl
index 0fee23b..609b8bd 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.idl
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.idl
@@ -29,7 +29,7 @@
 interface HTMLCanvasElement : HTMLElement
 {
     // Note: Due to dependecies on modules, getContext is defined in a partial
-    // interface in HTMLCanvasElementModule.idl
+    // interface in html_canvas_element_module.idl
 
     [RaisesException=Setter, CEReactions] attribute unsigned long width;
     [RaisesException=Setter, CEReactions] attribute unsigned long height;
diff --git a/third_party/blink/renderer/core/html/custom/v0_custom_element_processing_stack.h b/third_party/blink/renderer/core/html/custom/v0_custom_element_processing_stack.h
index 0563687..a2a799e 100644
--- a/third_party/blink/renderer/core/html/custom/v0_custom_element_processing_stack.h
+++ b/third_party/blink/renderer/core/html/custom/v0_custom_element_processing_stack.h
@@ -81,11 +81,11 @@
   }
 
   // The start of the element queue on the top of the processing
-  // stack. An offset into instance().m_flattenedProcessingStack.
+  // stack. An offset into Instance().flattened_processing_stack_.
   static wtf_size_t element_queue_start_;
 
   // The end of the element queue on the top of the processing
-  // stack. A cache of instance().m_flattenedProcessingStack.size().
+  // stack. A cache of Instance().flattened_processing_stack_.size().
   static wtf_size_t element_queue_end_;
 
   static V0CustomElementCallbackQueue::ElementQueueId CurrentElementQueue() {
diff --git a/third_party/blink/renderer/core/html/forms/base_text_input_type.h b/third_party/blink/renderer/core/html/forms/base_text_input_type.h
index 2d252058..9e82080 100644
--- a/third_party/blink/renderer/core/html/forms/base_text_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/base_text_input_type.h
@@ -55,7 +55,7 @@
   bool SupportsPlaceholder() const final;
   bool SupportsSelectionAPI() const override;
 
-  // m_regexp and m_patternForRegexp are mutable because they are kinds of
+  // regexp_ and pattern_for_regexp_ are mutable because they are kinds of
   // cache.
   mutable std::unique_ptr<ScriptRegexp> regexp_;
   mutable AtomicString pattern_for_regexp_;
diff --git a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
index 3ea98fad9..340e62e 100644
--- a/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
+++ b/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
@@ -54,9 +54,9 @@
 ColorChooserPopupUIController::~ColorChooserPopupUIController() = default;
 
 void ColorChooserPopupUIController::Dispose() {
-  // Finalized earlier so as to access m_chromeClient while alive.
+  // Finalized earlier so as to access chrome_client_ while alive.
   ClosePopup();
-  // ~ColorChooserUIController calls endChooser().
+  // ~ColorChooserUIController calls EndChooser().
 }
 
 void ColorChooserPopupUIController::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/html/forms/form_data.h b/third_party/blink/renderer/core/html/forms/form_data.h
index 5ef582cf..75cd949f 100644
--- a/third_party/blink/renderer/core/html/forms/form_data.h
+++ b/third_party/blink/renderer/core/html/forms/form_data.h
@@ -45,7 +45,7 @@
 class HTMLFormElement;
 class ScriptState;
 
-// Typedef from FormData.idl:
+// Typedef from form_data.idl:
 typedef FileOrUSVString FormDataEntryValue;
 
 class CORE_EXPORT FormData final
@@ -104,7 +104,7 @@
   IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
 
   WTF::TextEncoding encoding_;
-  // Entry pointers in m_entries never be nullptr.
+  // Entry pointers in entries_ never be nullptr.
   HeapVector<Member<const Entry>> entries_;
   bool contains_password_data_ = false;
 };
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 85a3af99..bd5a3f6 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -456,17 +456,17 @@
     return;
   will_validate_initialized_ = true;
   will_validate_ = new_will_validate;
-  // Needs to force setNeedsValidityCheck() to invalidate validity state of
+  // Needs to force SetNeedsValidityCheck() to invalidate validity state of
   // FORM/FIELDSET. If this element updates willValidate twice and
-  // isValidElement() is not called between them, the second call of this
-  // function still has m_validityIsDirty==true, which means
-  // setNeedsValidityCheck() doesn't invalidate validity state of
+  // IsValidElement() is not called between them, the second call of this
+  // function still has validity_is_dirty_==true, which means
+  // SetNeedsValidityCheck() doesn't invalidate validity state of
   // FORM/FIELDSET.
   validity_is_dirty_ = false;
   SetNeedsValidityCheck();
   // No need to trigger style recalculation here because
-  // setNeedsValidityCheck() does it in the right away. This relies on
-  // the assumption that valid() is always true if willValidate() is false.
+  // SetNeedsValidityCheck() does it in the right away. This relies on
+  // the assumption that Valid() is always true if willValidate() is false.
 
   if (!will_validate_)
     HideVisibleValidationMessage();
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.h b/third_party/blink/renderer/core/html/forms/html_form_control_element.h
index 7245757ad..0ec0570 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.h
@@ -220,9 +220,9 @@
   mutable bool may_have_field_set_ancestor_ : 1;
 
   bool has_validation_message_ : 1;
-  // The initial value of m_willValidate depends on the derived class. We can't
-  // initialize it with a virtual function in the constructor. m_willValidate
-  // is not deterministic as long as m_willValidateInitialized is false.
+  // The initial value of will_validate_ depends on the derived class. We can't
+  // initialize it with a virtual function in the constructor. will_validate_
+  // is not deterministic as long as will_validate_initialized_ is false.
   mutable bool will_validate_initialized_ : 1;
   mutable bool will_validate_ : 1;
 
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.h b/third_party/blink/renderer/core/html/forms/html_form_element.h
index 9dcf98152..14cefa21 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.h
@@ -158,10 +158,9 @@
 
   RadioButtonGroupScope radio_button_group_scope_;
 
-  // Do not access m_listedElements directly. Use listedElements()
-  // instead.
+  // Do not access listed_elements_ directly. Use ListedElements() instead.
   ListedElement::List listed_elements_;
-  // Do not access m_imageElements directly. Use imageElements() instead.
+  // Do not access image_elements_ directly. Use ImageElements() instead.
   HeapVector<Member<HTMLImageElement>> image_elements_;
 
   // https://html.spec.whatwg.org/multipage/forms.html#planned-navigation
diff --git a/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc b/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
index 873fe56..f04ceb6 100644
--- a/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
@@ -42,9 +42,9 @@
 inline HTMLOptGroupElement::HTMLOptGroupElement(Document& document)
     : HTMLElement(optgroupTag, document) {}
 
-// An explicit empty destructor should be in HTMLOptGroupElement.cpp, because
+// An explicit empty destructor should be in html_opt_group_element.cc, because
 // if an implicit destructor is used or an empty destructor is defined in
-// HTMLOptGroupElement.h, when including HTMLOptGroupElement.h,
+// html_opt_group_element.h, when including html_opt_group_element.h,
 // msvc tries to expand the destructor and causes
 // a compile error because of lack of ComputedStyle definition.
 HTMLOptGroupElement::~HTMLOptGroupElement() = default;
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.cc b/third_party/blink/renderer/core/html/forms/html_option_element.cc
index 7b5479e..d1c3e09 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.cc
@@ -49,9 +49,9 @@
 HTMLOptionElement::HTMLOptionElement(Document& document)
     : HTMLElement(optionTag, document), is_selected_(false) {}
 
-// An explicit empty destructor should be in HTMLOptionElement.cpp, because
+// An explicit empty destructor should be in html_option_element.cc, because
 // if an implicit destructor is used or an empty destructor is defined in
-// HTMLOptionElement.h, when including HTMLOptionElement.h,
+// html_option_element.h, when including html_option_element.h,
 // msvc tries to expand the destructor and causes
 // a compile error because of lack of ComputedStyle definition.
 HTMLOptionElement::~HTMLOptionElement() = default;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index 0e4ef963..464037d 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -79,7 +79,7 @@
 
 using namespace HTMLNames;
 
-// Upper limit of m_listItems. According to the HTML standard, options larger
+// Upper limit of list_items_. According to the HTML standard, options larger
 // than this limit doesn't work well because |selectedIndex| IDL attribute is
 // signed.
 static const unsigned kMaxListItems = INT_MAX;
@@ -523,7 +523,7 @@
     HTMLOptionElement* start_option,
     SkipDirection direction) const {
   const ListItems& items = GetListItems();
-  // Can't use m_size because layoutObject forces a minimum size.
+  // Can't use size_ because LayoutObject forces a minimum size.
   int page_size = 0;
   if (GetLayoutObject()->IsListBox()) {
     // -1 so we still show context.
@@ -585,12 +585,14 @@
   // selection pivots around this anchor index.
   // Example:
   // 1. Press the mouse button on the second OPTION
-  //   m_activeSelectionAnchorIndex = 1
+  //   active_selection_anchor_ points the second OPTION.
   // 2. Drag the mouse pointer onto the fifth OPTION
-  //   m_activeSelectionEndIndex = 4, options at 1-4 indices are selected.
+  //   active_selection_end_ points the fifth OPTION, OPTIONs at 1-4 indices
+  //   are selected.
   // 3. Drag the mouse pointer onto the fourth OPTION
-  //   m_activeSelectionEndIndex = 3, options at 1-3 indices are selected.
-  //   updateListBoxSelection needs to clear selection of the fifth OPTION.
+  //   active_selection_end_ points the fourth OPTION, OPTIONs at 1-3 indices
+  //   are selected.
+  //   UpdateListBoxSelection needs to clear selection of the fifth OPTION.
   cached_state_for_active_selection_.resize(0);
   for (auto* const option : GetOptionList()) {
     cached_state_for_active_selection_.push_back(option->Selected());
@@ -654,7 +656,7 @@
     return;
   }
 
-  // Update m_lastOnChangeSelection and fire dispatchFormControlChangeEvent.
+  // Update last_on_change_selection_ and fire a 'change' event.
   bool fire_on_change = false;
   for (unsigned i = 0; i < items.size(); ++i) {
     HTMLElement* element = items[i];
@@ -903,7 +905,7 @@
   HTMLOptionElement* option = option_to_scroll_to_.Release();
   if (!option || !isConnected())
     return;
-  // optionRemoved() makes sure m_optionToScrollTo doesn't have an option with
+  // OptionRemoved() makes sure option_to_scroll_to_ doesn't have an option with
   // another owner.
   DCHECK_EQ(option->OwnerSelectElement(), this);
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
@@ -999,7 +1001,7 @@
 
   bool should_update_popup = false;
 
-  // selectedOption() is O(N).
+  // SelectedOption() is O(N).
   if (IsAutofilled() && SelectedOption() != element)
     SetAutofillState(WebAutofillState::kNotFilled);
 
@@ -1011,7 +1013,7 @@
       element->SetDirty(true);
   }
 
-  // deselectItemsWithoutValidation() is O(N).
+  // DeselectItemsWithoutValidation() is O(N).
   if (flags & kDeselectOtherOptions)
     should_update_popup |= DeselectItemsWithoutValidation(element);
 
@@ -1027,8 +1029,8 @@
       SetActiveSelectionEnd(element);
   }
 
-  // Need to update m_lastOnChangeOption before
-  // LayoutMenuList::updateFromElement.
+  // Need to update last_on_change_option_ before
+  // LayoutMenuList::UpdateFromElement.
   bool should_dispatch_events = false;
   if (UsesMenuList()) {
     should_dispatch_events = (flags & kDispatchInputAndChangeEvent) &&
@@ -1039,7 +1041,7 @@
   // For the menu list case, this is what makes the selected element appear.
   if (LayoutObject* layout_object = GetLayoutObject())
     layout_object->UpdateFromElement();
-  // PopupMenu::updateFromElement() posts an O(N) task.
+  // PopupMenu::UpdateFromElement() posts an O(N) task.
   if (PopupIsVisible() && should_update_popup)
     popup_->UpdateFromElement(PopupMenu::kBySelectionChange);
 
@@ -1052,10 +1054,10 @@
       DispatchChangeEvent();
     }
     if (LayoutObject* layout_object = GetLayoutObject()) {
-      // Need to check usesMenuList() again because event handlers might
+      // Need to check UsesMenuList() again because event handlers might
       // change the status.
       if (UsesMenuList()) {
-        // didSelectOption() is O(N) because of HTMLOptionElement::index().
+        // DidSelectOption() is O(N) because of HTMLOptionElement::index().
         ToLayoutMenuList(layout_object)->DidSelectOption(element);
       }
     }
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.h b/third_party/blink/renderer/core/html/forms/html_select_element.h
index ab8d832..eab2da2 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.h
@@ -285,7 +285,7 @@
   void ObserveTreeMutation();
   void UnobserveTreeMutation();
 
-  // m_listItems contains HTMLOptionElement, HTMLOptGroupElement, and
+  // list_items_ contains HTMLOptionElement, HTMLOptGroupElement, and
   // HTMLHRElement objects.
   mutable ListItems list_items_;
   Vector<bool> last_on_change_selection_;
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element_test.cc b/third_party/blink/renderer/core/html/forms/html_select_element_test.cc
index ab10bd5..ba3bf95 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element_test.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element_test.cc
@@ -91,9 +91,9 @@
 }
 
 TEST_F(HTMLSelectElementTest, RestoreUnmatchedFormControlState) {
-  // We had a bug that selectedOption() and m_lastOnChangeOption were
-  // mismatched in optionToBeShown(). It happened when
-  // restoreFormControlState() couldn't find matched OPTIONs.
+  // We had a bug that SelectedOption() and last_on_change_option_ were
+  // mismatched in OptionToBeShown(). It happened when
+  // RestoreFormControlState() couldn't find matched OPTIONs.
   // crbug.com/627833.
 
   SetHtmlInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index f92cc421..56bfa62 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -464,7 +464,7 @@
 }
 
 void InternalPopupMenu::DidClosePopup() {
-  // Clearing m_popup first to prevent from trying to close the popup again.
+  // Clearing popup_ first to prevent from trying to close the popup again.
   popup_ = nullptr;
   if (owner_element_)
     owner_element_->PopupDidHide();
diff --git a/third_party/blink/renderer/core/html/forms/listed_element.h b/third_party/blink/renderer/core/html/forms/listed_element.h
index d7ba4cd..0da13d5 100644
--- a/third_party/blink/renderer/core/html/forms/listed_element.h
+++ b/third_party/blink/renderer/core/html/forms/listed_element.h
@@ -123,7 +123,7 @@
   Member<HTMLFormElement> form_;
   Member<ValidityState> validity_state_;
   String custom_validation_message_;
-  // If m_formWasSetByParser is true, m_form is always non-null.
+  // If form_was_set_by_parser_ is true, form_ is always non-null.
   bool form_was_set_by_parser_;
 };
 
diff --git a/third_party/blink/renderer/core/html/forms/option_list.cc b/third_party/blink/renderer/core/html/forms/option_list.cc
index 5beee4e..b6c0994 100644
--- a/third_party/blink/renderer/core/html/forms/option_list.cc
+++ b/third_party/blink/renderer/core/html/forms/option_list.cc
@@ -12,8 +12,8 @@
 
 void OptionListIterator::Advance(HTMLOptionElement* previous) {
   // This function returns only
-  // - An OPTION child of m_select, or
-  // - An OPTION child of an OPTGROUP child of m_select.
+  // - An OPTION child of select_, or
+  // - An OPTION child of an OPTGROUP child of select_.
 
   Element* current;
   if (previous) {
diff --git a/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc b/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
index 2b5a590..52fa2712 100644
--- a/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
+++ b/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
@@ -288,7 +288,7 @@
     return;
   group->Remove(element);
   if (group->IsEmpty()) {
-    // We don't remove an empty RadioButtonGroup from m_nameToGroupMap for
+    // We don't remove an empty RadioButtonGroup from name_to_group_map_ for
     // better performance.
     DCHECK(!group->IsRequired());
     SECURITY_DCHECK(!group->CheckedButton());
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
index 250b55c..faf40b2 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
@@ -352,11 +352,11 @@
   return Day.formatter.format(this.startDate());
 };
 
-// See WebCore/platform/DateComponents.h.
+// See platform/date_components.h.
 Day.Minimum = Day.createFromValue(-62135596800000.0);
 Day.Maximum = Day.createFromValue(8640000000000000.0);
 
-// See WebCore/html/DayInputType.cpp.
+// See core/html/forms/date_input_type.cc.
 Day.DefaultStep = 86400000;
 Day.DefaultStepBase = 0;
 
@@ -387,11 +387,11 @@
 
 Week.ISOStringRegExp = /^(\d+)-[wW](\d+)$/;
 
-// See WebCore/platform/DateComponents.h.
+// See platform/date_components.h.
 Week.Minimum = new Week(1, 1);
 Week.Maximum = new Week(275760, 37);
 
-// See WebCore/html/WeekInputType.cpp.
+// See core/html/forms/week_input_type.cc.
 Week.DefaultStep = 604800000;
 Week.DefaultStepBase = -259200000;
 
@@ -609,11 +609,11 @@
 
 Month.ISOStringRegExp = /^(\d+)-(\d+)$/;
 
-// See WebCore/platform/DateComponents.h.
+// See platform/date_components.h.
 Month.Minimum = new Month(1, 0);
 Month.Maximum = new Month(275760, 8);
 
-// See WebCore/html/MonthInputType.cpp.
+// See core/html/forms/month_input_type.cc.
 Month.DefaultStep = 1;
 Month.DefaultStepBase = 0;
 
diff --git a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
index bcaf25d..42355024 100644
--- a/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
+++ b/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -404,7 +404,7 @@
         sliding_direction_ = GetDirection(current_point, start_point_);
       }
 
-      // m_slidingDirection has been updated, so check whether it's okay to
+      // sliding_direction_ has been updated, so check whether it's okay to
       // slide again.
       if (CanSlide()) {
         thumb->SetPositionFromPoint(touches->item(0)->AbsoluteLocation());
diff --git a/third_party/blink/renderer/core/html/forms/step_range.cc b/third_party/blink/renderer/core/html/forms/step_range.cc
index 63a3a36d..3507a85 100644
--- a/third_party/blink/renderer/core/html/forms/step_range.cc
+++ b/third_party/blink/renderer/core/html/forms/step_range.cc
@@ -90,7 +90,7 @@
       rounded_value > maximum_
           ? rounded_value - step_
           : (rounded_value < minimum_ ? rounded_value + step_ : rounded_value);
-  // clampedValue can be outside of [m_minimum, m_maximum] if m_step is huge.
+  // clamped_value can be outside of [minimum_, maximum_] if step_ is huge.
   if (clamped_value < minimum_ || clamped_value > maximum_)
     return in_range_value;
   return clamped_value;
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.cc b/third_party/blink/renderer/core/html/forms/text_control_element.cc
index d5de47f..411880b4 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.cc
@@ -492,8 +492,8 @@
   return CreateVisiblePosition(it.EndPosition(), TextAffinity::kUpstream);
 }
 
-// TODO(yosin): We should move |TextControlElement::indexForVisiblePosition()|
-// to "AXLayoutObject.cpp" since this funciton is used only there.
+// TODO(yosin): We should move |TextControlElement::IndexForVisiblePosition()|
+// to "ax_layout_object.cc" since this function is used only there.
 int TextControlElement::IndexForVisiblePosition(
     const VisiblePosition& pos) const {
   Position index_position = pos.DeepEquivalent().ParentAnchoredEquivalent();
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.h b/third_party/blink/renderer/core/html/forms/text_control_element.h
index d0e6821..46a16b2 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.h
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.h
@@ -214,9 +214,9 @@
   // creating a number of TreeScope data structures to track elements by ID.
   Member<TextControlInnerEditorElement> inner_editor_;
 
-  // In m_valueBeforeFirstUserEdit, we distinguish a null String and zero-length
-  // String. Null String means the field doesn't have any data yet, and
-  // zero-length String is a valid data.
+  // In value_before_first_user_edit_, we distinguish a null String and
+  // zero-length String. Null String means the field doesn't have any data yet,
+  // and zero-length String is a valid data.
   String value_before_first_user_edit_;
   bool last_change_was_user_edit_;
 
diff --git a/third_party/blink/renderer/core/html/html_area_element.cc b/third_party/blink/renderer/core/html/html_area_element.cc
index 0c1e2a29..e7f42538 100644
--- a/third_party/blink/renderer/core/html/html_area_element.cc
+++ b/third_party/blink/renderer/core/html/html_area_element.cc
@@ -47,9 +47,9 @@
 inline HTMLAreaElement::HTMLAreaElement(Document& document)
     : HTMLAnchorElement(areaTag, document), shape_(kRect) {}
 
-// An explicit empty destructor should be in HTMLAreaElement.cpp, because
+// An explicit empty destructor should be in html_area_element.cc, because
 // if an implicit destructor is used or an empty destructor is defined in
-// HTMLAreaElement.h, when including HTMLAreaElement.h, msvc tries to expand
+// html_area_element.h, when including html_area_element.h, msvc tries to expand
 // the destructor and causes a compile error because of lack of blink::Path
 // definition.
 HTMLAreaElement::~HTMLAreaElement() = default;
diff --git a/third_party/blink/renderer/core/html/html_embed_element.cc b/third_party/blink/renderer/core/html/html_embed_element.cc
index eb45fcb..99907cd 100644
--- a/third_party/blink/renderer/core/html/html_embed_element.cc
+++ b/third_party/blink/renderer/core/html/html_embed_element.cc
@@ -140,8 +140,8 @@
     plugin_params.AppendAttribute(attribute);
 }
 
-// FIXME: This should be unified with HTMLObjectElement::updatePlugin and
-// moved down into HTMLPluginElement.cpp
+// FIXME: This should be unified with HTMLObjectElement::UpdatePlugin and
+// moved down into html_plugin_element.cc
 void HTMLEmbedElement::UpdatePluginInternal() {
   DCHECK(!GetLayoutEmbeddedObject()->ShowsUnavailablePluginIndicator());
   DCHECK(NeedsPluginUpdate());
@@ -150,7 +150,7 @@
   if (url_.IsEmpty() && service_type_.IsEmpty())
     return;
 
-  // Note these pass m_url and m_serviceType to allow better code sharing with
+  // Note these pass url_ and service_type_ to allow better code sharing with
   // <object> which modifies url and serviceType before calling these.
   if (!AllowedToLoadFrameURL(url_))
     return;
@@ -158,8 +158,8 @@
   PluginParameters plugin_params;
   ParametersForPlugin(plugin_params);
 
-  // FIXME: Can we not have layoutObject here now that beforeload events are
-  // gone?
+  // FIXME: Can we not have GetLayoutObject() here now that beforeload events
+  // are gone?
   if (!GetLayoutObject())
     return;
 
diff --git a/third_party/blink/renderer/core/html/html_html_element.cc b/third_party/blink/renderer/core/html/html_html_element.cc
index e245adb..eb9f3b1 100644
--- a/third_party/blink/renderer/core/html/html_html_element.cc
+++ b/third_party/blink/renderer/core/html/html_html_element.cc
@@ -59,7 +59,8 @@
   if (GetDocument().GetFrame()) {
     GetDocument().GetFrame()->Loader().DispatchDocumentElementAvailable();
     GetDocument().GetFrame()->Loader().RunScriptsAtDocumentElementAvailable();
-    // runScriptsAtDocumentElementAvailable might have invalidated m_document.
+    // RunScriptsAtDocumentElementAvailable might have invalidated
+    // GetDocument().
   }
 }
 
diff --git a/third_party/blink/renderer/core/html/html_image_element.cc b/third_party/blink/renderer/core/html/html_image_element.cc
index 0f32114..b7a6fe8 100644
--- a/third_party/blink/renderer/core/html/html_image_element.cc
+++ b/third_party/blink/renderer/core/html/html_image_element.cc
@@ -796,9 +796,9 @@
 }
 
 void HTMLImageElement::EnsureFallbackForGeneratedContent() {
-  // The special casing for generated content in createLayoutObject breaks the
+  // The special casing for generated content in CreateLayoutObject breaks the
   // invariant that the layout object attached to this element will always be
-  // appropriate for |m_layoutDisposition|. Force recreate it.
+  // appropriate for |layout_disposition_|. Force recreate it.
   // TODO(engedy): Remove this hack. See: https://crbug.com/671953.
   SetLayoutDisposition(LayoutDisposition::kFallbackContent,
                        true /* force_reattach */);
diff --git a/third_party/blink/renderer/core/html/html_object_element.cc b/third_party/blink/renderer/core/html/html_object_element.cc
index e71c7db..9babda6 100644
--- a/third_party/blink/renderer/core/html/html_object_element.cc
+++ b/third_party/blink/renderer/core/html/html_object_element.cc
@@ -241,7 +241,7 @@
 }
 
 // TODO(schenney): crbug.com/572908 This should be unified with
-// HTMLEmbedElement::updatePlugin and moved down into HTMLPluginElement.cpp
+// HTMLEmbedElement::UpdatePlugin and moved down into html_plugin_element.cc
 void HTMLObjectElement::UpdatePluginInternal() {
   DCHECK(!GetLayoutEmbeddedObject()->ShowsUnavailablePluginIndicator());
   DCHECK(NeedsPluginUpdate());
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index 61718697..8547bc0 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -114,10 +114,10 @@
     PreferPlugInsForImagesOption prefer_plug_ins_for_images_option)
     : HTMLFrameOwnerElement(tag_name, doc),
       is_delaying_load_event_(false),
-      // m_needsPluginUpdate(!createdByParser) allows HTMLObjectElement to delay
-      // EmbeddedContentView updates until after all children are parsed. For
-      // HTMLEmbedElement this delay is unnecessary, but it is simpler to make
-      // both classes share the same codepath in this class.
+      // needs_plugin_update_(!IsCreatedByParser) allows HTMLObjectElement to
+      // delay EmbeddedContentView updates until after all children are
+      // parsed. For HTMLEmbedElement this delay is unnecessary, but it is
+      // simpler to make both classes share the same codepath in this class.
       needs_plugin_update_(!flags.IsCreatedByParser()),
       should_prefer_plug_ins_for_images_(prefer_plug_ins_for_images_option ==
                                          kShouldPreferPlugInsForImages) {
@@ -553,7 +553,7 @@
   return ToLayoutEmbeddedObject(GetLayoutObject());
 }
 
-// We don't use m_url, as it may not be the final URL that the object loads,
+// We don't use url_, as it may not be the final URL that the object loads,
 // depending on <param> values.
 bool HTMLPlugInElement::AllowedToLoadFrameURL(const String& url) {
   KURL complete_url = GetDocument().CompleteURL(url);
diff --git a/third_party/blink/renderer/core/html/html_slot_element.cc b/third_party/blink/renderer/core/html/html_slot_element.cc
index 5b0d0f1..1f73df3 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.cc
+++ b/third_party/blink/renderer/core/html/html_slot_element.cc
@@ -314,7 +314,7 @@
   if (SupportsAssignment()) {
     AttachContext children_context(context);
 
-    for (auto& node : ChildrenInFlatTreeIfAssignmentIsSupported()) {
+    for (auto& node : AssignedNodes()) {
       if (node->NeedsAttach())
         node->AttachLayoutTree(children_context);
     }
@@ -323,14 +323,6 @@
   }
 }
 
-// TODO(hayato): Rename this function once we enable IncrementalShadowDOM
-// by default because this function doesn't consider fallback elements in case
-// of IncementalShadowDOM.
-const HeapVector<Member<Node>>&
-HTMLSlotElement::ChildrenInFlatTreeIfAssignmentIsSupported() {
-  return AssignedNodes();
-}
-
 void HTMLSlotElement::DetachLayoutTree(const AttachContext& context) {
   if (SupportsAssignment()) {
     const HeapVector<Member<Node>>& flat_tree_children = assigned_nodes_;
@@ -345,13 +337,11 @@
   if (!SupportsAssignment())
     return;
 
-  const HeapVector<Member<Node>>& flat_tree_children =
-      ChildrenInFlatTreeIfAssignmentIsSupported();
+  const HeapVector<Member<Node>>& assigned_nodes = AssignedNodes();
 
   // This loop traverses the nodes from right to left for the same reason as the
   // one described in ContainerNode::RebuildChildrenLayoutTrees().
-  for (auto it = flat_tree_children.rbegin(); it != flat_tree_children.rend();
-       ++it) {
+  for (auto it = assigned_nodes.rbegin(); it != assigned_nodes.rend(); ++it) {
     RebuildLayoutTreeForChild(*it, whitespace_attacher);
   }
 }
@@ -627,9 +617,6 @@
 void HTMLSlotElement::Trace(blink::Visitor* visitor) {
   visitor->Trace(assigned_nodes_);
   visitor->Trace(flat_tree_children_);
-  visitor->Trace(distributed_nodes_);
-  visitor->Trace(old_distributed_nodes_);
-  visitor->Trace(distributed_indices_);
   visitor->Trace(assigned_nodes_candidates_);
   HTMLElement::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/html/html_slot_element.h b/third_party/blink/renderer/core/html/html_slot_element.h
index 6d1f69c..3e03242 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.h
+++ b/third_party/blink/renderer/core/html/html_slot_element.h
@@ -53,19 +53,6 @@
   const HeapVector<Member<Element>> AssignedElementsForBinding(
       const AssignedNodesOptions&);
 
-  HeapVector<Member<Node>> DeleteCommonAssignedNodeAndReturnAddedAssignedNode(
-      const HeapVector<Member<Node>>& new_assigned_nodes);
-
-  void assign(HeapVector<Member<Node>> nodes);
-  bool ContainsInAssignedNodesCandidates(Node&) const;
-  HeapHashSet<Member<Node>>& AssignedNodesCandidate() {
-    return assigned_nodes_candidates_;
-  }
-  void SignalSlotChange();
-  void SignalSlotChangeAfterRemoved();
-
-  const HeapVector<Member<Node>> FlattenedAssignedNodes();
-
   Node* FirstAssignedNode() const {
     auto& nodes = AssignedNodes();
     return nodes.IsEmpty() ? nullptr : nodes.front().Get();
@@ -79,6 +66,10 @@
   Node* AssignedNodePreviousTo(const Node&) const;
 
   void AppendAssignedNode(Node&);
+  void ClearAssignedNodes();
+
+  const HeapVector<Member<Node>> FlattenedAssignedNodes();
+  void RecalcFlatTreeChildren();
 
   void AttachLayoutTree(AttachContext&) final;
   void DetachLayoutTree(const AttachContext& = AttachContext()) final;
@@ -112,10 +103,18 @@
   static const AtomicString& UserAgentCustomAssignSlotName();
   static const AtomicString& UserAgentDefaultSlotName();
 
-  void Trace(blink::Visitor*) override;
+  // For imperative Shadow DOM distribution APIs
+  HeapVector<Member<Node>> DeleteCommonAssignedNodeAndReturnAddedAssignedNode(
+      const HeapVector<Member<Node>>& new_assigned_nodes);
+  void assign(HeapVector<Member<Node>> nodes);
+  bool ContainsInAssignedNodesCandidates(Node&) const;
+  HeapHashSet<Member<Node>>& AssignedNodesCandidate() {
+    return assigned_nodes_candidates_;
+  }
+  void SignalSlotChange();
+  void SignalSlotChangeAfterRemoved();
 
-  void ClearAssignedNodes();
-  void RecalcFlatTreeChildren();
+  void Trace(blink::Visitor*) override;
 
  private:
   HTMLSlotElement(Document&);
@@ -128,8 +127,6 @@
 
   bool HasSlotableChild() const;
 
-  const HeapVector<Member<Node>>& ChildrenInFlatTreeIfAssignmentIsSupported();
-
   void LazyReattachNodesIfNeeded(const HeapVector<Member<Node>>& nodes1,
                                  const HeapVector<Member<Node>>& nodes2);
   static void LazyReattachNodesNaive(const HeapVector<Member<Node>>& nodes1,
@@ -145,17 +142,12 @@
   void ClearAssignedNodesAndFlatTreeChildren();
 
   HeapVector<Member<Node>> assigned_nodes_;
-  bool slotchange_event_enqueued_ = false;
-
-  HeapHashSet<Member<Node>> assigned_nodes_candidates_;
-
-  // For IncrementalShadowDOM
   HeapVector<Member<Node>> flat_tree_children_;
 
-  // For Non-IncrmentalShadowDOM. IncremntalShadowDOM never use these members.
-  HeapVector<Member<Node>> distributed_nodes_;
-  HeapVector<Member<Node>> old_distributed_nodes_;
-  HeapHashMap<Member<const Node>, wtf_size_t> distributed_indices_;
+  bool slotchange_event_enqueued_ = false;
+
+  // For imperative Shadow DOM distribution APIs
+  HeapHashSet<Member<Node>> assigned_nodes_candidates_;
 
   // TODO(hayato): Move this to more appropriate directory (e.g. platform/wtf)
   // if there are more than one usages.
diff --git a/third_party/blink/renderer/core/html/html_table_element.cc b/third_party/blink/renderer/core/html/html_table_element.cc
index 54fa5ee..bcf1f640 100644
--- a/third_party/blink/renderer/core/html/html_table_element.cc
+++ b/third_party/blink/renderer/core/html/html_table_element.cc
@@ -59,10 +59,10 @@
       rules_attr_(kUnsetRules),
       padding_(1) {}
 
-// An explicit empty destructor should be in HTMLTableElement.cpp, because
+// An explicit empty destructor should be in html_table_element.cc, because
 // if an implicit destructor is used or an empty destructor is defined in
-// HTMLTableElement.h, when including HTMLTableElement, msvc tries to expand
-// the destructor and causes a compile error because of lack of
+// html_table_element.h, when including html_table_element.h, msvc tries to
+// expand the destructor and causes a compile error because of lack of
 // CSSPropertyValueSet definition.
 HTMLTableElement::~HTMLTableElement() = default;
 
diff --git a/third_party/blink/renderer/core/html/image_document.h b/third_party/blink/renderer/core/html/image_document.h
index 6732122..36c0114 100644
--- a/third_party/blink/renderer/core/html/image_document.h
+++ b/third_party/blink/renderer/core/html/image_document.h
@@ -67,7 +67,7 @@
   // Calculates how large the div needs to be to properly center the image.
   int CalculateDivWidth();
 
-  // These methods are for m_shrinkToFitMode == Desktop.
+  // These methods are for shrink_to_fit_mode_ == kDesktop.
   void ResizeImageToFit();
   void RestoreImageSize();
   bool ImageFitsInWindow() const;
diff --git a/third_party/blink/renderer/core/html/imports/html_import.cc b/third_party/blink/renderer/core/html/imports/html_import.cc
index a9fd490..655f35e4 100644
--- a/third_party/blink/renderer/core/html/imports/html_import.cc
+++ b/third_party/blink/renderer/core/html/imports/html_import.cc
@@ -89,10 +89,10 @@
   }
 
   // The post-visit DFS order matters here because
-  // HTMLImportStateResolver in recalcState() Depends on
-  // |m_state| of its children and precedents of ancestors.
+  // HTMLImportStateResolver in RecalcState() Depends on
+  // |state_| of its children and precedents of ancestors.
   // Accidental cycle dependency of state computation is prevented
-  // by invalidateCachedState() and isStateCacheValid() check.
+  // by InvalidateCachedState() and IsStateCacheValid() check.
   for (HTMLImport* i = TraverseFirstPostOrder(root); i;
        i = TraverseNextPostOrder(i)) {
     DCHECK(!i->state_.IsValid());
diff --git a/third_party/blink/renderer/core/html/link_style.cc b/third_party/blink/renderer/core/html/link_style.cc
index 86d9cc8..fbec39a5 100644
--- a/third_party/blink/renderer/core/html/link_style.cc
+++ b/third_party/blink/renderer/core/html/link_style.cc
@@ -70,7 +70,7 @@
   }
 
   CSSStyleSheetResource* cached_style_sheet = ToCSSStyleSheetResource(resource);
-  // See the comment in PendingScript.cpp about why this check is necessary
+  // See the comment in pending_script.cc about why this check is necessary
   // here, instead of in the resource fetcher. https://crbug.com/500701.
   if (!cached_style_sheet->ErrorOccurred() &&
       !owner_->FastGetAttribute(integrityAttr).IsEmpty() &&
@@ -192,7 +192,7 @@
   if (type == kNone)
     return;
   if (type == kNonBlocking) {
-    // Tell StyleEngine to re-compute styleSheets of this m_owner's treescope.
+    // Tell StyleEngine to re-compute styleSheets of this owner_'s treescope.
     GetDocument().GetStyleEngine().ModifiedStyleSheetCandidateNode(*owner_);
     return;
   }
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc
index bd6bc6b..fb5b158 100644
--- a/third_party/blink/renderer/core/html/media/html_video_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -190,8 +190,8 @@
     // In case the poster attribute is set after playback, don't update the
     // display state, post playback the correct state will be picked up.
     if (GetDisplayMode() < kVideo || !HasAvailableVideoFrame()) {
-      // Force a poster recalc by setting m_displayMode to Unknown directly
-      // before calling updateDisplayState.
+      // Force a poster recalc by setting display_mode_ to kUnknown directly
+      // before calling UpdateDisplayState.
       HTMLMediaElement::SetDisplayMode(kUnknown);
       UpdateDisplayState();
     }
diff --git a/third_party/blink/renderer/core/html/parser/atomic_html_token.cc b/third_party/blink/renderer/core/html/parser/atomic_html_token.cc
index 24671e6..419fbf3f 100644
--- a/third_party/blink/renderer/core/html/parser/atomic_html_token.cc
+++ b/third_party/blink/renderer/core/html/parser/atomic_html_token.cc
@@ -56,7 +56,7 @@
     default:
       break;
   }
-  // TODO(kouhei): print m_attributes?
+  // TODO(kouhei): print attributes_?
   printf("\n");
 }
 #endif
diff --git a/third_party/blink/renderer/core/html/parser/background_html_parser.h b/third_party/blink/renderer/core/html/parser/background_html_parser.h
index 1b14977d..5579a54 100644
--- a/third_party/blink/renderer/core/html/parser/background_html_parser.h
+++ b/third_party/blink/renderer/core/html/parser/background_html_parser.h
@@ -124,8 +124,8 @@
   DocumentEncodingData last_seen_encoding_data_;
   scoped_refptr<base::SingleThreadTaskRunner> loading_task_runner_;
 
-  // Index into |m_pendingTokens| of the last <meta> csp token found. Will be
-  // |TokenizedChunk::noPendingToken| if none have been found.
+  // Index into |pending_tokens_| of the last <meta> csp token found. Will be
+  // |TokenizedChunk::kNoPendingToken| if none have been found.
   int pending_csp_meta_token_index_;
 
   bool starting_script_;
diff --git a/third_party/blink/renderer/core/html/parser/compact_html_token.cc b/third_party/blink/renderer/core/html/parser/compact_html_token.cc
index 291a648..8291327 100644
--- a/third_party/blink/renderer/core/html/parser/compact_html_token.cc
+++ b/third_party/blink/renderer/core/html/parser/compact_html_token.cc
@@ -54,7 +54,7 @@
       data_ = AttemptStaticStringCreation(token->GetName(), kLikely8Bit);
 
       // There is only 1 DOCTYPE token per document, so to avoid increasing the
-      // size of CompactHTMLToken, we just use the m_attributes vector.
+      // size of CompactHTMLToken, we just use the attributes_ vector.
       attributes_.push_back(Attribute(
           AttemptStaticStringCreation(token->PublicIdentifier(), kLikely8Bit),
           String(token->SystemIdentifier())));
diff --git a/third_party/blink/renderer/core/html/parser/compact_html_token.h b/third_party/blink/renderer/core/html/parser/compact_html_token.h
index 176f1ec..ccb3c6f 100644
--- a/third_party/blink/renderer/core/html/parser/compact_html_token.h
+++ b/third_party/blink/renderer/core/html/parser/compact_html_token.h
@@ -71,7 +71,7 @@
   const TextPosition& GetTextPosition() const { return text_position_; }
 
   // There is only 1 DOCTYPE token per document, so to avoid increasing the
-  // size of CompactHTMLToken, we just use the m_attributes vector.
+  // size of CompactHTMLToken, we just use the attributes_ vector.
   const String& PublicIdentifier() const { return attributes_[0].GetName(); }
   const String& SystemIdentifier() const { return attributes_[0].Value(); }
   bool DoctypeForcesQuirks() const { return doctype_forces_quirks_; }
@@ -82,7 +82,7 @@
   unsigned is_all8_bit_data_ : 1;
   unsigned doctype_forces_quirks_ : 1;
 
-  String data_;  // "name", "characters", or "data" depending on m_type
+  String data_;  // "name", "characters", or "data" depending on type_
   Vector<Attribute> attributes_;
   TextPosition text_position_;
 };
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index b784db02..b57759975 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -110,7 +110,7 @@
     : HTMLDocumentParser(fragment->GetDocument(),
                          parser_content_policy,
                          kForceSynchronousParsing) {
-  // No m_scriptRunner in fragment parser.
+  // No script_runner_ in fragment parser.
   tree_builder_ = HTMLTreeBuilder::Create(this, fragment, context_element,
                                           parser_content_policy, options_);
 
@@ -191,10 +191,10 @@
     parser_scheduler_->Detach();
     parser_scheduler_.Clear();
   }
-  // Oilpan: It is important to clear m_token to deallocate backing memory of
-  // HTMLToken::m_data and let the allocator reuse the memory for
-  // HTMLToken::m_data of a next HTMLDocumentParser. We need to clear
-  // m_tokenizer first because m_tokenizer has a raw pointer to m_token.
+  // Oilpan: It is important to clear token_ to deallocate backing memory of
+  // HTMLToken::data_ and let the allocator reuse the memory for
+  // HTMLToken::data_ of a next HTMLDocumentParser. We need to clear
+  // tokenizer_ first because tokenizer_ has a raw pointer to token_.
   tokenizer_.reset();
   token_.reset();
 }
@@ -370,14 +370,14 @@
     std::unique_ptr<TokenizedChunk> chunk) {
   DCHECK(chunk);
   // TODO(kouhei): We should simplify codepath here by disallowing
-  // validateSpeculations
-  // while isPaused, and m_lastChunkBeforePause can simply be
-  // pushed to m_speculations.
+  // ValidateSpeculations
+  // while IsPaused, and last_chunk_before_pause_ can simply be
+  // pushed to speculations_.
   if (IsPaused()) {
     // We're waiting on a network script or stylesheet, just save the chunk,
-    // we'll get a second validateSpeculations call after the script or
+    // we'll get a second ValidateSpeculations call after the script or
     // stylesheet completes. This call should have been made immediately after
-    // runScriptsForPausedTreeBuilder in the script case which may have started
+    // RunScriptsForPausedTreeBuilder in the script case which may have started
     // a network load and left us waiting.
     DCHECK(!last_chunk_before_pause_);
     last_chunk_before_pause_ = std::move(chunk);
@@ -561,8 +561,8 @@
 }
 
 void HTMLDocumentParser::PumpPendingSpeculations() {
-  // If this assert fails, you need to call validateSpeculations to make sure
-  // m_tokenizer and m_token don't have state that invalidates m_speculations.
+  // If this assert fails, you need to call ValidateSpeculations to make sure
+  // tokenizer_ and token_ don't have state that invalidates speculations_.
   DCHECK(!tokenizer_);
   DCHECK(!token_);
   DCHECK(!last_chunk_before_pause_);
@@ -593,9 +593,9 @@
         ProcessTokenizedChunkFromBackgroundParser(speculations_.TakeFirst());
     session.AddedElementTokens(element_token_count);
 
-    // Always check isParsing first as m_document may be null. Surprisingly,
-    // isScheduledForUnpause() may be set here as a result of
-    // processTokenizedChunkFromBackgroundParser running arbitrary javascript
+    // Always check IsParsing first as document_ may be null. Surprisingly,
+    // IsScheduledForUnpause() may be set here as a result of
+    // ProcessTokenizedChunkFromBackgroundParser running arbitrary javascript
     // which invokes nested event loops. (e.g. inspector breakpoints)
     CheckIfBodyStylesheetAdded();
     if (!IsParsing() || IsPaused() || IsScheduledForUnpause())
@@ -632,9 +632,9 @@
 
   // We tell the InspectorInstrumentation about every pump, even if we end up
   // pumping nothing.  It can filter out empty pumps itself.
-  // FIXME: m_input.current().length() is only accurate if we end up parsing the
+  // FIXME: input_.Current().length() is only accurate if we end up parsing the
   // whole buffer in this pump.  We should pass how much we parsed as part of
-  // didWriteHTML instead of willWriteHTML.
+  // DidWriteHTML instead of WillWriteHTML.
   probe::ParseHTML probe(GetDocument(), this);
 
   if (!IsParsingFragment())
@@ -684,7 +684,7 @@
     DCHECK_EQ(tokenizer_->GetState(), HTMLTokenizer::kDataState);
 
     DCHECK(preloader_);
-    // TODO(kouhei): m_preloader should be always available for synchronous
+    // TODO(kouhei): preloader_ should be always available for synchronous
     // parsing case, adding paranoia if for speculative crash fix for
     // crbug.com/465478
     if (preloader_) {
@@ -702,23 +702,23 @@
   DCHECK(!GetDocument()->IsPrefetchOnly());
   AtomicHTMLToken atomic_token(Token());
 
-  // We clear the m_token in case constructTreeFromAtomicToken
+  // We clear the token_ in case ConstructTreeFromAtomicToken
   // synchronously re-enters the parser. We don't clear the token immedately
-  // for Character tokens because the AtomicHTMLToken avoids copying the
+  // for kCharacter tokens because the AtomicHTMLToken avoids copying the
   // characters by keeping a pointer to the underlying buffer in the
-  // HTMLToken. Fortunately, Character tokens can't cause us to re-enter
+  // HTMLToken. Fortunately, kCharacter tokens can't cause us to re-enter
   // the parser.
   //
-  // FIXME: Stop clearing the m_token once we start running the parser off
+  // FIXME: Stop clearing the token_ once we start running the parser off
   // the main thread or once we stop allowing synchronous JavaScript
-  // execution from parseAttribute.
+  // execution from ParseAttribute.
   if (Token().GetType() != HTMLToken::kCharacter)
     Token().Clear();
 
   tree_builder_->ConstructTree(&atomic_token);
   CheckIfBodyStylesheetAdded();
 
-  // FIXME: constructTree may synchronously cause Document to be detached.
+  // FIXME: ConstructTree may synchronously cause Document to be detached.
   if (!token_)
     return;
 
@@ -931,9 +931,9 @@
 }
 
 void HTMLDocumentParser::Finish() {
-  // FIXME: We should DCHECK(!m_parserStopped) here, since it does not makes
+  // FIXME: We should DCHECK(!parser_stopped_) here, since it does not makes
   // sense to call any methods on DocumentParser once it's been stopped.
-  // However, FrameLoader::stop calls DocumentParser::finish unconditionally.
+  // However, FrameLoader::Stop calls DocumentParser::Finish unconditionally.
 
   Flush();
   if (IsDetached())
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.h b/third_party/blink/renderer/core/html/parser/html_document_parser.h
index 171f7eb4..342b24a 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.h
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.h
@@ -223,7 +223,7 @@
       TokenPreloadScanner::ScannerType);
 
   // Let the given HTMLPreloadScanner scan the input it has, and then preloads
-  // resources using the resulting PreloadRequests and |m_preloader|.
+  // resources using the resulting PreloadRequests and |preloader_|.
   void ScanAndPreload(HTMLPreloadScanner*);
   void FetchQueuedPreloads();
 
@@ -249,7 +249,7 @@
   XSSAuditor xss_auditor_;
   XSSAuditorDelegate xss_auditor_delegate_;
 
-  // FIXME: m_lastChunkBeforePause, m_tokenizer, m_token, and m_input should be
+  // FIXME: last_chunk_before_pause_, tokenizer_, token_, and input_ should be
   // combined into a single state object so they can be set and cleared together
   // and passed between threads together.
   std::unique_ptr<TokenizedChunk> last_chunk_before_pause_;
diff --git a/third_party/blink/renderer/core/html/parser/html_formatting_element_list.h b/third_party/blink/renderer/core/html/parser/html_formatting_element_list.h
index 074b2a76..fe0a6e3 100644
--- a/third_party/blink/renderer/core/html/parser/html_formatting_element_list.h
+++ b/third_party/blink/renderer/core/html/parser/html_formatting_element_list.h
@@ -61,8 +61,8 @@
 
     HTMLStackItem* StackItem() const { return item_; }
     Element* GetElement() const {
-      // The fact that !m_item == isMarker() is an implementation detail callers
-      // should check isMarker() before calling element().
+      // The fact that !item_ == IsMarker() is an implementation detail callers
+      // should check IsMarker() before calling GetElement().
       DCHECK(item_);
       return item_->GetElement();
     }
diff --git a/third_party/blink/renderer/core/html/parser/html_input_stream.h b/third_party/blink/renderer/core/html/parser/html_input_stream.h
index 32bc77a8..e0c6415 100644
--- a/third_party/blink/renderer/core/html/parser/html_input_stream.h
+++ b/third_party/blink/renderer/core/html/parser/html_input_stream.h
@@ -36,7 +36,7 @@
 // The InputStream is made up of a sequence of SegmentedStrings:
 //
 // [--current--][--next--][--next--] ... [--next--]
-//            /\                         (also called m_last)
+//            /\                         (also called last_)
 //            L_ current insertion point
 //
 // The current segmented string is stored in InputStream.  Each of the
@@ -46,7 +46,7 @@
 // document.write() will add characters at the current insertion point, which
 // appends them to the "current" string.
 //
-// m_last is a pointer to the last of the afterInsertionPoint strings. The
+// last_ is a pointer to the last of the afterInsertionPoint strings. The
 // network adds data at the end of the InputStream, which appends them to the
 // "last" string.
 class HTMLInputStream {
@@ -80,7 +80,7 @@
     first_ = SegmentedString();
     if (last_ == &first_) {
       // We used to only have one SegmentedString in the InputStream but now we
-      // have two.  That means m_first is no longer also the m_last string,
+      // have two.  That means first_ is no longer also the last_ string,
       // |next| is now the last one.
       last_ = &next;
     }
@@ -90,13 +90,13 @@
     first_.Append(next);
     if (last_ == &next) {
       // The string |next| used to be the last SegmentedString in
-      // the InputStream.  Now that it's been merged into m_first,
-      // that makes m_first the last one.
+      // the InputStream.  Now that it's been merged into first_,
+      // that makes first_ the last one.
       last_ = &first_;
     }
     if (next.IsClosed()) {
-      // We also need to merge the "closed" state from next to m_first.
-      // Arguably, this work could be done in append().
+      // We also need to merge the "closed" state from next to first_.
+      // Arguably, this work could be done in Append().
       first_.Close();
     }
   }
diff --git a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
index f3c1555..8ff868d 100644
--- a/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
+++ b/third_party/blink/renderer/core/html/parser/html_preload_scanner.cc
@@ -200,11 +200,11 @@
   void HandlePictureSourceURL(PictureData& picture_data) {
     if (Match(tag_impl_, sourceTag) && matched_ &&
         picture_data.source_url.IsEmpty()) {
-      // Must create an isolatedCopy() since the srcset attribute value will get
+      // Must create an IsolatedCopy() since the srcset attribute value will get
       // sent back to the main thread between when we set this, and when we
-      // process the closing tag which would clear m_pictureData. Having any ref
+      // process the closing tag which would clear picture_data_. Having any ref
       // to a string we're going to send will fail
-      // isSafeToSendToAnotherThread().
+      // IsSafeToSendToAnotherThread().
       picture_data.source_url =
           srcset_image_candidate_.ToString().IsolatedCopy();
       picture_data.source_size_set = source_size_set_;
diff --git a/third_party/blink/renderer/core/html/parser/html_token.h b/third_party/blink/renderer/core/html/parser/html_token.h
index de0d0e1..3176a0d 100644
--- a/third_party/blink/renderer/core/html/parser/html_token.h
+++ b/third_party/blink/renderer/core/html/parser/html_token.h
@@ -451,7 +451,7 @@
   bool self_closing_;
   AttributeList attributes_;
 
-  // A pointer into m_attributes used during lexing.
+  // A pointer into attributes_ used during lexing.
   Attribute* current_attribute_;
 
   // For DOCTYPE
diff --git a/third_party/blink/renderer/core/html/parser/html_tokenizer.cc b/third_party/blink/renderer/core/html/parser/html_tokenizer.cc
index d2e8e49..83353e4 100644
--- a/third_party/blink/renderer/core/html/parser/html_tokenizer.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tokenizer.cc
@@ -871,7 +871,7 @@
       // We're supposed to switch back to the attribute value state that
       // we were in when we were switched into this state. Rather than
       // keeping track of this explictly, we observe that the previous
-      // state can be determined by m_additionalAllowedCharacter.
+      // state can be determined by additional_allowed_character_.
       if (additional_allowed_character_ == '"')
         HTML_SWITCH_TO(kAttributeValueDoubleQuotedState);
       else if (additional_allowed_character_ == '\'')
@@ -1444,7 +1444,7 @@
 }
 
 String HTMLTokenizer::BufferedCharacters() const {
-  // FIXME: Add an assert about m_state.
+  // FIXME: Add a DCHECK about state_.
   StringBuilder characters;
   characters.ReserveCapacity(NumberOfBufferedCharacters());
   characters.Append('<');
diff --git a/third_party/blink/renderer/core/html/parser/html_tokenizer.h b/third_party/blink/renderer/core/html/parser/html_tokenizer.h
index a44d76ced..ca69ee9 100644
--- a/third_party/blink/renderer/core/html/parser/html_tokenizer.h
+++ b/third_party/blink/renderer/core/html/parser/html_tokenizer.h
@@ -139,8 +139,8 @@
   String BufferedCharacters() const;
 
   wtf_size_t NumberOfBufferedCharacters() const {
-    // Notice that we add 2 to the length of the m_temporaryBuffer to
-    // account for the "</" characters, which are effecitvely buffered in
+    // Notice that we add 2 to the length of the temporary_buffer_ to
+    // account for the "</" characters, which are effectively buffered in
     // the tokenizer's state machine.
     return temporary_buffer_.size() ? temporary_buffer_.size() + 2 : 0;
   }
@@ -260,7 +260,7 @@
   bool force_null_character_replacement_;
   bool should_allow_cdata_;
 
-  // m_token is owned by the caller. If nextToken is not on the stack,
+  // token_ is owned by the caller. If NextToken is not on the stack,
   // this member might be pointing to unallocated memory.
   HTMLToken* token_;
 
diff --git a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
index bbfd0b70..214c26e 100644
--- a/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
+++ b/third_party/blink/renderer/core/html/parser/html_tree_builder.cc
@@ -283,11 +283,11 @@
 void HTMLTreeBuilder::Detach() {
 #if DCHECK_IS_ON()
   // This call makes little sense in fragment mode, but for consistency
-  // DocumentParser expects detach() to always be called before it's destroyed.
+  // DocumentParser expects Detach() to always be called before it's destroyed.
   is_attached_ = false;
 #endif
-  // HTMLConstructionSite might be on the callstack when detach() is called
-  // otherwise we'd just call m_tree.clear() here instead.
+  // HTMLConstructionSite might be on the callstack when Detach() is called
+  // otherwise we'd just call tree_.Clear() here instead.
   tree_.Detach();
 }
 
@@ -1816,7 +1816,7 @@
     return false;
   }
   tree_.GenerateImpliedEndTags();
-  // FIXME: parse error if (!m_tree.currentStackItem()->hasTagName(captionTag))
+  // FIXME: parse error if (!tree_.CurrentStackItem()->HasTagName(captionTag))
   tree_.OpenElements()->PopUntilPopped(captionTag.LocalName());
   tree_.ActiveFormattingElements()->ClearToLastMarker();
   SetInsertionMode(kInTableMode);
diff --git a/third_party/blink/renderer/core/html/parser/text_resource_decoder.cc b/third_party/blink/renderer/core/html/parser/text_resource_decoder.cc
index 87c2953..23d1073a 100644
--- a/third_party/blink/renderer/core/html/parser/text_resource_decoder.cc
+++ b/third_party/blink/renderer/core/html/parser/text_resource_decoder.cc
@@ -369,7 +369,7 @@
 //      the encoding of the parent frame, which is also auto-detected.
 //   Note that condition #2 is NOT satisfied unless parent-child frame
 //   relationship is compliant to the same-origin policy. If they're from
-//   different domains, |m_source| would not be set to EncodingFromParentFrame
+//   different domains, |source_| would not be set to EncodingFromParentFrame
 //   in the first place.
 void TextResourceDecoder::AutoDetectEncodingIfAllowed(const char* data,
                                                       wtf_size_t len) {
diff --git a/third_party/blink/renderer/core/html/parser/xss_auditor.cc b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
index 23883ed..c9117fe 100644
--- a/third_party/blink/renderer/core/html/parser/xss_auditor.cc
+++ b/third_party/blink/renderer/core/html/parser/xss_auditor.cc
@@ -150,7 +150,7 @@
 }
 
 // If other files need this, we should move this to
-// core/html/parser/HTMLParserIdioms.h
+// core/html/parser/html_parser_idioms.h
 template <wtf_size_t inlineCapacity>
 bool ThreadSafeMatch(const Vector<UChar, inlineCapacity>& vector,
                      const QualifiedName& qname) {
@@ -204,8 +204,8 @@
 static inline String DecodeStandardURLEscapeSequences(
     const String& string,
     const WTF::TextEncoding& encoding) {
-  // We use decodeEscapeSequences() instead of decodeURLEscapeSequences()
-  // (declared in weborigin/KURL.h) to avoid platform-specific URL decoding
+  // We use DecodeEscapeSequences() instead of DecodeURLEscapeSequences()
+  // (declared in weborigin/kurl.h) to avoid platform-specific URL decoding
   // differences (e.g. KURLGoogle).
   return DecodeEscapeSequences<URLEscapeSequence>(string, encoding);
 }
diff --git a/third_party/blink/renderer/core/html/track/html_track_element.cc b/third_party/blink/renderer/core/html/track/html_track_element.cc
index fbe8ffce..ba60beff 100644
--- a/third_party/blink/renderer/core/html/track/html_track_element.cc
+++ b/third_party/blink/renderer/core/html/track/html_track_element.cc
@@ -250,7 +250,7 @@
   // track element. This task must use the DOM manipulation task source.
   //
   // (Note: We don't "queue a task" here because this method will only be called
-  // from a timer - m_loadTimer or TextTrackLoader::m_cueLoadTimer - which
+  // from a timer - load_timer_ or TextTrackLoader::cue_load_timer_ - which
   // should be a reasonable, and hopefully non-observable, approximation of the
   // spec text. I.e we could consider this to be run from the "networking task
   // source".)
diff --git a/third_party/blink/renderer/core/html/track/text_track_container.cc b/third_party/blink/renderer/core/html/track/text_track_container.cc
index bec2694..f2b58c04b 100644
--- a/third_party/blink/renderer/core/html/track/text_track_container.cc
+++ b/third_party/blink/renderer/core/html/track/text_track_container.cc
@@ -118,9 +118,9 @@
 
   // Avoid excessive FP precision issue.
   // C11 5.2.4.2.2:9 requires assignment and cast to remove extra precision, but
-  // the behavior is currently not portable. fontSize may have precision higher
-  // than m_fontSize thus straight comparison can fail despite they cast to the
-  // same float value.
+  // the behavior is currently not portable. font_size may have precision higher
+  // than default_font_size_ thus straight comparison can fail despite they cast
+  // to the same float value.
   volatile float& current_font_size = default_font_size_;
   float old_font_size = current_font_size;
   current_font_size = font_size;
diff --git a/third_party/blink/renderer/core/html/track/text_track_cue_list.cc b/third_party/blink/renderer/core/html/track/text_track_cue_list.cc
index b22c47c0..64d5936 100644
--- a/third_party/blink/renderer/core/html/track/text_track_cue_list.cc
+++ b/third_party/blink/renderer/core/html/track/text_track_cue_list.cc
@@ -126,13 +126,13 @@
   //   cueIndex(list[index-1]) + 1 == cueIndex(list[index]) [index > 0]
   // This is a stronger requirement than we need, but it's easier to maintain.
   // We can then check if a cue's index is valid by comparing it with
-  // |m_firstInvalidIndex| - if it's strictly less it is valid.
+  // |first_invalid_index_| - if it's strictly less it is valid.
   first_invalid_index_ = std::min(first_invalid_index_, index);
 }
 
 void TextTrackCueList::ValidateCueIndexes() {
   // Compute new index values for the cues starting at
-  // |m_firstInvalidIndex|. If said index is beyond the end of the list, no
+  // |first_invalid_index_|. If said index is beyond the end of the list, no
   // cues will need to be updated.
   for (wtf_size_t i = first_invalid_index_; i < list_.size(); ++i)
     list_[i]->UpdateCueIndex(i);
diff --git a/third_party/blink/renderer/core/html/track/vtt/buffered_line_reader.cc b/third_party/blink/renderer/core/html/track/vtt/buffered_line_reader.cc
index 1039224..9aaff444 100644
--- a/third_party/blink/renderer/core/html/track/vtt/buffered_line_reader.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/buffered_line_reader.cc
@@ -43,7 +43,7 @@
       ScanCharacter(kNewlineCharacter);
       maybe_skip_lf_ = false;
     }
-    // If there was no (new) data available, then keep m_maybeSkipLF set,
+    // If there was no (new) data available, then keep maybe_skip_lf_ set,
     // and fall through all the way down to the EOS check at the end of
     // the method.
   }
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc
index 53b4e16..a147bf0f 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_cue.cc
@@ -535,7 +535,7 @@
   }
 };
 
-// Almost the same as determineDirectionality in core/html/HTMLElement.cpp, but
+// Almost the same as determineDirectionality in core/html/html_element.cc, but
 // that one uses a "plain" TextRunIterator (which only checks for '\n').
 static TextDirection DetermineDirectionality(const String& value,
                                              bool& has_strong_directionality) {
@@ -692,7 +692,7 @@
         NOTREACHED();
     }
   } else {
-    // Cases for m_writingDirection being VerticalGrowing{Left|Right}
+    // Cases for writing_direction_ being kVerticalGrowing{Left|Right}
     switch (computed_cue_alignment) {
       case kStart:
         display_parameters.position.SetY(computed_text_position);
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
index 671f13c1..4a36c646 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
@@ -564,7 +564,7 @@
         break;
 
       // The only non-VTTElement would be the DocumentFragment root. (Text
-      // nodes and PIs will never appear as m_currentNode.)
+      // nodes and PIs will never appear as current_node_.)
       if (!current_node_->IsVTTElement())
         break;
 
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.h b/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.h
index cc5667c..2532e7c 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.h
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.h
@@ -148,7 +148,7 @@
   UChar CurrentChar() const;
   void Advance(unsigned amount = 1);
   // Adapt a UChar-predicate to an LChar-predicate.
-  // (For use with skipWhile/Until from ParsingUtilities.h).
+  // (For use with SkipWhile/Until from parsing_utilities.h).
   template <bool characterPredicate(UChar)>
   static inline bool LCharPredicateAdapter(LChar c) {
     return characterPredicate(c);
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 59b9423..632c6a6 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -903,7 +903,7 @@
           ->GetScriptController()
           .ExecuteScriptInMainWorldAndReturnValue(
               ScriptSourceCode(script, ScriptSourceLocationType::kInspector),
-              KURL(), kNotSharableCrossOrigin, ScriptFetchOptions(),
+              KURL(), kOpaqueResource, ScriptFetchOptions(),
               ScriptController::kExecuteScriptWhenScriptsDisabled);
   return ToCoreStringWithUndefinedOrNullCheck(string);
 }
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
index c238218..83e35ee4 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -879,9 +879,11 @@
                                                       security_origin);
     }
 
+    // Note: An error event in an isolated world will never be dispatched to
+    // a foreign world.
     v8::HandleScope handle_scope(V8PerIsolateData::MainThreadIsolate());
     frame->GetScriptController().ExecuteScriptInIsolatedWorld(
-        world_id, source, KURL(), kNotSharableCrossOrigin);
+        world_id, source, KURL(), kOpaqueResource);
   }
 
   if (!script_to_evaluate_on_load_once_.IsEmpty()) {
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index b136f89..f1415c0 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -437,14 +437,6 @@
   return StyleRef().FlexWrap() != EFlexWrap::kNowrap;
 }
 
-bool LayoutFlexibleBox::ShouldApplyMinSizeAutoForChild(
-    const LayoutBox& child) const {
-  Length min = IsHorizontalFlow() ? child.StyleRef().MinWidth()
-                                  : child.StyleRef().MinHeight();
-  return min.IsAuto() && !child.ShouldApplySizeContainment() &&
-         MainAxisOverflowForChild(child) == EOverflow::kVisible;
-}
-
 Length LayoutFlexibleBox::FlexBasisForChild(const LayoutBox& child) const {
   Length flex_length = child.StyleRef().FlexBasis();
   if (flex_length.IsAuto()) {
@@ -1005,7 +997,14 @@
     // computeMainAxisExtentForChild can return -1 when the child has a
     // percentage min size, but we have an indefinite size in that axis.
     sizes.min_size = std::max(LayoutUnit(), sizes.min_size);
-  } else if (ShouldApplyMinSizeAutoForChild(child)) {
+  } else if (min.IsAuto() && !child.ShouldApplySizeContainment() &&
+             MainAxisOverflowForChild(child) == EOverflow::kVisible &&
+             !(IsColumnFlow() && child.IsFlexibleBox())) {
+    // TODO(cbiesinger): For now, we do not handle min-height: auto for nested
+    // column flexboxes. We need to implement
+    // https://drafts.csswg.org/css-flexbox/#intrinsic-sizes before that
+    // produces reasonable results. Tracking bug: https://crbug.com/581553
+    // css-flexbox section 4.5
     LayoutUnit content_size =
         ComputeMainAxisExtentForChild(child, kMinSize, Length(kMinContent));
     DCHECK_GE(content_size, LayoutUnit());
@@ -1125,14 +1124,6 @@
 DISABLE_CFI_PERF
 FlexItem LayoutFlexibleBox::ConstructFlexItem(LayoutBox& child,
                                               ChildLayoutType layout_type) {
-  if (layout_type == kLayoutIfNeeded && IsColumnFlow() &&
-      child.IsFlexibleBox() && ShouldApplyMinSizeAutoForChild(child)) {
-    // In this case, we have to force-layout to update the intrinsic height of
-    // our child; otherwise, it may be too big because it is based on previous
-    // flexing of a descendant, which would be a problem for applying
-    // min-size: auto.
-    layout_type = kForceLayout;
-  }
   if (layout_type != kNeverLayout && ChildHasIntrinsicMainAxisSize(child)) {
     // If this condition is true, then ComputeMainAxisExtentForChild will call
     // child.IntrinsicContentLogicalHeight() and
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.h b/third_party/blink/renderer/core/layout/layout_flexible_box.h
index 156ca0fa..a2eb5329 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.h
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.h
@@ -111,7 +111,6 @@
   bool IsColumnFlow() const;
   bool IsLeftToRightFlow() const;
   bool IsMultiline() const;
-  bool ShouldApplyMinSizeAutoForChild(const LayoutBox& child) const;
   Length FlexBasisForChild(const LayoutBox& child) const;
   LayoutUnit CrossAxisExtentForChild(const LayoutBox& child) const;
   LayoutUnit CrossAxisIntrinsicExtentForChild(const LayoutBox& child) const;
diff --git a/third_party/blink/renderer/core/layout/layout_grid.cc b/third_party/blink/renderer/core/layout/layout_grid.cc
index aa0d5c3..f4abe07c 100644
--- a/third_party/blink/renderer/core/layout/layout_grid.cc
+++ b/third_party/blink/renderer/core/layout/layout_grid.cc
@@ -2109,8 +2109,8 @@
     LayoutUnit& start,
     LayoutUnit& end) const {
   DCHECK(!child.IsOutOfFlowPositioned());
-  const GridSpan& span =
-      track_sizing_algorithm_.GetGrid().GridItemSpan(child, direction);
+  const Grid& grid = track_sizing_algorithm_.GetGrid();
+  const GridSpan& span = grid.GridItemSpan(child, direction);
   // TODO (lajava): This is a common pattern, why not defining a function like
   // positions(direction) ?
   auto& positions =
@@ -2120,8 +2120,11 @@
   // The 'positions' vector includes distribution offset (because of content
   // alignment) and gutters so we need to subtract them to get the actual
   // end position for a given track (this does not have to be done for the
-  // last track as there are no more positions's elements after it).
-  if (span.EndLine() < positions.size() - 1)
+  // last track as there are no more positions's elements after it, nor for
+  // collapsed tracks).
+  if (span.EndLine() < positions.size() - 1 &&
+      !(grid.HasAutoRepeatEmptyTracks(direction) &&
+        grid.IsEmptyAutoRepeatTrack(direction, span.EndLine())))
     end -= GridGap(direction) + GridItemOffset(direction);
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index 72d5bf83..e2cde875 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -217,7 +217,9 @@
       for (InlineTextBox* box : TextBoxes())
         box->Remove();
     } else if (Parent()) {
-      Parent()->DirtyLinesFromChangedChild(this);
+      if (!FirstInlineFragment() ||
+          !NGPaintFragment::TryMarkLineBoxDirtyFor(*this))
+        Parent()->DirtyLinesFromChangedChild(this);
     }
   }
   DeleteTextBoxes();
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 f5c1a3d..1b686252 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
@@ -676,6 +676,7 @@
     FloatingObject* floating_object =
         ToLayoutBlockFlow(containing_block)->InsertFloatingObject(*layout_box);
     floating_object->SetIsInPlacedTree(false);
+    floating_object->SetShouldPaint(!layout_box->HasSelfPaintingLayer());
     LayoutUnit horizontal_margin_edge_offset = horizontal_offset;
     if (has_flipped_x_axis)
       horizontal_margin_edge_offset -= layout_box->MarginRight();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 8fe5749..7abb0f8e 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
-#include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_object_inlines.h"
@@ -77,6 +77,12 @@
          ToLayoutBox(layout_object)->ShouldClipOverflow();
 }
 
+bool NGPhysicalBoxFragment::HasControlClip() const {
+  const LayoutObject* layout_object = GetLayoutObject();
+  DCHECK(layout_object);
+  return layout_object->IsBox() && ToLayoutBox(layout_object)->HasControlClip();
+}
+
 LayoutRect NGPhysicalBoxFragment::OverflowClipRect(
     const LayoutPoint& location,
     OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
@@ -150,14 +156,11 @@
     Vector<LayoutRect>* outline_rects,
     const LayoutPoint& additional_offset,
     NGOutlineType outline_type) const {
-  DCHECK(GetLayoutObject());
+  // TODO(kojii): Needs inline_element_continuation logic from
+  // LayoutBlockFlow::AddOutlineRects?
 
   const LayoutObject* layout_object = GetLayoutObject();
-  if (!layout_object->IsAnonymousBlock() && !layout_object->IsLayoutInline()) {
-    LayoutRect outline_rect(additional_offset, Size().ToLayoutSize());
-    outline_rects->push_back(outline_rect);
-  }
-
+  DCHECK(layout_object);
   if (layout_object->IsLayoutInline()) {
     Vector<LayoutRect> blockflow_outline_rects;
     ToLayoutInline(layout_object)
@@ -174,46 +177,24 @@
     }
     return;
   }
-  if (!layout_object->IsBox())
-    return;
-  if (outline_type == NGOutlineType::kDontIncludeBlockVisualOverflow)
-    return;
-  if (HasOverflowClip() && ToLayoutBox(layout_object)->HasControlClip())
-    return;
+  DCHECK(layout_object->IsBox());
 
-  for (const auto& child : Children()) {
-    // List markers have no outline
-    if (child->IsListMarker())
-      continue;
-
-    if (child->IsLineBox()) {
-      // Traverse children of the linebox
-      Vector<NGPhysicalFragmentWithOffset> line_children =
-          NGInlineFragmentTraversal::DescendantsOf(
-              ToNGPhysicalLineBoxFragment(*child));
-      for (const auto& line_child : line_children) {
-        Vector<LayoutRect> line_child_rects;
-        line_child_rects.push_back(
-            line_child.RectInContainerBox().ToLayoutRect());
-        DCHECK(line_child.fragment->GetLayoutObject());
-        line_child.fragment->GetLayoutObject()->LocalToAncestorRects(
-            line_child_rects, ToLayoutBoxModelObject(GetLayoutObject()),
-            child.Offset().ToLayoutPoint(), additional_offset);
-        if (!line_child_rects.IsEmpty())
-          outline_rects->push_back(line_child_rects[0]);
-      }
-    } else {
-      DCHECK(child->GetLayoutObject());
-      LayoutObject* child_layout = child->GetLayoutObject();
-      Vector<LayoutRect> child_rects;
-      child_rects.push_back(child->InkOverflow().ToLayoutRect());
-      child_layout->LocalToAncestorRects(
-          child_rects, ToLayoutBoxModelObject(GetLayoutObject()), LayoutPoint(),
-          additional_offset);
-      if (!child_rects.IsEmpty())
-        outline_rects->push_back(child_rects[0]);
-    }
+  // For anonymous blocks, the children add outline rects.
+  if (!layout_object->IsAnonymous()) {
+    outline_rects->emplace_back(additional_offset, Size().ToLayoutSize());
   }
+
+  if (outline_type == NGOutlineType::kIncludeBlockVisualOverflow &&
+      !HasOverflowClip() && !HasControlClip()) {
+    AddOutlineRectsForNormalChildren(outline_rects, additional_offset,
+                                     outline_type);
+
+    // TODO(kojii): LayoutBlock::AddOutlineRects handles positioned objects
+    // here. Do we need it?
+  }
+
+  // TODO(kojii): Needs inline_element_continuation logic from
+  // LayoutBlockFlow::AddOutlineRects?
 }
 
 NGPhysicalOffsetRect NGPhysicalBoxFragment::InkOverflow(bool apply_clip) const {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index f6c1380..dc530ca5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -49,6 +49,7 @@
   // overflow clip; i.e., AllowOverflowClip() returns false.
   bool HasOverflowClip() const;
   bool ShouldClipOverflow() const;
+  bool HasControlClip() const;
 
   NGPhysicalOffsetRect ScrollableOverflow() const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
index f7faaeb..9262a11a 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
@@ -4,6 +4,13 @@
 
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h"
 
+#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
+#include "third_party/blink/renderer/core/layout/layout_inline.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_outline_utils.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
+
 namespace blink {
 
 NGPhysicalContainerFragment::NGPhysicalContainerFragment(
@@ -28,4 +35,91 @@
   DCHECK(children.IsEmpty());  // Ensure move semantics is used.
 }
 
+void NGPhysicalContainerFragment::AddOutlineRectsForNormalChildren(
+    Vector<LayoutRect>* outline_rects,
+    const LayoutPoint& additional_offset,
+    NGOutlineType outline_type) const {
+  for (const auto& child : Children()) {
+    // Outlines of out-of-flow positioned descendants are handled in
+    // NGPhysicalBoxFragment::AddSelfOutlineRects().
+    if (child->IsOutOfFlowPositioned())
+      continue;
+
+    // Outline of an element continuation or anonymous block continuation is
+    // added when we iterate the continuation chain.
+    // See NGPhysicalBoxFragment::AddSelfOutlineRects().
+    if (LayoutObject* child_layout_object = child->GetLayoutObject()) {
+      if (child_layout_object->IsElementContinuation() ||
+          (child_layout_object->IsLayoutBlockFlow() &&
+           ToLayoutBlockFlow(child_layout_object)
+               ->IsAnonymousBlockContinuation()))
+        continue;
+    }
+
+    AddOutlineRectsForDescendant(child, outline_rects, additional_offset,
+                                 outline_type);
+  }
+}
+
+void NGPhysicalContainerFragment::AddOutlineRectsForDescendant(
+    const NGLink& descendant,
+    Vector<LayoutRect>* outline_rects,
+    const LayoutPoint& additional_offset,
+    NGOutlineType outline_type) const {
+  if (descendant->IsText() || descendant->IsListMarker())
+    return;
+
+  if (const NGPhysicalBoxFragment* descendant_box =
+          ToNGPhysicalBoxFragmentOrNull(descendant.get())) {
+    LayoutObject* descendant_layout_object = descendant_box->GetLayoutObject();
+    DCHECK(descendant_layout_object);
+
+    if (descendant_box->HasLayer()) {
+      Vector<LayoutRect> layer_outline_rects;
+      descendant_box->AddSelfOutlineRects(&layer_outline_rects, LayoutPoint(),
+                                          outline_type);
+      descendant_layout_object->LocalToAncestorRects(
+          layer_outline_rects, ToLayoutBoxModelObject(GetLayoutObject()),
+          LayoutPoint(), additional_offset);
+      outline_rects->AppendVector(layer_outline_rects);
+      return;
+    }
+
+    if (descendant_layout_object->IsBox()) {
+      descendant_box->AddSelfOutlineRects(
+          outline_rects,
+          additional_offset + descendant.Offset().ToLayoutPoint(),
+          outline_type);
+      return;
+    }
+
+    DCHECK(descendant_layout_object->IsLayoutInline());
+    LayoutInline* descendant_layout_inline =
+        ToLayoutInline(descendant_layout_object);
+    // As an optimization, an ancestor has added rects for its line boxes
+    // covering descendants' line boxes, so descendants don't need to add line
+    // boxes again. For example, if the parent is a LayoutBlock, it adds rects
+    // for its line box which cover the line boxes of this LayoutInline. So
+    // the LayoutInline needs to add rects for children and continuations
+    // only.
+    if (!NGOutlineUtils::IsInlineOutlineNonpaintingFragment(*descendant)) {
+      descendant_layout_inline->AddOutlineRectsForChildrenAndContinuations(
+          *outline_rects, additional_offset, outline_type);
+    }
+    return;
+  }
+
+  if (const NGPhysicalLineBoxFragment* descendant_line_box =
+          ToNGPhysicalLineBoxFragmentOrNull(descendant.get())) {
+    descendant_line_box->AddOutlineRectsForNormalChildren(
+        outline_rects, additional_offset + descendant.Offset().ToLayoutPoint(),
+        outline_type);
+
+    if (!descendant_line_box->Size().IsEmpty()) {
+      outline_rects->emplace_back(additional_offset,
+                                  descendant_line_box->Size().ToLayoutSize());
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
index 1f9d364..97a7f37 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
@@ -13,6 +13,8 @@
 
 namespace blink {
 
+enum class NGOutlineType;
+
 class CORE_EXPORT NGPhysicalContainerFragment : public NGPhysicalFragment {
  public:
   const Vector<NGLink>& Children() const { return children_; }
@@ -22,6 +24,14 @@
     return contents_ink_overflow_;
   }
 
+  void AddOutlineRectsForNormalChildren(Vector<LayoutRect>* outline_rects,
+                                        const LayoutPoint& additional_offset,
+                                        NGOutlineType outline_type) const;
+  void AddOutlineRectsForDescendant(const NGLink& descendant,
+                                    Vector<LayoutRect>* rects,
+                                    const LayoutPoint& additional_offset,
+                                    NGOutlineType outline_type) const;
+
  protected:
   // This modifies the passed-in children vector.
   NGPhysicalContainerFragment(LayoutObject*,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
index 1527997d..ca6df77 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -278,7 +278,7 @@
 }
 
 bool NGPhysicalFragment::HasLayer() const {
-  return layout_object_->HasLayer();
+  return layout_object_ && layout_object_->HasLayer();
 }
 
 PaintLayer* NGPhysicalFragment::Layer() const {
diff --git a/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc b/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc
index fd2df2bf..cb5c811 100644
--- a/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc
+++ b/third_party/blink/renderer/core/mojo/tests/js_to_cpp_test.cc
@@ -74,7 +74,7 @@
   scoped_refptr<SharedBuffer> script_src = test::ReadFromFile(script_path);
   return frame.GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
       ScriptSourceCode(String(script_src->Data(), script_src->size())), KURL(),
-      kNotSharableCrossOrigin);
+      kOpaqueResource);
 }
 
 void CheckDataPipe(mojo::DataPipeConsumerHandle data_pipe_handle) {
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
index f839265..c65e8e9 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.cc
@@ -334,6 +334,9 @@
       ToNGPhysicalContainerFragment(fragment);
   children_.ReserveCapacity(container.Children().size());
 
+  bool children_are_inline =
+      !fragment.IsBox() || ToNGPhysicalBoxFragment(fragment).ChildrenInline();
+
   unsigned child_index = 0;
   for (const NGLink& child_fragment : container.Children()) {
     bool populate_children = child_fragment->IsContainer() &&
@@ -344,20 +347,21 @@
                                        : nullptr,
         &populate_children);
 
-    if (!child_fragment->IsFloating() &&
-        !child_fragment->IsOutOfFlowPositioned() &&
-        !child_fragment->IsListMarker()) {
-      if (LayoutObject* layout_object = child_fragment->GetLayoutObject()) {
-        child->AssociateWithLayoutObject(layout_object, last_fragment_map);
+    if (children_are_inline) {
+      if (!child_fragment->IsFloating() &&
+          !child_fragment->IsOutOfFlowPositioned() &&
+          !child_fragment->IsListMarker()) {
+        if (LayoutObject* layout_object = child_fragment->GetLayoutObject())
+          child->AssociateWithLayoutObject(layout_object, last_fragment_map);
+
+        child->inline_offset_to_container_box_ =
+            inline_offset_to_container_box + child_fragment.Offset();
       }
 
-      child->inline_offset_to_container_box_ =
-          inline_offset_to_container_box + child_fragment.Offset();
-    }
-
-    if (populate_children) {
-      child->PopulateDescendants(child->inline_offset_to_container_box_,
-                                 last_fragment_map);
+      if (populate_children) {
+        child->PopulateDescendants(child->inline_offset_to_container_box_,
+                                   last_fragment_map);
+      }
     }
 
     if (child_index < children_.size())
@@ -377,15 +381,7 @@
     HashMap<const LayoutObject*, NGPaintFragment*>* last_fragment_map) {
   DCHECK(layout_object);
   DCHECK(!next_for_same_layout_object_);
-
-  // TODO(kojii): The LayoutObject is inline, except for column container
-  // fragment. We should have better way to distinguish it, probably after we
-  // determined the generated fragment tree for multicol with fragmentations
-  // supported.
-  if (!layout_object->IsInline()) {
-    DCHECK(Parent() && layout_object == Parent()->GetLayoutObject());
-    return;
-  }
+  DCHECK(layout_object->IsInline());
 
   auto add_result = last_fragment_map->insert(layout_object, this);
   if (add_result.is_new_entry) {
@@ -554,31 +550,16 @@
     if (marked)
       return;
   }
-  // Since |layout_object| isn't in fragment tree, check both following and
-  // preceding siblings.
-  bool marked = false;
-  for (LayoutObject* next = layout_object.NextSibling(); next;
-       next = next->NextSibling()) {
-    if (next->IsFloatingOrOutOfFlowPositioned())
-      continue;
-    // |next| may not be in inline formatting context, e.g. <object>.
-    if (TryMarkLineBoxDirtyFor(*next)) {
-      marked = true;
-      break;
-    }
-  }
+  // Since |layout_object| isn't in fragment tree, check preceding siblings.
+  // Note: Once we reuse lines below dirty lines, we should check next siblings.
   for (LayoutObject* previous = layout_object.PreviousSibling(); previous;
        previous = previous->PreviousSibling()) {
     if (previous->IsFloatingOrOutOfFlowPositioned())
       continue;
     // |previous| may not be in inline formatting context, e.g. <object>.
-    if (TryMarkLineBoxDirtyFor(*previous)) {
-      marked = true;
-      break;
-    }
+    if (TryMarkLineBoxDirtyFor(*previous))
+      return;
   }
-  if (marked)
-    return;
   // There is no siblings, try parent.
   const LayoutObject& parent = *layout_object.Parent();
   if (parent.IsInline())
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
index 8495ce7..0c8167bd 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h
@@ -167,6 +167,9 @@
     return ShouldPaintCursorCaret() || ShouldPaintDragCaret();
   }
 
+  // Returns true when associated fragment of |layout_object| has line box.
+  static bool TryMarkLineBoxDirtyFor(const LayoutObject& layout_object);
+
   // A range of fragments for |FragmentsFor()|.
   class CORE_EXPORT FragmentRange {
    public:
@@ -280,9 +283,6 @@
   // formatting context.
   void MarkLineBoxDirty();
 
-  // Returns true when associated fragment of |layout_object| has line box.
-  static bool TryMarkLineBoxDirtyFor(const LayoutObject& layout_object);
-
   //
   // Following fields are computed in the layout phase.
   //
diff --git a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc
index 359f408..33781b4 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_paint_fragment_test.cc
@@ -490,7 +490,7 @@
   const NGPaintFragment& container = *GetPaintFragmentByElementId("container");
   EXPECT_FALSE(container.Children()[0]->IsDirty());
   EXPECT_TRUE(container.Children()[1]->IsDirty());
-  EXPECT_TRUE(container.Children()[2]->IsDirty());
+  EXPECT_FALSE(container.Children()[2]->IsDirty());
 }
 
 TEST_F(NGPaintFragmentTest, MarkLineBoxesDirtyByInsertAtStart) {
@@ -510,7 +510,7 @@
   GetDocument().UpdateStyleAndLayout();
 
   EXPECT_TRUE(line1->IsDirty());
-  EXPECT_TRUE(line2->IsDirty());
+  EXPECT_FALSE(line2->IsDirty());
   EXPECT_FALSE(line3->IsDirty());
 }
 
@@ -551,7 +551,7 @@
   GetDocument().UpdateStyleAndLayout();
 
   EXPECT_TRUE(line1->IsDirty());
-  EXPECT_TRUE(line2->IsDirty());
+  EXPECT_FALSE(line2->IsDirty());
   EXPECT_FALSE(line3->IsDirty());
 }
 
diff --git a/third_party/blink/renderer/core/resize_observer/resize_observer_test.cc b/third_party/blink/renderer/core/resize_observer/resize_observer_test.cc
index 1da861aa..1dfe3f98 100644
--- a/third_party/blink/renderer/core/resize_observer/resize_observer_test.cc
+++ b/third_party/blink/renderer/core/resize_observer/resize_observer_test.cc
@@ -117,11 +117,11 @@
   //
   script_controller.ExecuteScriptInMainWorldAndReturnValue(
       ScriptSourceCode("var ro = new ResizeObserver( entries => {});"), KURL(),
-      kNotSharableCrossOrigin, ScriptFetchOptions(),
+      kOpaqueResource, ScriptFetchOptions(),
       ScriptController::kExecuteScriptWhenScriptsDisabled);
   ASSERT_EQ(observers.size(), 1U);
   script_controller.ExecuteScriptInMainWorldAndReturnValue(
-      ScriptSourceCode("ro = undefined;"), KURL(), kNotSharableCrossOrigin,
+      ScriptSourceCode("ro = undefined;"), KURL(), kOpaqueResource,
       ScriptFetchOptions(),
       ScriptController::kExecuteScriptWhenScriptsDisabled);
   V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
@@ -136,14 +136,14 @@
                        "var el = document.createElement('div');"
                        "ro.observe(el);"
                        "ro = undefined;"),
-      KURL(), kNotSharableCrossOrigin, ScriptFetchOptions(),
+      KURL(), kOpaqueResource, ScriptFetchOptions(),
       ScriptController::kExecuteScriptWhenScriptsDisabled);
   ASSERT_EQ(observers.size(), 1U);
   V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
   WebHeap::CollectAllGarbageForTesting();
   ASSERT_EQ(observers.size(), 1U);
   script_controller.ExecuteScriptInMainWorldAndReturnValue(
-      ScriptSourceCode("el = undefined;"), KURL(), kNotSharableCrossOrigin,
+      ScriptSourceCode("el = undefined;"), KURL(), kOpaqueResource,
       ScriptFetchOptions(),
       ScriptController::kExecuteScriptWhenScriptsDisabled);
   V8GCController::CollectAllGarbageForTesting(v8::Isolate::GetCurrent());
diff --git a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
index cd6ef5e..1893df4 100644
--- a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
@@ -1197,7 +1197,7 @@
   v8::HandleScope scope(v8::Isolate::GetCurrent());
   v8::Local<v8::Value> result =
       local_frame->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
-          ScriptSourceCode("window.didRaf;"), KURL(), kNotSharableCrossOrigin);
+          ScriptSourceCode("window.didRaf;"), KURL(), kOpaqueResource);
   EXPECT_TRUE(result->IsTrue());
 }
 
diff --git a/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc b/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc
index 30055a0..0a5b1213 100644
--- a/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc
+++ b/third_party/blink/renderer/core/xml/document_xml_tree_viewer.cc
@@ -26,7 +26,7 @@
   document.GetFrame()->GetScriptController().ExecuteScriptInIsolatedWorld(
       IsolatedWorldId::kDocumentXMLTreeViewerWorldId,
       ScriptSourceCode(script_string, ScriptSourceLocationType::kInternal),
-      KURL(), kNotSharableCrossOrigin);
+      KURL(), kOpaqueResource);
 
   Element* element = document.getElementById("xml-viewer-style");
   if (element) {
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_registration.idl b/third_party/blink/renderer/modules/service_worker/service_worker_registration.idl
index a50faeb..2179593 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_registration.idl
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_registration.idl
@@ -3,9 +3,7 @@
 // found in the LICENSE file.
 
 // https://w3c.github.io/ServiceWorker/#enumdef-serviceworkerupdateviacache
-[
-    RuntimeEnabled=ServiceWorkerUpdateViaCache
-] enum ServiceWorkerUpdateViaCache {
+enum ServiceWorkerUpdateViaCache {
     "imports",
     "all",
     "none"
@@ -23,7 +21,7 @@
     readonly attribute NavigationPreloadManager navigationPreload;
 
     readonly attribute USVString scope;
-    [RuntimeEnabled=ServiceWorkerUpdateViaCache] readonly attribute ServiceWorkerUpdateViaCache updateViaCache;
+    readonly attribute ServiceWorkerUpdateViaCache updateViaCache;
 
     [CallWith=ScriptState] Promise<ServiceWorkerRegistration> update();
     [CallWith=ScriptState] Promise<boolean> unregister();
diff --git a/third_party/blink/renderer/platform/exported/platform.cc b/third_party/blink/renderer/platform/exported/platform.cc
index 67423a3..afd51a49 100644
--- a/third_party/blink/renderer/platform/exported/platform.cc
+++ b/third_party/blink/renderer/platform/exported/platform.cc
@@ -36,6 +36,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
+#include "build/build_config.h"
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/platform/interface_provider.h"
@@ -341,20 +342,32 @@
   event->Signal();
 }
 
-void Platform::InitializeCompositorThread(
-    const WebThreadCreationParams& params) {
+void Platform::InitializeCompositorThread() {
   DCHECK(!compositor_thread_);
+
+  WebThreadCreationParams params(WebThreadType::kCompositorThread);
+#if defined(OS_ANDROID)
+  params.thread_options.priority = base::ThreadPriority::DISPLAY;
+#endif
   std::unique_ptr<scheduler::WebThreadBase> compositor_thread =
       scheduler::WebThreadBase::CreateCompositorThread(params);
   compositor_thread->Init();
   WaitUntilWebThreadTLSUpdate(compositor_thread.get());
   compositor_thread_ = std::move(compositor_thread);
+  SetDisplayThreadPriority(compositor_thread_->ThreadId());
 }
 
 WebThread* Platform::CompositorThread() {
   return compositor_thread_.get();
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+Platform::CompositorThreadTaskRunner() {
+  if (WebThread* compositor_thread = CompositorThread())
+    return compositor_thread->GetTaskRunner();
+  return nullptr;
+}
+
 std::unique_ptr<WebGraphicsContext3DProvider>
 Platform::CreateOffscreenGraphicsContext3DProvider(
     const Platform::ContextAttributes&,
diff --git a/third_party/blink/renderer/platform/loader/fetch/access_control_status.h b/third_party/blink/renderer/platform/loader/fetch/access_control_status.h
index bd8d945..56de552 100644
--- a/third_party/blink/renderer/platform/loader/fetch/access_control_status.h
+++ b/third_party/blink/renderer/platform/loader/fetch/access_control_status.h
@@ -8,7 +8,6 @@
 namespace blink {
 
 enum AccessControlStatus {
-  kNotSharableCrossOrigin,
   kSharableCrossOrigin,
   kOpaqueResource
 };
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 5313537..e454e517 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1143,10 +1143,6 @@
       name: "ServiceWorkerScriptFullCodeCache",
     },
     {
-      name: "ServiceWorkerUpdateViaCache",
-      status: "stable",
-    },
-    {
       name: "SetRootScroller",
       status: "experimental",
     },
diff --git a/third_party/webrtc_overrides/BUILD.gn b/third_party/webrtc_overrides/BUILD.gn
index 7923d5de..60ea96f 100644
--- a/third_party/webrtc_overrides/BUILD.gn
+++ b/third_party/webrtc_overrides/BUILD.gn
@@ -39,7 +39,7 @@
   ]
 }
 
-static_library("webrtc") {
+source_set("webrtc") {
   public_deps = [
     ":jingle_deps",
 
@@ -57,29 +57,22 @@
     cflags = [ "/wd4005" ]
   }
 
-  # When WebRTC is built as part of Chromium it should exclude the default
-  # implementation of field_trial unless it is building for NACL or Chromecast.
+  # When Chromium is built for NACL or Chromecast, WebRTC provides a
+  # field_trial implementation so there is no need to depend on
+  # ":field_trial".
   # This configuration happens here:
   # https://cs.chromium.org/chromium/src/third_party/webrtc/webrtc.gni?l=44-51&rcl=95c56eebe0a2b31ad5752138d15b431124e17d36
   if (is_nacl) {
     deps += [
       "//native_client_sdk/src/libraries/nacl_io",
-      "//third_party/webrtc/system_wrappers:runtime_enabled_features_default",
     ]
-  } else if (is_chromecast) {
-    deps += [
-      "//third_party/webrtc/system_wrappers:runtime_enabled_features_default",
-    ]
-  } else {
-    # Otherwise, we just add the field_trial which redirects to base and
-    # runtime_enabled_features which redirects to content/public/common.
-    sources = [
-      "runtime_enabled_features.cc",
-    ]
+  } else if (!is_chromecast) {
+    # When Chromium doesn't build for NaCL or Chromecast, WebRTC doesn't
+    # provide an implementation for field_trial and a custom one (that uses
+    # base/metrics/field_trial.h is provided).
     deps += [
       ":field_trial",
       "//base",
-      "//third_party/webrtc/system_wrappers:runtime_enabled_features_api",
     ]
   }
 
diff --git a/third_party/webrtc_overrides/runtime_enabled_features.cc b/third_party/webrtc_overrides/runtime_enabled_features.cc
deleted file mode 100644
index bbb5deba..0000000
--- a/third_party/webrtc_overrides/runtime_enabled_features.cc
+++ /dev/null
@@ -1,26 +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.
-//
-// Define webrtc::runtime_features::IsFeatureEnabled to provide webrtc with a
-// chromium runtime flags access.
-
-#include "base/feature_list.h"
-#include "third_party/webrtc/system_wrappers/include/runtime_enabled_features.h"
-
-namespace webrtc {
-
-namespace runtime_enabled_features {
-
-const base::Feature kWebRtcDualStreamMode{"WebRTC-DualStreamMode",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
-
-bool IsFeatureEnabled(std::string feature_name) {
-  if (feature_name == kDualStreamModeFeatureName)
-    return base::FeatureList::IsEnabled(kWebRtcDualStreamMode);
-  return false;
-}
-
-}  // namespace runtime_enabled_features
-
-}  // namespace webrtc
diff --git a/third_party/widevine/cdm/BUILD.gn b/third_party/widevine/cdm/BUILD.gn
index 6f8f50c3..aa21ecb 100644
--- a/third_party/widevine/cdm/BUILD.gn
+++ b/third_party/widevine/cdm/BUILD.gn
@@ -15,6 +15,7 @@
   flags = [
     "ENABLE_WIDEVINE=$enable_widevine",
     "SHOULD_BUNDLE_WIDEVINE_CDM=$should_bundle_widevine_cdm",
+    "ENABLE_WIDEVINE_CDM_COMPONENT=$enable_widevine_cdm_component",
   ]
 }
 
diff --git a/third_party/widevine/cdm/widevine.gni b/third_party/widevine/cdm/widevine.gni
index 15c8328..e925eed 100644
--- a/third_party/widevine/cdm/widevine.gni
+++ b/third_party/widevine/cdm/widevine.gni
@@ -11,14 +11,21 @@
   enable_widevine = is_chrome_branded || is_android
 }
 
-enable_widevine_cdm_host_verification =
-    enable_widevine && enable_cdm_host_verification
+# The Widevine CDM can be updated as a component. This only applies to platforms
+# where the CDM is a standalone library (enable_library_cdms == true). Currently
+# it's only supported on Windows and Mac. The CDM can still be bundled when it's
+# a component, see below.
+enable_widevine_cdm_component =
+    enable_widevine && enable_library_cdms && (is_win || is_mac)
 
-# Only bundle Widevine CDM in Google Chrome builds.
-# TODO: Provide prebuilt libraries for ARM64.
+# The Widevine CDM is bundled as part of Google Chrome builds.
+# TODO(hmchen): Provide prebuilt libraries for ARM64.
 should_bundle_widevine_cdm = enable_widevine && is_chrome_branded &&
                              enable_library_cdms && target_cpu != "arm64"
 
+enable_widevine_cdm_host_verification =
+    enable_widevine && enable_cdm_host_verification
+
 template("widevine_sign_file") {
   # For official builds, generate a signature file for |file| which will
   # be used by Widevine. If |signature_file| is not specified, the signature
diff --git a/third_party/widevine/cdm/widevine_cdm_common.h b/third_party/widevine/cdm/widevine_cdm_common.h
index a8c1faf4..53317f04 100644
--- a/third_party/widevine/cdm/widevine_cdm_common.h
+++ b/third_party/widevine/cdm/widevine_cdm_common.h
@@ -5,8 +5,6 @@
 #ifndef WIDEVINE_CDM_WIDEVINE_CDM_COMMON_H_
 #define WIDEVINE_CDM_WIDEVINE_CDM_COMMON_H_
 
-#include "media/media_buildflags.h"
-
 // This file defines constants common to all Widevine CDM versions.
 
 // "alpha" is a temporary name until a convention is defined.
@@ -24,7 +22,6 @@
 
 const char kWidevineCdmDisplayName[] = "Widevine Content Decryption Module";
 
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
 // Identifier used by the PluginPrivateFileSystem to identify the files stored
 // for the Widevine CDM. This is used to store persistent files. As the files
 // were initially used by the CDM running as a pepper plugin, this ID is based
@@ -35,10 +32,4 @@
 // Name of the CDM library.
 const char kWidevineCdmLibraryName[] = "widevinecdm";
 
-#if defined(OS_MACOSX) || defined(OS_WIN)
-// CDM is installed by the component installer instead of the Chrome installer.
-#define WIDEVINE_CDM_IS_COMPONENT
-#endif  // defined(OS_MACOSX) || defined(OS_WIN)
-#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
-
 #endif  // WIDEVINE_CDM_WIDEVINE_CDM_COMMON_H_
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 524d4fbe..75cdcb8 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -352,7 +352,7 @@
     },
 
     'chromium.mac': {
-      'Mac Builder': 'gpu_tests_release_bot',
+      'Mac Builder': 'gpu_tests_release_bot_minimal_symbols',
       'Mac Builder (dbg)': 'gpu_tests_debug_bot',
       'mac-jumbo-rel': 'jumbo_release_bot_minimal_symbols',
       'ios-device': 'ios',
@@ -468,19 +468,6 @@
     },
 
     'chromium.webrtc.fyi': {
-      'Android Builder': 'android_release_bot_minimal_symbols',
-      'Android Builder (dbg)': 'android_debug_static_bot',
-      'Android Builder ARM64 (dbg)': 'android_debug_static_bot_arm64',
-      'Linux Builder': 'gpu_tests_release_bot',
-      'Linux Builder (dbg)': 'debug_bot',
-      'Mac Builder': 'gpu_tests_release_bot',
-      'Mac Builder (dbg)': 'debug_bot',
-      'Win Builder': 'release_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs',
-      'Win Builder (dbg)': 'debug_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs',
-    },
-
-    # TODO(crbug.com/877018): Remove once experimental configs are verified.
-    'chromium.webrtc.fyi.experimental': {
       'WebRTC Chromium FYI Android Builder': 'android_release_bot_minimal_symbols',
       'WebRTC Chromium FYI Android Builder (dbg)': 'android_debug_static_bot',
       'WebRTC Chromium FYI Android Builder ARM64 (dbg)': 'android_debug_static_bot_arm64',
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index fd245de..309c71bf 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -18669,6 +18669,35 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="StatusArea_NightLight_Disabled">
+  <owner>tetsui@chromium.org</owner>
+  <description>
+    User disabled NightLight from the feature button in the system menu.
+  </description>
+</action>
+
+<action name="StatusArea_NightLight_Enabled">
+  <owner>tetsui@chromium.org</owner>
+  <description>
+    User enabled NightLight from the feature button in the system menu.
+  </description>
+</action>
+
+<action name="StatusArea_NightLight_Settings">
+  <owner>tetsui@chromium.org</owner>
+  <description>
+    User opened NightLight WebUI settings from the feature button in the system
+    menu.
+  </description>
+</action>
+
+<action name="StatusArea_Notifications_ClearAll">
+  <owner>tetsui@chromium.org</owner>
+  <description>
+    User removed all notifications from the Clear All button in the system menu.
+  </description>
+</action>
+
 <action name="StatusArea_OS_Update_Default_Selected">
   <owner>bruthig@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
@@ -18678,6 +18707,22 @@
   </description>
 </action>
 
+<action name="StatusArea_QuietMode_Disabled">
+  <owner>tetsui@chromium.org</owner>
+  <description>
+    User disabled Quiet Mode (Do not disturb) from the feature button in the
+    system menu.
+  </description>
+</action>
+
+<action name="StatusArea_QuietMode_Enabled">
+  <owner>tetsui@chromium.org</owner>
+  <description>
+    User enabled Quiet Mode (Do not disturb) from the feature button in the
+    system menu.
+  </description>
+</action>
+
 <action name="StatusArea_ScreenCapture_Default_Stop">
   <owner>bruthig@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index bd6488f3..11f8dcc1 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -47926,6 +47926,8 @@
   <int value="23" label="Tracing"/>
   <int value="24" label="User"/>
   <int value="25" label="VPN"/>
+  <int value="26" label="Night light"/>
+  <int value="27" label="Do not disturb"/>
 </enum>
 
 <enum name="SystemNotificationType">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 980e688..0d7799e 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -12589,6 +12589,14 @@
   </summary>
 </histogram>
 
+<histogram name="ChromeOS.SystemTray.IsExpandedOnOpen" enum="Boolean">
+  <owner>tetsui@chromium.org</owner>
+  <summary>
+    If the value is true, SystemTray is expanded when it's opened. Otherwise,
+    it's closed when it's opened.
+  </summary>
+</histogram>
+
 <histogram name="ChromeOS.SystemTray.TimeToClick" units="ms">
   <owner>tetsui@chromium.org</owner>
   <summary>
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index ed07de7..6146e8d 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -282,4 +282,5 @@
  <item id="webstore_installer" hash_code="18764319" type="0" content_hash_code="11030110" os_list="linux,windows" file_path="chrome/browser/extensions/webstore_installer.cc"/>
  <item id="webui_content_scripts_download" hash_code="100545943" type="0" content_hash_code="119898059" os_list="linux,windows" file_path="extensions/browser/guest_view/web_view/web_ui/web_ui_url_fetcher.cc"/>
  <item id="xmpp_signal_strategy" hash_code="88906454" type="0" content_hash_code="88958321" os_list="linux,windows" file_path="remoting/signaling/xmpp_signal_strategy.cc"/>
+ <item id="service_worker_update_checker" hash_code="130931413" type="0" content_hash_code="46608086" os_list="linux,mac,windows" file_path="content/browser/service_worker/service_worker_single_script_update_checker.cc"/>
 </annotations>
diff --git a/ui/android/delegated_frame_host_android.cc b/ui/android/delegated_frame_host_android.cc
index 1c4fb2cc2..74162a94 100644
--- a/ui/android/delegated_frame_host_android.cc
+++ b/ui/android/delegated_frame_host_android.cc
@@ -177,7 +177,9 @@
 }
 
 bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const {
-  return pending_local_surface_id_.is_valid() && view_->GetWindowAndroid() &&
+  return content_layer_ && content_layer_->fallback_surface_id() &&
+         content_layer_->fallback_surface_id()->is_valid() &&
+         view_->GetWindowAndroid() &&
          view_->GetWindowAndroid()->GetCompositor();
 }
 
@@ -403,6 +405,7 @@
     return;
   }
 
+  content_layer_->SetFallbackSurfaceId(surface_info.id());
   active_local_surface_id_ = surface_info.id().local_surface_id();
   active_device_scale_factor_ = surface_info.device_scale_factor();
 
@@ -462,7 +465,9 @@
 }
 
 viz::SurfaceId DelegatedFrameHostAndroid::SurfaceId() const {
-  return viz::SurfaceId(frame_sink_id_, active_local_surface_id_);
+  return content_layer_ && content_layer_->fallback_surface_id()
+             ? *content_layer_->fallback_surface_id()
+             : viz::SurfaceId();
 }
 
 bool DelegatedFrameHostAndroid::HasPrimarySurface() const {
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 75b1099..c91999eb 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -6,12 +6,12 @@
 
 #include "base/auto_reset.h"
 #include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
+#include "components/viz/client/hit_test_data_provider_draw_quad.h"
 #include "components/viz/client/local_surface_id_provider.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/transient_window_client.h"
 #include "ui/aura/env.h"
-#include "ui/aura/hit_test_data_provider_aura.h"
 #include "ui/aura/mus/client_surface_embedder.h"
 #include "ui/aura/mus/property_converter.h"
 #include "ui/aura/mus/window_tree_client.h"
@@ -126,7 +126,7 @@
   params.pipes.compositor_frame_sink_info = std::move(sink_info);
   params.pipes.client_request = std::move(client_request);
   params.hit_test_data_provider =
-      std::make_unique<HitTestDataProviderAura>(window_);
+      std::make_unique<viz::HitTestDataProviderDrawQuad>(window_);
   params.local_surface_id_provider =
       std::make_unique<viz::DefaultLocalSurfaceIdProvider>();
   params.enable_surface_synchronization = true;
diff --git a/ui/events/event_unittest.cc b/ui/events/event_unittest.cc
index 6c40d31..7c7dbf9 100644
--- a/ui/events/event_unittest.cc
+++ b/ui/events/event_unittest.cc
@@ -25,7 +25,6 @@
 
 #if defined(USE_X11)
 #include "ui/events/test/events_test_utils_x11.h"
-#include "ui/events/x/events_x_utils.h"  // nogncheck
 #include "ui/gfx/x/x11.h"        // nogncheck
 #include "ui/gfx/x/x11_types.h"  // nogncheck
 #endif
@@ -431,15 +430,6 @@
 #if defined(USE_X11)
 namespace {
 
-class MockTimestampServer : public ui::TimestampServer {
- public:
-  Time GetCurrentServerTime() override { return base_time_; }
-  void SetBaseTime(Time time) { base_time_ = time; }
-
- private:
-  Time base_time_ = 0;
-};
-
 void SetKeyEventTimestamp(XEvent* event, int64_t time) {
   event->xkey.time = time & UINT32_MAX;
 }
@@ -450,23 +440,7 @@
 
 }  // namespace
 
-class X11EventTest : public testing::Test {
- public:
-  X11EventTest() {}
-  ~X11EventTest() override {}
-
-  void SetUp() override { SetTimestampServer(&server_); }
-
-  void TearDown() override { SetTimestampServer(nullptr); }
-
- protected:
-  MockTimestampServer server_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(X11EventTest);
-};
-
-TEST_F(X11EventTest, AutoRepeat) {
+TEST(EventTest, AutoRepeat) {
   const uint16_t kNativeCodeA =
       ui::KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A);
   const uint16_t kNativeCodeB =
@@ -494,7 +468,6 @@
 
   int64_t ticks_base =
       (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds() - 5000;
-  server_.SetBaseTime(static_cast<Time>(ticks_base));
   SetKeyEventTimestamp(native_event_a_pressed, ticks_base);
   SetKeyEventTimestamp(native_event_a_pressed_1500, ticks_base + 1500);
   SetKeyEventTimestamp(native_event_a_pressed_3000, ticks_base + 3000);
@@ -565,14 +538,12 @@
     EXPECT_FALSE(key_a4_pressed_nonstandard_state.is_repeat());
   }
 
-  // The nonstandard event from above was ignored, so the last event pressed was
-  // |native_event_a_pressed|. These are both repeats.
   {
     KeyEvent key_a1(native_event_a_pressed);
-    EXPECT_TRUE(key_a1.is_repeat());
+    EXPECT_FALSE(key_a1.is_repeat());
 
     KeyEvent key_a1_with_same_event(native_event_a_pressed);
-    EXPECT_TRUE(key_a1_with_same_event.is_repeat());
+    EXPECT_FALSE(key_a1_with_same_event.is_repeat());
   }
 }
 #endif  // USE_X11
diff --git a/ui/events/platform/x11/x11_event_source.cc b/ui/events/platform/x11/x11_event_source.cc
index a68188d..7e9f6df 100644
--- a/ui/events/platform/x11/x11_event_source.cc
+++ b/ui/events/platform/x11/x11_event_source.cc
@@ -100,7 +100,6 @@
       distribution_(0, 999) {
   DCHECK(!instance_);
   instance_ = this;
-  SetTimestampServer(this);
 
   DCHECK(delegate_);
   DCHECK(display_);
@@ -111,7 +110,6 @@
 X11EventSource::~X11EventSource() {
   DCHECK_EQ(this, instance_);
   instance_ = nullptr;
-  SetTimestampServer(nullptr);
   if (dummy_initialized_)
     XDestroyWindow(display_, dummy_window_);
 }
diff --git a/ui/events/platform/x11/x11_event_source.h b/ui/events/platform/x11/x11_event_source.h
index c2ee795..e818c4ac 100644
--- a/ui/events/platform/x11/x11_event_source.h
+++ b/ui/events/platform/x11/x11_event_source.h
@@ -14,7 +14,6 @@
 #include "base/optional.h"
 #include "build/build_config.h"
 #include "ui/events/events_export.h"
-#include "ui/events/x/events_x_utils.h"
 #include "ui/gfx/x/x11_types.h"
 
 using Time = unsigned long;
@@ -47,7 +46,7 @@
 
 // Receives X11 events and sends them to X11EventSourceDelegate. Handles
 // receiving, pre-process and post-processing XEvents.
-class EVENTS_EXPORT X11EventSource : TimestampServer {
+class EVENTS_EXPORT X11EventSource {
  public:
   X11EventSource(X11EventSourceDelegate* delegate, XDisplay* display);
   ~X11EventSource();
@@ -83,7 +82,7 @@
 
   // Explicitly asks the X11 server for the current timestamp, and updates
   // |last_seen_server_time_| with this value.
-  Time GetCurrentServerTime() override;
+  Time GetCurrentServerTime();
 
  protected:
   // Extracts cookie data from |xevent| if it's of GenericType, and dispatches
diff --git a/ui/events/x/events_x.cc b/ui/events/x/events_x.cc
index 6253031..b8df7b0 100644
--- a/ui/events/x/events_x.cc
+++ b/ui/events/x/events_x.cc
@@ -81,7 +81,9 @@
 }
 
 base::TimeTicks EventTimeFromNative(const PlatformEvent& native_event) {
-  return EventTimeFromXEvent(*native_event);
+  base::TimeTicks timestamp = EventTimeFromXEvent(*native_event);
+  ValidateEventTimeClock(&timestamp);
+  return timestamp;
 }
 
 gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) {
diff --git a/ui/events/x/events_x_unittest.cc b/ui/events/x/events_x_unittest.cc
index 3419309..897c5c5 100644
--- a/ui/events/x/events_x_unittest.cc
+++ b/ui/events/x/events_x_unittest.cc
@@ -76,15 +76,6 @@
   return rotation_angle;
 }
 
-class MockTimestampServer : public ui::TimestampServer {
- public:
-  Time GetCurrentServerTime() override { return base_time_; }
-  void SetBaseTime(Time time) { base_time_ = time; }
-
- private:
-  Time base_time_ = 0;
-};
-
 }  // namespace
 
 class EventsXTest : public testing::Test {
@@ -93,15 +84,13 @@
   ~EventsXTest() override {}
 
   void SetUp() override {
-    SetTimestampServer(&server_);
     DeviceDataManagerX11::CreateInstance();
     ui::TouchFactory::GetInstance()->ResetForTest();
+    ResetTimestampRolloverCountersForTesting();
   }
-
-  void TearDown() override { SetTimestampServer(nullptr); }
+  void TearDown() override { ResetTimestampRolloverCountersForTesting(); }
 
  private:
-  MockTimestampServer server_;
   DISALLOW_COPY_AND_ASSIGN(EventsXTest);
 };
 
@@ -553,4 +542,52 @@
   EXPECT_EQ(ui::ET_UNKNOWN, ui::EventTypeFromNative(xev));
 }
 
+namespace {
+
+// Returns a fake TimeTicks based on the given millisecond offset.
+base::TimeTicks TimeTicksFromMillis(int64_t millis) {
+  return base::TimeTicks() + base::TimeDelta::FromMilliseconds(millis);
+}
+
+}  // namespace
+
+TEST_F(EventsXTest, TimestampRolloverAndAdjustWhenDecreasing) {
+  XEvent event;
+  InitButtonEvent(&event, true, gfx::Point(5, 10), 1, 0);
+
+  test::ScopedEventTestTickClock clock;
+  clock.SetNowTicks(TimeTicksFromMillis(0x100000001));
+  ResetTimestampRolloverCountersForTesting();
+
+  event.xbutton.time = 0xFFFFFFFF;
+  EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromNative(&event));
+
+  clock.SetNowTicks(TimeTicksFromMillis(0x100000007));
+  ResetTimestampRolloverCountersForTesting();
+
+  event.xbutton.time = 3;
+  EXPECT_EQ(TimeTicksFromMillis(0x100000000 + 3),
+            ui::EventTimeFromNative(&event));
+}
+
+TEST_F(EventsXTest, NoTimestampRolloverWhenMonotonicIncreasing) {
+  XEvent event;
+  InitButtonEvent(&event, true, gfx::Point(5, 10), 1, 0);
+
+  test::ScopedEventTestTickClock clock;
+  clock.SetNowTicks(TimeTicksFromMillis(10));
+  ResetTimestampRolloverCountersForTesting();
+
+  event.xbutton.time = 6;
+  EXPECT_EQ(TimeTicksFromMillis(6), ui::EventTimeFromNative(&event));
+  event.xbutton.time = 7;
+  EXPECT_EQ(TimeTicksFromMillis(7), ui::EventTimeFromNative(&event));
+
+  clock.SetNowTicks(TimeTicksFromMillis(0x100000005));
+  ResetTimestampRolloverCountersForTesting();
+
+  event.xbutton.time = 0xFFFFFFFF;
+  EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromNative(&event));
+}
+
 }  // namespace ui
diff --git a/ui/events/x/events_x_utils.cc b/ui/events/x/events_x_utils.cc
index be8256c..86fbd19 100644
--- a/ui/events/x/events_x_utils.cc
+++ b/ui/events/x/events_x_utils.cc
@@ -27,24 +27,6 @@
 
 namespace {
 
-ui::TimestampServer* g_timestamp_server = nullptr;
-
-// Clamps a TimeDelta to be within [0 seconds, 30 seconds].
-base::TimeDelta ClampDeltaFromExternalSource(const base::TimeDelta& delta) {
-  // Ignore pathologically long deltas. External source is probably having
-  // issues.
-  constexpr base::TimeDelta pathologically_long_duration =
-      base::TimeDelta::FromSeconds(30);
-  if (delta > pathologically_long_duration)
-    return base::TimeDelta();
-
-  // Ignore negative deltas. External source is probably having issues.
-  if (delta < base::TimeDelta())
-    return base::TimeDelta();
-
-  return delta;
-}
-
 // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+.
 const int kWheelScrollAmount = 53;
 
@@ -326,29 +308,40 @@
   return true;
 }
 
+int64_t g_last_seen_timestamp_ms = 0;
+int64_t g_rollover_ms = 0;
+
+// Takes Xlib Time and returns a time delta that is immune to timer rollover.
+// This function is not thread safe as we do not use a lock.
 base::TimeTicks TimeTicksFromXEventTime(Time timestamp) {
-  // There's no way to convert from an X time to a base::TimeTicks without
-  // knowing the current X server time.
-  if (!g_timestamp_server)
-    return base::TimeTicks();
+  int64_t timestamp64 = timestamp;
 
-  // X11 uses a uint32_t on the wire protocol. Xlib casts this to an unsigned
-  // long by prepending with 0s. We cast back to a uint32_t so that subtraction
-  // works properly when the timestamp overflows back to 0.
-  uint32_t event_server_time_ms = static_cast<uint32_t>(timestamp);
-  uint32_t current_server_time_ms =
-      static_cast<uint32_t>(g_timestamp_server->GetCurrentServerTime());
+  if (!timestamp)
+    return ui::EventTimeForNow();
 
-  // On X11, event times are in X11 Server time. To convert to base::TimeTicks,
-  // we perform a round-trip to the X11 Server, subtract the two times to get a
-  // TimeDelta, and then subtract that from base::TimeTicks::Now(). Since we're
-  // working with units of time from an external source, we clamp the TimeDelta
-  // to reasonable values.
-  uint32_t delta_ms = current_server_time_ms - event_server_time_ms;
-  base::TimeDelta delta = base::TimeDelta::FromMilliseconds(delta_ms);
-  base::TimeDelta sanitized = ClampDeltaFromExternalSource(delta);
+  // If this is the first event that we get, assume the time stamp roll-over
+  // might have happened before the process was started.
+  // Register a rollover if the distance between last timestamp and current one
+  // is larger than half the width. This avoids false rollovers even in a case
+  // where X server delivers reasonably close events out-of-order.
+  bool had_recent_rollover =
+      !g_last_seen_timestamp_ms ||
+      g_last_seen_timestamp_ms - timestamp64 > (UINT32_MAX >> 1);
 
-  return base::TimeTicks::Now() - sanitized;
+  g_last_seen_timestamp_ms = timestamp64;
+  if (!had_recent_rollover)
+    return base::TimeTicks() +
+        base::TimeDelta::FromMilliseconds(g_rollover_ms + timestamp);
+
+  DCHECK(timestamp64 <= UINT32_MAX)
+      << "X11 Time does not roll over 32 bit, the below logic is likely wrong";
+
+  base::TimeTicks now_ticks = ui::EventTimeForNow();
+  int64_t now_ms = (now_ticks - base::TimeTicks()).InMilliseconds();
+
+  g_rollover_ms = now_ms & ~static_cast<int64_t>(UINT32_MAX);
+  uint32_t delta = static_cast<uint32_t>(now_ms - timestamp);
+  return base::TimeTicks() + base::TimeDelta::FromMilliseconds(now_ms - delta);
 }
 
 }  // namespace
@@ -838,12 +831,9 @@
   return XModifierStateWatcher::GetInstance()->state() & Mod1Mask;
 }
 
-void SetTimestampServer(TimestampServer* server) {
-  // This method must be setting or unsetting a timestamp server. It should
-  // never replace an existing timestamp server, nor change from
-  // nullptr->nullptr.
-  CHECK(!!g_timestamp_server ^ !!server);
-  g_timestamp_server = server;
+void ResetTimestampRolloverCountersForTesting() {
+  g_last_seen_timestamp_ms = 0;
+  g_rollover_ms = 0;
 }
 
 }  // namespace ui
diff --git a/ui/events/x/events_x_utils.h b/ui/events/x/events_x_utils.h
index df210f2..64814e87 100644
--- a/ui/events/x/events_x_utils.h
+++ b/ui/events/x/events_x_utils.h
@@ -16,8 +16,6 @@
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/x/x11_types.h"
 
-using Time = unsigned long;
-
 namespace ui {
 
 // Gets the EventType from a XEvent.
@@ -93,15 +91,6 @@
 
 EVENTS_X_EXPORT void ResetTimestampRolloverCountersForTesting();
 
-// Conversion from X Time to base::TimeTicks requires checking the current X
-// Server Time. This functionality is provided by X11EventSource, but due to odd
-// layering that cannot be referenced directly.
-class TimestampServer {
- public:
-  virtual Time GetCurrentServerTime() = 0;
-};
-EVENTS_X_EXPORT void SetTimestampServer(TimestampServer* server);
-
 }  // namespace ui
 
 #endif  // UI_EVENTS_X_EVENTS_X_UTILS_H_
diff --git a/ui/file_manager/audio_player/js/BUILD.gn b/ui/file_manager/audio_player/js/BUILD.gn
index bcfcc90..08c3cc9 100644
--- a/ui/file_manager/audio_player/js/BUILD.gn
+++ b/ui/file_manager/audio_player/js/BUILD.gn
@@ -18,7 +18,6 @@
   sources = []
   externs_list = [
     "$externs_path/chrome_extensions.js",
-    "$externs_path/command_line_private.js",
     "$externs_path/metrics_private.js",
     "../../externs/audio_player_foreground.js",
     "../../externs/entry_location.js",
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index 6ce1485b..a645f68 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -55,7 +55,6 @@
 js_library("closure_compile_externs") {
   sources = []
   externs_list = [
-    "$externs_path/command_line_private.js",
     "$externs_path/metrics_private.js",
     "../../../externs/background/file_browser_background.js",
     "../../../externs/background/file_browser_background_full.js",
diff --git a/ui/file_manager/file_manager/common/js/BUILD.gn b/ui/file_manager/file_manager/common/js/BUILD.gn
index 72772a1..30d6647 100644
--- a/ui/file_manager/file_manager/common/js/BUILD.gn
+++ b/ui/file_manager/file_manager/common/js/BUILD.gn
@@ -101,6 +101,7 @@
   deps = [
     "//ui/webui/resources/js:webui_resource_test",
   ]
+  externs_list = [ "$externs_path/command_line_private.js" ]
 }
 
 js_library("util") {
diff --git a/ui/file_manager/file_manager/common/js/unittest_util.js b/ui/file_manager/file_manager/common/js/unittest_util.js
index 2bdfc03..cf7b20b 100644
--- a/ui/file_manager/file_manager/common/js/unittest_util.js
+++ b/ui/file_manager/file_manager/common/js/unittest_util.js
@@ -12,12 +12,14 @@
  *     test failed.
  */
 function reportPromise(promise, callback) {
-  promise.then(function() {
-    callback(/* error */ false);
-  }, function(error) {
-    console.error(error.stack || error);
-    callback(/* error */ true);
-  });
+  promise.then(
+      function() {
+        callback(/* error */ false);
+      },
+      function(/** @type {Error} */ error) {
+        console.error(error.stack || error);
+        callback(/* error */ true);
+      });
 }
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn b/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn
index b66e809..f686653 100644
--- a/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn
@@ -38,7 +38,6 @@
 js_library("closure_compile_externs") {
   sources = []
   externs_list = [
-    "$externs_path/command_line_private.js",
     "../../../../externs/app_window_common.js",
     "../../../../externs/entry_location.js",
     "../../../../externs/platform.js",
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index 724d7e76..d66b4acb 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -45,7 +45,6 @@
 js_library("closure_compile_externs") {
   sources = []
   externs_list = [
-    "$externs_path/command_line_private.js",
     "$externs_path/metrics_private.js",
     "$externs_path/web_animations.js",
     "../../../../externs/app_window_common.js",
diff --git a/ui/file_manager/file_manager/foreground/js/ui/location_line.js b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
index da6a68f..183ca78 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/location_line.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/location_line.js
@@ -89,8 +89,13 @@
   }
   if (locationInfo.rootType === VolumeManagerCommon.RootType.DRIVE_OTHER) {
     // When target path is a shared directory, volume should be shared with me.
-    displayRootUrl = this.replaceRootName_(displayRootUrl, '/other');
-    displayRootFullPath = '/other';
+    const match = entry.fullPath.match(/\/\.files-by-id\/\d+\//);
+    if (match) {
+      displayRootFullPath = match[0];
+    } else {
+      displayRootFullPath = '/other';
+    }
+    displayRootUrl = this.replaceRootName_(displayRootUrl, displayRootFullPath);
     var sharedWithMeFakeEntry = locationInfo.volumeInfo.fakeEntries[
         VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME];
     components.push(new LocationLine.PathComponent(
diff --git a/ui/file_manager/image_loader/BUILD.gn b/ui/file_manager/image_loader/BUILD.gn
index 8c5fdb5..f883211 100644
--- a/ui/file_manager/image_loader/BUILD.gn
+++ b/ui/file_manager/image_loader/BUILD.gn
@@ -102,7 +102,6 @@
     ":piex_loader",
     "../file_manager/common/js:unittest_util",
     "//ui/webui/resources/js:webui_resource_test",
-    "//ui/webui/resources/js/cr:ui",
   ]
 }
 
@@ -126,8 +125,7 @@
 }
 
 js_unit_tests("unit_tests") {
-  # TODO(tapted): Uncomment the next line.
-  # closure_flags = default_closure_args + [ "jscomp_error=strictCheckTypes" ]
+  closure_flags = default_closure_args + [ "jscomp_error=strictCheckTypes" ]
   deps = [
     ":cache_unittest",
     ":image_loader_client_unittest",
diff --git a/ui/file_manager/image_loader/piex_loader_unittest.js b/ui/file_manager/image_loader/piex_loader_unittest.js
index 9d09aea..4f69e74c 100644
--- a/ui/file_manager/image_loader/piex_loader_unittest.js
+++ b/ui/file_manager/image_loader/piex_loader_unittest.js
@@ -8,36 +8,37 @@
   }
 };
 
-/**
- * @constructor
- * @extends {HTMLDivElement}
- */
-var MockModule = /** @type{function(new:MockModule)}*/ (cr.ui.define('div'));
-MockModule.prototype = Object.create(HTMLDivElement.prototype);
-MockModule.prototype.constructor = MockModule;
+class MockModule extends HTMLDivElement {
+  constructor() {
+    super();
+    /** @type{?function()} */
+    this.onBeforeMessageCallback_ = null;
+    setTimeout(() => {
+      this.dispatchEvent(new Event('load', {bubbles: true}));
+    });
+  }
 
-MockModule.prototype.setBeforeMessageCallback = function(callback) {
-  this.onBeforeMessageCallback_ = callback;
-};
+  setBeforeMessageCallback(callback) {
+    this.onBeforeMessageCallback_ = callback;
+  }
 
-MockModule.prototype.decorate = function() {
-  this.onBeforeMessageCallback_ = null;
-  setTimeout(function() {
-    this.dispatchEvent(new Event('load', {bubbles: true}));
-  }.bind(this));
-};
+  postMessage(message) {
+    setTimeout(() => {
+      this.dispatchEvent(new Event('load', {bubbles: true}));
+      if (this.onBeforeMessageCallback_)
+        this.onBeforeMessageCallback_();
 
-MockModule.prototype.postMessage = function(message) {
-  setTimeout(function() {
-    this.dispatchEvent(new Event('load', {bubbles: true}));
-    if (this.onBeforeMessageCallback_)
-      this.onBeforeMessageCallback_();
+      let e = new Event('message', {bubbles: true});
+      // Cast to a MessageEvent to write to |data|. We can't use
+      // `new MessageEvent` since its |data| property is read-only.
+      /** @type{MessageEvent} */ (e)
+          .data = {id: message.id, thumbnail: 'thumbnail-data', orientation: 1};
+      this.dispatchEvent(e);
+    });
+  }
+}
 
-    var e = new CustomEvent('message', {bubbles: true});
-    e.data = {id: message.id, thumbnail: 'thumbnail-data', orientation: 1};
-    this.dispatchEvent(e);
-  }.bind(this));
-};
+customElements.define('mock-module', MockModule, {extends: 'div'});
 
 function testUnloadingAfterTimeout(callback) {
   var loadCount = 0;
diff --git a/ui/file_manager/integration_tests/file_manager/drive_specific.js b/ui/file_manager/integration_tests/file_manager/drive_specific.js
index 4dc1dda..d2c93b3 100644
--- a/ui/file_manager/integration_tests/file_manager/drive_specific.js
+++ b/ui/file_manager/integration_tests/file_manager/drive_specific.js
@@ -119,10 +119,20 @@
  * "shared-with-me" should be shown.
  */
 testcase.driveOpenSidebarSharedWithMe = function() {
-  var appId;
+  let appId;
+  let isDriveFsEnabled;
   StepsRunner.run([
     function() {
-      setupAndWaitUntilReady(null, RootPath.DRIVE, this.next);
+      sendTestMessage({name: 'getDriveFsEnabled'}).then(this.next);
+    },
+    function(result) {
+      isDriveFsEnabled = result === 'true';
+
+      setupAndWaitUntilReady(
+          null, RootPath.DRIVE, this.next, [], BASIC_DRIVE_ENTRY_SET.concat([
+            ENTRIES.sharedDirectory,
+            ENTRIES.sharedDirectoryFile,
+          ]));
     },
     // Click the icon of the Shared With Me volume.
     function(results) {
@@ -132,17 +142,45 @@
                                     appId,
                                     ['drive_shared_with_me'], this.next);
     },
-    // Wait until the file list is updated.
+    // Wait until the breadcrumb path is updated.
     function(result) {
       chrome.test.assertFalse(!result);
-      remoteCall.waitForFileListChange(appId, BASIC_DRIVE_ENTRY_SET.length).
-          then(this.next);
+      remoteCall.waitUntilCurrentDirectoryIsChanged(appId, '/Shared with me')
+          .then(this.next);
     },
     // Verify the file list.
-    function(actualFilesAfter) {
-      chrome.test.assertEq(
-          TestEntryInfo.getExpectedRows(SHARED_WITH_ME_ENTRY_SET).sort(),
-          actualFilesAfter);
+    function() {
+      remoteCall
+          .waitForFiles(
+              appId,
+              TestEntryInfo.getExpectedRows(
+                  SHARED_WITH_ME_ENTRY_SET.concat([ENTRIES.sharedDirectory])))
+          .then(this.next);
+    },
+    // Navigate to the directory within Shared with me.
+    function() {
+      remoteCall.callRemoteTestUtil('openFile', appId, ['Shared Directory'])
+          .then(this.next);
+    },
+    // Wait until the breadcrumb path is updated.
+    function(result) {
+      chrome.test.assertFalse(!result);
+      remoteCall
+          .waitUntilCurrentDirectoryIsChanged(
+              appId,
+              isDriveFsEnabled ? '/Shared with me/Shared Directory' :
+                                 '/My Drive/Shared Directory')
+          .then(this.next);
+    },
+    // Verify the file list.
+    function() {
+      remoteCall
+          .waitForFiles(
+              appId,
+              TestEntryInfo.getExpectedRows([ENTRIES.sharedDirectoryFile]))
+          .then(this.next);
+    },
+    function() {
       checkIfNoErrorsOccured(this.next);
     }
   ]);
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index b499fcc1..82235ef 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -209,7 +209,9 @@
  */
 var SharedOption = Object.freeze({
   NONE: 'none',
-  SHARED: 'shared'
+  SHARED: 'shared',
+  SHARED_WITH_ME: 'sharedWithMe',
+  NESTED_SHARED_WITH_ME: 'nestedSharedWithMe',
 });
 
 /**
@@ -818,4 +820,26 @@
     sizeText: '51 bytes',
     typeText: 'Plain text'
   }),
+
+  sharedDirectory: new TestEntryInfo({
+    type: EntryType.DIRECTORY,
+    targetPath: 'Shared Directory',
+    sharedOption: SharedOption.SHARED_WITH_ME,
+    lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
+    nameText: 'Shared Directory',
+    sizeText: '--',
+    typeText: 'Folder'
+  }),
+
+  sharedDirectoryFile: new TestEntryInfo({
+    type: EntryType.FILE,
+    sourceFileName: 'text.txt',
+    targetPath: 'Shared Directory/file.txt',
+    mimeType: 'text/plain',
+    sharedOption: SharedOption.NESTED_SHARED_WITH_ME,
+    lastModifiedTime: 'Jan 1, 2000, 1:00 AM',
+    nameText: 'file.txt',
+    sizeText: '51 bytes',
+    typeText: 'Plain text'
+  }),
 };
diff --git a/ui/file_manager/video_player/js/BUILD.gn b/ui/file_manager/video_player/js/BUILD.gn
index 359597a..1ac9457 100644
--- a/ui/file_manager/video_player/js/BUILD.gn
+++ b/ui/file_manager/video_player/js/BUILD.gn
@@ -20,7 +20,6 @@
   sources = []
   externs_list = [
     "$externs_path/chrome_extensions.js",
-    "$externs_path/command_line_private.js",
     "$externs_path/media_player_private.js",
     "$externs_path/metrics_private.js",
     "../../externs/chrome_cast.js",
diff --git a/ui/file_manager/video_player/js/cast/BUILD.gn b/ui/file_manager/video_player/js/cast/BUILD.gn
index b0ca2c8..aaa6a83 100644
--- a/ui/file_manager/video_player/js/cast/BUILD.gn
+++ b/ui/file_manager/video_player/js/cast/BUILD.gn
@@ -18,7 +18,6 @@
   sources = []
   externs_list = [
     "$externs_path/chrome_extensions.js",
-    "$externs_path/command_line_private.js",
     "$externs_path/media_player_private.js",
     "$externs_path/metrics_private.js",
     "../../../externs/app_window_common.js",
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 4f727ce..02bc6279 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -220,6 +220,9 @@
       "shadow_util.h",
       "skia_paint_util.cc",
       "skia_paint_util.h",
+      "skia_vector_animation.cc",
+      "skia_vector_animation.h",
+      "skia_vector_animation_observer.h",
     ]
   }
 
@@ -686,6 +689,7 @@
       "sequential_id_generator_unittest.cc",
       "shadow_value_unittest.cc",
       "skbitmap_operations_unittest.cc",
+      "skia_vector_animation_unittest.cc",
       "skrect_conversion_unittest.cc",
       "transform_util_unittest.cc",
       "utf16_indexing_unittest.cc",
diff --git a/ui/gfx/skia_vector_animation.cc b/ui/gfx/skia_vector_animation.cc
new file mode 100644
index 0000000..191e327
--- /dev/null
+++ b/ui/gfx/skia_vector_animation.cc
@@ -0,0 +1,269 @@
+// 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/gfx/skia_vector_animation.h"
+
+#include "base/trace_event/trace_event.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/skia_vector_animation_observer.h"
+
+namespace gfx {
+
+SkiaVectorAnimation::TimerControl::TimerControl(
+    const base::TimeDelta& offset,
+    const base::TimeDelta& cycle_duration,
+    const base::TimeDelta& total_duration,
+    const base::TimeTicks& start_timestamp,
+    bool should_reverse)
+    : start_offset_(offset),
+      end_offset_((offset + cycle_duration)),
+      cycle_duration_(end_offset_ - start_offset_),
+      progress_per_millisecond_(1.0 / total_duration.InMillisecondsF()),
+      previous_tick_(start_timestamp),
+      progress_(base::TimeDelta::FromMilliseconds(0)),
+      current_cycle_progress_(start_offset_),
+      should_reverse_(should_reverse) {}
+
+void SkiaVectorAnimation::TimerControl::Step(const base::TimeTicks& timestamp) {
+  progress_ += timestamp - previous_tick_;
+  previous_tick_ = timestamp;
+
+  base::TimeDelta completed_cycles_duration =
+      completed_cycles_ * cycle_duration_;
+  if (progress_ >= completed_cycles_duration + cycle_duration_) {
+    completed_cycles_++;
+    completed_cycles_duration += cycle_duration_;
+  }
+
+  current_cycle_progress_ =
+      start_offset_ + progress_ - completed_cycles_duration;
+  if (should_reverse_ && completed_cycles_ % 2) {
+    current_cycle_progress_ =
+        end_offset_ - (current_cycle_progress_ - start_offset_);
+  }
+}
+
+void SkiaVectorAnimation::TimerControl::Resume(
+    const base::TimeTicks& timestamp) {
+  previous_tick_ = timestamp;
+}
+
+double SkiaVectorAnimation::TimerControl::GetNormalizedCurrentCycleProgress()
+    const {
+  return current_cycle_progress_.InMillisecondsF() * progress_per_millisecond_;
+}
+
+double SkiaVectorAnimation::TimerControl::GetNormalizedStartOffset() const {
+  return start_offset_.InMillisecondsF() * progress_per_millisecond_;
+}
+
+double SkiaVectorAnimation::TimerControl::GetNormalizedEndOffset() const {
+  return end_offset_.InMillisecondsF() * progress_per_millisecond_;
+}
+
+SkiaVectorAnimation::SkiaVectorAnimation(
+    const scoped_refptr<base::RefCountedMemory>& data_stream) {
+  TRACE_EVENT0("ui", "SkiaVectorAnimation Parse");
+  SkMemoryStream sk_stream(data_stream->front(), data_stream->size());
+  animation_ = skottie::Animation::Make(&sk_stream);
+}
+
+SkiaVectorAnimation::SkiaVectorAnimation(std::unique_ptr<SkMemoryStream> stream)
+    : animation_(skottie::Animation::Make(stream.get())) {}
+
+SkiaVectorAnimation::~SkiaVectorAnimation() {}
+
+void SkiaVectorAnimation::SetAnimationObserver(
+    SkiaVectorAnimationObserver* observer) {
+  DCHECK(!observer_ || !observer);
+  observer_ = observer;
+}
+
+base::TimeDelta SkiaVectorAnimation::GetAnimationDuration() const {
+  return base::TimeDelta::FromMilliseconds(
+      std::floor(SkScalarToFloat(animation_->duration()) * 1000.f));
+}
+
+gfx::Size SkiaVectorAnimation::GetOriginalSize() const {
+#if DCHECK_IS_ON()
+  // The size should have no fractional component.
+  gfx::SizeF float_size = gfx::SkSizeToSizeF(animation_->size());
+  gfx::Size rounded_size = gfx::ToRoundedSize(float_size);
+
+  float height_diff = std::abs(float_size.height() - rounded_size.height());
+  float width_diff = std::abs(float_size.width() - rounded_size.width());
+
+  DCHECK_LE(height_diff, std::numeric_limits<float>::epsilon());
+  DCHECK_LE(width_diff, std::numeric_limits<float>::epsilon());
+#endif
+  return gfx::ToRoundedSize(gfx::SkSizeToSizeF(animation_->size()));
+}
+
+void SkiaVectorAnimation::Start(Style style) {
+  DCHECK_NE(state_, PlayState::kPaused);
+  DCHECK_NE(state_, PlayState::kPlaying);
+  StartSubsection(base::TimeDelta(), GetAnimationDuration(), style);
+}
+
+void SkiaVectorAnimation::StartSubsection(base::TimeDelta start_offset,
+                                          base::TimeDelta duration,
+                                          Style style) {
+  DCHECK(state_ == PlayState::kStopped || state_ == PlayState::kEnded);
+  DCHECK_LE(start_offset + duration, GetAnimationDuration());
+
+  style_ = style;
+
+  // Reset the |timer_control_| object for a new animation play.
+  timer_control_.reset(nullptr);
+
+  // Schedule a play for the animation and store the necessary information
+  // needed to start playing.
+  state_ = PlayState::kSchedulePlay;
+  scheduled_start_offset_ = start_offset;
+  scheduled_duration_ = duration;
+}
+
+void SkiaVectorAnimation::Pause() {
+  DCHECK(state_ == PlayState::kPlaying || state_ == PlayState::kSchedulePlay);
+  state_ = PlayState::kPaused;
+}
+
+void SkiaVectorAnimation::ResumePlaying() {
+  DCHECK(state_ == PlayState::kPaused);
+  state_ = PlayState::kScheduleResume;
+}
+
+void SkiaVectorAnimation::Stop() {
+  state_ = PlayState::kStopped;
+  timer_control_.reset(nullptr);
+}
+
+float SkiaVectorAnimation::GetCurrentProgress() const {
+  switch (state_) {
+    case PlayState::kStopped:
+      return 0;
+    case PlayState::kEnded:
+      DCHECK(timer_control_);
+      return timer_control_->GetNormalizedEndOffset();
+    case PlayState::kPaused:
+      if (timer_control_) {
+        return timer_control_->GetNormalizedCurrentCycleProgress();
+      } else {
+        // It may be that the timer hasn't been initialized which may happen if
+        // the animation was paused while it was in |kScheculePlay| state.
+        return scheduled_start_offset_.InMillisecondsF() /
+               animation_->duration();
+      }
+    case PlayState::kSchedulePlay:
+    case PlayState::kPlaying:
+    case PlayState::kScheduleResume:
+      // The timer control needs to be initialized before making this call. It
+      // may not have been initialized if OnAnimationStep has not been called
+      // yet
+      DCHECK(timer_control_);
+      return timer_control_->GetNormalizedCurrentCycleProgress();
+  }
+}
+
+void SkiaVectorAnimation::Paint(gfx::Canvas* canvas,
+                                const base::TimeTicks& timestamp,
+                                const gfx::Size& size) {
+  switch (state_) {
+    case PlayState::kStopped:
+      return;
+    case PlayState::kSchedulePlay:
+      InitTimer(timestamp);
+      state_ = PlayState::kPlaying;
+      if (observer_)
+        observer_->AnimationWillStartPlaying(this);
+      break;
+    case PlayState::kPlaying:
+      UpdateState(timestamp);
+      break;
+    case PlayState::kPaused:
+      break;
+    case PlayState::kScheduleResume:
+      state_ = PlayState::kPlaying;
+      if (timer_control_) {
+        timer_control_->Resume(timestamp);
+      } else {
+        // The animation may have been paused after a play was scheduled but
+        // before it started playing.
+        InitTimer(timestamp);
+      }
+      if (observer_)
+        observer_->AnimationResuming(this);
+      break;
+    case PlayState::kEnded:
+      break;
+  }
+  PaintFrame(canvas, GetCurrentProgress(), size);
+}
+
+void SkiaVectorAnimation::PaintFrame(gfx::Canvas* canvas,
+                                     float t,
+                                     const gfx::Size& size) {
+  TRACE_EVENT0("ui", "SkiaVectorAnimation Paint");
+  DCHECK_GE(t, 0.f);
+  DCHECK_LE(t, 1.f);
+
+  animation_->seek(t);
+  float scale = canvas->UndoDeviceScaleFactor();
+
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(std::round(size.width() * scale),
+                        std::round(size.height() * scale), false);
+  SkCanvas skcanvas(bitmap);
+  skcanvas.clear(SK_ColorTRANSPARENT);
+  SkRect dst = SkRect::MakeXYWH(0, 0, std::round(size.width() * scale),
+                                std::round(size.height() * scale));
+  animation_->render(&skcanvas, &dst);
+
+  canvas->DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bitmap), 0, 0);
+}
+
+void SkiaVectorAnimation::InitTimer(const base::TimeTicks& timestamp) {
+  DCHECK(!timer_control_);
+  timer_control_ = std::make_unique<TimerControl>(
+      scheduled_start_offset_, scheduled_duration_, GetAnimationDuration(),
+      timestamp, style_ == Style::kThrobbing);
+}
+
+void SkiaVectorAnimation::UpdateState(const base::TimeTicks& timestamp) {
+  DCHECK(timer_control_);
+  int cycles = timer_control_->completed_cycles();
+  timer_control_->Step(timestamp);
+
+  if (cycles == timer_control_->completed_cycles())
+    return;
+
+  bool inform_observer = true;
+  switch (style_) {
+    case Style::kLoop:
+      break;
+    case Style::kThrobbing:
+      // For a throbbing animation, the animation cycle ends when the timer
+      // goes from 0 to 1 and then back to 0. So the number of timer cycles
+      // must be even at the end of one throbbing animation cycle.
+      if (timer_control_->completed_cycles() % 2 != 0)
+        inform_observer = false;
+      break;
+    case Style::kLinear:
+      state_ = PlayState::kEnded;
+      break;
+  }
+
+  // Inform observer if the cycle has ended.
+  if (observer_ && inform_observer) {
+    observer_->AnimationCycleEnded(this);
+  }
+}
+
+}  // namespace gfx
diff --git a/ui/gfx/skia_vector_animation.h b/ui/gfx/skia_vector_animation.h
new file mode 100644
index 0000000..f0c133f
--- /dev/null
+++ b/ui/gfx/skia_vector_animation.h
@@ -0,0 +1,239 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_SKIA_VECTOR_ANIMATION_H_
+#define UI_GFX_SKIA_VECTOR_ANIMATION_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/time/time.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/modules/skottie/include/Skottie.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+class Canvas;
+class SkiaVectorAnimationTest;
+class SkiaVectorAnimationObserver;
+
+// This class is a wrapper over the Skia object for lottie vector graphic
+// animations. It has its own timeline manager for the animation controls. The
+// framerate of the animation and the animation ticks are controlled externally
+// and hence the consumer must manage the timer and call paint at the desired
+// frame per second.
+// This helps keep multiple animations be synchronized by having a common
+// external tick clock.
+// In general you want to use the view framework integrated class that we have
+// for SkiaVectorAnimation instead of this class.
+//
+// Usage example:
+//   1. Rendering a single frame on the canvas:
+//        SkiaVectorAnimation animation_ = SkiaVectorAnimation(data);
+//        animation_.Paint(canvas, t);
+//
+//   2. Playing the animation and rendering each frame:
+//      void SampleClient::Init() {
+//        SkiaVectorAnimation animation_ = SkiaVectorAnimation(data);
+//        animation_.Start(SkiaVectorAnimation::Style::LINEAR);
+//      }
+//
+//      // overrides cc::CompositorAnimationObserver
+//      void SampleClient::OnAnimationStep(TimeTicks* timestamp) {
+//        timestamp_ = timestamp;
+//        SchedulePaint();
+//      }
+//
+//      void SampleClient::OnPaint(Canvas* canvas) {
+//        animation_.Paint(canvas, timestamp_);
+//      }
+//
+//   2. If you only want to play a subsection of the animation:
+//      void SampleClient::Init() {
+//        // This will seek to the 1st second of the animation and from there
+//        // play it for 5 seconds.
+//        SkiaVectorAnimation animation_ = SkiaVectorAnimation(data);
+//        animation_.Start(TimeDelta::FromSeconds(1),
+//                         TimeDelta::FromSeconds(5));
+//      }
+//
+//      // overrides cc::CompositorAnimationObserver
+//      void SampleClient::OnAnimationStep(TimeTicks*) {
+//        timestamp_ = timestamp;
+//        SchedulePaint();
+//      }
+//
+//      void SampleClient::OnPaint(Canvas* canvas) {
+//        animation_.Paint(canvas, timestamp_, gfx::Size(10, 10));
+//      }
+//
+class GFX_EXPORT SkiaVectorAnimation {
+ public:
+  enum class Style {
+    kLinear = 0,  // The animation plays from one time instant to another.
+    kThrobbing,   // The animation plays from one time instant to another and
+                  // then back. The animation plays in loop until stopped.
+    kLoop         // Same as LINEAR, except the animation repeats after it ends.
+  };
+
+  explicit SkiaVectorAnimation(
+      const scoped_refptr<base::RefCountedMemory>& data_stream);
+  explicit SkiaVectorAnimation(std::unique_ptr<SkMemoryStream> stream);
+  ~SkiaVectorAnimation();
+
+  void SetAnimationObserver(SkiaVectorAnimationObserver* Observer);
+
+  // Animation properties ------------------------------------------------------
+  // Returns the total duration of the animation as reported by |animation_|.
+  base::TimeDelta GetAnimationDuration() const;
+
+  // Returns the size of the vector graphic as reported by |animation_|. This is
+  // constant for a given |animation_|.
+  gfx::Size GetOriginalSize() const;
+
+  // Animation controls --------------------------------------------------------
+  // This is an asynchronous call that would start playing the animation on the
+  // next animation step. On a successful start the |observer_| would be
+  // notified. Use this if you want to play the entire animation.
+  void Start(Style style = Style::kLoop);
+
+  // This is an asynchronous call that would start playing the animation on the
+  // next animation step. On a successful start the |observer_| would be
+  // notified.
+  // The animation will be scheduled to play from the |start_offset| to
+  // |start_offset| + |duration|. The values will be clamped so as to not go out
+  // of bounds.
+  void StartSubsection(base::TimeDelta start_offset,
+                       base::TimeDelta duration,
+                       Style style = Style::kLoop);
+
+  // Pauses the animation.
+  void Pause();
+
+  // This is an asynchronous call that would resume playing a paused animation
+  // on the next animation step.
+  void ResumePlaying();
+
+  // Resets the animation to the first frame and stops.
+  void Stop();
+
+  // Returns the current normalized [0..1] value at which the animation frame
+  // is.
+  // 0 -> first frame and 1 -> last frame.
+  float GetCurrentProgress() const;
+
+  // Paint operations ----------------------------------------------------------
+  // Paints the frame of the animation for the given |timestamp| at the given
+  // |size|.
+  void Paint(gfx::Canvas* canvas,
+             const base::TimeTicks& timestamp,
+             const gfx::Size& size);
+
+  // Paints the frame of the animation for the normalized time instance |t|. Use
+  // this for special cases when you want to manually manage which frame to
+  // paint.
+  void PaintFrame(gfx::Canvas* canvas, float t, const gfx::Size& size);
+
+ private:
+  friend class SkiaVectorAnimationTest;
+
+  enum class PlayState {
+    kStopped = 0,     // Animation is stopped.
+    kSchedulePlay,    // Animation will start playing on the next animatin step.
+    kPlaying,         // Animation is playing.
+    kPaused,          // Animation is paused.
+    kScheduleResume,  // Animation will resume playing on the next animation
+                      // step
+    kEnded            // Animation has ended.
+  };
+
+  // Class to manage the timeline when playing the animation. Manages the
+  // normalized progress [0..1] between the given start and end offset. If the
+  // reverse flag is set, the progress runs in reverse.
+  class GFX_EXPORT TimerControl {
+   public:
+    TimerControl(const base::TimeDelta& offset,
+                 const base::TimeDelta& cycle_duration,
+                 const base::TimeDelta& total_duration,
+                 const base::TimeTicks& start_timestamp,
+                 bool should_reverse);
+    ~TimerControl() = default;
+
+    // Update timeline progress based on the new timetick |timestamp|.
+    void Step(const base::TimeTicks& timestamp);
+
+    // Resumes the timer.
+    void Resume(const base::TimeTicks& timestamp);
+
+    double GetNormalizedCurrentCycleProgress() const;
+    double GetNormalizedStartOffset() const;
+    double GetNormalizedEndOffset() const;
+    int completed_cycles() const { return completed_cycles_; }
+
+   private:
+    friend class SkiaVectorAnimationTest;
+
+    // Time duration from 0 which marks the beginning of a cycle.
+    const base::TimeDelta start_offset_;
+
+    // Time duration  from 0 which marks the end of a cycle.
+    const base::TimeDelta end_offset_;
+
+    // Time duration for one cycle. This is essentially a cache of the
+    // difference between |end_offset_| - |start_offset_|.
+    const base::TimeDelta cycle_duration_;
+
+    // Normalized animation progress delta per millisecond, that is, the
+    // normalized progress in per millisecond of time duration.
+    const double progress_per_millisecond_;
+
+    // The timetick at which |progress_| was updated last.
+    base::TimeTicks previous_tick_;
+
+    // The progress of the timer. This is a monotonically increasing value.
+    base::TimeDelta progress_;
+
+    // This is the progress of the timer in the current cycle.
+    base::TimeDelta current_cycle_progress_;
+
+    // If true, the progress will go into reverse after each cycle. This is used
+    // for throbbing animations.
+    bool should_reverse_ = false;
+
+    // The number of times each |cycle_duration_| is covered by the timer.
+    int completed_cycles_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(TimerControl);
+  };
+
+  void InitTimer(const base::TimeTicks& timestamp);
+  void UpdateState(const base::TimeTicks& timestamp);
+
+  // Manages the timeline for the current playing animation.
+  std::unique_ptr<TimerControl> timer_control_;
+
+  // The style of animation to play.
+  Style style_ = Style::kLoop;
+
+  // The current state of animation.
+  PlayState state_ = PlayState::kStopped;
+
+  // The below values of scheduled_* are set when we have scheduled a play.
+  // These will be used to initialize |timer_control_|.
+  base::TimeDelta scheduled_start_offset_;
+  base::TimeDelta scheduled_duration_;
+
+  SkiaVectorAnimationObserver* observer_ = nullptr;
+
+  sk_sp<skottie::Animation> animation_;
+
+  DISALLOW_COPY_AND_ASSIGN(SkiaVectorAnimation);
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_SKIA_VECTOR_ANIMATION_H_
diff --git a/ui/gfx/skia_vector_animation_observer.h b/ui/gfx/skia_vector_animation_observer.h
new file mode 100644
index 0000000..1e6e479a
--- /dev/null
+++ b/ui/gfx/skia_vector_animation_observer.h
@@ -0,0 +1,33 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_GFX_SKIA_VECTOR_ANIMATION_OBSERVER_H_
+#define UI_GFX_SKIA_VECTOR_ANIMATION_OBSERVER_H_
+
+#include "ui/gfx/gfx_export.h"
+
+namespace gfx {
+class SkiaVectorAnimation;
+
+class GFX_EXPORT SkiaVectorAnimationObserver {
+ public:
+  // Called when the animation started playing.
+  virtual void AnimationWillStartPlaying(const SkiaVectorAnimation* animation) {
+  }
+
+  // Called when one animation cycle has completed. This happens when a linear
+  // animation has reached its end, or a loop/throbbing animation has finished
+  // a cycle.
+  virtual void AnimationCycleEnded(const SkiaVectorAnimation* animation) {}
+
+  // Called when the animation has successfully resumed.
+  virtual void AnimationResuming(const SkiaVectorAnimation* animation) {}
+
+ protected:
+  virtual ~SkiaVectorAnimationObserver() = default;
+};
+
+}  // namespace gfx
+
+#endif  // UI_GFX_SKIA_VECTOR_ANIMATION_OBSERVER_H_
diff --git a/ui/gfx/skia_vector_animation_unittest.cc b/ui/gfx/skia_vector_animation_unittest.cc
new file mode 100644
index 0000000..f9d9737
--- /dev/null
+++ b/ui/gfx/skia_vector_animation_unittest.cc
@@ -0,0 +1,852 @@
+// 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/gfx/skia_vector_animation.h"
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/skia_vector_animation_observer.h"
+
+namespace gfx {
+namespace {
+
+// A skottie animation with solid green color for the first 2.5 seconds and then
+// a solid blue color for the next 2.5 seconds.
+constexpr char kData[] =
+    "{"
+    "  \"v\" : \"4.12.0\","
+    "  \"fr\": 30,"
+    "  \"w\" : 400,"
+    "  \"h\" : 200,"
+    "  \"ip\": 0,"
+    "  \"op\": 150,"
+    "  \"assets\": [],"
+
+    "  \"layers\": ["
+    "    {"
+    "      \"ty\": 1,"
+    "      \"sw\": 400,"
+    "      \"sh\": 200,"
+    "      \"sc\": \"#00ff00\","
+    "      \"ip\": 0,"
+    "      \"op\": 75"
+    "    },"
+    "    {"
+    "      \"ty\": 1,"
+    "      \"sw\": 400,"
+    "      \"sh\": 200,"
+    "      \"sc\": \"#0000ff\","
+    "      \"ip\": 76,"
+    "      \"op\": 150"
+    "    }"
+    "  ]"
+    "}";
+constexpr float kAnimationWidth = 400.f;
+constexpr float kAnimationHeight = 200.f;
+constexpr float kAnimationDuration = 5.f;
+
+class TestAnimationObserver : public SkiaVectorAnimationObserver {
+ public:
+  TestAnimationObserver() = default;
+
+  void AnimationWillStartPlaying(
+      const SkiaVectorAnimation* animation) override {
+    animation_will_start_playing_ = true;
+  }
+
+  void AnimationCycleEnded(const SkiaVectorAnimation* animation) override {
+    animation_cycle_ended_ = true;
+  }
+
+  void AnimationResuming(const SkiaVectorAnimation* animation) override {
+    animation_resuming_ = true;
+  }
+
+  void Reset() {
+    animation_cycle_ended_ = false;
+    animation_will_start_playing_ = false;
+    animation_resuming_ = false;
+  }
+
+  bool animation_cycle_ended() const { return animation_cycle_ended_; }
+  bool animation_will_start_playing() const {
+    return animation_will_start_playing_;
+  }
+  bool animation_resuming() const { return animation_resuming_; }
+
+ private:
+  bool animation_cycle_ended_ = false;
+  bool animation_will_start_playing_ = false;
+  bool animation_resuming_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAnimationObserver);
+};
+
+}  // namespace
+
+class SkiaVectorAnimationTest : public testing::Test {
+ public:
+  SkiaVectorAnimationTest() = default;
+  ~SkiaVectorAnimationTest() override {}
+
+  void SetUp() override {
+    canvas_.reset(new gfx::Canvas(gfx::Size(kAnimationWidth, kAnimationHeight),
+                                  1.f, false));
+  }
+
+  void TearDown() override { animation_.reset(nullptr); }
+
+  Canvas* canvas() { return canvas_.get(); }
+
+  SkiaVectorAnimation::Style GetStyle() const { return animation_->style_; }
+
+  SkiaVectorAnimation::PlayState GetState() const { return animation_->state_; }
+
+  bool IsStopped() const {
+    return GetState() == SkiaVectorAnimation::PlayState::kStopped;
+  }
+
+  bool IsScheduledToPlay() const {
+    return GetState() == SkiaVectorAnimation::PlayState::kSchedulePlay;
+  }
+
+  bool IsPlaying() const {
+    return GetState() == SkiaVectorAnimation::PlayState::kPlaying;
+  }
+
+  bool IsScheduledToResume() const {
+    return GetState() == SkiaVectorAnimation::PlayState::kScheduleResume;
+  }
+
+  bool HasAnimationEnded() const {
+    return GetState() == SkiaVectorAnimation::PlayState::kEnded;
+  }
+
+  bool IsPaused() const {
+    return GetState() == SkiaVectorAnimation::PlayState::kPaused;
+  }
+
+  const SkiaVectorAnimation::TimerControl* GetTimerControl() const {
+    return animation_->timer_control_.get();
+  }
+
+  const base::TickClock* test_clock() const { return &test_clock_; }
+
+  void AdvanceClock(int64_t ms) {
+    test_clock_.Advance(base::TimeDelta::FromMilliseconds(ms));
+  }
+
+  base::TimeDelta TimeDeltaSince(const base::TimeTicks& ticks) const {
+    return test_clock_.NowTicks() - ticks;
+  }
+
+  const base::TimeTicks NowTicks() const { return test_clock_.NowTicks(); }
+
+  double GetTimerStartOffset() const {
+    return animation_->timer_control_->GetNormalizedStartOffset();
+  }
+
+  double GetTimerEndOffset() const {
+    return animation_->timer_control_->GetNormalizedEndOffset();
+  }
+
+  const base::TimeTicks& GetTimerPreviousTick() const {
+    return animation_->timer_control_->previous_tick_;
+  }
+
+  double GetTimerProgressPerMs() const {
+    return animation_->timer_control_->progress_per_millisecond_;
+  }
+
+  int GetTimerCycles() const {
+    return animation_->timer_control_->completed_cycles();
+  }
+
+  void IsAllSameColor(SkColor color, const SkBitmap& bitmap) const {
+    if (bitmap.colorType() == kBGRA_8888_SkColorType) {
+      const SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
+      const int num_pixels = bitmap.width() * bitmap.height();
+      for (int i = 0; i < num_pixels; i++)
+        EXPECT_EQ(pixels[i], color);
+    } else {
+      for (int x = 0; x < bitmap.width(); x++)
+        for (int y = 0; y < bitmap.height(); y++)
+          EXPECT_EQ(bitmap.getColor(x, y), color);
+    }
+  }
+
+ protected:
+  std::unique_ptr<SkiaVectorAnimation> animation_;
+
+ private:
+  std::unique_ptr<gfx::Canvas> canvas_;
+  base::SimpleTestTickClock test_clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(SkiaVectorAnimationTest);
+};
+
+TEST_F(SkiaVectorAnimationTest, InitializationAndLoadingData) {
+  auto bytes = base::MakeRefCounted<base::RefCountedBytes>(
+      std::vector<unsigned char>(kData, kData + std::strlen(kData)));
+  animation_ = std::make_unique<SkiaVectorAnimation>(bytes.get());
+  EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
+  EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
+  EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
+                  kAnimationDuration);
+  EXPECT_TRUE(IsStopped());
+
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+  EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth);
+  EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight);
+  EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(),
+                  kAnimationDuration);
+  EXPECT_TRUE(IsStopped());
+}
+
+TEST_F(SkiaVectorAnimationTest, PlayLinearAnimation) {
+  TestAnimationObserver observer;
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+  animation_->SetAnimationObserver(&observer);
+
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+
+  EXPECT_TRUE(IsStopped());
+  animation_->Start(SkiaVectorAnimation::Style::kLinear);
+  EXPECT_TRUE(IsScheduledToPlay());
+  EXPECT_FALSE(observer.animation_will_start_playing());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FALSE(IsScheduledToPlay());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_will_start_playing());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
+  EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0);
+  EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f);
+
+  EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f);
+
+  AdvanceClock(50);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+
+  // Advance the clock to the end of the animation.
+  AdvanceClock(4951);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4951);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 1.f);
+  EXPECT_TRUE(HasAnimationEnded());
+  EXPECT_TRUE(observer.animation_cycle_ended());
+}
+
+TEST_F(SkiaVectorAnimationTest, StopLinearAnimation) {
+  TestAnimationObserver observer;
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+  animation_->SetAnimationObserver(&observer);
+
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+
+  animation_->Start(SkiaVectorAnimation::Style::kLinear);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
+
+  AdvanceClock(50);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
+
+  animation_->Stop();
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0.f);
+  EXPECT_TRUE(IsStopped());
+}
+
+TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLinearAnimation) {
+  const int start_time_ms = 400;
+  const int duration_ms = 1000;
+  const float total_duration_ms = kAnimationDuration * 1000.f;
+
+  TestAnimationObserver observer;
+
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+  animation_->SetAnimationObserver(&observer);
+
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+
+  EXPECT_FALSE(observer.animation_cycle_ended());
+  animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
+                              base::TimeDelta::FromMilliseconds(duration_ms),
+                              SkiaVectorAnimation::Style::kLinear);
+
+  EXPECT_TRUE(IsScheduledToPlay());
+  EXPECT_FALSE(observer.animation_will_start_playing());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FALSE(IsScheduledToPlay());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_will_start_playing());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
+  EXPECT_FLOAT_EQ(GetTimerEndOffset(),
+                  (start_time_ms + duration_ms) / total_duration_ms);
+
+  EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms);
+
+  AdvanceClock(100);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (100.f + start_time_ms) / total_duration_ms);
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  // Advance clock another 300 ms.
+  AdvanceClock(300);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (100.f + 300.f + start_time_ms) / total_duration_ms);
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  // Reach the end of animation.
+  AdvanceClock(601);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 601);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset());
+  EXPECT_TRUE(observer.animation_cycle_ended());
+  EXPECT_TRUE(HasAnimationEnded());
+}
+
+TEST_F(SkiaVectorAnimationTest, PausingLinearAnimation) {
+  const int start_time_ms = 400;
+  const int duration_ms = 1000;
+  const float total_duration_ms = kAnimationDuration * 1000.f;
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+  TestAnimationObserver observer;
+  animation_->SetAnimationObserver(&observer);
+
+  AdvanceClock(200);
+
+  animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
+                              base::TimeDelta::FromMilliseconds(duration_ms),
+                              SkiaVectorAnimation::Style::kLinear);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Pause();
+  EXPECT_TRUE(IsPaused());
+
+  // Advancing clock and stepping animation should have no effect when animation
+  // is paused.
+  AdvanceClock(5000);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+
+  // Resume playing the animation.
+  animation_->ResumePlaying();
+  EXPECT_TRUE(IsScheduledToResume());
+
+  // There should be no progress, since we haven't advanced the clock yet.
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 600.f / total_duration_ms);
+
+  AdvanceClock(801);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+}
+
+TEST_F(SkiaVectorAnimationTest, PlayLoopAnimation) {
+  TestAnimationObserver observer;
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+  animation_->SetAnimationObserver(&observer);
+
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+
+  EXPECT_TRUE(IsStopped());
+  animation_->Start(SkiaVectorAnimation::Style::kLoop);
+  EXPECT_TRUE(IsScheduledToPlay());
+  EXPECT_FALSE(observer.animation_will_start_playing());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FALSE(IsScheduledToPlay());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_will_start_playing());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
+  EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0);
+  EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f);
+
+  EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f);
+
+  AdvanceClock(50);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+
+  // Advance the clock to the end of the animation.
+  AdvanceClock(4950);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4950);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_EQ(GetTimerCycles(), 1);
+  EXPECT_TRUE(std::abs(animation_->GetCurrentProgress() - 0.f) < 0.0001f);
+  EXPECT_TRUE(observer.animation_cycle_ended());
+  EXPECT_TRUE(IsPlaying());
+}
+
+TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLoopAnimation) {
+  const int start_time_ms = 400;
+  const int duration_ms = 1000;
+  const float total_duration_ms = kAnimationDuration * 1000.f;
+
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+
+  TestAnimationObserver observer;
+  animation_->SetAnimationObserver(&observer);
+
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+  EXPECT_TRUE(IsStopped());
+  animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
+                              base::TimeDelta::FromMilliseconds(duration_ms),
+                              SkiaVectorAnimation::Style::kLoop);
+  EXPECT_TRUE(IsScheduledToPlay());
+  EXPECT_FALSE(observer.animation_will_start_playing());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FALSE(IsScheduledToPlay());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_will_start_playing());
+
+  EXPECT_FALSE(observer.animation_cycle_ended());
+  EXPECT_FLOAT_EQ(GetTimerStartOffset(), 400.f / total_duration_ms);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
+  EXPECT_FLOAT_EQ(GetTimerEndOffset(),
+                  (start_time_ms + duration_ms) / total_duration_ms);
+
+  EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms);
+
+  AdvanceClock(100);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100);
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (100.f + start_time_ms) / total_duration_ms);
+
+  // Advance clock another 300 ms.
+  AdvanceClock(300);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (100.f + 300.f + start_time_ms) / total_duration_ms);
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  // Reach the end of animation.
+  AdvanceClock(600);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 600);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(observer.animation_cycle_ended());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
+}
+
+TEST_F(SkiaVectorAnimationTest, PausingLoopAnimation) {
+  const int start_time_ms = 400;
+  const int duration_ms = 1000;
+  const float total_duration_ms = kAnimationDuration * 1000.f;
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+
+  TestAnimationObserver observer;
+  animation_->SetAnimationObserver(&observer);
+
+  AdvanceClock(200);
+
+  animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
+                              base::TimeDelta::FromMilliseconds(duration_ms),
+                              SkiaVectorAnimation::Style::kLoop);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 400.f / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Pause();
+  EXPECT_TRUE(IsPaused());
+
+  // Advancing clock and stepping animation should have no effect when animation
+  // is paused.
+  AdvanceClock(5000);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+
+  // Resume playing the animation.
+  animation_->ResumePlaying();
+  EXPECT_TRUE(IsScheduledToResume());
+
+  // There should be no progress, since we haven't advanced the clock yet.
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 600.f / total_duration_ms);
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  AdvanceClock(800);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_cycle_ended());
+}
+
+TEST_F(SkiaVectorAnimationTest, PlayThrobbingAnimation) {
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+
+  TestAnimationObserver observer;
+  animation_->SetAnimationObserver(&observer);
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+
+  animation_->Start(SkiaVectorAnimation::Style::kThrobbing);
+  EXPECT_TRUE(IsScheduledToPlay());
+  EXPECT_FALSE(observer.animation_will_start_playing());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FALSE(IsScheduledToPlay());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_will_start_playing());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
+  EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0);
+  EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f);
+
+  EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f);
+
+  AdvanceClock(50);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+
+  // Advance the clock to the end of the animation.
+  AdvanceClock(4950);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4950);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 1.f);
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  AdvanceClock(2500);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0.5f);
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  AdvanceClock(2500);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0);
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_cycle_ended());
+}
+
+TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfThrobbingAnimation) {
+  const int start_time_ms = 400;
+  const int duration_ms = 1000;
+  const float total_duration_ms = kAnimationDuration * 1000.f;
+
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+
+  TestAnimationObserver observer;
+  animation_->SetAnimationObserver(&observer);
+
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+
+  animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
+                              base::TimeDelta::FromMilliseconds(duration_ms),
+                              SkiaVectorAnimation::Style::kThrobbing);
+  EXPECT_TRUE(IsScheduledToPlay());
+  EXPECT_FALSE(observer.animation_will_start_playing());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FALSE(IsScheduledToPlay());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_will_start_playing());
+
+  EXPECT_FLOAT_EQ(GetTimerStartOffset(), 400.f / total_duration_ms);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
+  EXPECT_FLOAT_EQ(GetTimerEndOffset(),
+                  (start_time_ms + duration_ms) / total_duration_ms);
+
+  EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms);
+
+  AdvanceClock(100);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FALSE(observer.animation_cycle_ended());
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (100.f + start_time_ms) / total_duration_ms);
+
+  // Advance clock another 300 ms.
+  AdvanceClock(300);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FALSE(observer.animation_cycle_ended());
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0);
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (100.f + 300.f + start_time_ms) / total_duration_ms);
+
+  // Reach the end of animation.
+  AdvanceClock(600);
+  EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 600);
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset());
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  AdvanceClock(500);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 900.f / total_duration_ms);
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FALSE(observer.animation_cycle_ended());
+
+  AdvanceClock(500);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_TRUE(observer.animation_cycle_ended());
+
+  observer.Reset();
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 100.f) / total_duration_ms);
+  EXPECT_TRUE(IsPlaying());
+}
+
+TEST_F(SkiaVectorAnimationTest, PausingThrobbingAnimation) {
+  const int start_time_ms = 400;
+  const int duration_ms = 1000;
+  const float total_duration_ms = kAnimationDuration * 1000.f;
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+
+  AdvanceClock(200);
+
+  animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms),
+                              base::TimeDelta::FromMilliseconds(duration_ms),
+                              SkiaVectorAnimation::Style::kThrobbing);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_TRUE(IsPlaying());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  start_time_ms / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 100.f) / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Pause();
+  EXPECT_TRUE(IsPaused());
+
+  // Advancing clock and stepping animation should have no effect when animation
+  // is paused.
+  AdvanceClock(5000);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 100.f) / total_duration_ms);
+
+  // Resume playing the animation.
+  animation_->ResumePlaying();
+  EXPECT_TRUE(IsScheduledToResume());
+
+  // There should be no progress, since we haven't advanced the clock yet.
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(IsPlaying());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 100.f) / total_duration_ms);
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 200.f) / total_duration_ms);
+
+  AdvanceClock(800);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset());
+  EXPECT_TRUE(IsPlaying());
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 900.f) / total_duration_ms);
+  EXPECT_TRUE(IsPlaying());
+
+  animation_->Pause();
+  EXPECT_TRUE(IsPaused());
+
+  AdvanceClock(10000);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 900.f) / total_duration_ms);
+
+  // Resume playing the animation.
+  animation_->ResumePlaying();
+  EXPECT_TRUE(IsScheduledToResume());
+
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(IsPlaying());
+
+  AdvanceClock(500);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 400.f) / total_duration_ms);
+
+  AdvanceClock(400);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset());
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(),
+                  (start_time_ms + 100.f) / total_duration_ms);
+  EXPECT_TRUE(IsPlaying());
+}
+
+TEST_F(SkiaVectorAnimationTest, PauseBeforePlay) {
+  const float total_duration_ms = kAnimationDuration * 1000.f;
+
+  // Test to see if the race condition is handled correctly. It may happen that
+  // we pause the video before it even starts playing.
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+
+  TestAnimationObserver observer;
+  animation_->SetAnimationObserver(&observer);
+
+  AdvanceClock(300);
+
+  animation_->Start();
+  EXPECT_TRUE(IsScheduledToPlay());
+
+  animation_->Pause();
+  EXPECT_TRUE(IsPaused());
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+
+  animation_->ResumePlaying();
+  EXPECT_TRUE(IsScheduledToResume());
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_TRUE(IsPlaying());
+
+  AdvanceClock(100);
+  animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize());
+  EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 100.f / total_duration_ms);
+}
+
+TEST_F(SkiaVectorAnimationTest, PaintTest) {
+  std::unique_ptr<gfx::Canvas> canvas(new gfx::Canvas(
+      gfx::Size(kAnimationWidth, kAnimationHeight), 1.f, false));
+  animation_ = std::make_unique<SkiaVectorAnimation>(
+      std::make_unique<SkMemoryStream>(kData, std::strlen(kData)));
+
+  // Advance clock by 300 milliseconds.
+  AdvanceClock(300);
+
+  animation_->Start(SkiaVectorAnimation::Style::kLinear);
+  animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
+
+  AdvanceClock(50);
+  animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
+  SkBitmap bitmap = canvas->GetBitmap();
+  IsAllSameColor(SK_ColorGREEN, bitmap);
+
+  AdvanceClock(2450);
+  animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
+  bitmap = canvas->GetBitmap();
+  IsAllSameColor(SK_ColorGREEN, bitmap);
+
+  AdvanceClock(50);
+  animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
+  bitmap = canvas->GetBitmap();
+  IsAllSameColor(SK_ColorBLUE, bitmap);
+
+  AdvanceClock(1000);
+  animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
+  bitmap = canvas->GetBitmap();
+  IsAllSameColor(SK_ColorBLUE, bitmap);
+
+  AdvanceClock(1400);
+  animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize());
+  bitmap = canvas->GetBitmap();
+  IsAllSameColor(SK_ColorBLUE, bitmap);
+}
+
+}  // namespace gfx
diff --git a/ui/views/cocoa/bridge_factory_host.cc b/ui/views/cocoa/bridge_factory_host.cc
index 4fafac5..a660a20b 100644
--- a/ui/views/cocoa/bridge_factory_host.cc
+++ b/ui/views/cocoa/bridge_factory_host.cc
@@ -4,10 +4,18 @@
 
 #include "ui/views/cocoa/bridge_factory_host.h"
 
+#include "mojo/public/cpp/bindings/interface_request.h"
+
 namespace views {
 
+namespace {
+// Start the ids at something far from zero to help in debugging.
+uint64_t g_next_bridge_factory_host_id_ = 0x1000;
+}  // namespace
+
 BridgeFactoryHost::BridgeFactoryHost(
-    views_bridge_mac::mojom::BridgeFactoryRequest* request) {
+    views_bridge_mac::mojom::BridgeFactoryRequest* request)
+    : host_id_(g_next_bridge_factory_host_id_++) {
   *request = mojo::MakeRequest(&bridge_factory_ptr_);
 }
 
diff --git a/ui/views/cocoa/bridge_factory_host.h b/ui/views/cocoa/bridge_factory_host.h
index c5c68fe..e8027a4 100644
--- a/ui/views/cocoa/bridge_factory_host.h
+++ b/ui/views/cocoa/bridge_factory_host.h
@@ -24,11 +24,18 @@
 
   BridgeFactoryHost(views_bridge_mac::mojom::BridgeFactoryRequest* request);
   ~BridgeFactoryHost();
+
+  // Return an id for the host process. This can be used to look up other
+  // factories to create NSViews (e.g in content).
+  uint64_t GetHostId() const { return host_id_; }
+
   views_bridge_mac::mojom::BridgeFactory* GetFactory();
+
   void AddObserver(Observer* observer);
   void RemoveObserver(const Observer* observer);
 
  private:
+  const uint64_t host_id_;
   views_bridge_mac::mojom::BridgeFactoryPtr bridge_factory_ptr_;
   base::ObserverList<Observer> observers_;
 };
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
index f9968ae6..bf8f4c1 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html
@@ -25,7 +25,11 @@
         margin: auto;
         max-height: 640px;
         max-width: 768px;
-        padding: 60px 64px 32px 64px;
+        padding: 60px 32px 32px 32px;
+      }
+
+      iron-pages {
+        padding: 0 32px;
       }
     </style>
     <iron-pages attr-for-selected="is"
diff --git a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
index d9a187b6..7a3325d24 100644
--- a/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
+++ b/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html
@@ -13,6 +13,7 @@
       #selector-and-details-container {
         @apply --layout-horizontal;
         margin-top: 48px;
+        min-height: 246px;
       }
 
       #singleDeviceName {
@@ -42,6 +43,8 @@
       }
 
       #feature-details-container {
+        @apply --layout-vertical;
+        @apply --layout-center-justified;
         border-left: 1px solid rgb(218, 220, 224);
         padding-left: 24px;
       }
@@ -53,22 +56,24 @@
       .feature-detail {
         @apply --layout-horizontal;
         @apply --layout-center;
-        padding-bottom: 28px;
-        padding-top: 21px;
+        box-sizing: border-box;
+        min-height: 64px;
+        padding: 10px 0;
       }
 
       .feature-detail iron-icon {
         --iron-icon-height: 20px;
         --iron-icon-width: 20px;
-        position: fixed;
+        min-width: 20px;
       }
 
       .feature-detail span {
-        padding-left: 28px;
+        margin-left: 8px;
       }
 
       #footnote {
         color: var(--paper-grey-600);
+        margin-top: 12px;
       }
     </style>