diff --git a/AUTHORS b/AUTHORS
index 6714e69..bf850cc 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -366,6 +366,7 @@
 Ilia Demianenko <ilia.demianenko@gmail.com>
 Ilia K <ki.stfu@gmail.com>
 Ilya Konstantinov <ilya.konstantinov@gmail.com>
+Imam Mohammad Bokhary <imam.bokhary@samsung.com>
 Imranur Rahman <i.rahman@samsung.com>
 Imranur Rahman <ir.shimul@gmail.com>
 Ion Rosca <rosca@adobe.com>
diff --git a/DEPS b/DEPS
index a7dc181..08794874 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'f7e9d17791e406285df83449b95cd498d1a9cd1f',
+  'skia_revision': '3787f51c65c33eda4a5f81e16b5e7e2d01f93943',
   # 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': '377dc2acc20702d6b96dc7a9e32a8b7164448670',
+  'v8_revision': 'd033cf119f93f2fe742ea7970d3394a060cffb3c',
   # 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.
@@ -179,11 +179,11 @@
   # 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': '7f506bde16f165f923e9fd7e80c4f8b6314618fd',
+  'angle_revision': 'a0159c03485b0ac91784d12d544927d83453d186',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '6b4b8141e11d7e1a397aa04f39c0c7eb20428104',
+  'swiftshader_revision': 'faa2a365b5c0e90a5e1156c01a3acb4246632316',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -286,7 +286,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '510ca9d616f5682a225b894a64b2af8fa5997d48',
+  'spv_tools_revision': '10951a7c9a13ec663bc1dfdaec268840be865e9a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -302,11 +302,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '64f4dd71278a76e1cffb4578e6441a09fd231283',
+  'dawn_revision': '86ac0b93c9afc7682c9e31f573dd387dc601dfbf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': 'e3f2f7bd0dddec518b59fed97a3a6d48cbbd1f59',
+  'quiche_revision': 'f0fe2069005c907660534fc7d3ee9694c79799d6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -862,7 +862,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3004764570f3c3bc0669ffaf194ab828d6a0153d',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'a2aeb375f63ca0734f3eb63d9256b50221c1d920',
       'condition': 'checkout_linux',
   },
 
@@ -1269,7 +1269,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '5d1b15f34c87999b97d5b1d768e14b59b75934d4',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '7505235b577c4f720c439ae33f409d0563e13360',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1459,7 +1459,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c4e67ff117d6c640e6dd17989afe2fb7da7eecb',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1903a35e00e89555180b8442c42d3bdbd91565ee',
+    Var('webrtc_git') + '/src.git' + '@' + 'ba2ba59c4ba1009ad04e56bb3804c882bc63441b',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1521,7 +1521,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e1fd70911515747eeeb1e32f37ccf2f16a0b4a0a',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b2ed47b33935fb53bf38bf1af020f9dfbf7666be',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index dbdbc2f..1ebd144 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -560,24 +560,9 @@
   if (!ShouldUpdateDragIndicatorsOrSnap(location_in_screen))
     return;
 
-  IndicatorState indicator_state;
-  if (CanSnapInSplitview(item_->GetWindow())) {
-    snap_position_ = GetSnapPosition(location_in_screen);
-    switch (snap_position_) {
-      case SplitViewController::NONE:
-        indicator_state = IndicatorState::kDragArea;
-        break;
-      case SplitViewController::LEFT:
-        indicator_state = IndicatorState::kPreviewAreaLeft;
-        break;
-      case SplitViewController::RIGHT:
-        indicator_state = IndicatorState::kPreviewAreaRight;
-        break;
-    }
-  } else {
-    snap_position_ = SplitViewController::NONE;
-    indicator_state = IndicatorState::kCannotSnap;
-  }
+  snap_position_ = GetSnapPosition(location_in_screen);
+  IndicatorState indicator_state =
+      GetIndicatorState(item_->GetWindow(), snap_position_);
   item_->overview_grid()->RearrangeDuringDrag(item_->GetWindow(),
                                               indicator_state);
   overview_session_->SetSplitViewDragIndicatorsIndicatorState(
@@ -676,38 +661,9 @@
       return default_snap_position;
   }
 
-  if (is_landscape) {
-    // The window can be snapped if it reaches close enough to the screen
-    // edge of the screen (on primary axis). The edge insets are a fixed ratio
-    // of the screen plus some padding. This matches the drag indicators ui.
-    const int screen_edge_inset_for_drag =
-        area.width() * kHighlightScreenPrimaryAxisRatio +
-        kHighlightScreenEdgePaddingDp;
-    area.Inset(screen_edge_inset_for_drag, 0);
-    if (location_in_screen.x() <= area.x()) {
-      return is_primary ? SplitViewController::LEFT
-                        : SplitViewController::RIGHT;
-    }
-    if (location_in_screen.x() >= area.right() - 1) {
-      return is_primary ? SplitViewController::RIGHT
-                        : SplitViewController::LEFT;
-    }
-    return SplitViewController::NONE;
-  } else {
-    const int screen_edge_inset_for_drag =
-        area.height() * kHighlightScreenPrimaryAxisRatio +
-        kHighlightScreenEdgePaddingDp;
-    area.Inset(0, screen_edge_inset_for_drag);
-    if (location_in_screen.y() <= area.y()) {
-      return is_primary ? SplitViewController::LEFT
-                        : SplitViewController::RIGHT;
-    }
-    if (location_in_screen.y() >= area.bottom() - 1) {
-      return is_primary ? SplitViewController::RIGHT
-                        : SplitViewController::LEFT;
-    }
-    return SplitViewController::NONE;
-  }
+  return ::ash::GetSnapPosition(
+      item_->GetWindow(),
+      gfx::Point(location_in_screen.x(), location_in_screen.y()), area);
 }
 
 void OverviewWindowDragController::SnapWindow(
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index 6faaeb75..f8341fe 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -355,4 +355,64 @@
                           : SplitViewController::RIGHT);
 }
 
+SplitViewController::SnapPosition GetSnapPosition(
+    aura::Window* window,
+    const gfx::Point& location_in_screen,
+    const gfx::Rect& work_area) {
+  if (!ShouldAllowSplitView() || !CanSnapInSplitview(window))
+    return SplitViewController::NONE;
+
+  const bool is_landscape = IsCurrentScreenOrientationLandscape();
+  const bool is_primary = IsCurrentScreenOrientationPrimary();
+
+  // Check to see if the current event location |location_in_screen|is within
+  // the drag indicators bounds.
+  gfx::Rect area(work_area);
+  if (is_landscape) {
+    const int screen_edge_inset_for_drag =
+        area.width() * kHighlightScreenPrimaryAxisRatio +
+        kHighlightScreenEdgePaddingDp;
+    area.Inset(screen_edge_inset_for_drag, 0);
+    if (location_in_screen.x() <= area.x()) {
+      return is_primary ? SplitViewController::LEFT
+                        : SplitViewController::RIGHT;
+    }
+    if (location_in_screen.x() >= area.right() - 1) {
+      return is_primary ? SplitViewController::RIGHT
+                        : SplitViewController::LEFT;
+    }
+    return SplitViewController::NONE;
+  }
+
+  const int screen_edge_inset_for_drag =
+      area.height() * kHighlightScreenPrimaryAxisRatio +
+      kHighlightScreenEdgePaddingDp;
+  area.Inset(0, screen_edge_inset_for_drag);
+  if (location_in_screen.y() <= area.y())
+    return is_primary ? SplitViewController::LEFT : SplitViewController::RIGHT;
+  if (location_in_screen.y() >= area.bottom() - 1)
+    return is_primary ? SplitViewController::RIGHT : SplitViewController::LEFT;
+  return SplitViewController::NONE;
+}
+
+IndicatorState GetIndicatorState(
+    aura::Window* window,
+    SplitViewController::SnapPosition snap_position) {
+  if (!ShouldAllowSplitView())
+    return IndicatorState::kNone;
+
+  switch (snap_position) {
+    case SplitViewController::LEFT:
+      return IndicatorState::kPreviewAreaLeft;
+    case SplitViewController::RIGHT:
+      return IndicatorState::kPreviewAreaRight;
+    case SplitViewController::NONE:
+      return CanSnapInSplitview(window) ? IndicatorState::kDragArea
+                                        : IndicatorState::kCannotSnap;
+  }
+
+  NOTREACHED();
+  return IndicatorState::kNone;
+}
+
 }  // namespace ash
diff --git a/ash/wm/splitview/split_view_utils.h b/ash/wm/splitview/split_view_utils.h
index 0cd0552..f0cc61c9 100644
--- a/ash/wm/splitview/split_view_utils.h
+++ b/ash/wm/splitview/split_view_utils.h
@@ -8,6 +8,7 @@
 #include "ash/ash_export.h"
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/wm/splitview/split_view_controller.h"
+#include "ash/wm/splitview/split_view_drag_indicators.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/transform.h"
 
@@ -105,6 +106,19 @@
 
 ASH_EXPORT bool IsPhysicalLeftOrTop(SplitViewController::SnapPosition position);
 
+// Returns the desired snap position based on |location_in_screen|. The window
+// needs to be dragged into the drag indicator area on the edge of the screen
+// to be able to get snapped.
+ASH_EXPORT SplitViewController::SnapPosition GetSnapPosition(
+    aura::Window* window,
+    const gfx::Point& location_in_screen,
+    const gfx::Rect& work_area);
+
+// Returns the desried indicator state based on the desired |snap_position|.
+ASH_EXPORT IndicatorState
+GetIndicatorState(aura::Window* window,
+                  SplitViewController::SnapPosition snap_position);
+
 }  // namespace ash
 
 #endif  // ASH_WM_SPLITVIEW_SPLIT_VIEW_UTILS_H_
diff --git a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
index 3cb1c429..6c28bc0 100644
--- a/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
+++ b/ash/wm/tablet_mode/tablet_mode_window_drag_delegate.cc
@@ -316,31 +316,24 @@
 
 IndicatorState TabletModeWindowDragDelegate::GetIndicatorState(
     const gfx::Point& location_in_screen) const {
-  // Do not show the drag indicators if split view is disabled globally.
-  if (!ShouldAllowSplitView())
-    return IndicatorState::kNone;
-
   // Do not show the drag indicators if the window hasn't been considered as
   // moved.
   if (!is_window_considered_moved_)
     return IndicatorState::kNone;
 
-  SplitViewController::SnapPosition snap_position =
-      GetSnapPosition(location_in_screen);
-  if (snap_position != SplitViewController::NONE) {
-    return snap_position == SplitViewController::LEFT
-               ? IndicatorState::kPreviewAreaLeft
-               : IndicatorState::kPreviewAreaRight;
+  IndicatorState indicator_state = ::ash::GetIndicatorState(
+      dragged_window_, GetSnapPosition(location_in_screen));
+
+  // In portrait screen orientation no top drag indicator since we're dragging
+  // from top.
+  if (!IsCurrentScreenOrientationLandscape()) {
+    if (indicator_state == IndicatorState::kDragArea)
+      indicator_state = IndicatorState::kDragAreaRight;
+    else if (indicator_state == IndicatorState::kCannotSnap)
+      indicator_state = IndicatorState::kCannotSnapRight;
   }
 
-  const bool can_snap = CanSnapInSplitview(dragged_window_);
-
-  // No top drag indicator if in portrait screen orientation.
-  if (IsCurrentScreenOrientationLandscape())
-    return can_snap ? IndicatorState::kDragArea : IndicatorState::kCannotSnap;
-
-  return can_snap ? IndicatorState::kDragAreaRight
-                  : IndicatorState::kCannotSnapRight;
+  return indicator_state;
 }
 
 bool TabletModeWindowDragDelegate::ShouldOpenOverviewWhenDragStarts() {
@@ -362,39 +355,24 @@
   if (!(is_window_considered_moved_ && CanSnapInSplitview(dragged_window_)))
     return SplitViewController::NONE;
 
-  const bool is_landscape = IsCurrentScreenOrientationLandscape();
-  const bool is_primary = IsCurrentScreenOrientationPrimary();
-  gfx::Rect work_area_bounds = display::Screen::GetScreen()
-                                   ->GetDisplayNearestWindow(dragged_window_)
-                                   .work_area();
-  // Check to see if the current event location |location_in_screen|is within
-  // the drag indicators bounds.
-  if (is_landscape) {
-    const int screen_edge_inset =
-        work_area_bounds.width() * kHighlightScreenPrimaryAxisRatio +
-        kHighlightScreenEdgePaddingDp;
-    work_area_bounds.Inset(screen_edge_inset, 0);
-    if (location_in_screen.x() < work_area_bounds.x()) {
-      return is_primary ? SplitViewController::LEFT
-                        : SplitViewController::RIGHT;
-    }
-    if (location_in_screen.x() >= work_area_bounds.right()) {
-      return is_primary ? SplitViewController::RIGHT
-                        : SplitViewController::LEFT;
-    }
-    return SplitViewController::NONE;
-  }
+  SplitViewController::SnapPosition snap_position =
+      ::ash::GetSnapPosition(dragged_window_, location_in_screen,
+                             display::Screen::GetScreen()
+                                 ->GetDisplayNearestWindow(dragged_window_)
+                                 .work_area());
+
   // For portrait mode, since the drag always starts from the top of the
   // screen, we only allow the window to be dragged to snap to the bottom of
   // the screen.
-  const int screen_edge_inset =
-      work_area_bounds.height() * kHighlightScreenPrimaryAxisRatio +
-      kHighlightScreenEdgePaddingDp;
-  work_area_bounds.Inset(0, screen_edge_inset);
-  if (location_in_screen.y() >= work_area_bounds.bottom())
-    return is_primary ? SplitViewController::RIGHT : SplitViewController::LEFT;
+  const bool is_landscape = IsCurrentScreenOrientationLandscape();
+  const bool is_primary = IsCurrentScreenOrientationPrimary();
+  if (!is_landscape &&
+      ((is_primary && snap_position == SplitViewController::LEFT) ||
+       (!is_primary && snap_position == SplitViewController::RIGHT))) {
+    snap_position = SplitViewController::NONE;
+  }
 
-  return SplitViewController::NONE;
+  return snap_position;
 }
 
 void TabletModeWindowDragDelegate::UpdateDraggedWindowTransform(
diff --git a/base/allocator/debugallocation_shim.cc b/base/allocator/debugallocation_shim.cc
index 479cfca..24addf9 100644
--- a/base/allocator/debugallocation_shim.cc
+++ b/base/allocator/debugallocation_shim.cc
@@ -7,9 +7,10 @@
 // AFDO can mess with them. Better not to use AFDO there.  This is a
 // temporary hack. We will add a mechanism in the build system to
 // avoid using -fauto-profile for tcmalloc files.
-#if !defined(__clang__) && (defined(OS_CHROMEOS) || __GNUC__ > 5)
+#if !defined(__clang__) && \
+    (defined(OS_CHROMEOS) || (__GNUC__ > 5 && __GNUC__ < 7))
 // Note that this option only seems to be available in the chromeos GCC 4.9
-// toolchain, and stock GCC 5 and up.
+// toolchain, and stock GCC 5 upto 7.
 #pragma GCC optimize ("no-auto-profile")
 #endif
 
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 1dac8be..305a667 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -147,6 +147,8 @@
     "layers/video_layer_impl.h",
     "layers/viewport.cc",
     "layers/viewport.h",
+    "metrics/begin_main_frame_metrics.cc",
+    "metrics/begin_main_frame_metrics.h",
     "metrics/compositor_frame_reporter.cc",
     "metrics/compositor_frame_reporter.h",
     "metrics/compositor_frame_reporting_controller.cc",
diff --git a/cc/metrics/begin_main_frame_metrics.cc b/cc/metrics/begin_main_frame_metrics.cc
new file mode 100644
index 0000000..d4e83ead
--- /dev/null
+++ b/cc/metrics/begin_main_frame_metrics.cc
@@ -0,0 +1,11 @@
+// Copyright 2019 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 "cc/metrics/begin_main_frame_metrics.h"
+
+namespace cc {
+
+BeginMainFrameMetrics::BeginMainFrameMetrics() = default;
+
+}  // namespace cc
diff --git a/cc/metrics/begin_main_frame_metrics.h b/cc/metrics/begin_main_frame_metrics.h
new file mode 100644
index 0000000..2927360
--- /dev/null
+++ b/cc/metrics/begin_main_frame_metrics.h
@@ -0,0 +1,35 @@
+// Copyright 2019 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 CC_METRICS_BEGIN_MAIN_FRAME_METRICS_H_
+#define CC_METRICS_BEGIN_MAIN_FRAME_METRICS_H_
+
+#include "base/time/time.h"
+#include "cc/cc_export.h"
+
+namespace cc {
+
+// Latency timing data for Main Frame lifecycle updates triggered by cc.
+// The data is captured in LocalFrameViewUKMAggregator and passed back through
+// the proxy when a main frame ends. LayerTreeHost updates the update_layers_
+// value in LayerTreeHost::UpdateLayers.
+// TODO(schenney): Include work done in LayerTreeHost::AnimateLayers?
+struct CC_EXPORT BeginMainFrameMetrics {
+  base::TimeDelta handle_input_events;
+  base::TimeDelta animate;
+  base::TimeDelta style_update;
+  base::TimeDelta layout_update;
+  base::TimeDelta prepaint;
+  base::TimeDelta composite;
+  base::TimeDelta paint;
+  base::TimeDelta scrolling_coordinator;
+  base::TimeDelta composite_commit;
+  base::TimeDelta update_layers;
+
+  BeginMainFrameMetrics();
+};
+
+}  // namespace cc
+
+#endif  // CC_METRICS_BEGIN_MAIN_FRAME_METRICS_H_
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index ebc3206d..c5fb67f 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -26,6 +26,7 @@
 #include "cc/input/input_handler.h"
 #include "cc/layers/layer.h"
 #include "cc/layers/layer_impl.h"
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/metrics/compositor_timing_history.h"
 #include "cc/test/animation_test_common.h"
 #include "cc/test/fake_compositor_frame_reporting_controller.h"
@@ -401,6 +402,9 @@
 
   void RecordStartOfFrameMetrics() override {}
   void RecordEndOfFrameMetrics(base::TimeTicks) override {}
+  std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() override {
+    return nullptr;
+  }
 
   void UpdateLayerTreeHost() override { test_hooks_->UpdateLayerTreeHost(); }
 
diff --git a/cc/test/stub_layer_tree_host_client.cc b/cc/test/stub_layer_tree_host_client.cc
index 650faaf..f7bd15e 100644
--- a/cc/test/stub_layer_tree_host_client.cc
+++ b/cc/test/stub_layer_tree_host_client.cc
@@ -4,8 +4,15 @@
 
 #include "cc/test/stub_layer_tree_host_client.h"
 
+#include "cc/metrics/begin_main_frame_metrics.h"
+
 namespace cc {
 
 StubLayerTreeHostClient::~StubLayerTreeHostClient() = default;
 
+std::unique_ptr<BeginMainFrameMetrics>
+StubLayerTreeHostClient::GetBeginMainFrameMetrics() {
+  return nullptr;
+}
+
 }  // namespace cc
diff --git a/cc/test/stub_layer_tree_host_client.h b/cc/test/stub_layer_tree_host_client.h
index 3eabe45..1445f542 100644
--- a/cc/test/stub_layer_tree_host_client.h
+++ b/cc/test/stub_layer_tree_host_client.h
@@ -22,9 +22,9 @@
   void BeginMainFrame(const viz::BeginFrameArgs& args) override {}
   void OnDeferMainFrameUpdatesChanged(bool) override {}
   void OnDeferCommitsChanged(bool) override {}
-
   void RecordStartOfFrameMetrics() override {}
   void RecordEndOfFrameMetrics(base::TimeTicks) override {}
+  std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() override;
   void BeginMainFrameNotExpectedSoon() override {}
   void BeginMainFrameNotExpectedUntil(base::TimeTicks time) override {}
   void UpdateLayerTreeHost() override {}
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 47e6061..4020cb88 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -291,8 +291,12 @@
   return debug_state_;
 }
 
-void LayerTreeHost::RequestMainFrameUpdate() {
+void LayerTreeHost::RequestMainFrameUpdate(bool report_cc_metrics) {
   client_->UpdateLayerTreeHost();
+  if (report_cc_metrics)
+    begin_main_frame_metrics_ = client_->GetBeginMainFrameMetrics();
+  else
+    begin_main_frame_metrics_.reset();
 }
 
 // This function commits the LayerTreeHost to an impl tree. When modifying
@@ -678,7 +682,7 @@
   DCHECK(IsSingleThreaded());
   // This function is only valid when not using the scheduler.
   DCHECK(!settings_.single_thread_proxy_scheduler);
-  RequestMainFrameUpdate();
+  RequestMainFrameUpdate(false);
   UpdateLayers();
 }
 
@@ -704,8 +708,11 @@
   client_->DidUpdateLayers();
   micro_benchmark_controller_.DidUpdateLayers();
 
+  base::TimeDelta elapsed_delta = timer.Elapsed();
+  if (begin_main_frame_metrics_)
+    begin_main_frame_metrics_->update_layers = elapsed_delta;
   if (const char* client_name = GetClientNameForMetrics()) {
-    auto elapsed = timer.Elapsed().InMicroseconds();
+    auto elapsed = elapsed_delta.InMicroseconds();
 
     std::string histogram_name =
         base::StringPrintf("Compositing.%s.LayersUpdateTime", client_name);
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index af65ef80..95936f7 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -33,6 +33,7 @@
 #include "cc/input/scrollbar.h"
 #include "cc/layers/layer_collections.h"
 #include "cc/layers/layer_list_iterator.h"
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/paint/node_id.h"
 #include "cc/trees/compositor_mode.h"
 #include "cc/trees/layer_tree_frame_sink.h"
@@ -567,7 +568,7 @@
   void BeginMainFrameNotExpectedSoon();
   void BeginMainFrameNotExpectedUntil(base::TimeTicks time);
   void AnimateLayers(base::TimeTicks monotonic_frame_begin_time);
-  void RequestMainFrameUpdate();
+  void RequestMainFrameUpdate(bool report_cc_metrics);
   void FinishCommitOnImplThread(LayerTreeHostImpl* host_impl);
   void WillCommit();
   void CommitComplete();
@@ -876,6 +877,12 @@
   // added here.
   std::vector<PresentationTimeCallback> pending_presentation_time_callbacks_;
 
+  // Latency information for work done in ProxyMain::BeginMainFrame. The
+  // unique_ptr is allocated in RequestMainFrameUpdate, and passed to Blink's
+  // LocalFrameView that fills in the fields. This object adds the timing for
+  // UpdateLayers. CC reads the data during commit, and clears the unique_ptr.
+  std::unique_ptr<BeginMainFrameMetrics> begin_main_frame_metrics_;
+
   // Used to vend weak pointers to LayerTreeHost to ScopedDeferMainFrameUpdate
   // objects.
   base::WeakPtrFactory<LayerTreeHost> defer_main_frame_update_weak_ptr_factory_{
diff --git a/cc/trees/layer_tree_host_client.h b/cc/trees/layer_tree_host_client.h
index 7f424be..40ade06 100644
--- a/cc/trees/layer_tree_host_client.h
+++ b/cc/trees/layer_tree_host_client.h
@@ -22,6 +22,7 @@
 }
 
 namespace cc {
+struct BeginMainFrameMetrics;
 struct ElementId;
 
 struct ApplyViewportChangesArgs {
@@ -149,6 +150,13 @@
   // the time from the start of BeginMainFrame to the Commit, or early out.
   virtual void RecordStartOfFrameMetrics() = 0;
   virtual void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) = 0;
+  // Return metrics information for the stages of BeginMainFrame. This is
+  // ultimately implemented by Blink's LocalFrameUKMAggregator. It must be a
+  // distinct call from the FrameMetrics above because the BeginMainFrameMetrics
+  // for compositor latency must be gathered before the layer tree is
+  // committed to the compositor, which is before the call to
+  // RecordEndOfFrameMetrics.
+  virtual std::unique_ptr<BeginMainFrameMetrics> GetBeginMainFrameMetrics() = 0;
 
  protected:
   virtual ~LayerTreeHostClient() {}
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index 1205949b..2aaa7e2 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -217,6 +217,13 @@
   }
 
   layer_tree_host_->WillBeginMainFrame();
+
+  // This call winds through to the LocalFrameView to mark the beginning
+  // of a main frame for metrics purposes. Some metrics are only gathered
+  // between calls to RecordStartOfFrameMetrics and RecordEndOfFrameMetrics.
+  // This is not wrapped into layer_tree_host_->WillBeginMainFrame because
+  // it should only be called from the multi-threaded proxy (we do not want
+  // metrics gathering in tests).
   layer_tree_host_->RecordStartOfFrameMetrics();
 
   // See LayerTreeHostClient::BeginMainFrame for more documentation on
@@ -236,7 +243,7 @@
 
   // See LayerTreeHostClient::MainFrameUpdate for more documentation on
   // what this does.
-  layer_tree_host_->RequestMainFrameUpdate();
+  layer_tree_host_->RequestMainFrameUpdate(true /* report_cc_metrics */);
 
   // At this point the main frame may have deferred main frame updates to
   // avoid committing right now, or we may be deferring commits but not
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 39a7888d..13279a0 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -859,7 +859,7 @@
   layer_tree_host_->WillBeginMainFrame();
   layer_tree_host_->BeginMainFrame(begin_frame_args);
   layer_tree_host_->AnimateLayers(begin_frame_args.frame_time);
-  layer_tree_host_->RequestMainFrameUpdate();
+  layer_tree_host_->RequestMainFrameUpdate(false /* record_cc_metrics */);
 }
 
 void SingleThreadProxy::DoPainting() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
index 7de0777..6839d423 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenu.java
@@ -40,8 +40,8 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
-import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.widget.ViewHighlighter;
+import org.chromium.ui.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -412,7 +412,7 @@
                     ? resources.getString(R.string.menu_refresh)
                     : resources.getString(R.string.menu_stop_refresh);
         }
-        return AccessibilityUtil.showAccessibilityToast(context, view, description);
+        return Toast.showAnchoredToast(context, view, description);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
index c8adedc..dc409ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
@@ -32,8 +32,8 @@
 import org.chromium.chrome.browser.omnibox.SearchEngineLogoUtils;
 import org.chromium.chrome.browser.toolbar.ToolbarCommonPropertiesModel;
 import org.chromium.chrome.browser.ui.widget.CompositeTouchDelegate;
-import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.ui.UiUtils;
+import org.chromium.ui.widget.Toast;
 
 /**
  * StatusView is a location bar's view displaying status (icons and/or text).
@@ -252,7 +252,7 @@
             public boolean onLongClick(View view) {
                 if (mAccessibilityToast == 0) return false;
                 Context context = getContext();
-                return AccessibilityUtil.showAccessibilityToast(
+                return Toast.showAnchoredToast(
                         context, view, context.getResources().getString(mAccessibilityToast));
             }
         };
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java
index 450b8caa..bf356dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerService.java
@@ -14,7 +14,6 @@
 import com.google.android.gms.gcm.GcmListenerService;
 import com.google.ipc.invalidation.ticl.android2.channel.AndroidGcmController;
 
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
@@ -129,7 +128,7 @@
      * the next time Chrome is launched into foreground.
      */
     private static boolean maybePersistLazyMessage(GCMMessage message) {
-        if (ApplicationStatus.hasVisibleActivities()) {
+        if (isNativeLoaded()) {
             return false;
         }
 
@@ -236,4 +235,9 @@
             System.exit(-1);
         }
     }
+
+    private static boolean isNativeLoaded() {
+        return ChromeBrowserInitializer.getInstance(ContextUtils.getApplicationContext())
+                .hasNativeInitializationCompleted();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
index f9e68e8..4d31065 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -26,9 +26,9 @@
 /**
  * JNI wrapper for the native ProfileSyncService.
  *
- * This class purely makes calls to native and contains absolutely  no business logic. It is only
+ * This class mostly makes calls to native and contains a minimum of business logic. It is only
  * usable from the UI thread as the native ProfileSyncService requires its access to be on the
- * UI thread. See chrome/browser/sync/profile_sync_service.h for more details.
+ * UI thread. See components/sync/driver/profile_sync_service.h for more details.
  */
 public class ProfileSyncService {
 
@@ -40,34 +40,6 @@
         public void syncStateChanged();
     }
 
-    /**
-     * Callback for getAllNodes.
-     */
-    public static class GetAllNodesCallback {
-        private String mNodesString;
-
-        // Invoked when getAllNodes completes.
-        public void onResult(String nodesString) {
-            mNodesString = nodesString;
-        }
-
-        // Returns the result of GetAllNodes as a JSONArray.
-        @VisibleForTesting
-        public JSONArray getNodesAsJsonArray() throws JSONException {
-            return new JSONArray(mNodesString);
-        }
-    }
-
-    /**
-     * Provider for the Android master sync flag.
-     */
-    interface MasterSyncEnabledProvider {
-        // Returns whether master sync is enabled.
-        public boolean isMasterSyncEnabled();
-    }
-
-    private static final String TAG = "ProfileSyncService";
-
     private static final int[] ALL_SELECTABLE_TYPES = new int[] {
         ModelType.AUTOFILL,
         ModelType.BOOKMARKS,
@@ -90,11 +62,6 @@
      */
     private long mNativeProfileSyncServiceAndroid;
 
-    /**
-     * An object that knows whether Android's master sync setting is enabled.
-     */
-    private MasterSyncEnabledProvider mMasterSyncEnabledProvider;
-
     private int mSetupInProgressCounter;
 
     /**
@@ -148,7 +115,7 @@
         ThreadUtils.assertOnUiThread();
 
         // This may cause us to create ProfileSyncService even if sync has not
-        // been set up, but ProfileSyncService::Startup() won't be called until
+        // been set up, but ProfileSyncService won't actually start until
         // credentials are available.
         mNativeProfileSyncServiceAndroid =
                 ProfileSyncServiceJni.get().init(ProfileSyncService.this);
@@ -160,105 +127,9 @@
     }
 
     /**
-     * Sets the the machine tag used by session sync.
-     */
-    public void setSessionsId(String sessionTag) {
-        ThreadUtils.assertOnUiThread();
-        ProfileSyncServiceJni.get().setSyncSessionsId(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, sessionTag);
-    }
-
-    /**
-     * Returns the actual passphrase type being used for encryption. The sync engine must be
-     * running (isEngineInitialized() returns true) before calling this function.
-     * <p/>
-     * This method should only be used if you want to know the raw value. For checking whether
-     * we should ask the user for a passphrase, use isPassphraseRequiredForDecryption().
-     */
-    public @Passphrase.Type int getPassphraseType() {
-        assert isEngineInitialized();
-        int passphraseType = ProfileSyncServiceJni.get().getPassphraseType(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-        if (passphraseType < 0 || passphraseType >= Passphrase.Type.NUM_ENTRIES) {
-            throw new IllegalArgumentException();
-        }
-        return passphraseType;
-    }
-
-    /**
-     * Returns true if the current explicit passphrase time is defined.
-     */
-    public boolean hasExplicitPassphraseTime() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().hasExplicitPassphraseTime(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
-     * Returns the current explicit passphrase time in milliseconds since epoch.
-     */
-    public long getExplicitPassphraseTime() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().getExplicitPassphraseTime(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    public String getSyncEnterGooglePassphraseBodyWithDateText() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().getSyncEnterGooglePassphraseBodyWithDateText(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    public String getSyncEnterCustomPassphraseBodyWithDateText() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().getSyncEnterCustomPassphraseBodyWithDateText(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    public String getCurrentSignedInAccountText() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().getCurrentSignedInAccountText(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    public String getSyncEnterCustomPassphraseBodyText() {
-        return ProfileSyncServiceJni.get().getSyncEnterCustomPassphraseBodyText(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    public int getNumberOfSyncedDevices() {
-        return ProfileSyncServiceJni.get().getNumberOfSyncedDevices(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
-     * Checks if sync is currently set to use a custom passphrase. The sync engine must be running
-     * (isEngineInitialized() returns true) before calling this function.
+     * Checks if the sync engine is initialized.
      *
-     * @return true if sync is using a custom passphrase.
-     */
-    public boolean isUsingSecondaryPassphrase() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().isUsingSecondaryPassphrase(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
-     * Checks if we need a passphrase to decrypt a currently-enabled data type. This returns false
-     * if a passphrase is needed for a type that is not currently enabled.
-     *
-     * @return true if we need a passphrase.
-     */
-    public boolean isPassphraseRequiredForDecryption() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().isPassphraseRequiredForDecryption(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
-     * Checks if the sync engine is running.
-     *
-     * @return true if sync is initialized/running.
+     * @return true if the sync engine is initialized.
      */
     public boolean isEngineInitialized() {
         return ProfileSyncServiceJni.get().isEngineInitialized(
@@ -266,51 +137,28 @@
     }
 
     /**
-     * Checks if encrypting all the data types is allowed.
+     * Checks whether Sync-the-feature can (attempt to) start. This means that there is a primary
+     * account and no disable reasons. Note that the Sync machinery may start up in transport-only
+     * mode even if this is false.
      *
-     * @return true if encrypting all data types is allowed, false if only passwords are allowed to
-     * be encrypted.
+     * @return true if Sync can start, false otherwise.
      */
-    public boolean isEncryptEverythingAllowed() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().isEncryptEverythingAllowed(
+    public boolean canSyncFeatureStart() {
+        return ProfileSyncServiceJni.get().canSyncFeatureStart(
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
     }
 
     /**
-     * Checks if the user has chosen to encrypt all data types. Note that some data types (e.g.
-     * DEVICE_INFO) are never encrypted.
+     * Checks whether Sync-the-feature is currently active. Note that Sync-the-transport may be
+     * active even if this is false.
      *
-     * @return true if all data types are encrypted, false if only passwords are encrypted.
+     * @return true if Sync is active, false otherwise.
      */
-    public boolean isEncryptEverythingEnabled() {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().isEncryptEverythingEnabled(
+    public boolean isSyncActive() {
+        return ProfileSyncServiceJni.get().isSyncActive(
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
     }
 
-    /**
-     * Turns on encryption of all data types. This only takes effect after sync configuration is
-     * completed and setChosenDataTypes() is invoked.
-     */
-    public void enableEncryptEverything() {
-        assert isEngineInitialized();
-        ProfileSyncServiceJni.get().enableEncryptEverything(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    public void setEncryptionPassphrase(String passphrase) {
-        assert isEngineInitialized();
-        ProfileSyncServiceJni.get().setEncryptionPassphrase(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, passphrase);
-    }
-
-    public boolean setDecryptionPassphrase(String passphrase) {
-        assert isEngineInitialized();
-        return ProfileSyncServiceJni.get().setDecryptionPassphrase(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, passphrase);
-    }
-
     public @GoogleServiceAuthError.State int getAuthError() {
         int authErrorCode = ProfileSyncServiceJni.get().getAuthError(
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
@@ -320,12 +168,42 @@
         return authErrorCode;
     }
 
+    /**
+     * Checks whether Sync is disabled by enterprise policy (through prefs) or account policy
+     * received from the sync server.
+     *
+     * @return true if Sync is disabled, false otherwise.
+     */
+    public boolean isSyncDisabledByEnterprisePolicy() {
+        return ProfileSyncServiceJni.get().isSyncDisabledByEnterprisePolicy(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    public boolean hasUnrecoverableError() {
+        return ProfileSyncServiceJni.get().hasUnrecoverableError(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
     public boolean requiresClientUpgrade() {
         return ProfileSyncServiceJni.get().requiresClientUpgrade(
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
     }
 
     /**
+     * Gets the set of data types that are "preferred" in sync. Those are the
+     * chosen ones (see getChosenDataTypes), plus any that are implied by them.
+     *
+     * This is unaffected by whether sync is on.
+     *
+     * @return Set of preferred data types.
+     */
+    public Set<Integer> getPreferredDataTypes() {
+        int[] modelTypeArray = ProfileSyncServiceJni.get().getPreferredDataTypes(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+        return modelTypeArrayToSet(modelTypeArray);
+    }
+
+    /**
      * Gets the set of data types that are currently syncing.
      *
      * This is affected by whether sync is on.
@@ -352,37 +230,6 @@
         return modelTypeArrayToSet(modelTypeArray);
     }
 
-    /**
-     * Gets the set of data types that are "preferred" in sync. Those are the
-     * "chosen" ones (see above), plus any that are implied by them.
-     *
-     * This is unaffected by whether sync is on.
-     *
-     * @return Set of preferred types.
-     */
-    public Set<Integer> getPreferredDataTypes() {
-        int[] modelTypeArray = ProfileSyncServiceJni.get().getPreferredDataTypes(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-        return modelTypeArrayToSet(modelTypeArray);
-    }
-
-    private static Set<Integer> modelTypeArrayToSet(int[] modelTypeArray) {
-        Set<Integer> modelTypeSet = new HashSet<Integer>();
-        for (int i = 0; i < modelTypeArray.length; i++) {
-            modelTypeSet.add(modelTypeArray[i]);
-        }
-        return modelTypeSet;
-    }
-
-    private static int[] modelTypeSetToArray(Set<Integer> modelTypeSet) {
-        int[] modelTypeArray = new int[modelTypeSet.size()];
-        int i = 0;
-        for (int modelType : modelTypeSet) {
-            modelTypeArray[i++] = modelType;
-        }
-        return modelTypeArray;
-    }
-
     public boolean hasKeepEverythingSynced() {
         return ProfileSyncServiceJni.get().hasKeepEverythingSynced(
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
@@ -402,11 +249,6 @@
                 syncEverything ? ALL_SELECTABLE_TYPES : modelTypeSetToArray(enabledTypes));
     }
 
-    public void triggerRefresh() {
-        ProfileSyncServiceJni.get().triggerRefresh(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
     public void setFirstSetupComplete(int syncFirstSetupCompleteSource) {
         ProfileSyncServiceJni.get().setFirstSetupComplete(mNativeProfileSyncServiceAndroid,
                 ProfileSyncService.this, syncFirstSetupCompleteSource);
@@ -417,6 +259,16 @@
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
     }
 
+    public void requestStart() {
+        ProfileSyncServiceJni.get().requestStart(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    public void requestStop() {
+        ProfileSyncServiceJni.get().requestStop(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
     /**
      * Checks whether syncing is requested by the user, i.e. the user has at least started a Sync
      * setup flow, and has not disabled syncing in settings. Note that even if this is true, other
@@ -430,40 +282,6 @@
     }
 
     /**
-     * Checks whether Sync-the-feature can (attempt to) start. This means that there is a primary
-     * account and no disable reasons. Note that the Sync machinery may start up in transport-only
-     * mode even if this is false.
-     *
-     * @return true if Sync can start, false otherwise.
-     */
-    public boolean canSyncFeatureStart() {
-        return ProfileSyncServiceJni.get().canSyncFeatureStart(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
-     * Checks whether Sync-the-feature is currently active. Note that Sync-the-transport may be
-     * active even if this is false.
-     *
-     * @return true if Sync is active, false otherwise.
-     */
-    public boolean isSyncActive() {
-        return ProfileSyncServiceJni.get().isSyncActive(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
-     * Checks whether Sync is disabled by enterprise policy (through prefs) or account policy
-     * received from the sync server.
-     *
-     * @return true if Sync is disabled, false otherwise.
-     */
-    public boolean isSyncDisabledByEnterprisePolicy() {
-        return ProfileSyncServiceJni.get().isSyncDisabledByEnterprisePolicy(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
      * Instances of this class keep sync paused until {@link #close} is called. Use
      * {@link ProfileSyncService#getSetupInProgressHandle} to create. Please note that
      * {@link #close} should be called on every instance of this class.
@@ -523,45 +341,25 @@
         mListeners.remove(listener);
     }
 
-    public boolean hasUnrecoverableError() {
-        return ProfileSyncServiceJni.get().hasUnrecoverableError(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
     /**
      * Called when the state of the native sync engine has changed, so various
      * UI elements can update themselves.
      */
     @CalledByNative
+    @VisibleForTesting
     public void syncStateChanged() {
         for (SyncStateChangedListener listener : mListeners) {
             listener.syncStateChanged();
         }
     }
 
-    /**
-     * Starts the sync engine.
-     */
-    public void requestStart() {
-        ProfileSyncServiceJni.get().requestStart(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
-    /**
-     * Stops the sync engine.
-     */
-    public void requestStop() {
-        ProfileSyncServiceJni.get().requestStop(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
-    }
-
     public void setSyncAllowedByPlatform(boolean allowed) {
         ProfileSyncServiceJni.get().setSyncAllowedByPlatform(
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this, allowed);
     }
 
     /**
-     * Flushes the sync directory.
+     * Flushes the sync directory to disk.
      */
     public void flushDirectory() {
         ProfileSyncServiceJni.get().flushDirectory(
@@ -569,29 +367,131 @@
     }
 
     /**
-     * Returns the time when the last sync cycle was completed.
-     *
-     * @return The difference measured in microseconds, between last sync cycle completion time
-     * and 1 January 1970 00:00:00 UTC.
+     * Returns the actual passphrase type being used for encryption. The sync engine must be
+     * running (isEngineInitialized() returns true) before calling this function.
+     * <p/>
+     * This method should only be used if you want to know the raw value. For checking whether
+     * we should ask the user for a passphrase, use isPassphraseRequiredForDecryption().
      */
-    @VisibleForTesting
-    public long getLastSyncedTimeForTest() {
-        return ProfileSyncServiceJni.get().getLastSyncedTimeForTest(
+    public @Passphrase.Type int getPassphraseType() {
+        assert isEngineInitialized();
+        int passphraseType = ProfileSyncServiceJni.get().getPassphraseType(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+        if (passphraseType < 0 || passphraseType >= Passphrase.Type.NUM_ENTRIES) {
+            throw new IllegalArgumentException();
+        }
+        return passphraseType;
+    }
+
+    /**
+     * Returns true if the current explicit passphrase time is defined.
+     */
+    public boolean hasExplicitPassphraseTime() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().hasExplicitPassphraseTime(
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
     }
 
     /**
-     * Overrides the Sync engine's NetworkResources. This is used to set up the Sync FakeServer for
-     * testing.
-     *
-     * @param networkResources the pointer to the NetworkResources created by the native code. It
-     *                         is assumed that the Java caller has ownership of this pointer;
-     *                         ownership is transferred as part of this call.
+     * Returns the current explicit passphrase time in milliseconds since epoch.
      */
-    @VisibleForTesting
-    public void overrideNetworkResourcesForTest(long networkResources) {
-        ProfileSyncServiceJni.get().overrideNetworkResourcesForTest(
-                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, networkResources);
+    public long getExplicitPassphraseTime() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().getExplicitPassphraseTime(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    public String getSyncEnterGooglePassphraseBodyWithDateText() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().getSyncEnterGooglePassphraseBodyWithDateText(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    public String getSyncEnterCustomPassphraseBodyWithDateText() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().getSyncEnterCustomPassphraseBodyWithDateText(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    public String getCurrentSignedInAccountText() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().getCurrentSignedInAccountText(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    public String getSyncEnterCustomPassphraseBodyText() {
+        return ProfileSyncServiceJni.get().getSyncEnterCustomPassphraseBodyText(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Checks if sync is currently set to use a custom passphrase. The sync engine must be running
+     * (isEngineInitialized() returns true) before calling this function.
+     *
+     * @return true if sync is using a custom passphrase.
+     */
+    public boolean isUsingSecondaryPassphrase() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().isUsingSecondaryPassphrase(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Checks if we need a passphrase to decrypt a currently-enabled data type. This returns false
+     * if a passphrase is needed for a type that is not currently enabled.
+     *
+     * @return true if we need a passphrase.
+     */
+    public boolean isPassphraseRequiredForDecryption() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().isPassphraseRequiredForDecryption(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Checks if encrypting all the data types is allowed.
+     *
+     * @return true if encrypting all data types is allowed, false if only passwords are allowed to
+     * be encrypted.
+     */
+    public boolean isEncryptEverythingAllowed() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().isEncryptEverythingAllowed(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Checks if the user has chosen to encrypt all data types. Note that some data types (e.g.
+     * DEVICE_INFO) are never encrypted.
+     *
+     * @return true if all data types are encrypted, false if only passwords are encrypted.
+     */
+    public boolean isEncryptEverythingEnabled() {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().isEncryptEverythingEnabled(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Turns on encryption of all data types. This only takes effect after sync configuration is
+     * completed and setChosenDataTypes() is invoked.
+     */
+    public void enableEncryptEverything() {
+        assert isEngineInitialized();
+        ProfileSyncServiceJni.get().enableEncryptEverything(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    public void setEncryptionPassphrase(String passphrase) {
+        assert isEngineInitialized();
+        ProfileSyncServiceJni.get().setEncryptionPassphrase(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, passphrase);
+    }
+
+    public boolean setDecryptionPassphrase(String passphrase) {
+        assert isEngineInitialized();
+        return ProfileSyncServiceJni.get().setDecryptionPassphrase(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, passphrase);
     }
 
     /**
@@ -621,27 +521,72 @@
     }
 
     /**
-     * Set the MasterSyncEnabledProvider for ProfileSyncService.
-     *
-     * This method is intentionally package-scope and should only be called once.
+     * Sets the the machine tag used by session sync.
      */
-    void setMasterSyncEnabledProvider(MasterSyncEnabledProvider masterSyncEnabledProvider) {
+    public void setSessionsId(String sessionTag) {
         ThreadUtils.assertOnUiThread();
-        assert mMasterSyncEnabledProvider == null;
-        mMasterSyncEnabledProvider = masterSyncEnabledProvider;
+        ProfileSyncServiceJni.get().setSyncSessionsId(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, sessionTag);
     }
 
     /**
-     * Returns whether Android's master sync setting is enabled.
+     * Gets the number of devices known to sync.
+     *
+     * @return number of syncing devices
      */
-    @CalledByNative
-    public boolean isMasterSyncEnabled() {
-        ThreadUtils.assertOnUiThread();
-        // TODO(maxbogue): ensure that this method is never called before
-        // setMasterSyncEnabledProvider() and change the line below to an assert.
-        // See http://crbug.com/570569
-        if (mMasterSyncEnabledProvider == null) return true;
-        return mMasterSyncEnabledProvider.isMasterSyncEnabled();
+    public int getNumberOfSyncedDevices() {
+        return ProfileSyncServiceJni.get().getNumberOfSyncedDevices(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Returns the time when the last sync cycle was completed.
+     *
+     * @return The difference measured in microseconds, between last sync cycle completion time
+     * and 1 January 1970 00:00:00 UTC.
+     */
+    @VisibleForTesting
+    public long getLastSyncedTimeForTest() {
+        return ProfileSyncServiceJni.get().getLastSyncedTimeForTest(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Overrides the Sync engine's NetworkResources. This is used to set up the Sync FakeServer for
+     * testing.
+     *
+     * @param networkResources the pointer to the NetworkResources created by the native code. It
+     *                         is assumed that the Java caller has ownership of this pointer;
+     *                         ownership is transferred as part of this call.
+     */
+    @VisibleForTesting
+    public void overrideNetworkResourcesForTest(long networkResources) {
+        ProfileSyncServiceJni.get().overrideNetworkResourcesForTest(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this, networkResources);
+    }
+
+    @VisibleForTesting
+    public void triggerRefresh() {
+        ProfileSyncServiceJni.get().triggerRefresh(
+                mNativeProfileSyncServiceAndroid, ProfileSyncService.this);
+    }
+
+    /**
+     * Callback for getAllNodes.
+     */
+    public static class GetAllNodesCallback {
+        private String mNodesString;
+
+        // Invoked when getAllNodes completes.
+        public void onResult(String nodesString) {
+            mNodesString = nodesString;
+        }
+
+        // Returns the result of GetAllNodes as a JSONArray.
+        @VisibleForTesting
+        public JSONArray getNodesAsJsonArray() throws JSONException {
+            return new JSONArray(mNodesString);
+        }
     }
 
     /**
@@ -662,6 +607,23 @@
                 mNativeProfileSyncServiceAndroid, ProfileSyncService.this, callback);
     }
 
+    private static Set<Integer> modelTypeArrayToSet(int[] modelTypeArray) {
+        Set<Integer> modelTypeSet = new HashSet<Integer>();
+        for (int i = 0; i < modelTypeArray.length; i++) {
+            modelTypeSet.add(modelTypeArray[i]);
+        }
+        return modelTypeSet;
+    }
+
+    private static int[] modelTypeSetToArray(Set<Integer> modelTypeSet) {
+        int[] modelTypeArray = new int[modelTypeSet.size()];
+        int i = 0;
+        for (int modelType : modelTypeSet) {
+            modelTypeArray[i++] = modelType;
+        }
+        return modelTypeArray;
+    }
+
     @NativeMethods
     interface Natives {
         long init(ProfileSyncService caller);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
index 417d4e8..7bab384f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncController.java
@@ -74,13 +74,6 @@
         AndroidSyncSettings.get().registerObserver(this);
         mProfileSyncService = ProfileSyncService.get();
         mProfileSyncService.addSyncStateChangedListener(this);
-        mProfileSyncService.setMasterSyncEnabledProvider(
-                new ProfileSyncService.MasterSyncEnabledProvider() {
-                    @Override
-                    public boolean isMasterSyncEnabled() {
-                        return AndroidSyncSettings.get().isMasterSyncEnabled();
-                    }
-                });
 
         setSessionsId();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/NewTabButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/NewTabButton.java
index 74208d5..98d401531 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/NewTabButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/NewTabButton.java
@@ -18,10 +18,10 @@
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.device.DeviceClassManager;
 import org.chromium.chrome.browser.toolbar.IncognitoStateProvider.IncognitoStateObserver;
-import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.widget.ChromeImageButton;
+import org.chromium.ui.widget.Toast;
 
 /**
  * Button for creating new tabs.
@@ -55,7 +55,7 @@
         CharSequence description = getResources().getString(mIsIncognito
                         ? org.chromium.chrome.R.string.button_new_incognito_tab
                         : org.chromium.chrome.R.string.button_new_tab);
-        return AccessibilityUtil.showAccessibilityToast(getContext(), v, description);
+        return Toast.showAnchoredToast(getContext(), v, description);
     }
 
     public void setIncognitoStateProvider(IncognitoStateProvider incognitoStateProvider) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
index 9becd72b..99272c8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/TabSwitcherButtonCoordinator.java
@@ -15,9 +15,9 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver;
 import org.chromium.chrome.browser.toolbar.TabCountProvider.TabCountObserver;
-import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.widget.Toast;
 
 /**
  * The controller for the tab switcher button. This class handles all interactions that the tab
@@ -53,7 +53,7 @@
 
         CharSequence description = root.getResources().getString(R.string.open_tabs);
         mTabSwitcherButtonModel.set(TabSwitcherButtonProperties.ON_LONG_CLICK_LISTENER,
-                v -> AccessibilityUtil.showAccessibilityToast(root.getContext(), v, description));
+                v -> Toast.showAnchoredToast(root.getContext(), v, description));
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
index ea755221..eabdc4c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/CustomTabToolbar.java
@@ -60,7 +60,6 @@
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarTabController;
 import org.chromium.chrome.browser.ui.widget.TintedDrawable;
-import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -72,6 +71,7 @@
 import org.chromium.ui.interpolators.BakedBezierInterpolator;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.text.SpanApplier.SpanInfo;
+import org.chromium.ui.widget.Toast;
 
 import java.util.List;
 import java.util.regex.Pattern;
@@ -520,8 +520,7 @@
     @Override
     public boolean onLongClick(View v) {
         if (v == mCloseButton || v.getParent() == mCustomActionButtons) {
-            return AccessibilityUtil.showAccessibilityToast(
-                    getContext(), v, v.getContentDescription());
+            return Toast.showAnchoredToast(getContext(), v, v.getContentDescription());
         }
         if (v == mTitleUrlContainer) {
             Tab tab = getCurrentTab();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButton.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButton.java
index a1b9456..5173d896 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButton.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToggleTabStackButton.java
@@ -13,8 +13,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.toolbar.TabCountProvider;
 import org.chromium.chrome.browser.toolbar.TabSwitcherDrawable;
-import org.chromium.chrome.browser.util.AccessibilityUtil;
 import org.chromium.ui.widget.ChromeImageButton;
+import org.chromium.ui.widget.Toast;
 
 /**
  * A button displaying the number of open tabs. Clicking the button toggles the tab switcher view.
@@ -114,7 +114,7 @@
         } else {
             CharSequence description =
                     getResources().getString(org.chromium.chrome.R.string.open_tabs);
-            return AccessibilityUtil.showAccessibilityToast(getContext(), v, description);
+            return Toast.showAnchoredToast(getContext(), v, description);
         }
     }
 }
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index 2577f6eb..e8b3f1c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -43,6 +43,7 @@
 import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.DeviceFormFactor;
+import org.chromium.ui.widget.Toast;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -344,7 +345,7 @@
         } else if (v == mSaveOfflineButton) {
             description = resources.getString(R.string.menu_download);
         }
-        return AccessibilityUtil.showAccessibilityToast(context, v, description);
+        return Toast.showAnchoredToast(context, v, description);
     }
 
     private void updateSwitcherButtonVisibility(boolean enabled) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java
index 3c8d25f1..b457e3ee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/AccessibilityUtil.java
@@ -5,12 +5,9 @@
 package org.chromium.chrome.browser.util;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.Context;
 import android.os.Build;
-import android.view.Gravity;
-import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 
 import org.chromium.base.ActivityState;
@@ -18,7 +15,6 @@
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.TraceEvent;
-import org.chromium.ui.widget.Toast;
 
 import java.util.List;
 
@@ -110,37 +106,4 @@
         }
         return false;
     }
-
-    /**
-     * Shows the content description toast for items on the toolbar.
-     * @param context The context to use for the toast.
-     * @param view The view to anchor the toast.
-     * @param description The string shown in the toast.
-     * @return Whether a toast has been shown successfully.
-     */
-    @SuppressLint("RtlHardcoded")
-    public static boolean showAccessibilityToast(
-            Context context, View view, CharSequence description) {
-        if (description == null) return false;
-
-        final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
-        final int screenHeight = context.getResources().getDisplayMetrics().heightPixels;
-        final int[] screenPos = new int[2];
-        view.getLocationOnScreen(screenPos);
-        final int width = view.getWidth();
-        final int height = view.getHeight();
-
-        final int horizontalGravity =
-                (screenPos[0] < screenWidth / 2) ? Gravity.LEFT : Gravity.RIGHT;
-        final int xOffset = (screenPos[0] < screenWidth / 2)
-                ? screenPos[0] + width / 2
-                : screenWidth - screenPos[0] - width / 2;
-        final int yOffset = (screenPos[1] < screenHeight / 2) ? screenPos[1] + height / 2
-                                                              : screenPos[1] - height * 3 / 2;
-
-        Toast toast = Toast.makeText(context, description, Toast.LENGTH_SHORT);
-        toast.setGravity(Gravity.TOP | horizontalGravity, xOffset, yOffset);
-        toast.show();
-        return true;
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
index 1ac51a9..273b052 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -21,6 +21,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
@@ -241,6 +242,7 @@
 
     @Test
     @SmallTest
+    @DisabledTest
     @EnableFeatures(ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO)
     @Feature({"OmniboxSearchEngineLogo"})
     public void testOmniboxSearchEngineLogo_goneWhenIncognito() throws Exception {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarTest.java
index a7c7275b..f9d28715 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/bottom/BottomToolbarTest.java
@@ -16,6 +16,7 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -24,6 +25,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.OverviewModeBehaviorWatcher;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.test.util.UiRestriction;
 
 import java.util.concurrent.ExecutionException;
 
@@ -32,6 +34,7 @@
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
 public class BottomToolbarTest {
     @Rule
     public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 9bd2af42..4ea42dc 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -457,6 +457,7 @@
       "//chromeos/services/multidevice_setup/public/cpp:manifest",
       "//chromeos/services/multidevice_setup/public/mojom",
       "//chromeos/services/network_config/public/mojom",
+      "//components/chromeos_camera/common:camera_app_helper",
       "//media/capture/video/chromeos/mojom:cros_camera",
     ]
   }
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index fe64288..0b0bcd1 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -52,6 +52,7 @@
     "+chromeos/services/multidevice_setup",
     "+chromeos/services/network_config",
     "+components/autofill/content/common",
+    "+components/chromeos_camera/common",
     "+components/contextual_search/content/common",
     "+components/data_reduction_proxy/core/common",
     "+components/dom_distiller/content/common",
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc
index d65f3207..49bbeb4 100644
--- a/chrome/app/chrome_content_browser_overlay_manifest.cc
+++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -56,6 +56,7 @@
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "chromeos/services/network_config/public/mojom/constants.mojom.h"  // nogncheck
 #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"  // nogncheck
+#include "components/chromeos_camera/common/camera_app_helper.mojom.h"
 #include "media/capture/video/chromeos/mojom/camera_app.mojom.h"
 #endif
 
@@ -158,12 +159,12 @@
                 chrome::mojom::OfflinePageAutoFetcher,
                 chrome::mojom::PrerenderCanceler,
 #if defined(OS_CHROMEOS)
+                chromeos_camera::mojom::CameraAppHelper,
                 chromeos::crostini_installer::mojom::PageHandlerFactory,
                 chromeos::ime::mojom::InputEngineManager,
                 chromeos::machine_learning::mojom::PageHandler,
                 chromeos::media_perception::mojom::MediaPerception,
                 cros::mojom::CameraAppDeviceProvider,
-                cros::mojom::CameraAppHelper,
 #endif
                 contextual_search::mojom::ContextualSearchJsApiService,
                 dom_distiller::mojom::DistillabilityService,
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 7b0aac0e..e754648 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -474,48 +474,6 @@
         Google API keys are missing. Some functionality of Chromium will be disabled.
       </message>
 
-      <!-- About Chrome page -->
-      <if expr="not chromeos">
-        <message name="IDS_UPGRADE_SUCCESSFUL_RELAUNCH" desc="Status label: Successfully updated Chromium">
-          Nearly up to date! Relaunch Chromium to finish updating.
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_SUCCESSFUL_RELAUNCH" desc="Status label: Successfully updated Chromium OS">
-          Nearly up to date! Restart your device to finish updating.
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_SUCCESSFUL_CHANNEL_SWITCH" desc="Status label: Channel was successfully switched on Chromium OS">
-          Channel changed. Restart your device to apply changes.
-        </message>
-      </if>
-      <if expr="not chromeos">
-        <message name="IDS_UPGRADE_UP_TO_DATE" desc="Status label: Already up to date (Chromium)">
-          Chromium is up to date.
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_UP_TO_DATE" desc="Status label: Already up to date (Chromium OS)">
-          Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> is up to date.
-        </message>
-      </if>
-      <if expr="not chromeos">
-        <message name="IDS_UPGRADE_UPDATING" desc="Status label: Updating Chromium">
-          Updating Chromium...
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_UPDATING" desc="Status label: Updating Chromium OS">
-          Updating your device...
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_UPDATING_CHANNEL_SWITCH" desc="Status label: Updating Chromium OS to a specified channel">
-          Updating your device to <ph name="CHANNEL_NAME">$1<ex>stable</ex></ph> channel...
-        </message>
-      </if>
-
       <!-- Extension/App install prompt -->
       <if expr="enable_extensions">
         <!-- Extension installed bubble -->
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index e86e35e..be9f4d0 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -486,48 +486,6 @@
         Google API keys are missing. Some functionality of Google Chrome will be disabled.
       </message>
 
-      <!-- About Chrome page -->
-      <if expr="not chromeos">
-        <message name="IDS_UPGRADE_SUCCESSFUL_RELAUNCH" desc="Status label: Successfully updated Google Chrome">
-          Nearly up to date! Relaunch Google Chrome to finish updating.
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_SUCCESSFUL_RELAUNCH" desc="Status label: Successfully updated Chrome OS">
-          Nearly up to date! Restart your device to finish updating.
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_SUCCESSFUL_CHANNEL_SWITCH" desc="Status label: Channel was successfully switched on Chrome OS">
-          Channel changed. Restart your device to apply changes.
-        </message>
-      </if>
-      <if expr="not chromeos">
-        <message name="IDS_UPGRADE_UP_TO_DATE" desc="Status label: Already up to date (Google Chrome)">
-          Google Chrome is up to date.
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_UP_TO_DATE" desc="Status label: Already up to date (Chrome OS)">
-          Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> is up to date.
-        </message>
-      </if>
-      <if expr="not chromeos">
-        <message name="IDS_UPGRADE_UPDATING" desc="Status label: Updating Google Chrome">
-          Updating Google Chrome...
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_UPDATING" desc="Status label: Updating Chrome OS">
-          Updating your device...
-        </message>
-      </if>
-      <if expr="chromeos">
-        <message name="IDS_UPGRADE_UPDATING_CHANNEL_SWITCH" desc="Status label: Updating Chrome OS to a specified channel">
-          Updating your device to <ph name="CHANNEL_NAME">$1<ex>stable</ex></ph> channel...
-        </message>
-      </if>
-
       <!-- Extension/App install prompt -->
       <if expr="enable_extensions">
         <!-- Extension installed bubble -->
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index f73c85fa..e476f15 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -3604,6 +3604,9 @@
   <message name="IDS_SETTINGS_SITE_SETTINGS_CLEAR_THIRD_PARTY_COOKIES" desc="Label for the confirmation dialog button to delete all cookies and site data for third-party contexts.">
     Clear third-party cookies
   </message>
+  <message name="IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIES_EXCEPTION_LABEL" desc="Label for site exceptions that affect third party cookies.">
+    Including third-party cookies
+  </message>
   <message name="IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_DIALOG_TITLE" desc="Title of the dialog that warns about deleting all site data.">
     Clear site data
   </message>
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index e3433cd..e3620a1 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -259,23 +259,8 @@
       AllowShowingSoftKeyboard(true);
       SetSpinPoodle(false);
 
-      // make sure user sees the error message.
+      // Make sure the user sees the error message.
       ExpandBottomSheet();
-      {
-        const ClientSettings& settings = ui_delegate_->GetClientSettings();
-        if (settings.enable_graceful_shutdown) {
-          // Keep showing the current UI for a while, without getting updates
-          // from the controller, then shut down the UI portion.
-          //
-          // A controller might still get attached while the timer is running,
-          // canceling the destruction.
-          destroy_timer_ = std::make_unique<base::OneShotTimer>();
-          destroy_timer_->Start(
-              FROM_HERE, settings.graceful_shutdown_delay,
-              base::BindOnce(&UiControllerAndroid::DestroySelf,
-                             weak_ptr_factory_.GetWeakPtr()));
-        }
-      }
       Detach();
       return;
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 25c5904..c730089 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -66,7 +66,7 @@
               Client* client,
               UiDelegate* ui_delegate);
 
-  // Detaches the UI from the its delegate. This guarantees the delegate is not
+  // Detaches the UI from its delegate. This guarantees the delegate is not
   // called anymore after the call.
   void Detach();
 
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 3b3acf7..008ec7fd 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -389,7 +389,7 @@
     "DarkenWebsitesCheckboxInThemesSetting", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kDirectActions{"DirectActions",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kDrawVerticallyEdgeToEdge{
     "DrawVerticallyEdgeToEdge", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/CompositorViewImpl.java b/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/CompositorViewImpl.java
index 1ff2296a..1433d3e 100644
--- a/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/CompositorViewImpl.java
+++ b/chrome/browser/android/thin_webview/internal/java/src/org/chromium/chrome/browser/thinwebview/internal/CompositorViewImpl.java
@@ -146,7 +146,7 @@
     }
 
     private static boolean useSurfaceView() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
     }
 
     @NativeMethods
diff --git a/chrome/browser/android/thumbnail/thumbnail_cache.cc b/chrome/browser/android/thumbnail/thumbnail_cache.cc
index 0b41db09..683e93a 100644
--- a/chrome/browser/android/thumbnail/thumbnail_cache.cc
+++ b/chrome/browser/android/thumbnail/thumbnail_cache.cc
@@ -11,7 +11,6 @@
 #include "base/android/application_status_listener.h"
 #include "base/android/path_utils.h"
 #include "base/big_endian.h"
-#include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -22,7 +21,6 @@
 #include "build/build_config.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
-#include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/config/gpu_finch_features.h"
 #include "skia/ext/image_operations.h"
 #include "third_party/android_opengl/etc1/etc1.h"
@@ -199,14 +197,6 @@
   MakeSpaceForNewItemIfNecessary(tab_id);
   cache_.Put(tab_id, std::move(thumbnail));
 
-  // Vulkan does not yet support compressed texture uploads. Disable compression
-  // and approximation when in experimental Vulkan mode.
-  // TODO(ericrk): Remove this restriction. https://crbug.com/906794
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseVulkan) ||
-      base::FeatureList::IsEnabled(features::kVulkan)) {
-    return;
-  }
-
   if (use_approximation_thumbnail_) {
     std::pair<SkBitmap, float> approximation =
         CreateApproximation(bitmap, thumbnail_scale);
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc
index 55208cd..967069cd 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.cc
+++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -1157,7 +1157,7 @@
     return false;
 
   VLOG(1) << "Typing '" << value << "' inside `" << xpath << "`.";
-
+  PageActivityObserver page_activity_observer(frame);
   for (size_t index = 0; index < value->size(); index++) {
     ui::DomKey key = ui::DomKey::FromCharacter(value->at(index));
     ui::KeyboardCode key_code = ui::NonPrintableDomKeyToKeyboardCode(key);
@@ -1165,7 +1165,7 @@
     SimulateKeyPress(content::WebContents::FromRenderFrameHost(frame), key,
                      code, key_code, false, false, false, false);
   }
-
+  page_activity_observer.WaitTillPageIsIdle();
   return true;
 }
 
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.cc b/chrome/browser/autofill/manual_filling_controller_impl.cc
index 7f20822..1f64d03 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_accessory_controller.h"
 #include "chrome/browser/password_manager/password_accessory_metrics_util.h"
-#include "chrome/browser/password_manager/touch_to_fill_controller.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_util.h"
@@ -76,14 +75,12 @@
     base::WeakPtr<PasswordAccessoryController> pwd_controller,
     base::WeakPtr<AddressAccessoryController> address_controller,
     base::WeakPtr<CreditCardAccessoryController> cc_controller,
-    base::WeakPtr<TouchToFillController> touch_to_fill_controller,
     std::unique_ptr<ManualFillingViewInterface> view) {
   DCHECK(web_contents) << "Need valid WebContents to attach controller to!";
   DCHECK(!FromWebContents(web_contents)) << "Controller already attached!";
   DCHECK(pwd_controller);
   DCHECK(address_controller);
   DCHECK(cc_controller);
-  DCHECK(touch_to_fill_controller);
   DCHECK(view);
 
   web_contents->SetUserData(
@@ -92,7 +89,7 @@
       base::WrapUnique(new ManualFillingControllerImpl(
           web_contents, favicon_service, std::move(pwd_controller),
           std::move(address_controller), std::move(cc_controller),
-          std::move(touch_to_fill_controller), std::move(view))));
+          std::move(view))));
 
   FromWebContents(web_contents)->Initialize();
 }
@@ -235,11 +232,6 @@
         CreditCardAccessoryController::GetOrCreate(web_contents)->AsWeakPtr();
     DCHECK(cc_controller_);
   }
-
-  touch_to_fill_controller_ =
-      ChromePasswordManagerClient::FromWebContents(web_contents_)
-          ->GetOrCreateTouchToFillController()
-          ->AsWeakPtr();
 }
 
 ManualFillingControllerImpl::ManualFillingControllerImpl(
@@ -248,14 +240,12 @@
     base::WeakPtr<PasswordAccessoryController> pwd_controller,
     base::WeakPtr<AddressAccessoryController> address_controller,
     base::WeakPtr<CreditCardAccessoryController> cc_controller,
-    base::WeakPtr<TouchToFillController> touch_to_fill_controller,
     std::unique_ptr<ManualFillingViewInterface> view)
     : web_contents_(web_contents),
       favicon_service_(favicon_service),
       pwd_controller_for_testing_(std::move(pwd_controller)),
       address_controller_(std::move(address_controller)),
       cc_controller_(std::move(cc_controller)),
-      touch_to_fill_controller_(std::move(touch_to_fill_controller)),
       view_(std::move(view)) {}
 
 bool ManualFillingControllerImpl::ShouldShowAccessory() const {
@@ -325,7 +315,6 @@
     case AccessoryTabType::CREDIT_CARDS:
       return cc_controller_.get();
     case AccessoryTabType::TOUCH_TO_FILL:
-      return touch_to_fill_controller_.get();
     case AccessoryTabType::ALL:
     case AccessoryTabType::COUNT:
       break;  // Intentional failure.
diff --git a/chrome/browser/autofill/manual_filling_controller_impl.h b/chrome/browser/autofill/manual_filling_controller_impl.h
index bbd3a15..031090df4 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl.h
+++ b/chrome/browser/autofill/manual_filling_controller_impl.h
@@ -29,7 +29,6 @@
 
 class AccessoryController;
 class PasswordAccessoryController;
-class TouchToFillController;
 
 // Use ManualFillingController::GetOrCreate to obtain instances of this class.
 class ManualFillingControllerImpl
@@ -68,7 +67,6 @@
       base::WeakPtr<PasswordAccessoryController> pwd_controller,
       base::WeakPtr<autofill::AddressAccessoryController> address_controller,
       base::WeakPtr<autofill::CreditCardAccessoryController> cc_controller,
-      base::WeakPtr<TouchToFillController> touch_to_fill_controller,
       std::unique_ptr<ManualFillingViewInterface> test_view);
 
 #if defined(UNIT_TEST)
@@ -98,7 +96,6 @@
       base::WeakPtr<PasswordAccessoryController> pwd_controller,
       base::WeakPtr<autofill::AddressAccessoryController> address_controller,
       base::WeakPtr<autofill::CreditCardAccessoryController> cc_controller,
-      base::WeakPtr<TouchToFillController> touch_to_fill_controller,
       std::unique_ptr<ManualFillingViewInterface> view);
 
   // Returns true if the keyboard accessory needs to be shown.
@@ -144,7 +141,6 @@
   base::WeakPtr<PasswordAccessoryController> pwd_controller_for_testing_;
   base::WeakPtr<autofill::AddressAccessoryController> address_controller_;
   base::WeakPtr<autofill::CreditCardAccessoryController> cc_controller_;
-  base::WeakPtr<TouchToFillController> touch_to_fill_controller_;
 
   // Hold the native instance of the view. Must be last declared and initialized
   // member so the view can be created in the constructor with a fully set up
diff --git a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
index 9dee48f..031cd56 100644
--- a/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
+++ b/chrome/browser/autofill/manual_filling_controller_impl_unittest.cc
@@ -70,7 +70,6 @@
     ManualFillingControllerImpl::CreateForWebContentsForTesting(
         web_contents(), favicon_service(), mock_pwd_controller_.AsWeakPtr(),
         mock_address_controller_.AsWeakPtr(), mock_cc_controller_.AsWeakPtr(),
-        touch_to_fill_controller_.AsWeakPtr(),
         std::make_unique<NiceMock<MockManualFillingView>>());
   }
 
@@ -112,7 +111,6 @@
   NiceMock<MockPasswordAccessoryController> mock_pwd_controller_;
   NiceMock<MockAddressAccessoryController> mock_address_controller_;
   NiceMock<MockCreditCardAccessoryController> mock_cc_controller_;
-  TouchToFillController touch_to_fill_controller_{web_contents_};
 
   std::unique_ptr<StrictMock<favicon::MockFaviconService>>
       mock_favicon_service_ =
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index 735fb1b..76067de 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -23,6 +23,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "components/rappor/public/rappor_utils.h"
 #include "components/rappor/rappor_service_impl.h"
+#include "content/public/browser/back_forward_cache.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
@@ -554,6 +555,21 @@
       handle->IsSameDocument()) {
     return;
   }
+
+  // If the page gets stored in the back-forward cache we will not trigger the
+  // pipeline again when navigating back (DidFinishLoad will not trigger). So
+  // only allow the page to enter the cache if we know for sure that no
+  // installation is needed.
+  // Note: this check must happen before calling Terminate as it might set the
+  // installable_web_app_check_result_ to kNo.
+  if (installable_web_app_check_result_ != InstallableWebAppCheckResult::kNo) {
+    web_contents()
+        ->GetController()
+        .GetBackForwardCache()
+        .DisableForRenderFrameHost(handle->GetPreviousRenderFrameHostId(),
+                                   "banners::AppBannerManager");
+  }
+
   if (state_ != State::COMPLETE && state_ != State::INACTIVE)
     Terminate();
   ResetCurrentPageData();
diff --git a/chrome/browser/chromeos/account_manager/account_manager_policy_controller_browsertest.cc b/chrome/browser/chromeos/account_manager/account_manager_policy_controller_browsertest.cc
index 0cd175d..4da29a5 100644
--- a/chrome/browser/chromeos/account_manager/account_manager_policy_controller_browsertest.cc
+++ b/chrome/browser/chromeos/account_manager/account_manager_policy_controller_browsertest.cc
@@ -32,10 +32,6 @@
   AccountManagerPolicyControllerTest() = default;
   ~AccountManagerPolicyControllerTest() override = default;
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    scoped_feature_list_.InitAndEnableFeature(features::kAccountManager);
-  }
-
   void SetUpOnMainThread() override {
     // Prep private fields.
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -111,7 +107,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
   base::ScopedTempDir temp_dir_;
   // Non-owning pointer.
   AccountManager* account_manager_ = nullptr;
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 17a97c0..34d4abd6 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -674,6 +674,12 @@
         TestCase("saveFileDialogDefaultFilter").WithBrowser(),
         TestCase("openFileDialogFileListShowContextMenu").WithBrowser()));
 
+// Flaky on Chrome OS Debug. TODO(crbug.com/1008909).
+WRAPPED_INSTANTIATE_TEST_SUITE_P(
+    DISABLED_CopyBetweenWindows, /* copy_between_windows.js */
+    FilesAppBrowserTest,
+    ::testing::Values(TestCase("copyBetweenWindowsDriveToUsb")));
+
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     CopyBetweenWindows, /* copy_between_windows.js */
     FilesAppBrowserTest,
@@ -681,7 +687,7 @@
                       TestCase("copyBetweenWindowsLocalToUsb"),
                       TestCase("copyBetweenWindowsUsbToDrive"),
                       TestCase("copyBetweenWindowsDriveToLocal"),
-                      TestCase("copyBetweenWindowsDriveToUsb"),
+//                      TestCase("copyBetweenWindowsDriveToUsb"),
                       TestCase("copyBetweenWindowsUsbToLocal")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
index 25947b8b..e5f0324d 100644
--- a/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
+++ b/chrome/browser/chromeos/login/ui/login_display_host_webui.cc
@@ -654,7 +654,6 @@
 void LoginDisplayHostWebUI::OnStartArcKiosk() {
   finalize_animation_type_ = ANIMATION_FADE_OUT;
   if (!login_window_) {
-    LoadURL(GURL(kAppLaunchSplashURL));
     LoadURL(GURL(kArcKioskSplashURL));
   }
 
diff --git a/chrome/browser/chromeos/login/webview_login_browsertest.cc b/chrome/browser/chromeos/login/webview_login_browsertest.cc
index 8df3f4d5..b131575 100644
--- a/chrome/browser/chromeos/login/webview_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/webview_login_browsertest.cc
@@ -99,11 +99,11 @@
 
   base::RunLoop run_loop;
   cookie_manager->SetCanonicalCookie(
-      net::CanonicalCookie(kTestCookieName, kTestCookieValue, kTestCookieHost,
-                           "/", base::Time(), base::Time(), base::Time(), false,
-                           false, net::CookieSameSite::NO_RESTRICTION,
-                           net::COOKIE_PRIORITY_MEDIUM),
-      "http", net::CookieOptions(),
+      net::CanonicalCookie(
+          kTestCookieName, kTestCookieValue, kTestCookieHost, "/", base::Time(),
+          base::Time(), base::Time(), true /* secure */, false /* httponly*/,
+          net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_MEDIUM),
+      "https", net::CookieOptions(),
       base::Bind(&InjectCookieDoneCallback, run_loop.QuitClosure()));
   run_loop.Run();
 }
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
index 6bd3d81..28ae56a 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.cc
@@ -436,21 +436,17 @@
 void CloudExternalDataManagerBase::Connect(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!external_policy_data_fetcher_backend_);
 
-  external_policy_data_fetcher_backend_ =
-      std::make_unique<ExternalPolicyDataFetcherBackend>(
-          std::move(url_loader_factory));
   backend_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&Backend::Connect, base::Unretained(backend_.get()),
-                     external_policy_data_fetcher_backend_->CreateFrontend(
-                         backend_task_runner_)));
+                     std::make_unique<ExternalPolicyDataFetcher>(
+                         std::move(url_loader_factory), backend_task_runner_)));
 }
 
 void CloudExternalDataManagerBase::Disconnect() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  external_policy_data_fetcher_backend_.reset();
+
   backend_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&Backend::Disconnect, base::Unretained(backend_.get())));
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h
index 82606619..31849475 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h
+++ b/chrome/browser/chromeos/policy/cloud_external_data_manager_base.h
@@ -21,7 +21,6 @@
 namespace policy {
 
 class CloudExternalDataStore;
-class ExternalPolicyDataFetcherBackend;
 
 // Downloads, verifies, caches and retrieves external data referenced by
 // policies.
@@ -72,14 +71,6 @@
   scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
 
  private:
-  // The |external_policy_data_fetcher_backend_| handles network I/O for the
-  // |backend_| because URLRequestContextGetter and URLFetchers cannot be
-  // referenced from background threads. It is instantiated on the thread |this|
-  // runs on but after that, must only be accessed and eventually destroyed via
-  // the |io_task_runner_|.
-  std::unique_ptr<ExternalPolicyDataFetcherBackend>
-      external_policy_data_fetcher_backend_;
-
   // The |backend_| handles all data download scheduling, verification, caching
   // and retrieval. It is instantiated on the thread |this| runs on but after
   // that, must only be accessed and eventually destroyed via the
diff --git a/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc b/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc
index bbc3ed1..1bb5d08 100644
--- a/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager_browsertest.cc
@@ -186,9 +186,8 @@
   EXPECT_EQ(0, ComputeExternalDataCacheDirectorySize());
 }
 
-// Disabled. See https://crbug.com/1008275.
 IN_PROC_BROWSER_TEST_F(DevicePolicyCloudExternalDataManagerTest,
-                       DISABLED_CleanUpResourceCache) {
+                       CleanUpResourceCache) {
   EXPECT_EQ(0, ComputeExternalDataCacheDirectorySize());
 
   std::string external_data = ReadExternalDataFile(kExternalDataPath);
@@ -207,6 +206,10 @@
             ComputeExternalDataCacheDirectorySize());
 
   ClearDeviceNativePrintersExternalData();
+  // We have to wait until
+  // CloudExternalDataManagerBase::Backend::OnMetadataUpdated(), which is
+  // responsible for removing outdated external policy files, is completed.
+  content::RunAllTasksUntilIdle();
   // Check that policy data was cleared.
   EXPECT_EQ(0, ComputeExternalDataCacheDirectorySize());
 }
diff --git a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
index 187ac341..433d4333 100644
--- a/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
@@ -961,6 +961,31 @@
                       nullptr);
       }
     }
+    if (container.has_login_screen_caret_highlight_enabled()) {
+      PolicyLevel level;
+      if (GetPolicyLevel(
+              container.has_login_screen_caret_highlight_enabled_options(),
+              container.login_screen_caret_highlight_enabled_options(),
+              &level)) {
+        policies->Set(key::kDeviceLoginScreenCaretHighlightEnabled, level,
+                      POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
+                      std::make_unique<base::Value>(
+                          container.login_screen_caret_highlight_enabled()),
+                      nullptr);
+      }
+    }
+    if (container.has_login_screen_mono_audio_enabled()) {
+      PolicyLevel level;
+      if (GetPolicyLevel(
+              container.has_login_screen_mono_audio_enabled_options(),
+              container.login_screen_mono_audio_enabled_options(), &level)) {
+        policies->Set(key::kDeviceLoginScreenMonoAudioEnabled, level,
+                      POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
+                      std::make_unique<base::Value>(
+                          container.login_screen_mono_audio_enabled()),
+                      nullptr);
+      }
+    }
   }
 }
 
diff --git a/chrome/browser/chromeos/policy/login_profile_policy_provider.cc b/chrome/browser/chromeos/policy/login_profile_policy_provider.cc
index e0547de8b..1928551 100644
--- a/chrome/browser/chromeos/policy/login_profile_policy_provider.cc
+++ b/chrome/browser/chromeos/policy/login_profile_policy_provider.cc
@@ -48,6 +48,8 @@
     {key::kDeviceLoginScreenSelectToSpeakEnabled, key::kSelectToSpeakEnabled},
     {key::kDeviceLoginScreenCursorHighlightEnabled,
      key::kCursorHighlightEnabled},
+    {key::kDeviceLoginScreenCaretHighlightEnabled, key::kCaretHighlightEnabled},
+    {key::kDeviceLoginScreenMonoAudioEnabled, key::kMonoAudioEnabled},
 };
 
 const DevicePolicyToUserPolicyMapEntry kRecommendedDevicePoliciesMap[] = {
diff --git a/chrome/browser/chromeos/policy/login_screen_accessibility_policy_browsertest.cc b/chrome/browser/chromeos/policy/login_screen_accessibility_policy_browsertest.cc
index b8549ba..0d3e9c9 100644
--- a/chrome/browser/chromeos/policy/login_screen_accessibility_policy_browsertest.cc
+++ b/chrome/browser/chromeos/policy/login_screen_accessibility_policy_browsertest.cc
@@ -647,4 +647,108 @@
   accessibility_manager->SetCursorHighlightEnabled(false);
   EXPECT_FALSE(accessibility_manager->IsCursorHighlightEnabled());
 }
+
+IN_PROC_BROWSER_TEST_F(LoginScreenAccessibilityPolicyBrowsertest,
+                       DeviceLoginScreenCaretHighlightEnabled) {
+  // Verifies that the state of the caret highlight accessibility feature on
+  // the login screen can be controlled through device policy.
+  chromeos::AccessibilityManager* accessibility_manager =
+      chromeos::AccessibilityManager::Get();
+  ASSERT_TRUE(accessibility_manager);
+  EXPECT_FALSE(accessibility_manager->IsCaretHighlightEnabled());
+
+  // Manually enable the caret highlight.
+  accessibility_manager->SetCaretHighlightEnabled(true);
+  EXPECT_TRUE(accessibility_manager->IsCaretHighlightEnabled());
+
+  // Disable the caret highlight through device policy and wait for the change
+  // to take effect.
+  em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
+  proto.mutable_accessibility_settings()
+      ->set_login_screen_caret_highlight_enabled(false);
+  RefreshDevicePolicyAndWaitForPrefChange(
+      ash::prefs::kAccessibilityCaretHighlightEnabled);
+
+  // Verify that the pref which controls the caret highlight in the login
+  // profile is managed by the policy.
+  EXPECT_TRUE(IsPrefManaged(ash::prefs::kAccessibilityCaretHighlightEnabled));
+  EXPECT_EQ(base::Value(false),
+            GetPrefValue(ash::prefs::kAccessibilityCaretHighlightEnabled));
+
+  // Verify that the caret highlight cannot be enabled manually anymore.
+  accessibility_manager->SetCaretHighlightEnabled(true);
+  EXPECT_FALSE(accessibility_manager->IsCaretHighlightEnabled());
+
+  // Enable the caret highlight through device policy as a recommended value and
+  // wait for the change to take effect.
+  proto.mutable_accessibility_settings()
+      ->set_login_screen_caret_highlight_enabled(true);
+  proto.mutable_accessibility_settings()
+      ->mutable_login_screen_caret_highlight_enabled_options()
+      ->set_mode(em::PolicyOptions::RECOMMENDED);
+  RefreshDevicePolicyAndWaitForPrefChange(
+      ash::prefs::kAccessibilityCaretHighlightEnabled);
+
+  // Verify that the pref which controls the caret highlight in the login
+  // profile is being applied as recommended by the policy.
+  EXPECT_FALSE(IsPrefManaged(ash::prefs::kAccessibilityCaretHighlightEnabled));
+  EXPECT_EQ(base::Value(true),
+            GetPrefValue(ash::prefs::kAccessibilityCaretHighlightEnabled));
+
+  // Verify that the caret highlight can be enabled manually again.
+  accessibility_manager->SetCaretHighlightEnabled(false);
+  EXPECT_FALSE(accessibility_manager->IsCaretHighlightEnabled());
+}
+
+IN_PROC_BROWSER_TEST_F(LoginScreenAccessibilityPolicyBrowsertest,
+                       DeviceLoginScreenMonoAudioEnabled) {
+  // Verifies that the state of the mono audio accessibility feature on
+  // the login screen can be controlled through device policy.
+  chromeos::AccessibilityManager* accessibility_manager =
+      chromeos::AccessibilityManager::Get();
+  ASSERT_TRUE(accessibility_manager);
+  EXPECT_FALSE(accessibility_manager->IsMonoAudioEnabled());
+
+  // Manually enable the mono audio.
+  accessibility_manager->EnableMonoAudio(true);
+  EXPECT_TRUE(accessibility_manager->IsMonoAudioEnabled());
+
+  // Disable the mono audio through device policy and wait for the change
+  // to take effect.
+  em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
+  proto.mutable_accessibility_settings()->set_login_screen_mono_audio_enabled(
+      false);
+  RefreshDevicePolicyAndWaitForPrefChange(
+      ash::prefs::kAccessibilityMonoAudioEnabled);
+
+  // Verify that the pref which controls the mono audio in the login
+  // profile is managed by the policy.
+  EXPECT_TRUE(IsPrefManaged(ash::prefs::kAccessibilityMonoAudioEnabled));
+  EXPECT_EQ(base::Value(false),
+            GetPrefValue(ash::prefs::kAccessibilityMonoAudioEnabled));
+
+  // Verify that the mono audio cannot be enabled manually anymore.
+  accessibility_manager->EnableMonoAudio(true);
+  EXPECT_FALSE(accessibility_manager->IsMonoAudioEnabled());
+
+  // Enable the mono audio through device policy as a recommended value and wait
+  // for the change to take effect.
+  proto.mutable_accessibility_settings()->set_login_screen_mono_audio_enabled(
+      true);
+  proto.mutable_accessibility_settings()
+      ->mutable_login_screen_mono_audio_enabled_options()
+      ->set_mode(em::PolicyOptions::RECOMMENDED);
+  RefreshDevicePolicyAndWaitForPrefChange(
+      ash::prefs::kAccessibilityMonoAudioEnabled);
+
+  // Verify that the pref which controls the mono audio in the login
+  // profile is being applied as recommended by the policy.
+  EXPECT_FALSE(IsPrefManaged(ash::prefs::kAccessibilityMonoAudioEnabled));
+  EXPECT_EQ(base::Value(true),
+            GetPrefValue(ash::prefs::kAccessibilityMonoAudioEnabled));
+
+  // Verify that the mono audio can be enabled manually again.
+  accessibility_manager->EnableMonoAudio(false);
+  EXPECT_FALSE(accessibility_manager->IsMonoAudioEnabled());
+}
 }  // namespace policy
diff --git a/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
index f8fc80c5..4a84d88 100644
--- a/chrome/browser/extensions/api/sessions/sessions_apitest.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_apitest.cc
@@ -27,7 +27,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_browser_process.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/engine/data_type_activation_response.h"
 #include "components/sync/model/data_type_activation_request.h"
 #include "components/sync/model/model_type_controller_delegate.h"
@@ -144,8 +144,9 @@
   return testing::AssertionSuccess();
 }
 
-std::string TagHashFromSpecifics(const sync_pb::SessionSpecifics& specifics) {
-  return syncer::GenerateSyncableHash(
+syncer::ClientTagHash TagHashFromSpecifics(
+    const sync_pb::SessionSpecifics& specifics) {
+  return syncer::ClientTagHash::FromUnhashed(
       syncer::SESSIONS, sync_sessions::SessionStore::GetClientTag(specifics));
 }
 
@@ -236,7 +237,8 @@
     auto header_entity_data = std::make_unique<syncer::EntityData>();
     header_entity_data->client_tag_hash =
         TagHashFromSpecifics(header_entity.session());
-    header_entity_data->id = "FakeId:" + header_entity_data->client_tag_hash;
+    header_entity_data->id =
+        "FakeId:" + header_entity_data->client_tag_hash.value();
     header_entity_data->specifics = header_entity;
     header_entity_data->creation_time =
         time_now - base::TimeDelta::FromSeconds(index);
diff --git a/chrome/browser/extensions/chrome_extensions_interface_registration.cc b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
index b334d2e0..17c4d53 100644
--- a/chrome/browser/extensions/chrome_extensions_interface_registration.cc
+++ b/chrome/browser/extensions/chrome_extensions_interface_registration.cc
@@ -83,7 +83,8 @@
     uint32_t intent_id,
     arc::mojom::CameraIntentAction action,
     const std::vector<uint8_t>& data,
-    cros::mojom::CameraAppHelper::HandleCameraResultCallback callback) {
+    chromeos_camera::mojom::CameraAppHelper::HandleCameraResultCallback
+        callback) {
   auto* intent_helper =
       arc::ArcIntentHelperBridge::GetForBrowserContext(context);
   intent_helper->HandleCameraResult(intent_id, action, data,
@@ -118,8 +119,9 @@
 }
 
 // Connects to CameraAppHelper that could handle camera intents.
-void ConnectToCameraAppHelper(cros::mojom::CameraAppHelperRequest request,
-                              content::RenderFrameHost* source) {
+void ConnectToCameraAppHelper(
+    chromeos_camera::mojom::CameraAppHelperRequest request,
+    content::RenderFrameHost* source) {
   auto handle_result_callback = base::BindRepeating(
       &HandleCameraResult, source->GetProcess()->GetBrowserContext());
   auto camera_app_helper =
diff --git a/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc b/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
index 466f51b..e09ff30 100644
--- a/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
@@ -327,8 +327,16 @@
   TestVideoQuality("VP8", false /* prefer_hw_video_codec */);
 }
 
+// Flaky on windows.
+// TODO(crbug.com/1008766): re-enable when flakiness is investigated, diagnosed
+// and resolved.
+#if defined(OS_WIN)
+#define MAYBE_MANUAL_TestVideoQualityVp9 DISABLED_MANUAL_TestVideoQualityVp9
+#else
+#define MAYBE_MANUAL_TestVideoQualityVp9 MANUAL_TestVideoQualityVp9
+#endif
 IN_PROC_BROWSER_TEST_P(WebRtcVideoQualityBrowserTest,
-                       MANUAL_TestVideoQualityVp9) {
+                       MAYBE_MANUAL_TestVideoQualityVp9) {
   base::ScopedAllowBlockingForTesting allow_blocking;
   TestVideoQuality("VP9", true /* prefer_hw_video_codec */);
 }
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
index 7791e23d..30152a2 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.cc
@@ -109,6 +109,10 @@
   }
 
   // TODO(csharrison): Optimize the domain lookup.
+  // Note: If either |url| or |first_party_url| is empty, SameDomainOrHost will
+  // return false, and function execution will continue because it is considered
+  // 3rd party. Since |first_party_url| is actually the |site_for_cookies|, this
+  // will happen e.g. for a 3rd party iframe on document.cookie access.
   if (!url.is_valid() ||
       net::registry_controlled_domains::SameDomainOrHost(
           url, first_party_url,
@@ -120,8 +124,17 @@
       net::registry_controlled_domains::GetDomainAndRegistry(
           url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
 
-  if (registrable_domain.empty())
-    return;
+  // |registrable_domain| can be empty e.g. if |url| is on an IP address, or the
+  // domain is itself a TLD, or it's a file URL (in which case it has no host),
+  // etc. See comment for GetDomainAndRegistry() in
+  // //net/base/registry_controlled_domains/registry_controlled_domains.h.
+  if (registrable_domain.empty()) {
+    if (url.has_host()) {
+      registrable_domain = url.host();
+    } else {
+      return;
+    }
+  }
 
   auto it = third_party_cookie_access_types_.find(registrable_domain);
 
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
index 3e4e9bdd..08453cc 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h
@@ -66,8 +66,8 @@
   // third party document.cookie access happens when the context's registrable
   // domain differs from the main frame's. A third party resource request
   // happens when the URL request's registrable domain differs from the main
-  // frame's. URLs which have no registrable domain are not considered third
-  // party.
+  // frame's. For URLs which have no registrable domain, the hostname is used
+  // instead.
   std::map<std::string, CookieAccessTypes> third_party_cookie_access_types_;
 
   // A map of third parties that have accessed storage other than cookies. A
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc
index afc7615..b7ed0e5 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc
@@ -6,8 +6,10 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/network_session_configurator/common/network_switches.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -25,12 +27,20 @@
 
 class ThirdPartyMetricsObserverBrowserTest : public InProcessBrowserTest {
  protected:
-  ThirdPartyMetricsObserverBrowserTest() = default;
+  ThirdPartyMetricsObserverBrowserTest()
+      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
   ~ThirdPartyMetricsObserverBrowserTest() override = default;
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
-    ASSERT_TRUE(embedded_test_server()->Start());
+    https_server()->AddDefaultHandlers(GetChromeTestDataDir());
+    ASSERT_TRUE(https_server()->Start());
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // HTTPS server only serves a valid cert for 127.0.0.1 or localhost, so this
+    // is needed to load pages from other hosts (b.com, c.com) without an error.
+    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
   }
 
   void NavigateToUntrackedUrl() {
@@ -38,19 +48,29 @@
   }
 
   void NavigateToPageWithFrame(const std::string& host) {
-    GURL main_url(embedded_test_server()->GetURL(host, "/iframe.html"));
+    GURL main_url(https_server()->GetURL(host, "/iframe.html"));
     ui_test_utils::NavigateToURL(browser(), main_url);
   }
 
   void NavigateFrameTo(const std::string& host, const std::string& path) {
-    GURL page = embedded_test_server()->GetURL(host, path);
-    EXPECT_TRUE(NavigateIframeToURL(web_contents(), "test", page));
+    GURL page = https_server()->GetURL(host, path);
+    NavigateFrameToUrl(page);
+  }
+
+  void NavigateFrameToUrl(const GURL& url) {
+    EXPECT_TRUE(NavigateIframeToURL(web_contents(), "test", url));
   }
 
   content::WebContents* web_contents() {
     return browser()->tab_strip_model()->GetActiveWebContents();
   }
 
+  net::EmbeddedTestServer* https_server() { return &https_server_; }
+
+  // This is needed because third party cookies must be marked SameSite=None and
+  // Secure, so they must be accessed over HTTPS.
+  net::EmbeddedTestServer https_server_;
+
   DISALLOW_COPY_AND_ASSIGN(ThirdPartyMetricsObserverBrowserTest);
 };
 
@@ -80,8 +100,29 @@
                        ThirdPartyCookiesReadAndWrite) {
   base::HistogramTester histogram_tester;
   NavigateToPageWithFrame("a.com");  // Same origin cookie read.
-  NavigateFrameTo("b.com", "/set-cookie?thirdparty");  // 3p cookie write
-  NavigateFrameTo("b.com", "/");                       // 3p cookie read
+  // 3p cookie write
+  NavigateFrameTo("b.com", "/set-cookie?thirdparty=1;SameSite=None;Secure");
+  // 3p cookie read
+  NavigateFrameTo("b.com", "/");
+  NavigateToUntrackedUrl();
+
+  histogram_tester.ExpectUniqueSample(kReadCookieHistogram, 1, 1);
+  histogram_tester.ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(ThirdPartyMetricsObserverBrowserTest,
+                       ThirdPartyCookiesIPAddress) {
+  base::HistogramTester histogram_tester;
+  NavigateToPageWithFrame("a.com");  // Same origin cookie read.
+  GURL url =
+      https_server()->GetURL("/set-cookie?thirdparty=1;SameSite=None;Secure");
+  // Hostname is an IP address.
+  ASSERT_EQ(
+      "",
+      net::registry_controlled_domains::GetDomainAndRegistry(
+          url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
+  NavigateFrameToUrl(url);           // 3p cookie write
+  NavigateFrameTo(url.host(), "/");  // 3p cookie read
   NavigateToUntrackedUrl();
 
   histogram_tester.ExpectUniqueSample(kReadCookieHistogram, 1, 1);
@@ -92,10 +133,14 @@
                        MultipleThirdPartyCookiesReadAndWrite) {
   base::HistogramTester histogram_tester;
   NavigateToPageWithFrame("a.com");  // Same origin cookie read.
-  NavigateFrameTo("b.com", "/set-cookie?thirdparty");  // 3p cookie write
-  NavigateFrameTo("b.com", "/");                       // 3p cookie read
-  NavigateFrameTo("c.com", "/set-cookie?thirdparty");  // 3p cookie write
-  NavigateFrameTo("c.com", "/");                       // 3p cookie read
+  // 3p cookie write
+  NavigateFrameTo("b.com", "/set-cookie?thirdparty=1;SameSite=None;Secure");
+  // 3p cookie read
+  NavigateFrameTo("b.com", "/");
+  // 3p cookie write
+  NavigateFrameTo("c.com", "/set-cookie?thirdparty=1;SameSite=None;Secure");
+  // 3p cookie read
+  NavigateFrameTo("c.com", "/");
   NavigateToUntrackedUrl();
 
   histogram_tester.ExpectUniqueSample(kReadCookieHistogram, 2, 1);
@@ -130,7 +175,8 @@
       ChildFrameAt(web_contents()->GetMainFrame(), 0);
 
   // Write a third-party cookie.
-  EXPECT_TRUE(content::ExecJs(frame, "document.cookie = 'foo=bar';"));
+  EXPECT_TRUE(content::ExecJs(
+      frame, "document.cookie = 'foo=bar;SameSite=None;Secure';"));
 
   // Read a third-party cookie.
   EXPECT_TRUE(content::ExecJs(frame, "let x = document.cookie;"));
@@ -166,7 +212,8 @@
       ChildFrameAt(web_contents()->GetMainFrame(), 0);
 
   // Write a third-party cookie.
-  EXPECT_TRUE(content::ExecJs(frame, "document.cookie = 'foo=bar';"));
+  EXPECT_TRUE(content::ExecJs(
+      frame, "document.cookie = 'foo=bar;SameSite=None;Secure';"));
   NavigateToUntrackedUrl();
 
   histogram_tester.ExpectUniqueSample(kReadCookieHistogram, 0, 1);
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
index 3075dce..418d49d 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
@@ -54,16 +54,31 @@
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
-       NoRegistrableDomainCookiesRead_NoneRecorded) {
+       NoRegistrableDomainNoHostCookiesRead_NoneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookiesRead(GURL("data:,Hello%2C%20World!"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
+  GURL url = GURL("data:,Hello%2C%20World!");
+  ASSERT_FALSE(url.has_host());
+  SimulateCookiesRead(url, GURL("https://top.com"), net::CookieList(),
+                      false /* blocked_by_policy */);
   NavigateToUntrackedUrl();
 
   histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
 }
 
+TEST_F(ThirdPartyMetricsObserverTest,
+       NoRegistrableDomainWithHostCookiesRead_OneRecorded) {
+  NavigateAndCommit(GURL("https://top.com"));
+
+  GURL url = GURL("https://127.0.0.1/cookies");
+  ASSERT_TRUE(url.has_host());
+  SimulateCookiesRead(url, GURL("https://top.com"), net::CookieList(),
+                      false /* blocked_by_policy */);
+  NavigateToUntrackedUrl();
+
+  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
+}
+
 TEST_F(ThirdPartyMetricsObserverTest, OnlyFirstPartyCookiesRead_NotRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
@@ -138,16 +153,30 @@
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
-       NoRegistrableDomainCookiesChanged_NoneRecorded) {
+       NoRegistrableDomainNoHostCookiesChanged_NoneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookieChange(GURL("data:,Hello%2C%20World!"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
+  GURL url = GURL("data:,Hello%2C%20World!");
+  ASSERT_FALSE(url.has_host());
+  SimulateCookieChange(url, GURL("https://top.com"), net::CanonicalCookie(),
+                       false /* blocked_by_policy */);
   NavigateToUntrackedUrl();
   histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
+       NoRegistrableDomainWithHostCookiesChanged_OneRecorded) {
+  NavigateAndCommit(GURL("https://top.com"));
+
+  GURL url = GURL("https://127.0.0.1/cookies");
+  ASSERT_TRUE(url.has_host());
+  SimulateCookieChange(url, GURL("https://top.com"), net::CanonicalCookie(),
+                       false /* blocked_by_policy */);
+  NavigateToUntrackedUrl();
+  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
+}
+
+TEST_F(ThirdPartyMetricsObserverTest,
        OnlyFirstPartyCookiesChanged_NotRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index ae21c2c..fce5072 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -781,7 +781,6 @@
   ManualFillingControllerImpl::CreateForWebContentsForTesting(
       web_contents, favicon_service_.get(), mock_pwd_controller_.AsWeakPtr(),
       mock_address_controller_.AsWeakPtr(), mock_cc_controller_.AsWeakPtr(),
-      GetClient()->GetOrCreateTouchToFillController()->AsWeakPtr(),
       std::make_unique<NiceMock<MockManualFillingView>>());
 }
 
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index d7acbd7..56dae9d 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -329,9 +329,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, LoginFailed) {
-  // Log in, see a form on the landing page. That form is not related to the
-  // login form (=has a different action), so we should offer saving the
-  // password.
   NavigateToFile("/password/password_form.html");
 
   NavigationObserver observer(WebContents());
diff --git a/chrome/browser/password_manager/touch_to_fill_controller.cc b/chrome/browser/password_manager/touch_to_fill_controller.cc
index cf656f5d..19f8f88 100644
--- a/chrome/browser/password_manager/touch_to_fill_controller.cc
+++ b/chrome/browser/password_manager/touch_to_fill_controller.cc
@@ -4,117 +4,51 @@
 
 #include "chrome/browser/password_manager/touch_to_fill_controller.h"
 
-#include <string>
 #include <utility>
 
-#include "base/feature_list.h"
 #include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/autofill/manual_filling_controller.h"
-#include "chrome/browser/ui/autofill/autofill_popup_controller.h"
-#include "components/autofill/core/browser/ui/popup_item_ids.h"
-#include "components/autofill/core/browser/ui/suggestion.h"
-#include "components/autofill/core/common/autofill_util.h"
+#include "chrome/browser/touch_to_fill/touch_to_fill_view.h"
 #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
 #include "components/password_manager/core/browser/origin_credential_store.h"
 #include "components/password_manager/core/browser/password_manager_driver.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
+#include "components/url_formatter/elide_url.h"
+#include "content/public/browser/web_contents.h"
 
-using content::WebContents;
+using password_manager::CredentialPair;
+using password_manager::PasswordManagerDriver;
 
-TouchToFillController::TouchToFillController(WebContents* web_contents)
-    : web_contents_(web_contents) {
-  DCHECK(web_contents);
+namespace {
+
+void OnCredentialSelected(base::WeakPtr<PasswordManagerDriver> driver,
+                          const CredentialPair& credential) {
+  if (!driver)
+    return;
+
+  password_manager::metrics_util::LogFilledCredentialIsFromAndroidApp(
+      password_manager::IsValidAndroidFacetURI(credential.origin_url.spec()));
+  driver->FillSuggestion(credential.username, credential.password);
 }
 
-TouchToFillController::TouchToFillController(
-    base::WeakPtr<ManualFillingController> mf_controller,
-    util::PassKey<TouchToFillControllerTest>)
-    : mf_controller_(std::move(mf_controller)) {
-  DCHECK(mf_controller_);
-}
+}  // namespace
+
+TouchToFillController::TouchToFillController(content::WebContents* web_contents)
+    : web_contents_(web_contents) {}
 
 TouchToFillController::~TouchToFillController() = default;
 
-void TouchToFillController::Show(
-    base::span<const password_manager::CredentialPair> credentials,
-    base::WeakPtr<password_manager::PasswordManagerDriver> driver) {
-  credentials_.assign(credentials.begin(), credentials.end());
-  driver_ = std::move(driver);
+void TouchToFillController::Show(base::span<const CredentialPair> credentials,
+                                 base::WeakPtr<PasswordManagerDriver> driver) {
+  if (!view_)
+    view_ = TouchToFillViewFactory::Create(this);
 
-  autofill::AccessorySheetData::Builder builder(
-      autofill::AccessoryTabType::TOUCH_TO_FILL,
-      // TODO(crbug.com/957532): Update title once mocks are finalized.
-      base::ASCIIToUTF16("Touch to Fill"));
-
-  for (size_t i = 0; i < credentials_.size(); ++i) {
-    const auto& credential = credentials_[i];
-    if (credential.is_public_suffix_match)
-      builder.AddUserInfo(credential.origin_url.spec());
-    else
-      builder.AddUserInfo();
-
-    std::string field_id = base::NumberToString(i);
-    builder.AppendField(credential.username, credential.username, field_id,
-                        /*is_obfuscated=*/false,
-                        /*is_selectable=*/true);
-    builder.AppendField(credential.password, credential.password,
-                        std::move(field_id),
-                        /*is_obfuscated=*/true,
-                        /*is_selectable=*/false);
-  }
-
-  GetManualFillingController()->RefreshSuggestions(std::move(builder).Build());
-}
-
-void TouchToFillController::OnFillingTriggered(
-    const autofill::UserInfo::Field& selection) {
-  if (!driver_) {
-    LOG(DFATAL) << "|driver_| is not set or has been invalidated.";
-    return;
-  }
-
-  size_t index = 0;
-  if (!base::StringToSizeT(selection.id(), &index)) {
-    LOG(DFATAL) << "Failed to convert selection.id(): " << selection.id();
-    return;
-  }
-
-  if (index >= credentials_.size()) {
-    LOG(DFATAL) << "Received invalid suggestion index: " << index;
-    return;
-  }
-
-  const auto& credential = credentials_[index];
-  password_manager::metrics_util::LogFilledCredentialIsFromAndroidApp(
-      password_manager::IsValidAndroidFacetURI(credential.origin_url.spec()));
-  // Invalidate |driver_| and |credentials_| to ignore future invocations of
-  // OnFillingTriggered for the same credentials.
-  std::exchange(driver_, nullptr)
-      ->FillSuggestion(credential.username, credential.password);
-  credentials_.clear();
-
-  mf_controller_->UpdateSourceAvailability(
-      ManualFillingController::FillingSource::TOUCH_TO_FILL,
-      /*has_suggestions=*/false);
-}
-
-void TouchToFillController::OnOptionSelected(
-    autofill::AccessoryAction selected_action) {
-  // Not applicable for TouchToFillController. All user interactions should
-  // result in OnFillingTriggered().
-  NOTREACHED();
+  view_->Show(url_formatter::FormatUrlForSecurityDisplay(
+                  driver->GetLastCommittedURL(),
+                  url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC),
+              credentials,
+              base::BindOnce(OnCredentialSelected, std::move(driver)));
 }
 
 gfx::NativeView TouchToFillController::GetNativeView() {
   return web_contents_->GetNativeView();
 }
-
-ManualFillingController* TouchToFillController::GetManualFillingController() {
-  if (!mf_controller_)
-    mf_controller_ = ManualFillingController::GetOrCreate(web_contents_);
-  DCHECK(mf_controller_);
-  return mf_controller_.get();
-}
diff --git a/chrome/browser/password_manager/touch_to_fill_controller.h b/chrome/browser/password_manager/touch_to_fill_controller.h
index 36389a09..fdc3556 100644
--- a/chrome/browser/password_manager/touch_to_fill_controller.h
+++ b/chrome/browser/password_manager/touch_to_fill_controller.h
@@ -6,69 +6,54 @@
 #define CHROME_BROWSER_PASSWORD_MANAGER_TOUCH_TO_FILL_CONTROLLER_H_
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/containers/span.h"
 #include "base/memory/weak_ptr.h"
-#include "base/util/type_safety/pass_key.h"
-#include "chrome/browser/autofill/accessory_controller.h"
+#include "chrome/browser/touch_to_fill/touch_to_fill_view.h"
+#include "chrome/browser/touch_to_fill/touch_to_fill_view_factory.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace content {
 class WebContents;
-}
+}  // namespace content
 
 namespace password_manager {
 class PasswordManagerDriver;
 struct CredentialPair;
 }  // namespace password_manager
 
-class ManualFillingController;
-class TouchToFillControllerTest;
-
-class TouchToFillController
-    : public base::SupportsWeakPtr<TouchToFillController>,
-      public AccessoryController {
+class TouchToFillController {
  public:
   explicit TouchToFillController(content::WebContents* web_contents);
-  // Explicitly set the ManualFillingController in unit tests.
-  explicit TouchToFillController(
-      base::WeakPtr<ManualFillingController> mf_controller,
-      util::PassKey<TouchToFillControllerTest>);
-  ~TouchToFillController() override;
+  TouchToFillController(const TouchToFillController&) = delete;
+  TouchToFillController& operator=(const TouchToFillController&) = delete;
+  ~TouchToFillController();
 
   // Instructs the controller to show the provided |credentials| to the user.
   // Invokes FillSuggestion() on |driver| once the user made a selection.
   void Show(base::span<const password_manager::CredentialPair> credentials,
             base::WeakPtr<password_manager::PasswordManagerDriver> driver);
 
-  // AccessoryController:
-  void OnFillingTriggered(const autofill::UserInfo::Field& selection) override;
-  void OnOptionSelected(autofill::AccessoryAction selected_action) override;
-
   // The web page view containing the focused field.
   gfx::NativeView GetNativeView();
 
- private:
-  // Lazy-initializes and returns the ManualFillingController for the current
-  // |web_contents_|. The lazy initialization is required to break a circular
-  // dependency between the constructors of the TouchToFillController and
-  // ManualFillingController.
-  ManualFillingController* GetManualFillingController();
+#if defined(UNIT_TEST)
+  void set_view(std::unique_ptr<TouchToFillView> view) {
+    view_ = std::move(view);
+  }
+#endif
 
-  // The tab for which this class is scoped.
+ private:
+  // Weak pointer to the current WebContents. This is safe because the lifetime
+  // of this class is tied to ChromePasswordManagerClient, which implements
+  // WebContentsUserData.
   content::WebContents* web_contents_ = nullptr;
 
-  // The manual filling controller object to forward client requests to.
-  // TODO(crbug.com/957532): Make TouchToFillController independent of the
-  // ManualFillingController.
-  base::WeakPtr<ManualFillingController> mf_controller_;
-
-  // Credentials passed from the latest invocation of Show().
-  std::vector<password_manager::CredentialPair> credentials_;
-
-  // Driver passed from the latest invocation of Show().
-  base::WeakPtr<password_manager::PasswordManagerDriver> driver_;
+  // View used to communicate with the Android frontend. Lazily instantiated so
+  // that it can be injected by tests.
+  std::unique_ptr<TouchToFillView> view_;
 };
 
 #endif  // CHROME_BROWSER_PASSWORD_MANAGER_TOUCH_TO_FILL_CONTROLLER_H_
diff --git a/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
index 234107b..888e71f 100644
--- a/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
+++ b/chrome/browser/password_manager/touch_to_fill_controller_unittest.cc
@@ -4,163 +4,118 @@
 
 #include "chrome/browser/password_manager/touch_to_fill_controller.h"
 
+#include <memory>
+#include <tuple>
+
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/autofill/mock_manual_filling_controller.h"
-#include "components/autofill/core/common/autofill_features.h"
 #include "components/password_manager/core/browser/origin_credential_store.h"
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace {
+
 using password_manager::CredentialPair;
+using ::testing::_;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::ReturnRefOfCopy;
+using ::testing::WithArg;
+
+// Re-implementation of ::testing::SaveArg that works with move-only types.
+template <size_t K, typename T>
+auto SaveArg(T* ptr) {
+  return [ptr](auto&&... args) {
+    *ptr = std::get<K>(
+        std::forward_as_tuple(std::forward<decltype(args)>(args)...));
+  };
+}
+
+constexpr char kExampleCom[] = "https://example.com/";
 
 struct MockPasswordManagerDriver : password_manager::StubPasswordManagerDriver {
   MOCK_METHOD2(FillSuggestion,
                void(const base::string16&, const base::string16&));
+  MOCK_CONST_METHOD0(GetLastCommittedURL, const GURL&());
 };
 
+struct MockTouchToFillView : TouchToFillView {
+  MOCK_METHOD3(Show,
+               void(base::StringPiece16,
+                    base::span<const password_manager::CredentialPair>,
+                    ShowCallback));
+  MOCK_METHOD0(OnDismiss, void());
+};
+
+}  // namespace
+
 class TouchToFillControllerTest : public testing::Test {
  protected:
-  MockManualFillingController& manual_filling_controller() {
-    return mock_manual_filling_controller_;
+  TouchToFillControllerTest() {
+    auto mock_view = std::make_unique<MockTouchToFillView>();
+    mock_view_ = mock_view.get();
+    touch_to_fill_controller_.set_view(std::move(mock_view));
+
+    ON_CALL(driver_, GetLastCommittedURL())
+        .WillByDefault(ReturnRefOfCopy(GURL(kExampleCom)));
   }
 
   MockPasswordManagerDriver& driver() { return driver_; }
 
+  MockTouchToFillView& view() { return *mock_view_; }
+
   TouchToFillController& touch_to_fill_controller() {
     return touch_to_fill_controller_;
   }
 
  private:
-  testing::StrictMock<MockManualFillingController>
-      mock_manual_filling_controller_;
+  MockTouchToFillView* mock_view_ = nullptr;
   MockPasswordManagerDriver driver_;
-  TouchToFillController touch_to_fill_controller_{
-      mock_manual_filling_controller_.AsWeakPtr(),
-      util::PassKey<TouchToFillControllerTest>()};
+  TouchToFillController touch_to_fill_controller_{nullptr};
 };
 
-TEST_F(TouchToFillControllerTest, Show_Empty) {
-  EXPECT_CALL(manual_filling_controller(),
-              RefreshSuggestions(autofill::AccessorySheetData::Builder(
-                                     autofill::AccessoryTabType::TOUCH_TO_FILL,
-                                     base::ASCIIToUTF16("Touch to Fill"))
-                                     .Build()));
-  touch_to_fill_controller().Show({}, driver().AsWeakPtr());
-}
-
 TEST_F(TouchToFillControllerTest, Show_And_Fill) {
-  CredentialPair alice(
-      base::ASCIIToUTF16("alice"), base::ASCIIToUTF16("p4ssw0rd"),
-      GURL("https://example.com"), /*is_public_suffix_match=*/false);
+  CredentialPair credentials[] = {
+      {base::ASCIIToUTF16("alice"), base::ASCIIToUTF16("p4ssw0rd"),
+       GURL(kExampleCom), /*is_public_suffix_match=*/false}};
 
-  EXPECT_CALL(
-      manual_filling_controller(),
-      RefreshSuggestions(
-          autofill::AccessorySheetData::Builder(
-              autofill::AccessoryTabType::TOUCH_TO_FILL,
-              base::ASCIIToUTF16("Touch to Fill"))
-              .AddUserInfo()
-              .AppendField(base::ASCIIToUTF16("alice"),
-                           base::ASCIIToUTF16("alice"), "0",
-                           /*is_obfuscated=*/false, /*is_selectable=*/true)
-              .AppendField(base::ASCIIToUTF16("p4ssw0rd"),
-                           base::ASCIIToUTF16("p4ssw0rd"), "0",
-                           /*is_obfuscated=*/true, /*is_selectable=*/false)
-              .Build()));
-  touch_to_fill_controller().Show(std::vector<CredentialPair>{alice},
-                                  driver().AsWeakPtr());
-
-  // Test that OnFillingTriggered() with the right id results in the appropriate
-  // call to FillSuggestion() and UpdateSourceAvailability().
-  EXPECT_CALL(driver(), FillSuggestion(base::ASCIIToUTF16("alice"),
-                                       base::ASCIIToUTF16("p4ssw0rd")));
-  EXPECT_CALL(manual_filling_controller(),
-              UpdateSourceAvailability(
-                  ManualFillingController::FillingSource::TOUCH_TO_FILL,
-                  /*has_suggestions=*/false));
+  TouchToFillView::ShowCallback callback;
+  EXPECT_CALL(view(), Show(Eq(base::ASCIIToUTF16("example.com")),
+                           ElementsAreArray(credentials), _))
+      .WillOnce(SaveArg<2>(&callback));
+  touch_to_fill_controller().Show(credentials, driver().AsWeakPtr());
 
   // Test that we correctly log the absence of an Android credential.
   base::HistogramTester tester;
-  touch_to_fill_controller().OnFillingTriggered(autofill::UserInfo::Field(
-      base::ASCIIToUTF16("alice"), base::ASCIIToUTF16("alice"), "0",
-      /*is_obfuscated=*/false, /*is_selectable=*/true));
+  EXPECT_CALL(driver(), FillSuggestion(base::ASCIIToUTF16("alice"),
+                                       base::ASCIIToUTF16("p4ssw0rd")));
+  std::move(callback).Run(credentials[0]);
   tester.ExpectUniqueSample("PasswordManager.FilledCredentialWasFromAndroidApp",
                             false, 1);
 }
 
-TEST_F(TouchToFillControllerTest, Show_PSL_Credential) {
-  // Test that showing a PSL credentials results in the origin being passed to
-  // the manual filling controller.
-  CredentialPair alice(
-      base::ASCIIToUTF16("alice"), base::ASCIIToUTF16("p4ssw0rd"),
-      GURL("https://sub.example.com/"), /*is_public_suffix_match=*/true);
-
-  EXPECT_CALL(
-      manual_filling_controller(),
-      RefreshSuggestions(
-          autofill::AccessorySheetData::Builder(
-              autofill::AccessoryTabType::TOUCH_TO_FILL,
-              base::ASCIIToUTF16("Touch to Fill"))
-              .AddUserInfo("https://sub.example.com/")
-              .AppendField(base::ASCIIToUTF16("alice"),
-                           base::ASCIIToUTF16("alice"), "0",
-                           /*is_obfuscated=*/false, /*is_selectable=*/true)
-              .AppendField(base::ASCIIToUTF16("p4ssw0rd"),
-                           base::ASCIIToUTF16("p4ssw0rd"), "0",
-                           /*is_obfuscated=*/true, /*is_selectable=*/false)
-              .Build()));
-  touch_to_fill_controller().Show(std::vector<CredentialPair>{alice},
-                                  driver().AsWeakPtr());
-}
-
 TEST_F(TouchToFillControllerTest, Show_And_Fill_Android_Credential) {
   // Test multiple credentials with one of them being an Android credential.
-  CredentialPair alice(
-      base::ASCIIToUTF16("alice"), base::ASCIIToUTF16("p4ssw0rd"),
-      GURL("https://example.com"), /*is_public_suffix_match=*/false);
+  CredentialPair credentials[] = {
+      {base::ASCIIToUTF16("alice"), base::ASCIIToUTF16("p4ssw0rd"),
+       GURL(kExampleCom),
+       /*is_public_suffix_match=*/false},
+      {base::ASCIIToUTF16("bob"), base::ASCIIToUTF16("s3cr3t"),
+       GURL("android://hash@com.example.my"),
+       /*is_public_suffix_match=*/false}};
 
-  CredentialPair bob(base::ASCIIToUTF16("bob"), base::ASCIIToUTF16("s3cr3t"),
-                     GURL("android://hash@com.example.my"),
-                     /*is_public_suffix_match=*/false);
-
-  EXPECT_CALL(
-      manual_filling_controller(),
-      RefreshSuggestions(
-          autofill::AccessorySheetData::Builder(
-              autofill::AccessoryTabType::TOUCH_TO_FILL,
-              base::ASCIIToUTF16("Touch to Fill"))
-              .AddUserInfo()
-              .AppendField(base::ASCIIToUTF16("alice"),
-                           base::ASCIIToUTF16("alice"), "0",
-                           /*is_obfuscated=*/false, /*is_selectable=*/true)
-              .AppendField(base::ASCIIToUTF16("p4ssw0rd"),
-                           base::ASCIIToUTF16("p4ssw0rd"), "0",
-                           /*is_obfuscated=*/true, /*is_selectable=*/false)
-              .AddUserInfo()
-              .AppendField(base::ASCIIToUTF16("bob"), base::ASCIIToUTF16("bob"),
-                           "1", /*is_obfuscated=*/false, /*is_selectable=*/true)
-              .AppendField(base::ASCIIToUTF16("s3cr3t"),
-                           base::ASCIIToUTF16("s3cr3t"), "1",
-                           /*is_obfuscated=*/true, /*is_selectable=*/false)
-              .Build()));
-  touch_to_fill_controller().Show(std::vector<CredentialPair>{alice, bob},
-                                  driver().AsWeakPtr());
-
-  EXPECT_CALL(driver(), FillSuggestion(base::ASCIIToUTF16("bob"),
-                                       base::ASCIIToUTF16("s3cr3t")));
-  EXPECT_CALL(manual_filling_controller(),
-              UpdateSourceAvailability(
-                  ManualFillingController::FillingSource::TOUCH_TO_FILL,
-                  /*has_suggestions=*/false));
+  TouchToFillView::ShowCallback callback;
+  EXPECT_CALL(view(), Show(Eq(base::ASCIIToUTF16("example.com")),
+                           ElementsAreArray(credentials), _))
+      .WillOnce(SaveArg<2>(&callback));
+  touch_to_fill_controller().Show(credentials, driver().AsWeakPtr());
 
   // Test that we correctly log the presence of an Android credential.
   base::HistogramTester tester;
-  touch_to_fill_controller().OnFillingTriggered(autofill::UserInfo::Field(
-      base::ASCIIToUTF16("bob"), base::ASCIIToUTF16("bob"), "1",
-      /*is_obfuscated=*/false, /*is_selectable=*/true));
+  EXPECT_CALL(driver(), FillSuggestion(base::ASCIIToUTF16("bob"),
+                                       base::ASCIIToUTF16("s3cr3t")));
+  std::move(callback).Run(credentials[1]);
   tester.ExpectUniqueSample("PasswordManager.FilledCredentialWasFromAndroidApp",
                             true, 1);
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index b13601d1..2af1911 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -671,6 +671,12 @@
   { key::kDeviceLoginScreenCursorHighlightEnabled,
     nullptr,
     base::Value::Type::BOOLEAN },
+  { key::kDeviceLoginScreenCaretHighlightEnabled,
+    nullptr,
+    base::Value::Type::BOOLEAN },
+  { key::kDeviceLoginScreenMonoAudioEnabled,
+    nullptr,
+    base::Value::Type::BOOLEAN },
   { key::kRebootAfterUpdate,
     prefs::kRebootAfterUpdate,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 910a401..9a1e792 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -2311,10 +2311,7 @@
       break;
 
     case IDC_CONTENT_CONTEXT_RELOADFRAME:
-      // We always obey the cache here.
-      // TODO(evanm): Perhaps we could allow shift-clicking the menu item to do
-      // a cache-ignoring reload of the frame.
-      source_web_contents_->ReloadFocusedFrame(false);
+      source_web_contents_->ReloadFocusedFrame();
       break;
 
     case IDC_CONTENT_CONTEXT_VIEWFRAMESOURCE:
diff --git a/chrome/browser/resources/chromeos/camera/.eslintrc.js b/chrome/browser/resources/chromeos/camera/.eslintrc.js
index 875b7ac6..bb791539 100644
--- a/chrome/browser/resources/chromeos/camera/.eslintrc.js
+++ b/chrome/browser/resources/chromeos/camera/.eslintrc.js
@@ -150,6 +150,7 @@
     // Adds BigInt64Array here since current version of eslint does not treat
     // BigInt64Array as a defined type.
     'BigInt64Array': 'readable',
+    'chromeosCamera': 'readable',
     'cros': 'readable',
     'ImageCapture': 'readable',
     'webkitRequestFileSystem': 'readable',
diff --git a/chrome/browser/resources/chromeos/camera/BUILD.gn b/chrome/browser/resources/chromeos/camera/BUILD.gn
index 814152f..0aa961c3 100644
--- a/chrome/browser/resources/chromeos/camera/BUILD.gn
+++ b/chrome/browser/resources/chromeos/camera/BUILD.gn
@@ -234,6 +234,7 @@
 copy("chrome_camera_app_mojo_generated") {
   sources = [
     "$root_gen_dir/components/arc/mojom/camera_intent.mojom-lite.js",
+    "$root_gen_dir/components/chromeos_camera/common/camera_app_helper.mojom-lite.js",
     "$root_gen_dir/media/capture/mojom/image_capture.mojom-lite.js",
     "$root_gen_dir/media/capture/video/chromeos/mojom/camera_app.mojom-lite.js",
     "$root_gen_dir/media/capture/video/chromeos/mojom/camera_common.mojom-lite.js",
@@ -246,6 +247,7 @@
 
   deps = [
     "//components/arc/mojom:camera_intent_js",
+    "//components/chromeos_camera/common:camera_app_helper_js",
     "//media/capture/mojom:image_capture_js",
     "//media/capture/video/chromeos/mojom:cros_camera_js",
     "//mojo/public/js:bindings_lite",
diff --git a/chrome/browser/resources/chromeos/camera/src/js/mojo/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/js/mojo/BUILD.gn
index cbfdda4c..ba8aca3 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/mojo/BUILD.gn
+++ b/chrome/browser/resources/chromeos/camera/src/js/mojo/BUILD.gn
@@ -15,7 +15,7 @@
 js_library("chrome_helper") {
   deps = [
     "//components/arc/mojom:camera_intent_js_library_for_compile",
-    "//media/capture/video/chromeos/mojom:cros_camera_js_library_for_compile",
+    "//components/chromeos_camera/common:camera_app_helper_js_library_for_compile",
   ]
   externs_list = [ "$externs_path/pending.js" ]
 }
diff --git a/chrome/browser/resources/chromeos/camera/src/js/mojo/chrome_helper.js b/chrome/browser/resources/chromeos/camera/src/js/mojo/chrome_helper.js
index 6c431182..9e70d267 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/mojo/chrome_helper.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/mojo/chrome_helper.js
@@ -24,9 +24,9 @@
   constructor() {
     /**
      * An interface remote that is used to communicate with Chrome.
-     * @type {cros.mojom.CameraAppHelperRemote}
+     * @type {chromeosCamera.mojom.CameraAppHelperRemote}
      */
-    this.remote_ = cros.mojom.CameraAppHelper.getRemote();
+    this.remote_ = chromeosCamera.mojom.CameraAppHelper.getRemote();
   }
 
   /**
diff --git a/chrome/browser/resources/chromeos/camera/src/js/util.js b/chrome/browser/resources/chromeos/camera/src/js/util.js
index 2ebdcab..3ae841901 100644
--- a/chrome/browser/resources/chromeos/camera/src/js/util.js
+++ b/chrome/browser/resources/chromeos/camera/src/js/util.js
@@ -857,7 +857,8 @@
   // App-window's isFullscreen, isMaximized state and window's outer-size may
   // not be updated immediately during resizing. Use if app-window's outerBounds
   // width matches screen width here as workarounds.
-  return chrome.app.window.current().outerBounds.width >= screen.width;
+  return chrome.app.window.current().outerBounds.width >= screen.width ||
+      chrome.app.window.current().outerBounds.height >= screen.height;
 };
 
 /**
diff --git a/chrome/browser/resources/chromeos/camera/src/views/main.html b/chrome/browser/resources/chromeos/camera/src/views/main.html
index 2805f65..5d3f398 100644
--- a/chrome/browser/resources/chromeos/camera/src/views/main.html
+++ b/chrome/browser/resources/chromeos/camera/src/views/main.html
@@ -37,6 +37,7 @@
     <script src="../js/mojo/range.mojom-lite.js"></script>
     <script src="../js/mojo/camera_intent.mojom-lite.js"></script>
     <script src="../js/mojo/camera_app.mojom-lite.js"></script>
+    <script src="../js/mojo/camera_app_helper.mojom-lite.js"></script>
     <script src="../js/mojo/chrome_helper.js"></script>
     <script src="../js/mojo/device_operator.js"></script>
     <script src="../js/mojo/image_capture.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/md_screen_container.css b/chrome/browser/resources/chromeos/login/md_screen_container.css
index 3cbba1a68..1d559ae 100644
--- a/chrome/browser/resources/chromeos/login/md_screen_container.css
+++ b/chrome/browser/resources/chromeos/login/md_screen_container.css
@@ -123,6 +123,15 @@
   width: 100%;
 }
 
+#oobe.account-picker {
+  max-height: unset;
+  max-width: unset;
+  padding-bottom: unset;
+  padding-inline-end: unset;
+  padding-inline-start: unset;
+  padding-top: unset;
+}
+
 #outer-container.fullscreen,
 #outer-container.fullscreen #oobe,
 #inner-container,
diff --git a/chrome/browser/resources/settings/site_settings/constants.js b/chrome/browser/resources/settings/site_settings/constants.js
index a7cf78b..f65608a 100644
--- a/chrome/browser/resources/settings/site_settings/constants.js
+++ b/chrome/browser/resources/settings/site_settings/constants.js
@@ -121,3 +121,10 @@
   MOST_VISITED: 'most-visited',
   STORAGE: 'data-stored',
 };
+
+/**
+ * String representation of the wildcard used for universal
+ * match for SiteExceptions.
+ * @type {string}
+ */
+settings.SITE_EXCEPTION_WILDCARD = '*';
diff --git a/chrome/browser/resources/settings/site_settings/site_list_entry.html b/chrome/browser/resources/settings/site_settings/site_list_entry.html
index f296bf5..da64458 100644
--- a/chrome/browser/resources/settings/site_settings/site_list_entry.html
+++ b/chrome/browser/resources/settings/site_settings/site_list_entry.html
@@ -36,16 +36,17 @@
         <site-favicon url="[[model.origin]]"></site-favicon>
         <div class="middle no-min-width">
           <div class="text-elide">
-            <span class="url-directionality">[[model.displayName]]</span>
+            <span class="url-directionality">[[computeDisplayName_(model)]]
+            </span>
           </div>
 
           <!-- This div must not contain extra whitespace. -->
           <div class="secondary text-elide"
-              id="siteDescription">[[siteDescription_]]</div>
+              id="siteDescription">[[computeSiteDescription_(model)]]</div>
         </div>
         <template is="dom-if" if="[[allowNavigateToSiteDetail_]]">
           <cr-icon-button class="subpage-arrow"
-              aria-label$="[[model.displayName]]"
+              aria-label$="[[computeDisplayName_(model)]]"
               aria-describedby="siteDescription" focus-row-control
               focus-type="site-details"></cr-icon-button>
           <div class="separator"></div>
diff --git a/chrome/browser/resources/settings/site_settings/site_list_entry.js b/chrome/browser/resources/settings/site_settings/site_list_entry.js
index fda7071a..b296f31 100644
--- a/chrome/browser/resources/settings/site_settings/site_list_entry.js
+++ b/chrome/browser/resources/settings/site_settings/site_list_entry.js
@@ -55,12 +55,6 @@
     },
 
     /** @private */
-    siteDescription_: {
-      type: String,
-      computed: 'computeSiteDescription_(model)',
-    },
-
-    /** @private */
     showPolicyPrefIndicator_: {
       type: Boolean,
       computed: 'computeShowPolicyPrefIndicator_(model)',
@@ -126,34 +120,59 @@
   },
 
   /**
+   * Returns the appropriate display name to show for the exception.
+   * This can, for example, be the website that is affected itself,
+   * or the website whose third parties are also affected.
+   * @return {string}
+   */
+  computeDisplayName_: function() {
+    if (this.model.embeddingOrigin &&
+        this.model.category === settings.ContentSettingsTypes.COOKIES &&
+        this.model.origin.trim() == settings.SITE_EXCEPTION_WILDCARD) {
+      return this.model.embeddingOrigin;
+    }
+    return this.model.displayName;
+  },
+
+  /**
    * Returns the appropriate site description to display. This can, for example,
    * be blank, an 'embedded on <site>' or 'Current incognito session' (or a
    * mix of the last two).
-   * @return {string} The site description.
+   * @return {string}
    */
   computeSiteDescription_: function() {
-    let displayName = '';
+    let description = '';
+
     if (this.model.embeddingOrigin) {
-      displayName = loadTimeData.getStringF(
-          'embeddedOnHost', this.sanitizePort(this.model.embeddingOrigin));
+      if (this.model.category === settings.ContentSettingsTypes.COOKIES &&
+          this.model.origin.trim() == settings.SITE_EXCEPTION_WILDCARD) {
+        description =
+            loadTimeData.getString(
+                'siteSettingsCookiesThirdPartyExceptionLabel');
+       } else {
+         description = loadTimeData.getStringF(
+             'embeddedOnHost', this.sanitizePort(this.model.embeddingOrigin));
+       }
     } else if (this.category == settings.ContentSettingsTypes.GEOLOCATION) {
-      displayName = loadTimeData.getString('embeddedOnAnyHost');
+      description = loadTimeData.getString('embeddedOnAnyHost');
     }
 
     // <if expr="chromeos">
     if (this.model.category === settings.ContentSettingsTypes.NOTIFICATIONS &&
         this.model.showAndroidSmsNote) {
-      displayName = loadTimeData.getString('androidSmsNote');
+      description = loadTimeData.getString('androidSmsNote');
     }
     // </if>
 
     if (this.model.incognito) {
-      if (displayName.length > 0) {
-        return loadTimeData.getStringF('embeddedIncognitoSite', displayName);
+      if (description.length > 0) {
+        description =
+            loadTimeData.getStringF('embeddedIncognitoSite', description);
+      } else {
+        description = loadTimeData.getString('incognitoSite');
       }
-      return loadTimeData.getString('incognitoSite');
     }
-    return displayName;
+    return description;
   },
 
   /**
diff --git a/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc b/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc
index 532dc2f0..5b27f911c 100644
--- a/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc
+++ b/chrome/browser/signin/chromeos_mirror_account_consistency_browsertest.cc
@@ -143,37 +143,15 @@
                               "consistency_enabled_by_default=false");
 }
 
-class ChromeOsMirrorAccountConsistencyTestWithAccountManagerEnabled
-    : public ChromeOsMirrorAccountConsistencyTest {
- public:
-  ChromeOsMirrorAccountConsistencyTestWithAccountManagerEnabled() = default;
-  ~ChromeOsMirrorAccountConsistencyTestWithAccountManagerEnabled() override =
-      default;
-
-  void SetUp() override {
-    scoped_feature_list_.InitAndEnableFeature(
-        chromeos::features::kAccountManager);
-    ChromeOsMirrorAccountConsistencyTest::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(
-      ChromeOsMirrorAccountConsistencyTestWithAccountManagerEnabled);
-};
-
-IN_PROC_BROWSER_TEST_F(
-    ChromeOsMirrorAccountConsistencyTestWithAccountManagerEnabled,
-    PRE_TestMirrorRequestChromeOsNotChildAccount) {
+IN_PROC_BROWSER_TEST_F(ChromeOsMirrorAccountConsistencyTest,
+                       PRE_TestMirrorRequestChromeOsNotChildAccount) {
   RegisterUser(account_id_);
   chromeos::StartupUtils::MarkOobeCompleted();
 }
 
-// Mirror is not enabled for non-child accounts.
-IN_PROC_BROWSER_TEST_F(
-    ChromeOsMirrorAccountConsistencyTestWithAccountManagerEnabled,
-    TestMirrorRequestChromeOsNotChildAccount) {
+// Mirror is enabled for non-child accounts.
+IN_PROC_BROWSER_TEST_F(ChromeOsMirrorAccountConsistencyTest,
+                       TestMirrorRequestChromeOsNotChildAccount) {
   // Not a child user.
   LoginUser(account_id_);
 
diff --git a/chrome/browser/sync/device_info_sync_service_factory.cc b/chrome/browser/sync/device_info_sync_service_factory.cc
index 6fcf71c..6de949e 100644
--- a/chrome/browser/sync/device_info_sync_service_factory.cc
+++ b/chrome/browser/sync/device_info_sync_service_factory.cc
@@ -23,9 +23,43 @@
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/model/model_type_store_service.h"
 #include "components/sync_device_info/device_info_prefs.h"
+#include "components/sync_device_info/device_info_sync_client.h"
 #include "components/sync_device_info/device_info_sync_service_impl.h"
 #include "components/sync_device_info/local_device_info_provider_impl.h"
 
+namespace {
+
+class DeviceInfoSyncClient : public syncer::DeviceInfoSyncClient {
+ public:
+  explicit DeviceInfoSyncClient(Profile* profile) : profile_(profile) {}
+  ~DeviceInfoSyncClient() override = default;
+
+  // syncer::DeviceInfoSyncClient:
+  std::string GetSigninScopedDeviceId() const override {
+// Since the local sync backend is currently only supported on Windows don't
+// even check the pref on other os-es.
+#if defined(OS_WIN)
+    syncer::SyncPrefs prefs(profile_->GetPrefs());
+    if (prefs.IsLocalSyncEnabled()) {
+      return "local_device";
+    }
+#endif  // defined(OS_WIN)
+
+    return GetSigninScopedDeviceIdForProfile(profile_);
+  }
+
+  // syncer::DeviceInfoSyncClient:
+  bool GetSendTabToSelfReceivingEnabled() const override {
+    return send_tab_to_self::IsReceivingEnabledByUserOnThisDevice(
+        profile_->GetPrefs());
+  }
+
+ private:
+  Profile* const profile_;
+};
+
+}  // namespace
+
 // static
 syncer::DeviceInfoSyncService* DeviceInfoSyncServiceFactory::GetForProfile(
     Profile* profile) {
@@ -72,41 +106,17 @@
     content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
 
-  syncer::LocalDeviceInfoProviderImpl::SigninScopedDeviceIdCallback
-      signin_scoped_device_id_callback;
-  syncer::LocalDeviceInfoProviderImpl::SendTabToSelfReceivingEnabledCallback
-      send_tab_to_self_receiving_enabled_callback;
-  bool local_sync_backend_enabled = false;
-
-// Since the local sync backend is currently only supported on Windows don't
-// even check the pref on other os-es.
-#if defined(OS_WIN)
-  syncer::SyncPrefs prefs(profile->GetPrefs());
-  local_sync_backend_enabled = prefs.IsLocalSyncEnabled();
-#endif  // defined(OS_WIN)
-
-  if (local_sync_backend_enabled) {
-    signin_scoped_device_id_callback =
-        base::BindRepeating([]() { return std::string("local_device"); });
-  } else {
-    signin_scoped_device_id_callback =
-        base::BindRepeating(&GetSigninScopedDeviceIdForProfile, profile);
-  }
-
-  send_tab_to_self_receiving_enabled_callback = base::BindRepeating(
-      &send_tab_to_self::IsReceivingEnabledByUserOnThisDevice,
-      profile->GetPrefs());
-
+  auto device_info_sync_client =
+      std::make_unique<DeviceInfoSyncClient>(profile);
   auto local_device_info_provider =
       std::make_unique<syncer::LocalDeviceInfoProviderImpl>(
           chrome::GetChannel(), chrome::GetVersionString(),
-          signin_scoped_device_id_callback,
-          send_tab_to_self_receiving_enabled_callback);
-
+          device_info_sync_client.get());
   auto device_prefs =
       std::make_unique<syncer::DeviceInfoPrefs>(profile->GetPrefs());
 
   return new syncer::DeviceInfoSyncServiceImpl(
       ModelTypeStoreServiceFactory::GetForProfile(profile)->GetStoreFactory(),
-      std::move(local_device_info_provider), std::move(device_prefs));
+      std::move(local_device_info_provider), std::move(device_prefs),
+      std::move(device_info_sync_client));
 }
diff --git a/chrome/browser/sync/glue/synced_tab_delegate_android.cc b/chrome/browser/sync/glue/synced_tab_delegate_android.cc
index aab6d56b..acb90ef 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate_android.cc
+++ b/chrome/browser/sync/glue/synced_tab_delegate_android.cc
@@ -33,7 +33,7 @@
 }  // namespace
 
 SyncedTabDelegateAndroid::SyncedTabDelegateAndroid(TabAndroid* tab_android)
-    : tab_android_(tab_android), source_tab_id_(SessionID::InvalidValue()) {}
+    : tab_android_(tab_android) {}
 
 SyncedTabDelegateAndroid::~SyncedTabDelegateAndroid() {}
 
@@ -45,10 +45,6 @@
   return SessionIdFromAndroidId(tab_android_->GetAndroidId());
 }
 
-SessionID SyncedTabDelegateAndroid::GetSourceTabID() const {
-  return source_tab_id_;
-}
-
 bool SyncedTabDelegateAndroid::IsPlaceholderTab() const {
   return web_contents() == nullptr;
 }
@@ -57,8 +53,6 @@
     content::WebContents* web_contents,
     int source_tab_android_id) {
   TabContentsSyncedTabDelegate::SetWebContents(web_contents);
-
-  source_tab_id_ = SessionIdFromAndroidId(source_tab_android_id);
 }
 
 void SyncedTabDelegateAndroid::ResetWebContents() {
diff --git a/chrome/browser/sync/glue/synced_tab_delegate_android.h b/chrome/browser/sync/glue/synced_tab_delegate_android.h
index c4608ab..2a87b97 100644
--- a/chrome/browser/sync/glue/synced_tab_delegate_android.h
+++ b/chrome/browser/sync/glue/synced_tab_delegate_android.h
@@ -32,7 +32,6 @@
   // SyncedTabDelegate:
   SessionID GetWindowId() const override;
   SessionID GetSessionId() const override;
-  SessionID GetSourceTabID() const override;
   bool IsPlaceholderTab() const override;
 
   // Set the web contents for this tab and handles source tab ID initialization.
@@ -43,7 +42,6 @@
 
  private:
   TabAndroid* tab_android_;
-  SessionID source_tab_id_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncedTabDelegateAndroid);
 };
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc
index 6ea3241d6..30863e6e8 100644
--- a/chrome/browser/sync/profile_sync_service_android.cc
+++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -120,13 +120,6 @@
       env, weak_java_profile_sync_service_.get(env));
 }
 
-bool ProfileSyncServiceAndroid::IsSyncAllowedByAndroid() const {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  JNIEnv* env = AttachCurrentThread();
-  return Java_ProfileSyncService_isMasterSyncEnabled(
-      env, weak_java_profile_sync_service_.get(env));
-}
-
 // Pure ProfileSyncService calls.
 
 jboolean ProfileSyncServiceAndroid::IsSyncRequested(
diff --git a/chrome/browser/sync/profile_sync_service_android.h b/chrome/browser/sync/profile_sync_service_android.h
index b322c88..e5cbed0 100644
--- a/chrome/browser/sync/profile_sync_service_android.h
+++ b/chrome/browser/sync/profile_sync_service_android.h
@@ -184,9 +184,6 @@
                       const base::android::JavaParamRef<jobject>& obj);
 
  private:
-  // Returns whether sync is allowed by Android.
-  bool IsSyncAllowedByAndroid() const;
-
   // A reference to the Chrome profile object.
   Profile* profile_;
 
diff --git a/chrome/browser/sync/profile_sync_service_factory_unittest.cc b/chrome/browser/sync/profile_sync_service_factory_unittest.cc
index 5678ae8..409272c 100644
--- a/chrome/browser/sync/profile_sync_service_factory_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_factory_unittest.cc
@@ -45,7 +45,7 @@
 
   // Returns the collection of default datatypes.
   std::vector<syncer::ModelType> DefaultDatatypes() {
-    static_assert(46 == syncer::ModelType::NUM_ENTRIES,
+    static_assert(39 == syncer::ModelType::NUM_ENTRIES,
                   "When adding a new type, you probably want to add it here as "
                   "well (assuming it is already enabled).");
 
diff --git a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
index 1a166c3..e352d2d 100644
--- a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
+++ b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.cc
@@ -33,9 +33,7 @@
 SyncSessionsRouterTabHelper::SyncSessionsRouterTabHelper(
     content::WebContents* web_contents,
     SyncSessionsWebContentsRouter* router)
-    : content::WebContentsObserver(web_contents),
-      router_(router),
-      source_tab_id_(SessionID::InvalidValue()) {
+    : content::WebContentsObserver(web_contents), router_(router) {
   chrome_translate_client_ =
       ChromeTranslateClient::FromWebContents(web_contents);
   // A translate client is not always attached to web contents (e.g. tests).
@@ -86,7 +84,9 @@
     ui::PageTransition transition,
     bool started_from_context_menu,
     bool renderer_initiated) {
-  SetSourceTabIdForChild(new_contents);
+  // TODO(crbug.com/1007969): This is a relic from when we actually did change
+  // something about the tab here. It should be safe to remove now.
+  NotifyRouter();
 }
 
 void SyncSessionsRouterTabHelper::OnLanguageDetermined(
@@ -95,18 +95,6 @@
     NotifyRouter();
 }
 
-void SyncSessionsRouterTabHelper::SetSourceTabIdForChild(
-    content::WebContents* child_contents) {
-  SessionID source_tab_id = SessionTabHelper::IdForTab(web_contents());
-  if (child_contents &&
-      SyncSessionsRouterTabHelper::FromWebContents(child_contents) &&
-      child_contents != web_contents() && source_tab_id.is_valid()) {
-    SyncSessionsRouterTabHelper::FromWebContents(child_contents)
-        ->set_source_tab_id(source_tab_id);
-  }
-  NotifyRouter();
-}
-
 void SyncSessionsRouterTabHelper::NotifyRouter(bool page_load_completed) {
   if (router_)
     router_->NotifyTabModified(web_contents(), page_load_completed);
diff --git a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h
index b76434b..fe9716a3 100644
--- a/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h
+++ b/chrome/browser/sync/sessions/sync_sessions_router_tab_helper.h
@@ -22,8 +22,7 @@
 
 // TabHelper class that forwards tab-level WebContentsObserver events to a
 // (per-profile) sessions router. The router is responsible for forwarding
-// these events to sessions sync. This class also tracks the source tab id
-// of its corresponding tab, if available.
+// these events to sessions sync.
 // A TabHelper is a WebContentsObserver tied to the top level WebContents for a
 // browser tab.
 // https://chromium.googlesource.com/chromium/src/+/master/docs/tab_helpers.md
@@ -67,35 +66,16 @@
       bool icon_url_changed,
       const gfx::Image& image) override;
 
-  // Sets the source tab id for the given child WebContents to the id of the
-  // WebContents that owns this helper.
-  void SetSourceTabIdForChild(content::WebContents* child_contents);
-
-  // Get the tab id of the tab responsible for creating the tab this helper
-  // corresponds to. Returns an invalid ID if there is no such tab.
-  SessionID source_tab_id() const { return source_tab_id_; }
-
  private:
   friend class content::WebContentsUserData<SyncSessionsRouterTabHelper>;
 
   explicit SyncSessionsRouterTabHelper(content::WebContents* web_contents,
                                        SyncSessionsWebContentsRouter* router);
 
-  // Set the tab id of the tab reponsible for creating the tab this helper
-  // corresponds to.
-  void set_source_tab_id(SessionID id) { source_tab_id_ = id; }
-
   void NotifyRouter(bool page_load_completed = false);
 
   // |router_| is a KeyedService and is guaranteed to outlive |this|.
   SyncSessionsWebContentsRouter* router_;
-  // Tab id of the tab from which this tab was created. Example events that
-  // create this relationship:
-  // * From context menu, "Open link in new tab".
-  // * From context menu, "Open link in new window".
-  // * Ctrl-click.
-  // * Click on a link with target='_blank'.
-  SessionID source_tab_id_;
 
   ChromeTranslateClient* chrome_translate_client_;
 
diff --git a/chrome/browser/sync/test/integration/enable_disable_test.cc b/chrome/browser/sync/test/integration/enable_disable_test.cc
index 3aed8c6..ab0552b4 100644
--- a/chrome/browser/sync/test/integration/enable_disable_test.cc
+++ b/chrome/browser/sync/test/integration/enable_disable_test.cc
@@ -171,14 +171,16 @@
           << " for " << GetUserSelectableTypeName(type);
 
       if (syncer::CommitOnlyTypes().Has(grouped_type)) {
-        EXPECT_EQ(0, histogram_tester.GetBucketCount(
-                         "Sync.PostedDataTypeGetUpdatesRequest",
-                         ModelTypeToHistogramInt(grouped_type)))
+        EXPECT_EQ(0,
+                  histogram_tester.GetBucketCount(
+                      "Sync.PostedDataTypeGetUpdatesRequest",
+                      static_cast<int>(ModelTypeHistogramValue(grouped_type))))
             << " for " << ModelTypeToString(grouped_type);
       } else {
-        EXPECT_NE(0, histogram_tester.GetBucketCount(
-                         "Sync.PostedDataTypeGetUpdatesRequest",
-                         ModelTypeToHistogramInt(grouped_type)))
+        EXPECT_NE(0,
+                  histogram_tester.GetBucketCount(
+                      "Sync.PostedDataTypeGetUpdatesRequest",
+                      static_cast<int>(ModelTypeHistogramValue(grouped_type))))
             << " for " << ModelTypeToString(grouped_type);
       }
     }
diff --git a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
index 8f6ede2..94eba27 100644
--- a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
@@ -289,7 +289,8 @@
 
   EXPECT_EQ(1, histogram_tester.GetBucketCount(
                    "Sync.USSMigrationSuccess",
-                   syncer::ModelTypeToHistogramInt(syncer::PASSWORDS)));
+                   static_cast<int>(
+                       syncer::ModelTypeHistogramValue(syncer::PASSWORDS))));
   EXPECT_THAT(
       histogram_tester.GetAllSamples("Sync.USSMigrationEntityCount.PASSWORD"),
       ElementsAre(base::Bucket(/*min=*/2, /*count=*/1)));
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index 8520666..b2feacd 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -692,33 +692,6 @@
       {{base_url.spec()}, {new_window_url.spec(), moved_tab_url.spec()}}));
 }
 
-IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, SourceTabIDSet) {
-  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
-  ASSERT_TRUE(CheckInitialState(0));
-
-  GURL base_url = GURL(kURL1);
-  ASSERT_TRUE(OpenTab(0, base_url));
-
-  WaitForURLOnServer(base_url);
-
-  GURL new_tab_url = GURL(kURL2);
-  ASSERT_TRUE(OpenTabFromSourceIndex(
-      0, 0, new_tab_url, WindowOpenDisposition::NEW_FOREGROUND_TAB));
-  WaitForHierarchyOnServer(
-      SessionsHierarchy({{base_url.spec(), new_tab_url.spec()}}));
-
-  content::WebContents* original_tab_contents =
-      GetBrowser(0)->tab_strip_model()->GetWebContentsAt(0);
-  content::WebContents* new_tab_contents =
-      GetBrowser(0)->tab_strip_model()->GetWebContentsAt(1);
-
-  SessionID source_tab_id = SessionTabHelper::IdForTab(original_tab_contents);
-  sync_sessions::SyncSessionsRouterTabHelper* new_tab_helper =
-      sync_sessions::SyncSessionsRouterTabHelper::FromWebContents(
-          new_tab_contents);
-  EXPECT_EQ(new_tab_helper->source_tab_id(), source_tab_id);
-}
-
 IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, CookieJarMismatch) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
 
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc
index 52a028e..fc6efb7 100644
--- a/chrome/browser/themes/browser_theme_pack.cc
+++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -62,7 +62,7 @@
 // change default theme assets, if you need themes to recreate their generated
 // images (which are cached), or if you changed how missing values are
 // generated.
-const int kThemePackVersion = 71;
+const int kThemePackVersion = 72;
 
 // IDs that are in the DataPack won't clash with the positive integer
 // uint16_t. kHeaderID should always have the maximum value because we want the
diff --git a/chrome/browser/touch_to_fill/android/OWNERS b/chrome/browser/touch_to_fill/android/OWNERS
index 6dfa6ec..ea9b03f9 100644
--- a/chrome/browser/touch_to_fill/android/OWNERS
+++ b/chrome/browser/touch_to_fill/android/OWNERS
@@ -1,4 +1,5 @@
 jdoerrie@chromium.org
 fhorschig@chromium.org
+bsazonov@chromium.org
 
 # COMPONENT: UI>Browser>Passwords
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 9b9c939..f8a1d1e 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -2480,8 +2480,9 @@
 // Browser, In-progress download termination handling (private):
 
 bool Browser::CanCloseWithInProgressDownloads() {
-#if defined(OS_MACOSX)
-  // On Mac, non-incognito download can still continue after window is closed.
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
+  // On Mac and ChromeOS, non-incognito download can still continue after window
+  // is closed.
   if (!profile_->IsOffTheRecord())
     return true;
 #endif
diff --git a/chrome/browser/ui/browser_close_unittest.cc b/chrome/browser/ui/browser_close_unittest.cc
index b06a9bb..9faf1b7e 100644
--- a/chrome/browser/ui/browser_close_unittest.cc
+++ b/chrome/browser/ui/browser_close_unittest.cc
@@ -269,7 +269,7 @@
   EXPECT_EQ(Browser::DownloadCloseType::kBrowserShutdown,
             browser->OkToCloseWithInProgressDownloads(&num_downloads_blocking));
   EXPECT_EQ(num_downloads_blocking, 1);
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
   EXPECT_EQ(true, browser->CanCloseWithInProgressDownloads());
 #else
   EXPECT_EQ(false, browser->CanCloseWithInProgressDownloads());
diff --git a/chrome/browser/ui/cocoa/tab_menu_bridge.mm b/chrome/browser/ui/cocoa/tab_menu_bridge.mm
index 4832a1a..55ae27d 100644
--- a/chrome/browser/ui/cocoa/tab_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/tab_menu_bridge.mm
@@ -131,6 +131,20 @@
   DCHECK(tab_strip_model);
   DCHECK_EQ(tab_strip_model, model_);
 
+  // The menu doesn't represent selection in any way, so ignore it.
+  if (change.type() == TabStripModelChange::kSelectionOnly)
+    return;
+
+  // If a single WebContents is being replaced, just regenerate that one menu
+  // item.
+  if (change.type() == TabStripModelChange::kReplaced) {
+    const TabStripModelChange::Replace* replace = change.GetReplace();
+    int menu_index = replace->index + dynamic_items_start_;
+    UpdateItemForWebContents([menu_item_.submenu itemAtIndex:menu_index],
+                             replace->new_contents);
+    return;
+  }
+
   // Rather than doing clever updating from |change|, just destroy the dynamic
   // menu items and re-add them.
   RemoveAllDynamicItems();
diff --git a/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm b/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm
index e7aec59..cfeedaa 100644
--- a/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm
@@ -70,6 +70,10 @@
     return contents;
   }
 
+  content::WebContents* GetModelWebContentsAt(int index) {
+    return model()->GetWebContentsAt(index);
+  }
+
   void AddModelTabNamed(const std::string& name) {
     model()->AppendWebContents(CreateWebContents(name), true);
   }
@@ -103,6 +107,16 @@
     }
   }
 
+  void ReplaceModelTabNamed(const std::string& old_name,
+                            const std::string& new_name) {
+    int index = ModelIndexForTabNamed(old_name);
+    if (index >= 0) {
+      std::unique_ptr<content::WebContents> old_contents =
+          model()->ReplaceWebContentsAt(index, CreateWebContents(new_name));
+      // Let the old WebContents be destroyed here.
+    }
+  }
+
   NSMenuItem* MenuItemForTabNamed(const std::string& name) {
     return [menu() itemWithTitle:base::SysUTF8ToNSString(name)];
   }
@@ -166,6 +180,11 @@
 
   RenameModelTabNamed("Tab 1", "Tab 4");
   ExpectDynamicTabsInMenuAre({"Tab 4", "Tab 3", "Tab 2"});
+
+  content::WebContents* old_contents = GetModelWebContentsAt(0);
+  ReplaceModelTabNamed("Tab 4", "Tab 5");
+  ASSERT_NE(GetModelWebContentsAt(0), old_contents);
+  ExpectDynamicTabsInMenuAre({"Tab 5", "Tab 3", "Tab 2"});
 }
 
 TEST_F(TabMenuBridgeTest, ClickingMenuActivatesTab) {
diff --git a/chrome/browser/ui/hats/hats_service.cc b/chrome/browser/ui/hats/hats_service.cc
index dd374619..2edb44b 100644
--- a/chrome/browser/ui/hats/hats_service.cc
+++ b/chrome/browser/ui/hats/hats_service.cc
@@ -136,6 +136,10 @@
     return true;
   }
 
+  // Survey can not be loaded and shown if there is no network connection.
+  if (net::NetworkChangeNotifier::IsOffline())
+    return false;
+
   bool consent_given =
       g_browser_process->GetMetricsServicesManager()->IsMetricsConsentGiven();
   if (!consent_given)
diff --git a/chrome/browser/ui/sync/browser_synced_tab_delegate.cc b/chrome/browser/ui/sync/browser_synced_tab_delegate.cc
index a900e92..e003a56 100644
--- a/chrome/browser/ui/sync/browser_synced_tab_delegate.cc
+++ b/chrome/browser/ui/sync/browser_synced_tab_delegate.cc
@@ -22,13 +22,6 @@
   return SessionTabHelper::FromWebContents(web_contents())->session_id();
 }
 
-SessionID BrowserSyncedTabDelegate::GetSourceTabID() const {
-  const sync_sessions::SyncSessionsRouterTabHelper* helper =
-      sync_sessions::SyncSessionsRouterTabHelper::FromWebContents(
-          web_contents());
-  return helper->source_tab_id();
-}
-
 bool BrowserSyncedTabDelegate::IsPlaceholderTab() const {
   return false;
 }
diff --git a/chrome/browser/ui/sync/browser_synced_tab_delegate.h b/chrome/browser/ui/sync/browser_synced_tab_delegate.h
index 49fc3407..0ec9d5c5 100644
--- a/chrome/browser/ui/sync/browser_synced_tab_delegate.h
+++ b/chrome/browser/ui/sync/browser_synced_tab_delegate.h
@@ -25,7 +25,6 @@
   // SyncedTabDelegate:
   SessionID GetWindowId() const override;
   SessionID GetSessionId() const override;
-  SessionID GetSourceTabID() const override;
   bool IsPlaceholderTab() const override;
 
  private:
diff --git a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate_unittest.cc b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate_unittest.cc
index 1d9f20a..1b63782 100644
--- a/chrome/browser/ui/sync/tab_contents_synced_tab_delegate_unittest.cc
+++ b/chrome/browser/ui/sync/tab_contents_synced_tab_delegate_unittest.cc
@@ -20,9 +20,6 @@
 
   SessionID GetWindowId() const override { return SessionID::InvalidValue(); }
   SessionID GetSessionId() const override { return SessionID::InvalidValue(); }
-  SessionID GetSourceTabID() const override {
-    return SessionID::InvalidValue();
-  }
   bool IsPlaceholderTab() const override { return false; }
 };
 
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
index 2780cb1..dd5237e 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
@@ -14,7 +14,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/engine/model_type_processor.h"
 #include "components/sync/engine/non_blocking_sync_common.h"
 #include "components/sync/model/entity_data.h"
@@ -309,9 +309,9 @@
   *entity->specifics.mutable_session() = specifics;
   entity->creation_time = timestamp;
   entity->modification_time = timestamp;
-  entity->client_tag_hash = syncer::GenerateSyncableHash(
+  entity->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
       syncer::SESSIONS, sync_sessions::SessionStore::GetClientTag(specifics));
-  entity->id = entity->client_tag_hash;
+  entity->id = entity->client_tag_hash.value();
 
   auto update = std::make_unique<syncer::UpdateResponseData>();
   update->entity = std::move(entity);
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 663af44..ec038cc 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -284,6 +284,11 @@
   profiles::CloseProfileWindows(browser()->profile());
 }
 
+void ProfileMenuView::OnSyncSettingsButtonClicked() {
+  RecordClick(ActionableItem::kSyncSettingsButton);
+  chrome::ShowSettingsSubPage(browser(), chrome::kSyncSetupSubPage);
+}
+
 void ProfileMenuView::OnSyncErrorButtonClicked(
     sync_ui_util::AvatarSyncErrorType error) {
   RecordClick(ActionableItem::kSyncErrorButton);
@@ -507,7 +512,32 @@
       IdentityManagerFactory::GetForProfile(browser()->profile());
 
   if (identity_manager->HasPrimaryAccount()) {
-    // TODO(crbug.com/995720): Implement sync-is-on state.
+    // Show sync state.
+    int description_string_id, button_string_id;
+    sync_ui_util::AvatarSyncErrorType error =
+        sync_ui_util::GetMessagesForAvatarSyncError(
+            browser()->profile(), &description_string_id, &button_string_id);
+    switch (error) {
+      case sync_ui_util::NO_SYNC_ERROR:
+        SetSyncInfo(
+            /*description=*/base::string16(),
+            l10n_util::GetStringUTF16(IDS_SETTINGS_SYNC_ADVANCED_PAGE_TITLE),
+            base::BindRepeating(&ProfileMenuView::OnSyncSettingsButtonClicked,
+                                base::Unretained(this)));
+        break;
+      case sync_ui_util::MANAGED_USER_UNRECOVERABLE_ERROR:
+      case sync_ui_util::UNRECOVERABLE_ERROR:
+      case sync_ui_util::UPGRADE_CLIENT_ERROR:
+      case sync_ui_util::PASSPHRASE_ERROR:
+      case sync_ui_util::SETTINGS_UNCONFIRMED_ERROR:
+      case sync_ui_util::AUTH_ERROR:
+        SetSyncInfo(
+            l10n_util::GetStringUTF16(description_string_id),
+            l10n_util::GetStringUTF16(button_string_id),
+            base::BindRepeating(&ProfileMenuView::OnSyncErrorButtonClicked,
+                                base::Unretained(this), error));
+        break;
+    }
     return;
   }
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.h b/chrome/browser/ui/views/profiles/profile_menu_view.h
index d6f0e02..d84ac154 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.h
@@ -49,7 +49,8 @@
     kOtherProfileButton = 13,
     kCookiesClearedOnExitLink = 14,
     kAddNewProfileButton = 15,
-    kMaxValue = kAddNewProfileButton,
+    kSyncSettingsButton = 16,
+    kMaxValue = kSyncSettingsButton,
   };
 
   ProfileMenuView(views::Button* anchor_button,
@@ -79,6 +80,7 @@
   void OnManageProfilesButtonClicked();
   void OnLockButtonClicked();
   void OnExitProfileButtonClicked();
+  void OnSyncSettingsButtonClicked();
   void OnSyncErrorButtonClicked(sync_ui_util::AvatarSyncErrorType error);
   void OnCurrentProfileCardClicked();
   void OnSigninButtonClicked();
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index 0026389..8280958 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -26,6 +26,7 @@
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/scoped_account_consistency.h"
+#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/secondary_account_helper.h"
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
@@ -46,6 +47,8 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/extension_registry.h"
@@ -622,6 +625,14 @@
 
   void SetTargetBrowser(Browser* browser) { target_browser_ = browser; }
 
+  signin::IdentityManager* identity_manager() {
+    return IdentityManagerFactory::GetForProfile(browser()->profile());
+  }
+
+  syncer::SyncService* sync_service() {
+    return ProfileSyncServiceFactory::GetForProfile(browser()->profile());
+  }
+
  private:
   void OpenProfileMenu() {
     BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(
@@ -716,14 +727,15 @@
         base::size(
             ProfileMenuClickTest_MultipleProfiles::kOrderedActionableItems)));
 
-class ProfileMenuClickTest_WithPrimaryAccount : public ProfileMenuClickTest {
+class ProfileMenuClickTest_SyncEnabled : public ProfileMenuClickTest {
  public:
   // List of actionable items in the correct order as they appear in the menu.
   // If a new button is added to the menu, it should also be added to this list.
-  static constexpr ProfileMenuView::ActionableItem kOrderedActionableItems[8] =
+  static constexpr ProfileMenuView::ActionableItem kOrderedActionableItems[9] =
       {ProfileMenuView::ActionableItem::kPasswordsButton,
        ProfileMenuView::ActionableItem::kCreditCardsButton,
        ProfileMenuView::ActionableItem::kAddressesButton,
+       ProfileMenuView::ActionableItem::kSyncSettingsButton,
        ProfileMenuView::ActionableItem::kManageGoogleAccountButton,
        ProfileMenuView::ActionableItem::kManageProfilesButton,
        ProfileMenuView::ActionableItem::kGuestProfileButton,
@@ -732,37 +744,92 @@
        // there are no other buttons at the end.
        ProfileMenuView::ActionableItem::kPasswordsButton};
 
-  ProfileMenuClickTest_WithPrimaryAccount() = default;
+  ProfileMenuClickTest_SyncEnabled() = default;
 
   ProfileMenuView::ActionableItem GetExpectedActionableItemAtIndex(
       size_t index) override {
     return kOrderedActionableItems[index];
   }
 
-  DISALLOW_COPY_AND_ASSIGN(ProfileMenuClickTest_WithPrimaryAccount);
+  DISALLOW_COPY_AND_ASSIGN(ProfileMenuClickTest_SyncEnabled);
 };
 
 // static
 constexpr ProfileMenuView::ActionableItem
-    ProfileMenuClickTest_WithPrimaryAccount::kOrderedActionableItems[];
+    ProfileMenuClickTest_SyncEnabled::kOrderedActionableItems[];
 
-IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_WithPrimaryAccount,
-                       SetupAndRunTest) {
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(browser()->profile());
-  ASSERT_FALSE(identity_manager->HasPrimaryAccount());
-  signin::MakePrimaryAccountAvailable(identity_manager, "primary@example.com");
+IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_SyncEnabled,
+                       DISABLED_SetupAndRunTest) {
+  // Set primary account.
+  ASSERT_FALSE(identity_manager()->HasPrimaryAccount());
+  signin::MakePrimaryAccountAvailable(identity_manager(),
+                                      "primary@example.com");
+  ASSERT_TRUE(identity_manager()->HasPrimaryAccount());
+  // Start sync.
+  sync_service()->GetUserSettings()->SetSyncRequested(true);
+  sync_service()->GetUserSettings()->SetFirstSetupComplete(
+      syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
+  ASSERT_TRUE(sync_service()->IsSyncFeatureEnabled());
 
   RunTest();
 }
 
 INSTANTIATE_TEST_SUITE_P(
     ,
-    ProfileMenuClickTest_WithPrimaryAccount,
+    ProfileMenuClickTest_SyncEnabled,
     ::testing::Range(
         size_t(0),
-        base::size(
-            ProfileMenuClickTest_WithPrimaryAccount::kOrderedActionableItems)));
+        base::size(ProfileMenuClickTest_SyncEnabled::kOrderedActionableItems)));
+
+class ProfileMenuClickTest_SyncError : public ProfileMenuClickTest {
+ public:
+  // List of actionable items in the correct order as they appear in the menu.
+  // If a new button is added to the menu, it should also be added to this list.
+  static constexpr ProfileMenuView::ActionableItem kOrderedActionableItems[9] =
+      {ProfileMenuView::ActionableItem::kPasswordsButton,
+       ProfileMenuView::ActionableItem::kCreditCardsButton,
+       ProfileMenuView::ActionableItem::kAddressesButton,
+       ProfileMenuView::ActionableItem::kSyncErrorButton,
+       ProfileMenuView::ActionableItem::kManageGoogleAccountButton,
+       ProfileMenuView::ActionableItem::kManageProfilesButton,
+       ProfileMenuView::ActionableItem::kGuestProfileButton,
+       ProfileMenuView::ActionableItem::kAddNewProfileButton,
+       // The first button is added again to finish the cycle and test that
+       // there are no other buttons at the end.
+       ProfileMenuView::ActionableItem::kPasswordsButton};
+
+  ProfileMenuClickTest_SyncError() = default;
+
+  ProfileMenuView::ActionableItem GetExpectedActionableItemAtIndex(
+      size_t index) override {
+    return kOrderedActionableItems[index];
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ProfileMenuClickTest_SyncError);
+};
+
+// static
+constexpr ProfileMenuView::ActionableItem
+    ProfileMenuClickTest_SyncError::kOrderedActionableItems[];
+
+IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_SyncError,
+                       DISABLED_SetupAndRunTest) {
+  // Set primary account without setting up sync.
+  ASSERT_FALSE(identity_manager()->HasPrimaryAccount());
+  signin::MakePrimaryAccountAvailable(identity_manager(),
+                                      "primary@example.com");
+  ASSERT_TRUE(identity_manager()->HasPrimaryAccount());
+  ASSERT_FALSE(sync_service()->IsSyncFeatureEnabled());
+
+  RunTest();
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    ProfileMenuClickTest_SyncError,
+    ::testing::Range(
+        size_t(0),
+        base::size(ProfileMenuClickTest_SyncError::kOrderedActionableItems)));
 
 class ProfileMenuClickTest_WithUnconsentedPrimaryAccount
     : public ProfileMenuClickTest {
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index eea1608..775abda 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -471,13 +471,6 @@
     // the keyboard navigation to work.
     DCHECK(Button::AsButton(fullscreen_button_));
 
-    fullscreen_button_->SetImage(
-        ImageButton::STATE_NORMAL,
-        gfx::CreateVectorIcon(
-            kFullscreenIcon,
-            GetNativeTheme()->GetSystemColor(
-                ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor)));
-
     // Since |fullscreen_button_| will reside in a menu, make it ALWAYS
     // focusable regardless of the platform.
     fullscreen_button_->SetFocusBehavior(FocusBehavior::ALWAYS);
@@ -549,6 +542,13 @@
     ui::NativeTheme* theme = GetNativeTheme();
     zoom_label_->SetEnabledColor(theme->GetSystemColor(
         ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor));
+
+    fullscreen_button_->SetImage(
+        ImageButton::STATE_NORMAL,
+        gfx::CreateVectorIcon(
+            kFullscreenIcon,
+            GetNativeTheme()->GetSystemColor(
+                ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor)));
     gfx::ImageSkia hovered_fullscreen_image = gfx::CreateVectorIcon(
         kFullscreenIcon,
         theme->GetSystemColor(
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 49cc12d8..f93df85 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2987,6 +2987,8 @@
      IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIE_REMOVE_CONFIRMATION},
     {"siteSettingsCookiesClearThirdParty",
      IDS_SETTINGS_SITE_SETTINGS_CLEAR_THIRD_PARTY_COOKIES},
+    {"siteSettingsCookiesThirdPartyExceptionLabel",
+     IDS_SETTINGS_SITE_SETTINGS_THIRD_PARTY_COOKIES_EXCEPTION_LABEL},
     {"siteSettingsCookieRemoveDialogTitle",
      IDS_SETTINGS_SITE_SETTINGS_COOKIE_REMOVE_DIALOG_TITLE},
     {"siteSettingsCookieRemoveMultipleConfirmation",
diff --git a/chrome/browser/web_applications/components/app_registrar.cc b/chrome/browser/web_applications/components/app_registrar.cc
index b5c625e8..9025044 100644
--- a/chrome/browser/web_applications/components/app_registrar.cc
+++ b/chrome/browser/web_applications/components/app_registrar.cc
@@ -45,6 +45,11 @@
   RecordWebAppUninstallation(profile()->GetPrefs(), app_id);
 }
 
+void AppRegistrar::NotifyWebAppProfileWillBeDeleted(const AppId& app_id) {
+  for (AppRegistrarObserver& observer : observers_)
+    observer.OnWebAppProfileWillBeDeleted(app_id);
+}
+
 void AppRegistrar::NotifyAppRegistrarShutdown() {
   for (AppRegistrarObserver& observer : observers_)
     observer.OnAppRegistrarShutdown();
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h
index f4334f4d..06597c1 100644
--- a/chrome/browser/web_applications/components/app_registrar.h
+++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -102,6 +102,7 @@
   Profile* profile() const { return profile_; }
 
   void NotifyWebAppUninstalled(const AppId& app_id);
+  void NotifyWebAppProfileWillBeDeleted(const AppId& app_id);
   void NotifyAppRegistrarShutdown();
 
  private:
diff --git a/chrome/browser/web_applications/components/app_registrar_observer.h b/chrome/browser/web_applications/components/app_registrar_observer.h
index 8462f1f5..08830d2b 100644
--- a/chrome/browser/web_applications/components/app_registrar_observer.h
+++ b/chrome/browser/web_applications/components/app_registrar_observer.h
@@ -14,6 +14,7 @@
  public:
   virtual void OnWebAppInstalled(const AppId& app_id) {}
   virtual void OnWebAppUninstalled(const AppId& app_id) {}
+  virtual void OnWebAppProfileWillBeDeleted(const AppId& app_id) {}
   virtual void OnAppRegistrarShutdown() {}
   virtual void OnAppRegistrarDestroyed() {}
 };
diff --git a/chrome/browser/web_applications/components/file_handler_manager.cc b/chrome/browser/web_applications/components/file_handler_manager.cc
index 4b52699..44a8c4e 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.cc
+++ b/chrome/browser/web_applications/components/file_handler_manager.cc
@@ -42,6 +42,13 @@
   }
 }
 
+void FileHandlerManager::OnWebAppProfileWillBeDeleted(const AppId& app_id) {
+  if (base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI) &&
+      OsSupportsWebAppFileHandling()) {
+    UnregisterFileHandlersForWebApp(app_id, *profile_);
+  }
+}
+
 void FileHandlerManager::OnAppRegistrarDestroyed() {
   registrar_observer_.RemoveAll();
 }
diff --git a/chrome/browser/web_applications/components/file_handler_manager.h b/chrome/browser/web_applications/components/file_handler_manager.h
index a88aee9..d43e01a 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.h
+++ b/chrome/browser/web_applications/components/file_handler_manager.h
@@ -44,6 +44,7 @@
   // AppRegistrarObserver:
   void OnWebAppInstalled(const AppId& app_id) override;
   void OnWebAppUninstalled(const AppId& app_id) override;
+  void OnWebAppProfileWillBeDeleted(const AppId& app_id) override;
   void OnAppRegistrarDestroyed() override;
 
   ScopedObserver<AppRegistrar, AppRegistrarObserver> registrar_observer_;
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
index 1c8317d..e076fbac 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -70,13 +70,26 @@
 
 void BookmarkAppRegistrar::OnExtensionUninstalled(
     content::BrowserContext* browser_context,
-    const extensions::Extension* extension,
-    extensions::UninstallReason reason) {
+    const Extension* extension,
+    UninstallReason reason) {
   DCHECK_EQ(browser_context, profile());
   if (extension->from_bookmark())
     NotifyWebAppUninstalled(extension->id());
 }
 
+void BookmarkAppRegistrar::OnExtensionUnloaded(
+    content::BrowserContext* browser_context,
+    const Extension* extension,
+    UnloadedExtensionReason reason) {
+  DCHECK_EQ(browser_context, profile());
+  if (!extension->from_bookmark())
+    return;
+  // If a profile is removed, notify the web app that it is uninstalled, so it
+  // can cleanup any state outside the profile dir (e.g., registry settings).
+  if (reason == UnloadedExtensionReason::PROFILE_SHUTDOWN)
+    NotifyWebAppProfileWillBeDeleted(extension->id());
+}
+
 void BookmarkAppRegistrar::OnShutdown(ExtensionRegistry* registry) {
   NotifyAppRegistrarShutdown();
   extension_observer_.RemoveAll();
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
index 7b4f625a..d012abe5 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
@@ -46,6 +46,9 @@
   void OnExtensionUninstalled(content::BrowserContext* browser_context,
                               const Extension* extension,
                               UninstallReason reason) override;
+  void OnExtensionUnloaded(content::BrowserContext* browser_context,
+                           const Extension* extension,
+                           UnloadedExtensionReason reason) override;
   void OnShutdown(ExtensionRegistry* registry) override;
 
  private:
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index cde3851..cb5c87d 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -29,7 +29,6 @@
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/safe_browsing/buildflags.h"
 #include "content/public/renderer/render_frame.h"
@@ -62,6 +61,7 @@
 using blink::WebFormElement;
 using blink::WebFrame;
 using blink::WebInputElement;
+using blink::WebLocalFrame;
 using blink::WebString;
 using testing::_;
 
@@ -262,6 +262,18 @@
                        read_only ? WebString::FromUTF8("true") : WebString());
 }
 
+bool FormHasFieldWithValue(const autofill::FormData& form,
+                           const std::string& value) {
+  base::string16 value_16 = ASCIIToUTF16(value);
+  for (const auto& field : form.fields) {
+    if (field.value == value_16)
+      return true;
+    if (field.typed_value == value_16)
+      return true;
+  }
+  return false;
+}
+
 enum PasswordFormSourceType {
   PasswordFormSubmitted,
   PasswordFormSameDocumentNavigation,
@@ -606,24 +618,6 @@
     return fake_driver_.called_show_pw_suggestions();
   }
 
-  void ExpectFormSubmittedWithUsernameAndPasswords(
-      const std::string& username_value,
-      const std::string& password_value,
-      const std::string& new_password_value) {
-    base::RunLoop().RunUntilIdle();
-    ASSERT_TRUE(fake_driver_.called_password_form_submitted());
-    ASSERT_TRUE(static_cast<bool>(fake_driver_.password_form_submitted()));
-    const autofill::PasswordForm& form =
-        *(fake_driver_.password_form_submitted());
-    EXPECT_EQ(ASCIIToUTF16(username_value), form.username_value);
-    EXPECT_EQ(ASCIIToUTF16(password_value), form.password_value);
-    EXPECT_EQ(ASCIIToUTF16(new_password_value), form.new_password_value);
-    EXPECT_EQ(SubmissionIndicatorEvent::HTML_FORM_SUBMISSION,
-              form.submission_event);
-    EXPECT_EQ(SubmissionIndicatorEvent::HTML_FORM_SUBMISSION,
-              form.form_data.submission_event);
-  }
-
   void ExpectFieldPropertiesMasks(
       PasswordFormSourceType expected_type,
       const std::map<base::string16, FieldPropertiesMask>&
@@ -658,7 +652,45 @@
         << "Some expected masks are missed in FormData";
   }
 
+  uint32_t GetFormUniqueRendererId(const WebString& form_id) {
+    WebLocalFrame* frame = GetMainFrame();
+    if (!frame)
+      return FormData::kNotSetFormRendererId;
+    WebFormElement web_form =
+        frame->GetDocument().GetElementById(form_id).To<WebFormElement>();
+    return web_form.UniqueRendererFormId();
+  }
+
+  void ExpectFormWithUsernameAndPasswordsAndEvent(
+      const autofill::PasswordForm& form,
+      uint32_t form_rendere_id,
+      const std::string& username_value,
+      const std::string& password_value,
+      const std::string& new_password_value,
+      SubmissionIndicatorEvent event) {
+    EXPECT_EQ(form_rendere_id, form.form_data.unique_renderer_id);
+    EXPECT_TRUE(FormHasFieldWithValue(form.form_data, username_value));
+    EXPECT_TRUE(FormHasFieldWithValue(form.form_data, password_value));
+    EXPECT_TRUE(FormHasFieldWithValue(form.form_data, new_password_value));
+    EXPECT_EQ(form.form_data.submission_event, event);
+  }
+
+  void ExpectFormSubmittedWithUsernameAndPasswords(
+      uint32_t form_rendere_id,
+      const std::string& username_value,
+      const std::string& password_value,
+      const std::string& new_password_value) {
+    base::RunLoop().RunUntilIdle();
+    ASSERT_TRUE(fake_driver_.called_password_form_submitted());
+    ASSERT_TRUE(static_cast<bool>(fake_driver_.password_form_submitted()));
+    ExpectFormWithUsernameAndPasswordsAndEvent(
+        *(fake_driver_.password_form_submitted()), form_rendere_id,
+        username_value, password_value, new_password_value,
+        SubmissionIndicatorEvent::HTML_FORM_SUBMISSION);
+  }
+
   void ExpectSameDocumentNavigationWithUsernameAndPasswords(
+      uint32_t form_rendere_id,
       const std::string& username_value,
       const std::string& password_value,
       const std::string& new_password_value,
@@ -667,13 +699,9 @@
     ASSERT_TRUE(fake_driver_.called_same_document_navigation());
     ASSERT_TRUE(
         static_cast<bool>(fake_driver_.password_form_maybe_submitted()));
-    const autofill::PasswordForm& form =
-        *(fake_driver_.password_form_maybe_submitted());
-    EXPECT_EQ(ASCIIToUTF16(username_value), form.username_value);
-    EXPECT_EQ(ASCIIToUTF16(password_value), form.password_value);
-    EXPECT_EQ(ASCIIToUTF16(new_password_value), form.new_password_value);
-    EXPECT_EQ(event, form.submission_event);
-    EXPECT_EQ(event, form.form_data.submission_event);
+    ExpectFormWithUsernameAndPasswordsAndEvent(
+        *(fake_driver_.password_form_maybe_submitted()), form_rendere_id,
+        username_value, password_value, new_password_value, event);
   }
 
   void CheckIfEventsAreCalled(const std::vector<base::string16>& checkers,
@@ -1117,10 +1145,14 @@
   fake_driver_.reset_password_forms_calls();
   LoadHTML(kFormWithoutPasswordsHTML);
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(fake_driver_.called_password_forms_parsed());
+
+  EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
+  ASSERT_TRUE(fake_driver_.password_forms_parsed());
+  EXPECT_FALSE(fake_driver_.password_forms_parsed()->empty());
+
   EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
   ASSERT_TRUE(fake_driver_.password_forms_rendered());
-  EXPECT_TRUE(fake_driver_.password_forms_rendered()->empty());
+  EXPECT_FALSE(fake_driver_.password_forms_rendered()->empty());
 }
 
 TEST_F(PasswordAutofillAgentTest,
@@ -1133,7 +1165,7 @@
       "document.getElementById('random_field').type = 'password';";
   ExecuteJavaScriptForTests(script.c_str());
   base::RunLoop().RunUntilIdle();
-  EXPECT_FALSE(fake_driver_.called_password_forms_parsed());
+  EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
 
   // When the user clicks on the field, a request to the store will be sent.
   EXPECT_TRUE(SimulateElementClick("random_field"));
@@ -1141,6 +1173,10 @@
   EXPECT_TRUE(fake_driver_.called_password_forms_parsed());
   ASSERT_TRUE(fake_driver_.password_forms_parsed());
   EXPECT_FALSE(fake_driver_.password_forms_parsed()->empty());
+
+  EXPECT_TRUE(fake_driver_.called_password_forms_rendered());
+  ASSERT_TRUE(fake_driver_.password_forms_rendered());
+  EXPECT_FALSE(fake_driver_.password_forms_rendered()->empty());
 }
 
 TEST_F(PasswordAutofillAgentTest, SendPasswordFormsTest_NonDisplayedForm) {
@@ -1909,7 +1945,8 @@
 
   // Observe that the PasswordAutofillAgent still remembered the last non-empty
   // username and password and sent that to the browser.
-  ExpectFormSubmittedWithUsernameAndPasswords("username", "", "random");
+  ExpectFormSubmittedWithUsernameAndPasswords(GetFormUniqueRendererId("form"),
+                                              "username", "", "random");
 
   // Also check that |*_element| fields are correct.
   const autofill::PasswordForm& form =
@@ -1938,7 +1975,8 @@
 
   // Observe that the PasswordAutofillAgent respects the user having cleared the
   // password.
-  ExpectFormSubmittedWithUsernameAndPasswords("", "", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "", "", "");
 }
 
 // Similar to RememberLastNonEmptyPasswordOnSubmit_ScriptCleared, but uses the
@@ -1966,7 +2004,8 @@
 
   // Observe that the PasswordAutofillAgent still remembered the last non-empty
   // password and sent that to the browser.
-  ExpectFormSubmittedWithUsernameAndPasswords("temp", "", "random");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "temp", "", "random");
 }
 
 // The user first accepts a suggestion, but then overwrites the password. This
@@ -2030,7 +2069,8 @@
 
   // Observe that the PasswordAutofillAgent still remembered the last typed
   // username and password and sent that to the browser.
-  ExpectFormSubmittedWithUsernameAndPasswords("temp", "random", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "temp", "random", "");
 }
 
 TEST_F(PasswordAutofillAgentTest, RememberFieldPropertiesOnSubmit) {
@@ -2130,8 +2170,9 @@
 
   // Observe that the PasswordAutofillAgent still remembered the autofilled
   // username and password and sent that to the browser.
-  ExpectFormSubmittedWithUsernameAndPasswords(kAliceUsername, kAlicePassword,
-                                              "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), kAliceUsername, kAlicePassword,
+      "");
 }
 
 // The username/password is autofilled by password manager then user types in a
@@ -2155,7 +2196,8 @@
 
   // Observe that the PasswordAutofillAgent still remembered the last typed
   // username and password and sent that to the browser.
-  ExpectFormSubmittedWithUsernameAndPasswords("temp", "random", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "temp", "random", "");
 }
 
 // The user starts typing username then it is autofilled.
@@ -2171,7 +2213,8 @@
 
   // Observe that the PasswordAutofillAgent still remembered the last typed
   // username and password and sent that to the browser.
-  ExpectFormSubmittedWithUsernameAndPasswords("temp", "random", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "temp", "random", "");
 }
 
 // The user starts typing username then javascript suggests to select another
@@ -2194,7 +2237,8 @@
 
   // Observe that the PasswordAutofillAgent still remembered the last typed
   // username and password and sent that to the browser.
-  ExpectFormSubmittedWithUsernameAndPasswords("foo.smith", "random", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "foo.smith", "random", "");
 }
 
 // If credentials contain username+password but the form contains only a
@@ -2315,7 +2359,8 @@
 
   // Observe that the PasswordAutofillAgent can correctly process submitted
   // form.
-  ExpectFormSubmittedWithUsernameAndPasswords("temp", "random", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "temp", "random", "");
 }
 
 // Verify that typed passwords are saved correctly when autofill and generation
@@ -2335,7 +2380,8 @@
 
   SaveAndSubmitForm();
 
-  ExpectFormSubmittedWithUsernameAndPasswords("NewGuy", "NewPassword", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), "NewGuy", "NewPassword", "");
 }
 
 // Verify that generated passwords are saved correctly when autofill and
@@ -2359,7 +2405,9 @@
 
   SaveAndSubmitForm();
 
-  ExpectFormSubmittedWithUsernameAndPasswords(kAliceUsername, "NewPass22", "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), kAliceUsername, "NewPass22",
+      "");
 }
 
 TEST_F(PasswordAutofillAgentTest,
@@ -2392,8 +2440,9 @@
 
   SaveAndSubmitForm();
 
-  ExpectFormSubmittedWithUsernameAndPasswords(kAliceUsername, kAlicePassword,
-                                              "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), kAliceUsername, kAlicePassword,
+      "");
 }
 
 // If password generation is enabled for a field, password autofill should not
@@ -2533,7 +2582,8 @@
   FireAjaxSucceeded();
 
   ExpectSameDocumentNavigationWithUsernameAndPasswords(
-      "Bob", "mypassword", "", SubmissionIndicatorEvent::XHR_SUCCEEDED);
+      FormData::kNotSetFormRendererId, "Bob", "mypassword", "",
+      SubmissionIndicatorEvent::XHR_SUCCEEDED);
 }
 
 TEST_F(PasswordAutofillAgentTest,
@@ -2556,7 +2606,7 @@
   base::RunLoop().RunUntilIdle();
 
   ExpectSameDocumentNavigationWithUsernameAndPasswords(
-      "Bob", "mypassword", "",
+      FormData::kNotSetFormRendererId, "Bob", "mypassword", "",
       SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR);
 }
 
@@ -2580,7 +2630,7 @@
   base::RunLoop().RunUntilIdle();
 
   ExpectSameDocumentNavigationWithUsernameAndPasswords(
-      "Bob", "mypassword", "",
+      GetFormUniqueRendererId("form"), "Bob", "mypassword", "",
       SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR);
 }
 
@@ -2591,6 +2641,7 @@
        PromptForAJAXSubmitAfterDeletingParentElement) {
   LoadHTML(kDivWrappedFormHTML);
   UpdateUsernameAndPasswordElements();
+  uint32_t renderer_id = GetFormUniqueRendererId("form");
 
   SimulateUsernameTyping("Bob");
   SimulatePasswordTyping("mypassword");
@@ -2606,7 +2657,7 @@
   base::RunLoop().RunUntilIdle();
 
   ExpectSameDocumentNavigationWithUsernameAndPasswords(
-      "Bob", "mypassword", "",
+      renderer_id, "Bob", "mypassword", "",
       SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR);
 }
 
@@ -2980,7 +3031,8 @@
 
   // Observe that the PasswordAutofillAgent sends to the browser selected
   // credentials.
-  ExpectFormSubmittedWithUsernameAndPasswords(kBobUsername, kBobPassword, "");
+  ExpectFormSubmittedWithUsernameAndPasswords(
+      GetFormUniqueRendererId("LoginTestForm"), kBobUsername, kBobPassword, "");
 }
 
 // Tests that we can correctly suggest to autofill two forms without username
@@ -3023,7 +3075,8 @@
     FireAjaxSucceeded();
 
     ExpectSameDocumentNavigationWithUsernameAndPasswords(
-        "Alice", "mypassword", "", SubmissionIndicatorEvent::XHR_SUCCEEDED);
+        FormData::kNotSetFormRendererId, "Alice", "mypassword", "",
+        SubmissionIndicatorEvent::XHR_SUCCEEDED);
   }
 }
 
@@ -3048,7 +3101,7 @@
   base::RunLoop().RunUntilIdle();
 
   ExpectSameDocumentNavigationWithUsernameAndPasswords(
-      "Alice", "mypassword", "",
+      FormData::kNotSetFormRendererId, "Alice", "mypassword", "",
       SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR);
 }
 
@@ -3075,7 +3128,8 @@
 
     SaveAndSubmitForm();
 
-    ExpectFormSubmittedWithUsernameAndPasswords("Alice", "mypassword", "");
+    ExpectFormSubmittedWithUsernameAndPasswords(
+        GetFormUniqueRendererId("LoginTestForm"), "Alice", "mypassword", "");
   }
 }
 
@@ -3187,6 +3241,7 @@
        SameDocumentNavigationSubmissionUsernameIsEmpty) {
   username_element_.SetValue(WebString());
   SimulatePasswordTyping("random");
+  uint32_t renderer_id = GetFormUniqueRendererId("LoginTestForm");
 
   // Simulate that JavaScript removes the submitted form from DOM. That means
   // that a submission was successful.
@@ -3198,15 +3253,17 @@
   FireDidCommitProvisionalLoad();
 
   ExpectSameDocumentNavigationWithUsernameAndPasswords(
-      std::string(), "random", std::string(),
+      renderer_id, std::string(), "random", std::string(),
       SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION);
 }
 
 #if BUILDFLAG(SAFE_BROWSING_DB_LOCAL)
 // Verify CheckSafeBrowsingReputation() is called when user starts filling
 // username or password field, and that this function is only called once.
-TEST_F(PasswordAutofillAgentTest,
-       CheckSafeBrowsingReputationWhenUserStartsFillingUsernamePassword) {
+// TODO(crbug.com/1008402): Enable this test after fixing the linked bug.
+TEST_F(
+    PasswordAutofillAgentTest,
+    DISABLED_CheckSafeBrowsingReputationWhenUserStartsFillingUsernamePassword) {
   ASSERT_EQ(0, fake_driver_.called_check_safe_browsing_reputation_cnt());
   // Simulate a click on password field to set its on focus,
   // CheckSafeBrowsingReputation() should be called.
diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
index 2494458..96a335c 100644
--- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
@@ -381,11 +381,6 @@
   blink::WebString form_signature_in_username = username_element.GetAttribute(
       blink::WebString::FromUTF8("form_signature"));
   EXPECT_EQ(kFormSignature, form_signature_in_username.Ascii());
-  EXPECT_EQ(
-      "username_element",
-      username_element
-          .GetAttribute(blink::WebString::FromUTF8("pm_parser_annotation"))
-          .Ascii());
 
   blink::WebElement password_element =
       document.GetElementById(blink::WebString::FromUTF8("first_password"));
@@ -397,14 +392,6 @@
   blink::WebString form_signature_in_password = password_element.GetAttribute(
       blink::WebString::FromUTF8("form_signature"));
   EXPECT_EQ(kFormSignature, form_signature_in_password.Ascii());
-  // The parser annotation is based on local heuristics, but not server side
-  // prediction. So, the new password element is classified as the current
-  // password.
-  EXPECT_EQ(
-      "password_element",
-      password_element
-          .GetAttribute(blink::WebString::FromUTF8("pm_parser_annotation"))
-          .Ascii());
 
   // Check the generation element is marked.
   blink::WebString generation_mark = password_element.GetAttribute(
@@ -414,14 +401,6 @@
 
   blink::WebElement confirmation_password_element =
       document.GetElementById(blink::WebString::FromUTF8("second_password"));
-  // The parser annotation is based on local heuristics, but not server side
-  // prediction. So, the confirmation password element is classified as the
-  // new password.
-  EXPECT_EQ(
-      "new_password_element",
-      confirmation_password_element
-          .GetAttribute(blink::WebString::FromUTF8("pm_parser_annotation"))
-          .Ascii());
 }
 
 TEST_F(PasswordGenerationAgentTest, HiddenSecondPasswordDetectionTest) {
diff --git a/chrome/test/data/password/captured_sites/testcases.json b/chrome/test/data/password/captured_sites/testcases.json
index c2534b8..0c4cdf54 100644
--- a/chrome/test/data/password/captured_sites/testcases.json
+++ b/chrome/test/data/password/captured_sites/testcases.json
@@ -6,7 +6,7 @@
     { "scenario_dir":"capture_update_pass", "site_name":"autodesk" },
     { "scenario_dir":"capture_update_pass", "site_name":"clever", "disabled":true, "bug_number":984662 },
     { "scenario_dir":"capture_update_pass", "site_name":"github", "disabled":true, "bug_number":951847 },
-    { "scenario_dir":"capture_update_pass", "site_name":"go_daddy", "disabled":true, "bug_number":984662 },
+    { "scenario_dir":"capture_update_pass", "site_name":"go_daddy" },
     { "scenario_dir":"capture_update_pass", "site_name":"grammarly" },
     { "scenario_dir":"capture_update_pass", "site_name":"indeed" },
     { "scenario_dir":"capture_update_pass", "site_name":"librus" },
@@ -21,7 +21,7 @@
     { "scenario_dir":"capture_update_pass", "site_name":"steam" },
     { "scenario_dir":"capture_update_pass", "site_name":"the_sims_resource" },
     { "scenario_dir":"capture_update_pass", "site_name":"united" },
-    { "scenario_dir":"capture_update_pass", "site_name":"wargaming", "disabled":true, "bug_number":984662 },
+    { "scenario_dir":"capture_update_pass", "site_name":"wargaming" },
     { "scenario_dir":"capture_update_pass", "site_name":"yahoo" },
     { "scenario_dir":"sign_in_pass", "site_name":"4shared" },
     { "scenario_dir":"sign_in_pass", "site_name":"airbnb", "disabled":true, "bug_number":951847 },
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index d641af6..a61de386 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -4060,6 +4060,12 @@
   "DeviceLoginScreenCursorHighlightEnabled" : {
   },
 
+  "DeviceLoginScreenCaretHighlightEnabled" : {
+  },
+
+  "DeviceLoginScreenMonoAudioEnabled" : {
+  },
+
   "DeviceLoginScreenLocales" : {
   },
 
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index ffaa061..4cc02d6e 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -2531,7 +2531,13 @@
   }
 };
 
-TEST_F('CrSettingsSplitSettingsFlagTest', 'All', function() {
+// Test is consistently failing. http://crbug.com/1008916
+GEN('#if !defined(NDEBUG)');
+GEN('#define MAYBE_All3 DISABLED_All');
+GEN('#else');
+GEN('#define MAYBE_All3 All');
+GEN('#endif');
+TEST_F('CrSettingsSplitSettingsFlagTest', 'MAYBE_All3', function() {
   mocha.run();
 });
 
diff --git a/chromecast/browser/android/BUILD.gn b/chromecast/browser/android/BUILD.gn
index 7f15cb25..55140db4 100644
--- a/chromecast/browser/android/BUILD.gn
+++ b/chromecast/browser/android/BUILD.gn
@@ -69,6 +69,10 @@
 
 android_library("cast_audio_manager_java") {
   java_src_dir = "//chromecast/browser/android/apk/src"
+
+  alternative_android_sdk_dep =
+      "//third_party/android_sdk:public_framework_system_java"
+
   java_files = [
     "$java_src_dir/org/chromium/chromecast/shell/CastAudioManager.java",
     "$java_src_dir/org/chromium/chromecast/shell/CastAudioFocusRequest.java",
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java
index d1b7246..43bbf7e7 100644
--- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java
+++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastAudioManager.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.media.AudioManager;
+import android.media.audiopolicy.AudioPolicy;
 import android.os.Build;
 import android.support.annotation.Nullable;
 
@@ -116,6 +117,14 @@
         return mInternal.getStreamMaxVolume(streamType);
     }
 
+    public int registerAudioPolicy(AudioPolicy audioPolicy) {
+        return mInternal.registerAudioPolicy(audioPolicy);
+    }
+
+    public void unregisterAudioPolicyAsync(AudioPolicy audioPolicy) {
+        mInternal.unregisterAudioPolicyAsync(audioPolicy);
+    }
+
     // TODO(sanfin): Do not expose this. All needed AudioManager methods can be adapted with
     // CastAudioManager.
     public AudioManager getInternal() {
diff --git a/chromecast/media/audio/BUILD.gn b/chromecast/media/audio/BUILD.gn
index 337825e..98225e55 100644
--- a/chromecast/media/audio/BUILD.gn
+++ b/chromecast/media/audio/BUILD.gn
@@ -23,6 +23,8 @@
     "cast_audio_mixer.h",
     "cast_audio_output_stream.cc",
     "cast_audio_output_stream.h",
+    "cma_audio_output_stream.cc",
+    "cma_audio_output_stream.h",
   ]
 
   deps = [
diff --git a/chromecast/media/audio/cast_audio_output_stream.cc b/chromecast/media/audio/cast_audio_output_stream.cc
index 3186245..a517348c 100644
--- a/chromecast/media/audio/cast_audio_output_stream.cc
+++ b/chromecast/media/audio/cast_audio_output_stream.cc
@@ -4,8 +4,6 @@
 
 #include "chromecast/media/audio/cast_audio_output_stream.h"
 
-#include <algorithm>
-#include <limits>
 #include <string>
 #include <utility>
 
@@ -22,23 +20,22 @@
 #include "chromecast/base/metrics/cast_metrics_helper.h"
 #include "chromecast/common/mojom/constants.mojom.h"
 #include "chromecast/media/audio/cast_audio_manager.h"
+#include "chromecast/media/audio/cma_audio_output_stream.h"
 #include "chromecast/media/audio/mixer_service/mixer_service.pb.h"
 #include "chromecast/media/audio/mixer_service/output_stream_connection.h"
-#include "chromecast/media/base/monotonic_clock.h"
 #include "chromecast/media/cma/backend/cma_backend_factory.h"
 #include "chromecast/public/cast_media_shlib.h"
 #include "chromecast/public/media/decoder_config.h"
 #include "chromecast/public/media/media_pipeline_device_params.h"
 #include "chromecast/public/volume_control.h"
 #include "media/audio/audio_device_description.h"
-#include "media/base/decoder_buffer.h"
 
 #define POST_TO_CMA_WRAPPER(method, ...)                                      \
   do {                                                                        \
     DCHECK(cma_wrapper_);                                                     \
     audio_manager_->media_task_runner()->PostTask(                            \
         FROM_HERE,                                                            \
-        base::BindOnce(&CmaWrapper::method,                                   \
+        base::BindOnce(&CmaAudioOutputStream::method,                         \
                        base::Unretained(cma_wrapper_.get()), ##__VA_ARGS__)); \
   } while (0)
 
@@ -57,7 +54,6 @@
 constexpr base::TimeDelta kFadeTime = base::TimeDelta::FromMilliseconds(5);
 constexpr base::TimeDelta kMixerStartThreshold =
     base::TimeDelta::FromMilliseconds(60);
-constexpr base::TimeDelta kRenderBufferSize = base::TimeDelta::FromSeconds(4);
 }  // namespace
 
 namespace chromecast {
@@ -96,377 +92,6 @@
 
 }  // namespace
 
-class CastAudioOutputStream::CmaWrapper : public CmaBackend::Decoder::Delegate {
- public:
-  CmaWrapper(scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
-             const ::media::AudioParameters& audio_params,
-             const std::string& device_id,
-             CmaBackendFactory* cma_backend_factory);
-
-  void SetRunning(bool running);
-  void Initialize(const std::string& application_session_id,
-                  chromecast::mojom::MultiroomInfoPtr multiroom_info);
-  void Start(AudioSourceCallback* source_callback);
-  void Stop(base::WaitableEvent* finished);
-  void Flush(base::WaitableEvent* finished);
-  void Close(base::OnceClosure closure);
-  void SetVolume(double volume);
-
- private:
-  enum class CmaBackendState {
-    kUinitialized,
-    kStopped,
-    kPaused,
-    kStarted,
-  };
-
-  void PushBuffer();
-
-  // CmaBackend::Decoder::Delegate implementation:
-  void OnEndOfStream() override {}
-  void OnDecoderError() override;
-  void OnKeyStatusChanged(const std::string& key_id,
-                          CastKeyStatus key_status,
-                          uint32_t system_code) override {}
-  void OnVideoResolutionChanged(const Size& size) override {}
-  void OnPushBufferComplete(BufferStatus status) override;
-
-  const bool is_audio_prefetch_;
-
-  scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner_;
-  const ::media::AudioParameters audio_params_;
-  const std::string device_id_;
-  CmaBackendFactory* const cma_backend_factory_;
-
-  base::Lock running_lock_;
-  bool running_ = true;
-  AudioOutputState media_thread_state_;
-  CmaBackendState cma_backend_state_ = CmaBackendState::kUinitialized;
-  ::media::AudioTimestampHelper timestamp_helper_;
-  const base::TimeDelta buffer_duration_;
-  std::unique_ptr<TaskRunnerImpl> cma_backend_task_runner_;
-  std::unique_ptr<CmaBackend> cma_backend_;
-  std::unique_ptr<::media::AudioBus> audio_bus_;
-  base::OneShotTimer push_timer_;
-  bool push_in_progress_;
-  bool encountered_error_;
-  base::TimeTicks next_push_time_;
-  base::TimeTicks last_push_complete_time_;
-  base::TimeDelta last_rendering_delay_;
-  base::TimeDelta render_buffer_size_estimate_ = kRenderBufferSize;
-  CmaBackend::AudioDecoder* audio_decoder_;
-  AudioSourceCallback* source_callback_;
-
-  THREAD_CHECKER(media_thread_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(CmaWrapper);
-};
-
-CastAudioOutputStream::CmaWrapper::CmaWrapper(
-    scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
-    const ::media::AudioParameters& audio_params,
-    const std::string& device_id,
-    CmaBackendFactory* cma_backend_factory)
-    : is_audio_prefetch_(audio_params.effects() &
-                         ::media::AudioParameters::AUDIO_PREFETCH),
-      audio_task_runner_(audio_task_runner),
-      audio_params_(audio_params),
-      device_id_(device_id),
-      cma_backend_factory_(cma_backend_factory),
-      media_thread_state_(kClosed),
-      timestamp_helper_(audio_params_.sample_rate()),
-      buffer_duration_(audio_params_.GetBufferDuration()),
-      render_buffer_size_estimate_(kRenderBufferSize) {
-  DETACH_FROM_THREAD(media_thread_checker_);
-  DCHECK(audio_task_runner_);
-  DCHECK(cma_backend_factory_);
-
-  LOG(INFO) << "Enable audio prefetch: " << is_audio_prefetch_;
-
-  // Set the default state.
-  push_in_progress_ = false;
-  encountered_error_ = false;
-  audio_decoder_ = nullptr;
-  source_callback_ = nullptr;
-}
-
-void CastAudioOutputStream::CmaWrapper::SetRunning(bool running) {
-  base::AutoLock lock(running_lock_);
-  running_ = running;
-}
-
-void CastAudioOutputStream::CmaWrapper::Initialize(
-    const std::string& application_session_id,
-    chromecast::mojom::MultiroomInfoPtr multiroom_info) {
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  DCHECK(cma_backend_factory_);
-
-  if (media_thread_state_ != kClosed)
-    return;
-  media_thread_state_ = kOpened;
-
-  cma_backend_task_runner_ = std::make_unique<TaskRunnerImpl>();
-  MediaPipelineDeviceParams device_params(
-      MediaPipelineDeviceParams::kModeIgnorePts,
-      MediaPipelineDeviceParams::kAudioStreamNormal,
-      cma_backend_task_runner_.get(), GetContentType(device_id_), device_id_);
-  device_params.session_id = application_session_id;
-  device_params.multiroom = multiroom_info->multiroom;
-  device_params.audio_channel = multiroom_info->audio_channel;
-  device_params.output_delay_us = multiroom_info->output_delay.InMicroseconds();
-  cma_backend_ = cma_backend_factory_->CreateBackend(device_params);
-  if (!cma_backend_) {
-    encountered_error_ = true;
-    return;
-  }
-
-  audio_decoder_ = cma_backend_->CreateAudioDecoder();
-  if (!audio_decoder_) {
-    encountered_error_ = true;
-    return;
-  }
-  audio_decoder_->SetDelegate(this);
-
-  AudioConfig audio_config;
-  audio_config.codec = kCodecPCM;
-  audio_config.channel_layout =
-      ChannelLayoutFromChannelNumber(audio_params_.channels());
-  audio_config.sample_format = kSampleFormatS16;
-  audio_config.bytes_per_channel = 2;
-  audio_config.channel_number = audio_params_.channels();
-  audio_config.samples_per_second = audio_params_.sample_rate();
-  DCHECK(IsValidConfig(audio_config));
-  if (!audio_decoder_->SetConfig(audio_config)) {
-    encountered_error_ = true;
-    return;
-  }
-
-  if (!cma_backend_->Initialize()) {
-    encountered_error_ = true;
-    return;
-  }
-  cma_backend_state_ = CmaBackendState::kStopped;
-
-  audio_bus_ = ::media::AudioBus::Create(audio_params_);
-  timestamp_helper_.SetBaseTimestamp(base::TimeDelta());
-}
-
-void CastAudioOutputStream::CmaWrapper::Start(
-    AudioSourceCallback* source_callback) {
-  DCHECK(source_callback);
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  if (media_thread_state_ == kPendingClose)
-    return;
-
-  source_callback_ = source_callback;
-  if (encountered_error_) {
-    source_callback_->OnError();
-    return;
-  }
-
-  if (media_thread_state_ == kOpened) {
-    DCHECK(cma_backend_state_ == CmaBackendState::kPaused ||
-           cma_backend_state_ == CmaBackendState::kStopped);
-    if (cma_backend_state_ == CmaBackendState::kPaused) {
-      cma_backend_->Resume();
-    } else {
-      cma_backend_->Start(0);
-      render_buffer_size_estimate_ = kRenderBufferSize;
-    }
-    next_push_time_ = base::TimeTicks::Now();
-    last_push_complete_time_ = base::TimeTicks::Now();
-    cma_backend_state_ = CmaBackendState::kStarted;
-    media_thread_state_ = kStarted;
-  }
-
-  if (!push_in_progress_) {
-    push_in_progress_ = true;
-    PushBuffer();
-  }
-}
-
-void CastAudioOutputStream::CmaWrapper::Stop(base::WaitableEvent* finished) {
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  // Prevent further pushes to the audio buffer after stopping.
-  push_timer_.Stop();
-  // Don't actually stop the backend.  Stop() gets called when the stream is
-  // paused.  We rely on Flush() to stop the backend.
-  if (cma_backend_) {
-    cma_backend_->Pause();
-    cma_backend_state_ = CmaBackendState::kPaused;
-  }
-  push_in_progress_ = false;
-  media_thread_state_ = kOpened;
-  source_callback_ = nullptr;
-  finished->Signal();
-}
-
-void CastAudioOutputStream::CmaWrapper::Flush(base::WaitableEvent* finished) {
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  // Prevent further pushes to the audio buffer after stopping.
-  push_timer_.Stop();
-
-  if (cma_backend_ &&
-      (media_thread_state_ == kStarted || media_thread_state_ == kOpened)) {
-    if (cma_backend_state_ == CmaBackendState::kPaused ||
-        cma_backend_state_ == CmaBackendState::kStarted) {
-      cma_backend_->Stop();
-      cma_backend_state_ = CmaBackendState::kStopped;
-    }
-  }
-  push_in_progress_ = false;
-  media_thread_state_ = kOpened;
-  source_callback_ = nullptr;
-  finished->Signal();
-}
-
-void CastAudioOutputStream::CmaWrapper::Close(base::OnceClosure closure) {
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  // Prevent further pushes to the audio buffer after stopping.
-  push_timer_.Stop();
-  // Only stop the backend if it was started.
-  if (cma_backend_ && cma_backend_state_ != CmaBackendState::kStopped) {
-    cma_backend_->Stop();
-    cma_backend_state_ = CmaBackendState::kStopped;
-  }
-  push_in_progress_ = false;
-  media_thread_state_ = kPendingClose;
-
-  cma_backend_task_runner_.reset();
-  cma_backend_.reset();
-  audio_bus_.reset();
-
-  audio_task_runner_->PostTask(FROM_HERE, std::move(closure));
-}
-
-void CastAudioOutputStream::CmaWrapper::SetVolume(double volume) {
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  if (!audio_decoder_) {
-    return;
-  }
-  if (encountered_error_) {
-    return;
-  }
-  audio_decoder_->SetVolume(volume);
-}
-
-void CastAudioOutputStream::CmaWrapper::PushBuffer() {
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-
-  // Acquire running_lock_ for the scope of this push call to
-  // prevent the source callback from closing the output stream
-  // mid-push.
-  base::AutoLock lock(running_lock_);
-
-  // Do not fill more buffers if we have stopped running.
-  if (!running_)
-    return;
-
-  // It is possible that this function is called when we are stopped.
-  // Return quickly if so.
-  if (!source_callback_ || encountered_error_ ||
-      media_thread_state_ != kStarted) {
-    push_in_progress_ = false;
-    return;
-  }
-  DCHECK(push_in_progress_);
-
-  CmaBackend::AudioDecoder::RenderingDelay rendering_delay =
-      audio_decoder_->GetRenderingDelay();
-
-  base::TimeDelta delay;
-  if (rendering_delay.delay_microseconds < 0 ||
-      rendering_delay.timestamp_microseconds < 0) {
-    // This occurs immediately after start/resume when there isn't a good
-    // estimate of the buffer delay.  Use the last known good delay.
-    delay = last_rendering_delay_;
-  } else {
-    // The rendering delay to account for buffering is not included in
-    // rendering_delay.delay_microseconds but is in delay_timestamp which isn't
-    // used by AudioOutputStreamImpl.
-    delay = base::TimeDelta::FromMicroseconds(
-        rendering_delay.delay_microseconds +
-        rendering_delay.timestamp_microseconds - MonotonicClockNow());
-    if (delay.InMicroseconds() < 0) {
-      delay = base::TimeDelta();
-    }
-  }
-  last_rendering_delay_ = delay;
-
-  int frame_count = source_callback_->OnMoreData(delay, base::TimeTicks(), 0,
-                                                 audio_bus_.get());
-
-  DVLOG(3) << "frames_filled=" << frame_count << " with latency=" << delay;
-
-  if (frame_count == 0) {
-    OnPushBufferComplete(CmaBackend::BufferStatus::kBufferFailed);
-    return;
-  }
-  auto decoder_buffer =
-      base::MakeRefCounted<DecoderBufferAdapter>(new ::media::DecoderBuffer(
-          audio_params_.GetBytesPerBuffer(::media::kSampleFormatS16)));
-  audio_bus_->ToInterleaved<::media::SignedInt16SampleTypeTraits>(
-      frame_count, reinterpret_cast<int16_t*>(decoder_buffer->writable_data()));
-  decoder_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
-  timestamp_helper_.AddFrames(frame_count);
-
-  BufferStatus status = audio_decoder_->PushBuffer(std::move(decoder_buffer));
-  if (status != CmaBackend::BufferStatus::kBufferPending)
-    OnPushBufferComplete(status);
-}
-
-void CastAudioOutputStream::CmaWrapper::OnPushBufferComplete(
-    BufferStatus status) {
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-  DCHECK_NE(status, CmaBackend::BufferStatus::kBufferPending);
-
-  DCHECK(push_in_progress_);
-  push_in_progress_ = false;
-
-  if (!source_callback_ || encountered_error_)
-    return;
-
-  if (status != CmaBackend::BufferStatus::kBufferSuccess) {
-    source_callback_->OnError();
-    return;
-  }
-
-  // Schedule next push buffer.
-  const base::TimeTicks now = base::TimeTicks::Now();
-  base::TimeDelta delay;
-  if (is_audio_prefetch_) {
-    // For multizone-playback, we don't care about AV sync and want to pre-fetch
-    // audio.
-    render_buffer_size_estimate_ -= buffer_duration_;
-    render_buffer_size_estimate_ += now - last_push_complete_time_;
-    last_push_complete_time_ = now;
-
-    if (render_buffer_size_estimate_ >= buffer_duration_) {
-      delay = base::TimeDelta::FromSeconds(0);
-    } else {
-      delay = buffer_duration_;
-    }
-  } else {
-    next_push_time_ = std::max(now, next_push_time_ + buffer_duration_);
-    delay = next_push_time_ - now;
-  }
-
-  DVLOG(3) << "render_buffer_size_estimate_=" << render_buffer_size_estimate_
-           << " delay=" << delay << " buffer_duration_=" << buffer_duration_;
-
-  push_timer_.Start(FROM_HERE, delay, this, &CmaWrapper::PushBuffer);
-  push_in_progress_ = true;
-}
-
-void CastAudioOutputStream::CmaWrapper::OnDecoderError() {
-  DVLOG(1) << this << ": " << __func__;
-  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
-
-  encountered_error_ = true;
-  if (source_callback_)
-    source_callback_->OnError();
-}
-
 class CastAudioOutputStream::MixerServiceWrapper
     : public mixer_service::OutputStreamConnection::Delegate {
  public:
@@ -642,7 +267,7 @@
     const std::string& device_id_or_group_id,
     bool use_mixer_service)
     : volume_(1.0),
-      audio_thread_state_(kClosed),
+      audio_thread_state_(AudioOutputState::kClosed),
       audio_manager_(audio_manager),
       connector_(connector),
       audio_params_(audio_params),
@@ -659,6 +284,7 @@
   DETACH_FROM_THREAD(audio_thread_checker_);
   DVLOG(1) << __func__ << " " << this << " created from group_id=" << group_id_
            << " with audio_params=" << audio_params_.AsHumanReadableString();
+  audio_weak_this_ = audio_weak_factory_.GetWeakPtr();
 }
 
 CastAudioOutputStream::~CastAudioOutputStream() {
@@ -668,7 +294,7 @@
 bool CastAudioOutputStream::Open() {
   DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
   DVLOG(1) << this << ": " << __func__;
-  if (audio_thread_state_ != kClosed)
+  if (audio_thread_state_ != AudioOutputState::kClosed)
     return false;
 
   // Sanity check the audio parameters.
@@ -692,16 +318,15 @@
   // Connect to the Multiroom interface and fetch the current info.
   connector_->Connect(chromecast::mojom::kChromecastServiceName,
                       multiroom_manager_.BindNewPipeAndPassReceiver());
-  multiroom_manager_.set_disconnect_handler(
-      base::BindOnce(&CastAudioOutputStream::OnGetMultiroomInfo,
-                     audio_weak_factory_.GetWeakPtr(), "error",
-                     chromecast::mojom::MultiroomInfo::New()));
+  multiroom_manager_.set_disconnect_handler(base::BindOnce(
+      &CastAudioOutputStream::OnGetMultiroomInfo, audio_weak_this_, "error",
+      chromecast::mojom::MultiroomInfo::New()));
   multiroom_manager_->GetMultiroomInfo(
       application_session_id,
       base::BindOnce(&CastAudioOutputStream::OnGetMultiroomInfo,
-                     audio_weak_factory_.GetWeakPtr(), application_session_id));
+                     audio_weak_this_, application_session_id));
 
-  audio_thread_state_ = kOpened;
+  audio_thread_state_ = AudioOutputState::kOpened;
 
   // Always return success on the audio thread even though we are unsure at this
   // point if the backend has opened successfully. Errors will be reported via
@@ -717,9 +342,9 @@
   DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
   DVLOG(1) << this << ": " << __func__;
 
-  audio_thread_state_ = kPendingClose;
-  base::OnceClosure finish_callback = base::BindOnce(
-      &CastAudioOutputStream::FinishClose, audio_weak_factory_.GetWeakPtr());
+  audio_thread_state_ = AudioOutputState::kPendingClose;
+  base::OnceClosure finish_callback = BindToCurrentThread(
+      base::BindOnce(&CastAudioOutputStream::FinishClose, audio_weak_this_));
 
   if (mixer_service_wrapper_) {
     // Synchronously set running to false to guarantee that
@@ -749,9 +374,9 @@
   DCHECK(source_callback);
   DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
   // We allow calls to start even in the unopened state.
-  DCHECK(audio_thread_state_ != kPendingClose);
+  DCHECK_NE(audio_thread_state_, AudioOutputState::kPendingClose);
   DVLOG(2) << this << ": " << __func__;
-  audio_thread_state_ = kStarted;
+  audio_thread_state_ = AudioOutputState::kStarted;
   metrics::CastMetricsHelper::GetInstance()->LogTimeToFirstAudio();
 
   if (!cma_wrapper_ && !mixer_service_wrapper_) {
@@ -775,9 +400,9 @@
   DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
   DVLOG(2) << this << ": " << __func__;
   // We allow calls to stop even in the unstarted/unopened state.
-  if (audio_thread_state_ != kStarted)
+  if (audio_thread_state_ != AudioOutputState::kStarted)
     return;
-  audio_thread_state_ = kOpened;
+  audio_thread_state_ = AudioOutputState::kOpened;
   pending_start_.Reset();
   pending_volume_.Reset();
 
@@ -815,7 +440,7 @@
 
 void CastAudioOutputStream::SetVolume(double volume) {
   DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
-  DCHECK(audio_thread_state_ != kPendingClose);
+  DCHECK_NE(audio_thread_state_, AudioOutputState::kPendingClose);
   DVLOG(2) << this << ": " << __func__ << "(" << volume << ")";
   volume_ = volume;
 
@@ -851,13 +476,12 @@
   // Close the MultiroomManager message pipe so that a connection error does
   // not trigger a second call to this function.
   multiroom_manager_.reset();
-  if (audio_thread_state_ == kPendingClose)
+  if (audio_thread_state_ == AudioOutputState::kPendingClose)
     return;
 
   if (!use_mixer_service_) {
-    cma_wrapper_ = std::make_unique<CmaWrapper>(
-        audio_manager_->GetTaskRunner(), audio_params_, device_id_,
-        audio_manager_->cma_backend_factory());
+    cma_wrapper_ = std::make_unique<CmaAudioOutputStream>(
+        audio_params_, device_id_, audio_manager_->cma_backend_factory());
     POST_TO_CMA_WRAPPER(Initialize, application_session_id,
                         std::move(multiroom_info));
   } else {
diff --git a/chromecast/media/audio/cast_audio_output_stream.h b/chromecast/media/audio/cast_audio_output_stream.h
index e85c64e..c383668 100644
--- a/chromecast/media/audio/cast_audio_output_stream.h
+++ b/chromecast/media/audio/cast_audio_output_stream.h
@@ -16,8 +16,6 @@
 #include "build/build_config.h"
 #include "chromecast/base/task_runner_impl.h"
 #include "chromecast/common/mojom/multiroom.mojom.h"
-#include "chromecast/media/cma/backend/cma_backend.h"
-#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
 #include "media/audio/audio_io.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/audio_timestamp_helper.h"
@@ -27,13 +25,7 @@
 namespace chromecast {
 namespace media {
 
-enum AudioOutputState {
-  kClosed = 0,
-  kOpened = 1,
-  kStarted = 2,
-  kPendingClose = 3,
-};
-
+class CmaAudioOutputStream;
 class CastAudioManager;
 
 // Chromecast implementation of AudioOutputStream.
@@ -128,14 +120,18 @@
   void Flush() override;
 
  private:
-  class CmaWrapper;
+  enum class AudioOutputState {
+    kClosed,
+    kOpened,
+    kStarted,
+    kPendingClose,
+  };
+
   class MixerServiceWrapper;
 
   void FinishClose();
   void OnGetMultiroomInfo(const std::string& application_session_id,
                           chromecast::mojom::MultiroomInfoPtr multiroom_info);
-  void InitializeCmaBackend(const std::string& application_session_id,
-                            chromecast::mojom::MultiroomInfoPtr multiroom_info);
 
   double volume_;
   AudioOutputState audio_thread_state_;
@@ -149,7 +145,7 @@
   const std::string group_id_;
   const bool use_mixer_service_;
   mojo::Remote<chromecast::mojom::MultiroomManager> multiroom_manager_;
-  std::unique_ptr<CmaWrapper> cma_wrapper_;
+  std::unique_ptr<CmaAudioOutputStream> cma_wrapper_;
   std::unique_ptr<MixerServiceWrapper> mixer_service_wrapper_;
 
   // Hold bindings to Start and SetVolume if they were called before Open
@@ -159,6 +155,7 @@
   base::OnceCallback<void()> pending_volume_;
 
   THREAD_CHECKER(audio_thread_checker_);
+  base::WeakPtr<CastAudioOutputStream> audio_weak_this_;
   base::WeakPtrFactory<CastAudioOutputStream> audio_weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CastAudioOutputStream);
diff --git a/chromecast/media/audio/cma_audio_output_stream.cc b/chromecast/media/audio/cma_audio_output_stream.cc
new file mode 100644
index 0000000..031b3be
--- /dev/null
+++ b/chromecast/media/audio/cma_audio_output_stream.cc
@@ -0,0 +1,329 @@
+// Copyright 2019 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 "chromecast/media/audio/cma_audio_output_stream.h"
+
+#include <algorithm>
+#include <limits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/synchronization/waitable_event.h"
+#include "chromecast/base/task_runner_impl.h"
+#include "chromecast/media/base/monotonic_clock.h"
+#include "chromecast/media/cma/backend/cma_backend_factory.h"
+#include "chromecast/media/cma/base/decoder_buffer_adapter.h"
+#include "chromecast/public/media/media_pipeline_device_params.h"
+#include "chromecast/public/volume_control.h"
+#include "media/audio/audio_device_description.h"
+#include "media/base/audio_bus.h"
+#include "media/base/decoder_buffer.h"
+
+namespace chromecast {
+namespace media {
+
+namespace {
+
+constexpr base::TimeDelta kRenderBufferSize = base::TimeDelta::FromSeconds(4);
+
+AudioContentType GetContentType(const std::string& device_id) {
+  if (::media::AudioDeviceDescription::IsCommunicationsDevice(device_id)) {
+    return AudioContentType::kCommunication;
+  }
+  return AudioContentType::kMedia;
+}
+
+}  // namespace
+
+CmaAudioOutputStream::CmaAudioOutputStream(
+    const ::media::AudioParameters& audio_params,
+    const std::string& device_id,
+    CmaBackendFactory* cma_backend_factory)
+    : is_audio_prefetch_(audio_params.effects() &
+                         ::media::AudioParameters::AUDIO_PREFETCH),
+      audio_params_(audio_params),
+      device_id_(device_id),
+      cma_backend_factory_(cma_backend_factory),
+      timestamp_helper_(audio_params_.sample_rate()),
+      buffer_duration_(audio_params_.GetBufferDuration()),
+      render_buffer_size_estimate_(kRenderBufferSize) {
+  DCHECK(cma_backend_factory_);
+  DETACH_FROM_THREAD(media_thread_checker_);
+
+  LOG(INFO) << "Enable audio prefetch: " << is_audio_prefetch_;
+}
+
+CmaAudioOutputStream::~CmaAudioOutputStream() = default;
+
+void CmaAudioOutputStream::SetRunning(bool running) {
+  base::AutoLock lock(running_lock_);
+  running_ = running;
+}
+
+void CmaAudioOutputStream::Initialize(
+    const std::string& application_session_id,
+    chromecast::mojom::MultiroomInfoPtr multiroom_info) {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  DCHECK(cma_backend_factory_);
+
+  if (cma_backend_state_ != CmaBackendState::kUninitialized)
+    return;
+
+  cma_backend_task_runner_ = std::make_unique<TaskRunnerImpl>();
+  MediaPipelineDeviceParams device_params(
+      MediaPipelineDeviceParams::kModeIgnorePts,
+      MediaPipelineDeviceParams::kAudioStreamNormal,
+      cma_backend_task_runner_.get(), GetContentType(device_id_), device_id_);
+  device_params.session_id = application_session_id;
+  device_params.multiroom = multiroom_info->multiroom;
+  device_params.audio_channel = multiroom_info->audio_channel;
+  device_params.output_delay_us = multiroom_info->output_delay.InMicroseconds();
+  cma_backend_ = cma_backend_factory_->CreateBackend(device_params);
+  if (!cma_backend_) {
+    encountered_error_ = true;
+    return;
+  }
+
+  audio_decoder_ = cma_backend_->CreateAudioDecoder();
+  if (!audio_decoder_) {
+    encountered_error_ = true;
+    return;
+  }
+  audio_decoder_->SetDelegate(this);
+
+  AudioConfig audio_config;
+  audio_config.codec = kCodecPCM;
+  audio_config.channel_layout =
+      ChannelLayoutFromChannelNumber(audio_params_.channels());
+  audio_config.sample_format = kSampleFormatS16;
+  audio_config.bytes_per_channel = 2;
+  audio_config.channel_number = audio_params_.channels();
+  audio_config.samples_per_second = audio_params_.sample_rate();
+  DCHECK(IsValidConfig(audio_config));
+  if (!audio_decoder_->SetConfig(audio_config)) {
+    encountered_error_ = true;
+    return;
+  }
+
+  if (!cma_backend_->Initialize()) {
+    encountered_error_ = true;
+    return;
+  }
+  cma_backend_state_ = CmaBackendState::kStopped;
+
+  audio_bus_ = ::media::AudioBus::Create(audio_params_);
+  timestamp_helper_.SetBaseTimestamp(base::TimeDelta());
+}
+
+void CmaAudioOutputStream::Start(
+    ::media::AudioOutputStream::AudioSourceCallback* source_callback) {
+  DCHECK(source_callback);
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  if (cma_backend_state_ == CmaBackendState::kPendingClose)
+    return;
+
+  source_callback_ = source_callback;
+  if (encountered_error_) {
+    source_callback_->OnError();
+    return;
+  }
+
+  if (cma_backend_state_ == CmaBackendState::kPaused ||
+      cma_backend_state_ == CmaBackendState::kStopped) {
+    if (cma_backend_state_ == CmaBackendState::kPaused) {
+      cma_backend_->Resume();
+    } else {
+      cma_backend_->Start(0);
+      render_buffer_size_estimate_ = kRenderBufferSize;
+    }
+    next_push_time_ = base::TimeTicks::Now();
+    last_push_complete_time_ = base::TimeTicks::Now();
+    cma_backend_state_ = CmaBackendState::kStarted;
+  }
+
+  if (!push_in_progress_) {
+    push_in_progress_ = true;
+    PushBuffer();
+  }
+}
+
+void CmaAudioOutputStream::Stop(base::WaitableEvent* finished) {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  // Prevent further pushes to the audio buffer after stopping.
+  push_timer_.Stop();
+  // Don't actually stop the backend.  Stop() gets called when the stream is
+  // paused.  We rely on Flush() to stop the backend.
+  if (cma_backend_) {
+    cma_backend_->Pause();
+    cma_backend_state_ = CmaBackendState::kPaused;
+  }
+  push_in_progress_ = false;
+  source_callback_ = nullptr;
+  finished->Signal();
+}
+
+void CmaAudioOutputStream::Flush(base::WaitableEvent* finished) {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  // Prevent further pushes to the audio buffer after stopping.
+  push_timer_.Stop();
+
+  if (cma_backend_ && (cma_backend_state_ == CmaBackendState::kPaused ||
+                       cma_backend_state_ == CmaBackendState::kStarted)) {
+    cma_backend_->Stop();
+    cma_backend_state_ = CmaBackendState::kStopped;
+  }
+  push_in_progress_ = false;
+  source_callback_ = nullptr;
+  finished->Signal();
+}
+
+void CmaAudioOutputStream::Close(base::OnceClosure closure) {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  // Prevent further pushes to the audio buffer after stopping.
+  push_timer_.Stop();
+  // Only stop the backend if it was started.
+  if (cma_backend_ && cma_backend_state_ != CmaBackendState::kStopped) {
+    cma_backend_->Stop();
+  }
+  push_in_progress_ = false;
+  cma_backend_state_ = CmaBackendState::kPendingClose;
+
+  cma_backend_task_runner_.reset();
+  cma_backend_.reset();
+  audio_bus_.reset();
+
+  std::move(closure).Run();
+}
+
+void CmaAudioOutputStream::SetVolume(double volume) {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  if (!audio_decoder_) {
+    return;
+  }
+  if (encountered_error_) {
+    return;
+  }
+  audio_decoder_->SetVolume(volume);
+}
+
+void CmaAudioOutputStream::PushBuffer() {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+
+  // Acquire running_lock_ for the scope of this push call to
+  // prevent the source callback from closing the output stream
+  // mid-push.
+  base::AutoLock lock(running_lock_);
+
+  // Do not fill more buffers if we have stopped running.
+  if (!running_)
+    return;
+
+  // It is possible that this function is called when we are stopped.
+  // Return quickly if so.
+  if (!source_callback_ || encountered_error_ ||
+      cma_backend_state_ != CmaBackendState::kStarted) {
+    push_in_progress_ = false;
+    return;
+  }
+  DCHECK(push_in_progress_);
+
+  CmaBackend::AudioDecoder::RenderingDelay rendering_delay =
+      audio_decoder_->GetRenderingDelay();
+
+  base::TimeDelta delay;
+  if (rendering_delay.delay_microseconds < 0 ||
+      rendering_delay.timestamp_microseconds < 0) {
+    // This occurs immediately after start/resume when there isn't a good
+    // estimate of the buffer delay.  Use the last known good delay.
+    delay = last_rendering_delay_;
+  } else {
+    // The rendering delay to account for buffering is not included in
+    // rendering_delay.delay_microseconds but is in delay_timestamp which isn't
+    // used by AudioOutputStreamImpl.
+    delay = base::TimeDelta::FromMicroseconds(
+        rendering_delay.delay_microseconds +
+        rendering_delay.timestamp_microseconds - MonotonicClockNow());
+    if (delay.InMicroseconds() < 0) {
+      delay = base::TimeDelta();
+    }
+  }
+  last_rendering_delay_ = delay;
+
+  int frame_count = source_callback_->OnMoreData(delay, base::TimeTicks(), 0,
+                                                 audio_bus_.get());
+
+  DVLOG(3) << "frames_filled=" << frame_count << " with latency=" << delay;
+
+  if (frame_count == 0) {
+    OnPushBufferComplete(CmaBackend::BufferStatus::kBufferFailed);
+    return;
+  }
+  auto decoder_buffer =
+      base::MakeRefCounted<DecoderBufferAdapter>(new ::media::DecoderBuffer(
+          audio_params_.GetBytesPerBuffer(::media::kSampleFormatS16)));
+  audio_bus_->ToInterleaved<::media::SignedInt16SampleTypeTraits>(
+      frame_count, reinterpret_cast<int16_t*>(decoder_buffer->writable_data()));
+  decoder_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
+  timestamp_helper_.AddFrames(frame_count);
+
+  BufferStatus status = audio_decoder_->PushBuffer(std::move(decoder_buffer));
+  if (status != CmaBackend::BufferStatus::kBufferPending)
+    OnPushBufferComplete(status);
+}
+
+void CmaAudioOutputStream::OnPushBufferComplete(BufferStatus status) {
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+  DCHECK_NE(status, CmaBackend::BufferStatus::kBufferPending);
+
+  DCHECK(push_in_progress_);
+  push_in_progress_ = false;
+
+  if (!source_callback_ || encountered_error_)
+    return;
+
+  if (status != CmaBackend::BufferStatus::kBufferSuccess) {
+    source_callback_->OnError();
+    return;
+  }
+
+  // Schedule next push buffer.
+  const base::TimeTicks now = base::TimeTicks::Now();
+  base::TimeDelta delay;
+  if (is_audio_prefetch_) {
+    // For multizone-playback, we don't care about AV sync and want to pre-fetch
+    // audio.
+    render_buffer_size_estimate_ -= buffer_duration_;
+    render_buffer_size_estimate_ += now - last_push_complete_time_;
+    last_push_complete_time_ = now;
+
+    if (render_buffer_size_estimate_ >= buffer_duration_) {
+      delay = base::TimeDelta::FromSeconds(0);
+    } else {
+      delay = buffer_duration_;
+    }
+  } else {
+    next_push_time_ = std::max(now, next_push_time_ + buffer_duration_);
+    delay = next_push_time_ - now;
+  }
+
+  DVLOG(3) << "render_buffer_size_estimate_=" << render_buffer_size_estimate_
+           << " delay=" << delay << " buffer_duration_=" << buffer_duration_;
+
+  push_timer_.Start(FROM_HERE, delay, this, &CmaAudioOutputStream::PushBuffer);
+  push_in_progress_ = true;
+}
+
+void CmaAudioOutputStream::OnDecoderError() {
+  DLOG(INFO) << this << ": " << __func__;
+  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
+
+  encountered_error_ = true;
+  if (source_callback_)
+    source_callback_->OnError();
+}
+
+}  // namespace media
+}  // namespace chromecast
diff --git a/chromecast/media/audio/cma_audio_output_stream.h b/chromecast/media/audio/cma_audio_output_stream.h
new file mode 100644
index 0000000..f4afe7a
--- /dev/null
+++ b/chromecast/media/audio/cma_audio_output_stream.h
@@ -0,0 +1,109 @@
+// Copyright 2019 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 CHROMECAST_MEDIA_AUDIO_CMA_AUDIO_OUTPUT_STREAM_H_
+#define CHROMECAST_MEDIA_AUDIO_CMA_AUDIO_OUTPUT_STREAM_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chromecast/common/mojom/multiroom.mojom.h"
+#include "chromecast/media/cma/backend/cma_backend.h"
+#include "media/audio/audio_io.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/audio_timestamp_helper.h"
+
+namespace base {
+class WaitableEvent;
+}  // namespace base
+
+namespace media {
+class AudioBus;
+}  // namespace media
+
+namespace chromecast {
+
+class TaskRunnerImpl;
+
+namespace media {
+
+class CmaBackendFactory;
+
+class CmaAudioOutputStream : public CmaBackend::Decoder::Delegate {
+ public:
+  CmaAudioOutputStream(const ::media::AudioParameters& audio_params,
+                       const std::string& device_id,
+                       CmaBackendFactory* cma_backend_factory);
+  ~CmaAudioOutputStream() override;
+
+  void SetRunning(bool running);
+  void Initialize(const std::string& application_session_id,
+                  chromecast::mojom::MultiroomInfoPtr multiroom_info);
+  void Start(::media::AudioOutputStream::AudioSourceCallback* source_callback);
+  void Stop(base::WaitableEvent* finished);
+  void Flush(base::WaitableEvent* finished);
+  void Close(base::OnceClosure closure);
+  void SetVolume(double volume);
+
+ private:
+  enum class CmaBackendState {
+    kUninitialized,
+    kStopped,
+    kPaused,
+    kStarted,
+    kPendingClose,
+  };
+
+  void PushBuffer();
+
+  // CmaBackend::Decoder::Delegate implementation:
+  void OnEndOfStream() override {}
+  void OnDecoderError() override;
+  void OnKeyStatusChanged(const std::string& key_id,
+                          CastKeyStatus key_status,
+                          uint32_t system_code) override {}
+  void OnVideoResolutionChanged(const Size& size) override {}
+  void OnPushBufferComplete(BufferStatus status) override;
+
+  const bool is_audio_prefetch_;
+
+  const ::media::AudioParameters audio_params_;
+  const std::string device_id_;
+  CmaBackendFactory* const cma_backend_factory_;
+
+  base::Lock running_lock_;
+  bool running_ = true;
+  CmaBackendState cma_backend_state_ = CmaBackendState::kUninitialized;
+  ::media::AudioTimestampHelper timestamp_helper_;
+  const base::TimeDelta buffer_duration_;
+  std::unique_ptr<TaskRunnerImpl> cma_backend_task_runner_;
+  std::unique_ptr<CmaBackend> cma_backend_;
+  std::unique_ptr<::media::AudioBus> audio_bus_;
+  base::OneShotTimer push_timer_;
+  bool push_in_progress_ = false;
+  bool encountered_error_ = false;
+  base::TimeTicks next_push_time_;
+  base::TimeTicks last_push_complete_time_;
+  base::TimeDelta last_rendering_delay_;
+  base::TimeDelta render_buffer_size_estimate_;
+  CmaBackend::AudioDecoder* audio_decoder_ = nullptr;
+  ::media::AudioOutputStream::AudioSourceCallback* source_callback_ = nullptr;
+
+  THREAD_CHECKER(media_thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(CmaAudioOutputStream);
+};
+
+}  // namespace media
+}  // namespace chromecast
+
+#endif  // CHROMECAST_MEDIA_AUDIO_CMA_AUDIO_OUTPUT_STREAM_H_
diff --git a/chromeos/dbus/debug_daemon_client.cc b/chromeos/dbus/debug_daemon_client.cc
index cd4c4fb..686ecf0 100644
--- a/chromeos/dbus/debug_daemon_client.cc
+++ b/chromeos/dbus/debug_daemon_client.cc
@@ -567,6 +567,20 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void SetSchedulerConfigurationV2(const std::string& config_name,
+                                   bool lock_policy,
+                                   VoidDBusMethodCallback callback) override {
+    dbus::MethodCall method_call(debugd::kDebugdInterface,
+                                 debugd::kSetSchedulerConfigurationV2);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(config_name);
+    writer.AppendBool(lock_policy);
+    debugdaemon_proxy_->CallMethod(
+        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+        base::BindOnce(&DebugDaemonClientImpl::OnVoidMethod,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void SetU2fFlags(const std::set<std::string>& flags,
                    VoidDBusMethodCallback callback) override {
     dbus::MethodCall method_call(debugd::kDebugdInterface,
diff --git a/chromeos/dbus/debug_daemon_client.h b/chromeos/dbus/debug_daemon_client.h
index b67dc797..507a027 100644
--- a/chromeos/dbus/debug_daemon_client.h
+++ b/chromeos/dbus/debug_daemon_client.h
@@ -248,12 +248,19 @@
   // to 0.
   virtual void SetRlzPingSent(SetRlzPingSentCallback callback) = 0;
 
-  // Request switching to the scheduler configuration profile indicated. The
-  // profile names are defined by debugd, which adjusts various knobs affecting
-  // kernel level task scheduling (see debugd source code for details).
+  // Deprecated. Use SetSchedulerConfigurationV2 instead.
+  // TODO(abhishekbh): Remove the method.
   virtual void SetSchedulerConfiguration(const std::string& config_name,
                                          VoidDBusMethodCallback callback) = 0;
 
+  // Request switching to the scheduler configuration profile indicated. The
+  // profile names are defined by debugd, which adjusts various knobs affecting
+  // kernel level task scheduling (see debugd source code for details). When
+  // |lock_policy| is true, the policy is locked until the device is rebooted.
+  virtual void SetSchedulerConfigurationV2(const std::string& config_name,
+                                           bool lock_policy,
+                                           VoidDBusMethodCallback callback) = 0;
+
   // Set U2F flags.
   virtual void SetU2fFlags(const std::set<std::string>& flags,
                            VoidDBusMethodCallback callback) = 0;
diff --git a/chromeos/dbus/fake_debug_daemon_client.cc b/chromeos/dbus/fake_debug_daemon_client.cc
index f144e53..45e0a35 100644
--- a/chromeos/dbus/fake_debug_daemon_client.cc
+++ b/chromeos/dbus/fake_debug_daemon_client.cc
@@ -268,6 +268,15 @@
       FROM_HERE, base::BindOnce(std::move(callback), true));
 }
 
+void FakeDebugDaemonClient::SetSchedulerConfigurationV2(
+    const std::string& config_name,
+    bool lock_policy,
+    VoidDBusMethodCallback callback) {
+  scheduler_configuration_name_ = config_name;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(std::move(callback), true));
+}
+
 void FakeDebugDaemonClient::SetU2fFlags(const std::set<std::string>& flags,
                                         VoidDBusMethodCallback callback) {
   u2f_flags_ = flags;
diff --git a/chromeos/dbus/fake_debug_daemon_client.h b/chromeos/dbus/fake_debug_daemon_client.h
index a920f8ad..e467cb3 100644
--- a/chromeos/dbus/fake_debug_daemon_client.h
+++ b/chromeos/dbus/fake_debug_daemon_client.h
@@ -91,6 +91,9 @@
   void SetRlzPingSent(SetRlzPingSentCallback callback) override;
   void SetSchedulerConfiguration(const std::string& config_name,
                                  VoidDBusMethodCallback callback) override;
+  void SetSchedulerConfigurationV2(const std::string& config_name,
+                                   bool lock_policy,
+                                   VoidDBusMethodCallback callback) override;
   void SetU2fFlags(const std::set<std::string>& flags,
                    VoidDBusMethodCallback callback) override;
   void GetU2fFlags(DBusMethodCallback<std::set<std::string>> callback) override;
diff --git a/components/BUILD.gn b/components/BUILD.gn
index a426bb0f..61d4b09 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -503,6 +503,7 @@
       "autofill/content/browser/risk/fingerprint_browsertest.cc",
       "autofill/content/renderer/field_data_manager_browsertest.cc",
       "autofill/content/renderer/form_autofill_util_browsertest.cc",
+      "autofill/content/renderer/form_cache_browsertest.cc",
       "autofill/content/renderer/password_form_conversion_utils_browsertest.cc",
       "dom_distiller/content/browser/distillable_page_utils_browsertest.cc",
       "dom_distiller/content/browser/distiller_page_web_contents_browsertest.cc",
diff --git a/components/arc/mojom/BUILD.gn b/components/arc/mojom/BUILD.gn
index 11f97dd8..b08ccf02 100644
--- a/components/arc/mojom/BUILD.gn
+++ b/components/arc/mojom/BUILD.gn
@@ -70,6 +70,7 @@
       ":camera_intent",
       ":media",
       ":notifications",
+      "//components/chromeos_camera/common:camera_app_helper",
       "//media/capture/video/chromeos/mojom:cros_camera",
       "//mojo/public/mojom/base",
       "//services/device/public/mojom:usb",
diff --git a/components/arc/mojom/video_accelerator_mojom_traits.cc b/components/arc/mojom/video_accelerator_mojom_traits.cc
index 07add4c1..54ebd05 100644
--- a/components/arc/mojom/video_accelerator_mojom_traits.cc
+++ b/components/arc/mojom/video_accelerator_mojom_traits.cc
@@ -239,9 +239,9 @@
 
 // static
 bool StructTraits<arc::mojom::MediaVideoFramePlaneDataView,
-                  media::VideoFrameLayout::Plane>::
+                  media::ColorPlaneLayout>::
     Read(arc::mojom::MediaVideoFramePlaneDataView data,
-         media::VideoFrameLayout::Plane* out) {
+         media::ColorPlaneLayout* out) {
   out->offset = data.offset();
   out->stride = data.stride();
   out->size = data.size();
@@ -255,7 +255,7 @@
          std::unique_ptr<media::VideoFrameLayout>* out) {
   media::VideoPixelFormat format;
   gfx::Size coded_size;
-  std::vector<media::VideoFrameLayout::Plane> planes;
+  std::vector<media::ColorPlaneLayout> planes;
   if (!data.ReadFormat(&format) || !data.ReadCodedSize(&coded_size) ||
       !data.ReadPlanes(&planes)) {
     return false;
diff --git a/components/arc/mojom/video_accelerator_mojom_traits.h b/components/arc/mojom/video_accelerator_mojom_traits.h
index 2ad0341..7a714cc 100644
--- a/components/arc/mojom/video_accelerator_mojom_traits.h
+++ b/components/arc/mojom/video_accelerator_mojom_traits.h
@@ -13,6 +13,7 @@
 #include "components/arc/mojom/video_common.mojom.h"
 #include "components/arc/video_accelerator/decoder_buffer.h"
 #include "components/arc/video_accelerator/video_frame_plane.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/decode_status.h"
 #include "media/base/video_codecs.h"
 #include "media/base/video_frame.h"
@@ -73,21 +74,15 @@
 
 template <>
 struct StructTraits<arc::mojom::MediaVideoFramePlaneDataView,
-                    media::VideoFrameLayout::Plane> {
-  static int32_t stride(const media::VideoFrameLayout::Plane& r) {
-    return r.stride;
-  }
+                    media::ColorPlaneLayout> {
+  static int32_t stride(const media::ColorPlaneLayout& r) { return r.stride; }
 
-  static uint32_t offset(const media::VideoFrameLayout::Plane& r) {
-    return r.offset;
-  }
+  static uint32_t offset(const media::ColorPlaneLayout& r) { return r.offset; }
 
-  static uint32_t size(const media::VideoFrameLayout::Plane& r) {
-    return r.size;
-  }
+  static uint32_t size(const media::ColorPlaneLayout& r) { return r.size; }
 
   static bool Read(arc::mojom::MediaVideoFramePlaneDataView data,
-                   media::VideoFrameLayout::Plane* out);
+                   media::ColorPlaneLayout* out);
 };
 
 // Because `media::VideoFrameLayout` doesn't have default constructor, we cannot
@@ -116,7 +111,7 @@
     return input->coded_size();
   }
 
-  static const std::vector<media::VideoFrameLayout::Plane>& planes(
+  static const std::vector<media::ColorPlaneLayout>& planes(
       const std::unique_ptr<media::VideoFrameLayout>& input) {
     DCHECK(input);
     return input->planes();
diff --git a/components/arc/mojom/video_accelerator_mojom_traits_unittest.cc b/components/arc/mojom/video_accelerator_mojom_traits_unittest.cc
index 0ce4fc4..ad091dd 100644
--- a/components/arc/mojom/video_accelerator_mojom_traits_unittest.cc
+++ b/components/arc/mojom/video_accelerator_mojom_traits_unittest.cc
@@ -27,7 +27,7 @@
 }  // namespace
 
 TEST(VideoAcceleratorStructTraitsTest, ConvertVideoFrameLayout) {
-  std::vector<media::VideoFrameLayout::Plane> planes;
+  std::vector<media::ColorPlaneLayout> planes;
   planes.emplace_back(kWidth, 0, kWidth * kHeight);
   planes.emplace_back(kWidth / 2, kWidth * kHeight, kWidth * kHeight / 4);
   planes.emplace_back(kWidth / 2, kWidth * kHeight + kWidth * kHeight / 4,
diff --git a/components/arc/mojom/video_common.typemap b/components/arc/mojom/video_common.typemap
index c06a852..9d7b7f2 100644
--- a/components/arc/mojom/video_common.typemap
+++ b/components/arc/mojom/video_common.typemap
@@ -6,10 +6,10 @@
 public_headers = [
   "//components/arc/video_accelerator/decoder_buffer.h",
   "//components/arc/video_accelerator/video_frame_plane.h",
+  "//media/base/color_plane_layout.h",
   "//media/base/decode_status.h",
   "//media/base/video_codecs.h",
   "//media/base/video_frame.h",
-  "//media/base/video_frame_layout.h",
   "//media/base/video_types.h",
   "//ui/gfx/geometry/size.h",
 ]
@@ -33,7 +33,7 @@
 type_mappings = [
   "arc.mojom.DecoderBuffer=arc::DecoderBuffer[move_only]",
   "arc.mojom.DecodeStatus=media::DecodeStatus",
-  "arc.mojom.MediaVideoFramePlane=media::VideoFrameLayout::Plane",
+  "arc.mojom.MediaVideoFramePlane=media::ColorPlaneLayout",
   "arc.mojom.Size=gfx::Size",
   "arc.mojom.VideoCodecProfile=media::VideoCodecProfile",
   "arc.mojom.VideoFrame=scoped_refptr<media::VideoFrame>[nullable_is_same_type]",
diff --git a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
index 1af2343..4e3f09f 100644
--- a/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
+++ b/components/arc/video_accelerator/gpu_arc_video_encode_accelerator.cc
@@ -14,6 +14,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/system/sys_info.h"
 #include "components/arc/video_accelerator/arc_video_accelerator_util.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/video_types.h"
 #include "media/gpu/gpu_video_encode_accelerator_factory.h"
@@ -31,7 +32,7 @@
     const gfx::GpuMemoryBufferHandle& gmb_handle) {
   const size_t num_planes = gmb_handle.native_pixmap_handle.planes.size();
 
-  std::vector<media::VideoFrameLayout::Plane> layout_planes(num_planes);
+  std::vector<media::ColorPlaneLayout> layout_planes(num_planes);
   for (size_t i = 0; i < num_planes; i++) {
     const auto& plane = gmb_handle.native_pixmap_handle.planes[i];
     if (!base::IsValueInRangeForNumericType<int32_t>(plane.stride)) {
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index 8bbe645..ac542f3d5 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -10,10 +10,13 @@
 #include <set>
 #include <vector>
 
+#include "base/i18n/rtl.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "components/autofill/core/common/autofill_constants.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
+#include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_element_collection.h"
 #include "ui/gfx/geometry/rect_f.h"
diff --git a/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index bbec17a5..a0a8c93 100644
--- a/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
 #include "content/public/test/render_view_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_string.h"
diff --git a/components/autofill/content/renderer/form_cache.cc b/components/autofill/content/renderer/form_cache.cc
index 0e67646..cb6edb1 100644
--- a/components/autofill/content/renderer/form_cache.cc
+++ b/components/autofill/content/renderer/form_cache.cc
@@ -5,6 +5,7 @@
 #include "components/autofill/content/renderer/form_cache.h"
 
 #include <algorithm>
+#include <set>
 #include <string>
 #include <utility>
 
@@ -12,6 +13,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
@@ -161,15 +163,16 @@
   for (const char* str : deprecated) {
     if (autocomplete_attribute.find(str) == std::string::npos)
       continue;
-    std::string msg = std::string("autocomplete='") + str +
-        "' is deprecated and will soon be ignored. See http://goo.gl/YjeSsW";
+    std::string msg = base::StrCat(
+        {"autocomplete='", str,
+         "' is deprecated and will soon be ignored. See http://goo.gl/YjeSsW"});
     WebConsoleMessage console_message = WebConsoleMessage(
         blink::mojom::ConsoleMessageLevel::kWarning, WebString::FromASCII(msg));
     element.GetDocument().GetFrame()->AddMessageToConsole(console_message);
   }
 }
 
-// Determines whether the form is interesting enough to send to the browser
+// Determines whether the form is interesting enough to be sent to the browser
 // for further operations.
 bool IsFormInteresting(const FormData& form, size_t num_editable_elements) {
   if (form.fields.empty())
@@ -199,9 +202,7 @@
 }  // namespace
 
 FormCache::FormCache(WebLocalFrame* frame) : frame_(frame) {}
-
-FormCache::~FormCache() {
-}
+FormCache::~FormCache() = default;
 
 std::vector<FormData> FormCache::ExtractNewForms() {
   std::vector<FormData> forms;
@@ -214,6 +215,8 @@
   WebVector<WebFormElement> web_forms;
   document.Forms(web_forms);
 
+  std::set<uint32_t> observed_unique_renderer_ids;
+
   // Log an error message for deprecated attributes, but only the first time
   // the form is parsed.
   bool log_deprecation_messages = parsed_forms_.empty();
@@ -223,14 +226,12 @@
                                           form_util::EXTRACT_OPTIONS);
 
   size_t num_fields_seen = 0;
-  for (size_t i = 0; i < web_forms.size(); ++i) {
-    const WebFormElement& form_element = web_forms[i];
-
+  for (const WebFormElement& form_element : web_forms) {
     std::vector<WebFormControlElement> control_elements =
         form_util::ExtractAutofillableElementsInForm(form_element);
+
     size_t num_editable_elements =
         ScanFormControlElements(control_elements, log_deprecation_messages);
-
     if (num_editable_elements == 0)
       continue;
 
@@ -240,9 +241,14 @@
       continue;
     }
 
+    for (const auto& field : form.fields)
+      observed_unique_renderer_ids.insert(field.unique_renderer_id);
+
     num_fields_seen += form.fields.size();
-    if (num_fields_seen > form_util::kMaxParseableFields)
+    if (num_fields_seen > form_util::kMaxParseableFields) {
+      PruneInitialValueCaches(observed_unique_renderer_ids);
       return forms;
+    }
 
     if (!base::Contains(parsed_forms_, form) &&
         IsFormInteresting(form, num_editable_elements)) {
@@ -267,22 +273,29 @@
 
   size_t num_editable_elements =
       ScanFormControlElements(control_elements, log_deprecation_messages);
-
-  if (num_editable_elements == 0)
+  if (num_editable_elements == 0) {
+    PruneInitialValueCaches(observed_unique_renderer_ids);
     return forms;
+  }
 
   FormData synthetic_form;
   if (!UnownedCheckoutFormElementsAndFieldSetsToFormData(
           fieldsets, control_elements, nullptr, document, extract_mask,
           &synthetic_form, nullptr)) {
+    PruneInitialValueCaches(observed_unique_renderer_ids);
     return forms;
   }
 
-  num_fields_seen += synthetic_form.fields.size();
-  if (num_fields_seen > form_util::kMaxParseableFields)
-    return forms;
+  for (const auto& field : synthetic_form.fields)
+    observed_unique_renderer_ids.insert(field.unique_renderer_id);
 
-  if (!parsed_forms_.count(synthetic_form) &&
+  num_fields_seen += synthetic_form.fields.size();
+  if (num_fields_seen > form_util::kMaxParseableFields) {
+    PruneInitialValueCaches(observed_unique_renderer_ids);
+    return forms;
+  }
+
+  if (!base::Contains(parsed_forms_, synthetic_form) &&
       IsFormInteresting(synthetic_form, num_editable_elements)) {
     SaveInitialValues(control_elements);
     forms.push_back(synthetic_form);
@@ -290,6 +303,8 @@
     parsed_forms_.erase(synthetic_form_);
     synthetic_form_ = synthetic_form;
   }
+
+  PruneInitialValueCaches(observed_unique_renderer_ids);
   return forms;
 }
 
@@ -302,16 +317,13 @@
 
 bool FormCache::ClearSectionWithElement(const WebFormControlElement& element) {
   WebFormElement form_element = element.Form();
-  std::vector<WebFormControlElement> control_elements;
-  if (form_element.IsNull()) {
-    control_elements = form_util::GetUnownedAutofillableFormFieldElements(
-        element.GetDocument().All(), nullptr);
-  } else {
-    control_elements =
-        form_util::ExtractAutofillableElementsInForm(form_element);
-  }
-  for (size_t i = 0; i < control_elements.size(); ++i) {
-    WebFormControlElement control_element = control_elements[i];
+  std::vector<WebFormControlElement> control_elements =
+      form_element.IsNull()
+          ? form_util::GetUnownedAutofillableFormFieldElements(
+                element.GetDocument().All(), nullptr)
+          : form_util::ExtractAutofillableElementsInForm(form_element);
+
+  for (WebFormControlElement& control_element : control_elements) {
     // Don't modify the value of disabled fields.
     if (!control_element.IsEnabled())
       continue;
@@ -341,8 +353,8 @@
     } else if (form_util::IsSelectElement(control_element)) {
       WebSelectElement select_element = control_element.To<WebSelectElement>();
 
-      std::map<const WebSelectElement, base::string16>::const_iterator
-          initial_value_iter = initial_select_values_.find(select_element);
+      auto initial_value_iter = initial_select_values_.find(
+          select_element.UniqueRendererFormControlId());
       if (initial_value_iter != initial_select_values_.end() &&
           select_element.Value().Utf16() != initial_value_iter->second) {
         select_element.SetAutofillValue(
@@ -351,11 +363,11 @@
     } else {
       WebInputElement input_element = control_element.To<WebInputElement>();
       DCHECK(form_util::IsCheckableElement(&input_element));
-      std::map<const WebInputElement, bool>::const_iterator it =
-          initial_checked_state_.find(input_element);
-      if (it != initial_checked_state_.end() &&
-          input_element.IsChecked() != it->second) {
-        input_element.SetChecked(it->second, true);
+      auto checkable_element_it = initial_checked_state_.find(
+          input_element.UniqueRendererFormControlId());
+      if (checkable_element_it != initial_checked_state_.end() &&
+          input_element.IsChecked() != checkable_element_it->second) {
+        input_element.SetChecked(checkable_element_it->second, true);
       }
     }
   }
@@ -381,12 +393,10 @@
   if (!found_synthetic_form) {
     // Find the real form by searching through the WebDocuments.
     bool found_form = false;
-    WebFormElement form_element;
     WebVector<WebFormElement> web_forms;
     frame_->GetDocument().Forms(web_forms);
 
-    for (size_t i = 0; i < web_forms.size(); ++i) {
-      form_element = web_forms[i];
+    for (const WebFormElement& form_element : web_forms) {
       // To match two forms, we look for the form's name and the number of
       // fields on that form. (Form names may not be unique.)
       // Note: WebString() == WebString(string16()) does not evaluate to |true|
@@ -445,32 +455,15 @@
       const base::string16 truncated_label = field_data.label.substr(
           0, std::min(field_data.label.length(), kMaxLabelSize));
 
-      // A rough estimate of the maximum title size is:
-      //    8 field titles at <17 chars each
-      //    + 7 values at <40 chars each
-      //    + 1 truncated label at <kMaxLabelSize;
-      //    = 516 chars, rounded up to the next multiple of 64 = 576
-      // A particularly large parseable name could blow through this and cause
-      // another allocation, but that's OK.
-      constexpr size_t kMaxTitleSize = 576;
-      std::string title;
-      title.reserve(kMaxTitleSize);
-      title += "overall type: ";
-      title += field.overall_type;
-      title += "\nserver type: ";
-      title += field.server_type;
-      title += "\nheuristic type: ";
-      title += field.heuristic_type;
-      title += "\nlabel: ";
-      title += base::UTF16ToUTF8(truncated_label);
-      title += "\nparseable name: ";
-      title += field.parseable_name;
-      title += "\nsection: ";
-      title += field.section;
-      title += "\nfield signature: ";
-      title += field.signature;
-      title += "\nform signature: ";
-      title += form.signature;
+      std::string title =
+          base::StrCat({"overall type: ", field.overall_type,             //
+                        "\nserver type: ", field.server_type,             //
+                        "\nheuristic type: ", field.heuristic_type,       //
+                        "\nlabel: ", base::UTF16ToUTF8(truncated_label),  //
+                        "\nparseable name: ", field.parseable_name,       //
+                        "\nsection: ", field.section,                     //
+                        "\nfield signature: ", field.signature,           //
+                        "\nform signature: ", form.signature});
 
       // Set this debug string to the title so that a developer can easily debug
       // by hovering the mouse over the input field.
@@ -494,9 +487,7 @@
     const std::vector<WebFormControlElement>& control_elements,
     bool log_deprecation_messages) {
   size_t num_editable_elements = 0;
-  for (size_t i = 0; i < control_elements.size(); ++i) {
-    const WebFormControlElement& element = control_elements[i];
-
+  for (const WebFormControlElement& element : control_elements) {
     if (log_deprecation_messages)
       LogDeprecationMessages(element);
 
@@ -521,12 +512,14 @@
       const WebSelectElement select_element =
           element.ToConst<WebSelectElement>();
       initial_select_values_.insert(
-          std::make_pair(select_element, select_element.Value().Utf16()));
+          std::make_pair(select_element.UniqueRendererFormControlId(),
+                         select_element.Value().Utf16()));
     } else {
       const WebInputElement* input_element = ToWebInputElement(&element);
       if (form_util::IsCheckableElement(input_element)) {
         initial_checked_state_.insert(
-            std::make_pair(*input_element, input_element->IsChecked()));
+            std::make_pair(input_element->UniqueRendererFormControlId(),
+                           input_element->IsChecked()));
       }
     }
   }
@@ -564,4 +557,25 @@
   return false;
 }
 
+void FormCache::PruneInitialValueCaches(
+    const std::set<uint32_t> ids_to_retain) {
+  // Prune initial_select_values_.
+  for (auto iter = initial_select_values_.begin();
+       iter != initial_select_values_.end();) {
+    if (!base::Contains(ids_to_retain, iter->first))
+      iter = initial_select_values_.erase(iter);
+    else
+      ++iter;
+  }
+
+  // Prune initial_checked_state_.
+  for (auto iter = initial_checked_state_.begin();
+       iter != initial_checked_state_.end();) {
+    if (!base::Contains(ids_to_retain, iter->first))
+      iter = initial_checked_state_.erase(iter);
+    else
+      ++iter;
+  }
+}
+
 }  // namespace autofill
diff --git a/components/autofill/content/renderer/form_cache.h b/components/autofill/content/renderer/form_cache.h
index c4db022d..9cc7da6 100644
--- a/components/autofill/content/renderer/form_cache.h
+++ b/components/autofill/content/renderer/form_cache.h
@@ -18,13 +18,12 @@
 
 namespace blink {
 class WebFormControlElement;
-class WebInputElement;
 class WebLocalFrame;
-class WebSelectElement;
 }
 
 namespace autofill {
 
+struct FormData;
 struct FormDataPredictions;
 
 // Manages the forms in a single RenderFrame.
@@ -34,7 +33,8 @@
   ~FormCache();
 
   // Scans the DOM in |frame_| extracting and storing forms that have not been
-  // seen before. Returns the extracted forms.
+  // seen before. Returns the extracted forms. Note that modified forms are
+  // considered new forms.
   std::vector<FormData> ExtractNewForms();
 
   // Resets the forms.
@@ -57,10 +57,10 @@
                            ShouldShowAutocompleteConsoleWarnings_Enabled);
   FRIEND_TEST_ALL_PREFIXES(FormCacheTest,
                            ShouldShowAutocompleteConsoleWarnings_Disabled);
+  FRIEND_TEST_ALL_PREFIXES(FormCacheBrowserTest, FreeDataOnElementRemoval);
 
   // Scans |control_elements| and returns the number of editable elements.
-  // Also remembers the initial <select> and <input> element states, and
-  // logs warning messages for deprecated attribute if
+  // Also logs warning messages for deprecated attribute if
   // |log_deprecation_messages| is set.
   size_t ScanFormControlElements(
       const std::vector<blink::WebFormControlElement>& control_elements,
@@ -78,6 +78,10 @@
       const std::string& predicted_autocomplete,
       const std::string& actual_autocomplete);
 
+  // Clears all entries from |initial_select_values_| and
+  // |initial_checked_state_| whose keys not contained in |ids_to_retain|.
+  void PruneInitialValueCaches(const std::set<uint32_t> ids_to_retain);
+
   // The frame this FormCache is associated with. Weak reference.
   blink::WebLocalFrame* frame_;
 
@@ -88,12 +92,13 @@
   // form owner.
   FormData synthetic_form_;
 
-  // The cached initial values for <select> elements.
-  std::map<const blink::WebSelectElement, base::string16>
-      initial_select_values_;
+  // The cached initial values for <select> elements. Entries are keyed by
+  // unique_renderer_form_control_id of the WebSelectElements.
+  std::map<uint32_t, base::string16> initial_select_values_;
 
-  // The cached initial values for checkable <input> elements.
-  std::map<const blink::WebInputElement, bool> initial_checked_state_;
+  // The cached initial values for checkable <input> elements. Entries are
+  // keyed by the unique_renderer_form_control_id of the WebInputElements.
+  std::map<uint32_t, bool> initial_checked_state_;
 
   DISALLOW_COPY_AND_ASSIGN(FormCache);
 };
diff --git a/components/autofill/content/renderer/form_cache_browsertest.cc b/components/autofill/content/renderer/form_cache_browsertest.cc
new file mode 100644
index 0000000..f55fadb
--- /dev/null
+++ b/components/autofill/content/renderer/form_cache_browsertest.cc
@@ -0,0 +1,194 @@
+// Copyright 2019 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/content/renderer/form_cache.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
+#include "components/autofill/core/common/form_field_data.h"
+#include "content/public/test/render_view_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_input_element.h"
+#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/public/web/web_select_element.h"
+
+using base::ASCIIToUTF16;
+using blink::WebDocument;
+using blink::WebElement;
+using blink::WebInputElement;
+using blink::WebSelectElement;
+using blink::WebString;
+
+namespace autofill {
+
+const FormData* GetFormByName(const std::vector<FormData>& forms,
+                              base::StringPiece name) {
+  for (const FormData& form : forms) {
+    if (form.name == ASCIIToUTF16(name))
+      return &form;
+  }
+  return nullptr;
+}
+
+class FormCacheBrowserTest : public content::RenderViewTest {
+ public:
+  FormCacheBrowserTest() = default;
+  ~FormCacheBrowserTest() override = default;
+};
+
+TEST_F(FormCacheBrowserTest, ExtractForms) {
+  LoadHTML(R"(
+    <form id="form1">
+      <input type="text" name="foo1">
+      <input type="text" name="foo2">
+      <input type="text" name="foo3">
+    </form>
+    <input type="text" name="unowned_element">
+  )");
+
+  FormCache form_cache(GetMainFrame());
+  std::vector<FormData> forms = form_cache.ExtractNewForms();
+
+  const FormData* form1 = GetFormByName(forms, "form1");
+  ASSERT_TRUE(form1);
+  EXPECT_EQ(3u, form1->fields.size());
+
+  const FormData* unowned_form = GetFormByName(forms, "");
+  ASSERT_TRUE(unowned_form);
+  EXPECT_EQ(1u, unowned_form->fields.size());
+}
+
+TEST_F(FormCacheBrowserTest, ExtractFormsTwice) {
+  LoadHTML(R"(
+    <form id="form1">
+      <input type="text" name="foo1">
+      <input type="text" name="foo2">
+      <input type="text" name="foo3">
+    </form>
+    <input type="text" name="unowned_element">
+  )");
+
+  FormCache form_cache(GetMainFrame());
+  std::vector<FormData> forms = form_cache.ExtractNewForms();
+
+  forms = form_cache.ExtractNewForms();
+  // As nothing has changed, there are no new forms and |forms| should be empty.
+  EXPECT_TRUE(forms.empty());
+}
+
+TEST_F(FormCacheBrowserTest, ExtractFormsAfterModification) {
+  LoadHTML(R"(
+    <form id="form1">
+      <input type="text" name="foo1">
+      <input type="text" name="foo2">
+      <input type="text" name="foo3">
+    </form>
+    <input type="text" name="unowned_element">
+  )");
+
+  FormCache form_cache(GetMainFrame());
+  std::vector<FormData> forms = form_cache.ExtractNewForms();
+
+  // Append an input element to the form and to the list of unowned inputs.
+  ExecuteJavaScriptForTests(R"(
+    var new_input_1 = document.createElement("input");
+    new_input_1.setAttribute("type", "text");
+    new_input_1.setAttribute("name", "foo4");
+
+    var form1 = document.getElementById("form1");
+    form1.appendChild(new_input_1);
+
+    var new_input_2 = document.createElement("input");
+    new_input_2.setAttribute("type", "text");
+    new_input_2.setAttribute("name", "unowned_element_2");
+    document.body.appendChild(new_input_2);
+  )");
+
+  forms = form_cache.ExtractNewForms();
+
+  const FormData* form1 = GetFormByName(forms, "form1");
+  ASSERT_TRUE(form1);
+  EXPECT_EQ(4u, form1->fields.size());
+
+  const FormData* unowned_form = GetFormByName(forms, "");
+  ASSERT_TRUE(unowned_form);
+  EXPECT_EQ(2u, unowned_form->fields.size());
+}
+
+TEST_F(FormCacheBrowserTest, FillAndClear) {
+  LoadHTML(R"(
+    <input type="text" name="text" id="text">
+    <input type="checkbox" checked name="checkbox" id="checkbox">
+    <select name="select" id="select">
+      <option value="first">first</option>
+      <option value="second" selected>second</option>
+    </select>
+  )");
+
+  FormCache form_cache(GetMainFrame());
+  std::vector<FormData> forms = form_cache.ExtractNewForms();
+
+  ASSERT_EQ(1u, forms.size());
+  FormData values_to_fill = forms[0];
+  values_to_fill.fields[0].value = ASCIIToUTF16("test");
+  values_to_fill.fields[0].is_autofilled = true;
+  values_to_fill.fields[1].check_status =
+      FormFieldData::CheckStatus::kCheckableButUnchecked;
+  values_to_fill.fields[1].is_autofilled = true;
+  values_to_fill.fields[2].value = ASCIIToUTF16("first");
+  values_to_fill.fields[2].is_autofilled = true;
+
+  WebDocument doc = GetMainFrame()->GetDocument();
+  auto text = doc.GetElementById("text").To<WebInputElement>();
+  auto checkbox = doc.GetElementById("checkbox").To<WebInputElement>();
+  auto select_element = doc.GetElementById("select").To<WebSelectElement>();
+
+  form_util::FillForm(values_to_fill, text);
+
+  EXPECT_EQ("test", text.Value().Ascii());
+  EXPECT_FALSE(checkbox.IsChecked());
+  EXPECT_EQ("first", select_element.Value().Ascii());
+
+  // Validate that clearing works, in particular that the previous values
+  // were saved correctly.
+  form_cache.ClearSectionWithElement(text);
+
+  EXPECT_EQ("", text.Value().Ascii());
+  EXPECT_TRUE(checkbox.IsChecked());
+  EXPECT_EQ("second", select_element.Value().Ascii());
+}
+
+TEST_F(FormCacheBrowserTest, FreeDataOnElementRemoval) {
+  LoadHTML(R"(
+    <div id="container">
+      <input type="text" name="text" id="text">
+      <input type="checkbox" checked name="checkbox" id="checkbox">
+      <select name="select" id="select">
+        <option value="first">first</option>
+        <option value="second" selected>second</option>
+      </select>
+    </div>
+  )");
+
+  FormCache form_cache(GetMainFrame());
+  form_cache.ExtractNewForms();
+
+  EXPECT_EQ(1u, form_cache.initial_select_values_.size());
+  EXPECT_EQ(1u, form_cache.initial_checked_state_.size());
+
+  ExecuteJavaScriptForTests(R"(
+    const container = document.getElementById('container');
+    while (container.childElementCount > 0) {
+      container.removeChild(container.children.item(0));
+    }
+  )");
+
+  std::vector<FormData> forms = form_cache.ExtractNewForms();
+  EXPECT_EQ(0u, forms.size());
+  EXPECT_EQ(0u, form_cache.initial_select_values_.size());
+  EXPECT_EQ(0u, form_cache.initial_checked_state_.size());
+}
+
+}  // namespace autofill
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 54e9884..011f1b8 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -84,13 +84,9 @@
 // The size above which we stop triggering autocomplete.
 const size_t kMaximumTextSizeForAutocomplete = 1000;
 
-const char kDummyUsernameField[] = "anonymous_username";
-const char kDummyPasswordField[] = "anonymous_password";
-
 // Names of HTML attributes to show form and field signatures for debugging.
 const char kDebugAttributeForFormSignature[] = "form_signature";
 const char kDebugAttributeForFieldSignature[] = "field_signature";
-const char kDebugAttributeForParserAnnotations[] = "pm_parser_annotation";
 
 // Maps element names to the actual elements to simplify form filling.
 typedef std::map<base::string16, WebInputElement> FormInputElementMap;
@@ -102,24 +98,6 @@
 
 typedef std::vector<FormInputElementMap> FormElementsList;
 
-bool FillDataContainsFillableUsername(const PasswordFormFillData& fill_data) {
-  return !fill_data.username_field.name.empty() &&
-         (!fill_data.additional_logins.empty() ||
-          !fill_data.username_field.value.empty());
-}
-
-// Returns true if password form has username and password fields with either
-// same or no name and id attributes supplied.
-bool DoesFormContainAmbiguousOrEmptyNames(
-    const PasswordFormFillData& fill_data) {
-  return (fill_data.username_field.name == fill_data.password_field.name) ||
-         (fill_data.password_field.name ==
-              base::ASCIIToUTF16(kDummyPasswordField) &&
-          (!FillDataContainsFillableUsername(fill_data) ||
-           fill_data.username_field.name ==
-               base::ASCIIToUTF16(kDummyUsernameField)));
-}
-
 bool IsElementEditable(const WebInputElement& element) {
   return element.IsEnabled() && !element.IsReadOnly();
 }
@@ -263,32 +241,11 @@
       base::NumberToString(CalculateFormSignature(password_form.form_data)));
 }
 
-// Add parser annotations saved in |password_form| to |element|.
-void AddParserAnnotations(PasswordForm* password_form,
-                          blink::WebFormControlElement* element) {
-  base::string16 element_name = element->NameForAutofill().Utf16();
-  std::string attribute_value;
-  if (password_form->username_element == element_name) {
-    attribute_value = "username_element";
-  } else if (password_form->password_element == element_name) {
-    attribute_value = "password_element";
-  } else if (password_form->new_password_element == element_name) {
-    attribute_value = "new_password_element";
-  } else if (password_form->confirmation_password_element == element_name) {
-    attribute_value = "confirmation_password_element";
-  }
-  element->SetAttribute(
-      blink::WebString::FromASCII(kDebugAttributeForParserAnnotations),
-      attribute_value.empty() ? blink::WebString()
-                              : blink::WebString::FromASCII(attribute_value));
-}
-
 // Annotate |fields| with field signatures and form signature as HTML
 // attributes.
 void AnnotateFieldsWithSignatures(
     std::vector<blink::WebFormControlElement>* fields,
-    const blink::WebString& form_signature,
-    PasswordForm* password_form) {
+    const blink::WebString& form_signature) {
   for (blink::WebFormControlElement& control_element : *fields) {
     FieldSignature field_signature = CalculateFieldSignatureByNameAndType(
         control_element.NameForAutofill().Utf16(),
@@ -299,8 +256,6 @@
     control_element.SetAttribute(
         blink::WebString::FromASCII(kDebugAttributeForFormSignature),
         form_signature);
-    if (password_form)
-      AddParserAnnotations(password_form, &control_element);
   }
 }
 
@@ -310,7 +265,9 @@
                                           WebVector<WebFormElement>* forms) {
   for (WebFormElement& form : *forms) {
     std::unique_ptr<PasswordForm> password_form(
-        CreatePasswordFormFromWebForm(form, nullptr, nullptr, nullptr));
+        CreateSimplifiedPasswordFormFromWebForm(
+            form, /*field_data_manager=*/nullptr,
+            /*username_detector_cache=*/nullptr));
     WebString form_signature;
     if (password_form) {
       form_signature = GetFormSignatureAsWebString(*password_form);
@@ -319,21 +276,20 @@
     }
     std::vector<WebFormControlElement> form_fields =
         form_util::ExtractAutofillableElementsInForm(form);
-    AnnotateFieldsWithSignatures(&form_fields, form_signature,
-                                 password_form ? password_form.get() : nullptr);
+    AnnotateFieldsWithSignatures(&form_fields, form_signature);
   }
 
   std::vector<WebFormControlElement> unowned_elements =
       form_util::GetUnownedAutofillableFormFieldElements(
           frame->GetDocument().All(), nullptr);
   std::unique_ptr<PasswordForm> password_form(
-      CreatePasswordFormFromUnownedInputElements(*frame, nullptr, nullptr,
-                                                 nullptr));
+      CreateSimplifiedPasswordFormFromUnownedInputElements(
+          *frame, /*field_data_manager=*/nullptr,
+          /*username_detector_cache=*/nullptr));
   WebString form_signature;
   if (password_form)
     form_signature = GetFormSignatureAsWebString(*password_form);
-  AnnotateFieldsWithSignatures(&unowned_elements, form_signature,
-                               password_form ? password_form.get() : nullptr);
+  AnnotateFieldsWithSignatures(&unowned_elements, form_signature);
 }
 
 // Returns true iff there is a password field in |frame|.
@@ -413,6 +369,17 @@
   return false;
 }
 
+// Whether any of the fields in |form) is a non-empty password field.
+bool FormHasNonEmptyPasswordField(const FormData& form) {
+  for (const auto& field : form.fields) {
+    if (field.IsPasswordInputElement()) {
+      if (!field.value.empty() || !field.typed_value.empty())
+        return true;
+    }
+  }
+  return false;
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -844,11 +811,6 @@
     return true;
   }
 
-  if (element.NameForAutofill().IsEmpty() &&
-      !DoesFormContainAmbiguousOrEmptyNames(password_info->fill_data)) {
-    return false;  // If the field has no name, then we won't have values.
-  }
-
   // Don't attempt to autofill with values that are too large.
   if (element.Value().length() > kMaximumTextSizeForAutocomplete)
     return false;
@@ -1274,16 +1236,15 @@
 
 std::unique_ptr<PasswordForm> PasswordAutofillAgent::GetPasswordFormFromWebForm(
     const WebFormElement& web_form) {
-  return CreatePasswordFormFromWebForm(web_form, &field_data_manager_,
-                                       &form_predictions_,
-                                       &username_detector_cache_);
+  return CreateSimplifiedPasswordFormFromWebForm(web_form, &field_data_manager_,
+                                                 &username_detector_cache_);
 }
 
 std::unique_ptr<PasswordForm>
 PasswordAutofillAgent::GetSimplifiedPasswordFormFromWebForm(
     const WebFormElement& web_form) {
-  return CreateSimplifiedPasswordFormFromWebForm(web_form,
-                                                 &field_data_manager_);
+  return CreateSimplifiedPasswordFormFromWebForm(web_form, &field_data_manager_,
+                                                 &username_detector_cache_);
 }
 
 std::unique_ptr<PasswordForm>
@@ -1298,9 +1259,8 @@
   WebLocalFrame* web_frame = frame->GetWebFrame();
   if (!web_frame)
     return nullptr;
-  return CreatePasswordFormFromUnownedInputElements(
-      *web_frame, &field_data_manager_, &form_predictions_,
-      &username_detector_cache_);
+  return CreateSimplifiedPasswordFormFromUnownedInputElements(
+      *web_frame, &field_data_manager_, &username_detector_cache_);
 }
 
 std::unique_ptr<PasswordForm>
@@ -1312,7 +1272,7 @@
   if (!web_frame)
     return nullptr;
   return CreateSimplifiedPasswordFormFromUnownedInputElements(
-      *web_frame, &field_data_manager_);
+      *web_frame, &field_data_manager_, &username_detector_cache_);
 }
 
 // mojom::PasswordAutofillAgent:
@@ -1375,7 +1335,6 @@
   sent_request_to_store_ = false;
   checked_safe_browsing_reputation_ = false;
   username_query_prefix_.clear();
-  form_predictions_.clear();
   username_detector_cache_.clear();
   forms_structure_cache_.clear();
   autofilled_elements_cache_.clear();
@@ -1415,15 +1374,13 @@
   if (!password_form)
     return;
 
-  bool has_password = !password_form->password_value.empty() ||
-                      !password_form->new_password_value.empty();
+  bool has_password = FormHasNonEmptyPasswordField(password_form->form_data);
   if (restriction == RESTRICTION_NON_EMPTY_PASSWORD && !has_password)
     return;
 
   if (!FrameCanAccessPasswordManager())
     return;
 
-
   if (has_password) {
     GetPasswordManagerDriver()->ShowManualFallbackForSaving(*password_form);
   } else {
@@ -1794,8 +1751,6 @@
                                      WebString::FromUTF16(value));
 }
 
-void SetLastUpdatedFormAndField(const blink::WebFormElement& Form,
-                                const blink::WebFormControlElement& input);
 void PasswordAutofillAgent::SetLastUpdatedFormAndField(
     const WebFormElement& form,
     const WebFormControlElement& input) {
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 6bb319ac..b62e536 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -20,10 +20,8 @@
 #include "components/autofill/content/renderer/field_data_manager.h"
 #include "components/autofill/content/renderer/form_tracker.h"
 #include "components/autofill/content/renderer/html_based_username_detector.h"
-#include "components/autofill/core/common/form_data_predictions.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_view_observer.h"
@@ -494,10 +492,6 @@
   // Records the username typed before suggestions preview.
   base::string16 username_query_prefix_;
 
-  // Contains server predictions for username, password and/or new password
-  // fields for individual forms.
-  FormsPredictionsMap form_predictions_;
-
   // The HTML based username detector's cache which maps form elements to
   // username predictions.
   UsernameDetectorCache username_detector_cache_;
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.cc b/components/autofill/content/renderer/password_form_conversion_utils.cc
index 9e6c94e..597a023 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils.cc
@@ -4,34 +4,17 @@
 
 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
 
-#include <stddef.h>
-
-#include <algorithm>
-#include <set>
-#include <string>
-
-#include "base/i18n/case_conversion.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
-#include "base/stl_util.h"
-#include "base/strings/string16.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
 #include "components/autofill/content/renderer/form_autofill_util.h"
 #include "components/autofill/content/renderer/html_based_username_detector.h"
-#include "components/autofill/core/common/autofill_regex_constants.h"
-#include "components/autofill/core/common/autofill_regexes.h"
-#include "components/autofill/core/common/autofill_util.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/base/url_util.h"
 #include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_document.h"
 #include "third_party/blink/public/web/web_form_control_element.h"
 #include "third_party/blink/public/web/web_input_element.h"
@@ -48,8 +31,6 @@
 
 namespace autofill {
 
-using mojom::PasswordFormFieldPredictionType;
-
 namespace {
 
 constexpr char kAutocompleteUsername[] = "username";
@@ -129,136 +110,6 @@
   return it->second;
 }
 
-// Describes fields filtering criteria. More priority criteria has higher value
-// in the enum. The fields with the maximal criteria are considered in a form,
-// others are ignored. Criteria for password and username fields are calculated
-// separately. For example, if there is a password field with user input, the
-// password fields without user input are ignored (independently whether the
-// fields are visible or not).
-enum class FieldFilteringLevel {
-  NO_FILTER = 0,
-  VISIBILITY = 1,
-  USER_INPUT = 2
-};
-
-// Helper to determine which password is the main (current) one, and which is
-// the new password (e.g., on a sign-up or change password form), if any. If the
-// new password is found and there is another password field with the same user
-// input, the function also sets |confirmation_password| to this field.
-void LocateSpecificPasswords(std::vector<const FormFieldData*> passwords,
-                             const FormFieldData** current_password,
-                             const FormFieldData** new_password,
-                             const FormFieldData** confirmation_password,
-                             const AutocompleteCache& autocomplete_cache) {
-  DCHECK(!passwords.empty());
-  DCHECK(current_password && !*current_password);
-  DCHECK(new_password && !*new_password);
-  DCHECK(confirmation_password && !*confirmation_password);
-
-  // First, look for elements marked with either autocomplete='current-password'
-  // or 'new-password' -- if we find any, take the hint, and treat the first of
-  // each kind as the element we are looking for.
-  for (const FormFieldData* password : passwords) {
-    const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(password);
-    if (flag == AutocompleteFlag::CURRENT_PASSWORD && !*current_password) {
-      *current_password = password;
-    } else if (flag == AutocompleteFlag::NEW_PASSWORD && !*new_password) {
-      *new_password = password;
-    } else if (*new_password && ((*new_password)->value == password->value)) {
-      *confirmation_password = password;
-    }
-  }
-
-  // If we have seen an element with either of autocomplete attributes above,
-  // take that as a signal that the page author must have intentionally left the
-  // rest of the password fields unmarked. Perhaps they are used for other
-  // purposes, e.g., PINs, OTPs, and the like. So we skip all the heuristics we
-  // normally do, and ignore the rest of the password fields.
-  if (*current_password || *new_password)
-    return;
-
-  switch (passwords.size()) {
-    case 1:
-      // Single password, easy.
-      *current_password = passwords[0];
-      break;
-    case 2:
-      if (!passwords[0]->value.empty() &&
-          passwords[0]->value == passwords[1]->value) {
-        // Two identical non-empty passwords: assume we are seeing a new
-        // password with a confirmation. This can be either a sign-up form or a
-        // password change form that does not ask for the old password.
-        *new_password = passwords[0];
-        *confirmation_password = passwords[1];
-      } else {
-        // Assume first is old password, second is new (no choice but to guess).
-        // This case also includes empty passwords in order to allow filling of
-        // password change forms (that also could autofill for sign up form, but
-        // we can't do anything with this using only client side information).
-        *current_password = passwords[0];
-        *new_password = passwords[1];
-      }
-      break;
-    default:
-      if (!passwords[0]->value.empty() &&
-          passwords[0]->value == passwords[1]->value &&
-          passwords[0]->value == passwords[2]->value) {
-        // All three passwords are the same and non-empty? It may be a change
-        // password form where old and new passwords are the same. It doesn't
-        // matter what field is correct, let's save the value.
-        *current_password = passwords[0];
-      } else if (passwords[1]->value == passwords[2]->value) {
-        // New password is the duplicated one, and comes second; or empty form
-        // with 3 password fields, in which case we will assume this layout.
-        *current_password = passwords[0];
-        *new_password = passwords[1];
-        *confirmation_password = passwords[2];
-      } else if (passwords[0]->value == passwords[1]->value) {
-        // It is strange that the new password comes first, but trust more which
-        // fields are duplicated than the ordering of fields. Assume that
-        // any password fields after the new password contain sensitive
-        // information that isn't actually a password (security hint, SSN, etc.)
-        *new_password = passwords[0];
-        *confirmation_password = passwords[1];
-      } else {
-        // Three different passwords, or first and last match with middle
-        // different. No idea which is which. Let's save the first password.
-        // Password selection in a prompt will allow to correct the choice.
-        *current_password = passwords[0];
-      }
-  }
-}
-
-void FindPredictedElements(
-    const FormData& form_data,
-    const FormsPredictionsMap& form_predictions,
-    std::map<const FormFieldData*, PasswordFormFieldPredictionType>*
-        predicted_fields) {
-  // Matching only requires that action and name of the form match to allow
-  // the username to be updated even if the form is changed after page load.
-  // See https://crbug.com/476092 for more details.
-  auto field_predictions = std::find_if(
-      form_predictions.begin(), form_predictions.end(),
-      [&form_data](const auto& form_predictions_pair) {
-        return form_predictions_pair.first.action == form_data.action &&
-               form_predictions_pair.first.name == form_data.name;
-      });
-
-  if (field_predictions == form_predictions.end())
-    return;
-
-  for (const auto& prediction : field_predictions->second) {
-    const FormFieldData& target_field = prediction.first;
-    const PasswordFormFieldPredictionType& type = prediction.second;
-    for (const FormFieldData& field : form_data.fields) {
-      if (field.name == target_field.name) {
-        (*predicted_fields)[&field] = type;
-        break;
-      }
-    }
-  }
-}
-
 const char kPasswordSiteUrlRegex[] =
     "passwords(?:-[a-z-]+\\.corp)?\\.google\\.com";
 
@@ -272,116 +123,6 @@
 base::LazyInstance<re2::RE2, PasswordSiteUrlLazyInstanceTraits>
     g_password_site_matcher = LAZY_INSTANCE_INITIALIZER;
 
-// Returns the |input_field| name if its non-empty; otherwise a |dummy_name|.
-base::string16 FieldName(const FormFieldData* input_field,
-                         const char* dummy_name) {
-  return input_field->name.empty() ? base::ASCIIToUTF16(dummy_name)
-                                   : input_field->name;
-}
-
-// Return the maximal filtering criteria that |field| passes.
-// If |ignore_autofilled_values|, autofilled value isn't considered user input.
-FieldFilteringLevel GetFiltertingLevelForField(const FormFieldData& field,
-                                               bool ignore_autofilled_values) {
-  FieldPropertiesMask user_input_mask =
-      ignore_autofilled_values
-          ? FieldPropertiesFlags::USER_TYPED
-          : FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::AUTOFILLED;
-  if (field.properties_mask & user_input_mask)
-    return FieldFilteringLevel::USER_INPUT;
-  return field.is_focusable ? FieldFilteringLevel::VISIBILITY
-                            : FieldFilteringLevel::NO_FILTER;
-}
-
-// Calculates the maximal filtering levels for password and username fields and
-// saves them to |username_fields_level| and |password_fields_level|. The
-// criteria for username fields considers only the fields before the first
-// password field that has the maximal filtering level.
-void GetFieldFilteringLevels(const std::vector<FormFieldData>& fields,
-                             FieldFilteringLevel* username_fields_level,
-                             FieldFilteringLevel* password_fields_level) {
-  DCHECK(password_fields_level);
-  DCHECK(username_fields_level);
-  *username_fields_level = FieldFilteringLevel::NO_FILTER;
-  *password_fields_level = FieldFilteringLevel::NO_FILTER;
-
-  FieldFilteringLevel max_level_found_for_username_fields =
-      FieldFilteringLevel::NO_FILTER;
-  for (const FormFieldData& field : fields) {
-    if (!field.is_enabled || !field.IsTextInputElement())
-      continue;
-
-    // TODO(crbug.com/789917): Ignore autofilled values here because if there
-    // are only autofilled values then a form may not be filled completely (i.e.
-    // some user input is still expected). So, user input shouldn't be used for
-    // fields filtering. Once the bug is resolved, autofilled values will not be
-    // ignored.
-    FieldFilteringLevel current_field_filtering_level =
-        GetFiltertingLevelForField(field, true /* ignore_autofilled_values */);
-
-    if (field.form_control_type == "password") {
-      if (*password_fields_level < current_field_filtering_level) {
-        *password_fields_level = current_field_filtering_level;
-        *username_fields_level = max_level_found_for_username_fields;
-      }
-    } else {
-      max_level_found_for_username_fields = std::max(
-          max_level_found_for_username_fields, current_field_filtering_level);
-    }
-  }
-}
-
-ValueElementPair MakePossibleUsernamePair(const FormFieldData* input) {
-  base::string16 trimmed_input_value;
-  base::TrimString(input->value, base::ASCIIToUTF16(" "), &trimmed_input_value);
-  return {trimmed_input_value, input->name};
-}
-
-bool StringMatchesCVC(const base::string16& str) {
-  static const base::NoDestructor<base::string16> kCardCvcReCached(
-      base::UTF8ToUTF16(kCardCvcRe));
-
-  return MatchesPattern(str, *kCardCvcReCached);
-}
-
-// Which types of password fields are present in a form?
-enum class PasswordContents {
-  kEnabled,       // At least one enabled password field.
-  kOnlyDisabled,  // At least one password field, but not enabled.
-  kNone           // No password fields present.
-};
-
-// Returns the PasswordContents reflecting the contents of |fields|.
-PasswordContents GetPasswordContents(const std::vector<FormFieldData>& fields) {
-  PasswordContents result = PasswordContents::kNone;
-  for (const FormFieldData& field : fields) {
-    if (field.form_control_type != "password")
-      continue;
-    result = PasswordContents::kOnlyDisabled;
-    if (field.is_enabled)
-      return PasswordContents::kEnabled;
-  }
-  return result;
-}
-
-// Find the first element in |username_predictions| (i.e. the most reliable
-// prediction) that occurs in |possible_usernames|.
-const FormFieldData* FindUsernameInPredictions(
-    const std::vector<uint32_t>& username_predictions,
-    const std::vector<const FormFieldData*>& possible_usernames) {
-  for (uint32_t predicted_id : username_predictions) {
-    auto iter =
-        std::find_if(possible_usernames.begin(), possible_usernames.end(),
-                     [predicted_id](const FormFieldData* field) {
-                       return field->unique_renderer_id == predicted_id;
-                     });
-    if (iter != possible_usernames.end()) {
-      return *iter;
-    }
-  }
-  return nullptr;
-}
-
 // Extracts the username predictions. |control_elements| should be all the DOM
 // elements of the form, |form_data| should be the already extracted FormData
 // representation of that form. |username_detector_cache| is optional, and can
@@ -401,387 +142,6 @@
                                                   username_detector_cache);
 }
 
-// Get information about a login form encapsulated in a PasswordForm struct.
-// If an element of |form| has an entry in |nonscript_modified_values|, the
-// associated string is used instead of the element's value to create
-// the PasswordForm.
-bool GetPasswordForm(const GURL& form_origin,
-                     const std::vector<WebFormControlElement>& control_elements,
-                     PasswordForm* password_form,
-                     const FormsPredictionsMap* form_predictions,
-                     UsernameDetectorCache* username_detector_cache) {
-  DCHECK(!control_elements.empty());
-
-  const FormData& form_data = password_form->form_data;
-  PasswordContents password_contents = GetPasswordContents(form_data.fields);
-  switch (password_contents) {
-    case PasswordContents::kEnabled:
-      // All well, continue parsing.
-      break;
-    case PasswordContents::kOnlyDisabled:
-      // The current parser gives up, but returns a fallback form so that the
-      // newer parser can try parsing as well.
-      password_form->scheme = PasswordForm::Scheme::kHtml;
-      password_form->origin = form_origin;
-      password_form->signon_realm = GetSignOnRealm(password_form->origin);
-      return true;
-    case PasswordContents::kNone:
-      return false;
-  }
-  // Evaluate the context of the fields.
-  password_form->form_data.username_predictions = GetUsernamePredictions(
-      control_elements, form_data, username_detector_cache);
-
-  // Narrow the scope to enabled text inputs.
-  std::vector<const FormFieldData*> enabled_fields;
-  enabled_fields.reserve(form_data.fields.size());
-  for (const FormFieldData& field : form_data.fields) {
-    if (field.is_enabled && field.IsTextInputElement())
-      enabled_fields.push_back(&field);
-  }
-
-  // Remember the list of password fields without any heuristics applied in case
-  // the heuristics fail and a fall-back is needed:
-  // All password fields.
-  std::vector<const FormFieldData*> passwords_without_heuristics;
-  // Map from all password fields to the most recent non-password text input.
-  std::map<const FormFieldData*, const FormFieldData*>
-      preceding_text_input_for_password_without_heuristics;
-  const FormFieldData* most_recent_text_input = nullptr;  // Just a temporary.
-  for (const FormFieldData* input : enabled_fields) {
-    if (input->form_control_type == "password") {
-      passwords_without_heuristics.push_back(input);
-      preceding_text_input_for_password_without_heuristics[input] =
-          most_recent_text_input;
-    } else {
-      most_recent_text_input = input;
-    }
-  }
-
-  // Fill the cache with autocomplete flags.
-  AutocompleteCache autocomplete_cache;
-  for (const FormFieldData* input : enabled_fields) {
-    autocomplete_cache.Store(input);
-  }
-
-  // Narrow the scope further: drop credit-card fields.
-  std::vector<const FormFieldData*> plausible_inputs;
-  plausible_inputs.reserve(enabled_fields.size());
-  for (const FormFieldData* input : enabled_fields) {
-    const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input);
-    if (flag == AutocompleteFlag::CURRENT_PASSWORD ||
-        flag == AutocompleteFlag::NEW_PASSWORD) {
-      // A field marked as a password is considered not a credit-card field, no
-      // matter what.
-      plausible_inputs.push_back(input);
-    } else if (flag != AutocompleteFlag::CREDIT_CARD) {
-      const bool is_credit_card_verification =
-          input->form_control_type == "password" &&
-          (StringMatchesCVC(input->name_attribute) ||
-           StringMatchesCVC(input->id_attribute));
-      if (!is_credit_card_verification) {
-        // Otherwise ensure that nothing hints that |input| is a credit-card
-        // field.
-        plausible_inputs.push_back(input);
-      }
-    }
-  }
-
-  // Further narrow to interesting fields (e.g., with user input, visible), if
-  // present.
-  // Compute the best filtering levels for usernames and for passwords.
-  FieldFilteringLevel username_fields_level = FieldFilteringLevel::NO_FILTER;
-  FieldFilteringLevel password_fields_level = FieldFilteringLevel::NO_FILTER;
-  GetFieldFilteringLevels(form_data.fields, &username_fields_level,
-                          &password_fields_level);
-  // Remove all fields with filtering level below the best.
-  base::EraseIf(
-      plausible_inputs, [password_fields_level,
-                         username_fields_level](const FormFieldData* input) {
-        FieldFilteringLevel current_field_level = GetFiltertingLevelForField(
-            *input, false /* ignore_autofilled_values */);
-        if (input->form_control_type == "password")
-          return current_field_level < password_fields_level;
-        return current_field_level < username_fields_level;
-      });
-
-  // Further, remove all readonly passwords. If the password field is readonly,
-  // the page is likely using a virtual keyboard and bypassing the password
-  // field value (see http://crbug.com/475488). There is nothing Chrome can do
-  // to fill passwords for now. Notable exceptions: if the password field was
-  // made readonly by JavaScript before submission, it remains interesting. If
-  // the password was marked via the autocomplete attribute, it also remains
-  // interesting.
-  base::EraseIf(plausible_inputs, [&autocomplete_cache](
-                                      const FormFieldData* input) {
-    if (!input->is_readonly)
-      return false;
-    if (input->form_control_type != "password")
-      return false;
-    // Check if the field was only made readonly before submission.
-    if (input->properties_mask &
-        (FieldPropertiesFlags::USER_TYPED | FieldPropertiesFlags::AUTOFILLED)) {
-      return false;
-    }
-    // Check whether the field was explicitly marked as password.
-    const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input);
-    if (flag == AutocompleteFlag::CURRENT_PASSWORD ||
-        flag == AutocompleteFlag::NEW_PASSWORD) {
-      return false;
-    }
-    return true;
-  });
-
-  // Evaluate available server-side predictions.
-  std::map<const FormFieldData*, PasswordFormFieldPredictionType>
-      predicted_fields;
-  const FormFieldData* predicted_username_field = nullptr;
-  if (form_predictions) {
-    FindPredictedElements(password_form->form_data, *form_predictions,
-                          &predicted_fields);
-
-    for (const auto& predicted_pair : predicted_fields) {
-      if (predicted_pair.second == PasswordFormFieldPredictionType::kUsername) {
-        predicted_username_field = predicted_pair.first;
-        break;
-      }
-    }
-  }
-
-  // Finally, remove all password fields for which we have a negative
-  // prediction, unless they are explicitly marked by the autocomplete attribute
-  // as a password.
-  base::EraseIf(plausible_inputs, [&predicted_fields, &autocomplete_cache](
-                                      const FormFieldData* input) {
-    if (input->form_control_type != "password")
-      return false;
-    const AutocompleteFlag flag = autocomplete_cache.RetrieveFor(input);
-    if (flag == AutocompleteFlag::CURRENT_PASSWORD ||
-        flag == AutocompleteFlag::NEW_PASSWORD) {
-      return false;
-    }
-    auto possible_password_field_iterator = predicted_fields.find(input);
-    return possible_password_field_iterator != predicted_fields.end() &&
-           possible_password_field_iterator->second ==
-               PasswordFormFieldPredictionType::kNotPassword;
-  });
-
-  // Derive the list of all plausible passwords, usernames and the non-password
-  // inputs preceding the plausible passwords.
-  std::vector<const FormFieldData*> plausible_passwords;
-  std::vector<const FormFieldData*> plausible_usernames;
-  std::map<const FormFieldData*, const FormFieldData*>
-      preceding_text_input_for_plausible_password;
-  most_recent_text_input = nullptr;
-  plausible_usernames.reserve(plausible_inputs.size());
-  for (const FormFieldData* input : plausible_inputs) {
-    if (input->form_control_type == "password") {
-      plausible_passwords.push_back(input);
-      preceding_text_input_for_plausible_password[input] =
-          most_recent_text_input;
-    } else {
-      plausible_usernames.push_back(input);
-      most_recent_text_input = input;
-    }
-  }
-
-  // Evaluate autocomplete attributes for username.
-  const FormFieldData* username_by_attribute = nullptr;
-  for (const FormFieldData* input : plausible_inputs) {
-    if (input->form_control_type != "password") {
-      if (autocomplete_cache.RetrieveFor(input) == AutocompleteFlag::USERNAME) {
-        // Only consider the first occurrence of autocomplete='username'.
-        // Multiple occurences hint at the attribute being used incorrectly, in
-        // which case sticking to the first one is just a bet.
-        if (!username_by_attribute) {
-          username_by_attribute = input;
-          break;
-        }
-      }
-    }
-  }
-
-  // Evaluate the context of the fields.
-  const FormFieldData* username_field_by_context = nullptr;
-  // Use HTML based username detector only if neither server predictions nor
-  // autocomplete attributes were useful to detect the username.
-  if (!predicted_username_field && !username_by_attribute) {
-    username_field_by_context = FindUsernameInPredictions(
-        form_data.username_predictions, plausible_usernames);
-  }
-
-  // Populate all_possible_passwords and form_has_autofilled_value in
-  // |password_form|.
-  // Contains the first password element for each non-empty password value.
-  std::vector<ValueElementPair> all_possible_passwords;
-  // Reserve enough space to prevent re-allocation. A re-allocation would
-  // invalidate the contents of |seen_values|.
-  all_possible_passwords.reserve(passwords_without_heuristics.size());
-  std::set<base::StringPiece16> seen_values;
-  // Pretend that an empty value has been already seen, so that empty-valued
-  // password elements won't get added to |all_possible_passwords|.
-  seen_values.insert(base::StringPiece16());
-  for (const FormFieldData* password_field : passwords_without_heuristics) {
-    if (seen_values.count(password_field->value) > 0)
-      continue;
-    all_possible_passwords.push_back(
-        {password_field->value, password_field->name});
-    seen_values.insert(
-        base::StringPiece16(all_possible_passwords.back().first));
-  }
-
-  bool form_has_autofilled_value = false;
-  for (const FormFieldData* password_field : passwords_without_heuristics) {
-    bool field_has_autofilled_value =
-        password_field->properties_mask & FieldPropertiesFlags::AUTOFILLED;
-    form_has_autofilled_value |= field_has_autofilled_value;
-  }
-
-  if (!all_possible_passwords.empty()) {
-    password_form->all_possible_passwords = std::move(all_possible_passwords);
-    password_form->form_has_autofilled_value = form_has_autofilled_value;
-  }
-
-  // If for some reason (e.g. only credit card fields, confusing autocomplete
-  // attributes) the passwords list is empty, build list based on user input (if
-  // there is any non-empty password field) and the type of a field. Also mark
-  // that the form should be available only for fallback saving (automatic
-  // bubble will not pop up) and filling.
-  password_form->only_for_fallback = plausible_passwords.empty();
-  if (plausible_passwords.empty()) {
-    plausible_passwords = std::move(passwords_without_heuristics);
-    preceding_text_input_for_plausible_password =
-        std::move(preceding_text_input_for_password_without_heuristics);
-  }
-
-  // Find the password fields.
-  const FormFieldData* password = nullptr;
-  const FormFieldData* new_password = nullptr;
-  const FormFieldData* confirmation_password = nullptr;
-  LocateSpecificPasswords(std::move(plausible_passwords), &password,
-                          &new_password, &confirmation_password,
-                          autocomplete_cache);
-
-  // Choose the username element.
-  const FormFieldData* username_field = nullptr;
-  UsernameDetectionMethod username_detection_method =
-      UsernameDetectionMethod::NO_USERNAME_DETECTED;
-  password_form->username_marked_by_site = false;
-
-  if (predicted_username_field) {
-    // Server predictions are most trusted, so try them first. Only if the form
-    // already has user input and the predicted username field has an empty
-    // value, then don't trust the prediction (can be caused by, e.g., a <form>
-    // actually contains several forms).
-    if (password_fields_level < FieldFilteringLevel::USER_INPUT ||
-        !predicted_username_field->value.empty()) {
-      username_field = predicted_username_field;
-      password_form->was_parsed_using_autofill_predictions = true;
-      username_detection_method =
-          UsernameDetectionMethod::SERVER_SIDE_PREDICTION;
-    }
-  }
-
-  if (!username_field && username_by_attribute) {
-    // Next in the trusted queue: autocomplete attributes.
-    username_field = username_by_attribute;
-    username_detection_method = UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE;
-  }
-
-  if (!username_field && username_field_by_context) {
-    // Last step before base heuristics: HTML-based classifier.
-    username_field = username_field_by_context;
-    username_detection_method = UsernameDetectionMethod::HTML_BASED_CLASSIFIER;
-  }
-
-  // Compute base heuristic for username detection.
-  const FormFieldData* base_heuristic_username = nullptr;
-  if (password) {
-    base_heuristic_username =
-        preceding_text_input_for_plausible_password[password];
-  }
-  if (!base_heuristic_username && new_password) {
-    base_heuristic_username =
-        preceding_text_input_for_plausible_password[new_password];
-  }
-
-  // Apply base heuristic for username detection.
-  if (!username_field) {
-    username_field = base_heuristic_username;
-    if (username_field)
-      username_detection_method = UsernameDetectionMethod::BASE_HEURISTIC;
-  } else if (base_heuristic_username == username_field &&
-             username_detection_method !=
-                 UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE) {
-    // TODO(crbug.com/786404): when the bug is fixed, remove this block and
-    // calculate |base_heuristic_username| only if |username_field| is null.
-    // This block was added to measure the impact of server-side predictions and
-    // HTML based classifier compared to "old classifiers" (the based heuristic
-    // and 'autocomplete' attribute).
-    username_detection_method = UsernameDetectionMethod::BASE_HEURISTIC;
-  }
-  UMA_HISTOGRAM_ENUMERATION(
-      "PasswordManager.UsernameDetectionMethod", username_detection_method,
-      UsernameDetectionMethod::USERNAME_DETECTION_METHOD_COUNT);
-
-  // Populate the username fields in |password_form|.
-  if (username_field) {
-    password_form->username_element =
-        FieldName(username_field, "anonymous_username");
-    password_form->username_value = username_field->value;
-    if ((username_field->properties_mask &
-         (FieldPropertiesFlags::USER_TYPED |
-          FieldPropertiesFlags::AUTOFILLED)) &&
-        !username_field->typed_value.empty()) {
-      password_form->username_value = username_field->typed_value;
-    }
-  }
-
-  // Populate the password fields in |password_form|.
-  if (password) {
-    password_form->password_element = FieldName(password, "anonymous_password");
-    password_form->password_value = password->value;
-    if ((password->properties_mask & (FieldPropertiesFlags::USER_TYPED |
-                                      FieldPropertiesFlags::AUTOFILLED)) &&
-        !password->typed_value.empty()) {
-      password_form->password_value = password->typed_value;
-    }
-  }
-  if (new_password) {
-    password_form->new_password_element =
-        FieldName(new_password, "anonymous_new_password");
-    password_form->new_password_value = new_password->value;
-    if (autocomplete_cache.RetrieveFor(new_password) ==
-        AutocompleteFlag::NEW_PASSWORD) {
-      password_form->new_password_marked_by_site = true;
-    }
-    if (confirmation_password) {
-      password_form->confirmation_password_element =
-          FieldName(confirmation_password, "anonymous_confirmation_password");
-    }
-  }
-
-  // Populate |all_possible_usernames| in |password_form|.
-  ValueElementVector all_possible_usernames;
-  for (const FormFieldData* plausible_username : plausible_usernames) {
-    if (plausible_username == username_field)
-      continue;
-    auto pair = MakePossibleUsernamePair(plausible_username);
-    if (!pair.first.empty())
-      all_possible_usernames.push_back(std::move(pair));
-  }
-  password_form->all_possible_usernames = std::move(all_possible_usernames);
-
-  password_form->origin = std::move(form_origin);
-  password_form->signon_realm = GetSignOnRealm(password_form->origin);
-  password_form->scheme = PasswordForm::Scheme::kHtml;
-  password_form->preferred = false;
-  password_form->blacklisted_by_user = false;
-  password_form->type = PasswordForm::Type::kManual;
-
-  return true;
-}
-
 bool HasGaiaSchemeAndHost(const WebFormElement& form) {
   GURL form_url = form.GetDocument().Url();
   GURL gaia_url = GaiaUrls::GetInstance()->gaia_url();
@@ -854,7 +214,8 @@
 
 std::unique_ptr<PasswordForm> CreateSimplifiedPasswordFormFromWebForm(
     const WebFormElement& web_form,
-    const FieldDataManager* field_data_manager) {
+    const FieldDataManager* field_data_manager,
+    UsernameDetectorCache* username_detector_cache) {
   if (web_form.IsNull())
     return nullptr;
 
@@ -867,32 +228,6 @@
       IsGaiaWithSkipSavePasswordForm(web_form) ||
       IsGaiaReauthenticationForm(web_form);
 
-  if (!WebFormElementToFormData(web_form, WebFormControlElement(),
-                                field_data_manager, form_util::EXTRACT_VALUE,
-                                &password_form->form_data,
-                                nullptr /* FormFieldData */)) {
-    return nullptr;
-  }
-
-  return password_form;
-}
-
-std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm(
-    const WebFormElement& web_form,
-    const FieldDataManager* field_data_manager,
-    const FormsPredictionsMap* form_predictions,
-    UsernameDetectorCache* username_detector_cache) {
-  if (web_form.IsNull())
-    return nullptr;
-
-  auto password_form = std::make_unique<PasswordForm>();
-  password_form->action = form_util::GetCanonicalActionForForm(web_form);
-  if (!password_form->action.is_valid())
-    return nullptr;
-  password_form->form_data.is_gaia_with_skip_save_password_form =
-      IsGaiaWithSkipSavePasswordForm(web_form) ||
-      IsGaiaReauthenticationForm(web_form);
-
   blink::WebVector<WebFormControlElement> control_elements;
   web_form.GetFormControlElements(control_elements);
   if (control_elements.empty())
@@ -904,20 +239,18 @@
                                 nullptr /* FormFieldData */)) {
     return nullptr;
   }
+  password_form->form_data.username_predictions =
+      GetUsernamePredictions(control_elements.ReleaseVector(),
+                             password_form->form_data, username_detector_cache);
 
-  if (!GetPasswordForm(
-          form_util::GetCanonicalOriginForDocument(web_form.GetDocument()),
-          control_elements.ReleaseVector(), password_form.get(),
-          form_predictions, username_detector_cache)) {
-    return nullptr;
-  }
   return password_form;
 }
 
 std::unique_ptr<PasswordForm>
 CreateSimplifiedPasswordFormFromUnownedInputElements(
     const WebLocalFrame& frame,
-    const FieldDataManager* field_data_manager) {
+    const FieldDataManager* field_data_manager,
+    UsernameDetectorCache* username_detector_cache) {
   std::vector<WebElement> fieldsets;
   std::vector<WebFormControlElement> control_elements =
       form_util::GetUnownedFormFieldElements(frame.GetDocument().All(),
@@ -936,38 +269,8 @@
   password_form->origin =
       form_util::GetCanonicalOriginForDocument(frame.GetDocument());
   password_form->signon_realm = GetSignOnRealm(password_form->origin);
-  return password_form;
-}
-
-std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
-    const WebLocalFrame& frame,
-    const FieldDataManager* field_data_manager,
-    const FormsPredictionsMap* form_predictions,
-    UsernameDetectorCache* username_detector_cache) {
-  std::vector<WebElement> fieldsets;
-  std::vector<WebFormControlElement> control_elements =
-      form_util::GetUnownedFormFieldElements(frame.GetDocument().All(),
-                                             &fieldsets);
-  if (control_elements.empty())
-    return nullptr;
-
-  auto password_form = std::make_unique<PasswordForm>();
-  if (!UnownedPasswordFormElementsAndFieldSetsToFormData(
-          fieldsets, control_elements, nullptr, frame.GetDocument(),
-          field_data_manager, form_util::EXTRACT_VALUE,
-          &password_form->form_data, nullptr /* FormFieldData */)) {
-    return nullptr;
-  }
-
-  if (!GetPasswordForm(
-          form_util::GetCanonicalOriginForDocument(frame.GetDocument()),
-          control_elements, password_form.get(), form_predictions,
-          username_detector_cache)) {
-    return nullptr;
-  }
-
-  // No actual action on the form, so use the the origin as the action.
-  password_form->action = password_form->origin;
+  password_form->form_data.username_predictions = GetUsernamePredictions(
+      control_elements, password_form->form_data, username_detector_cache);
   return password_form;
 }
 
diff --git a/components/autofill/content/renderer/password_form_conversion_utils.h b/components/autofill/content/renderer/password_form_conversion_utils.h
index e02034c8..40585c4 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils.h
+++ b/components/autofill/content/renderer/password_form_conversion_utils.h
@@ -14,7 +14,6 @@
 #include "base/strings/string_piece.h"
 #include "components/autofill/content/renderer/html_based_username_detector.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "url/gurl.h"
 
@@ -34,15 +33,6 @@
 
 class FieldDataManager;
 
-enum UsernameDetectionMethod {
-  NO_USERNAME_DETECTED,
-  BASE_HEURISTIC,
-  HTML_BASED_CLASSIFIER,
-  AUTOCOMPLETE_ATTRIBUTE,
-  SERVER_SIDE_PREDICTION,
-  USERNAME_DETECTION_METHOD_COUNT
-};
-
 // The susbset of autocomplete flags related to passwords.
 enum class AutocompleteFlag {
   NONE,
@@ -66,34 +56,11 @@
 // Tests whether the given form is a GAIA form with a skip password argument.
 bool IsGaiaWithSkipSavePasswordForm(const blink::WebFormElement& form);
 
-// Create a PasswordForm from DOM form. Webkit doesn't allow storing
-// custom metadata to DOM nodes, so we have to do this every time an event
-// happens with a given form and compare against previously Create'd forms
-// to identify..which sucks.
-// If an element of |form| has an entry in |field_data_manager|, the associated
-// string is used instead of the element's value to create the PasswordForm.
-// |form_predictions| is Autofill server response, if present it's used for
-// overwriting default username element selection.
-// |username_detector_cache| is used by the built-in HTML based username
-// detector to cache results. Can be null.
-std::unique_ptr<PasswordForm> CreatePasswordFormFromWebForm(
-    const blink::WebFormElement& form,
-    const FieldDataManager* field_data_manager,
-    const FormsPredictionsMap* form_predictions,
-    UsernameDetectorCache* username_detector_cache);
-
 // Creates a |PasswordForm| from DOM which only contains the |form_data| as well
 // as origin, action and gaia flags.
 std::unique_ptr<PasswordForm> CreateSimplifiedPasswordFormFromWebForm(
     const blink::WebFormElement& form,
-    const FieldDataManager* field_data_manager);
-
-// Same as CreatePasswordFormFromWebForm() but for input elements that are not
-// enclosed in <form> element.
-std::unique_ptr<PasswordForm> CreatePasswordFormFromUnownedInputElements(
-    const blink::WebLocalFrame& frame,
     const FieldDataManager* field_data_manager,
-    const FormsPredictionsMap* form_predictions,
     UsernameDetectorCache* username_detector_cache);
 
 // Same as CreateSimlePasswordFormFromWebForm() but for input elements that are
@@ -101,7 +68,8 @@
 std::unique_ptr<PasswordForm>
 CreateSimplifiedPasswordFormFromUnownedInputElements(
     const blink::WebLocalFrame& frame,
-    const FieldDataManager* field_data_manager);
+    const FieldDataManager* field_data_manager,
+    UsernameDetectorCache* username_detector_cache);
 
 // The "Realm" for the sign-on. This is scheme, host, port.
 std::string GetSignOnRealm(const GURL& origin);
diff --git a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
index 02b36b862..7b67bc5 100644
--- a/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
+++ b/components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc
@@ -5,47 +5,24 @@
 #include <stddef.h>
 #include <memory>
 
-#include "base/stl_util.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "components/autofill/content/renderer/field_data_manager.h"
-#include "components/autofill/content/renderer/form_autofill_util.h"
 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
-#include "components/autofill/core/browser/form_structure.h"
-#include "components/autofill/core/common/form_field_data.h"
-#include "components/autofill/core/common/password_form.h"
 #include "content/public/test/render_view_test.h"
 #include "google_apis/gaia/gaia_urls.h"
-#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/web_document.h"
-#include "third_party/blink/public/web/web_form_control_element.h"
 #include "third_party/blink/public/web/web_form_element.h"
-#include "third_party/blink/public/web/web_input_element.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 
-using blink::WebElement;
-using blink::WebFormControlElement;
 using blink::WebFormElement;
-using blink::WebInputElement;
 using blink::WebLocalFrame;
-using blink::WebString;
 using blink::WebVector;
 
 namespace autofill {
 
-using mojom::PasswordFormFieldPredictionType;
-
 namespace {
 
-const char kTestFormActionURL[] = "http://cnn.com";
-
 // A builder to produce HTML code for a password form composed of the desired
 // number and kinds of username and password fields.
 class PasswordFormBuilder {
@@ -73,20 +50,6 @@
         name_and_id, name_and_id, value, autocomplete_attribute.c_str());
   }
 
-  // Add a text field with name, id, value, label and placeholder,
-  // without autocomplete.
-  void AddTextFieldWithoutAutocomplete(const char* name,
-                                       const char* id,
-                                       const char* value,
-                                       const char* label,
-                                       const char* placeholder) {
-    base::StringAppendF(&html_,
-                        "<LABEL for=\"%s\">%s</LABEL>"
-                        "<INPUT type=\"text\" name=\"%s\" id=\"%s\" "
-                        "value=\"%s\" placeholder=\"%s\"/>",
-                        id, label, name, id, value, placeholder);
-  }
-
   // Appends a new password-type field at the end of the form, having the
   // specified |name_and_id|, |value|, and |autocomplete| attributes. Special
   // values for |autocomplete| are the same as in AddTextField.
@@ -102,19 +65,6 @@
         name_and_id, name_and_id, value, autocomplete_attribute.c_str());
   }
 
-  // Appends a disabled text-type field at the end of the form.
-  void AddDisabledUsernameField() {
-    html_ += "<INPUT name=\"disabled field\" type=\"text\" disabled/>";
-  }
-
-  // Appends a disabled password-type field at the end of the form.
-  void AddDisabledPasswordField() {
-    html_ += "<INPUT name=\"disabled field\" type=\"password\" disabled/>";
-  }
-
-  // Appends a hidden field at the end of the form.
-  void AddHiddenField() { html_ += "<INPUT type=\"hidden\"/>"; }
-
   // Appends a new hidden-type field at the end of the form, having the
   // specified |name_and_id| and |value| attributes.
   void AddHiddenField(const char* name_and_id, const char* value) {
@@ -123,68 +73,10 @@
         name_and_id, name_and_id, value);
   }
 
-  // Append a text field with "display: none".
-  void AddNonDisplayedTextField(const char* name_and_id, const char* value) {
-    base::StringAppendF(
-        &html_,
-        "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\""
-        "style=\"display: none;\"/>",
-        name_and_id, name_and_id, value);
-  }
-
-  // Append a password field with "display: none".
-  void AddNonDisplayedPasswordField(const char* name_and_id,
-                                    const char* value) {
-    base::StringAppendF(
-        &html_,
-        "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\""
-        "style=\"display: none;\"/>",
-        name_and_id, name_and_id, value);
-  }
-
-  // Append a text field with "visibility: hidden".
-  void AddNonVisibleTextField(const char* name_and_id, const char* value) {
-    base::StringAppendF(
-        &html_,
-        "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\""
-        "style=\"visibility: hidden;\"/>",
-        name_and_id, name_and_id, value);
-  }
-
-  // Append a password field with "visibility: hidden".
-  void AddNonVisiblePasswordField(const char* name_and_id, const char* value) {
-    base::StringAppendF(
-        &html_,
-        "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\""
-        "style=\"visibility: hidden;\"/>",
-        name_and_id, name_and_id, value);
-  }
-
-  // Add a field with a given type. Useful to add non-text fields.
-  void AddFieldWithType(const char* name_and_id, const char* type) {
-    base::StringAppendF(&html_, "<INPUT type=\"%s\" name=\"%s\" id=\"%s\"/>",
-                        type, name_and_id, name_and_id);
-  }
-
-  // Appends a new submit-type field at the end of the form with the specified
-  // |name|.
-  void AddSubmitButton(const char* name) {
-    base::StringAppendF(
-        &html_, "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\"/>", name);
-  }
-
   // Returns the HTML code for the form containing the fields that have been
   // added so far.
   std::string ProduceHTML() const { return html_ + "</FORM>"; }
 
-  // Appends a field of |type| without name or id attribute at the end of the
-  // form.
-  void AddAnonymousInputField(const char* type) {
-    std::string type_attribute(type ? base::StringPrintf("type=\"%s\"", type)
-                                    : "");
-    base::StringAppendF(&html_, "<INPUT %s/>", type_attribute.c_str());
-  }
-
  private:
   std::string html_;
 
@@ -197,74 +89,6 @@
   ~PasswordFormConversionUtilsTest() override = default;
 
  protected:
-  // Loads the given |html|, retrieves the sole WebFormElement from it, and then
-  // calls CreatePasswordForm(), passing it the |predictions| to convert it to
-  // a PasswordForm. If |with_user_input| == true it's considered that all
-  // values in the form elements came from the user input.
-  std::unique_ptr<PasswordForm> LoadHTMLAndConvertForm(
-      const std::string& html,
-      FormsPredictionsMap* predictions,
-      bool with_user_input) {
-    WebFormElement form;
-    LoadWebFormFromHTML(html, &form, nullptr);
-
-    WebVector<WebFormControlElement> control_elements;
-    form.GetFormControlElements(control_elements);
-    FieldDataManager field_data_manager;
-    for (size_t i = 0; i < control_elements.size(); ++i) {
-      WebInputElement* input_element = ToWebInputElement(&control_elements[i]);
-      if (input_element->HasAttribute("set-activated-submit"))
-        input_element->SetActivatedSubmit(true);
-      if (with_user_input) {
-        field_data_manager.UpdateFieldDataMap(control_elements[i],
-                                              input_element->Value().Utf16(),
-                                              FieldPropertiesFlags::USER_TYPED);
-      }
-    }
-
-    return CreatePasswordFormFromWebForm(
-        form, with_user_input ? &field_data_manager : nullptr, predictions,
-        &username_detector_cache_);
-  }
-
-  // Iterates on the form generated by the |html| and adds the fields and type
-  // predictions corresponding to |predictions_positions| to |predictions|.
-  void SetPredictions(const std::string& html,
-                      FormsPredictionsMap* predictions,
-                      const std::map<int, PasswordFormFieldPredictionType>&
-                          predictions_positions) {
-    WebFormElement form;
-    LoadWebFormFromHTML(html, &form, nullptr);
-
-    FormData form_data;
-    ASSERT_TRUE(form_util::WebFormElementToFormData(
-        form, WebFormControlElement(), nullptr, form_util::EXTRACT_NONE,
-        &form_data, nullptr));
-
-    FormStructure form_structure(form_data);
-
-    int field_index = 0;
-    for (auto field = form_structure.begin(); field != form_structure.end();
-         ++field, ++field_index) {
-      if (predictions_positions.find(field_index) !=
-          predictions_positions.end()) {
-        (*predictions)[form_data][*(*field)] =
-            predictions_positions.find(field_index)->second;
-      }
-    }
-  }
-
-  void GetFirstForm(WebFormElement* form) {
-    WebLocalFrame* frame = GetMainFrame();
-    ASSERT_TRUE(frame);
-
-    WebVector<WebFormElement> forms;
-    frame->GetDocument().Forms(forms);
-    ASSERT_LE(1U, forms.size());
-
-    *form = forms[0];
-  }
-
   // Loads the given |html| and retrieves the sole WebFormElement from it.
   void LoadWebFormFromHTML(const std::string& html,
                            WebFormElement* form,
@@ -277,1799 +101,27 @@
     GetFirstForm(form);
   }
 
-  bool ExtractFormDataForFirstForm(FormData* data) {
-    WebFormElement form;
-    GetFirstForm(&form);
-    return form_util::ExtractFormData(form, data);
-  }
-
   void TearDown() override {
-    username_detector_cache_.clear();
     content::RenderViewTest::TearDown();
   }
 
-  uint32_t GetRendererIdFromWebElementId(WebString id) {
+ private:
+  void GetFirstForm(WebFormElement* form) {
     WebLocalFrame* frame = GetMainFrame();
-    if (!frame || frame->GetDocument().IsNull())
-      return FormData::kNotSetFormRendererId;
-    WebElement element = frame->GetDocument().GetElementById(id);
-    if (element.IsNull())
-      return FormData::kNotSetFormRendererId;
-    return element.To<WebInputElement>().UniqueRendererFormControlId();
+    ASSERT_TRUE(frame);
+
+    WebVector<WebFormElement> forms;
+    frame->GetDocument().Forms(forms);
+    ASSERT_LE(1U, forms.size());
+
+    *form = forms[0];
   }
 
-  UsernameDetectorCache username_detector_cache_;
-
- private:
   DISALLOW_COPY_AND_ASSIGN(PasswordFormConversionUtilsTest);
 };
 
 }  // namespace
 
-TEST_F(PasswordFormConversionUtilsTest, BasicFormAttributes) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username", "johnsmith", nullptr);
-  builder.AddSubmitButton("inactive_submit");
-  builder.AddSubmitButton("active_submit");
-  builder.AddSubmitButton("inactive_submit2");
-  builder.AddPasswordField("password", "secret", nullptr);
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ("data:", password_form->signon_realm);
-  EXPECT_EQ(GURL(kTestFormActionURL), password_form->action);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-  EXPECT_EQ(PasswordForm::Scheme::kHtml, password_form->scheme);
-  EXPECT_FALSE(password_form->preferred);
-  EXPECT_FALSE(password_form->blacklisted_by_user);
-  EXPECT_EQ(PasswordForm::Type::kManual, password_form->type);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username", "johnsmith", nullptr);
-  builder.AddDisabledUsernameField();
-  builder.AddDisabledPasswordField();
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-}
-
-// When not enough fields are enabled to parse the form, the result should still
-// be not null. It must contain only minimal information, so that it is not used
-// for fill on load, for example. It must contain the full FormData, so that the
-// new parser can be run as well.
-TEST_F(PasswordFormConversionUtilsTest, OnlyDisabledFields) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddDisabledUsernameField();
-  builder.AddDisabledPasswordField();
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_TRUE(password_form->username_element.empty());
-  EXPECT_TRUE(password_form->password_element.empty());
-  EXPECT_TRUE(password_form->new_password_element.empty());
-  EXPECT_EQ(2u, password_form->form_data.fields.size());
-}
-
-TEST_F(PasswordFormConversionUtilsTest, HTMLDetector_DeveloperGroupAttributes) {
-  // Each test case consists of a set of parameters to be plugged into the
-  // PasswordFormBuilder below, plus the corresponding expectations.
-  // The test data contains cases that are identified by HTML detector, and not
-  // by base heuristic. Thus, username field does not necessarely have to be
-  // right before password field.
-  // These tests basically check searching in developer group (i.e. name and id
-  // attribute, concatenated, with "$" guard in between).
-  struct TestCase {
-    // Field parameters represent, in order of appearance, field name, field id
-    // and field value.
-    const char* first_text_field_parameters[3];
-    const char* second_text_field_parameters[3];
-    const char* expected_username_element;
-    const WebString expected_username_id;
-    const char* expected_username_value;
-  } cases[] = {
-      // There are both field name and id.
-      {{"username", "x1d", "johnsmith"},
-       {"email", "y1d", "js@google.com"},
-       "username",
-       "x1d",
-       "johnsmith"},
-      // there is no field id.
-      {{"username", "x1d", "johnsmith"},
-       {"email", "y1d", "js@google.com"},
-       "username",
-       "x1d",
-       "johnsmith"},
-      // Upper or mixed case shouldn't matter.
-      {{"uSeRnAmE", "x1d", "johnsmith"},
-       {"email", "y1d", "js@google.com"},
-       "uSeRnAmE",
-       "x1d",
-       "johnsmith"},
-      // Check removal of special characters.
-      {{"u1_s2-e3~r4/n5(a)6m#e", "x1d", "johnsmith"},
-       {"email", "y1d", "js@google.com"},
-       "u1_s2-e3~r4/n5(a)6m#e",
-       "x1d",
-       "johnsmith"},
-      // Check guard between field name and field id.
-      {{"us", "ername", "johnsmith"},
-       {"email", "id", "js@google.com"},
-       "email",
-       "id",
-       "js@google.com"},
-      // Check removal of fields with latin negative words in developer group.
-      {{"email", "x", "js@google.com"},
-       {"fake_username", "y", "johnsmith"},
-       "email",
-       "x",
-       "js@google.com"},
-      {{"email", "mail", "js@google.com"},
-       {"user_name", "fullname", "johnsmith"},
-       "email",
-       "mail",
-       "js@google.com"},
-      // Identify latin translations of "username".
-      {{"benutzername", "x", "johnsmith"},
-       {"email", "y", "js@google.com"},
-       "benutzername",
-       "x",
-       "johnsmith"},
-      // Identify latin translations of "user".
-      {{"utilizator", "x1d", "johnsmith"},
-       {"email", "y1d", "js@google.com"},
-       "utilizator",
-       "x1d",
-       "johnsmith"},
-      // Identify technical words.
-      {{"loginid", "x1d", "johnsmith"},
-       {"email", "y1d", "js@google.com"},
-       "loginid",
-       "x1d",
-       "johnsmith"},
-      // Identify weak words.
-      {{"usrname", "x1d", "johnsmith"},
-       {"email", "y1d", "js@google.com"},
-       "email",
-       "y1d",
-       "js@google.com"},
-      // If a word matches in maximum 2 fields, it is accepted.
-      // First encounter is selected as username.
-      {{"username", "x1d", "johnsmith"},
-       {"repeat_username", "y1d", "johnsmith"},
-       "username",
-       "x1d",
-       "johnsmith"},
-      // A short word should be enclosed between delimiters. Otherwise, an
-      // Occurrence doesn't count.
-      {{"identity_name", "idn", "johnsmith"},
-       {"id", "xid", "123"},
-       "id",
-       "xid",
-       "123"}};
-
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Iteration " << i);
-
-    PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextFieldWithoutAutocomplete(
-        cases[i].first_text_field_parameters[0],
-        cases[i].first_text_field_parameters[1],
-        cases[i].first_text_field_parameters[2], "", "");
-    builder.AddTextFieldWithoutAutocomplete(
-        cases[i].second_text_field_parameters[0],
-        cases[i].second_text_field_parameters[1],
-        cases[i].second_text_field_parameters[2], "", "");
-    builder.AddPasswordField("password", "secret", nullptr);
-    builder.AddSubmitButton("submit");
-    std::string html = builder.ProduceHTML();
-
-    username_detector_cache_.clear();
-
-    std::unique_ptr<PasswordForm> password_form =
-        LoadHTMLAndConvertForm(html, nullptr, false);
-
-    uint32_t username_renderer_id =
-        GetRendererIdFromWebElementId(cases[i].expected_username_id);
-
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
-              password_form->username_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
-              password_form->username_value);
-    // Check that the username field was found by HTML detector.
-    ASSERT_EQ(1u, username_detector_cache_.size());
-    ASSERT_FALSE(username_detector_cache_.begin()->second.empty());
-    EXPECT_EQ(username_renderer_id,
-              username_detector_cache_.begin()->second[0]);
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest, HTMLDetector_SeveralDetections) {
-  // If word matches in more than 2 fields, we don't match on it.
-  // We search for match with another word.
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextFieldWithoutAutocomplete("address", "xuser", "someaddress", "",
-                                          "");
-  builder.AddTextFieldWithoutAutocomplete("loginid", "yuser", "johnsmith", "",
-                                          "");
-  builder.AddTextFieldWithoutAutocomplete("tel", "zuser", "sometel", "", "");
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  DCHECK(username_detector_cache_.empty());
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-
-  uint32_t username_renderer_id = GetRendererIdFromWebElementId("yuser");
-
-  ASSERT_TRUE(password_form);
-
-  EXPECT_EQ(base::UTF8ToUTF16("loginid"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
-  // Check that the username field was found by HTML detector.
-  ASSERT_EQ(1u, username_detector_cache_.size());
-  ASSERT_EQ(1u, username_detector_cache_.begin()->second.size());
-  EXPECT_EQ(username_renderer_id, username_detector_cache_.begin()->second[0]);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, HTMLDetector_UserGroupAttributes) {
-  // Each test case consists of a set of parameters to be plugged into the
-  // PasswordFormBuilder below, plus the corresponding expectations.
-  // The test data contains cases that are identified by HTML detector, and not
-  // by base heuristic. Thus, username field does not necessarely have to be
-  // right before password field.
-  // These tests basically check searching in user group.
-  struct TestCase {
-    // Field parameters represent, in order of appearance, field name, field
-    // id, field value and field label or placeholder.
-    // Field name and field id don't contain any significant information.
-    const char* first_text_field_parameters[4];
-    const char* second_text_field_parameters[4];
-    const char* expected_username_element;
-    const WebString expected_username_id;
-    const char* expected_username_value;
-  } cases[] = {
-      // Label information will decide username.
-      {{"name1", "id1", "johnsmith", "Username:"},
-       {"name2", "id2", "js@google.com", "Email:"},
-       "name1",
-       "id1",
-       "johnsmith"},
-      // Placeholder information will decide username.
-      {{"name1", "id1", "js@google.com", "Email:"},
-       {"name2", "id2", "johnsmith", "Username:"},
-       "name2",
-       "id2",
-       "johnsmith"},
-      // Check removal of special characters.
-      {{"name1", "id1", "johnsmith", "U s er n a m e:"},
-       {"name2", "id2", "js@google.com", "Email:"},
-       "name1",
-       "id1",
-       "johnsmith"},
-      // Check removal of fields with latin negative words in user group.
-      {{"name1", "id1", "johnsmith", "Username password:"},
-       {"name2", "id2", "js@google.com", "Email:"},
-       "name2",
-       "id2",
-       "js@google.com"},
-      // Check removal of fields with non-latin negative words in user group.
-      {{"name1", "id1", "js@google.com", "Email:"},
-       {"name2", "id2", "johnsmith", "የይለፍቃልየይለፍቃል:"},
-       "name1",
-       "id1",
-       "js@google.com"},
-      // Identify latin translations of "username".
-      {{"name1", "id1", "johnsmith", "Username:"},
-       {"name2", "id2", "js@google.com", "Email:"},
-       "name1",
-       "id1",
-       "johnsmith"},
-      // Identify non-latin translations of "username".
-      {{"name1", "id1", "johnsmith", "用户名:"},
-       {"name2", "id2", "js@google.com", "Email:"},
-       "name1",
-       "id1",
-       "johnsmith"},
-      // Identify latin translations of "user".
-      {{"name1", "id1", "johnsmith", "Wosuta:"},
-       {"name2", "id2", "js@google.com", "Email:"},
-       "name1",
-       "id1",
-       "johnsmith"},
-      // Identify non-latin translations of "user".
-      {{"name1", "id1", "johnsmith", "истифода:"},
-       {"name2", "id2", "js@google.com", "Email:"},
-       "name1",
-       "id1",
-       "johnsmith"},
-      // Identify weak words.
-      {{"name1", "id1", "johnsmith", "Insert your login details:"},
-       {"name2", "id2", "js@google.com", "Insert your email:"},
-       "name1",
-       "id1",
-       "johnsmith"},
-      // Check user group priority, compared to developer group.
-      // User group should have higher priority than developer group.
-      {{"email", "id1", "js@google.com", "Username:"},
-       {"username", "id2", "johnsmith", "Email:"},
-       "email",
-       "id1",
-       "js@google.com"},
-      // Check treatment for short dictionary words. "uid" has higher priority,
-      // but its occurrence is ignored because it is a part of another word.
-      {{"name1", "noword", "johnsmith", "Insert your id:"},
-       {"name2", "uidentical", "js@google.com", "Insert something:"},
-       "name1",
-       "noword",
-       "johnsmith"}};
-
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Iteration " << i);
-
-    PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextFieldWithoutAutocomplete(
-        cases[i].first_text_field_parameters[0],
-        cases[i].first_text_field_parameters[1],
-        cases[i].first_text_field_parameters[2],
-        cases[i].first_text_field_parameters[3], "");
-    builder.AddTextFieldWithoutAutocomplete(
-        cases[i].second_text_field_parameters[0],
-        cases[i].second_text_field_parameters[1],
-        cases[i].second_text_field_parameters[2], "",
-        cases[i].second_text_field_parameters[3]);
-    builder.AddPasswordField("password", "secret", nullptr);
-    builder.AddSubmitButton("submit");
-    std::string html = builder.ProduceHTML();
-
-    username_detector_cache_.clear();
-    std::unique_ptr<PasswordForm> password_form =
-        LoadHTMLAndConvertForm(html, nullptr, false);
-
-    uint32_t username_renderer_id =
-        GetRendererIdFromWebElementId(cases[i].expected_username_id);
-
-    ASSERT_TRUE(password_form);
-
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
-              password_form->username_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
-              password_form->username_value);
-    // Check that the username field was found by HTML detector.
-    ASSERT_EQ(1u, username_detector_cache_.size());
-    ASSERT_FALSE(username_detector_cache_.begin()->second.empty());
-    EXPECT_EQ(username_renderer_id,
-              username_detector_cache_.begin()->second[0]);
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest, HTMLDetectorCache) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("unknown", "12345", nullptr);
-  builder.AddTextField("something", "smith", nullptr);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-  UsernameDetectorCache detector_cache;
-
-  // No signals from HTML attributes. The classifier found nothing and cached
-  // it.
-  base::HistogramTester histogram_tester;
-  std::unique_ptr<PasswordForm> password_form =
-      CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache);
-
-  EXPECT_TRUE(password_form);
-  ASSERT_EQ(1u, detector_cache.size());
-  EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first);
-  EXPECT_TRUE(detector_cache.begin()->second.empty());
-  histogram_tester.ExpectUniqueSample("PasswordManager.UsernameDetectionMethod",
-                                      UsernameDetectionMethod::BASE_HEURISTIC,
-                                      1);
-
-  // Changing attributes would change the classifier's output. But the output
-  // will be the same because it was cached in |username_detector_cache|.
-  WebVector<WebFormControlElement> control_elements;
-  form.GetFormControlElements(control_elements);
-  control_elements[0].SetAttribute("name", "id");
-  password_form =
-      CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache);
-  EXPECT_TRUE(password_form);
-  ASSERT_EQ(1u, detector_cache.size());
-  EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first);
-  EXPECT_TRUE(detector_cache.begin()->second.empty());
-  histogram_tester.ExpectUniqueSample("PasswordManager.UsernameDetectionMethod",
-                                      UsernameDetectionMethod::BASE_HEURISTIC,
-                                      2);
-
-  // Clear the cache. The classifier will find username field and cache it.
-  detector_cache.clear();
-  ASSERT_EQ(4u, control_elements.size());
-  password_form =
-      CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache);
-  EXPECT_TRUE(password_form);
-  ASSERT_EQ(1u, detector_cache.size());
-  EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first);
-  ASSERT_EQ(1u, detector_cache.begin()->second.size());
-  EXPECT_EQ(control_elements[0].UniqueRendererFormControlId(),
-            detector_cache.begin()->second[0]);
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("PasswordManager.UsernameDetectionMethod"),
-      testing::UnorderedElementsAre(
-          base::Bucket(UsernameDetectionMethod::BASE_HEURISTIC, 2),
-          base::Bucket(UsernameDetectionMethod::HTML_BASED_CLASSIFIER, 1)));
-
-  // Change the attributes again ("username" is stronger signal than "login"),
-  // but keep the cache. The classifier's output should be the same.
-  control_elements[1].SetAttribute("name", "username");
-  password_form =
-      CreatePasswordFormFromWebForm(form, nullptr, nullptr, &detector_cache);
-  EXPECT_TRUE(password_form);
-  ASSERT_EQ(1u, detector_cache.size());
-  EXPECT_EQ(form.UniqueRendererFormId(), detector_cache.begin()->first);
-  ASSERT_EQ(1u, detector_cache.begin()->second.size());
-  EXPECT_EQ(control_elements[0].UniqueRendererFormControlId(),
-            detector_cache.begin()->second[0]);
-  EXPECT_THAT(
-      histogram_tester.GetAllSamples("PasswordManager.UsernameDetectionMethod"),
-      testing::UnorderedElementsAre(
-          base::Bucket(UsernameDetectionMethod::BASE_HEURISTIC, 2),
-          base::Bucket(UsernameDetectionMethod::HTML_BASED_CLASSIFIER, 2)));
-}
-
-TEST_F(PasswordFormConversionUtilsTest, HTMLDetectorCache_SkipSomePredictions) {
-  // The cache of HTML based username detector may contain several predictions
-  // (in the order of decreasing reliability) for the given form, but the
-  // detector should consider only |possible_usernames| passed to
-  // GetUsernameFieldBasedOnHtmlAttributes. For example, if a field has no user
-  // input while others has, the field cannot be an username field.
-
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username", "12345", nullptr);
-  builder.AddTextField("email", "smith@google.com", nullptr);
-  builder.AddTextField("id", "12345", nullptr);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-  WebVector<WebFormControlElement> control_elements;
-  form.GetFormControlElements(control_elements);
-  ASSERT_FALSE(control_elements.empty());
-
-  // Add predictions for "email" and "id" fields to the cache.
-  UsernameDetectorCache username_detector_cache;
-  username_detector_cache[control_elements[0].Form().UniqueRendererFormId()] = {
-      ToWebInputElement(&control_elements[1])
-          ->UniqueRendererFormControlId(),  // email
-      ToWebInputElement(&control_elements[2])
-          ->UniqueRendererFormControlId()};  // id
-
-  // A user typed only into "id" and "password" fields. So, the prediction for
-  // "email" field should be ignored despite it is more reliable than prediction
-  // for "id" field.
-  FieldDataManager field_data_manager;
-  field_data_manager.UpdateFieldDataMap(
-      control_elements[2], control_elements[2].Value().Utf16(),
-      FieldPropertiesFlags::USER_TYPED);  // id
-  field_data_manager.UpdateFieldDataMap(
-      control_elements[3], control_elements[3].Value().Utf16(),
-      FieldPropertiesFlags::USER_TYPED);  // password
-
-  std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm(
-      form, &field_data_manager, nullptr, &username_detector_cache);
-
-  ASSERT_TRUE(password_form);
-  EXPECT_EQ(base::UTF8ToUTF16("id"), password_form->username_element);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       IdentifyingUsernameFieldsWithBaseHeuristic) {
-  // Each test case consists of a set of parameters to be plugged into the
-  // PasswordFormBuilder below, plus the corresponding expectations.
-  // The test data should not contain field names that are identified by the
-  // HTML based username detector, because with these tests only the base
-  // heuristic (i.e. select as username the field before the password field)
-  // is tested.
-  struct TestCase {
-    const char* autocomplete[3];
-    const char* expected_username_element;
-    const char* expected_username_value;
-    const char* expected_all_possible_usernames;
-  } cases[] = {
-      // When no elements are marked with autocomplete='username', the text-type
-      // input field before the first password element should get selected as
-      // the username, and the rest should be marked as alternatives.
-      {{nullptr, nullptr, nullptr},
-       "usrname2",
-       "William",
-       "John+usrname1, Smith+usrname3"},
-      // When a sole element is marked with autocomplete='username', it should
-      // be treated as the username, but other text fields should be added to
-      // |all_possible_usernames|.
-      {{"username", nullptr, nullptr},
-       "usrname1",
-       "John",
-       "William+usrname2, Smith+usrname3"},
-      {{nullptr, "username", nullptr},
-       "usrname2",
-       "William",
-       "John+usrname1, Smith+usrname3"},
-      {{nullptr, nullptr, "username"},
-       "usrname3",
-       "Smith",
-       "John+usrname1, William+usrname2"},
-      // When >=2 elements have the attribute, the first should be selected as
-      // the username, and the rest should go to |all_possible_usernames|.
-      {{"username", "username", nullptr},
-       "usrname1",
-       "John",
-       "William+usrname2, Smith+usrname3"},
-      {{nullptr, "username", "username"},
-       "usrname2",
-       "William",
-       "John+usrname1, Smith+usrname3"},
-      {{"username", nullptr, "username"},
-       "usrname1",
-       "John",
-       "William+usrname2, Smith+usrname3"},
-      {{"username", "username", "username"},
-       "usrname1",
-       "John",
-       "William+usrname2, Smith+usrname3"},
-      // When there is an empty autocomplete attribute (i.e. autocomplete=""),
-      // it should have the same effect as having no attribute whatsoever.
-      {{"", "", ""}, "usrname2", "William", "John+usrname1, Smith+usrname3"},
-      {{"", "", "username"},
-       "usrname3",
-       "Smith",
-       "John+usrname1, William+usrname2"},
-      {{"username", "", "username"},
-       "usrname1",
-       "John",
-       "William+usrname2, Smith+usrname3"},
-      // It should not matter if attribute values are upper or mixed case.
-      {{"USERNAME", nullptr, "uSeRNaMe"},
-       "usrname1",
-       "John",
-       "William+usrname2, Smith+usrname3"},
-      {{"uSeRNaMe", nullptr, "USERNAME"},
-       "usrname1",
-       "John",
-       "William+usrname2, Smith+usrname3"}};
-
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    for (size_t nonempty_username_fields = 0; nonempty_username_fields < 2;
-         ++nonempty_username_fields) {
-      SCOPED_TRACE(testing::Message()
-                   << "Iteration " << i << " "
-                   << (nonempty_username_fields ? "nonempty" : "empty"));
-
-      // Repeat each test once with empty, and once with non-empty usernames.
-      // In the former case, no empty all_possible_usernames should be saved.
-      const char* names[3];
-      if (nonempty_username_fields) {
-        names[0] = "John";
-        names[1] = "William";
-        names[2] = "Smith";
-      } else {
-        names[0] = names[1] = names[2] = "";
-      }
-
-      PasswordFormBuilder builder(kTestFormActionURL);
-      builder.AddTextField("usrname1", names[0], cases[i].autocomplete[0]);
-      builder.AddTextField("usrname2", names[1], cases[i].autocomplete[1]);
-      builder.AddPasswordField("password", "secret", nullptr);
-      builder.AddTextField("usrname3", names[2], cases[i].autocomplete[2]);
-      builder.AddPasswordField("password2", "othersecret", nullptr);
-      builder.AddSubmitButton("submit");
-      std::string html = builder.ProduceHTML();
-
-      std::unique_ptr<PasswordForm> password_form =
-          LoadHTMLAndConvertForm(html, nullptr, false);
-      ASSERT_TRUE(password_form);
-
-      EXPECT_FALSE(password_form->only_for_fallback);
-      EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
-                password_form->username_element);
-
-      if (nonempty_username_fields) {
-        EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
-                  password_form->username_value);
-        EXPECT_EQ(
-            base::UTF8ToUTF16(cases[i].expected_all_possible_usernames),
-            ValueElementVectorToString(password_form->all_possible_usernames));
-      } else {
-        EXPECT_TRUE(password_form->username_value.empty());
-        EXPECT_TRUE(password_form->all_possible_usernames.empty());
-      }
-
-      // Do a basic sanity check that we are still having a password field.
-      EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-      EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-    }
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) {
-  // Each test case consists of a set of parameters to be plugged into the
-  // PasswordFormBuilder below, plus the corresponding expectations.
-  struct TestCase {
-    const char* password_values[2];
-    const char* expected_password_element;
-    const char* expected_password_value;
-    const char* expected_new_password_element;
-    const char* expected_new_password_value;
-    const char* expected_confirmation_element;
-  } cases[] = {
-      // Two non-empty fields with the same value should be treated as a new
-      // password field plus a confirmation field for the new password.
-      {{"alpha", "alpha"}, "", "", "password1", "alpha", "password2"},
-      // The same goes if the fields are yet empty: we speculate that we will
-      // identify them as new password fields once they are filled out, and we
-      // want to keep our abstract interpretation of the form less flaky.
-      {{"", ""}, "password1", "", "password2", "", ""},
-      // Two different values should be treated as a password change form, one
-      // that also asks for the current password, but only once for the new.
-      {{"alpha", ""}, "password1", "alpha", "password2", "", ""},
-      {{"", "beta"}, "password1", "", "password2", "beta", ""},
-      {{"alpha", "beta"}, "password1", "alpha", "password2", "beta", ""}};
-
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Iteration " << i);
-
-    PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextField("usrname1", "William", nullptr);
-    builder.AddPasswordField("password1", cases[i].password_values[0], nullptr);
-    builder.AddTextField("usrname2", "Smith", nullptr);
-    builder.AddPasswordField("password2", cases[i].password_values[1], nullptr);
-    builder.AddSubmitButton("submit");
-    std::string html = builder.ProduceHTML();
-
-    std::unique_ptr<PasswordForm> password_form =
-        LoadHTMLAndConvertForm(html, nullptr, false);
-    ASSERT_TRUE(password_form);
-
-    EXPECT_FALSE(password_form->only_for_fallback);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
-              password_form->password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
-              password_form->password_value);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
-              password_form->new_password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
-              password_form->new_password_value);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_confirmation_element),
-              password_form->confirmation_password_element);
-
-    // Do a basic sanity check that we are still selecting the right username.
-    EXPECT_EQ(base::UTF8ToUTF16("usrname1"), password_form->username_element);
-    EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
-    EXPECT_THAT(
-        password_form->all_possible_usernames,
-        testing::ElementsAre(ValueElementPair(base::UTF8ToUTF16("Smith"),
-                                              base::UTF8ToUTF16("usrname2"))));
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) {
-  // Each test case consists of a set of parameters to be plugged into the
-  // PasswordFormBuilder below, plus the corresponding expectations.
-  struct TestCase {
-    const char* password_values[3];
-    const char* expected_password_element;
-    const char* expected_password_value;
-    const char* expected_new_password_element;
-    const char* expected_new_password_value;
-    const char* expected_confirmation_element;
-  } cases[] = {
-      // Two fields with the same value, and one different: we should treat this
-      // as a password change form with confirmation for the new password. Note
-      // that we only recognize (current + new + new) and (new + new + current)
-      // without autocomplete attributes.
-      {{"alpha", "", ""}, "password1", "alpha", "password2", "", "password3"},
-      {{"", "beta", "beta"}, "password1", "", "password2", "beta", "password3"},
-      {{"alpha", "beta", "beta"},
-       "password1",
-       "alpha",
-       "password2",
-       "beta",
-       "password3"},
-      // If confirmed password comes first, assume that the third password
-      // field is related to security question, SSN, or credit card and ignore
-      // it.
-      {{"beta", "beta", "alpha"}, "", "", "password1", "beta", "password2"},
-      // If the fields are yet empty, we speculate that we will identify them as
-      // (current + new + new) once they are filled out, so we should classify
-      // them the same for now to keep our abstract interpretation less flaky.
-      {{"", "", ""}, "password1", "", "password2", "", "password3"}};
-
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Iteration " << i);
-
-    PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextField("usrname1", "William", nullptr);
-    builder.AddPasswordField("password1", cases[i].password_values[0], nullptr);
-    builder.AddPasswordField("password2", cases[i].password_values[1], nullptr);
-    builder.AddTextField("usrname2", "Smith", nullptr);
-    builder.AddPasswordField("password3", cases[i].password_values[2], nullptr);
-    builder.AddSubmitButton("submit");
-    std::string html = builder.ProduceHTML();
-
-    std::unique_ptr<PasswordForm> password_form =
-        LoadHTMLAndConvertForm(html, nullptr, false);
-    ASSERT_TRUE(password_form);
-
-    EXPECT_FALSE(password_form->only_for_fallback);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
-              password_form->password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
-              password_form->password_value);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
-              password_form->new_password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
-              password_form->new_password_value);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_confirmation_element),
-              password_form->confirmation_password_element);
-
-    // Do a basic sanity check that we are still selecting the right username.
-    EXPECT_EQ(base::UTF8ToUTF16("usrname1"), password_form->username_element);
-    EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
-    EXPECT_THAT(
-        password_form->all_possible_usernames,
-        testing::ElementsAre(ValueElementPair(base::UTF8ToUTF16("Smith"),
-                                              base::UTF8ToUTF16("usrname2"))));
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       IdentifyingPasswordFieldsWithAutocompleteAttributes) {
-  // Each test case consists of a set of parameters to be plugged into the
-  // PasswordFormBuilder below, plus the corresponding expectations.
-  // The test data should not contain field names that are identified by the
-  // HTML based username detector, because with these tests only the base
-  // heuristic (i.e. select as username the field before the password field)
-  // is tested.
-  struct TestCase {
-    const char* autocomplete[3];
-    const char* expected_password_element;
-    const char* expected_password_value;
-    const char* expected_new_password_element;
-    const char* expected_new_password_value;
-    bool expected_new_password_marked_by_site;
-    const char* expected_username_element;
-    const char* expected_username_value;
-  } cases[] = {
-      // When there are elements marked with autocomplete='current-password',
-      // but no elements with 'new-password', we should treat the first of the
-      // former kind as the current password, and ignore all other password
-      // fields, assuming they are not intentionally not marked. They might be
-      // for other purposes, such as PINs, OTPs, and the like. Actual values in
-      // the password fields should be ignored in all cases below.
-      // Username is the element just before the first 'current-password' (even
-      // if 'new-password' comes earlier). If no 'current-password', then the
-      // element just before the first 'new-passwords'.
-      {{"current-password", nullptr, nullptr},
-       "password1",
-       "alpha",
-       "",
-       "",
-       false,
-       "usrname1",
-       "William"},
-      {{nullptr, "current-password", nullptr},
-       "password2",
-       "beta",
-       "",
-       "",
-       false,
-       "usrname2",
-       "Smith"},
-      {{nullptr, nullptr, "current-password"},
-       "password3",
-       "gamma",
-       "",
-       "",
-       false,
-       "usrname2",
-       "Smith"},
-      {{nullptr, "current-password", "current-password"},
-       "password2",
-       "beta",
-       "",
-       "",
-       false,
-       "usrname2",
-       "Smith"},
-      {{"current-password", nullptr, "current-password"},
-       "password1",
-       "alpha",
-       "",
-       "",
-       false,
-       "usrname1",
-       "William"},
-      {{"current-password", "current-password", nullptr},
-       "password1",
-       "alpha",
-       "",
-       "",
-       false,
-       "usrname1",
-       "William"},
-      {{"current-password", "current-password", "current-password"},
-       "password1",
-       "alpha",
-       "",
-       "",
-       false,
-       "usrname1",
-       "William"},
-      // The same goes vice versa for autocomplete='new-password'.
-      {{"new-password", nullptr, nullptr},
-       "",
-       "",
-       "password1",
-       "alpha",
-       true,
-       "usrname1",
-       "William"},
-      {{nullptr, "new-password", nullptr},
-       "",
-       "",
-       "password2",
-       "beta",
-       true,
-       "usrname2",
-       "Smith"},
-      {{nullptr, nullptr, "new-password"},
-       "",
-       "",
-       "password3",
-       "gamma",
-       true,
-       "usrname2",
-       "Smith"},
-      {{nullptr, "new-password", "new-password"},
-       "",
-       "",
-       "password2",
-       "beta",
-       true,
-       "usrname2",
-       "Smith"},
-      {{"new-password", nullptr, "new-password"},
-       "",
-       "",
-       "password1",
-       "alpha",
-       true,
-       "usrname1",
-       "William"},
-      {{"new-password", "new-password", nullptr},
-       "",
-       "",
-       "password1",
-       "alpha",
-       true,
-       "usrname1",
-       "William"},
-      {{"new-password", "new-password", "new-password"},
-       "",
-       "",
-       "password1",
-       "alpha",
-       true,
-       "usrname1",
-       "William"},
-      // When there is one element marked with autocomplete='current-password',
-      // and one with 'new-password', just comply. Ignore the unmarked password
-      // field(s) for the same reason as above.
-      {{"current-password", "new-password", nullptr},
-       "password1",
-       "alpha",
-       "password2",
-       "beta",
-       true,
-       "usrname1",
-       "William"},
-      {{"current-password", nullptr, "new-password"},
-       "password1",
-       "alpha",
-       "password3",
-       "gamma",
-       true,
-       "usrname1",
-       "William"},
-      {{nullptr, "current-password", "new-password"},
-       "password2",
-       "beta",
-       "password3",
-       "gamma",
-       true,
-       "usrname2",
-       "Smith"},
-      {{"new-password", "current-password", nullptr},
-       "password2",
-       "beta",
-       "password1",
-       "alpha",
-       true,
-       "usrname2",
-       "Smith"},
-      {{"new-password", nullptr, "current-password"},
-       "password3",
-       "gamma",
-       "password1",
-       "alpha",
-       true,
-       "usrname2",
-       "Smith"},
-      {{nullptr, "new-password", "current-password"},
-       "password3",
-       "gamma",
-       "password2",
-       "beta",
-       true,
-       "usrname2",
-       "Smith"},
-      // In case of duplicated elements of either kind, go with the first one of
-      // its kind.
-      {{"current-password", "current-password", "new-password"},
-       "password1",
-       "alpha",
-       "password3",
-       "gamma",
-       true,
-       "usrname1",
-       "William"},
-      {{"current-password", "new-password", "current-password"},
-       "password1",
-       "alpha",
-       "password2",
-       "beta",
-       true,
-       "usrname1",
-       "William"},
-      {{"new-password", "current-password", "current-password"},
-       "password2",
-       "beta",
-       "password1",
-       "alpha",
-       true,
-       "usrname2",
-       "Smith"},
-      {{"current-password", "new-password", "new-password"},
-       "password1",
-       "alpha",
-       "password2",
-       "beta",
-       true,
-       "usrname1",
-       "William"},
-      {{"new-password", "current-password", "new-password"},
-       "password2",
-       "beta",
-       "password1",
-       "alpha",
-       true,
-       "usrname2",
-       "Smith"},
-      {{"new-password", "new-password", "current-password"},
-       "password3",
-       "gamma",
-       "password1",
-       "alpha",
-       true,
-       "usrname2",
-       "Smith"},
-      // When there is an empty autocomplete attribute (i.e. autocomplete=""),
-      // it should have the same effect as having no attribute whatsoever.
-      {{"current-password", "", ""},
-       "password1",
-       "alpha",
-       "",
-       "",
-       false,
-       "usrname1",
-       "William"},
-      {{"", "", "new-password"},
-       "",
-       "",
-       "password3",
-       "gamma",
-       true,
-       "usrname2",
-       "Smith"},
-      {{"", "new-password", ""},
-       "",
-       "",
-       "password2",
-       "beta",
-       true,
-       "usrname2",
-       "Smith"},
-      {{"", "current-password", "current-password"},
-       "password2",
-       "beta",
-       "",
-       "",
-       false,
-       "usrname2",
-       "Smith"},
-      {{"new-password", "", "new-password"},
-       "",
-       "",
-       "password1",
-       "alpha",
-       true,
-       "usrname1",
-       "William"},
-      {{"new-password", "", "current-password"},
-       "password3",
-       "gamma",
-       "password1",
-       "alpha",
-       true,
-       "usrname2",
-       "Smith"},
-      // It should not matter if attribute values are upper or mixed case.
-      {{nullptr, "current-password", nullptr},
-       "password2",
-       "beta",
-       "",
-       "",
-       false,
-       "usrname2",
-       "Smith"},
-      {{nullptr, "CURRENT-PASSWORD", nullptr},
-       "password2",
-       "beta",
-       "",
-       "",
-       false,
-       "usrname2",
-       "Smith"},
-      {{nullptr, "new-password", nullptr},
-       "",
-       "",
-       "password2",
-       "beta",
-       true,
-       "usrname2",
-       "Smith"},
-      {{nullptr, "nEw-PaSsWoRd", nullptr},
-       "",
-       "",
-       "password2",
-       "beta",
-       true,
-       "usrname2",
-       "Smith"}};
-
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Iteration " << i);
-
-    PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddPasswordField("pin1", "123456", nullptr);
-    builder.AddPasswordField("pin2", "789101", nullptr);
-    builder.AddTextField("usrname1", "William", nullptr);
-    builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]);
-    builder.AddTextField("usrname2", "Smith", nullptr);
-    builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]);
-    builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]);
-    builder.AddSubmitButton("submit");
-    std::string html = builder.ProduceHTML();
-
-    std::unique_ptr<PasswordForm> password_form =
-        LoadHTMLAndConvertForm(html, nullptr, false);
-    ASSERT_TRUE(password_form);
-
-    EXPECT_FALSE(password_form->only_for_fallback);
-    // In the absence of username autocomplete attributes, the username should
-    // be the text input field just before 'current-password' or before
-    // 'new-password', if there is no 'current-password'.
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
-              password_form->username_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
-              password_form->username_value);
-    if (strcmp(cases[i].expected_username_value, "William") == 0) {
-      EXPECT_THAT(
-          password_form->all_possible_usernames,
-          testing::ElementsAre(ValueElementPair(
-              base::UTF8ToUTF16("Smith"), base::UTF8ToUTF16("usrname2"))));
-    } else {
-      EXPECT_THAT(
-          password_form->all_possible_usernames,
-          testing::ElementsAre(ValueElementPair(
-              base::UTF8ToUTF16("William"), base::UTF8ToUTF16("usrname1"))));
-    }
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
-              password_form->password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
-              password_form->password_value);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
-              password_form->new_password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
-              password_form->new_password_value);
-    EXPECT_EQ(cases[i].expected_new_password_marked_by_site,
-              password_form->new_password_marked_by_site);
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       UsernameDetection_AutocompleteAttribute) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username", "JohnSmith", "username");
-  builder.AddTextField("Full name", "John A. Smith", nullptr);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  base::HistogramTester histogram_tester;
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("JohnSmith"), password_form->username_value);
-  histogram_tester.ExpectUniqueSample(
-      "PasswordManager.UsernameDetectionMethod",
-      UsernameDetectionMethod::AUTOCOMPLETE_ATTRIBUTE, 1);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, IgnoreInvisibledTextFields) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddNonDisplayedTextField("nondisplayed1", "nodispalyed_value1");
-  builder.AddNonVisibleTextField("nonvisible1", "nonvisible_value1");
-  builder.AddTextField("username", "johnsmith", nullptr);
-  builder.AddNonDisplayedTextField("nondisplayed2", "nodispalyed_value2");
-  builder.AddNonVisiblePasswordField("nonvisible2", "nonvisible_value2");
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->new_password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->new_password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, IgnoreInvisiblLoginPairs) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddNonDisplayedTextField("nondisplayed1", "nodispalyed_value1");
-  builder.AddNonDisplayedPasswordField("nondisplayed2", "nodispalyed_value2");
-  builder.AddNonVisibleTextField("nonvisible1", "nonvisible_value1");
-  builder.AddNonVisiblePasswordField("nonvisible2", "nonvisible_value2");
-  builder.AddTextField("username", "johnsmith", nullptr);
-  builder.AddNonVisibleTextField("nonvisible3", "nonvisible_value3");
-  builder.AddNonVisiblePasswordField("nonvisible4", "nonvisible_value4");
-  builder.AddNonDisplayedTextField("nondisplayed3", "nodispalyed_value3");
-  builder.AddNonDisplayedPasswordField("nondisplayed4", "nodispalyed_value4");
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->new_password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->new_password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, OnlyNonDisplayedLoginPair) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddNonDisplayedTextField("username", "William");
-  builder.AddNonDisplayedPasswordField("password", "secret");
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"),
-            password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("William"),
-            password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"),
-            password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"),
-            password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, OnlyNonVisibleLoginPair) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddNonVisibleTextField("username", "William");
-  builder.AddNonVisiblePasswordField("password", "secret");
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, VisiblePasswordAndInvisibleUsername) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddNonDisplayedTextField("username", "William");
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       InvisiblePassword_LatestUsernameIsVisible) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddNonDisplayedTextField("search", "query");
-  builder.AddTextField("username", "William", nullptr);
-  builder.AddNonDisplayedPasswordField("password", "secret");
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       InvisiblePassword_LatestUsernameIsInvisible) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddTextField("search", "query", nullptr);
-  builder.AddNonDisplayedTextField("username", "William");
-  builder.AddNonDisplayedPasswordField("password", "secret");
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-}
-
-// Checks that username/password fields are detected based on user input even if
-// visibility heuristics disagree.
-TEST_F(PasswordFormConversionUtilsTest, UserInput) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-
-  builder.AddNonVisibleTextField("nonvisible_text", "actual_username");
-  builder.AddTextField("visible_text", "fake_username", nullptr);
-
-  builder.AddNonVisiblePasswordField("nonvisible_password", "actual_password");
-  builder.AddPasswordField("visible_password", "fake_password", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-
-  FieldDataManager field_data_manager;
-  WebVector<WebFormControlElement> control_elements;
-  form.GetFormControlElements(control_elements);
-  ASSERT_EQ("nonvisible_text", control_elements[0].NameForAutofill().Utf8());
-  field_data_manager.UpdateFieldDataMap(control_elements[0],
-                                        control_elements[0].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-  ASSERT_EQ("nonvisible_password",
-            control_elements[2].NameForAutofill().Utf8());
-  field_data_manager.UpdateFieldDataMap(control_elements[2],
-                                        control_elements[2].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-
-  std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm(
-      form, &field_data_manager, nullptr, nullptr);
-
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("nonvisible_text"),
-            password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("actual_username"),
-            password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("nonvisible_password"),
-            password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("actual_password"),
-            password_form->password_value);
-  EXPECT_EQ(base::UTF8ToUTF16(""), password_form->new_password_element);
-  EXPECT_EQ(base::UTF8ToUTF16(""), password_form->new_password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, TypedPasswordAndUsernameCachedOnPage) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  // The heuristics should consider only password field with user input (i.e.
-  // password_with_user_input?) and visible username fields (i.e. nickname,
-  // visible_text, captcha) since there is no username field with user input.
-  builder.AddNonDisplayedPasswordField("nondisplayed1", "fake_password");
-  builder.AddNonDisplayedTextField("nondisplayed1", "fake_username1");
-  builder.AddTextField("nickname", "bob", nullptr);
-  builder.AddTextField("visible_text", "cached_username",
-                       nullptr);  // Username.
-  builder.AddNonVisibleTextField("nonvisible_text", "fake_username2");
-  builder.AddNonVisiblePasswordField("nonvisible_password", "not_password");
-  builder.AddNonVisibleTextField("nonvisible_text", "fake_username2");
-  builder.AddPasswordField("password_wo_user_input", "", nullptr);
-  builder.AddNonVisibleTextField("nonvisible_text", "");
-  builder.AddPasswordField("password_with_user_input1", "actual_password",
-                           nullptr);  // Password to save.
-  builder.AddPasswordField("password_with_user_input2", "actual_password",
-                           nullptr);
-  builder.AddTextField("captcha", "12345", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-
-  FieldDataManager field_data_manager;
-  WebVector<WebFormControlElement> control_elements;
-  form.GetFormControlElements(control_elements);
-  ASSERT_EQ("password_with_user_input1",
-            control_elements[9].NameForAutofill().Utf8());
-  field_data_manager.UpdateFieldDataMap(control_elements[9],
-                                        control_elements[9].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-  ASSERT_EQ("password_with_user_input2",
-            control_elements[10].NameForAutofill().Utf8());
-  field_data_manager.UpdateFieldDataMap(control_elements[10],
-                                        control_elements[10].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-
-  std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm(
-      form, &field_data_manager, nullptr, nullptr);
-
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-
-  EXPECT_EQ(base::UTF8ToUTF16("visible_text"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("cached_username"),
-            password_form->username_value);
-
-  EXPECT_EQ(base::string16(), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_value);
-
-  EXPECT_EQ(base::UTF8ToUTF16("password_with_user_input1"),
-            password_form->new_password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("actual_password"),
-            password_form->new_password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, TypedPasswordAndInvisibleUsername) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  // The heuristics should consider only password field with user input (i.e.
-  // password_with_user_input?) and invisible username fields since all username
-  // fields have no user input and are invisible.
-  builder.AddNonDisplayedPasswordField("nondisplayed1", "fake_password");
-  builder.AddNonDisplayedTextField("nondisplayed1", "fake_username1");
-  builder.AddNonVisibleTextField("nickname", "bob");
-  builder.AddNonVisibleTextField("this_is_username", "invisible_username");
-  builder.AddNonVisiblePasswordField("nonvisible_password", "not_password");
-  builder.AddPasswordField("password_wo_user_input", "", nullptr);
-  builder.AddNonVisiblePasswordField("nonvisible_password2", "");
-  builder.AddPasswordField("password_with_user_input1", "actual_password",
-                           nullptr);  // Password to save.
-  builder.AddNonVisibleTextField("nonvisible_text3", "---H09-$%");
-  builder.AddPasswordField("password_with_user_input2", "actual_password",
-                           nullptr);
-  builder.AddNonVisibleTextField("nonvisible_text3", "debug_info");
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-
-  FieldDataManager field_data_manager;
-  WebVector<WebFormControlElement> control_elements;
-  form.GetFormControlElements(control_elements);
-  ASSERT_EQ("password_with_user_input1",
-            control_elements[7].NameForAutofill().Utf8());
-  field_data_manager.UpdateFieldDataMap(control_elements[7],
-                                        control_elements[7].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-  ASSERT_EQ("password_with_user_input2",
-            control_elements[9].NameForAutofill().Utf8());
-  field_data_manager.UpdateFieldDataMap(control_elements[9],
-                                        control_elements[9].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-
-  std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm(
-      form, &field_data_manager, nullptr, nullptr);
-
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-
-  EXPECT_EQ(base::UTF8ToUTF16("this_is_username"),
-            password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("invisible_username"),
-            password_form->username_value);
-
-  EXPECT_EQ(base::string16(), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16(""), password_form->password_value);
-
-  EXPECT_EQ(base::UTF8ToUTF16("password_with_user_input1"),
-            password_form->new_password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("actual_password"),
-            password_form->new_password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToBadActionURL) {
-  PasswordFormBuilder builder("invalid_target");
-  builder.AddTextField("username", "JohnSmith", nullptr);
-  builder.AddSubmitButton("submit");
-  builder.AddPasswordField("password", "secret", nullptr);
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  EXPECT_FALSE(password_form);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, InvalidFormDueToNoPasswordFields) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username1", "John", nullptr);
-  builder.AddTextField("username2", "Smith", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  EXPECT_FALSE(password_form);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, ConfusingPasswordFields) {
-  // Each test case consists of a set of parameters to be plugged into the
-  // PasswordFormBuilder below.
-  const char* cases[][3] = {
-      // No autocomplete attributes to guide us, and we see:
-      //  * three password values that are all different,
-      //  * three password values that are all the same;
-      //  * three password values with the first and last matching.
-      // In any case, we should just give up on this form.
-      {"alpha", "beta", "gamma"},
-      {"alpha", "alpha", "alpha"},
-      {"alpha", "beta", "alpha"}};
-
-  for (size_t i = 0; i < base::size(cases); ++i) {
-    SCOPED_TRACE(testing::Message() << "Iteration " << i);
-
-    PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextField("username1", "John", nullptr);
-    builder.AddPasswordField("password1", cases[i][0], nullptr);
-    builder.AddPasswordField("password2", cases[i][1], nullptr);
-    builder.AddPasswordField("password3", cases[i][2], nullptr);
-    builder.AddSubmitButton("submit");
-    std::string html = builder.ProduceHTML();
-
-    std::unique_ptr<PasswordForm> password_form =
-        LoadHTMLAndConvertForm(html, nullptr, false);
-    ASSERT_TRUE(password_form);
-    EXPECT_FALSE(password_form->only_for_fallback);
-    EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
-    EXPECT_EQ(base::UTF8ToUTF16("John"), password_form->username_value);
-    EXPECT_EQ(base::UTF8ToUTF16("password1"), password_form->password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(cases[i][0]), password_form->password_value);
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       ManyPasswordFieldsWithoutAutocompleteAttributes) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username1", "John", nullptr);
-  builder.AddPasswordField("password1", "alpha", nullptr);
-  builder.AddPasswordField("password2", "alpha", nullptr);
-  builder.AddPasswordField("password3", "alpha", nullptr);
-  builder.AddPasswordField("password4", "alpha", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("John"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password1"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("alpha"), password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, SetOtherPossiblePasswords) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username1", "John", nullptr);
-  builder.AddPasswordField("password1", "alpha1", nullptr);
-  builder.AddPasswordField("password2", "alpha2", nullptr);
-  builder.AddPasswordField("password3", "alpha3", nullptr);
-  builder.AddPasswordField("password4", "alpha4", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->form_has_autofilled_value);
-
-  // Make sure we have all possible passwords along with the username info.
-  EXPECT_EQ(base::ASCIIToUTF16("username1"), password_form->username_element);
-  EXPECT_EQ(base::ASCIIToUTF16("John"), password_form->username_value);
-  EXPECT_EQ(base::ASCIIToUTF16("alpha1"), password_form->password_value);
-  EXPECT_THAT(
-      password_form->all_possible_passwords,
-      testing::ElementsAre(ValueElementPair(base::ASCIIToUTF16("alpha1"),
-                                            base::ASCIIToUTF16("password1")),
-                           ValueElementPair(base::ASCIIToUTF16("alpha2"),
-                                            base::ASCIIToUTF16("password2")),
-                           ValueElementPair(base::ASCIIToUTF16("alpha3"),
-                                            base::ASCIIToUTF16("password3")),
-                           ValueElementPair(base::ASCIIToUTF16("alpha4"),
-                                            base::ASCIIToUTF16("password4"))));
-  EXPECT_EQ(base::ASCIIToUTF16("alpha1+password1, alpha2+password2, "
-                               "alpha3+password3, alpha4+password4"),
-            ValueElementVectorToString(password_form->all_possible_passwords));
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       AllPossiblePasswordsIncludeAutofilledValue) {
-  for (bool autofilled_value_was_modified_by_user : {false, true}) {
-    PasswordFormBuilder builder(kTestFormActionURL);
-    builder.AddTextField("username1", "John", nullptr);
-    builder.AddPasswordField("old-password", "autofilled_value", nullptr);
-    builder.AddPasswordField("new-password", "user_value", nullptr);
-    builder.AddSubmitButton("submit");
-    std::string html = builder.ProduceHTML();
-
-    WebFormElement form;
-    LoadWebFormFromHTML(html, &form, nullptr);
-    WebVector<WebFormControlElement> control_elements;
-    form.GetFormControlElements(control_elements);
-
-    FieldDataManager field_data_manager;
-    FieldPropertiesMask mask = FieldPropertiesFlags::AUTOFILLED;
-    if (autofilled_value_was_modified_by_user)
-      mask |= FieldPropertiesFlags::USER_TYPED;
-    field_data_manager.UpdateFieldDataMap(
-        control_elements[1], base::ASCIIToUTF16("autofilled_value"), mask);
-    field_data_manager.UpdateFieldDataMap(control_elements[2],
-                                          base::ASCIIToUTF16("user_value"),
-                                          FieldPropertiesFlags::USER_TYPED);
-
-    std::unique_ptr<PasswordForm> password_form(CreatePasswordFormFromWebForm(
-        form, &field_data_manager, nullptr, nullptr));
-    ASSERT_TRUE(password_form);
-    EXPECT_TRUE(password_form->form_has_autofilled_value);
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest, CreditCardNumberWithTypePasswordForm) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr);
-  builder.AddPasswordField("Credit-card-number", "0000 0000 0000 0000",
-                           nullptr);
-  builder.AddTextField("cvc", "000", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::map<int, PasswordFormFieldPredictionType> predictions_positions;
-  predictions_positions[1] = PasswordFormFieldPredictionType::kNotPassword;
-
-  FormsPredictionsMap predictions;
-  SetPredictions(html, &predictions, predictions_positions);
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, &predictions, false);
-  EXPECT_TRUE(password_form);
-  EXPECT_TRUE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("Credit-card-owner-name"),
-            password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("John Smith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"),
-            password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"),
-            password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, UsernamePredictionFromServer) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username", "JohnSmith", nullptr);
-  // 'autocomplete' attribute cannot override server's prediction.
-  builder.AddTextField("Full name", "John A. Smith", "username");
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::map<int, PasswordFormFieldPredictionType> predictions_positions;
-  predictions_positions[0] = PasswordFormFieldPredictionType::kUsername;
-  FormsPredictionsMap predictions;
-  SetPredictions(html, &predictions, predictions_positions);
-
-  base::HistogramTester histogram_tester;
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, &predictions, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("JohnSmith"), password_form->username_value);
-  histogram_tester.ExpectUniqueSample(
-      "PasswordManager.UsernameDetectionMethod",
-      UsernameDetectionMethod::SERVER_SIDE_PREDICTION, 1);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       UsernamePredictionFromServerToEmptyField) {
-  // Tests that if a form has user input and the username prediction by the
-  // server points to an empty field, then the prediction is ignored.
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("empty-field", "", "");  // The prediction points here.
-  builder.AddTextField("full-name", "John A. Smith", nullptr);
-  builder.AddTextField("username", "JohnSmith", nullptr);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::map<int, PasswordFormFieldPredictionType> predictions_positions;
-  predictions_positions[0] = PasswordFormFieldPredictionType::kUsername;
-  FormsPredictionsMap predictions;
-  SetPredictions(html, &predictions, predictions_positions);
-
-  // The password field has user input.
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-  WebVector<WebFormControlElement> control_elements;
-  form.GetFormControlElements(control_elements);
-  FieldDataManager field_data_manager;
-  field_data_manager.UpdateFieldDataMap(control_elements[3],
-                                        control_elements[3].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-
-  std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm(
-      form, &field_data_manager, &predictions, &username_detector_cache_);
-  ASSERT_TRUE(password_form);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("JohnSmith"), password_form->username_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       CreditCardVerificationNumberWithTypePasswordForm) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr);
-  builder.AddTextField("Credit-card-number", "0000 0000 0000 0000", nullptr);
-  builder.AddPasswordField("cvc", "000", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::map<int, PasswordFormFieldPredictionType> predictions_positions;
-  predictions_positions[2] = PasswordFormFieldPredictionType::kNotPassword;
-
-  FormsPredictionsMap predictions;
-  SetPredictions(html, &predictions, predictions_positions);
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, &predictions, false);
-  EXPECT_TRUE(password_form);
-  EXPECT_TRUE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"),
-            password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"),
-            password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("cvc"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("000"), password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       CreditCardNumberWithTypePasswordFormWithAutocomplete) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr);
-  builder.AddPasswordField("Credit-card-number", "0000 0000 0000 0000",
-                           "current-password");
-  builder.AddTextField("cvc", "000", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::map<int, PasswordFormFieldPredictionType> predictions_positions;
-  predictions_positions[1] = PasswordFormFieldPredictionType::kNotPassword;
-
-  FormsPredictionsMap predictions;
-  SetPredictions(html, &predictions, predictions_positions);
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, &predictions, false);
-  EXPECT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("Credit-card-owner-name"),
-            password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("John Smith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"),
-            password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"),
-            password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       CreditCardVerificationNumberWithTypePasswordFormWithAutocomplete) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("Credit-card-owner-name", "John Smith", nullptr);
-  builder.AddTextField("Credit-card-number", "0000 0000 0000 0000", nullptr);
-  builder.AddPasswordField("cvc", "000", "new-password");
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::map<int, PasswordFormFieldPredictionType> predictions_positions;
-  predictions_positions[2] = PasswordFormFieldPredictionType::kNotPassword;
-
-  FormsPredictionsMap predictions;
-  SetPredictions(html, &predictions, predictions_positions);
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, &predictions, false);
-  ASSERT_TRUE(password_form);
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("Credit-card-number"),
-            password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("0000 0000 0000 0000"),
-            password_form->username_value);
-  EXPECT_TRUE(password_form->password_element.empty());
-  EXPECT_TRUE(password_form->password_value.empty());
-  EXPECT_EQ(base::UTF8ToUTF16("cvc"), password_form->new_password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("000"), password_form->new_password_value);
-}
-
 TEST_F(PasswordFormConversionUtilsTest, IsGaiaReauthFormIgnored) {
   struct TestCase {
     const char* origin;
@@ -2201,255 +253,4 @@
   }
 }
 
-TEST_F(PasswordFormConversionUtilsTest,
-       IdentifyingFieldsWithoutNameOrIdAttributes) {
-  const char* kEmpty = nullptr;
-  const struct {
-    const char* username_fieldname;
-    const char* password_fieldname;
-    const char* new_password_fieldname;
-    const char* expected_username_element;
-    const char* expected_password_element;
-    const char* expected_new_password_element;
-  } test_cases[] = {
-      {"username", "password", "new_password", "username", "password",
-       "new_password"},
-      {"username", "password", kEmpty, "username", "password",
-       "anonymous_new_password"},
-      {"username", kEmpty, kEmpty, "username", "anonymous_password",
-       "anonymous_new_password"},
-      {kEmpty, kEmpty, kEmpty, "anonymous_username", "anonymous_password",
-       "anonymous_new_password"},
-  };
-
-  for (size_t i = 0; i < base::size(test_cases); ++i) {
-    SCOPED_TRACE(testing::Message()
-                 << "Iteration " << i << ", expected_username "
-                 << test_cases[i].expected_username_element
-                 << ", expected_password"
-                 << test_cases[i].expected_password_element
-                 << ", expected_new_password "
-                 << test_cases[i].expected_new_password_element);
-
-    PasswordFormBuilder builder(kTestFormActionURL);
-    if (test_cases[i].username_fieldname == kEmpty) {
-      builder.AddAnonymousInputField("text");
-    } else {
-      builder.AddTextField(test_cases[i].username_fieldname, "", kEmpty);
-    }
-
-    if (test_cases[i].password_fieldname == kEmpty) {
-      builder.AddAnonymousInputField("password");
-    } else {
-      builder.AddPasswordField(test_cases[i].password_fieldname, "", kEmpty);
-    }
-
-    if (test_cases[i].new_password_fieldname == kEmpty) {
-      builder.AddAnonymousInputField("password");
-    } else {
-      builder.AddPasswordField(test_cases[i].new_password_fieldname, "",
-                               kEmpty);
-    }
-    std::string html = builder.ProduceHTML();
-
-    std::unique_ptr<PasswordForm> password_form =
-        LoadHTMLAndConvertForm(html, nullptr, false);
-    EXPECT_TRUE(password_form);
-
-    EXPECT_FALSE(password_form->only_for_fallback);
-    EXPECT_EQ(base::UTF8ToUTF16(test_cases[i].expected_username_element),
-              password_form->username_element);
-    EXPECT_EQ(base::UTF8ToUTF16(test_cases[i].expected_password_element),
-              password_form->password_element);
-    EXPECT_EQ(base::UTF8ToUTF16(test_cases[i].expected_new_password_element),
-              password_form->new_password_element);
-  }
-}
-
-TEST_F(PasswordFormConversionUtilsTest, TooManyFieldsToParseForm) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  for (size_t i = 0; i < form_util::kMaxParseableFields + 1; ++i)
-    builder.AddTextField("id", "value", "autocomplete");
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(builder.ProduceHTML(), nullptr, false);
-  EXPECT_FALSE(password_form);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, OnlyCreditCardFields) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("ccname", "johnsmith", "cc-name");
-  builder.AddPasswordField("cc_security_code", "0123456789", "cc-csc");
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  EXPECT_TRUE(password_form);
-  EXPECT_TRUE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("ccname"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("cc_security_code"),
-            password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("0123456789"), password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest,
-       FieldsWithAndWithoutCreditCardAttributes) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username", "johnsmith", nullptr);
-  builder.AddTextField("ccname", "john_smith", "cc-name");
-  builder.AddPasswordField("cc_security_code", "0123456789", "random cc-csc");
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-
-  ASSERT_TRUE(password_form);
-
-  EXPECT_FALSE(password_form->only_for_fallback);
-  EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
-  EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, ResetPasswordForm) {
-  // GetPassword (including HTML classifier) should process correctly forms
-  // without any text fields.
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-  base::HistogramTester histogram_tester;
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-
-  ASSERT_TRUE(password_form);
-
-  EXPECT_TRUE(password_form->username_element.empty());
-  EXPECT_TRUE(password_form->username_value.empty());
-  EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
-  EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
-  histogram_tester.ExpectUniqueSample(
-      "PasswordManager.UsernameDetectionMethod",
-      UsernameDetectionMethod::NO_USERNAME_DETECTED, 1);
-}
-
-TEST_F(PasswordFormConversionUtilsTest, StickyPasswordType) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("username", "johnsmith", nullptr);
-  builder.AddPasswordField("password", "secret", nullptr);
-  builder.AddSubmitButton("submit");
-  std::string html = builder.ProduceHTML();
-
-  std::unique_ptr<PasswordForm> password_form =
-      LoadHTMLAndConvertForm(html, nullptr, false);
-  ASSERT_TRUE(password_form);
-
-  FormData old_form_data;
-  ASSERT_TRUE(ExtractFormDataForFirstForm(&old_form_data));
-
-  // Change password field to type="text".
-  ExecuteJavaScriptForTests(
-      "document.getElementById(\"password\").type = \"text\";");
-
-  // Validate that - despite the change - the old password field is still
-  // recognized as a password field.
-  WebFormElement new_form;
-  GetFirstForm(&new_form);
-  std::unique_ptr<PasswordForm> new_password_form =
-      CreatePasswordFormFromWebForm(new_form, nullptr, nullptr,
-                                    &username_detector_cache_);
-  ASSERT_TRUE(new_password_form);
-
-  EXPECT_EQ(*password_form, *new_password_form);
-
-  FormData new_form_data;
-  ASSERT_TRUE(ExtractFormDataForFirstForm(&new_form_data));
-
-  EXPECT_EQ(old_form_data, new_form_data);
-}
-
-// Check that Chrome remembers the value typed by the user in cases when it gets
-// overridden by the page.
-TEST_F(PasswordFormConversionUtilsTest, TypedValuePreserved) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  builder.AddTextField("fine", "", "username");
-  builder.AddPasswordField("mangled", "", "current-password");
-  builder.AddTextField("completed_for_user", "", nullptr);
-  std::string html = builder.ProduceHTML();
-
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-
-  FieldDataManager field_data_manager;
-  WebVector<WebFormControlElement> control_elements;
-  form.GetFormControlElements(control_elements);
-
-  ASSERT_EQ(3u, control_elements.size());
-  ASSERT_EQ("fine", control_elements[0].NameForAutofill().Utf8());
-  control_elements[0].SetAutofillValue("same_value");
-  field_data_manager.UpdateFieldDataMap(control_elements[0],
-                                        control_elements[0].Value().Utf16(),
-                                        FieldPropertiesFlags::USER_TYPED);
-
-  ASSERT_EQ("mangled", control_elements[1].NameForAutofill().Utf8());
-  control_elements[1].SetAutofillValue("mangled_value");
-  field_data_manager.UpdateFieldDataMap(control_elements[1],
-                                        base::UTF8ToUTF16("original_value"),
-                                        FieldPropertiesFlags::USER_TYPED);
-
-  ASSERT_EQ("completed_for_user", control_elements[2].NameForAutofill().Utf8());
-  control_elements[2].SetAutofillValue("email@gmail.com");
-  field_data_manager.UpdateFieldDataMap(control_elements[2],
-                                        base::UTF8ToUTF16("email"),
-                                        FieldPropertiesFlags::USER_TYPED);
-
-  std::unique_ptr<PasswordForm> password_form = CreatePasswordFormFromWebForm(
-      form, &field_data_manager, nullptr, nullptr);
-
-  ASSERT_TRUE(password_form);
-
-  EXPECT_EQ(base::UTF8ToUTF16("same_value"), password_form->username_value);
-  EXPECT_EQ(base::UTF8ToUTF16("original_value"), password_form->password_value);
-
-  ASSERT_EQ(3u, password_form->form_data.fields.size());
-
-  EXPECT_EQ(base::UTF8ToUTF16("same_value"),
-            password_form->form_data.fields[0].value);
-  EXPECT_EQ(base::string16(), password_form->form_data.fields[0].typed_value);
-
-  EXPECT_EQ(base::UTF8ToUTF16("mangled_value"),
-            password_form->form_data.fields[1].value);
-  EXPECT_EQ(base::UTF8ToUTF16("original_value"),
-            password_form->form_data.fields[1].typed_value);
-
-  EXPECT_EQ(base::UTF8ToUTF16("email@gmail.com"),
-            password_form->form_data.fields[2].value);
-  EXPECT_EQ(base::string16(), password_form->form_data.fields[2].typed_value);
-}
-
-// Check that non-text fields are ignored.
-TEST_F(PasswordFormConversionUtilsTest, NonTextFields) {
-  PasswordFormBuilder builder(kTestFormActionURL);
-  // Avoid calling the text fields anything related to "username" to prevent the
-  // local HTML classifier from influencing the test result.
-  builder.AddTextField("textField", "", "");
-  builder.AddFieldWithType("radioInput", "radio");
-  builder.AddPasswordField("password", "", "");
-  std::string html = builder.ProduceHTML();
-
-  WebFormElement form;
-  LoadWebFormFromHTML(html, &form, nullptr);
-
-  std::unique_ptr<PasswordForm> password_form =
-      CreatePasswordFormFromWebForm(form, nullptr, nullptr, nullptr);
-
-  ASSERT_TRUE(password_form);
-  EXPECT_EQ(base::UTF8ToUTF16("textField"), password_form->username_element);
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_data_util.cc b/components/autofill/core/browser/autofill_data_util.cc
index fde2484..35620873 100644
--- a/components/autofill/core/browser/autofill_data_util.cc
+++ b/components/autofill/core/browser/autofill_data_util.cc
@@ -458,59 +458,6 @@
   return base::JoinString(full_name, base::ASCIIToUTF16(separator));
 }
 
-bool ProfileMatchesFullName(base::StringPiece16 full_name,
-                            const autofill::AutofillProfile& profile) {
-  const base::string16 kSpace = base::ASCIIToUTF16(" ");
-  const base::string16 kPeriodSpace = base::ASCIIToUTF16(". ");
-
-  // First Last
-  base::string16 candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace +
-                             profile.GetRawInfo(autofill::NAME_LAST);
-  if (!full_name.compare(candidate)) {
-    return true;
-  }
-
-  // First Middle Last
-  candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace +
-              profile.GetRawInfo(autofill::NAME_MIDDLE) + kSpace +
-              profile.GetRawInfo(autofill::NAME_LAST);
-  if (!full_name.compare(candidate)) {
-    return true;
-  }
-
-  // First M Last
-  candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace +
-              profile.GetRawInfo(autofill::NAME_MIDDLE_INITIAL) + kSpace +
-              profile.GetRawInfo(autofill::NAME_LAST);
-  if (!full_name.compare(candidate)) {
-    return true;
-  }
-
-  // First M. Last
-  candidate = profile.GetRawInfo(autofill::NAME_FIRST) + kSpace +
-              profile.GetRawInfo(autofill::NAME_MIDDLE_INITIAL) + kPeriodSpace +
-              profile.GetRawInfo(autofill::NAME_LAST);
-  if (!full_name.compare(candidate)) {
-    return true;
-  }
-
-  // Last First
-  candidate = profile.GetRawInfo(autofill::NAME_LAST) + kSpace +
-              profile.GetRawInfo(autofill::NAME_FIRST);
-  if (!full_name.compare(candidate)) {
-    return true;
-  }
-
-  // LastFirst
-  candidate = profile.GetRawInfo(autofill::NAME_LAST) +
-              profile.GetRawInfo(autofill::NAME_FIRST);
-  if (!full_name.compare(candidate)) {
-    return true;
-  }
-
-  return false;
-}
-
 const PaymentRequestData& GetPaymentRequestData(
     const std::string& issuer_network) {
   for (const PaymentRequestData& data : kPaymentRequestData) {
diff --git a/components/autofill/core/browser/autofill_data_util.h b/components/autofill/core/browser/autofill_data_util.h
index a6060345..d99dc253 100644
--- a/components/autofill/core/browser/autofill_data_util.h
+++ b/components/autofill/core/browser/autofill_data_util.h
@@ -96,11 +96,6 @@
                              base::StringPiece16 middle,
                              base::StringPiece16 family);
 
-// Returns true iff |full_name| is a concatenation of some combination of the
-// first/middle/last (incl. middle initial) in |profile|.
-bool ProfileMatchesFullName(base::StringPiece16 full_name,
-                            const autofill::AutofillProfile& profile);
-
 // Returns the Payment Request API basic card payment spec data for the provided
 // autofill credit card |network|.  Will set the network and the icon to
 // "generic" for any unrecognized type.
diff --git a/components/autofill/core/browser/autofill_data_util_unittest.cc b/components/autofill/core/browser/autofill_data_util_unittest.cc
index 7b1ea28..2646b113 100644
--- a/components/autofill/core/browser/autofill_data_util_unittest.cc
+++ b/components/autofill/core/browser/autofill_data_util_unittest.cc
@@ -233,33 +233,6 @@
         // Has a middle-name, too unusual
         ));
 
-TEST(AutofillDataUtilTest, ProfileMatchesFullName) {
-  autofill::AutofillProfile profile;
-  autofill::test::SetProfileInfo(
-      &profile, "First", "Middle", "Last", "fml@example.com", "Acme inc",
-      "123 Main", "Apt 2", "Laredo", "TX", "77300", "US", "832-555-1000");
-
-  EXPECT_TRUE(ProfileMatchesFullName(base::UTF8ToUTF16("First Last"), profile));
-
-  EXPECT_TRUE(
-      ProfileMatchesFullName(base::UTF8ToUTF16("First Middle Last"), profile));
-
-  EXPECT_TRUE(
-      ProfileMatchesFullName(base::UTF8ToUTF16("First M Last"), profile));
-
-  EXPECT_TRUE(
-      ProfileMatchesFullName(base::UTF8ToUTF16("First M. Last"), profile));
-
-  EXPECT_TRUE(
-      ProfileMatchesFullName(base::UTF8ToUTF16("Last First"), profile));
-
-  EXPECT_TRUE(
-      ProfileMatchesFullName(base::UTF8ToUTF16("LastFirst"), profile));
-
-  EXPECT_FALSE(
-      ProfileMatchesFullName(base::UTF8ToUTF16("Kirby Puckett"), profile));
-}
-
 struct ValidCountryCodeTestCase {
   std::string country_code;
   bool expected_result;
diff --git a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
index 50b4ab8..66e412c 100644
--- a/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autocomplete_sync_bridge_unittest.cc
@@ -23,7 +23,7 @@
 #include "components/autofill/core/browser/webdata/autofill_entry.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/data_type_activation_request.h"
 #include "components/sync/model/metadata_batch.h"
@@ -245,7 +245,7 @@
       const AutofillSpecifics& specifics) {
     auto data = std::make_unique<EntityData>();
     *data->specifics.mutable_autofill() = specifics;
-    data->client_tag_hash = syncer::GenerateSyncableHash(
+    data->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
         syncer::AUTOFILL, bridge()->GetClientTag(*data));
     return data;
   }
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
index 8e303e2..0ebf17d 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
@@ -28,7 +28,7 @@
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h"
 #include "components/autofill/core/common/autofill_constants.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/data_type_activation_request.h"
 #include "components/sync/model/entity_data.h"
@@ -296,7 +296,7 @@
       const AutofillProfileSpecifics& specifics) {
     auto data = std::make_unique<EntityData>();
     *data->specifics.mutable_autofill_profile() = specifics;
-    data->client_tag_hash = syncer::GenerateSyncableHash(
+    data->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
         syncer::AUTOFILL_PROFILE, bridge()->GetClientTag(*data));
     return data;
   }
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
index 0ca087c..997d51b1 100644
--- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
@@ -14,7 +14,7 @@
 #include "components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/common/autofill_constants.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/entity_data.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -50,8 +50,8 @@
     const std::string& client_tag) {
   auto data = std::make_unique<syncer::EntityData>();
   *data->specifics.mutable_autofill_wallet() = specifics;
-  data->client_tag_hash =
-      syncer::GenerateSyncableHash(syncer::AUTOFILL_WALLET_DATA, client_tag);
+  data->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
+      syncer::AUTOFILL_WALLET_DATA, client_tag);
   return data;
 }
 
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index e3b3e953..c6448b4 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -2595,11 +2595,12 @@
                               "SELECT ?, storage_key, value "
                               "FROM autofill_sync_metadata"));
   // Note: This uses the *wrong* ID for the ModelType - instead of
-  // |syncer::ModelTypeToHistogramInt|, this should be |GetKeyValueForModelType|
+  // |syncer::ModelTypeHistogramValue|, this should be |GetKeyValueForModelType|
   // aka |syncer::ModelTypeToStableIdentifier|. But at this point, fixing it
   // here would just make an even bigger mess. Instead, we clean this up in the
   // migration to version 81. See also crbug.com/895826.
-  insert_metadata.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL));
+  insert_metadata.BindInt(
+      0, static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL)));
 
   // Prior to this migration, the table was a singleton, containing only one
   // entry with id being hard-coded to 1.
@@ -2608,7 +2609,8 @@
                               "(model_type, value) SELECT ?, value "
                               "FROM autofill_model_type_state WHERE id=1"));
   // Note: Like above, this uses the *wrong* ID for the ModelType.
-  insert_state.BindInt(0, syncer::ModelTypeToHistogramInt(syncer::AUTOFILL));
+  insert_state.BindInt(
+      0, static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL)));
 
   if (!insert_metadata.Run() || !insert_state.Run()) {
     return false;
@@ -2641,7 +2643,7 @@
   // in trying to recover anything, since by now it'll have been redownloaded
   // anyway.
   const int bad_model_type_id =
-      syncer::ModelTypeToHistogramInt(syncer::AUTOFILL);
+      static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL));
   DCHECK_NE(bad_model_type_id, GetKeyValueForModelType(syncer::AUTOFILL));
 
   sql::Transaction transaction(db_);
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
index 2452ca9..0f78f82 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
@@ -29,7 +29,7 @@
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h"
 #include "components/autofill/core/common/autofill_constants.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/entity_data.h"
 #include "components/sync/model/mock_model_type_change_processor.h"
@@ -353,7 +353,7 @@
       bool is_deleted = false) {
     auto data = std::make_unique<EntityData>();
     *data->specifics.mutable_wallet_metadata() = specifics;
-    data->client_tag_hash = syncer::GenerateSyncableHash(
+    data->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
         syncer::AUTOFILL_WALLET_METADATA, bridge()->GetClientTag(*data));
     if (is_deleted) {
       // Specifics had to be set in order to generate the client tag. Since
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
index c733a43..12e7e536 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
@@ -31,7 +31,7 @@
 #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_backend.h"
 #include "components/autofill/core/common/autofill_constants.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/model/entity_data.h"
 #include "components/sync/model/mock_model_type_change_processor.h"
@@ -269,7 +269,7 @@
       const AutofillWalletSpecifics& specifics) {
     auto data = std::make_unique<EntityData>();
     *data->specifics.mutable_autofill_wallet() = specifics;
-    data->client_tag_hash = syncer::GenerateSyncableHash(
+    data->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
         syncer::AUTOFILL_WALLET_DATA, bridge()->GetClientTag(*data));
     return data;
   }
diff --git a/components/autofill/core/common/BUILD.gn b/components/autofill/core/common/BUILD.gn
index 88abc76..d0d4d220d 100644
--- a/components/autofill/core/common/BUILD.gn
+++ b/components/autofill/core/common/BUILD.gn
@@ -43,7 +43,6 @@
     "logging/log_buffer.h",
     "password_form.cc",
     "password_form.h",
-    "password_form_field_prediction_map.h",
     "password_form_fill_data.cc",
     "password_form_fill_data.h",
     "password_form_generation_data.cc",
diff --git a/components/autofill/core/common/mojom/autofill_types.mojom b/components/autofill/core/common/mojom/autofill_types.mojom
index 8e8a3c3..2b26b1b 100644
--- a/components/autofill/core/common/mojom/autofill_types.mojom
+++ b/components/autofill/core/common/mojom/autofill_types.mojom
@@ -294,17 +294,3 @@
   SubmissionIndicatorEvent submission_event;
   bool only_for_fallback;
 };
-
-// Note: Even though https://crbug.com/628104 is solved, we still can not use a
-// map directly as long as https://crbug.com/914074 is not fixed.
-struct PasswordFormFieldPredictionMap {
-  array<FormFieldData> keys;
-  array<PasswordFormFieldPredictionType> values;
-};
-
-// Note: Even though https://crbug.com/628104 is solved, we still can not use a
-// map directly as long as https://crbug.com/914074 is not fixed.
-struct FormsPredictionsMap {
-  array<FormData> keys;
-  array<PasswordFormFieldPredictionMap> values;
-};
diff --git a/components/autofill/core/common/mojom/autofill_types.typemap b/components/autofill/core/common/mojom/autofill_types.typemap
index 6b444c3..7d801ef 100644
--- a/components/autofill/core/common/mojom/autofill_types.typemap
+++ b/components/autofill/core/common/mojom/autofill_types.typemap
@@ -9,7 +9,6 @@
   "//components/autofill/core/common/form_field_data.h",
   "//components/autofill/core/common/form_field_data_predictions.h",
   "//components/autofill/core/common/password_form.h",
-  "//components/autofill/core/common/password_form_field_prediction_map.h",
   "//components/autofill/core/common/password_form_fill_data.h",
   "//components/autofill/core/common/password_form_generation_data.h",
   "//components/autofill/core/common/password_generation_util.h",
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
index 9128a85..dead49b5 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
@@ -298,92 +298,6 @@
 }
 
 // static
-std::vector<autofill::FormFieldData>
-StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView,
-             autofill::PasswordFormFieldPredictionMap>::
-    keys(const autofill::PasswordFormFieldPredictionMap& r) {
-  std::vector<autofill::FormFieldData> data;
-  for (const auto& i : r)
-    data.push_back(i.first);
-  return data;
-}
-
-// static
-std::vector<autofill::mojom::PasswordFormFieldPredictionType>
-StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView,
-             autofill::PasswordFormFieldPredictionMap>::
-    values(const autofill::PasswordFormFieldPredictionMap& r) {
-  std::vector<autofill::mojom::PasswordFormFieldPredictionType> types;
-  for (const auto& i : r)
-    types.push_back(i.second);
-  return types;
-}
-
-// static
-bool StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView,
-                  autofill::PasswordFormFieldPredictionMap>::
-    Read(autofill::mojom::PasswordFormFieldPredictionMapDataView data,
-         autofill::PasswordFormFieldPredictionMap* out) {
-  // Combines keys vector and values vector to the map.
-  std::vector<autofill::FormFieldData> keys;
-  if (!data.ReadKeys(&keys))
-    return false;
-  std::vector<autofill::mojom::PasswordFormFieldPredictionType> values;
-  if (!data.ReadValues(&values))
-    return false;
-  if (keys.size() != values.size())
-    return false;
-  out->clear();
-  for (size_t i = 0; i < keys.size(); ++i)
-    out->insert({keys[i], values[i]});
-
-  return true;
-}
-
-// static
-std::vector<autofill::FormData> StructTraits<
-    autofill::mojom::FormsPredictionsMapDataView,
-    autofill::FormsPredictionsMap>::keys(const autofill::FormsPredictionsMap&
-                                             r) {
-  std::vector<autofill::FormData> data;
-  for (const auto& i : r)
-    data.push_back(i.first);
-  return data;
-}
-
-// static
-std::vector<autofill::PasswordFormFieldPredictionMap> StructTraits<
-    autofill::mojom::FormsPredictionsMapDataView,
-    autofill::FormsPredictionsMap>::values(const autofill::FormsPredictionsMap&
-                                               r) {
-  std::vector<autofill::PasswordFormFieldPredictionMap> maps;
-  for (const auto& i : r)
-    maps.push_back(i.second);
-  return maps;
-}
-
-// static
-bool StructTraits<autofill::mojom::FormsPredictionsMapDataView,
-                  autofill::FormsPredictionsMap>::
-    Read(autofill::mojom::FormsPredictionsMapDataView data,
-         autofill::FormsPredictionsMap* out) {
-  // Combines keys vector and values vector to the map.
-  std::vector<autofill::FormData> keys;
-  if (!data.ReadKeys(&keys))
-    return false;
-  std::vector<autofill::PasswordFormFieldPredictionMap> values;
-  if (!data.ReadValues(&values))
-    return false;
-  if (keys.size() != values.size())
-    return false;
-  out->clear();
-  for (size_t i = 0; i < keys.size(); ++i)
-    out->insert({keys[i], values[i]});
-
-  return true;
-}
-
-// static
 bool StructTraits<autofill::mojom::ValueElementPairDataView,
                   autofill::ValueElementPair>::
     Read(autofill::mojom::ValueElementPairDataView data,
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
index 6a1ca29d..2b7b914 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
@@ -18,7 +18,6 @@
 #include "components/autofill/core/common/form_field_data_predictions.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/autofill/core/common/password_form_generation_data.h"
 #include "components/autofill/core/common/password_generation_util.h"
@@ -578,32 +577,6 @@
 };
 
 template <>
-struct StructTraits<autofill::mojom::PasswordFormFieldPredictionMapDataView,
-                    autofill::PasswordFormFieldPredictionMap> {
-  static std::vector<autofill::FormFieldData> keys(
-      const autofill::PasswordFormFieldPredictionMap& r);
-
-  static std::vector<autofill::mojom::PasswordFormFieldPredictionType> values(
-      const autofill::PasswordFormFieldPredictionMap& r);
-
-  static bool Read(autofill::mojom::PasswordFormFieldPredictionMapDataView data,
-                   autofill::PasswordFormFieldPredictionMap* out);
-};
-
-template <>
-struct StructTraits<autofill::mojom::FormsPredictionsMapDataView,
-                    autofill::FormsPredictionsMap> {
-  static std::vector<autofill::FormData> keys(
-      const autofill::FormsPredictionsMap& r);
-
-  static std::vector<autofill::PasswordFormFieldPredictionMap> values(
-      const autofill::FormsPredictionsMap& r);
-
-  static bool Read(autofill::mojom::FormsPredictionsMapDataView data,
-                   autofill::FormsPredictionsMap* out);
-};
-
-template <>
 struct StructTraits<autofill::mojom::ValueElementPairDataView,
                     autofill::ValueElementPair> {
   static base::string16 value(const autofill::ValueElementPair& r) {
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
index b1b17958..24061ea 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits_unittest.cc
@@ -110,39 +110,6 @@
       mojom::SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION;
 }
 
-void CreateTestFormsPredictionsMap(FormsPredictionsMap* predictions) {
-  FormsPredictionsMap& result_map = *predictions;
-  // 1st element.
-  FormData form_data;
-  test::CreateTestAddressFormData(&form_data);
-  ASSERT_TRUE(form_data.fields.size() >= 4);
-  result_map[form_data][form_data.fields[0]] =
-      PasswordFormFieldPredictionType::kUsername;
-  result_map[form_data][form_data.fields[1]] =
-      PasswordFormFieldPredictionType::kCurrentPassword;
-  result_map[form_data][form_data.fields[2]] =
-      PasswordFormFieldPredictionType::kNewPassword;
-  result_map[form_data][form_data.fields[3]] =
-      PasswordFormFieldPredictionType::kNotPassword;
-
-  // 2nd element.
-  form_data.fields.clear();
-  result_map[form_data] = {};
-
-  // 3rd element.
-  FormFieldData field_data;
-  test::CreateTestSelectField("TestLabel1", "TestName1", "TestValue1", kOptions,
-                              kOptions, 4, &field_data);
-  form_data.fields.push_back(field_data);
-  test::CreateTestSelectField("TestLabel2", "TestName2", "TestValue2", kOptions,
-                              kOptions, 4, &field_data);
-  form_data.fields.push_back(field_data);
-  result_map[form_data][form_data.fields[0]] =
-      PasswordFormFieldPredictionType::kNewPassword;
-  result_map[form_data][form_data.fields[1]] =
-      PasswordFormFieldPredictionType::kCurrentPassword;
-}
-
 void CreatePasswordGenerationUIData(
     password_generation::PasswordGenerationUIData* data) {
   data->bounds = gfx::RectF(1, 1, 200, 100);
@@ -248,12 +215,6 @@
     std::move(callback).Run(s);
   }
 
-  void PassFormsPredictionsMap(
-      const FormsPredictionsMap& s,
-      PassFormsPredictionsMapCallback callback) override {
-    std::move(callback).Run(s);
-  }
-
  private:
   base::test::TaskEnvironment task_environment_;
 
@@ -322,13 +283,6 @@
   std::move(closure).Run();
 }
 
-void ExpectFormsPredictionsMap(const FormsPredictionsMap& expected,
-                               base::OnceClosure closure,
-                               const FormsPredictionsMap& passed) {
-  EXPECT_EQ(expected, passed);
-  std::move(closure).Run();
-}
-
 TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) {
   FormFieldData input;
   test::CreateTestSelectField("TestLabel", "TestName", "TestValue", kOptions,
@@ -453,16 +407,4 @@
   loop.Run();
 }
 
-TEST_F(AutofillTypeTraitsTestImpl, PassFormsPredictionsMap) {
-  FormsPredictionsMap input;
-  CreateTestFormsPredictionsMap(&input);
-
-  base::RunLoop loop;
-  mojo::Remote<mojom::TypeTraitsTest> remote(GetTypeTraitsTestRemote());
-  remote->PassFormsPredictionsMap(
-      input,
-      base::BindOnce(&ExpectFormsPredictionsMap, input, loop.QuitClosure()));
-  loop.Run();
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/common/mojom/test_autofill_types.mojom b/components/autofill/core/common/mojom/test_autofill_types.mojom
index 9f5be9e..dd13926 100644
--- a/components/autofill/core/common/mojom/test_autofill_types.mojom
+++ b/components/autofill/core/common/mojom/test_autofill_types.mojom
@@ -16,8 +16,6 @@
   PassPasswordForm(PasswordForm s) => (PasswordForm passed);
   PassPasswordFormFillData(PasswordFormFillData s) =>
       (PasswordFormFillData passed);
-  PassFormsPredictionsMap(FormsPredictionsMap s) =>
-      (FormsPredictionsMap passed);
   PassPasswordFormGenerationData(PasswordFormGenerationData s) =>
       (PasswordFormGenerationData passed);
   PassPasswordGenerationUIData(PasswordGenerationUIData s) =>
diff --git a/components/autofill/core/common/password_form_field_prediction_map.h b/components/autofill/core/common/password_form_field_prediction_map.h
deleted file mode 100644
index b0d149c..0000000
--- a/components/autofill/core/common/password_form_field_prediction_map.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_FORM_FIELD_PREDICTION_MAP_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_FORM_FIELD_PREDICTION_MAP_H_
-
-#include "base/containers/flat_map.h"
-#include "components/autofill/core/common/form_data.h"
-#include "components/autofill/core/common/form_field_data.h"
-#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
-
-namespace autofill {
-
-using PasswordFormFieldPredictionMap =
-    base::flat_map<FormFieldData, mojom::PasswordFormFieldPredictionType>;
-using FormsPredictionsMap =
-    base::flat_map<FormData, PasswordFormFieldPredictionMap>;
-
-}  // namespace autofill
-
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_PASSWORD_FORM_FIELD_PREDICTION_MAP_H_
diff --git a/components/autofill_assistant/browser/client_settings.cc b/components/autofill_assistant/browser/client_settings.cc
index f375b67..9b9bffed 100644
--- a/components/autofill_assistant/browser/client_settings.cc
+++ b/components/autofill_assistant/browser/client_settings.cc
@@ -44,13 +44,6 @@
   if (proto.has_document_ready_check_count()) {
     document_ready_check_count = proto.document_ready_check_count();
   }
-  if (proto.has_enable_graceful_shutdown()) {
-    enable_graceful_shutdown = proto.enable_graceful_shutdown();
-  }
-  if (proto.has_graceful_shutdown_delay_ms()) {
-    graceful_shutdown_delay =
-        base::TimeDelta::FromMilliseconds(proto.graceful_shutdown_delay_ms());
-  }
   if (proto.has_cancel_delay_ms()) {
     cancel_delay = base::TimeDelta::FromMilliseconds(proto.cancel_delay_ms());
   }
diff --git a/components/autofill_assistant/browser/client_settings.h b/components/autofill_assistant/browser/client_settings.h
index f007b58..8fd70fc 100644
--- a/components/autofill_assistant/browser/client_settings.h
+++ b/components/autofill_assistant/browser/client_settings.h
@@ -61,14 +61,6 @@
   // ready.
   int document_ready_check_count = 50;
 
-  // Whether graceful shutdown should be enabled. If false, the UI stays
-  // up until it's dismissed.
-  bool enable_graceful_shutdown = true;
-
-  // How long to wait before shutting down during graceful shutdown. If 0
-  // shutdown happens immediately.
-  base::TimeDelta graceful_shutdown_delay = base::TimeDelta::FromSeconds(5);
-
   // How much time to give users to tap undo when they tap a cancel button.
   base::TimeDelta cancel_delay = base::TimeDelta::FromSeconds(5);
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 50d7769..e6f242b 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -74,6 +74,8 @@
 }
 
 message ClientSettingsProto {
+  reserved 10, 11;
+
   // Time between two periodic script precondition checks.
   optional int32 periodic_script_check_interval_ms = 1;
 
@@ -108,14 +110,6 @@
   // ready.
   optional int32 document_ready_check_count = 9;
 
-  // Whether graceful shutdown should be enabled. If false, the UI stays
-  // up until it's dismissed.
-  optional bool enable_graceful_shutdown = 10;
-
-  // How long to wait before shutting down during graceful shutdown. If 0
-  // shutdown happens immediately.
-  optional int32 graceful_shutdown_delay_ms = 11;
-
   // How much time to give users to tap undo when they tap a cancel button.
   optional int32 cancel_delay_ms = 12;
 
@@ -440,6 +434,8 @@
   // script finishes with this action. It has no effect if there is any other
   // action sent to the client after this one. Default is false.
   optional bool clean_contextual_ui = 33;
+
+  reserved 47;
 }
 
 // Result of |CollectUserDataProto| to be sent to the server.
diff --git a/components/autofill_assistant/browser/state.h b/components/autofill_assistant/browser/state.h
index d7479a72..3cad37da 100644
--- a/components/autofill_assistant/browser/state.h
+++ b/components/autofill_assistant/browser/state.h
@@ -81,14 +81,13 @@
   // Autofill assistant is stopped, but the controller is still available.
   //
   // This is a final state for the UI, which, when entering this state, detaches
-  // itself from the controller, waits for a few seconds to let the user read
-  // the message and then disappears.
+  // itself from the controller and lets the user read  the message.
   //
   // In that scenario, the status message at the time of transition to STOPPED
   // is supposed to contain the final message.
   //
-  // Next states: TRACKING.
-  STOPPED
+  // Next states: TRACKING
+  STOPPED,
 };
 
 inline std::ostream& operator<<(std::ostream& out,
diff --git a/components/chromeos_camera/BUILD.gn b/components/chromeos_camera/BUILD.gn
index 33ce314..ed195d1f 100644
--- a/components/chromeos_camera/BUILD.gn
+++ b/components/chromeos_camera/BUILD.gn
@@ -202,8 +202,8 @@
   ]
 
   deps = [
+    "common:camera_app_helper",
     "//ash/public/cpp",
-    "//media/capture/video/chromeos/mojom:cros_camera",
   ]
 }
 
diff --git a/components/chromeos_camera/camera_app_helper_impl.h b/components/chromeos_camera/camera_app_helper_impl.h
index 599602b..fd478e21 100644
--- a/components/chromeos_camera/camera_app_helper_impl.h
+++ b/components/chromeos_camera/camera_app_helper_impl.h
@@ -7,11 +7,11 @@
 
 #include <vector>
 
-#include "media/capture/video/chromeos/mojom/camera_app.mojom.h"
+#include "components/chromeos_camera/common/camera_app_helper.mojom.h"
 
 namespace chromeos_camera {
 
-class CameraAppHelperImpl : public cros::mojom::CameraAppHelper {
+class CameraAppHelperImpl : public chromeos_camera::mojom::CameraAppHelper {
  public:
   using CameraResultCallback =
       base::RepeatingCallback<void(uint32_t,
@@ -22,7 +22,7 @@
   explicit CameraAppHelperImpl(CameraResultCallback camera_result_callback);
   ~CameraAppHelperImpl() override;
 
-  // cros::mojom::CameraAppHelper implementations.
+  // chromeos_camera::mojom::CameraAppHelper implementations.
   void HandleCameraResult(uint32_t intent_id,
                           arc::mojom::CameraIntentAction action,
                           const std::vector<uint8_t>& data,
@@ -37,4 +37,4 @@
 
 }  // namespace chromeos_camera
 
-#endif  // COMPONENTS_CHROMEOS_CAMERA_CAMERA_APP_HELPER_IMPL_H_
\ No newline at end of file
+#endif  // COMPONENTS_CHROMEOS_CAMERA_CAMERA_APP_HELPER_IMPL_H_
diff --git a/components/chromeos_camera/common/BUILD.gn b/components/chromeos_camera/common/BUILD.gn
index d49f9e7..a2ca91d 100644
--- a/components/chromeos_camera/common/BUILD.gn
+++ b/components/chromeos_camera/common/BUILD.gn
@@ -17,3 +17,12 @@
     "//ui/gfx/geometry/mojom",
   ]
 }
+
+mojom("camera_app_helper") {
+  sources = [
+    "camera_app_helper.mojom",
+  ]
+  deps = [
+    "//components/arc/mojom:camera_intent",
+  ]
+}
diff --git a/components/chromeos_camera/common/camera_app_helper.mojom b/components/chromeos_camera/common/camera_app_helper.mojom
new file mode 100644
index 0000000..a0b8c822
--- /dev/null
+++ b/components/chromeos_camera/common/camera_app_helper.mojom
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chromeos_camera.mojom;
+
+import "components/arc/mojom/camera_intent.mojom";
+
+// Interface for communication between Chrome Camera App (Remote) and Chrome
+// (Receiver).
+interface CameraAppHelper {
+  // Sends the captured result |data| for corresponding intent recognized by
+  // |intent_id| back to ARC. The handler should handle |data| and may notify
+  // the intent caller according to the intention of the |action|. |is_success|
+  // will be set to true if the ARC received the result and set to false for
+  // invalid input.
+  HandleCameraResult(uint32 intent_id,
+                     arc.mojom.CameraIntentAction action,
+                     array<uint8> data) => (bool is_success);
+
+  // Checks if device is under tablet mode currently.
+  IsTabletMode() => (bool is_tablet_mode);
+};
diff --git a/components/chromeos_camera/dmabuf_utils.cc b/components/chromeos_camera/dmabuf_utils.cc
index dd420e15..3c63e9f 100644
--- a/components/chromeos_camera/dmabuf_utils.cc
+++ b/components/chromeos_camera/dmabuf_utils.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/time/time.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
 #include "mojo/public/cpp/system/platform_handle.h"
@@ -34,7 +35,7 @@
   const gfx::Rect visible_rect(coded_size);
 
   std::vector<base::ScopedFD> dma_buf_fds(num_planes);
-  std::vector<media::VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<media::ColorPlaneLayout> planes(num_planes);
   for (size_t i = 0; i < num_planes; ++i) {
     mojo::PlatformHandle handle =
         mojo::UnwrapPlatformHandle(std::move(dma_buf_planes[i]->fd_handle));
@@ -43,7 +44,7 @@
       return nullptr;
     }
     dma_buf_fds[i] = handle.TakeFD();
-    planes[i] = media::VideoFrameLayout::Plane(
+    planes[i] = media::ColorPlaneLayout(
         dma_buf_planes[i]->stride,
         base::strict_cast<size_t>(dma_buf_planes[i]->offset),
         base::strict_cast<size_t>(dma_buf_planes[i]->size));
diff --git a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
index fe3ff09..fc9889e 100644
--- a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
+++ b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
@@ -26,6 +26,7 @@
 #include "build/build_config.h"
 #include "components/chromeos_camera/gpu_jpeg_encode_accelerator_factory.h"
 #include "components/chromeos_camera/jpeg_encode_accelerator.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/test_data_util.h"
 #include "media/capture/video/chromeos/local_gpu_memory_buffer_manager.h"
 #include "media/gpu/buildflags.h"
@@ -92,7 +93,7 @@
   auto buffer_handle = buffer->CloneHandle().native_pixmap_handle;
 
   size_t num_planes = media::VideoFrame::NumPlanes(format);
-  std::vector<media::VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<media::ColorPlaneLayout> planes(num_planes);
   std::vector<base::ScopedFD> fds(num_planes);
   for (size_t i = 0; i < num_planes; i++) {
     auto& plane = buffer_handle.planes[i];
diff --git a/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc b/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
index 0fd7c1b4..a2f8f2e5 100644
--- a/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
+++ b/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
@@ -36,6 +36,7 @@
 #include "build/build_config.h"
 #include "components/chromeos_camera/gpu_mjpeg_decode_accelerator_factory.h"
 #include "components/chromeos_camera/mjpeg_decode_accelerator.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/test_data_util.h"
 #include "media/base/video_frame_layout.h"
@@ -348,7 +349,7 @@
   gmb->Unmap();
 
   // Create a VideoFrame from the NativePixmapHandle.
-  std::vector<media::VideoFrameLayout::Plane> planes;
+  std::vector<media::ColorPlaneLayout> planes;
   std::vector<base::ScopedFD> dmabuf_fds;
   for (size_t i = 0; i < num_planes; i++) {
     gfx::NativePixmapPlane& plane = gmb_handle.native_pixmap_handle.planes[i];
diff --git a/components/dom_distiller/core/dom_distiller_model.cc b/components/dom_distiller/core/dom_distiller_model.cc
index 81d2667..ea60747e 100644
--- a/components/dom_distiller/core/dom_distiller_model.cc
+++ b/components/dom_distiller/core/dom_distiller_model.cc
@@ -89,14 +89,6 @@
   return entries_list;
 }
 
-SyncDataList DomDistillerModel::GetAllSyncData() const {
-  SyncDataList data;
-  for (auto it = entries_.begin(); it != entries_.end(); ++it) {
-    data.push_back(CreateLocalData(it->second));
-  }
-  return data;
-}
-
 void DomDistillerModel::CalculateChangesForMerge(
     const SyncDataList& data,
     SyncChangeList* changes_to_apply,
diff --git a/components/dom_distiller/core/dom_distiller_model.h b/components/dom_distiller/core/dom_distiller_model.h
index e74bd26a..2b5d0614 100644
--- a/components/dom_distiller/core/dom_distiller_model.h
+++ b/components/dom_distiller/core/dom_distiller_model.h
@@ -16,7 +16,6 @@
 #include "base/macros.h"
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/sync/model/sync_change.h"
-#include "components/sync/model/sync_change_processor.h"  // syncer::SyncChangeList
 #include "components/sync/model/sync_data.h"
 #include "url/gurl.h"
 
@@ -41,8 +40,6 @@
   std::vector<ArticleEntry> GetEntries() const;
   size_t GetNumEntries() const;
 
-  syncer::SyncDataList GetAllSyncData() const;
-
   // Convert a SyncDataList to a SyncChangeList of add or update changes based
   // on the state of the model. Also calculate the entries missing from the
   // SyncDataList.
diff --git a/components/dom_distiller/core/dom_distiller_store.cc b/components/dom_distiller/core/dom_distiller_store.cc
index bfaa2b3..2cd9dce 100644
--- a/components/dom_distiller/core/dom_distiller_store.cc
+++ b/components/dom_distiller/core/dom_distiller_store.cc
@@ -11,23 +11,13 @@
 #include "base/bind.h"
 #include "base/location.h"
 #include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/sync/model/sync_change.h"
-#include "components/sync/protocol/article_specifics.pb.h"
-#include "components/sync/protocol/sync.pb.h"
 
 using leveldb_proto::ProtoDatabase;
-using sync_pb::ArticleSpecifics;
-using sync_pb::EntitySpecifics;
-using syncer::ModelType;
 using syncer::SyncChange;
 using syncer::SyncChangeList;
-using syncer::SyncData;
 using syncer::SyncDataList;
-using syncer::SyncError;
-using syncer::SyncMergeResult;
 
 namespace dom_distiller {
 
@@ -227,48 +217,17 @@
   return true;
 }
 
-SyncMergeResult DomDistillerStore::MergeDataWithModel(
-    const SyncDataList& data,
-    SyncChangeList* changes_applied,
-    SyncChangeList* changes_missing) {
+void DomDistillerStore::MergeDataWithModel(const SyncDataList& data,
+                                           SyncChangeList* changes_applied,
+                                           SyncChangeList* changes_missing) {
   // TODO(cjhopman): This naive merge algorithm could cause flip-flopping
   // between database/sync of multiple clients.
   DCHECK(changes_applied);
   DCHECK(changes_missing);
 
-  SyncMergeResult result(syncer::DEPRECATED_ARTICLES);
-  result.set_num_items_before_association(model_.GetNumEntries());
-
   SyncChangeList changes_to_apply;
   model_.CalculateChangesForMerge(data, &changes_to_apply, changes_missing);
-  SyncError error;
   ApplyChangesToModel(changes_to_apply, changes_applied, changes_missing);
-
-  int num_added = 0;
-  int num_modified = 0;
-  for (SyncChangeList::const_iterator it = changes_applied->begin();
-       it != changes_applied->end(); ++it) {
-    DCHECK(it->IsValid());
-    switch (it->change_type()) {
-      case SyncChange::ACTION_ADD:
-        num_added++;
-        break;
-      case SyncChange::ACTION_UPDATE:
-        num_modified++;
-        break;
-      default:
-        NOTREACHED();
-    }
-  }
-  result.set_num_items_added(num_added);
-  result.set_num_items_modified(num_modified);
-  result.set_num_items_deleted(0);
-
-  result.set_pre_association_version(0);
-  result.set_num_items_after_association(model_.GetNumEntries());
-  result.set_error(error);
-
-  return result;
 }
 
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/dom_distiller_store.h b/components/dom_distiller/core/dom_distiller_store.h
index 77385e1..ae2ac1f 100644
--- a/components/dom_distiller/core/dom_distiller_store.h
+++ b/components/dom_distiller/core/dom_distiller_store.h
@@ -17,15 +17,11 @@
 #include "components/leveldb_proto/public/proto_database.h"
 #include "components/sync/model/sync_change.h"
 #include "components/sync/model/sync_data.h"
-#include "components/sync/model/sync_error.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 "url/gurl.h"
 
 namespace dom_distiller {
 
-// Interface for accessing the stored/synced DomDistiller entries.
+// Interface for accessing the stored DomDistiller entries.
 class DomDistillerStoreInterface {
  public:
   virtual ~DomDistillerStoreInterface() {}
@@ -100,17 +96,9 @@
   bool ChangeEntry(const ArticleEntry& entry,
                    syncer::SyncChange::SyncChangeType changeType);
 
-  syncer::SyncMergeResult MergeDataWithModel(
-      const syncer::SyncDataList& data,
-      syncer::SyncChangeList* changes_applied,
-      syncer::SyncChangeList* changes_missing);
-
-  // Convert a SyncDataList to a SyncChangeList of add or update changes based
-  // on the state of the in-memory model. Also calculate the entries missing
-  // from the SyncDataList.
-  void CalculateChangesForMerge(const syncer::SyncDataList& data,
-                                syncer::SyncChangeList* changes_to_apply,
-                                syncer::SyncChangeList* changes_missing);
+  void MergeDataWithModel(const syncer::SyncDataList& data,
+                          syncer::SyncChangeList* changes_applied,
+                          syncer::SyncChangeList* changes_missing);
 
   bool ApplyChangesToDatabase(const syncer::SyncChangeList& change_list);
 
@@ -122,8 +110,6 @@
 
   void NotifyObservers(const syncer::SyncChangeList& changes);
 
-  std::unique_ptr<syncer::SyncChangeProcessor> sync_processor_;
-  std::unique_ptr<syncer::SyncErrorFactory> error_factory_;
   std::unique_ptr<leveldb_proto::ProtoDatabase<ArticleEntry>> database_;
   bool database_loaded_;
   base::ObserverList<DomDistillerObserver>::Unchecked observers_;
diff --git a/components/dom_distiller/core/dom_distiller_store_unittest.cc b/components/dom_distiller/core/dom_distiller_store_unittest.cc
index 7ba9ae5..32db73b 100644
--- a/components/dom_distiller/core/dom_distiller_store_unittest.cc
+++ b/components/dom_distiller/core/dom_distiller_store_unittest.cc
@@ -19,21 +19,11 @@
 #include "components/dom_distiller/core/article_entry.h"
 #include "components/dom_distiller/core/dom_distiller_test_util.h"
 #include "components/leveldb_proto/testing/fake_db.h"
-#include "components/sync/protocol/sync.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::Time;
 using leveldb_proto::test::FakeDB;
-using sync_pb::EntitySpecifics;
-using syncer::ModelType;
-using syncer::SyncChange;
-using syncer::SyncChangeList;
-using syncer::SyncChangeProcessor;
-using syncer::SyncData;
-using syncer::SyncDataList;
-using syncer::SyncError;
-using syncer::SyncErrorFactory;
 using testing::_;
 using testing::AssertionFailure;
 using testing::AssertionResult;
@@ -50,35 +40,6 @@
   (*map)[e.entry_id()] = e;
 }
 
-class FakeSyncErrorFactory : public syncer::SyncErrorFactory {
- public:
-  syncer::SyncError CreateAndUploadError(const base::Location& location,
-                                         const std::string& message) override {
-    return syncer::SyncError();
-  }
-};
-
-class FakeSyncChangeProcessor : public syncer::SyncChangeProcessor {
- public:
-  explicit FakeSyncChangeProcessor(EntryMap* model) : model_(model) {}
-
-  syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const override {
-    ADD_FAILURE() << "FakeSyncChangeProcessor::GetAllSyncData not implemented.";
-    return syncer::SyncDataList();
-  }
-
-  SyncError ProcessSyncChanges(const base::Location&,
-                               const syncer::SyncChangeList& changes) override {
-    for (auto it = changes.begin(); it != changes.end(); ++it) {
-      AddEntry(GetEntryFromChange(*it), model_);
-    }
-    return SyncError();
-  }
-
- private:
-  EntryMap* model_;
-};
-
 ArticleEntry CreateEntry(const std::string& entry_id,
                          const std::string& page_url1,
                          const std::string& page_url2,
@@ -128,9 +89,7 @@
  public:
   void SetUp() override {
     db_model_.clear();
-    sync_model_.clear();
     store_model_.clear();
-    next_sync_id_ = 1;
   }
 
   void TearDown() override {
@@ -146,31 +105,15 @@
   }
 
  protected:
-  SyncData CreateSyncData(const ArticleEntry& entry) {
-    EntitySpecifics specifics = SpecificsFromEntry(entry);
-    return SyncData::CreateRemoteData(next_sync_id_++, specifics);
-  }
-
-  SyncDataList SyncDataFromEntryMap(const EntryMap& model) {
-    SyncDataList data;
-    for (auto it = model.begin(); it != model.end(); ++it) {
-      data.push_back(CreateSyncData(it->second));
-    }
-    return data;
-  }
-
   base::test::SingleThreadTaskEnvironment task_environment_;
 
   EntryMap db_model_;
-  EntryMap sync_model_;
   FakeDB<ArticleEntry>::EntryMap store_model_;
 
   std::unique_ptr<DomDistillerStore> store_;
 
   // Both owned by |store_|.
   FakeDB<ArticleEntry>* fake_db_;
-
-  int64_t next_sync_id_;
 };
 
 AssertionResult AreEntriesEqual(const DomDistillerStore::EntryVector& entries,
diff --git a/components/domain_reliability/util.cc b/components/domain_reliability/util.cc
index 815e4ee..5c61a4e0 100644
--- a/components/domain_reliability/util.cc
+++ b/components/domain_reliability/util.cc
@@ -134,6 +134,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_46:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_47:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_48:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_49:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_99:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_999:
       return "QUIC";
diff --git a/components/download/internal/common/download_job_factory.cc b/components/download/internal/common/download_job_factory.cc
index cd6274c..daa7b44a 100644
--- a/components/download/internal/common/download_job_factory.cc
+++ b/components/download/internal/common/download_job_factory.cc
@@ -58,6 +58,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_46:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_47:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_48:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_49:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_99:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_999:
       return ConnectionType::kQUIC;
diff --git a/components/history/core/browser/history_backend.cc b/components/history/core/browser/history_backend.cc
index 2b39cfc..d4040fe46 100644
--- a/components/history/core/browser/history_backend.cc
+++ b/components/history/core/browser/history_backend.cc
@@ -1427,11 +1427,8 @@
       url_filter);
 
   MostVisitedURLList result;
-  for (const std::unique_ptr<PageUsageData>& current_data : data) {
-    RedirectList redirects = QueryRedirectsFrom(current_data->GetURL());
-    result.emplace_back(current_data->GetURL(), current_data->GetTitle(),
-                        redirects);
-  }
+  for (const std::unique_ptr<PageUsageData>& current_data : data)
+    result.emplace_back(current_data->GetURL(), current_data->GetTitle());
 
   UMA_HISTOGRAM_TIMES("History.QueryMostVisitedURLsTime",
                       base::TimeTicks::Now() - begin_time);
diff --git a/components/history/core/browser/history_service_unittest.cc b/components/history/core/browser/history_service_unittest.cc
index 1baac2af..4c54e51 100644
--- a/components/history/core/browser/history_service_unittest.cc
+++ b/components/history/core/browser/history_service_unittest.cc
@@ -556,7 +556,6 @@
   EXPECT_EQ(url2, most_visited_urls_[1].url);
   EXPECT_EQ(url0, most_visited_urls_[2].url);
   EXPECT_EQ(url3, most_visited_urls_[3].url);
-  EXPECT_EQ(2U, most_visited_urls_[3].redirects.size());
 }
 
 namespace {
diff --git a/components/history/core/browser/history_types.cc b/components/history/core/browser/history_types.cc
index 530831a..7915c4b 100644
--- a/components/history/core/browser/history_types.cc
+++ b/components/history/core/browser/history_types.cc
@@ -195,33 +195,11 @@
 MostVisitedURL::MostVisitedURL(const GURL& url, const base::string16& title)
     : url(url), title(title) {}
 
-MostVisitedURL::MostVisitedURL(const GURL& url,
-                               const base::string16& title,
-                               const RedirectList& preceding_redirects)
-    : url(url), title(title) {
-  InitRedirects(preceding_redirects);
-}
-
 MostVisitedURL::MostVisitedURL(const MostVisitedURL& other) = default;
 
 MostVisitedURL::MostVisitedURL(MostVisitedURL&& other) noexcept = default;
 
-MostVisitedURL::~MostVisitedURL() {}
-
-void MostVisitedURL::InitRedirects(const RedirectList& redirects_from) {
-  redirects.clear();
-
-  if (redirects_from.empty()) {
-    // Redirects must contain at least the target URL.
-    redirects.push_back(url);
-  } else {
-    redirects = redirects_from;
-    if (redirects.back() != url) {
-      // The last url must be the target URL.
-      redirects.push_back(url);
-    }
-  }
-}
+MostVisitedURL::~MostVisitedURL() = default;
 
 MostVisitedURL& MostVisitedURL::operator=(const MostVisitedURL&) = default;
 
diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h
index 63f801d..3e57793 100644
--- a/components/history/core/browser/history_types.h
+++ b/components/history/core/browser/history_types.h
@@ -305,27 +305,18 @@
 struct MostVisitedURL {
   MostVisitedURL();
   MostVisitedURL(const GURL& url, const base::string16& title);
-  MostVisitedURL(const GURL& url,
-                 const base::string16& title,
-                 const RedirectList& preceding_redirects);
   MostVisitedURL(const MostVisitedURL& other);
   MostVisitedURL(MostVisitedURL&& other) noexcept;
   ~MostVisitedURL();
 
-  // Initializes |redirects| from |preceding_redirects|, ensuring that |url| is
-  // always present as the last item.
-  void InitRedirects(const RedirectList& preceding_redirects);
-
-  GURL url;
-  base::string16 title;
-
-  RedirectList redirects;
-
   MostVisitedURL& operator=(const MostVisitedURL&);
 
   bool operator==(const MostVisitedURL& other) const {
     return url == other.url;
   }
+
+  GURL url;
+  base::string16 title;
 };
 
 // FilteredURL -----------------------------------------------------------------
diff --git a/components/history/core/browser/top_sites.cc b/components/history/core/browser/top_sites.cc
index 3f5ead1c..5446d5c 100644
--- a/components/history/core/browser/top_sites.cc
+++ b/components/history/core/browser/top_sites.cc
@@ -17,14 +17,11 @@
     : most_visited(url, title),
       favicon_id(favicon_id),
       color(color) {
-  most_visited.redirects.push_back(url);
 }
 
-TopSites::TopSites() {
-}
+TopSites::TopSites() = default;
 
-TopSites::~TopSites() {
-}
+TopSites::~TopSites() = default;
 
 void TopSites::AddObserver(TopSitesObserver* observer) {
   observer_list_.AddObserver(observer);
diff --git a/components/history/core/browser/top_sites_database.cc b/components/history/core/browser/top_sites_database.cc
index 42607274..54fcfbe 100644
--- a/components/history/core/browser/top_sites_database.cc
+++ b/components/history/core/browser/top_sites_database.cc
@@ -32,7 +32,8 @@
 //                    will be the next one evicted.
 //   title            The title to display under that site.
 //   redirects        A space separated list of URLs that are known to redirect
-//                    to this url.
+//                    to this url. As of 9/2019 this column is not used. It will
+//                    be removed shortly.
 
 namespace {
 
@@ -67,25 +68,6 @@
   return db->Execute(kTopSitesSql);
 }
 
-// Encodes redirects into a string.
-std::string GetRedirects(const MostVisitedURL& url) {
-  std::vector<base::StringPiece> redirects;
-  for (const auto& redirect : url.redirects)
-    redirects.push_back(redirect.spec());
-  return base::JoinString(redirects, " ");
-}
-
-// Decodes redirects from a string and sets them for the url.
-void SetRedirects(const std::string& redirects, MostVisitedURL* url) {
-  for (const std::string& redirect : base::SplitString(
-           redirects, base::kWhitespaceASCII,
-           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
-    GURL redirect_url(redirect);
-    if (redirect_url.is_valid())
-      url->redirects.push_back(redirect_url);
-  }
-}
-
 // Track various failure (and success) cases in recovery code.
 //
 // TODO(shess): The recovery code is complete, but by nature runs in challenging
@@ -430,7 +412,7 @@
 void TopSitesDatabase::GetSites(MostVisitedURLList* urls) {
   sql::Statement statement(
       db_->GetCachedStatement(SQL_FROM_HERE,
-                              "SELECT url, url_rank, title, redirects "
+                              "SELECT url, url_rank, title "
                               "FROM top_sites ORDER BY url_rank"));
 
   if (!statement.is_valid()) {
@@ -446,8 +428,6 @@
     GURL gurl(statement.ColumnString(0));
     url.url = gurl;
     url.title = statement.ColumnString16(2);
-    std::string redirects = statement.ColumnString(3);
-    SetRedirects(redirects, &url);
     urls->push_back(url);
   }
 }
@@ -467,12 +447,11 @@
   sql::Statement statement(
       db_->GetCachedStatement(SQL_FROM_HERE,
                               "INSERT OR REPLACE INTO top_sites "
-                              "(url, url_rank, title, redirects) "
-                              "VALUES (?, ?, ?, ?)"));
+                              "(url, url_rank, title) "
+                              "VALUES (?, ?, ?)"));
   statement.BindString(0, url.url.spec());
   statement.BindInt(1, kRankOfNewURL);
   statement.BindString16(2, url.title);
-  statement.BindString(3, GetRedirects(url));
   if (!statement.Run())
     return;
 
@@ -483,10 +462,9 @@
 bool TopSitesDatabase::UpdateSite(const MostVisitedURL& url) {
   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
                                                    "UPDATE top_sites SET "
-                                                   "title = ?, redirects = ?"
+                                                   "title = ? "
                                                    "WHERE url = ?"));
   statement.BindString16(0, url.title);
-  statement.BindString(1, GetRedirects(url));
 
   return statement.Run();
 }
diff --git a/components/history/core/browser/top_sites_impl_unittest.cc b/components/history/core/browser/top_sites_impl_unittest.cc
index ffd99d1..f06649b4 100644
--- a/components/history/core/browser/top_sites_impl_unittest.cc
+++ b/components/history/core/browser/top_sites_impl_unittest.cc
@@ -160,34 +160,17 @@
   }
 
   // Adds a page to history.
-  void AddPageToHistory(const GURL& url) {
-    RedirectList redirects;
-    redirects.push_back(url);
-    history_service()->AddPage(
-        url, base::Time::Now(), reinterpret_cast<ContextID>(1), 0, GURL(),
-        redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false);
-  }
-
-  // Adds a page to history.
-  void AddPageToHistory(const GURL& url, const base::string16& title) {
-    RedirectList redirects;
-    redirects.push_back(url);
-    history_service()->AddPage(
-        url, base::Time::Now(), reinterpret_cast<ContextID>(1), 0, GURL(),
-        redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false);
-    history_service()->SetPageTitle(url, title);
-  }
-
-  // Adds a page to history.
   void AddPageToHistory(const GURL& url,
-                        const base::string16& title,
-                        const history::RedirectList& redirects,
-                        base::Time time) {
-    history_service()->AddPage(
-        url, time, reinterpret_cast<ContextID>(1), 0, GURL(),
-        redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED,
-        false);
-    history_service()->SetPageTitle(url, title);
+                        const base::string16& title = base::string16(),
+                        base::Time time = base::Time::Now(),
+                        RedirectList redirects = RedirectList()) {
+    if (redirects.empty())
+      redirects.emplace_back(url);
+    history_service()->AddPage(url, time, reinterpret_cast<ContextID>(1), 0,
+                               GURL(), redirects, ui::PAGE_TRANSITION_TYPED,
+                               history::SOURCE_BROWSED, false);
+    if (!title.empty())
+      history_service()->SetPageTitle(url, title);
   }
 
   // Delets a url.
@@ -269,39 +252,6 @@
   DISALLOW_COPY_AND_ASSIGN(TopSitesImplTest);
 };  // Class TopSitesImplTest
 
-// Helper function for appending a URL to a vector of "most visited" URLs,
-// using the default values for everything but the URL.
-void AppendMostVisitedURL(const GURL& url, std::vector<MostVisitedURL>* list) {
-  MostVisitedURL mv;
-  mv.url = url;
-  mv.redirects.push_back(url);
-  list->push_back(mv);
-}
-
-// Same as AppendMostVisitedURL except that it adds a redirect from the first
-// URL to the second.
-void AppendMostVisitedURLWithRedirect(const GURL& redirect_source,
-                                      const GURL& redirect_dest,
-                                      std::vector<MostVisitedURL>* list) {
-  MostVisitedURL mv;
-  mv.url = redirect_dest;
-  mv.redirects.push_back(redirect_source);
-  mv.redirects.push_back(redirect_dest);
-  list->push_back(mv);
-}
-
-// Helper function for appending a URL to a vector of "most visited" URLs,
-// using the default values for everything but the URL and the title.
-void AppendMostVisitedURLwithTitle(const GURL& url,
-                                   const base::string16& title,
-                                   std::vector<MostVisitedURL>* list) {
-  MostVisitedURL mv;
-  mv.url = url;
-  mv.title = title;
-  mv.redirects.push_back(url);
-  list->push_back(mv);
-}
-
 class MockTopSitesObserver : public TopSitesObserver {
  public:
   MockTopSitesObserver() {}
@@ -334,7 +284,7 @@
 
   // TopSites has a new list of sites and should notify its observers.
   std::vector<MostVisitedURL> list_1;
-  AppendMostVisitedURLwithTitle(url_1, title_1, &list_1);
+  list_1.emplace_back(url_1, title_1);
   SetTopSites(list_1);
   EXPECT_TRUE(observer.is_notified());
   observer.ResetIsNotifiedState();
@@ -343,8 +293,8 @@
   // list_1 and list_2 have different sizes. TopSites should notify its
   // observers.
   std::vector<MostVisitedURL> list_2;
-  AppendMostVisitedURLwithTitle(url_1, title_1, &list_2);
-  AppendMostVisitedURLwithTitle(url_2, title_2, &list_2);
+  list_2.emplace_back(url_1, title_1);
+  list_2.emplace_back(url_2, title_2);
   SetTopSites(list_2);
   EXPECT_TRUE(observer.is_notified());
   observer.ResetIsNotifiedState();
@@ -352,14 +302,14 @@
 
   // list_1 and list_2 are exactly the same now. TopSites should not notify its
   // observers.
-  AppendMostVisitedURLwithTitle(url_2, title_2, &list_1);
+  list_1.emplace_back(url_2, title_2);
   SetTopSites(list_1);
   EXPECT_FALSE(observer.is_notified());
 
   // Change |url_2|'s title to |title_1| in list_2. The two lists are different
   // in titles now. TopSites should notify its observers.
   list_2.pop_back();
-  AppendMostVisitedURLwithTitle(url_2, title_1, &list_2);
+  list_2.emplace_back(url_2, title_1);
   SetTopSites(list_2);
   EXPECT_TRUE(observer.is_notified());
 
@@ -375,15 +325,15 @@
   GURL gets_moved_1("http://getsmoved1/");
 
   std::vector<MostVisitedURL> old_list;
-  AppendMostVisitedURL(stays_the_same, &old_list);  // 0  (unchanged)
-  AppendMostVisitedURL(gets_deleted_1, &old_list);  // 1  (deleted)
-  AppendMostVisitedURL(gets_moved_1, &old_list);    // 2  (moved to 3)
+  old_list.emplace_back(stays_the_same, base::string16());  // 0  (unchanged)
+  old_list.emplace_back(gets_deleted_1, base::string16());  // 1  (deleted)
+  old_list.emplace_back(gets_moved_1, base::string16());    // 2  (moved to 3)
 
   std::vector<MostVisitedURL> new_list;
-  AppendMostVisitedURL(stays_the_same, &new_list);  // 0  (unchanged)
-  AppendMostVisitedURL(gets_added_1, &new_list);    // 1  (added)
-  AppendMostVisitedURL(gets_added_2, &new_list);    // 2  (added)
-  AppendMostVisitedURL(gets_moved_1, &new_list);    // 3  (moved from 2)
+  new_list.emplace_back(stays_the_same, base::string16());  // 0  (unchanged)
+  new_list.emplace_back(gets_added_1, base::string16());    // 1  (added)
+  new_list.emplace_back(gets_added_2, base::string16());    // 2  (added)
+  new_list.emplace_back(gets_moved_1, base::string16());    // 3  (moved from 2)
 
   history::TopSitesDelta delta;
   TopSitesImpl::DiffMostVisited(old_list, new_list, &delta);
@@ -431,9 +381,8 @@
   GURL www("https://www.cnn.com/");
   GURL edition("https://edition.cnn.com/");
 
-  AddPageToHistory(edition, base::ASCIIToUTF16("CNN"),
-                   history::RedirectList{bare, www, edition},
-                   base::Time::Now());
+  AddPageToHistory(edition, base::ASCIIToUTF16("CNN"), base::Time::Now(),
+                   history::RedirectList{bare, www, edition});
   AddPageToHistory(edition);
 
   StartQueryForMostVisited();
@@ -492,7 +441,6 @@
   MostVisitedURL url2;
   url2.url = google_url;
   url2.title = google_title;
-  url2.redirects.push_back(url2.url);
 
   AddPageToHistory(url2.url, url2.title);
 
@@ -525,10 +473,9 @@
 
   url.url = asdf_url;
   url.title = asdf_title;
-  url.redirects.push_back(url.url);
 
   base::Time add_time(base::Time::Now());
-  AddPageToHistory(url.url, url.title, url.redirects, add_time);
+  AddPageToHistory(url.url, url.title, add_time);
 
   RefreshTopSitesAndRecreate();
 
@@ -545,15 +492,16 @@
   MostVisitedURL url2;
   url2.url = google3_url;
   url2.title = google_title;
-  url2.redirects.push_back(google1_url);
-  url2.redirects.push_back(google2_url);
-  url2.redirects.push_back(google3_url);
+  history::RedirectList url2_redirects;
+  url2_redirects.push_back(google1_url);
+  url2_redirects.push_back(google2_url);
+  url2_redirects.push_back(google3_url);
 
-  AddPageToHistory(google3_url, url2.title, url2.redirects,
-                   add_time - base::TimeDelta::FromMinutes(1));
+  AddPageToHistory(google3_url, url2.title,
+                   add_time - base::TimeDelta::FromMinutes(1), url2_redirects);
   // Add google twice so that it becomes the first visited site.
-  AddPageToHistory(google3_url, url2.title, url2.redirects,
-                   add_time - base::TimeDelta::FromMinutes(2));
+  AddPageToHistory(google3_url, url2.title,
+                   add_time - base::TimeDelta::FromMinutes(2), url2_redirects);
 
   RefreshTopSitesAndRecreate();
 
@@ -565,7 +513,6 @@
     ASSERT_EQ(2u + GetPrepopulatedPages().size(), querier.urls().size());
     EXPECT_EQ(google1_url, querier.urls()[0].url);
     EXPECT_EQ(google_title, querier.urls()[0].title);
-    ASSERT_EQ(3u, querier.urls()[0].redirects.size());
 
     EXPECT_EQ(asdf_url, querier.urls()[1].url);
     EXPECT_EQ(asdf_title, querier.urls()[1].title);
@@ -687,10 +634,8 @@
   MostVisitedURLList pages;
   MostVisitedURL url;
   url.url = GURL("http://1.com/");
-  url.redirects.push_back(url.url);
   pages.push_back(url);
   url.url = GURL("http://2.com/");
-  url.redirects.push_back(url.url);
   pages.push_back(url);
   SetTopSites(pages);
 
@@ -720,7 +665,6 @@
 
   // Reset the top sites again, this time don't reload.
   url.url = GURL("http://3.com/");
-  url.redirects.push_back(url.url);
   pages.push_back(url);
   SetTopSites(pages);
 
@@ -774,10 +718,8 @@
   MostVisitedURLList pages;
   MostVisitedURL url, url1;
   url.url = GURL("http://bbc.com/");
-  url.redirects.push_back(url.url);
   pages.push_back(url);
   url1.url = GURL("http://google.com/");
-  url1.redirects.push_back(url1.url);
   pages.push_back(url1);
 
   SetTopSites(pages);
@@ -838,10 +780,8 @@
   MostVisitedURLList pages;
   MostVisitedURL url, url1;
   url.url = GURL("http://bbc.com/");
-  url.redirects.push_back(url.url);
   pages.push_back(url);
   url1.url = GURL("http://google.com/");
-  url1.redirects.push_back(url1.url);
   pages.push_back(url1);
 
   SetTopSites(pages);
diff --git a/components/offline_pages/core/offline_clock.cc b/components/offline_pages/core/offline_clock.cc
index 60fc4b0..e39d794b 100644
--- a/components/offline_pages/core/offline_clock.cc
+++ b/components/offline_pages/core/offline_clock.cc
@@ -11,16 +11,16 @@
 namespace offline_pages {
 
 namespace {
-base::Clock* custom_clock_ = nullptr;
+const base::Clock* custom_clock_ = nullptr;
 }
 
-base::Clock* OfflineClock() {
+const base::Clock* OfflineClock() {
   if (custom_clock_)
     return custom_clock_;
   return base::DefaultClock::GetInstance();
 }
 
-void SetOfflineClockForTesting(base::Clock* clock) {
+void SetOfflineClockForTesting(const base::Clock* clock) {
   DCHECK(clock == nullptr || custom_clock_ == nullptr)
       << "Offline clock is being overridden a second time, which might "
          "indicate a bug.";
diff --git a/components/offline_pages/core/offline_clock.h b/components/offline_pages/core/offline_clock.h
index 5cf384c..695ee67 100644
--- a/components/offline_pages/core/offline_clock.h
+++ b/components/offline_pages/core/offline_clock.h
@@ -14,11 +14,11 @@
 
 // Returns the clock to be used for obtaining the current time. This function
 // can be called from any threads.
-base::Clock* OfflineClock();
+const base::Clock* OfflineClock();
 
 // Allows tests to override the clock returned by |OfflineClock()|. For safety,
 // use |TestScopedOfflineClock| instead if possible.
-void SetOfflineClockForTesting(base::Clock* clock);
+void SetOfflineClockForTesting(const base::Clock* clock);
 
 // Returns the current time given by |OfflineClock|. This used as a shortcut
 // for calls to |OfflineClock()->Now()|
diff --git a/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.cc b/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.cc
index c6b4ac7..3f6edc3 100644
--- a/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.cc
+++ b/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.cc
@@ -36,7 +36,7 @@
 }  // namespace
 
 PrefetchDownloaderQuota::PrefetchDownloaderQuota(sql::Database* db,
-                                                 base::Clock* clock)
+                                                 const base::Clock* clock)
     : db_(db), clock_(clock) {
   DCHECK(db_);
   DCHECK(clock_);
diff --git a/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.h b/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.h
index 7abb791..411aafff 100644
--- a/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.h
+++ b/components/offline_pages/core/prefetch/store/prefetch_downloader_quota.h
@@ -25,7 +25,7 @@
   // Public for unit tests.
   static const int64_t kDefaultMaxDailyQuotaBytes;
 
-  PrefetchDownloaderQuota(sql::Database* db, base::Clock* clock);
+  PrefetchDownloaderQuota(sql::Database* db, const base::Clock* clock);
   ~PrefetchDownloaderQuota();
 
   // Gets the max daily quota from Finch.
@@ -44,7 +44,7 @@
   sql::Database* db_;
 
   // Clock used for time related calculation and quota updates in DB. Not owned.
-  base::Clock* clock_;
+  const base::Clock* clock_;
 
   DISALLOW_COPY_AND_ASSIGN(PrefetchDownloaderQuota);
 };
diff --git a/components/offline_pages/core/test_scoped_offline_clock.cc b/components/offline_pages/core/test_scoped_offline_clock.cc
index 1374c3d9..0166d64 100644
--- a/components/offline_pages/core/test_scoped_offline_clock.cc
+++ b/components/offline_pages/core/test_scoped_offline_clock.cc
@@ -9,7 +9,7 @@
 namespace offline_pages {
 
 TestScopedOfflineClockOverride::TestScopedOfflineClockOverride(
-    base::Clock* clock) {
+    const base::Clock* clock) {
   SetOfflineClockForTesting(clock);
 }
 
diff --git a/components/offline_pages/core/test_scoped_offline_clock.h b/components/offline_pages/core/test_scoped_offline_clock.h
index 00a5775..322afe9 100644
--- a/components/offline_pages/core/test_scoped_offline_clock.h
+++ b/components/offline_pages/core/test_scoped_offline_clock.h
@@ -14,7 +14,7 @@
 // |OfflineClock()| to its original state upon destruction.
 class TestScopedOfflineClockOverride {
  public:
-  explicit TestScopedOfflineClockOverride(base::Clock* clock);
+  explicit TestScopedOfflineClockOverride(const base::Clock* clock);
   ~TestScopedOfflineClockOverride();
 
  private:
diff --git a/components/page_load_metrics/browser/protocol_util.cc b/components/page_load_metrics/browser/protocol_util.cc
index 3769048..3dc0c31 100644
--- a/components/page_load_metrics/browser/protocol_util.cc
+++ b/components/page_load_metrics/browser/protocol_util.cc
@@ -40,6 +40,7 @@
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_46:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_47:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_48:
+    case net::HttpResponseInfo::CONNECTION_INFO_QUIC_49:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_99:
     case net::HttpResponseInfo::CONNECTION_INFO_QUIC_999:
       return NetworkProtocol::kQuic;
diff --git a/components/paint_preview/common/BUILD.gn b/components/paint_preview/common/BUILD.gn
index 270c99e..a8ab99b 100644
--- a/components/paint_preview/common/BUILD.gn
+++ b/components/paint_preview/common/BUILD.gn
@@ -11,6 +11,8 @@
       "file_stream.h",
       "glyph_usage.cc",
       "glyph_usage.h",
+      "serial_utils.cc",
+      "serial_utils.h",
       "subset_font.cc",
       "subset_font.h",
     ]
@@ -28,6 +30,7 @@
     sources = [
       "file_stream_unittest.cc",
       "glyph_usage_unittest.cc",
+      "serial_utils_unittest.cc",
       "subset_font_unittest.cc",
     ]
 
diff --git a/components/paint_preview/common/serial_utils.cc b/components/paint_preview/common/serial_utils.cc
new file mode 100644
index 0000000..e74c492
--- /dev/null
+++ b/components/paint_preview/common/serial_utils.cc
@@ -0,0 +1,99 @@
+// Copyright 2019 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/paint_preview/common/serial_utils.h"
+
+#include "components/paint_preview/common/subset_font.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+
+namespace paint_preview {
+
+namespace {
+
+// Serializes a SkPicture representing a subframe as a custom data placeholder.
+sk_sp<SkData> SerializeSubframe(SkPicture* picture, void* ctx) {
+  const PictureSerializationContext* context =
+      reinterpret_cast<PictureSerializationContext*>(ctx);
+  uint32_t content_id = picture->uniqueID();
+  if (context->count(content_id))
+    return SkData::MakeWithCopy(&content_id, sizeof(content_id));
+  // Defers picture serialization behavior to Skia.
+  return nullptr;
+}
+
+// De-duplicates and subsets used typefaces and discards any unused typefaces.
+sk_sp<SkData> SerializeTypeface(SkTypeface* typeface, void* ctx) {
+  TypefaceSerializationContext* context =
+      reinterpret_cast<TypefaceSerializationContext*>(ctx);
+
+  if (context->finished.count(typeface->uniqueID()))
+    return typeface->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
+  context->finished.insert(typeface->uniqueID());
+
+  auto usage_it = context->usage->find(typeface->uniqueID());
+  if (usage_it == context->usage->end())
+    return typeface->serialize(SkTypeface::SerializeBehavior::kDontIncludeData);
+
+  auto subset_data = SubsetFont(typeface, *usage_it->second);
+  // This will fail if the font cannot be subsetted properly. In such cases
+  // all typeface data should be added for portability.
+  if (!subset_data)
+    return typeface->serialize(SkTypeface::SerializeBehavior::kDoIncludeData);
+  return subset_data;
+}
+
+// Deserializies a SkPicture within the main SkPicture. These represent
+// subframes and require special decoding as they are custom data rather than a
+// valid SkPicture.
+// Precondition: the version of the SkPicture should be checked prior to
+// invocation to ensure deserialization will succeed.
+sk_sp<SkPicture> DeserializeSubframe(const void* data,
+                                     size_t length,
+                                     void* ctx) {
+  uint32_t content_id;
+  if (length < sizeof(content_id))
+    return MakeEmptyPicture();
+  memcpy(&content_id, data, sizeof(content_id));
+  auto* context = reinterpret_cast<DeserializationContext*>(ctx);
+  auto it = context->find(content_id);
+  if (it == context->end() || !it->second)
+    return MakeEmptyPicture();
+  return it->second;
+}
+
+}  // namespace
+
+TypefaceSerializationContext::TypefaceSerializationContext(
+    TypefaceUsageMap* usage)
+    : usage(usage) {}
+TypefaceSerializationContext::~TypefaceSerializationContext() = default;
+
+sk_sp<SkPicture> MakeEmptyPicture() {
+  // Effectively a no-op.
+  SkPictureRecorder rec;
+  rec.beginRecording(1, 1);
+  return rec.finishRecordingAsPicture();
+}
+
+SkSerialProcs MakeSerialProcs(PictureSerializationContext* picture_ctx,
+                              TypefaceSerializationContext* typeface_ctx) {
+  SkSerialProcs procs;
+  procs.fPictureProc = SerializeSubframe;
+  procs.fPictureCtx = picture_ctx;
+  procs.fTypefaceProc = SerializeTypeface;
+  procs.fTypefaceCtx = typeface_ctx;
+  // TODO(crbug/1008875): find a consistently smaller and low-memory overhead
+  // image downsampling method to use as fImageProc.
+  return procs;
+}
+
+SkDeserialProcs MakeDeserialProcs(DeserializationContext* ctx) {
+  SkDeserialProcs procs;
+  procs.fPictureProc = DeserializeSubframe;
+  procs.fPictureCtx = ctx;
+  return procs;
+}
+
+}  // namespace paint_preview
diff --git a/components/paint_preview/common/serial_utils.h b/components/paint_preview/common/serial_utils.h
new file mode 100644
index 0000000..5a61c4af
--- /dev/null
+++ b/components/paint_preview/common/serial_utils.h
@@ -0,0 +1,53 @@
+// Copyright 2019 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_PAINT_PREVIEW_COMMON_SERIAL_UTILS_H_
+#define COMPONENTS_PAINT_PREVIEW_COMMON_SERIAL_UTILS_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/files/file.h"
+#include "components/paint_preview/common/glyph_usage.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSerialProcs.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace paint_preview {
+
+// Maps a content ID to a frame ID (Process ID || Routing ID).
+using PictureSerializationContext = base::flat_map<uint32_t, int64_t>;
+
+// Maps a typeface ID to a glyph usage tracker.
+using TypefaceUsageMap = base::flat_map<SkFontID, std::unique_ptr<GlyphUsage>>;
+
+// Tracks typeface deduplication and handles subsetting.
+struct TypefaceSerializationContext {
+  TypefaceSerializationContext(TypefaceUsageMap* usage);
+  ~TypefaceSerializationContext();
+
+  TypefaceUsageMap* usage;
+  base::flat_set<SkFontID> finished;  // Should be empty on first use.
+};
+
+// Maps a content ID to a SkPicture.
+using DeserializationContext = base::flat_map<uint32_t, sk_sp<SkPicture>>;
+
+// Creates a no-op SkPicture.
+sk_sp<SkPicture> MakeEmptyPicture();
+
+// Creates a SkSerialProcs object. The object *does not* copy |picture_ctx| or
+// |typeface_ctx| so they must outlive the use of the returned object.
+SkSerialProcs MakeSerialProcs(PictureSerializationContext* picture_ctx,
+                              TypefaceSerializationContext* typeface_ctx);
+
+// Creates a SkDeserialProcs object. The object *does not* copy |ctx| so |ctx|
+// must outlive the use of the returned object.
+SkDeserialProcs MakeDeserialProcs(DeserializationContext* ctx);
+
+}  // namespace paint_preview
+
+#endif  // COMPONENTS_PAINT_PREVIEW_COMMON_SERIAL_UTILS_H_
diff --git a/components/paint_preview/common/serial_utils_unittest.cc b/components/paint_preview/common/serial_utils_unittest.cc
new file mode 100644
index 0000000..5795f11
--- /dev/null
+++ b/components/paint_preview/common/serial_utils_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2019 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/paint_preview/common/serial_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSerialProcs.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
+namespace paint_preview {
+
+TEST(PaintPreviewSerialUtils, TestMakeEmptyPicture) {
+  sk_sp<SkPicture> pic = MakeEmptyPicture();
+  ASSERT_NE(pic, nullptr);
+  auto data = pic->serialize();
+  ASSERT_NE(data, nullptr);
+  EXPECT_GE(data->size(), 0U);
+}
+
+TEST(PaintPreviewSerialUtils, TestPictureProcs) {
+  auto pic = MakeEmptyPicture();
+  uint32_t content_id = pic->uniqueID();
+  const uint64_t kFrameGuid = 2;
+  PictureSerializationContext picture_ctx;
+  EXPECT_TRUE(
+      picture_ctx.insert(std::make_pair(content_id, kFrameGuid)).second);
+
+  DeserializationContext deserial_ctx;
+  EXPECT_TRUE(deserial_ctx.insert(std::make_pair(content_id, pic)).second);
+
+  TypefaceUsageMap usage_map;
+  TypefaceSerializationContext typeface_ctx(&usage_map);
+
+  SkSerialProcs serial_procs = MakeSerialProcs(&picture_ctx, &typeface_ctx);
+  EXPECT_EQ(serial_procs.fPictureCtx, &picture_ctx);
+  EXPECT_EQ(serial_procs.fTypefaceCtx, &typeface_ctx);
+
+  SkDeserialProcs deserial_procs = MakeDeserialProcs(&deserial_ctx);
+  EXPECT_EQ(deserial_procs.fPictureCtx, &deserial_ctx);
+
+  // Check that serializing then deserialize the picture works.
+  sk_sp<SkData> serial_pic_data =
+      serial_procs.fPictureProc(pic.get(), serial_procs.fPictureCtx);
+  sk_sp<SkPicture> deserial_pic = deserial_procs.fPictureProc(
+      serial_pic_data->data(), serial_pic_data->size(),
+      deserial_procs.fPictureCtx);
+  EXPECT_EQ(deserial_pic->uniqueID(), content_id);
+}
+
+TEST(PaintPreviewSerialUtils, TestSerialPictureNotInMap) {
+  PictureSerializationContext picture_ctx;
+
+  TypefaceUsageMap usage_map;
+  TypefaceSerializationContext typeface_ctx(&usage_map);
+
+  SkSerialProcs serial_procs = MakeSerialProcs(&picture_ctx, &typeface_ctx);
+  EXPECT_EQ(serial_procs.fPictureCtx, &picture_ctx);
+  EXPECT_EQ(serial_procs.fTypefaceCtx, &typeface_ctx);
+
+  auto pic = MakeEmptyPicture();
+  EXPECT_EQ(serial_procs.fPictureProc(pic.get(), serial_procs.fPictureCtx),
+            nullptr);
+}
+
+TEST(PaintPreviewSerialUtils, TestDeserialPictureNotInMap) {
+  uint32_t empty_content_id = 5;
+  DeserializationContext deserial_ctx;
+  EXPECT_TRUE(
+      deserial_ctx.insert(std::make_pair(empty_content_id, nullptr)).second);
+  SkDeserialProcs deserial_procs = MakeDeserialProcs(&deserial_ctx);
+  EXPECT_EQ(deserial_procs.fPictureCtx, &deserial_ctx);
+
+  sk_sp<SkPicture> deserial_pic =
+      deserial_procs.fPictureProc(nullptr, 0U, deserial_procs.fPictureCtx);
+  EXPECT_NE(deserial_pic, nullptr);  // Produce empty pic rather than nullptr.
+
+  uint32_t missing_content_id = 5;
+  deserial_pic = deserial_procs.fPictureProc(&missing_content_id,
+                                             sizeof(missing_content_id),
+                                             deserial_procs.fPictureCtx);
+  EXPECT_NE(deserial_pic, nullptr);  // Produce empty pic rather than nullptr.
+
+  deserial_pic = deserial_procs.fPictureProc(
+      &empty_content_id, sizeof(empty_content_id), deserial_procs.fPictureCtx);
+  EXPECT_NE(deserial_pic, nullptr);  // Produce empty pic rather than nullptr.
+}
+
+TEST(PaintPreviewSerialUtils, TestSerialTypeface) {
+  PictureSerializationContext picture_ctx;
+
+  auto typeface = SkTypeface::MakeDefault();
+  TypefaceUsageMap usage_map;
+  std::unique_ptr<GlyphUsage> usage =
+      std::make_unique<SparseGlyphUsage>(typeface->countGlyphs());
+  usage->Set(0);
+  usage->Set('a');
+  usage->Set('b');
+  EXPECT_TRUE(
+      usage_map.insert(std::make_pair(typeface->uniqueID(), std::move(usage)))
+          .second);
+  TypefaceSerializationContext typeface_ctx(&usage_map);
+
+  SkSerialProcs serial_procs = MakeSerialProcs(&picture_ctx, &typeface_ctx);
+  EXPECT_EQ(serial_procs.fPictureCtx, &picture_ctx);
+  EXPECT_EQ(serial_procs.fTypefaceCtx, &typeface_ctx);
+
+  EXPECT_NE(
+      serial_procs.fTypefaceProc(typeface.get(), serial_procs.fTypefaceCtx),
+      nullptr);
+  EXPECT_GT(typeface_ctx.finished.count(typeface->uniqueID()), 0U);
+}
+
+TEST(PaintPreviewSerialUtils, TestSerialNoTypefaceInMap) {
+  PictureSerializationContext picture_ctx;
+
+  auto typeface = SkTypeface::MakeDefault();
+  TypefaceUsageMap usage_map;
+  TypefaceSerializationContext typeface_ctx(&usage_map);
+
+  SkSerialProcs serial_procs = MakeSerialProcs(&picture_ctx, &typeface_ctx);
+  EXPECT_EQ(serial_procs.fPictureCtx, &picture_ctx);
+  EXPECT_EQ(serial_procs.fTypefaceCtx, &typeface_ctx);
+
+  EXPECT_NE(
+      serial_procs.fTypefaceProc(typeface.get(), serial_procs.fTypefaceCtx),
+      nullptr);
+  EXPECT_GT(typeface_ctx.finished.count(typeface->uniqueID()), 0U);
+  // Still serialize font metadata for lookup if the font is serialized again.
+  EXPECT_NE(
+      serial_procs.fTypefaceProc(typeface.get(), serial_procs.fTypefaceCtx),
+      nullptr);
+}
+
+}  // namespace paint_preview
diff --git a/components/password_manager/content/browser/content_password_manager_driver.h b/components/password_manager/content/browser/content_password_manager_driver.h
index e9bece9..46e35d2 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.h
+++ b/components/password_manager/content/browser/content_password_manager_driver.h
@@ -13,7 +13,6 @@
 #include "base/macros.h"
 #include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
 #include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "components/autofill/core/common/password_form_generation_data.h"
 #include "components/password_manager/core/browser/password_autofill_manager.h"
 #include "components/password_manager/core/browser/password_generation_frame_helper.h"
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
index 447b6f3c..6bcae96 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -421,8 +421,8 @@
 TEST(FormParserTest, NotPasswordForm) {
   CheckTestData({
       {
-          "No fields",
-          {},
+          .description_for_logging = "No fields",
+          .fields = {},
       },
       {
           .description_for_logging = "No password fields",
@@ -440,13 +440,15 @@
 TEST(FormParserTest, SkipNotTextFields) {
   CheckTestData({
       {
-          "A 'select' between username and password fields",
-          {
-              {.role = ElementRole::USERNAME},
-              {.form_control_type = "select"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "A 'select' between username and password fields",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME},
+                  {.form_control_type = "select"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
           .number_of_all_possible_passwords = 1,
           .number_of_all_possible_usernames = 1,
       },
@@ -466,42 +468,48 @@
           .number_of_all_possible_usernames = 0,
       },
       {
-          "2 password fields, new and confirmation password",
-          {
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw"},
-              {.role = ElementRole::CONFIRMATION_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw"},
-          },
+          .description_for_logging =
+              "2 password fields, new and confirmation password",
+          .fields =
+              {
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw"},
+                  {.role = ElementRole::CONFIRMATION_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw"},
+              },
           .is_new_password_reliable = false,
       },
       {
-          "2 password fields, current and new password",
-          {
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw1"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw2"},
-          },
+          .description_for_logging =
+              "2 password fields, current and new password",
+          .fields =
+              {
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw1"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw2"},
+              },
           .is_new_password_reliable = false,
       },
       {
-          "3 password fields, current, new, confirm password",
-          {
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw1"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw2"},
-              {.role = ElementRole::CONFIRMATION_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw2"},
-          },
+          .description_for_logging =
+              "3 password fields, current, new, confirm password",
+          .fields =
+              {
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw1"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw2"},
+                  {.role = ElementRole::CONFIRMATION_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw2"},
+              },
           .is_new_password_reliable = false,
       },
       {
@@ -517,46 +525,51 @@
           .number_of_all_possible_passwords = 3,
       },
       {
-          "4 password fields, only the first 3 are considered",
-          {
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw1"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw2"},
-              {.role = ElementRole::CONFIRMATION_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw2"},
-              {.form_control_type = "password", .value = "pw3"},
-          },
+          .description_for_logging =
+              "4 password fields, only the first 3 are considered",
+          .fields =
+              {
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw1"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw2"},
+                  {.role = ElementRole::CONFIRMATION_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw2"},
+                  {.form_control_type = "password", .value = "pw3"},
+              },
       },
       {
-          "4 password fields, 4th same value as 3rd and 2nd, only the first 3 "
-          "are considered",
-          {
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw1"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw2"},
-              {.role = ElementRole::CONFIRMATION_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw2"},
-              {.form_control_type = "password", .value = "pw2"},
-          },
+          .description_for_logging = "4 password fields, 4th same value as 3rd "
+                                     "and 2nd, only the first 3 "
+                                     "are considered",
+          .fields =
+              {
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw1"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw2"},
+                  {.role = ElementRole::CONFIRMATION_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw2"},
+                  {.form_control_type = "password", .value = "pw2"},
+              },
       },
       {
-          "4 password fields, all same value",
-          {
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = "pw"},
-              {.form_control_type = "password", .value = "pw"},
-              {.form_control_type = "password", .value = "pw"},
-              {.form_control_type = "password", .value = "pw"},
-          },
+          .description_for_logging = "4 password fields, all same value",
+          .fields =
+              {
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = "pw"},
+                  {.form_control_type = "password", .value = "pw"},
+                  {.form_control_type = "password", .value = "pw"},
+                  {.form_control_type = "password", .value = "pw"},
+              },
       },
   });
 }
@@ -564,50 +577,58 @@
 TEST(FormParserTest, TestFocusability) {
   CheckTestData({
       {
-          "non-focusable fields are considered when there are no focusable "
-          "fields",
-          {
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = false},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = false},
-          },
+          .description_for_logging =
+              "non-focusable fields are considered when there are no focusable "
+              "fields",
+          .fields =
+              {
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = false},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = false},
+              },
       },
       {
-          "non-focusable should be skipped when there are focusable fields",
-          {
-              {.form_control_type = "password", .is_focusable = false},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = true},
-          },
+          .description_for_logging =
+              "non-focusable should be skipped when there are focusable fields",
+          .fields =
+              {
+                  {.form_control_type = "password", .is_focusable = false},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = true},
+              },
       },
       {
-          "non-focusable text fields before password",
-          {
-              {.form_control_type = "text", .is_focusable = false},
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .is_focusable = false},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = true},
-          },
+          .description_for_logging =
+              "non-focusable text fields before password",
+          .fields =
+              {
+                  {.form_control_type = "text", .is_focusable = false},
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .is_focusable = false},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = true},
+              },
           .number_of_all_possible_usernames = 2,
       },
       {
-          "focusable and non-focusable text fields before password",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .is_focusable = true},
-              {.form_control_type = "text", .is_focusable = false},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = true},
-          },
+          .description_for_logging =
+              "focusable and non-focusable text fields before password",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .is_focusable = true},
+                  {.form_control_type = "text", .is_focusable = false},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = true},
+              },
       },
       {
           .description_for_logging = "many passwords, some of them focusable",
@@ -641,17 +662,18 @@
 TEST(FormParserTest, TextAndPasswordFields) {
   CheckTestData({
       {
-          "Simple empty sign-in form",
+          .description_for_logging = "Simple empty sign-in form",
           // Forms with empty fields cannot be saved, so the parsing result for
           // saving is empty.
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .value = ""},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = ""},
-          },
+          .fields =
+              {
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .value = ""},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = ""},
+              },
           // all_possible_* only count fields with non-empty values.
           .number_of_all_possible_passwords = 0,
           .number_of_all_possible_usernames = 0,
@@ -667,87 +689,103 @@
           .number_of_all_possible_passwords = 1,
       },
       {
-          "Empty sign-in form with an extra text field",
-          {
-              {.form_control_type = "text", .value = ""},
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .value = ""},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = ""},
-          },
+          .description_for_logging =
+              "Empty sign-in form with an extra text field",
+          .fields =
+              {
+                  {.form_control_type = "text", .value = ""},
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .value = ""},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = ""},
+              },
       },
       {
-          "Non-empty sign-in form with an extra text field",
-          {
-              {.role_saving = ElementRole::USERNAME,
-               .form_control_type = "text"},
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .value = ""},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Non-empty sign-in form with an extra text field",
+          .fields =
+              {
+                  {.role_saving = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .value = ""},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Empty sign-in form with an extra invisible text field",
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .value = ""},
-              {.form_control_type = "text", .is_focusable = false, .value = ""},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = ""},
-          },
+          .description_for_logging =
+              "Empty sign-in form with an extra invisible text field",
+          .fields =
+              {
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .value = ""},
+                  {.form_control_type = "text",
+                   .is_focusable = false,
+                   .value = ""},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = ""},
+              },
       },
       {
-          "Non-empty sign-in form with an extra invisible text field",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.form_control_type = "text", .is_focusable = false},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Non-empty sign-in form with an extra invisible text field",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.form_control_type = "text", .is_focusable = false},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Simple empty sign-in form with empty username",
+          .description_for_logging =
+              "Simple empty sign-in form with empty username",
           // Filled forms with a username field which is left empty are
           // suspicious. The parser will just omit the username altogether.
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .value = ""},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .fields =
+              {
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .value = ""},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Simple empty sign-in form with empty password",
+          .description_for_logging =
+              "Simple empty sign-in form with empty password",
           // Empty password, nothing to save.
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text"},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .value = ""},
-          },
+          .fields =
+              {
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .value = ""},
+              },
       },
   });
 }
 
 TEST(FormParserTest, TextFieldValueIsNotUsername) {
   CheckTestData({{
-      "Text field value is unlikely username so it should be ignored on saving",
-      {
-          {.role_filling = ElementRole::USERNAME,
-           .form_control_type = "text",
-           .value = "12"},
-          {.role = ElementRole::CURRENT_PASSWORD,
-           .form_control_type = "password",
-           .value = "strong_pw"},
-      },
+      .description_for_logging = "Text field value is unlikely username so it "
+                                 "should be ignored on saving",
+      .fields =
+          {
+              {.role_filling = ElementRole::USERNAME,
+               .form_control_type = "text",
+               .value = "12"},
+              {.role = ElementRole::CURRENT_PASSWORD,
+               .form_control_type = "password",
+               .value = "strong_pw"},
+          },
   }});
 }
 
@@ -805,109 +843,122 @@
           .number_of_all_possible_usernames = 2,
       },
       {
-          "Basic heuristics kick in if autocomplete analysis fails",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "email"},
-              // NB: 'password' is not a valid autocomplete type hint.
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "password"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Basic heuristics kick in if autocomplete analysis fails",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "email"},
+                  // NB: 'password' is not a valid autocomplete type hint.
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "password"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Partial autocomplete analysis doesn't fail if no passwords are "
-          "found",
+          .description_for_logging =
+              "Partial autocomplete analysis doesn't fail if no passwords are "
+              "found",
           // The attribute 'username' is used even if there was no password
           // marked up.
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "username"},
-              {.form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "username"},
+                  {.form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Multiple username autocomplete attributes, fallback to base "
-          "heuristics",
-          {
-              {.form_control_type = "text",
-               .autocomplete_attribute = "username"},
-              {.form_control_type = "text",
-               .autocomplete_attribute = "username"},
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.form_control_type = "password"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "current-password"},
-          },
+          .description_for_logging =
+              "Multiple username autocomplete attributes, fallback to base "
+              "heuristics",
+          .fields =
+              {
+                  {.form_control_type = "text",
+                   .autocomplete_attribute = "username"},
+                  {.form_control_type = "text",
+                   .autocomplete_attribute = "username"},
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.form_control_type = "password"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "current-password"},
+              },
       },
       {
-          "Parsing complex autocomplete attributes",
-          {
-              // Valid information about form sections, in addition to the
-              // username hint.
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "section-test billing username"},
-              {.form_control_type = "text"},
-              // Invalid composition, but the parser is simplistic and just
-              // grabs the last token.
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "new-password current-password"},
-              {.form_control_type = "password"},
-          },
+          .description_for_logging = "Parsing complex autocomplete attributes",
+          .fields =
+              {
+                  // Valid information about form sections, in addition to the
+                  // username hint.
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "section-test billing username"},
+                  {.form_control_type = "text"},
+                  // Invalid composition, but the parser is simplistic and just
+                  // grabs the last token.
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "new-password current-password"},
+                  {.form_control_type = "password"},
+              },
       },
       {
-          "Ignored autocomplete attributes",
-          {
-              // 'off' is ignored.
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "off"},
-              // Invalid composition, the parser ignores all but the last token.
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "new-password abc"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Ignored autocomplete attributes",
+          .fields =
+              {
+                  // 'off' is ignored.
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "off"},
+                  // Invalid composition, the parser ignores all but the last
+                  // token.
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "new-password abc"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Swapped username/password autocomplete attributes",
+          .description_for_logging =
+              "Swapped username/password autocomplete attributes",
           // Swap means ignoring autocomplete analysis and falling back to basic
           // heuristics.
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "current-password"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "username"},
-          },
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "current-password"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "username"},
+              },
       },
       {
-          "Autocomplete mark-up overrides visibility",
-          {
-              {.role = ElementRole::USERNAME,
-               .is_focusable = false,
-               .form_control_type = "text",
-               .autocomplete_attribute = "username"},
-              {.is_focusable = true, .form_control_type = "text"},
-              {.is_focusable = true, .form_control_type = "password"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = false,
-               .autocomplete_attribute = "current-password"},
-          },
+          .description_for_logging =
+              "Autocomplete mark-up overrides visibility",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .is_focusable = false,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "username"},
+                  {.is_focusable = true, .form_control_type = "text"},
+                  {.is_focusable = true, .form_control_type = "password"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = false,
+                   .autocomplete_attribute = "current-password"},
+              },
       },
   });
 }
@@ -937,13 +988,14 @@
 TEST(FormParserTest, SkippingFieldsWithCreditCardFields) {
   CheckTestData({
       {
-          "Simple form, all fields are credit-card-related",
-          {{.role = ElementRole::USERNAME,
-            .form_control_type = "text",
-            .autocomplete_attribute = "cc-name"},
-           {.role = ElementRole::CURRENT_PASSWORD,
-            .form_control_type = "password",
-            .autocomplete_attribute = "cc-any-string"}},
+          .description_for_logging =
+              "Simple form, all fields are credit-card-related",
+          .fields = {{.role = ElementRole::USERNAME,
+                      .form_control_type = "text",
+                      .autocomplete_attribute = "cc-name"},
+                     {.role = ElementRole::CURRENT_PASSWORD,
+                      .form_control_type = "password",
+                      .autocomplete_attribute = "cc-any-string"}},
           .fallback_only = true,
       },
       {
@@ -966,46 +1018,51 @@
 TEST(FormParserTest, ReadonlyFields) {
   CheckTestData({
       {
-          "For usernames, readonly does not matter",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .is_readonly = true},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "For usernames, readonly does not matter",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .is_readonly = true},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "For passwords, readonly means: 'give up', perhaps there is a "
-          "virtual keyboard, filling might be ignored",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_readonly = true},
-          },
+          .description_for_logging =
+              "For passwords, readonly means: 'give up', perhaps there is a "
+              "virtual keyboard, filling might be ignored",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_readonly = true},
+              },
           // And "give-up" means "fallback-only".
           .fallback_only = true,
       },
       {
-          "But correctly marked passwords are accepted even if readonly",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "username"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .autocomplete_attribute = "new-password",
-               .form_control_type = "password",
-               .is_readonly = true},
-              {.role = ElementRole::CONFIRMATION_PASSWORD,
-               .autocomplete_attribute = "new-password",
-               .form_control_type = "password",
-               .is_readonly = true},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .autocomplete_attribute = "current-password",
-               .form_control_type = "password",
-               .is_readonly = true},
-          },
+          .description_for_logging =
+              "But correctly marked passwords are accepted even if readonly",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "username"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .autocomplete_attribute = "new-password",
+                   .form_control_type = "password",
+                   .is_readonly = true},
+                  {.role = ElementRole::CONFIRMATION_PASSWORD,
+                   .autocomplete_attribute = "new-password",
+                   .form_control_type = "password",
+                   .is_readonly = true},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .autocomplete_attribute = "current-password",
+                   .form_control_type = "password",
+                   .is_readonly = true},
+              },
           .is_new_password_reliable = true,
       },
       {
@@ -1056,81 +1113,98 @@
 TEST(FormParserTest, ServerPredictionsForClearTextPasswordFields) {
   CheckTestData({
       {
-          "Server prediction for account change password and username field.",
-          {
+          .description_for_logging = "Server prediction for account change "
+                                     "password and username field.",
+          .fields =
               {
-                  .role_filling = ElementRole::USERNAME,
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS},
+                  {
+                      .role_filling = ElementRole::USERNAME,
+                      .form_control_type = "text",
+                      .prediction = {.type =
+                                         autofill::USERNAME_AND_EMAIL_ADDRESS},
+                  },
+                  {
+                      .role_filling = ElementRole::NEW_PASSWORD,
+                      .form_control_type = "text",
+                      .prediction = {.type = autofill::NEW_PASSWORD},
+                  },
               },
-              {
-                  .role_filling = ElementRole::NEW_PASSWORD,
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::NEW_PASSWORD},
-              },
-          },
       },
       {
-          "Server prediction for account change password field only.",
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text"},
+          .description_for_logging =
+              "Server prediction for account change password field only.",
+          .fields =
               {
-                  .role_filling = ElementRole::NEW_PASSWORD,
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::NEW_PASSWORD},
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {
+                      .role_filling = ElementRole::NEW_PASSWORD,
+                      .form_control_type = "text",
+                      .prediction = {.type = autofill::NEW_PASSWORD},
+                  },
               },
-          },
       },
       {
-          "Server prediction for account password and username field.",
-          {
+          .description_for_logging =
+              "Server prediction for account password and username field.",
+          .fields =
               {
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS},
+                  {
+                      .form_control_type = "text",
+                      .prediction = {.type =
+                                         autofill::USERNAME_AND_EMAIL_ADDRESS},
+                  },
+                  {
+                      .form_control_type = "text",
+                      .prediction = {.type = autofill::PASSWORD},
+                  },
               },
-              {
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::PASSWORD},
-              },
-          },
       },
       {
-          "Server prediction for account password field only.",
-          {
-              {.form_control_type = "text"},
+          .description_for_logging =
+              "Server prediction for account password field only.",
+          .fields =
               {
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::PASSWORD},
+                  {.form_control_type = "text"},
+                  {
+                      .form_control_type = "text",
+                      .prediction = {.type = autofill::PASSWORD},
+                  },
               },
-          },
       },
       {
-          "Server prediction for account creation password and username field.",
-          {
+          .description_for_logging = "Server prediction for account creation "
+                                     "password and username field.",
+          .fields =
               {
-                  .role_filling = ElementRole::USERNAME,
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS},
+                  {
+                      .role_filling = ElementRole::USERNAME,
+                      .form_control_type = "text",
+                      .prediction = {.type =
+                                         autofill::USERNAME_AND_EMAIL_ADDRESS},
+                  },
+                  {
+                      .role_filling = ElementRole::NEW_PASSWORD,
+                      .form_control_type = "text",
+                      .prediction = {.type =
+                                         autofill::ACCOUNT_CREATION_PASSWORD},
+                  },
               },
-              {
-                  .role_filling = ElementRole::NEW_PASSWORD,
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD},
-              },
-          },
       },
       {
-          "Server prediction for account creation password field only.",
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text"},
+          .description_for_logging =
+              "Server prediction for account creation password field only.",
+          .fields =
               {
-                  .role_filling = ElementRole::NEW_PASSWORD,
-                  .form_control_type = "text",
-                  .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD},
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {
+                      .role_filling = ElementRole::NEW_PASSWORD,
+                      .form_control_type = "text",
+                      .prediction = {.type =
+                                         autofill::ACCOUNT_CREATION_PASSWORD},
+                  },
               },
-          },
       },
   });
 }
@@ -1138,41 +1212,45 @@
 TEST(FormParserTest, ServerHints) {
   CheckTestData({
       {
-          "Empty predictions don't cause panic",
-          {
-              {.form_control_type = "text"},
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Empty predictions don't cause panic",
+          .fields =
+              {
+                  {.form_control_type = "text"},
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Username-only predictions are not ignored",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Username-only predictions are not ignored",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Simple predictions work",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS,
-                              .may_use_prefilled_placeholder = true}},
-              {.form_control_type = "text"},
-              {.role_saving = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .role_saving = ElementRole::NEW_PASSWORD,
-               .prediction = {.type = autofill::PASSWORD,
-                              .may_use_prefilled_placeholder = true},
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Simple predictions work",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS,
+                                  .may_use_prefilled_placeholder = true}},
+                  {.form_control_type = "text"},
+                  {.role_saving = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .role_saving = ElementRole::NEW_PASSWORD,
+                   .prediction = {.type = autofill::PASSWORD,
+                                  .may_use_prefilled_placeholder = true},
+                   .form_control_type = "password"},
+              },
           .username_may_use_prefilled_placeholder = true,
       },
       {
@@ -1199,14 +1277,16 @@
           .is_new_password_reliable = true,
       },
       {
-          "password prediction for a non-password field is ignored",
-          {
-              {.role = ElementRole::USERNAME,
-               .prediction = {.type = autofill::PASSWORD},
-               .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "password prediction for a non-password field is ignored",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .prediction = {.type = autofill::PASSWORD},
+                   .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
   });
 }
@@ -1214,19 +1294,21 @@
 TEST(FormParserTest, Interactability) {
   CheckTestData({
       {
-          "If all fields are hidden, all are considered",
-          {
-              {.form_control_type = "text", .is_focusable = false},
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .is_focusable = false},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = false},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = false},
-          },
+          .description_for_logging =
+              "If all fields are hidden, all are considered",
+          .fields =
+              {
+                  {.form_control_type = "text", .is_focusable = false},
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .is_focusable = false},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = false},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = false},
+              },
       },
       {
           .description_for_logging =
@@ -1269,37 +1351,44 @@
           .form_has_autofilled_value = true,
       },
       {
-          "Interactability for usernames is only considered before the first "
-          "relevant password. That way, if, e.g., the username gets filled and "
-          "hidden (to let the user enter password), and there is another text "
-          "field visible below, the maximum Interactability won't end up being "
-          "kPossible, which would exclude the hidden username.",
-          {
-              {.role = ElementRole::USERNAME,
-               .properties_mask = FieldPropertiesFlags::AUTOFILLED,
-               .form_control_type = "text",
-               .is_focusable = false},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .properties_mask = FieldPropertiesFlags::AUTOFILLED,
-               .is_focusable = true},
-              {.form_control_type = "text", .is_focusable = true, .value = ""},
-          },
+          .description_for_logging =
+              "Interactability for usernames is only considered before the "
+              "first relevant password. That way, if, e.g., the username gets "
+              "filled and hidden (to let the user enter password), and there "
+              "is another text field visible below, the maximum "
+              "Interactability won't end up being kPossible, which would "
+              "exclude the hidden username.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .properties_mask = FieldPropertiesFlags::AUTOFILLED,
+                   .form_control_type = "text",
+                   .is_focusable = false},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .properties_mask = FieldPropertiesFlags::AUTOFILLED,
+                   .is_focusable = true},
+                  {.form_control_type = "text",
+                   .is_focusable = true,
+                   .value = ""},
+              },
           .form_has_autofilled_value = true,
       },
       {
-          "Interactability also matters for HTML classifier.",
-          {
-              {.form_control_type = "text",
-               .is_focusable = false,
-               .predicted_username = 0},
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .is_focusable = true},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_focusable = true},
-          },
+          .description_for_logging =
+              "Interactability also matters for HTML classifier.",
+          .fields =
+              {
+                  {.form_control_type = "text",
+                   .is_focusable = false,
+                   .predicted_username = 0},
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .is_focusable = true},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_focusable = true},
+              },
       },
   });
 }
@@ -1418,64 +1507,73 @@
 TEST(FormParserTest, UsernamePredictions) {
   CheckTestData({
       {
-          "Username prediction overrides structure",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .predicted_username = 0},
-              {.form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Username prediction overrides structure",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .predicted_username = 0},
+                  {.form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Username prediction does not override structure if empty and mode "
-          "is SAVING",
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .predicted_username = 2,
-               .value = ""},
-              {.role_saving = ElementRole::USERNAME,
-               .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Username prediction does not override "
+                                     "structure if empty and mode "
+                                     "is SAVING",
+          .fields =
+              {
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .predicted_username = 2,
+                   .value = ""},
+                  {.role_saving = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Username prediction does not override autocomplete analysis",
-          {
-              {.form_control_type = "text", .predicted_username = 0},
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "username"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "current-password"},
-          },
+          .description_for_logging =
+              "Username prediction does not override autocomplete analysis",
+          .fields =
+              {
+                  {.form_control_type = "text", .predicted_username = 0},
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "username"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "current-password"},
+              },
       },
       {
-          "Username prediction does not override server hints",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS}},
-              {.form_control_type = "text", .predicted_username = 0},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .prediction = {.type = autofill::PASSWORD},
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Username prediction does not override server hints",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type =
+                                      autofill::USERNAME_AND_EMAIL_ADDRESS}},
+                  {.form_control_type = "text", .predicted_username = 0},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .prediction = {.type = autofill::PASSWORD},
+                   .form_control_type = "password"},
+              },
       },
       {
-          "Username prediction order matters",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .predicted_username = 1},
-              {.form_control_type = "text", .predicted_username = 4},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Username prediction order matters",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .predicted_username = 1},
+                  {.form_control_type = "text", .predicted_username = 4},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
   });
 }
@@ -1491,41 +1589,47 @@
 TEST(FormParserTest, ComplementingResults) {
   CheckTestData({
       {
-          "Current password from autocomplete analysis, username from basic "
-          "heuristics",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "current-password"},
-          },
+          .description_for_logging = "Current password from autocomplete "
+                                     "analysis, username from basic "
+                                     "heuristics",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "current-password"},
+              },
       },
       {
-          "New and confirmation passwords from server, username from basic "
-          "heuristics",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role_filling = ElementRole::CONFIRMATION_PASSWORD,
-               .role_saving = ElementRole::CURRENT_PASSWORD,
-               .prediction = {.type = autofill::CONFIRMATION_PASSWORD},
-               .form_control_type = "password"},
-              {.role = ElementRole::NEW_PASSWORD,
-               .prediction = {.type = autofill::NEW_PASSWORD},
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "New and confirmation passwords from server, username from basic "
+              "heuristics",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role_filling = ElementRole::CONFIRMATION_PASSWORD,
+                   .role_saving = ElementRole::CURRENT_PASSWORD,
+                   .prediction = {.type = autofill::CONFIRMATION_PASSWORD},
+                   .form_control_type = "password"},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .prediction = {.type = autofill::NEW_PASSWORD},
+                   .form_control_type = "password"},
+              },
           .is_new_password_reliable = true,
       },
       {
-          "No password from server still means. Username hint from server is "
-          "used.",
-          {
-              {.role = ElementRole::USERNAME,
-               .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS},
-               .form_control_type = "text"},
-              {.form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "No password from server still means. "
+                                     "Username hint from server is "
+                                     "used.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .prediction = {.type = autofill::USERNAME_AND_EMAIL_ADDRESS},
+                   .form_control_type = "text"},
+                  {.form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
       },
   });
 }
@@ -1534,49 +1638,60 @@
 TEST(FormParserTest, CVC) {
   CheckTestData({
       {
-          "Server hints: CREDIT_CARD_VERIFICATION_CODE.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.form_control_type = "password",
-               .prediction = {.type = autofill::CREDIT_CARD_VERIFICATION_CODE}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Server hints: CREDIT_CARD_VERIFICATION_CODE.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.form_control_type = "password",
+                   .prediction = {.type =
+                                      autofill::CREDIT_CARD_VERIFICATION_CODE}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
           // The result should be trusted for more than just fallback, because
           // the chosen password was not a suspected CVC.
           .fallback_only = false,
       },
       {
-          "Server hints: CREDIT_CARD_VERIFICATION_CODE on only password.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .prediction = {.type = autofill::CREDIT_CARD_VERIFICATION_CODE},
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Server hints: CREDIT_CARD_VERIFICATION_CODE on only password.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .prediction = {.type =
+                                      autofill::CREDIT_CARD_VERIFICATION_CODE},
+                   .form_control_type = "password"},
+              },
           .fallback_only = true,
       },
       {
-          "Name of 'verification_type' matches the CVC pattern, ignore that "
-          "one.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.form_control_type = "password", .name = "verification_type"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Name of 'verification_type' matches the "
+                                     "CVC pattern, ignore that "
+                                     "one.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.form_control_type = "password",
+                   .name = "verification_type"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
           // The result should be trusted for more than just fallback, because
           // the chosen password was not a suspected CVC.
           .fallback_only = false,
       },
       {
-          "Create a fallback for the only password being a CVC field.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .name = "verification_type"},
-          },
+          .description_for_logging =
+              "Create a fallback for the only password being a CVC field.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .name = "verification_type"},
+              },
           .fallback_only = true,
       },
   });
@@ -1586,24 +1701,27 @@
 TEST(FormParserTest, NotPasswordField) {
   CheckTestData({
       {
-          "Server hints: NOT_PASSWORD.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.form_control_type = "password",
-               .prediction = {.type = autofill::NOT_PASSWORD}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Server hints: NOT_PASSWORD.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.form_control_type = "password",
+                   .prediction = {.type = autofill::NOT_PASSWORD}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
           .fallback_only = false,
       },
       {
-          "Server hints: NOT_PASSWORD on only password.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .prediction = {.type = autofill::NOT_PASSWORD},
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Server hints: NOT_PASSWORD on only password.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .prediction = {.type = autofill::NOT_PASSWORD},
+                   .form_control_type = "password"},
+              },
           .fallback_only = true,
       },
   });
@@ -1613,23 +1731,25 @@
 TEST(FormParserTest, NotUsernameField) {
   CheckTestData({
       {
-          "Server hints: NOT_USERNAME.",
-          {{.role = ElementRole::USERNAME, .form_control_type = "text"},
-           {.role = ElementRole::NONE,
-            .form_control_type = "text",
-            .prediction = {.type = autofill::NOT_USERNAME}},
-           {.role = ElementRole::CURRENT_PASSWORD,
-            .form_control_type = "password",
-            .prediction = {.type = autofill::PASSWORD}}},
+          .description_for_logging = "Server hints: NOT_USERNAME.",
+          .fields = {{.role = ElementRole::USERNAME,
+                      .form_control_type = "text"},
+                     {.role = ElementRole::NONE,
+                      .form_control_type = "text",
+                      .prediction = {.type = autofill::NOT_USERNAME}},
+                     {.role = ElementRole::CURRENT_PASSWORD,
+                      .form_control_type = "password",
+                      .prediction = {.type = autofill::PASSWORD}}},
           .fallback_only = false,
       },
       {
-          "Server hints: NOT_USERNAME on only username.",
-          {{.role = ElementRole::NONE,
-            .form_control_type = "text",
-            .prediction = {.type = autofill::NOT_USERNAME}},
-           {.role = ElementRole::CURRENT_PASSWORD,
-            .form_control_type = "password"}},
+          .description_for_logging =
+              "Server hints: NOT_USERNAME on only username.",
+          .fields = {{.role = ElementRole::NONE,
+                      .form_control_type = "text",
+                      .prediction = {.type = autofill::NOT_USERNAME}},
+                     {.role = ElementRole::CURRENT_PASSWORD,
+                      .form_control_type = "password"}},
           .fallback_only = false,
       },
   });
@@ -1640,26 +1760,29 @@
 TEST(FormParserTest, NotUsernameFieldDespiteAutocompelteAtrribute) {
   CheckTestData({
       {
-          "Server hints: NOT_USERNAME.",
-          {{.role = ElementRole::USERNAME, .form_control_type = "text"},
-           {.form_control_type = "text",
-            .autocomplete_attribute = "username",
-            .prediction = {.type = autofill::NOT_USERNAME}},
-           {.role = ElementRole::CURRENT_PASSWORD,
-            .form_control_type = "password",
-            .prediction = {.type = autofill::PASSWORD}}},
+          .description_for_logging = "Server hints: NOT_USERNAME.",
+          .fields = {{.role = ElementRole::USERNAME,
+                      .form_control_type = "text"},
+                     {.form_control_type = "text",
+                      .autocomplete_attribute = "username",
+                      .prediction = {.type = autofill::NOT_USERNAME}},
+                     {.role = ElementRole::CURRENT_PASSWORD,
+                      .form_control_type = "password",
+                      .prediction = {.type = autofill::PASSWORD}}},
           .fallback_only = false,
       },
       {
-          "Server hints: NOT_USERNAME on only username.",
-          {
-              {.role = ElementRole::NONE,
-               .form_control_type = "text",
-               .autocomplete_attribute = "username",
-               .prediction = {.type = autofill::NOT_USERNAME}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Server hints: NOT_USERNAME on only username.",
+          .fields =
+              {
+                  {.role = ElementRole::NONE,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "username",
+                   .prediction = {.type = autofill::NOT_USERNAME}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
           .fallback_only = false,
       },
   });
@@ -1669,31 +1792,34 @@
 TEST(FormParserTest, NotPasswordFieldDespiteAutocompleteAttribute) {
   CheckTestData({
       {
-          "Server hints: NOT_PASSWORD.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.form_control_type = "password",
-               .prediction = {.type = autofill::NOT_PASSWORD},
-               .autocomplete_attribute = "current-password"},
-              {.form_control_type = "password",
-               .prediction = {.type = autofill::NOT_PASSWORD},
-               .autocomplete_attribute = "new-password"},
-              {.form_control_type = "password",
-               .prediction = {.type = autofill::NOT_PASSWORD},
-               .autocomplete_attribute = "password"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Server hints: NOT_PASSWORD.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.form_control_type = "password",
+                   .prediction = {.type = autofill::NOT_PASSWORD},
+                   .autocomplete_attribute = "current-password"},
+                  {.form_control_type = "password",
+                   .prediction = {.type = autofill::NOT_PASSWORD},
+                   .autocomplete_attribute = "new-password"},
+                  {.form_control_type = "password",
+                   .prediction = {.type = autofill::NOT_PASSWORD},
+                   .autocomplete_attribute = "password"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+              },
           .fallback_only = false,
       },
       {
-          "Server hints: NOT_PASSWORD on only password.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .prediction = {.type = autofill::NOT_PASSWORD},
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Server hints: NOT_PASSWORD on only password.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .prediction = {.type = autofill::NOT_PASSWORD},
+                   .form_control_type = "password"},
+              },
           .fallback_only = true,
       },
   });
@@ -1703,14 +1829,16 @@
 TEST(FormParserTest, ReadonlyStatus) {
   CheckTestData({
       {
-          "Server predictions are ignored in saving mode",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .prediction = {.type = autofill::PASSWORD},
-               .is_readonly = true,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Server predictions are ignored in saving mode",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .prediction = {.type = autofill::PASSWORD},
+                   .is_readonly = true,
+                   .form_control_type = "password"},
+              },
           .readonly_status_for_filling =
               FormDataParser::ReadonlyPasswordFields::kNoHeuristics,
           .readonly_status_for_saving =
@@ -1718,55 +1846,62 @@
           .fallback_only = true,
       },
       {
-          "Autocomplete attributes prevent heuristics from using readonly.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .autocomplete_attribute = "current-password",
-               .is_readonly = true,
-               .form_control_type = "password"},
-          },
+          .description_for_logging =
+              "Autocomplete attributes prevent heuristics from using readonly.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .autocomplete_attribute = "current-password",
+                   .is_readonly = true,
+                   .form_control_type = "password"},
+              },
           .readonly_status =
               FormDataParser::ReadonlyPasswordFields::kNoHeuristics,
       },
       {
-          "No password fields are a special case of not going through local "
-          "heuristics.",
-          {
-              {.form_control_type = "text"},
-          },
+          .description_for_logging = "No password fields are a special case of "
+                                     "not going through local "
+                                     "heuristics.",
+          .fields =
+              {
+                  {.form_control_type = "text"},
+              },
           .readonly_status =
               FormDataParser::ReadonlyPasswordFields::kNoHeuristics,
       },
       {
-          "No readonly passwords ignored.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .role_saving = ElementRole::CURRENT_PASSWORD,
-               // While readonly, this field is not ignored because it was
-               // autofilled before.
-               .is_readonly = true,
-               .properties_mask = FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD,
-               .form_control_type = "password"},
-              {.role_filling = ElementRole::NEW_PASSWORD,
-               .role_saving = ElementRole::NEW_PASSWORD,
-               .is_readonly = false,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "No readonly passwords ignored.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .role_saving = ElementRole::CURRENT_PASSWORD,
+                   // While readonly, this field is not ignored because it was
+                   // autofilled before.
+                   .is_readonly = true,
+                   .properties_mask =
+                       FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD,
+                   .form_control_type = "password"},
+                  {.role_filling = ElementRole::NEW_PASSWORD,
+                   .role_saving = ElementRole::NEW_PASSWORD,
+                   .is_readonly = false,
+                   .form_control_type = "password"},
+              },
           .readonly_status =
               FormDataParser::ReadonlyPasswordFields::kNoneIgnored,
           .form_has_autofilled_value = true,
       },
       {
-          "Some readonly passwords ignored.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.is_readonly = true, .form_control_type = "password"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .is_readonly = false,
-               .form_control_type = "password"},
-          },
+          .description_for_logging = "Some readonly passwords ignored.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.is_readonly = true, .form_control_type = "password"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .is_readonly = false,
+                   .form_control_type = "password"},
+              },
           .readonly_status =
               FormDataParser::ReadonlyPasswordFields::kSomeIgnored,
           // The result should be trusted for more than just fallback, because
@@ -1774,13 +1909,15 @@
           .fallback_only = false,
       },
       {
-          "All readonly passwords ignored, only returned as a fallback.",
-          {
-              {.role = ElementRole::USERNAME, .form_control_type = "text"},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .is_readonly = true},
-          },
+          .description_for_logging =
+              "All readonly passwords ignored, only returned as a fallback.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .is_readonly = true},
+              },
           .readonly_status =
               FormDataParser::ReadonlyPasswordFields::kAllIgnored,
           .fallback_only = true,
@@ -1792,56 +1929,62 @@
 TEST(FormParserTest, NoEmptyValues) {
   CheckTestData({
       {
-          "Server hints overridden for non-empty values.",
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME},
-               .value = ""},
-              {.role_saving = ElementRole::USERNAME,
-               .form_control_type = "text"},
-              {.role_saving = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-              {.role_filling = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD},
-               .value = ""},
-          },
+          .description_for_logging =
+              "Server hints overridden for non-empty values.",
+          .fields =
+              {
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME},
+                   .value = ""},
+                  {.role_saving = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {.role_saving = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+                  {.role_filling = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD},
+                   .value = ""},
+              },
           .is_new_password_reliable = true,
       },
       {
-          "Autocomplete attributes overridden for non-empty values.",
-          {
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .autocomplete_attribute = "username",
-               .value = ""},
-              {.role_saving = ElementRole::USERNAME,
-               .form_control_type = "text"},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "current-password",
-               .value = ""},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .autocomplete_attribute = "new-password"},
-          },
+          .description_for_logging =
+              "Autocomplete attributes overridden for non-empty values.",
+          .fields =
+              {
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .autocomplete_attribute = "username",
+                   .value = ""},
+                  {.role_saving = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "current-password",
+                   .value = ""},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .autocomplete_attribute = "new-password"},
+              },
           .is_new_password_reliable = true,
       },
       {
-          "Structure heuristics overridden for non-empty values.",
-          {
-              {.role_saving = ElementRole::USERNAME,
-               .form_control_type = "text"},
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .value = ""},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password"},
-              {.role_filling = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .value = ""},
-          },
+          .description_for_logging =
+              "Structure heuristics overridden for non-empty values.",
+          .fields =
+              {
+                  {.role_saving = ElementRole::USERNAME,
+                   .form_control_type = "text"},
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .value = ""},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password"},
+                  {.role_filling = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .value = ""},
+              },
       },
   });
 }
@@ -1850,120 +1993,132 @@
 TEST(FormParserTest, MultipleUsernames) {
   CheckTestData({
       {
-          "More than two usernames are ignored.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::PASSWORD}},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-          },
+          .description_for_logging = "More than two usernames are ignored.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD}},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+              },
           .is_new_password_reliable = true,
       },
       {
-          "No current password -> ignore additional usernames.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role_filling = ElementRole::NEW_PASSWORD,
-               .role_saving = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-          },
+          .description_for_logging =
+              "No current password -> ignore additional usernames.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role_filling = ElementRole::NEW_PASSWORD,
+                   .role_saving = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+              },
       },
       {
-          "2 current passwods -> ignore additional usernames.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::PASSWORD}},
-              {.role_saving = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::PASSWORD}},
-          },
+          .description_for_logging =
+              "2 current passwods -> ignore additional usernames.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD}},
+                  {.role_saving = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD}},
+              },
       },
       {
-          "No new password -> ignore additional usernames.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::PASSWORD}},
-          },
+          .description_for_logging =
+              "No new password -> ignore additional usernames.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD}},
+              },
       },
       {
-          "Two usernames in sign-in, sign-up order.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::PASSWORD}},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-          },
+          .description_for_logging = "Two usernames in sign-in, sign-up order.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD}},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+              },
           .is_new_password_reliable = true,
       },
       {
-          "Two usernames in sign-up, sign-in order.",
-          {
-              {.role_saving = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role_filling = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role_filling = ElementRole::NEW_PASSWORD,
-               .role_saving = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-              {.form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-              {.role_filling = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::PASSWORD}},
-          },
+          .description_for_logging = "Two usernames in sign-up, sign-in order.",
+          .fields =
+              {
+                  {.role_saving = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role_filling = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role_filling = ElementRole::NEW_PASSWORD,
+                   .role_saving = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+                  {.form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+                  {.role_filling = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD}},
+              },
       },
       {
-          "Two usernames in sign-in, sign-up order; sign-in is pre-filled.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .properties_mask = FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD,
-               .prediction = {.type = autofill::USERNAME}},
-              {.form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::PASSWORD}},
-              {.role = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-          },
+          .description_for_logging =
+              "Two usernames in sign-in, sign-up order; sign-in is pre-filled.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .properties_mask =
+                       FieldPropertiesFlags::AUTOFILLED_ON_PAGELOAD,
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::PASSWORD}},
+                  {.role = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+              },
       },
   });
 }
@@ -1976,36 +2131,39 @@
 TEST(FormParserTest, MultipleNewPasswords) {
   CheckTestData({
       {
-          "Only one new-password recognised.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role_filling = ElementRole::NEW_PASSWORD,
-               .role_saving = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-              {.role_saving = ElementRole::NEW_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-          },
+          .description_for_logging = "Only one new-password recognised.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role_filling = ElementRole::NEW_PASSWORD,
+                   .role_saving = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+                  {.role_saving = ElementRole::NEW_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+              },
       },
       {
-          "Only one new-password recognised, confirmation unaffected.",
-          {
-              {.role = ElementRole::USERNAME,
-               .form_control_type = "text",
-               .prediction = {.type = autofill::USERNAME}},
-              {.role_filling = ElementRole::NEW_PASSWORD,
-               .role_saving = ElementRole::CURRENT_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-              {.form_control_type = "password",
-               .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
-              {.role_filling = ElementRole::CONFIRMATION_PASSWORD,
-               .form_control_type = "password",
-               .prediction = {.type = autofill::CONFIRMATION_PASSWORD}},
-          },
+          .description_for_logging =
+              "Only one new-password recognised, confirmation unaffected.",
+          .fields =
+              {
+                  {.role = ElementRole::USERNAME,
+                   .form_control_type = "text",
+                   .prediction = {.type = autofill::USERNAME}},
+                  {.role_filling = ElementRole::NEW_PASSWORD,
+                   .role_saving = ElementRole::CURRENT_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+                  {.form_control_type = "password",
+                   .prediction = {.type = autofill::ACCOUNT_CREATION_PASSWORD}},
+                  {.role_filling = ElementRole::CONFIRMATION_PASSWORD,
+                   .form_control_type = "password",
+                   .prediction = {.type = autofill::CONFIRMATION_PASSWORD}},
+              },
       },
   });
 }
@@ -2017,92 +2175,105 @@
   } kHistogramTestCases[] = {
       {
           {
-              "No username",
-              {
-                  {.role = ElementRole::CURRENT_PASSWORD,
-                   .form_control_type = "password",
-                   .prediction = {.type = autofill::PASSWORD}},
-              },
+              .description_for_logging = "No username",
+              .fields =
+                  {
+                      {.role = ElementRole::CURRENT_PASSWORD,
+                       .form_control_type = "password",
+                       .prediction = {.type = autofill::PASSWORD}},
+                  },
           },
           UsernameDetectionMethod::kNoUsernameDetected,
       },
       {
           {
-              "Reporting server analysis",
-              {
-                  {.role = ElementRole::USERNAME,
-                   .form_control_type = "text",
-                   .prediction = {.type = autofill::USERNAME}},
-                  {.role = ElementRole::CURRENT_PASSWORD,
-                   .form_control_type = "password",
-                   .prediction = {.type = autofill::PASSWORD}},
-              },
+              .description_for_logging = "Reporting server analysis",
+              .fields =
+                  {
+                      {.role = ElementRole::USERNAME,
+                       .form_control_type = "text",
+                       .prediction = {.type = autofill::USERNAME}},
+                      {.role = ElementRole::CURRENT_PASSWORD,
+                       .form_control_type = "password",
+                       .prediction = {.type = autofill::PASSWORD}},
+                  },
           },
           UsernameDetectionMethod::kServerSidePrediction,
       },
       {
           {
-              "Reporting autocomplete analysis",
-              {
-                  {.role = ElementRole::USERNAME,
-                   .form_control_type = "text",
-                   .autocomplete_attribute = "username"},
-                  {.role = ElementRole::CURRENT_PASSWORD,
-                   .form_control_type = "password",
-                   .autocomplete_attribute = "current-password"},
-              },
+              .description_for_logging = "Reporting autocomplete analysis",
+              .fields =
+                  {
+                      {.role = ElementRole::USERNAME,
+                       .form_control_type = "text",
+                       .autocomplete_attribute = "username"},
+                      {.role = ElementRole::CURRENT_PASSWORD,
+                       .form_control_type = "password",
+                       .autocomplete_attribute = "current-password"},
+                  },
           },
           UsernameDetectionMethod::kAutocompleteAttribute,
       },
       {
           {
-              "Reporting HTML classifier",
-              {
-                  {.role = ElementRole::USERNAME,
-                   .form_control_type = "text",
-                   .predicted_username = 0},
-                  {.role = ElementRole::CURRENT_PASSWORD,
-                   .form_control_type = "password"},
-              },
+              .description_for_logging = "Reporting HTML classifier",
+              .fields =
+                  {
+                      {.role = ElementRole::USERNAME,
+                       .form_control_type = "text",
+                       .predicted_username = 0},
+                      {.role = ElementRole::CURRENT_PASSWORD,
+                       .form_control_type = "password"},
+                  },
           },
           UsernameDetectionMethod::kHtmlBasedClassifier,
       },
       {
           {
-              "Reporting basic heuristics",
-              {
-                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
-                  {.role = ElementRole::CURRENT_PASSWORD,
-                   .form_control_type = "password"},
-              },
+              .description_for_logging = "Reporting basic heuristics",
+              .fields =
+                  {
+                      {.role = ElementRole::USERNAME,
+                       .form_control_type = "text"},
+                      {.role = ElementRole::CURRENT_PASSWORD,
+                       .form_control_type = "password"},
+                  },
           },
           UsernameDetectionMethod::kBaseHeuristic,
       },
       {
           {
-              "Mixing server analysis on password and HTML classifier on "
-              "username is reported as HTML classifier",
-              {
-                  {.role = ElementRole::USERNAME,
-                   .form_control_type = "text",
-                   .predicted_username = 0},
-                  {.role = ElementRole::CURRENT_PASSWORD,
-                   .form_control_type = "password",
-                   .prediction = {.type = autofill::PASSWORD}},
-              },
+              .description_for_logging =
+                  "Mixing server analysis on password and HTML classifier "
+                  "on "
+                  "username is reported as HTML classifier",
+              .fields =
+                  {
+                      {.role = ElementRole::USERNAME,
+                       .form_control_type = "text",
+                       .predicted_username = 0},
+                      {.role = ElementRole::CURRENT_PASSWORD,
+                       .form_control_type = "password",
+                       .prediction = {.type = autofill::PASSWORD}},
+                  },
           },
           UsernameDetectionMethod::kHtmlBasedClassifier,
       },
       {
           {
-              "Mixing autocomplete analysis on password and basic heuristics "
-              "on username is reported as basic heuristics",
-              {
-                  {.role = ElementRole::USERNAME, .form_control_type = "text"},
-                  {.role = ElementRole::CURRENT_PASSWORD,
-                   .form_control_type = "password",
-                   .autocomplete_attribute = "current-password"},
-              },
+              .description_for_logging =
+                  "Mixing autocomplete analysis on password and basic "
+                  "heuristics "
+                  "on username is reported as basic heuristics",
+              .fields =
+                  {
+                      {.role = ElementRole::USERNAME,
+                       .form_control_type = "text"},
+                      {.role = ElementRole::CURRENT_PASSWORD,
+                       .form_control_type = "password",
+                       .autocomplete_attribute = "current-password"},
+                  },
           },
           UsernameDetectionMethod::kBaseHeuristic,
       },
@@ -2120,12 +2291,13 @@
 
 TEST(FormParserTest, SubmissionEvent) {
   CheckTestData({
-      {"Sign-in form, submission event is not None",
-       {
-           {.role = ElementRole::USERNAME, .form_control_type = "text"},
-           {.role = ElementRole::CURRENT_PASSWORD,
-            .form_control_type = "password"},
-       },
+      {.description_for_logging = "Sign-in form, submission event is not None",
+       .fields =
+           {
+               {.role = ElementRole::USERNAME, .form_control_type = "text"},
+               {.role = ElementRole::CURRENT_PASSWORD,
+                .form_control_type = "password"},
+           },
        .submission_event = SubmissionIndicatorEvent::XHR_SUCCEEDED},
   });
 }
diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc
index 38adbff1..a5cdbfee 100644
--- a/components/password_manager/core/browser/password_form_manager.cc
+++ b/components/password_manager/core/browser/password_form_manager.cc
@@ -126,6 +126,41 @@
   return differences_bitmask;
 }
 
+bool URLsEqualUpToScheme(const GURL& a, const GURL& b) {
+  return (a.GetContent() == b.GetContent());
+}
+
+bool URLsEqualUpToHttpHttpsSubstitution(const GURL& a, const GURL& b) {
+  if (a == b)
+    return true;
+
+  // The first-time and retry login forms action URLs sometimes differ in
+  // switching from HTTP to HTTPS, see http://crbug.com/400769.
+  if (a.SchemeIsHTTPOrHTTPS() && b.SchemeIsHTTPOrHTTPS())
+    return URLsEqualUpToScheme(a, b);
+
+  return false;
+}
+
+// Since empty or unspecified form's action is automatically set to the page
+// origin, this function checks if a form's action is empty by comparing it to
+// its origin.
+bool HasNonEmptyAction(const FormData& form) {
+  // TODO(crbug.com/1008798): The logic isn't accurate and should be fixed.
+  return form.action != form.url;
+}
+
+bool FormContainsFieldWithName(const FormData& form,
+                               const base::string16& element) {
+  if (element.empty())
+    return false;
+  for (const auto& field : form.fields) {
+    if (base::EqualsCaseInsensitiveASCII(field.name, element))
+      return true;
+  }
+  return false;
+}
+
 }  // namespace
 
 PasswordFormManager::PasswordFormManager(
@@ -212,10 +247,29 @@
     return false;
   if (IsHttpAuth())
     return false;
-  if (form.action == submitted_form_.action)
+
+  if (form.action.is_valid() && HasNonEmptyAction(form) &&
+      HasNonEmptyAction(submitted_form_) &&
+      URLsEqualUpToHttpHttpsSubstitution(submitted_form_.action, form.action)) {
     return true;
-  // TODO(https://crbug.com/831123): Implement other checks from a function
-  // IsPasswordFormReappeared from password_manager.cc.
+  }
+
+  // Match the form if username and password fields are same.
+  if (FormContainsFieldWithName(form,
+                                parsed_submitted_form_->username_element) &&
+      FormContainsFieldWithName(form,
+                                parsed_submitted_form_->password_element)) {
+    return true;
+  }
+
+  // Match the form if the observed username field has the same value as in
+  // the submitted form.
+  if (!parsed_submitted_form_->username_value.empty()) {
+    for (const auto& field : form.fields) {
+      if (field.value == parsed_submitted_form_->username_value)
+        return true;
+    }
+  }
   return false;
 }
 
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.cc b/components/password_manager/core/browser/password_form_metrics_recorder.cc
index 9390846..76e2c45 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.cc
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.cc
@@ -16,7 +16,6 @@
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/statistics_table.h"
-#include "password_form_metrics_recorder.h"
 
 using autofill::FieldPropertiesFlags;
 using autofill::FormData;
@@ -234,10 +233,6 @@
     ukm_entry_builder_.SetGeneration_PopupShown(
         static_cast<int64_t>(password_generation_popup_shown_));
   }
-  if (spec_priority_of_generated_password_) {
-    ukm_entry_builder_.SetGeneration_SpecPriority(
-        spec_priority_of_generated_password_.value());
-  }
 
   if (showed_manual_fallback_for_saving_) {
     ukm_entry_builder_.SetSaving_ShowedManualFallbackForSaving(
@@ -294,11 +289,6 @@
   generated_password_status_ = status;
 }
 
-void PasswordFormMetricsRecorder::ReportSpecPriorityForGeneratedPassword(
-    uint32_t spec_priority) {
-  spec_priority_of_generated_password_ = spec_priority;
-}
-
 void PasswordFormMetricsRecorder::SetManagerAction(
     ManagerAction manager_action) {
   manager_action_ = manager_action;
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.h b/components/password_manager/core/browser/password_form_metrics_recorder.h
index f61927f2..dc7f09b5a 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.h
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.h
@@ -304,13 +304,6 @@
   // Stores the user action associated with a generated password.
   void SetGeneratedPasswordStatus(GeneratedPasswordStatus status);
 
-  // Reports the priority of a PasswordGenerationRequirementsSpec for a
-  // generated password. This can be used for debugging as a 0 means that
-  // no spec was used, a 10 means that the spec came from autofill and was crowd
-  // sourced, a 20 means that it was overrideen per domain and a 30 means that
-  // is was overridden for the form.
-  void ReportSpecPriorityForGeneratedPassword(uint32_t spec_priority);
-
   // Stores the password manager action. During destruction the last
   // set value will be logged.
   void SetManagerAction(ManagerAction manager_action);
@@ -458,8 +451,6 @@
   // action.
   base::Optional<GeneratedPasswordStatus> generated_password_status_;
 
-  base::Optional<uint32_t> spec_priority_of_generated_password_;
-
   // Tracks which bubble is currently being displayed to the user.
   CurrentBubbleOfInterest current_bubble_ = CurrentBubbleOfInterest::kNone;
 
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 729ae8a8..74f52f40 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -21,7 +21,6 @@
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/common/form_data_predictions.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 #include "components/autofill/core/common/save_password_progress_logger.h"
 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
 #include "components/password_manager/core/browser/form_saver_impl.h"
@@ -60,58 +59,6 @@
 // already.
 using Logger = autofill::SavePasswordProgressLogger;
 
-bool URLsEqualUpToScheme(const GURL& a, const GURL& b) {
-  return (a.GetContent() == b.GetContent());
-}
-
-bool URLsEqualUpToHttpHttpsSubstitution(const GURL& a, const GURL& b) {
-  if (a == b)
-    return true;
-
-  // The first-time and retry login forms action URLs sometimes differ in
-  // switching from HTTP to HTTPS, see http://crbug.com/400769.
-  if (a.SchemeIsHTTPOrHTTPS() && b.SchemeIsHTTPOrHTTPS())
-    return URLsEqualUpToScheme(a, b);
-
-  return false;
-}
-
-// Since empty or unspecified form's action is automatically set to the page
-// origin, this function checks if a form's action is empty by comparing it to
-// its origin.
-bool HasNonEmptyAction(const PasswordForm& form) {
-  return form.action != form.origin;
-}
-
-// Checks if the observed form looks like the submitted one to handle "Invalid
-// password entered" case so we don't offer a password save when we shouldn't.
-bool IsPasswordFormReappeared(const PasswordForm& observed_form,
-                              const PasswordForm& submitted_form) {
-  if (observed_form.action.is_valid() && HasNonEmptyAction(observed_form) &&
-      HasNonEmptyAction(submitted_form) &&
-      URLsEqualUpToHttpHttpsSubstitution(submitted_form.action,
-                                         observed_form.action)) {
-    return true;
-  }
-
-  // Match the form if username and password fields are same.
-  if (base::EqualsCaseInsensitiveASCII(observed_form.username_element,
-                                       submitted_form.username_element) &&
-      base::EqualsCaseInsensitiveASCII(observed_form.password_element,
-                                       submitted_form.password_element)) {
-    return true;
-  }
-
-  // Match the form if the observed username field has the same value as in
-  // the submitted form.
-  if (!submitted_form.username_value.empty() &&
-      observed_form.username_value == submitted_form.username_value) {
-    return true;
-  }
-
-  return false;
-}
-
 bool AreAllFieldsEmpty(const PasswordForm& form) {
   return form.username_value.empty() && form.password_value.empty() &&
          form.new_password_value.empty();
@@ -804,8 +751,7 @@
   if (submitted_manager->GetPendingCredentials().scheme ==
       PasswordForm::Scheme::kHtml) {
     for (const PasswordForm& form : all_visible_forms_) {
-      if (IsPasswordFormReappeared(
-              form, submitted_manager->GetPendingCredentials())) {
+      if (submitted_manager->IsEqualToSubmittedForm(form.form_data)) {
         if (submitted_manager->IsPossibleChangePasswordFormWithoutUsername() &&
             AreAllFieldsEmpty(form)) {
           continue;
diff --git a/components/password_manager/core/browser/password_manager_driver.h b/components/password_manager/core/browser/password_manager_driver.h
index 8c81c21e..81914bb 100644
--- a/components/password_manager/core/browser/password_manager_driver.h
+++ b/components/password_manager/core/browser/password_manager_driver.h
@@ -13,7 +13,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
-#include "components/autofill/core/common/password_form_field_prediction_map.h"
 
 namespace autofill {
 class AutofillDriver;
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 29235fe..1a3c2d9 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -1481,7 +1481,9 @@
 
 // Tests whether two submissions to the same origin but different schemes
 // result in only saving the first submission, which has a secure scheme.
-TEST_F(PasswordManagerTest, AttemptedSavePasswordSameOriginInsecureScheme) {
+// TODO(crbug.com/1008818): reenable this test after addressing the linked bug.
+TEST_F(PasswordManagerTest,
+       DISABLED_AttemptedSavePasswordSameOriginInsecureScheme) {
   PasswordForm secure_form(MakeSimpleForm());
   secure_form.origin = GURL("https://example.com/login");
   secure_form.action = GURL("https://example.com/login");
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index 4d60261..2aedcda1 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "components/password_manager/core/browser/password_store_sync.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/entity_change.h"
 #include "components/sync/model/metadata_batch.h"
@@ -262,7 +262,7 @@
       const sync_pb::PasswordSpecifics& specifics) {
     auto data = std::make_unique<syncer::EntityData>();
     *data->specifics.mutable_password() = specifics;
-    data->client_tag_hash = syncer::GenerateSyncableHash(
+    data->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
         syncer::PASSWORDS, bridge()->GetClientTag(*data));
     return data;
   }
diff --git a/components/policy/core/common/cloud/component_cloud_policy_service.cc b/components/policy/core/common/cloud/component_cloud_policy_service.cc
index ec8b27e2..8d3389ba 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_service.cc
+++ b/components/policy/core/common/cloud/component_cloud_policy_service.cc
@@ -285,14 +285,11 @@
          policy_type == dm_protocol::kChromeSigninExtensionPolicyType);
   CHECK(!core_->client());
 
-  external_policy_data_fetcher_backend_.reset(
-      new ExternalPolicyDataFetcherBackend(client->GetURLLoaderFactory()));
-
   backend_.reset(
       new Backend(weak_ptr_factory_.GetWeakPtr(), backend_task_runner_,
                   base::ThreadTaskRunnerHandle::Get(), std::move(cache),
-                  external_policy_data_fetcher_backend_->CreateFrontend(
-                      backend_task_runner_),
+                  std::make_unique<ExternalPolicyDataFetcher>(
+                      client->GetURLLoaderFactory(), backend_task_runner_),
                   policy_type, policy_source));
 
   // Observe the schema registry for keeping |current_schema_map_| up to date.
diff --git a/components/policy/core/common/cloud/component_cloud_policy_service.h b/components/policy/core/common/cloud/component_cloud_policy_service.h
index 8777889e..e92e72b 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_service.h
+++ b/components/policy/core/common/cloud/component_cloud_policy_service.h
@@ -29,7 +29,6 @@
 
 namespace policy {
 
-class ExternalPolicyDataFetcherBackend;
 class ResourceCache;
 class SchemaMap;
 
@@ -154,12 +153,6 @@
   CloudPolicyCore* core_;
   scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
 
-  // The |external_policy_data_fetcher_backend_| handles network I/O for the
-  // |backend_| because the system SharedURLLoaderFactory cannot be referenced
-  // from background threads. It is owned by the thread |this| runs on.
-  std::unique_ptr<ExternalPolicyDataFetcherBackend>
-      external_policy_data_fetcher_backend_;
-
   // The |backend_| handles all download scheduling, validation and caching of
   // policies. It is instantiated on the thread |this| runs on but after that,
   // must only be accessed and eventually destroyed via the
diff --git a/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc b/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc
index 6d065f05c..42b2bc4 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc
+++ b/components/policy/core/common/cloud/component_cloud_policy_updater_unittest.cc
@@ -91,7 +91,6 @@
  private:
   base::ScopedTempDir temp_dir_;
   std::unique_ptr<ResourceCache> cache_;
-  std::unique_ptr<ExternalPolicyDataFetcherBackend> fetcher_backend_;
   std::string public_key_;
 };
 
@@ -130,11 +129,10 @@
   auto url_loader_factory =
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &loader_factory_);
-  fetcher_backend_ = std::make_unique<ExternalPolicyDataFetcherBackend>(
-      std::move(url_loader_factory));
   updater_ = std::make_unique<ComponentCloudPolicyUpdater>(
       task_env_.GetMainThreadTaskRunner(),
-      fetcher_backend_->CreateFrontend(task_env_.GetMainThreadTaskRunner()),
+      std::make_unique<ExternalPolicyDataFetcher>(
+          std::move(url_loader_factory), task_env_.GetMainThreadTaskRunner()),
       store_.get());
   ASSERT_EQ(store_->policy().end(), store_->policy().begin());
 }
diff --git a/components/policy/core/common/cloud/external_policy_data_fetcher.cc b/components/policy/core/common/cloud/external_policy_data_fetcher.cc
index 27658cac..42823fd 100644
--- a/components/policy/core/common/cloud/external_policy_data_fetcher.cc
+++ b/components/policy/core/common/cloud/external_policy_data_fetcher.cc
@@ -22,28 +22,16 @@
 
 namespace policy {
 
-namespace {
-
-// Helper that forwards a job cancelation confirmation from the thread that the
-// ExternalPolicyDataFetcherBackend runs on to the thread that the
-// ExternalPolicyDataFetcher which canceled the job runs on.
-void ForwardJobCanceled(scoped_refptr<base::SequencedTaskRunner> task_runner,
-                        base::OnceClosure callback) {
-  task_runner->PostTask(FROM_HERE, std::move(callback));
-}
-
-}  // namespace
-
 class ExternalPolicyDataFetcher::Job
     : public network::SimpleURLLoaderStreamConsumer {
  public:
-  Job(base::WeakPtr<ExternalPolicyDataFetcher> fetcher,
-      scoped_refptr<base::SequencedTaskRunner> frontend_task_runner,
+  Job(std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+          url_loader_factory_info,
+      base::WeakPtr<ExternalPolicyDataFetcher> fetcher,
+      scoped_refptr<base::SequencedTaskRunner> fetcher_task_runner,
       ExternalPolicyDataFetcher::FetchCallback callback);
 
-  void Start(network::mojom::URLLoaderFactory* url_loader_factory,
-             const GURL& url,
-             int64_t max_size);
+  void Start(const GURL& url, int64_t max_size);
   void Cancel();
   void OnResponseStarted(const GURL& final_url,
                          const network::mojom::URLResponseHead& response_head);
@@ -59,8 +47,9 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
+  std::unique_ptr<network::SharedURLLoaderFactoryInfo> url_loader_factory_info_;
   base::WeakPtr<ExternalPolicyDataFetcher> fetcher_;
-  scoped_refptr<base::SequencedTaskRunner> frontend_task_runner_;
+  scoped_refptr<base::SequencedTaskRunner> fetcher_task_runner_;
   ExternalPolicyDataFetcher::FetchCallback callback_;
   std::unique_ptr<network::SimpleURLLoader> url_loader_;
   std::string response_body_;
@@ -70,21 +59,25 @@
 };
 
 ExternalPolicyDataFetcher::Job::Job(
+    std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+        url_loader_factory_info,
     base::WeakPtr<ExternalPolicyDataFetcher> fetcher,
-    scoped_refptr<base::SequencedTaskRunner> frontend_task_runner,
+    scoped_refptr<base::SequencedTaskRunner> fetcher_task_runner,
     ExternalPolicyDataFetcher::FetchCallback callback)
-    : fetcher_(std::move(fetcher)),
-      frontend_task_runner_(std::move(frontend_task_runner)),
+    : url_loader_factory_info_(std::move(url_loader_factory_info)),
+      fetcher_(std::move(fetcher)),
+      fetcher_task_runner_(std::move(fetcher_task_runner)),
       callback_(std::move(callback)) {
-  // A job is created on the frontend thread but receives callbacks on the
-  // backend's sequence.
+  // A job is created on the fetcher sequence but it then lives on the separate
+  // job sequence.
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
 void ExternalPolicyDataFetcher::Job::Start(
-    network::mojom::URLLoaderFactory* url_loader_factory,
     const GURL& url,
     int64_t max_size) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   DCHECK_GE(max_size, 0);
   max_size_ = max_size;
 
@@ -125,16 +118,23 @@
   url_loader_->SetOnResponseStartedCallback(
       base::BindOnce(&ExternalPolicyDataFetcher::Job::OnResponseStarted,
                      base::Unretained(this)));
-  url_loader_->DownloadAsStream(url_loader_factory, this);
+  url_loader_->DownloadAsStream(network::SharedURLLoaderFactory::Create(
+                                    std::move(url_loader_factory_info_))
+                                    .get(),
+                                this);
 }
 
 void ExternalPolicyDataFetcher::Job::Cancel() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   url_loader_.reset();
 }
 
 void ExternalPolicyDataFetcher::Job::OnResponseStarted(
     const GURL& /* final_url */,
     const network::mojom::URLResponseHead& response_head) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (response_head.content_length != -1 &&
       response_head.content_length > max_size_) {
     url_loader_.reset();
@@ -146,6 +146,8 @@
 void ExternalPolicyDataFetcher::Job::OnDataReceived(
     base::StringPiece string_piece,
     base::OnceClosure resume) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   if (response_body_.length() + string_piece.length() >
       static_cast<uint64_t>(max_size_)) {
     url_loader_.reset();
@@ -199,6 +201,8 @@
 }
 
 void ExternalPolicyDataFetcher::Job::OnRetry(base::OnceClosure start_retry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   response_body_.clear();
   std::move(start_retry).Run();
 }
@@ -206,18 +210,21 @@
 void ExternalPolicyDataFetcher::Job::ReportFinished(
     Result result,
     std::unique_ptr<std::string> data) {
-  frontend_task_runner_->PostTask(
+  fetcher_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&ExternalPolicyDataFetcher::OnJobFinished, fetcher_,
                      std::move(callback_), this, result, std::move(data)));
 }
 
 ExternalPolicyDataFetcher::ExternalPolicyDataFetcher(
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    const base::WeakPtr<ExternalPolicyDataFetcherBackend>& backend)
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
     : task_runner_(std::move(task_runner)),
-      backend_task_runner_(base::ThreadTaskRunnerHandle::Get()),
-      backend_(backend) {}
+      job_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+  // |url_loader_factory| is null in some tests.
+  if (url_loader_factory)
+    url_loader_factory_info_ = url_loader_factory->Clone();
+}
 
 ExternalPolicyDataFetcher::~ExternalPolicyDataFetcher() {
   // No RunsTasksInCurrentSequence() check to avoid unit tests failures.
@@ -237,12 +244,17 @@
     int64_t max_size,
     FetchCallback callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
+  if (!cloned_url_loader_factory_) {
+    cloned_url_loader_factory_ = network::SharedURLLoaderFactory::Create(
+        std::move(url_loader_factory_info_));
+  }
   Job* job =
-      new Job(weak_factory_.GetWeakPtr(), task_runner_, std::move(callback));
+      new Job(cloned_url_loader_factory_->Clone(), weak_factory_.GetWeakPtr(),
+              task_runner_, std::move(callback));
   jobs_.insert(job);
-  backend_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&ExternalPolicyDataFetcherBackend::StartJob,
-                                backend_, url, max_size, job));
+  job_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&Job::Start, base::Unretained(job), url, max_size));
   return job;
 }
 
@@ -250,19 +262,15 @@
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   DCHECK(jobs_.find(job) != jobs_.end());
   jobs_.erase(job);
-  // Post a task that will cancel the |job| in the |backend_|. The |job| is
-  // removed from |jobs_| immediately to indicate that it has been canceled but
-  // is not actually deleted until the cancelation has reached the |backend_|
-  // and a confirmation has been posted back. This ensures that no new job can
-  // be allocated at the same address while an OnJobFinished() callback may
-  // still be pending for the canceled |job|.
-  backend_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &ExternalPolicyDataFetcherBackend::CancelJob, backend_, job,
-          base::BindOnce(&ForwardJobCanceled, task_runner_,
-                         base::BindOnce(base::DoNothing::Once<Job*>(),
-                                        base::Owned(job)))));
+  // Post a task that will cancel the |job| in the |job_task_runner_|. The |job|
+  // is removed from |jobs_| immediately to indicate that it has been canceled
+  // but is not actually deleted until the cancellation has reached the
+  // |job_task_runner_| and a confirmation has been posted back. This ensures
+  // that no new job can be allocated at the same address while an
+  // OnJobFinished() callback may still be pending for the canceled |job|.
+  job_task_runner_->PostTaskAndReply(
+      FROM_HERE, base::BindOnce(&Job::Cancel, base::Unretained(job)),
+      base::BindOnce(base::DoNothing::Once<Job*>(), base::Owned(job)));
 }
 
 void ExternalPolicyDataFetcher::OnJobFinished(
@@ -274,8 +282,8 @@
   auto it = jobs_.find(job);
   if (it == jobs_.end()) {
     // The |job| has been canceled and removed from |jobs_| already. This can
-    // happen because the |backend_| runs on a different thread and a |job| may
-    // finish before the cancellation has reached that thread.
+    // happen because the jobs run on a different sequence and a |job| may
+    // finish before the cancellation has reached that sequence.
     return;
   }
   std::move(callback).Run(result, std::move(data));
@@ -283,35 +291,4 @@
   delete job;
 }
 
-ExternalPolicyDataFetcherBackend::ExternalPolicyDataFetcherBackend(
-    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : url_loader_factory_(std::move(url_loader_factory)) {}
-
-ExternalPolicyDataFetcherBackend::~ExternalPolicyDataFetcherBackend() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-std::unique_ptr<ExternalPolicyDataFetcher>
-ExternalPolicyDataFetcherBackend::CreateFrontend(
-    scoped_refptr<base::SequencedTaskRunner> frontend_task_runner) {
-  return std::make_unique<ExternalPolicyDataFetcher>(
-      std::move(frontend_task_runner), weak_factory_.GetWeakPtr());
-}
-
-void ExternalPolicyDataFetcherBackend::StartJob(
-    const GURL& url,
-    int64_t max_size,
-    ExternalPolicyDataFetcher::Job* job) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  job->Start(url_loader_factory_.get(), url, max_size);
-}
-
-void ExternalPolicyDataFetcherBackend::CancelJob(
-    ExternalPolicyDataFetcher::Job* job,
-    base::OnceClosure callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  job->Cancel();
-  std::move(callback).Run();
-}
-
 }  // namespace policy
diff --git a/components/policy/core/common/cloud/external_policy_data_fetcher.h b/components/policy/core/common/cloud/external_policy_data_fetcher.h
index acc8aec2..ae3d12a 100644
--- a/components/policy/core/common/cloud/external_policy_data_fetcher.h
+++ b/components/policy/core/common/cloud/external_policy_data_fetcher.h
@@ -26,17 +26,14 @@
 }
 
 namespace network {
+class SharedURLLoaderFactoryInfo;
 class SharedURLLoaderFactory;
 }
 
 namespace policy {
 
-class ExternalPolicyDataFetcherBackend;
-
 // This class handles network fetch jobs for the ExternalPolicyDataUpdater by
-// forwarding them to an ExternalPolicyDataFetcherBackend running on a different
-// thread. This is necessary because the ExternalPolicyDataUpdater runs on a
-// background thread where network I/O is not allowed.
+// forwarding them to a job running on a different thread.
 // The class can be instantiated on any thread but from then on, it must be
 // accessed and destroyed on the background thread that the
 // ExternalPolicyDataUpdater runs on only.
@@ -71,11 +68,9 @@
       base::OnceCallback<void(Result, std::unique_ptr<std::string>)>;
 
   // |task_runner| represents the background thread that |this| runs on.
-  // |backend| is used to perform network I/O. It must be dereferenced and
-  // accessed via |task_runner| only.
   ExternalPolicyDataFetcher(
-      scoped_refptr<base::SequencedTaskRunner> task_runner,
-      const base::WeakPtr<ExternalPolicyDataFetcherBackend>& backend);
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      scoped_refptr<base::SequencedTaskRunner> task_runner);
   ~ExternalPolicyDataFetcher();
 
   // Fetch data from |url| and invoke |callback| with the result. See the
@@ -98,13 +93,15 @@
 
   // Task runner representing the thread that |this| runs on.
   const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  // Task runner for running the fetch jobs. It's the task runner on which this
+  // instance was created.
+  const scoped_refptr<base::SequencedTaskRunner> job_task_runner_;
 
-  // Task runner representing the thread on which the |backend_| runs.
-  const scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
-
-  // The |backend_| is used to perform network I/O. It may be dereferenced and
-  // accessed via |backend_task_runner_| only.
-  base::WeakPtr<ExternalPolicyDataFetcherBackend> backend_;
+  // The information for the lazy creation of |cloned_url_loader_factory_|.
+  std::unique_ptr<network::SharedURLLoaderFactoryInfo> url_loader_factory_info_;
+  // The cloned factory that can be used from |task_runner_|. It's created
+  // lazily, as our constructor runs on a difference sequence.
+  scoped_refptr<network::SharedURLLoaderFactory> cloned_url_loader_factory_;
 
   // Set that owns all currently running Jobs.
   typedef std::set<Job*> JobSet;
@@ -115,44 +112,6 @@
   DISALLOW_COPY_AND_ASSIGN(ExternalPolicyDataFetcher);
 };
 
-// This class handles network I/O for one or more ExternalPolicyDataFetchers. It
-// can be instantiated on any thread that is allowed to reference
-// SharedURLLoaderFactories (in Chrome, these are the UI and IO threads) and
-// CreateFrontend() may be called from the same thread after instantiation. From
-// then on, it must be accessed and destroyed on the thread that handles network
-// I/O only (in Chrome, this is the IO thread).
-class POLICY_EXPORT ExternalPolicyDataFetcherBackend {
- public:
-  explicit ExternalPolicyDataFetcherBackend(
-      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-  ~ExternalPolicyDataFetcherBackend();
-
-  // Create an ExternalPolicyDataFetcher that allows fetch jobs to be started
-  // from |frontend_task_runner|.
-  std::unique_ptr<ExternalPolicyDataFetcher> CreateFrontend(
-      scoped_refptr<base::SequencedTaskRunner> frontend_task_runner);
-
-  // Start a fetch job defined by |job|. The caller retains ownership of |job|
-  // and must ensure that it remains valid until the job ends, CancelJob() is
-  // called or |this| is destroyed.
-  void StartJob(const GURL& url,
-                int64_t max_size,
-                ExternalPolicyDataFetcher::Job* job);
-
-  // Cancel the fetch job defined by |job| and invoke |callback| to confirm.
-  void CancelJob(ExternalPolicyDataFetcher::Job* job,
-                 base::OnceClosure callback);
-
- private:
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
-  base::WeakPtrFactory<ExternalPolicyDataFetcherBackend> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(ExternalPolicyDataFetcherBackend);
-};
-
-
 }  // namespace policy
 
 #endif  // COMPONENTS_POLICY_CORE_COMMON_CLOUD_EXTERNAL_POLICY_DATA_FETCHER_H_
diff --git a/components/policy/core/common/cloud/external_policy_data_fetcher_unittest.cc b/components/policy/core/common/cloud/external_policy_data_fetcher_unittest.cc
index e3cfe139..e839482 100644
--- a/components/policy/core/common/cloud/external_policy_data_fetcher_unittest.cc
+++ b/components/policy/core/common/cloud/external_policy_data_fetcher_unittest.cc
@@ -55,7 +55,6 @@
   base::test::TaskEnvironment task_environment_;
   scoped_refptr<base::TestSimpleTaskRunner> owner_task_runner_;
   network::TestURLLoaderFactory test_url_loader_factory_;
-  std::unique_ptr<ExternalPolicyDataFetcherBackend> fetcher_backend_;
   std::unique_ptr<ExternalPolicyDataFetcher> fetcher_;
 
   std::map<int, ExternalPolicyDataFetcher::Job*> jobs_;  // Not owned.
@@ -79,10 +78,9 @@
   auto url_loader_factory =
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &test_url_loader_factory_);
-  fetcher_backend_ = std::make_unique<ExternalPolicyDataFetcherBackend>(
-      std::move(url_loader_factory));
   owner_task_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
-  fetcher_ = fetcher_backend_->CreateFrontend(owner_task_runner_);
+  fetcher_ = std::make_unique<ExternalPolicyDataFetcher>(
+      std::move(url_loader_factory), owner_task_runner_);
 }
 
 void ExternalPolicyDataFetcherTest::StartJob(int index) {
@@ -313,7 +311,7 @@
   EXPECT_EQ(0, test_url_loader_factory_.NumPending());
 
   // Cancel the fetch job before the successful fetch result has arrived from
-  // the backend.
+  // the job.
   CancelJob(0);
 
   // Verify that the callback is not invoked.
diff --git a/components/policy/core/common/cloud/external_policy_data_updater_unittest.cc b/components/policy/core/common/cloud/external_policy_data_updater_unittest.cc
index 8fed109..ff036ea7 100644
--- a/components/policy/core/common/cloud/external_policy_data_updater_unittest.cc
+++ b/components/policy/core/common/cloud/external_policy_data_updater_unittest.cc
@@ -77,7 +77,6 @@
   network::TestURLLoaderFactory test_url_loader_factory_;
   MockFetchSuccessCallbackListener callback_listener_;
   scoped_refptr<base::TestSimpleTaskRunner> backend_task_runner_;
-  std::unique_ptr<ExternalPolicyDataFetcherBackend> fetcher_backend_;
   std::unique_ptr<ExternalPolicyDataUpdater> updater_;
 };
 
@@ -89,11 +88,10 @@
   auto url_loader_factory =
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &test_url_loader_factory_);
-  fetcher_backend_ = std::make_unique<ExternalPolicyDataFetcherBackend>(
-      std::move(url_loader_factory));
   updater_ = std::make_unique<ExternalPolicyDataUpdater>(
       backend_task_runner_,
-      fetcher_backend_->CreateFrontend(backend_task_runner_),
+      std::make_unique<ExternalPolicyDataFetcher>(std::move(url_loader_factory),
+                                                  backend_task_runner_),
       max_parallel_fetches);
 }
 
diff --git a/components/policy/proto/chrome_device_policy.proto b/components/policy/proto/chrome_device_policy.proto
index c5c25b72..e96148c8 100644
--- a/components/policy/proto/chrome_device_policy.proto
+++ b/components/policy/proto/chrome_device_policy.proto
@@ -692,6 +692,38 @@
   // status on the login screen is persisted between users.
   optional bool login_screen_cursor_highlight_enabled = 18;
   optional PolicyOptions login_screen_cursor_highlight_enabled_options = 19;
+
+  // Sets the state of the caret highlight accessibility feature on the login
+  // screen. If this policy is set to true, the caret highlight will be enabled
+  // when the login screen is shown. If this policy is set to false, the spoken
+  // feedback will be disabled when the caret highlight is shown. If the
+  // PolicyOptions mode was being mandatory then the user won't be able to
+  // change these settings. Only if PolicyOptions was being set as recommended
+  // users can temporarily override this setting by enabling or disabling the
+  // caret highlight. However, the user's choice is not persistent and the
+  // default is restored whenever the login screen is shown anew or the user
+  // remains idle on the login screen for a minute. If this policy is left
+  // unset, the caret highlight is disabled when the login screen is first
+  // shown. Users can enable or disable the caret highlight anytime and its
+  // status on the login screen is persisted between users.
+  optional bool login_screen_caret_highlight_enabled = 20;
+  optional PolicyOptions login_screen_caret_highlight_enabled_options = 21;
+
+  // Sets the state of the mono audio accessibility feature on the login
+  // screen. If this policy is set to true, the mono audio will be enabled
+  // when the login screen is shown. If this policy is set to false, the mono
+  // audio will be disabled when the mono audio is shown. If the PolicyOptions
+  // mode was being mandatory then the user won't be able to change these
+  // settings. Only if PolicyOptions was being set as recommended users can
+  // temporarily override this setting by enabling or disabling the mono audio.
+  // However, the user's choice is not persistent and the default is restored
+  // whenever the login screen is shown anew or the user remains idle on the
+  // login screen for a minute. If this policy is left unset, the mono audio is
+  // disabled when the login screen is first shown. Users can enable or disable
+  // the mono audio anytime and its status on the login screen is persisted
+  // between users.
+  optional bool login_screen_mono_audio_enabled = 22;
+  optional PolicyOptions login_screen_mono_audio_enabled_options = 23;
 }
 
 message SupervisedUsersSettingsProto {
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index fee5798..0244913 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -648,6 +648,8 @@
         'DeviceLoginScreenDictationEnabled',
         'DeviceLoginScreenSelectToSpeakEnabled',
         'DeviceLoginScreenCursorHighlightEnabled',
+        'DeviceLoginScreenCaretHighlightEnabled',
+        'DeviceLoginScreenMonoAudioEnabled',
       ],
     },
     {
@@ -10454,6 +10456,58 @@
           If this policy is left unset, the cursor highlight is disabled on the login screen initially but can be enabled by the user anytime.''',
     },
     {
+      'name': 'DeviceLoginScreenCaretHighlightEnabled',
+      'owners': ['amraboelkher@chromium.org', 'emaxx@chromium.org'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome_os:79-'],
+      'device_only': True,
+      'features': {
+        'can_be_recommended': True,
+        'dynamic_refresh': True,
+      },
+      'example_value': True,
+      'id': 614,
+      'caption': '''Enable the caret highlight on the login screen''',
+      'tags': [],
+      'desc': '''Enable the caret highlight accessibility feature on the login screen.
+
+          If this policy is set to true, the caret highlight will always be enabled on the login screen.
+
+          If this policy is set to false, the caret highlight will always be disabled on the login screen.
+
+          If you set this policy, users cannot change or override it.
+
+          If this policy is left unset, the caret highlight is disabled on the login screen initially but can be enabled by the user anytime.''',
+    },
+    {
+      'name': 'DeviceLoginScreenMonoAudioEnabled',
+      'owners': ['amraboelkher@chromium.org', 'emaxx@chromium.org'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome_os:79-'],
+      'device_only': True,
+      'features': {
+        'can_be_recommended': True,
+        'dynamic_refresh': True,
+      },
+      'example_value': True,
+      'id': 615,
+      'caption': '''Enable the mono audio on the login screen''',
+      'tags': [],
+      'desc': '''Enable the mono audio accessibility feature on the login screen.
+
+          This feature allows to switch the device mode from the default stereo audio to the mono audio.
+
+          If this policy is set to true, the mono audio will always be enabled on the login screen.
+
+          If this policy is set to false, the mono audio will always be disabled on the login screen.
+
+          If you set this policy, users cannot change or override it.
+
+          If this policy is left unset, the mono audio is disabled on the login screen initially but can be enabled by the user anytime.''',
+    },
+    {
       'name': 'HideWebStoreIcon',
       'owners': ['file://components/policy/resources/OWNERS'],
       'type': 'main',
@@ -18245,6 +18299,8 @@
     'DeviceLoginScreenDictationEnabled': 'accessibility_settings.login_screen_dictation_enabled',
     'DeviceLoginScreenSelectToSpeakEnabled': 'accessibility_settings.login_screen_select_to_speak_enabled',
     'DeviceLoginScreenCursorHighlightEnabled': 'accessibility_settings.login_screen_cursor_highlight_enabled',
+    'DeviceLoginScreenCaretHighlightEnabled': 'accessibility_settings.login_screen_caret_highlight_enabled',
+    'DeviceLoginScreenMonoAudioEnabled': 'accessibility_settings.login_screen_mono_audio_enabled',
     'AttestationEnabledForDevice': 'attestation_settings.attestation_enabled',
     'AttestationForContentProtectionEnabled': 'attestation_settings.content_protection_enabled',
     'SystemTimezone': 'system_timezone.timezone',
@@ -18745,6 +18801,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569],
-  'highest_id_currently_used': 613,
+  'highest_id_currently_used': 615,
   'highest_atomic_group_id_currently_used': 37
 }
diff --git a/components/signin/ios/browser/BUILD.gn b/components/signin/ios/browser/BUILD.gn
index 095aa04f..dd87e0b6 100644
--- a/components/signin/ios/browser/BUILD.gn
+++ b/components/signin/ios/browser/BUILD.gn
@@ -7,6 +7,8 @@
   sources = [
     "account_consistency_service.h",
     "account_consistency_service.mm",
+    "features.cc",
+    "features.h",
     "manage_accounts_delegate.h",
     "wait_for_network_callback_helper.cc",
     "wait_for_network_callback_helper.h",
diff --git a/components/signin/ios/browser/features.cc b/components/signin/ios/browser/features.cc
new file mode 100644
index 0000000..9419512
--- /dev/null
+++ b/components/signin/ios/browser/features.cc
@@ -0,0 +1,16 @@
+// Copyright 2019 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/signin/ios/browser/features.h"
+
+namespace signin {
+
+const base::Feature kForceStartupSigninPromo{"ForceStartupSigninPromo",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool ForceStartupSigninPromo() {
+  return base::FeatureList::IsEnabled(kForceStartupSigninPromo);
+}
+
+}  // namespace signin
diff --git a/components/signin/ios/browser/features.h b/components/signin/ios/browser/features.h
new file mode 100644
index 0000000..5cdf203
--- /dev/null
+++ b/components/signin/ios/browser/features.h
@@ -0,0 +1,20 @@
+// Copyright 2019 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_SIGNIN_IOS_BROWSER_FEATURES_H_
+#define COMPONENTS_SIGNIN_IOS_BROWSER_FEATURES_H_
+
+#include "base/feature_list.h"
+
+namespace signin {
+
+// Features to trigger the startup sign-in promo at boot.
+extern const base::Feature kForceStartupSigninPromo;
+
+// Returns true if the startup sign-in promo should be displayed at boot.
+bool ForceStartupSigninPromo();
+
+}  // namespace signin
+
+#endif  // COMPONENTS_SIGNIN_IOS_BROWSER_FEATURES_H_
diff --git a/components/ssl_errors_strings.grdp b/components/ssl_errors_strings.grdp
index d39cd09..5026b76 100644
--- a/components/ssl_errors_strings.grdp
+++ b/components/ssl_errors_strings.grdp
@@ -13,7 +13,7 @@
 
   <message name="IDS_CERT_ERROR_EXPIRED_DETAILS" desc="Details for an expired X509 certificate [ICU Syntax]">
     {1, plural,
-     =1 {This server could not prove that it is <ph name="DOMAIN">&lt;strong&gt;{0}<ex>paypal.com</ex>&lt;/strong&gt;</ph>; its security certificate expired yesterday. This may be caused by a misconfiguration or an attacker intercepting your connection. Your computer's clock is currently set to <ph name="CURRENT_DATE">{2, date, full}<ex>Monday, July 16, 2012</ex></ph>. Does that look right? If not, you should correct your system's clock and then refresh this page.}
+     =1 {This server could not prove that it is <ph name="DOMAIN">&lt;strong&gt;{0}<ex>paypal.com</ex>&lt;/strong&gt;</ph>; its security certificate expired in the last day. This may be caused by a misconfiguration or an attacker intercepting your connection. Your computer's clock is currently set to <ph name="CURRENT_DATE">{2, date, full}<ex>Monday, July 16, 2012</ex></ph>. Does that look right? If not, you should correct your system's clock and then refresh this page.}
      other {This server could not prove that it is <ph name="DOMAIN">&lt;strong&gt;{0}<ex>paypal.com</ex>&lt;/strong&gt;</ph>; its security certificate expired # days ago. This may be caused by a misconfiguration or an attacker intercepting your connection. Your computer's clock is currently set to <ph name="CURRENT_DATE">{2, date, full}<ex>Monday, July 16, 2012</ex></ph>. Does that look right? If not, you should correct your system's clock and then refresh this page.}}
   </message>
   <message name="IDS_CERT_ERROR_EXPIRED_DESCRIPTION" desc="Description for an expired X509 certificate">
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 6905a3bb..382d2c6e39 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -210,7 +210,6 @@
     "engine_impl/non_blocking_type_commit_contribution.h",
     "engine_impl/nudge_handler.cc",
     "engine_impl/nudge_handler.h",
-    "engine_impl/nudge_source.cc",
     "engine_impl/nudge_source.h",
     "engine_impl/process_updates_util.cc",
     "engine_impl/process_updates_util.h",
@@ -429,6 +428,7 @@
     "//base:i18n",
     "//build:branding_buildflags",
     "//components/keyed_service/core",
+    "//components/variations/net",
     "//services/network/public/cpp",
     "//sql",
     "//third_party/cacheinvalidation",
@@ -562,9 +562,9 @@
   sources = [
     "base/bind_to_task_runner_unittest.cc",
     "base/cancelation_signal_unittest.cc",
+    "base/client_tag_hash_unittest.cc",
     "base/data_type_histogram_unittest.cc",
     "base/enum_set_unittest.cc",
-    "base/hash_util_unittest.cc",
     "base/immutable_unittest.cc",
     "base/model_type_unittest.cc",
     "base/node_ordinal_unittest.cc",
diff --git a/components/sync/PRESUBMIT.py b/components/sync/PRESUBMIT.py
index 08f919e92..062e29ea 100644
--- a/components/sync/PRESUBMIT.py
+++ b/components/sync/PRESUBMIT.py
@@ -27,13 +27,6 @@
   'SUPERVISED_USER_WHITELISTS',  # See previous.
 
   # Deprecated types:
-  'DEPRECATED_APP_NOTIFICATIONS',
-  'DEPRECATED_ARTICLES',
-  'DEPRECATED_SUPERVISED_USERS',
-  'DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS',
-  'DEPRECATED_SYNCED_NOTIFICATIONS',
-  'DEPRECATED_SYNCED_NOTIFICATION_APP_INFO',
-  'DEPRECATED_WIFI_CREDENTIALS',
   'DEPRECATED_EXPERIMENTS']
 
 # Root tags are used as prefixes when creating storage keys, so certain strings
@@ -176,8 +169,8 @@
   for line in file_contents.splitlines():
     current_line_number += 1
     if line.strip().startswith('//'):
-        # Ignore comments.
-        continue
+      # Ignore comments.
+      continue
     if start_pattern.match(line):
       inside_enum = True
       continue
@@ -376,8 +369,7 @@
     => 'AppSpecifics'
   """
   return field_number.replace(FIELD_NUMBER_PREFIX, '').replace(
-    'FieldNumber', 'Specifics').replace(
-    'AppNotificationSpecifics', 'AppNotification')
+    'FieldNumber', 'Specifics')
 
 def CheckChangeLintsClean(input_api, output_api):
   source_filter = lambda x: input_api.FilterSourceFile(
diff --git a/components/sync/PRESUBMIT_test.py b/components/sync/PRESUBMIT_test.py
index f717bcbf..4a84129 100755
--- a/components/sync/PRESUBMIT_test.py
+++ b/components/sync/PRESUBMIT_test.py
@@ -77,8 +77,7 @@
   '    AppSpecifics app = 456;\n'
   '    AppSettingSpecifics app_setting = 789;\n'
   '    ExtensionSettingSpecifics extension_setting = 910;\n'
-  '    ManagedUserSharedSettingSpecifics managed_user_shared_setting'
-                                                                    ' = 915;\n'
+  '    ExperimentsSpecifics experiments = 161496;\n'
   '    //comment\n'
   '  }\n'
   '}\n'
@@ -114,10 +113,9 @@
     self.assertEqual(0, len(results))
 
   def testValidChangeDeprecatedEntry(self):
-    results = self._testChange('{DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS,'
-    '"MANAGED_USER_SHARED_SETTING", "managed_user_shared_settings",'
-    '"Managed User Shared Settings",'
-    'sync_pb::EntitySpecifics::kManagedUserSharedSettingFieldNumber, 30},')
+    results = self._testChange('{DEPRECATED_EXPERIMENTS, "EXPERIMENTS",'
+      '"experiments", "Experiments",'
+      'sync_pb::EntitySpecifics::kExperimentsFieldNumber, 19},')
     self.assertEqual(0, len(results))
 
   def testInvalidChangeMismatchedNotificationType(self):
diff --git a/components/sync/base/BUILD.gn b/components/sync/base/BUILD.gn
index 5146d93..2986bd5 100644
--- a/components/sync/base/BUILD.gn
+++ b/components/sync/base/BUILD.gn
@@ -12,6 +12,8 @@
     "cancelation_observer.h",
     "cancelation_signal.cc",
     "cancelation_signal.h",
+    "client_tag_hash.cc",
+    "client_tag_hash.h",
     "data_type_histogram.cc",
     "data_type_histogram.h",
     "encryptor.h",
diff --git a/components/sync/base/client_tag_hash.cc b/components/sync/base/client_tag_hash.cc
new file mode 100644
index 0000000..04fdbe4
--- /dev/null
+++ b/components/sync/base/client_tag_hash.cc
@@ -0,0 +1,72 @@
+// Copyright 2019 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/base/client_tag_hash.h"
+
+#include <utility>
+
+#include "base/base64.h"
+#include "base/hash/sha1.h"
+#include "base/trace_event/memory_usage_estimator.h"
+#include "components/sync/protocol/sync.pb.h"
+
+namespace syncer {
+
+// static
+ClientTagHash ClientTagHash::FromUnhashed(ModelType model_type,
+                                          const std::string& client_tag) {
+  // Blank PB with just the field in it has termination symbol,
+  // handy for delimiter.
+  sync_pb::EntitySpecifics serialized_type;
+  AddDefaultFieldValue(model_type, &serialized_type);
+  std::string hash_input;
+  serialized_type.AppendToString(&hash_input);
+  hash_input.append(client_tag);
+
+  std::string encode_output;
+  base::Base64Encode(base::SHA1HashString(hash_input), &encode_output);
+  return FromHashed(encode_output);
+}
+
+// static
+ClientTagHash ClientTagHash::FromHashed(std::string hash_value) {
+  return ClientTagHash(std::move(hash_value));
+}
+
+ClientTagHash::ClientTagHash() = default;
+
+ClientTagHash::ClientTagHash(std::string value) : value_(std::move(value)) {}
+
+ClientTagHash::ClientTagHash(const ClientTagHash& other) = default;
+
+ClientTagHash::ClientTagHash(ClientTagHash&& other) = default;
+
+ClientTagHash::~ClientTagHash() = default;
+
+ClientTagHash& ClientTagHash::operator=(const ClientTagHash& other) = default;
+
+ClientTagHash& ClientTagHash::operator=(ClientTagHash&& other) = default;
+
+size_t ClientTagHash::EstimateMemoryUsage() const {
+  return base::trace_event::EstimateMemoryUsage(value_);
+}
+
+bool operator<(const ClientTagHash& lhs, const ClientTagHash& rhs) {
+  return lhs.value() < rhs.value();
+}
+
+bool operator==(const ClientTagHash& lhs, const ClientTagHash& rhs) {
+  return lhs.value() == rhs.value();
+}
+
+bool operator!=(const ClientTagHash& lhs, const ClientTagHash& rhs) {
+  return lhs.value() != rhs.value();
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const ClientTagHash& client_tag_hash) {
+  return os << client_tag_hash.value();
+}
+
+}  // namespace syncer
diff --git a/components/sync/base/client_tag_hash.h b/components/sync/base/client_tag_hash.h
new file mode 100644
index 0000000..21c7d18
--- /dev/null
+++ b/components/sync/base/client_tag_hash.h
@@ -0,0 +1,53 @@
+// Copyright 2019 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_BASE_CLIENT_TAG_HASH_H_
+#define COMPONENTS_SYNC_BASE_CLIENT_TAG_HASH_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "components/sync/base/model_type.h"
+
+namespace syncer {
+
+// Represents a client defined unique hash for sync entities. Hash is derived
+// from client tag, and should be used as |client_defined_unique_tag| for
+// SyncEntity at least for CommitMessages. For convenience it supports storing
+// in ordered stl containers, logging and equality comparisons.
+class ClientTagHash {
+ public:
+  // Creates ClientTagHash based on |client_tag|.
+  static ClientTagHash FromUnhashed(ModelType type,
+                                    const std::string& client_tag);
+
+  // Creates ClientTagHash from already hashed client tag.
+  static ClientTagHash FromHashed(std::string hash_value);
+
+  ClientTagHash();
+  ClientTagHash(const ClientTagHash& other);
+  ClientTagHash(ClientTagHash&& other);
+  ~ClientTagHash();
+
+  ClientTagHash& operator=(const ClientTagHash& other);
+  ClientTagHash& operator=(ClientTagHash&& other);
+
+  const std::string& value() const { return value_; }
+
+  size_t EstimateMemoryUsage() const;
+
+ private:
+  explicit ClientTagHash(std::string value);
+  std::string value_;
+};
+
+bool operator<(const ClientTagHash& lhs, const ClientTagHash& rhs);
+bool operator==(const ClientTagHash& lhs, const ClientTagHash& rhs);
+bool operator!=(const ClientTagHash& lhs, const ClientTagHash& rhs);
+std::ostream& operator<<(std::ostream& os,
+                         const ClientTagHash& client_tag_hash);
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_BASE_CLIENT_TAG_HASH_H_
diff --git a/components/sync/base/client_tag_hash_unittest.cc b/components/sync/base/client_tag_hash_unittest.cc
new file mode 100644
index 0000000..35dfc17
--- /dev/null
+++ b/components/sync/base/client_tag_hash_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright 2019 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/base/client_tag_hash.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+// Tests that the hashing algorithm has not changed.
+TEST(ClientTagHashTest, ShouldGenerateFromUnhashed) {
+  EXPECT_EQ("iNFQtRFQb+IZcn1kKUJEZDDkLs4=",
+            ClientTagHash::FromUnhashed(PREFERENCES, "tag1").value());
+  EXPECT_EQ("gO1cPZQXaM73sHOvSA+tKCKFs58=",
+            ClientTagHash::FromUnhashed(AUTOFILL, "tag1").value());
+
+  EXPECT_EQ("XYxkF7bhS4eItStFgiOIAU23swI=",
+            ClientTagHash::FromUnhashed(PREFERENCES, "tag2").value());
+  EXPECT_EQ("GFiWzo5NGhjLlN+OyCfhy28DJTQ=",
+            ClientTagHash::FromUnhashed(AUTOFILL, "tag2").value());
+}
+
+}  // namespace syncer
diff --git a/components/sync/base/data_type_histogram.h b/components/sync/base/data_type_histogram.h
index 4c5131b..c9b7674 100644
--- a/components/sync/base/data_type_histogram.h
+++ b/components/sync/base/data_type_histogram.h
@@ -89,18 +89,9 @@
       case ::syncer::EXTENSION_SETTINGS:                         \
         PER_DATA_TYPE_MACRO("ExtensionSettings");                \
         break;                                                   \
-      case ::syncer::DEPRECATED_APP_NOTIFICATIONS:               \
-        PER_DATA_TYPE_MACRO("AppNotifications");                 \
-        break;                                                   \
       case ::syncer::HISTORY_DELETE_DIRECTIVES:                  \
         PER_DATA_TYPE_MACRO("HistoryDeleteDirectives");          \
         break;                                                   \
-      case ::syncer::DEPRECATED_SYNCED_NOTIFICATIONS:            \
-        PER_DATA_TYPE_MACRO("SyncedNotifications");              \
-        break;                                                   \
-      case ::syncer::DEPRECATED_SYNCED_NOTIFICATION_APP_INFO:    \
-        PER_DATA_TYPE_MACRO("SyncedNotificationAppInfo");        \
-        break;                                                   \
       case ::syncer::DICTIONARY:                                 \
         PER_DATA_TYPE_MACRO("Dictionary");                       \
         break;                                                   \
@@ -119,21 +110,9 @@
       case ::syncer::SUPERVISED_USER_SETTINGS:                   \
         PER_DATA_TYPE_MACRO("ManagedUserSetting");               \
         break;                                                   \
-      case ::syncer::DEPRECATED_SUPERVISED_USERS:                \
-        PER_DATA_TYPE_MACRO("ManagedUser");                      \
-        break;                                                   \
-      case ::syncer::DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS: \
-        PER_DATA_TYPE_MACRO("ManagedUserSharedSetting");         \
-        break;                                                   \
-      case ::syncer::DEPRECATED_ARTICLES:                        \
-        PER_DATA_TYPE_MACRO("Article");                          \
-        break;                                                   \
       case ::syncer::APP_LIST:                                   \
         PER_DATA_TYPE_MACRO("AppList");                          \
         break;                                                   \
-      case ::syncer::DEPRECATED_WIFI_CREDENTIALS:                \
-        PER_DATA_TYPE_MACRO("WifiCredentials");                  \
-        break;                                                   \
       case ::syncer::SUPERVISED_USER_WHITELISTS:                 \
         PER_DATA_TYPE_MACRO("ManagedUserWhitelist");             \
         break;                                                   \
diff --git a/components/sync/base/hash_util.cc b/components/sync/base/hash_util.cc
index e5e7a0f1..38251821 100644
--- a/components/sync/base/hash_util.cc
+++ b/components/sync/base/hash_util.cc
@@ -6,30 +6,25 @@
 
 #include "base/base64.h"
 #include "base/hash/sha1.h"
+#include "components/sync/base/model_type.h"
 #include "components/sync/protocol/sync.pb.h"
 
 namespace syncer {
 
-std::string GenerateSyncableHash(ModelType model_type,
-                                 const std::string& client_tag) {
+std::string GenerateSyncableBookmarkHash(
+    const std::string& originator_cache_guid,
+    const std::string& originator_client_item_id) {
   // Blank PB with just the field in it has termination symbol,
   // handy for delimiter.
   sync_pb::EntitySpecifics serialized_type;
-  AddDefaultFieldValue(model_type, &serialized_type);
+  AddDefaultFieldValue(BOOKMARKS, &serialized_type);
   std::string hash_input;
   serialized_type.AppendToString(&hash_input);
-  hash_input.append(client_tag);
+  hash_input.append(originator_cache_guid + originator_client_item_id);
 
   std::string encode_output;
   base::Base64Encode(base::SHA1HashString(hash_input), &encode_output);
   return encode_output;
 }
 
-std::string GenerateSyncableBookmarkHash(
-    const std::string& originator_cache_guid,
-    const std::string& originator_client_item_id) {
-  return GenerateSyncableHash(
-      BOOKMARKS, originator_cache_guid + originator_client_item_id);
-}
-
 }  // namespace syncer
diff --git a/components/sync/base/hash_util.h b/components/sync/base/hash_util.h
index efde407f..8f8de24c 100644
--- a/components/sync/base/hash_util.h
+++ b/components/sync/base/hash_util.h
@@ -7,14 +7,8 @@
 
 #include <string>
 
-#include "components/sync/base/model_type.h"
-
 namespace syncer {
 
-// Generates a fixed-length tag for the given string under the given model_type.
-std::string GenerateSyncableHash(ModelType model_type,
-                                 const std::string& client_tag);
-
 // A helper for generating the bookmark type's tag.  This is required in more
 // than one place, so we define the algorithm here to make sure the
 // implementation is consistent.
diff --git a/components/sync/base/hash_util_unittest.cc b/components/sync/base/hash_util_unittest.cc
deleted file mode 100644
index c0925e89..0000000
--- a/components/sync/base/hash_util_unittest.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/base/hash_util.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace syncer {
-
-// Tests that the hashing algorithm has not changed.
-TEST(SyncHashUtilTest, GenerateSyncableHash) {
-  EXPECT_EQ("OyaXV5mEzrPS4wbogmtKvRfekAI=",
-            GenerateSyncableHash(BOOKMARKS, "tag1"));
-  EXPECT_EQ("iNFQtRFQb+IZcn1kKUJEZDDkLs4=",
-            GenerateSyncableHash(PREFERENCES, "tag1"));
-  EXPECT_EQ("gO1cPZQXaM73sHOvSA+tKCKFs58=",
-            GenerateSyncableHash(AUTOFILL, "tag1"));
-
-  EXPECT_EQ("A0eYIHXM1/jVwKDDp12Up20IkKY=",
-            GenerateSyncableHash(BOOKMARKS, "tag2"));
-  EXPECT_EQ("XYxkF7bhS4eItStFgiOIAU23swI=",
-            GenerateSyncableHash(PREFERENCES, "tag2"));
-  EXPECT_EQ("GFiWzo5NGhjLlN+OyCfhy28DJTQ=",
-            GenerateSyncableHash(AUTOFILL, "tag2"));
-}
-
-}  // namespace syncer
diff --git a/components/sync/base/model_type.cc b/components/sync/base/model_type.cc
index f8cd9d7..7fdb224 100644
--- a/components/sync/base/model_type.cc
+++ b/components/sync/base/model_type.cc
@@ -9,7 +9,6 @@
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/values.h"
-#include "components/sync/protocol/app_notification_specifics.pb.h"
 #include "components/sync/protocol/app_setting_specifics.pb.h"
 #include "components/sync/protocol/app_specifics.pb.h"
 #include "components/sync/protocol/autofill_specifics.pb.h"
@@ -107,21 +106,10 @@
      "Extension settings",
      sync_pb::EntitySpecifics::kExtensionSettingFieldNumber,
      ModelTypeForHistograms::kExtensionSettings},
-    {DEPRECATED_APP_NOTIFICATIONS, "APP_NOTIFICATION", "app_notifications",
-     "App Notifications", sync_pb::EntitySpecifics::kAppNotificationFieldNumber,
-     ModelTypeForHistograms::kDeprecatedAppNotifications},
     {HISTORY_DELETE_DIRECTIVES, "HISTORY_DELETE_DIRECTIVE",
      "history_delete_directives", "History Delete Directives",
      sync_pb::EntitySpecifics::kHistoryDeleteDirectiveFieldNumber,
      ModelTypeForHistograms::kHistoryDeleteDirectices},
-    {DEPRECATED_SYNCED_NOTIFICATIONS, "SYNCED_NOTIFICATION",
-     "synced_notifications", "Synced Notifications",
-     sync_pb::EntitySpecifics::kSyncedNotificationFieldNumber,
-     ModelTypeForHistograms::kDeprecatedSyncedNotifications},
-    {DEPRECATED_SYNCED_NOTIFICATION_APP_INFO, "SYNCED_NOTIFICATION_APP_INFO",
-     "synced_notification_app_info", "Synced Notification App Info",
-     sync_pb::EntitySpecifics::kSyncedNotificationAppInfoFieldNumber,
-     ModelTypeForHistograms::kDeprecatedSyncedNotificationAppInfo},
     {DICTIONARY, "DICTIONARY", "dictionary", "Dictionary",
      sync_pb::EntitySpecifics::kDictionaryFieldNumber,
      ModelTypeForHistograms::kDictionary},
@@ -142,22 +130,9 @@
      "Managed User Settings",
      sync_pb::EntitySpecifics::kManagedUserSettingFieldNumber,
      ModelTypeForHistograms::kSupervisedUserSettings},
-    {DEPRECATED_SUPERVISED_USERS, "MANAGED_USER", "managed_users",
-     "Managed Users", sync_pb::EntitySpecifics::kManagedUserFieldNumber,
-     ModelTypeForHistograms::kDeprecatedSupervisedUsers},
-    {DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS, "MANAGED_USER_SHARED_SETTING",
-     "managed_user_shared_settings", "Managed User Shared Settings",
-     sync_pb::EntitySpecifics::kManagedUserSharedSettingFieldNumber,
-     ModelTypeForHistograms::kDeprecatedSupervisedUserSharedSettings},
-    {DEPRECATED_ARTICLES, "ARTICLE", "articles", "Articles",
-     sync_pb::EntitySpecifics::kArticleFieldNumber,
-     ModelTypeForHistograms::kDeprecatedArticles},
     {APP_LIST, "APP_LIST", "app_list", "App List",
      sync_pb::EntitySpecifics::kAppListFieldNumber,
      ModelTypeForHistograms::kAppList},
-    {DEPRECATED_WIFI_CREDENTIALS, "WIFI_CREDENTIAL", "wifi_credentials",
-     "WiFi Credentials", sync_pb::EntitySpecifics::kWifiCredentialFieldNumber,
-     ModelTypeForHistograms::kDeprecatedWifiCredentials},
     {SUPERVISED_USER_WHITELISTS, "MANAGED_USER_WHITELIST",
      "managed_user_whitelists", "Managed User Whitelists",
      sync_pb::EntitySpecifics::kManagedUserWhitelistFieldNumber,
@@ -207,11 +182,11 @@
 static_assert(base::size(kModelTypeInfoMap) == ModelType::NUM_ENTRIES,
               "kModelTypeInfoMap should have ModelType::NUM_ENTRIES elements");
 
-static_assert(46 == syncer::ModelType::NUM_ENTRIES,
+static_assert(39 == syncer::ModelType::NUM_ENTRIES,
               "When adding a new type, update enum SyncModelTypes in enums.xml "
               "and suffix SyncModelType in histograms.xml.");
 
-static_assert(46 == syncer::ModelType::NUM_ENTRIES,
+static_assert(39 == syncer::ModelType::NUM_ENTRIES,
               "When adding a new type, update kAllocatorDumpNameWhitelist in "
               "base/trace_event/memory_infra_background_whitelist.cc.");
 
@@ -266,18 +241,9 @@
     case EXTENSION_SETTINGS:
       specifics->mutable_extension_setting();
       break;
-    case DEPRECATED_APP_NOTIFICATIONS:
-      specifics->mutable_app_notification();
-      break;
     case HISTORY_DELETE_DIRECTIVES:
       specifics->mutable_history_delete_directive();
       break;
-    case DEPRECATED_SYNCED_NOTIFICATIONS:
-      specifics->mutable_synced_notification();
-      break;
-    case DEPRECATED_SYNCED_NOTIFICATION_APP_INFO:
-      specifics->mutable_synced_notification_app_info();
-      break;
     case DICTIONARY:
       specifics->mutable_dictionary();
       break;
@@ -296,21 +262,9 @@
     case SUPERVISED_USER_SETTINGS:
       specifics->mutable_managed_user_setting();
       break;
-    case DEPRECATED_SUPERVISED_USERS:
-      specifics->mutable_managed_user();
-      break;
-    case DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS:
-      specifics->mutable_managed_user_shared_setting();
-      break;
-    case DEPRECATED_ARTICLES:
-      specifics->mutable_article();
-      break;
     case APP_LIST:
       specifics->mutable_app_list();
       break;
-    case DEPRECATED_WIFI_CREDENTIALS:
-      specifics->mutable_wifi_credential();
-      break;
     case SUPERVISED_USER_WHITELISTS:
       specifics->mutable_managed_user_whitelist();
       break;
@@ -395,7 +349,7 @@
 }
 
 ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) {
-  static_assert(46 == ModelType::NUM_ENTRIES,
+  static_assert(39 == ModelType::NUM_ENTRIES,
                 "When adding new protocol types, the following type lookup "
                 "logic must be updated.");
   if (specifics.has_bookmark())
@@ -428,14 +382,8 @@
     return APP_SETTINGS;
   if (specifics.has_extension_setting())
     return EXTENSION_SETTINGS;
-  if (specifics.has_app_notification())
-    return DEPRECATED_APP_NOTIFICATIONS;
   if (specifics.has_history_delete_directive())
     return HISTORY_DELETE_DIRECTIVES;
-  if (specifics.has_synced_notification())
-    return DEPRECATED_SYNCED_NOTIFICATIONS;
-  if (specifics.has_synced_notification_app_info())
-    return DEPRECATED_SYNCED_NOTIFICATION_APP_INFO;
   if (specifics.has_dictionary())
     return DICTIONARY;
   if (specifics.has_favicon_image())
@@ -448,16 +396,8 @@
     return PRIORITY_PREFERENCES;
   if (specifics.has_managed_user_setting())
     return SUPERVISED_USER_SETTINGS;
-  if (specifics.has_managed_user())
-    return DEPRECATED_SUPERVISED_USERS;
-  if (specifics.has_managed_user_shared_setting())
-    return DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS;
-  if (specifics.has_article())
-    return DEPRECATED_ARTICLES;
   if (specifics.has_app_list())
     return APP_LIST;
-  if (specifics.has_wifi_credential())
-    return DEPRECATED_WIFI_CREDENTIALS;
   if (specifics.has_managed_user_whitelist())
     return SUPERVISED_USER_WHITELISTS;
   if (specifics.has_arc_package())
@@ -489,7 +429,7 @@
 }
 
 ModelTypeSet EncryptableUserTypes() {
-  static_assert(46 == ModelType::NUM_ENTRIES,
+  static_assert(39 == ModelType::NUM_ENTRIES,
                 "If adding an unencryptable type, remove from "
                 "encryptable_user_types below.");
   ModelTypeSet encryptable_user_types = UserTypes();
@@ -497,11 +437,6 @@
   encryptable_user_types.Remove(AUTOFILL_WALLET_DATA);
   // We never encrypt history delete directives.
   encryptable_user_types.Remove(HISTORY_DELETE_DIRECTIVES);
-  // Synced notifications are not encrypted since the server must see changes.
-  encryptable_user_types.Remove(DEPRECATED_SYNCED_NOTIFICATIONS);
-  // Synced Notification App Info does not have private data, so it is not
-  // encrypted.
-  encryptable_user_types.Remove(DEPRECATED_SYNCED_NOTIFICATION_APP_INFO);
   // Device info data is not encrypted because it might be synced before
   // encryption is ready.
   encryptable_user_types.Remove(DEVICE_INFO);
@@ -510,11 +445,6 @@
   encryptable_user_types.Remove(PRIORITY_PREFERENCES);
   // Supervised user settings are not encrypted since they are set server-side.
   encryptable_user_types.Remove(SUPERVISED_USER_SETTINGS);
-  // Supervised users are not encrypted since they are managed server-side.
-  encryptable_user_types.Remove(DEPRECATED_SUPERVISED_USERS);
-  // Supervised user shared settings are not encrypted since they are managed
-  // server-side and shared between manager and supervised user.
-  encryptable_user_types.Remove(DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS);
   // Supervised user whitelists are not encrypted since they are managed
   // server-side.
   encryptable_user_types.Remove(SUPERVISED_USER_WHITELISTS);
@@ -549,19 +479,6 @@
   return kModelTypeInfoMap[model_type].notification_type;
 }
 
-// The normal rules about histograms apply here.  Always append to the bottom of
-// the list, and be careful to not reuse integer values that have already been
-// assigned.
-//
-// Don't forget to update the "SyncModelTypes" enum in enums.xml when you make
-// changes to this list.
-int ModelTypeToHistogramInt(ModelType model_type) {
-  DCHECK_GE(model_type, UNSPECIFIED);
-  DCHECK_LT(model_type, ModelType::NUM_ENTRIES);
-  return static_cast<int>(
-      kModelTypeInfoMap[model_type].model_type_histogram_val);
-}
-
 ModelTypeForHistograms ModelTypeHistogramValue(ModelType model_type) {
   DCHECK_GE(model_type, UNSPECIFIED);
   DCHECK_LT(model_type, ModelType::NUM_ENTRIES);
@@ -572,7 +489,7 @@
   DCHECK_GE(model_type, UNSPECIFIED);
   DCHECK_LT(model_type, ModelType::NUM_ENTRIES);
   // Make sure the value is stable and positive.
-  return ModelTypeToHistogramInt(model_type) + 1;
+  return static_cast<int>(ModelTypeHistogramValue(model_type)) + 1;
 }
 
 std::unique_ptr<base::Value> ModelTypeToValue(ModelType model_type) {
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h
index d78f394..1ee889c7 100644
--- a/components/sync/base/model_type.h
+++ b/components/sync/base/model_type.h
@@ -92,13 +92,9 @@
   APP_SETTINGS,
   // An extension setting from the extension settings API.
   EXTENSION_SETTINGS,
-  // Deprecated.
-  DEPRECATED_APP_NOTIFICATIONS,
   // History delete directives, used to propagate history deletions (e.g. based
   // on a time range).
   HISTORY_DELETE_DIRECTIVES,
-  DEPRECATED_SYNCED_NOTIFICATIONS,
-  DEPRECATED_SYNCED_NOTIFICATION_APP_INFO,
   // Custom spelling dictionary entries.
   DICTIONARY,
   // Favicon images, including both the image URL and the actual pixels.
@@ -114,12 +110,8 @@
   PRIORITY_PREFERENCES,
   // Supervised user settings. Cannot be encrypted.
   SUPERVISED_USER_SETTINGS,
-  DEPRECATED_SUPERVISED_USERS,
-  DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS,
-  DEPRECATED_ARTICLES,
   // App List items, used by the ChromeOS app launcher.
   APP_LIST,
-  DEPRECATED_WIFI_CREDENTIALS,
   // Supervised user whitelists. Each item contains a CRX ID (like an extension
   // ID) and a name.
   SUPERVISED_USER_WHITELISTS,
@@ -256,16 +248,12 @@
       BOOKMARKS, PREFERENCES, PASSWORDS, AUTOFILL_PROFILE, AUTOFILL,
       AUTOFILL_WALLET_DATA, AUTOFILL_WALLET_METADATA, THEMES, TYPED_URLS,
       EXTENSIONS, SEARCH_ENGINES, SESSIONS, APPS, APP_SETTINGS,
-      EXTENSION_SETTINGS, DEPRECATED_APP_NOTIFICATIONS,
-      HISTORY_DELETE_DIRECTIVES, DEPRECATED_SYNCED_NOTIFICATIONS,
-      DEPRECATED_SYNCED_NOTIFICATION_APP_INFO, DICTIONARY, FAVICON_IMAGES,
+      EXTENSION_SETTINGS, HISTORY_DELETE_DIRECTIVES, DICTIONARY, FAVICON_IMAGES,
       FAVICON_TRACKING, DEVICE_INFO, PRIORITY_PREFERENCES,
-      SUPERVISED_USER_SETTINGS, DEPRECATED_SUPERVISED_USERS,
-      DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS, DEPRECATED_ARTICLES, APP_LIST,
-      DEPRECATED_WIFI_CREDENTIALS, SUPERVISED_USER_WHITELISTS, ARC_PACKAGE,
-      PRINTERS, READING_LIST, USER_EVENTS, NIGORI, DEPRECATED_EXPERIMENTS,
-      MOUNTAIN_SHARES, USER_CONSENTS, SEND_TAB_TO_SELF, SECURITY_EVENTS,
-      WEB_APPS, WIFI_CONFIGURATIONS);
+      SUPERVISED_USER_SETTINGS, APP_LIST, SUPERVISED_USER_WHITELISTS,
+      ARC_PACKAGE, PRINTERS, READING_LIST, USER_EVENTS, NIGORI,
+      DEPRECATED_EXPERIMENTS, MOUNTAIN_SHARES, USER_CONSENTS, SEND_TAB_TO_SELF,
+      SECURITY_EVENTS, WEB_APPS, WIFI_CONFIGURATIONS);
 }
 
 // These are the normal user-controlled types. This is to distinguish from
@@ -364,9 +352,6 @@
 // The mapping from ModelType to integer is defined here. It defines a
 // completely different order than the ModelType enum itself. The mapping should
 // match the SyncModelTypes mapping from integer to labels defined in enums.xml.
-// TODO(crbug.com/1007293): Update all histogram recording sites to use
-// ModelTypeHistogramValue() and remove ModelTypeToHistogramInt();
-int ModelTypeToHistogramInt(ModelType model_type);
 ModelTypeForHistograms ModelTypeHistogramValue(ModelType model_type);
 
 // Returns for every model_type a positive unique integer that is stable over
diff --git a/components/sync/base/user_selectable_type.cc b/components/sync/base/user_selectable_type.cc
index df12e4d..b792ec5 100644
--- a/components/sync/base/user_selectable_type.cc
+++ b/components/sync/base/user_selectable_type.cc
@@ -79,7 +79,10 @@
 }
 
 int UserSelectableTypeToHistogramInt(UserSelectableType type) {
-  return ModelTypeToHistogramInt(UserSelectableTypeToCanonicalModelType(type));
+  // TODO(crbug.com/1007293): Use ModelTypeHistogramValue instead of casting to
+  // int.
+  return static_cast<int>(
+      ModelTypeHistogramValue(UserSelectableTypeToCanonicalModelType(type)));
 }
 
 }  // namespace syncer
diff --git a/components/sync/driver/about_sync_util.cc b/components/sync/driver/about_sync_util.cc
index 2536557..4336e2e0 100644
--- a/components/sync/driver/about_sync_util.cc
+++ b/components/sync/driver/about_sync_util.cc
@@ -428,15 +428,6 @@
       section_that_cycle->AddIntStat("Committed Count");
   Stat<int>* entries = section_that_cycle->AddIntStat("Entries");
 
-  Section* section_nudge_info =
-      section_list.AddSection("Nudge Source Counters");
-  Stat<int>* nudge_source_notification =
-      section_nudge_info->AddIntStat("Server Invalidations");
-  Stat<int>* nudge_source_local =
-      section_nudge_info->AddIntStat("Local Changes");
-  Stat<int>* nudge_source_local_refresh =
-      section_nudge_info->AddIntStat("Local Refreshes");
-
   // Populate all the fields we declared above.
   client_version->Set(GetVersionString(channel));
 
@@ -566,13 +557,6 @@
     entries->Set(static_cast<int>(snapshot.num_entries()));
   }
 
-  // Nudge Source Counters.
-  if (is_status_valid) {
-    nudge_source_notification->Set(full_status.nudge_source_notification);
-    nudge_source_local->Set(full_status.nudge_source_local);
-    nudge_source_local_refresh->Set(full_status.nudge_source_local_refresh);
-  }
-
   // This list of sections belongs in the 'details' field of the returned
   // message.
   about_info->SetKey(kDetailsKey, section_list.ToValue());
diff --git a/components/sync/driver/async_directory_type_controller.cc b/components/sync/driver/async_directory_type_controller.cc
index 34c8c55..971301a1 100644
--- a/components/sync/driver/async_directory_type_controller.cc
+++ b/components/sync/driver/async_directory_type_controller.cc
@@ -211,8 +211,7 @@
   DCHECK(CalledOnValidThread());
   // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures2",
-                            ModelTypeToHistogramInt(type()),
-                            static_cast<int>(ModelType::NUM_ENTRIES));
+                            ModelTypeHistogramValue(type()));
 #define PER_DATA_TYPE_MACRO(type_str)                                    \
   UMA_HISTOGRAM_ENUMERATION("Sync." type_str "ConfigureFailure", result, \
                             MAX_CONFIGURE_RESULT);
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index 1e15945f..0f18f383 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -297,8 +297,7 @@
     for (ModelType type : last_requested_types_) {
       // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
       UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureDataTypes",
-                                ModelTypeToHistogramInt(type),
-                                static_cast<int>(ModelType::NUM_ENTRIES));
+                                ModelTypeHistogramValue(type));
     }
   }
 
diff --git a/components/sync/driver/frontend_data_type_controller.cc b/components/sync/driver/frontend_data_type_controller.cc
index 5ab8d202..48f789f66 100644
--- a/components/sync/driver/frontend_data_type_controller.cc
+++ b/components/sync/driver/frontend_data_type_controller.cc
@@ -235,10 +235,8 @@
 
 void FrontendDataTypeController::RecordStartFailure(ConfigureResult result) {
   DCHECK(CalledOnValidThread());
-  // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures2",
-                            ModelTypeToHistogramInt(type()),
-                            static_cast<int>(ModelType::NUM_ENTRIES));
+                            ModelTypeHistogramValue(type()));
 #define PER_DATA_TYPE_MACRO(type_str)                                    \
   UMA_HISTOGRAM_ENUMERATION("Sync." type_str "ConfigureFailure", result, \
                             MAX_CONFIGURE_RESULT);
diff --git a/components/sync/driver/generic_change_processor_unittest.cc b/components/sync/driver/generic_change_processor_unittest.cc
index fc397ea..492d79c 100644
--- a/components/sync/driver/generic_change_processor_unittest.cc
+++ b/components/sync/driver/generic_change_processor_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/run_loop.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/task_environment.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/driver/data_type_manager.h"
 #include "components/sync/driver/sync_api_component_factory_mock.h"
@@ -295,7 +296,7 @@
   ASSERT_EQ(sync_data.size(), 1U);
   ASSERT_EQ("session tag 2",
             sync_data[0].GetSpecifics().session().session_tag());
-  EXPECT_FALSE(SyncDataRemote(sync_data[0]).GetClientTagHash().empty());
+  EXPECT_FALSE(SyncDataRemote(sync_data[0]).GetClientTagHash().value().empty());
 }
 
 }  // namespace
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc
index 4507524..4953d03 100644
--- a/components/sync/driver/glue/sync_engine_backend.cc
+++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -61,15 +61,14 @@
 const base::FilePath::CharType kNigoriStorageFilename[] =
     FILE_PATH_LITERAL("Nigori.bin");
 
-void RecordPerModelTypeInvalidation(int model_type, bool is_grouped) {
-  UMA_HISTOGRAM_ENUMERATION("Sync.InvalidationPerModelType", model_type,
-                            static_cast<int>(syncer::ModelType::NUM_ENTRIES));
+void RecordPerModelTypeInvalidation(ModelTypeForHistograms value,
+                                    bool is_grouped) {
+  UMA_HISTOGRAM_ENUMERATION("Sync.InvalidationPerModelType", value);
   if (!is_grouped) {
     // When recording metrics it's important to distinguish between
     // many/one case, since "many" aka grouped case is only common in
     // the deprecated implementation.
-    UMA_HISTOGRAM_ENUMERATION("Sync.NonGroupedInvalidation", model_type,
-                              static_cast<int>(syncer::ModelType::NUM_ENTRIES));
+    UMA_HISTOGRAM_ENUMERATION("Sync.NonGroupedInvalidation", value);
   }
 }
 
@@ -293,7 +292,7 @@
                     << ObjectIdToString(object_id);
     } else {
       bool is_grouped = (ids.size() != 1);
-      RecordPerModelTypeInvalidation(ModelTypeToHistogramInt(type), is_grouped);
+      RecordPerModelTypeInvalidation(ModelTypeHistogramValue(type), is_grouped);
       SingleObjectInvalidationSet invalidation_set =
           invalidation_map.ForObject(object_id);
       for (Invalidation invalidation : invalidation_set) {
@@ -303,8 +302,7 @@
 
         if (!is_grouped && !invalidation.is_unknown_version()) {
           UMA_HISTOGRAM_ENUMERATION("Sync.NonGroupedInvalidationKnownVersion",
-                                    ModelTypeToHistogramInt(type),
-                                    static_cast<int>(ModelType::NUM_ENTRIES));
+                                    ModelTypeHistogramValue(type));
         }
         std::unique_ptr<InvalidationInterface> inv_adapter(
             new InvalidationAdapter(invalidation));
diff --git a/components/sync/driver/model_association_manager.cc b/components/sync/driver/model_association_manager.cc
index 895f4762..8d2bec1 100644
--- a/components/sync/driver/model_association_manager.cc
+++ b/components/sync/driver/model_association_manager.cc
@@ -36,17 +36,13 @@
     // in parallel with the UI types.
     PASSWORDS, AUTOFILL, AUTOFILL_PROFILE, AUTOFILL_WALLET_DATA,
     AUTOFILL_WALLET_METADATA, EXTENSION_SETTINGS, APP_SETTINGS, TYPED_URLS,
-    HISTORY_DELETE_DIRECTIVES, DEPRECATED_SYNCED_NOTIFICATIONS,
-    DEPRECATED_SYNCED_NOTIFICATION_APP_INFO,
+    HISTORY_DELETE_DIRECTIVES,
 
     // UI thread data types.
     BOOKMARKS, PREFERENCES, PRIORITY_PREFERENCES, EXTENSIONS, APPS, APP_LIST,
-    ARC_PACKAGE, READING_LIST, THEMES, SEARCH_ENGINES, SESSIONS,
-    DEPRECATED_APP_NOTIFICATIONS, DICTIONARY, FAVICON_IMAGES, FAVICON_TRACKING,
-    PRINTERS, USER_CONSENTS, USER_EVENTS, SUPERVISED_USER_SETTINGS,
-    SUPERVISED_USER_WHITELISTS, DEPRECATED_WIFI_CREDENTIALS,
-    DEPRECATED_SUPERVISED_USERS, MOUNTAIN_SHARES,
-    DEPRECATED_SUPERVISED_USER_SHARED_SETTINGS, DEPRECATED_ARTICLES,
+    ARC_PACKAGE, READING_LIST, THEMES, SEARCH_ENGINES, SESSIONS, DICTIONARY,
+    FAVICON_IMAGES, FAVICON_TRACKING, PRINTERS, USER_CONSENTS, USER_EVENTS,
+    SUPERVISED_USER_SETTINGS, SUPERVISED_USER_WHITELISTS, MOUNTAIN_SHARES,
     SEND_TAB_TO_SELF, SECURITY_EVENTS, WEB_APPS, WIFI_CONFIGURATIONS};
 
 static_assert(base::size(kStartOrder) ==
@@ -451,8 +447,7 @@
         dtc->state() != DataTypeController::STOPPING) {
       // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
       UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
-                                ModelTypeToHistogramInt(dtc->type()),
-                                static_cast<int>(ModelType::NUM_ENTRIES));
+                                ModelTypeHistogramValue(dtc->type()));
       StopDatatypeImpl(SyncError(FROM_HERE, SyncError::DATATYPE_ERROR,
                                  "Association timed out.", dtc->type()),
                        STOP_SYNC, dtc, base::DoNothing());
diff --git a/components/sync/driver/model_type_controller.cc b/components/sync/driver/model_type_controller.cc
index 1fbf4f15..8133aad 100644
--- a/components/sync/driver/model_type_controller.cc
+++ b/components/sync/driver/model_type_controller.cc
@@ -299,22 +299,14 @@
 
 void ModelTypeController::RecordStartFailure() const {
   DCHECK(CalledOnValidThread());
-  // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
-  // This is not strongly typed because historically, ModelTypeToHistogramInt()
-  // defines quite a different order from the type() enum.
   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures2",
-                            ModelTypeToHistogramInt(type()),
-                            static_cast<int>(ModelType::NUM_ENTRIES));
+                            ModelTypeHistogramValue(type()));
 }
 
 void ModelTypeController::RecordRunFailure() const {
   DCHECK(CalledOnValidThread());
-  // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
-  // This is not strongly typed because historically, ModelTypeToHistogramInt()
-  // defines quite a different order from the type() enum.
   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures2",
-                            ModelTypeToHistogramInt(type()),
-                            static_cast<int>(ModelType::NUM_ENTRIES));
+                            ModelTypeHistogramValue(type()));
 }
 
 void ModelTypeController::OnDelegateStarted(
diff --git a/components/sync/driver/model_type_controller_unittest.cc b/components/sync/driver/model_type_controller_unittest.cc
index d4c99d69..32f69fa 100644
--- a/components/sync/driver/model_type_controller_unittest.cc
+++ b/components/sync/driver/model_type_controller_unittest.cc
@@ -301,7 +301,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(DataTypeController::FAILED, controller()->state());
   histogram_tester.ExpectBucketCount(
-      kStartFailuresHistogram, ModelTypeToHistogramInt(kTestModelType), 1);
+      kStartFailuresHistogram, ModelTypeHistogramValue(kTestModelType), 1);
   histogram_tester.ExpectTotalCount(kRunFailuresHistogram, 0);
 }
 
@@ -444,7 +444,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(DataTypeController::FAILED, controller()->state());
   histogram_tester.ExpectBucketCount(kStartFailuresHistogram,
-                                     ModelTypeToHistogramInt(kTestModelType),
+                                     ModelTypeHistogramValue(kTestModelType),
                                      /*count=*/1);
   histogram_tester.ExpectTotalCount(kRunFailuresHistogram, 0);
 }
@@ -654,7 +654,7 @@
   EXPECT_EQ(DataTypeController::FAILED, controller()->state());
   histogram_tester.ExpectTotalCount(kStartFailuresHistogram, 0);
   histogram_tester.ExpectBucketCount(kRunFailuresHistogram,
-                                     ModelTypeToHistogramInt(kTestModelType),
+                                     ModelTypeHistogramValue(kTestModelType),
                                      /*count=*/1);
 }
 
diff --git a/components/sync/driver/startup_controller.cc b/components/sync/driver/startup_controller.cc
index 233162a..5ddbe0a4 100644
--- a/components/sync/driver/startup_controller.cc
+++ b/components/sync/driver/startup_controller.cc
@@ -162,10 +162,8 @@
   // Measure the time spent waiting for init and the type that triggered it.
   // We could measure the time spent deferred on a per-datatype basis, but
   // for now this is probably sufficient.
-  // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
   UMA_HISTOGRAM_ENUMERATION("Sync.Startup.TypeTriggeringInit",
-                            ModelTypeToHistogramInt(type),
-                            static_cast<int>(ModelType::NUM_ENTRIES));
+                            ModelTypeHistogramValue(type));
   if (!start_up_time_.is_null()) {
     RecordTimeDeferred();
     UMA_HISTOGRAM_ENUMERATION("Sync.Startup.DeferredInitTrigger",
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
index a4faac8..30c1ea9 100644
--- a/components/sync/driver/sync_user_settings_impl.cc
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -186,7 +186,7 @@
     types.RetainAll(registered_model_types_);
   }
 
-  static_assert(46 == ModelType::NUM_ENTRIES,
+  static_assert(39 == ModelType::NUM_ENTRIES,
                 "If adding a new sync data type, update the list below below if"
                 " you want to disable the new data type for local sync.");
   types.PutAll(ControlTypes());
diff --git a/components/sync/engine/net/DEPS b/components/sync/engine/net/DEPS
index 5d6c237..2fb0451 100644
--- a/components/sync/engine/net/DEPS
+++ b/components/sync/engine/net/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+components/variations",
   "+net",
   "+services/network/public/cpp",
   "+services/network/test",
diff --git a/components/sync/engine/net/http_bridge.cc b/components/sync/engine/net/http_bridge.cc
index e0dab5a..84ce13c7 100644
--- a/components/sync/engine/net/http_bridge.cc
+++ b/components/sync/engine/net/http_bridge.cc
@@ -19,6 +19,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/task/post_task.h"
 #include "components/sync/base/cancelation_signal.h"
+#include "components/variations/net/variations_http_headers.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_cache.h"
@@ -277,6 +278,10 @@
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kUserAgent,
                                       user_agent_);
 
+  variations::AppendVariationsHeader(
+      url_for_request_, variations::InIncognito::kNo,
+      variations::SignedIn::kYes, resource_request.get());
+
   fetch_state_.url_loader = network::SimpleURLLoader::Create(
       std::move(resource_request), traffic_annotation);
   network::SimpleURLLoader* url_loader = fetch_state_.url_loader.get();
diff --git a/components/sync/engine/non_blocking_sync_common.h b/components/sync/engine/non_blocking_sync_common.h
index f4264b8c..82044a01 100644
--- a/components/sync/engine/non_blocking_sync_common.h
+++ b/components/sync/engine/non_blocking_sync_common.h
@@ -13,6 +13,7 @@
 
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/entity_data.h"
 #include "components/sync/protocol/sync.pb.h"
 
@@ -52,7 +53,7 @@
   // |id|. It could be different because the server can change the sync id
   // (e.g. for newly created bookmarks),
   std::string id_in_request;
-  std::string client_tag_hash;
+  ClientTagHash client_tag_hash;
   int64_t sequence_number = 0;
   int64_t response_version = 0;
   std::string specifics_hash;
diff --git a/components/sync/engine/sync_encryption_handler.cc b/components/sync/engine/sync_encryption_handler.cc
index a53e6c0e8..f716dcc 100644
--- a/components/sync/engine/sync_encryption_handler.cc
+++ b/components/sync/engine/sync_encryption_handler.cc
@@ -18,7 +18,6 @@
   types.Put(PASSWORDS);  // Has its own encryption, but include it anyway.
   types.Put(
       WIFI_CONFIGURATIONS);  // Has its own encryption, but include it anyway.
-  types.Put(DEPRECATED_WIFI_CREDENTIALS);
   return types;
 }
 
diff --git a/components/sync/engine/sync_status.cc b/components/sync/engine/sync_status.cc
index 48b579d1..f488301 100644
--- a/components/sync/engine/sync_status.cc
+++ b/components/sync/engine/sync_status.cc
@@ -20,9 +20,6 @@
       num_commits_total(0),
       num_local_overwrites_total(0),
       num_server_overwrites_total(0),
-      nudge_source_notification(0),
-      nudge_source_local(0),
-      nudge_source_local_refresh(0),
       cryptographer_can_encrypt(false),
       crypto_has_pending_keys(false),
       has_keystore_key(false),
diff --git a/components/sync/engine/sync_status.h b/components/sync/engine/sync_status.h
index c493155..da54f38 100644
--- a/components/sync/engine/sync_status.h
+++ b/components/sync/engine/sync_status.h
@@ -66,12 +66,6 @@
   int num_local_overwrites_total;
   int num_server_overwrites_total;
 
-  // Nudge counts for each possible source
-  // TODO(crbug.com/1007969): Remove these; they're always 0.
-  int nudge_source_notification;
-  int nudge_source_local;
-  int nudge_source_local_refresh;
-
   // Encryption related.
   ModelTypeSet encrypted_types;
   bool cryptographer_can_encrypt;
diff --git a/components/sync/engine_impl/commit.cc b/components/sync/engine_impl/commit.cc
index 703f0f43..ed5abe9 100644
--- a/components/sync/engine_impl/commit.cc
+++ b/components/sync/engine_impl/commit.cc
@@ -115,8 +115,7 @@
     ModelType request_type = it->first;
     request_types.Put(request_type);
     UMA_HISTOGRAM_ENUMERATION("Sync.PostedDataTypeCommitRequest",
-                              ModelTypeToHistogramInt(request_type),
-                              static_cast<int>(ModelType::NUM_ENTRIES));
+                              ModelTypeHistogramValue(request_type));
   }
 
   if (cycle->context()->debug_info_getter()) {
diff --git a/components/sync/engine_impl/directory_update_handler_unittest.cc b/components/sync/engine_impl/directory_update_handler_unittest.cc
index 0340b0e..70b17ad 100644
--- a/components/sync/engine_impl/directory_update_handler_unittest.cc
+++ b/components/sync/engine_impl/directory_update_handler_unittest.cc
@@ -251,31 +251,25 @@
 }
 
 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, GarbageCollectionByVersion) {
-  DirectoryTypeDebugInfoEmitter emitter(DEPRECATED_SYNCED_NOTIFICATIONS,
-                                        &type_observers_);
-  DirectoryUpdateHandler handler(dir(), DEPRECATED_SYNCED_NOTIFICATIONS,
-                                 ui_worker(), &emitter);
+  DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
+  DirectoryUpdateHandler handler(dir(), PREFERENCES, ui_worker(), &emitter);
   StatusController status;
 
   sync_pb::DataTypeProgressMarker progress;
-  progress.set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(DEPRECATED_SYNCED_NOTIFICATIONS));
+  progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(PREFERENCES));
   progress.set_token("token");
   progress.mutable_gc_directive()->set_version_watermark(kDefaultVersion + 10);
 
   sync_pb::DataTypeContext context;
-  context.set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(DEPRECATED_SYNCED_NOTIFICATIONS));
+  context.set_data_type_id(GetSpecificsFieldNumberFromModelType(PREFERENCES));
   context.set_context("context");
   context.set_version(1);
 
-  std::unique_ptr<sync_pb::SyncEntity> e1 =
-      CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")), "",
-                   DEPRECATED_SYNCED_NOTIFICATIONS);
+  std::unique_ptr<sync_pb::SyncEntity> e1 = CreateUpdate(
+      SyncableIdToProto(Id::CreateFromServerId("e1")), "", PREFERENCES);
 
-  std::unique_ptr<sync_pb::SyncEntity> e2 =
-      CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")), "",
-                   DEPRECATED_SYNCED_NOTIFICATIONS);
+  std::unique_ptr<sync_pb::SyncEntity> e2 = CreateUpdate(
+      SyncableIdToProto(Id::CreateFromServerId("e2")), "", PREFERENCES);
   e2->set_version(kDefaultVersion + 100);
 
   // Add to the applicable updates list.
@@ -291,7 +285,7 @@
   handler.ApplyUpdates(&status);
 
   // Verify none is deleted because they are unapplied during GC.
-  EXPECT_TRUE(TypeRootExists(DEPRECATED_SYNCED_NOTIFICATIONS));
+  EXPECT_TRUE(TypeRootExists(PREFERENCES));
   EXPECT_TRUE(EntryExists(e1->id_string()));
   EXPECT_TRUE(EntryExists(e2->id_string()));
 
@@ -310,33 +304,27 @@
 // Create 2 entries, one is 15-days-old, another is 5-days-old. Check if sync
 // will delete 15-days-old entry when server set expired age is 10 days.
 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, GarbageCollectionByAge) {
-  DirectoryTypeDebugInfoEmitter emitter(DEPRECATED_SYNCED_NOTIFICATIONS,
-                                        &type_observers_);
-  DirectoryUpdateHandler handler(dir(), DEPRECATED_SYNCED_NOTIFICATIONS,
-                                 ui_worker(), &emitter);
+  DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
+  DirectoryUpdateHandler handler(dir(), PREFERENCES, ui_worker(), &emitter);
   StatusController status;
 
   sync_pb::DataTypeProgressMarker progress;
-  progress.set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(DEPRECATED_SYNCED_NOTIFICATIONS));
+  progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(PREFERENCES));
   progress.set_token("token");
   progress.mutable_gc_directive()->set_age_watermark_in_days(20);
 
   sync_pb::DataTypeContext context;
-  context.set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(DEPRECATED_SYNCED_NOTIFICATIONS));
+  context.set_data_type_id(GetSpecificsFieldNumberFromModelType(PREFERENCES));
   context.set_context("context");
   context.set_version(1);
 
-  std::unique_ptr<sync_pb::SyncEntity> e1 =
-      CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")), "",
-                   DEPRECATED_SYNCED_NOTIFICATIONS);
+  std::unique_ptr<sync_pb::SyncEntity> e1 = CreateUpdate(
+      SyncableIdToProto(Id::CreateFromServerId("e1")), "", PREFERENCES);
   e1->set_mtime(
       TimeToProtoTime(base::Time::Now() - base::TimeDelta::FromDays(15)));
 
-  std::unique_ptr<sync_pb::SyncEntity> e2 =
-      CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")), "",
-                   DEPRECATED_SYNCED_NOTIFICATIONS);
+  std::unique_ptr<sync_pb::SyncEntity> e2 = CreateUpdate(
+      SyncableIdToProto(Id::CreateFromServerId("e2")), "", PREFERENCES);
   e2->set_mtime(
       TimeToProtoTime(base::Time::Now() - base::TimeDelta::FromDays(5)));
 
@@ -353,7 +341,7 @@
   handler.ApplyUpdates(&status);
 
   // Verify none is deleted because they are unapplied during GC.
-  EXPECT_TRUE(TypeRootExists(DEPRECATED_SYNCED_NOTIFICATIONS));
+  EXPECT_TRUE(TypeRootExists(PREFERENCES));
   EXPECT_TRUE(EntryExists(e1->id_string()));
   EXPECT_TRUE(EntryExists(e2->id_string()));
 
@@ -371,17 +359,13 @@
 }
 
 TEST_F(DirectoryUpdateHandlerProcessUpdateTest, ContextVersion) {
-  DirectoryTypeDebugInfoEmitter emitter(DEPRECATED_SYNCED_NOTIFICATIONS,
-                                        &type_observers_);
-  DirectoryUpdateHandler handler(dir(), DEPRECATED_SYNCED_NOTIFICATIONS,
-                                 ui_worker(), &emitter);
+  DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
+  DirectoryUpdateHandler handler(dir(), PREFERENCES, ui_worker(), &emitter);
   StatusController status;
-  int field_number =
-      GetSpecificsFieldNumberFromModelType(DEPRECATED_SYNCED_NOTIFICATIONS);
+  int field_number = GetSpecificsFieldNumberFromModelType(PREFERENCES);
 
   sync_pb::DataTypeProgressMarker progress;
-  progress.set_data_type_id(
-      GetSpecificsFieldNumberFromModelType(DEPRECATED_SYNCED_NOTIFICATIONS));
+  progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(PREFERENCES));
   progress.set_token("token");
 
   sync_pb::DataTypeContext old_context;
@@ -389,9 +373,8 @@
   old_context.set_context("data");
   old_context.set_data_type_id(field_number);
 
-  std::unique_ptr<sync_pb::SyncEntity> e1 =
-      CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e1")), "",
-                   DEPRECATED_SYNCED_NOTIFICATIONS);
+  std::unique_ptr<sync_pb::SyncEntity> e1 = CreateUpdate(
+      SyncableIdToProto(Id::CreateFromServerId("e1")), "", PREFERENCES);
 
   SyncEntityList updates;
   updates.push_back(e1.get());
@@ -404,15 +387,14 @@
   handler.ApplyUpdates(&status);
 
   // The PREFERENCES root should be auto-created.
-  EXPECT_TRUE(TypeRootExists(DEPRECATED_SYNCED_NOTIFICATIONS));
+  EXPECT_TRUE(TypeRootExists(PREFERENCES));
 
   EXPECT_TRUE(EntryExists(e1->id_string()));
 
   {
     sync_pb::DataTypeContext dir_context;
     syncable::ReadTransaction trans(FROM_HERE, dir());
-    trans.directory()->GetDataTypeContext(
-        &trans, DEPRECATED_SYNCED_NOTIFICATIONS, &dir_context);
+    trans.directory()->GetDataTypeContext(&trans, PREFERENCES, &dir_context);
     EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
   }
 
@@ -421,9 +403,8 @@
   new_context.set_context("old");
   new_context.set_data_type_id(field_number);
 
-  std::unique_ptr<sync_pb::SyncEntity> e2 =
-      CreateUpdate(SyncableIdToProto(Id::CreateFromServerId("e2")), "",
-                   DEPRECATED_SYNCED_NOTIFICATIONS);
+  std::unique_ptr<sync_pb::SyncEntity> e2 = CreateUpdate(
+      SyncableIdToProto(Id::CreateFromServerId("e2")), "", PREFERENCES);
   updates.clear();
   updates.push_back(e2.get());
 
@@ -440,8 +421,7 @@
   {
     sync_pb::DataTypeContext dir_context;
     syncable::ReadTransaction trans(FROM_HERE, dir());
-    trans.directory()->GetDataTypeContext(
-        &trans, DEPRECATED_SYNCED_NOTIFICATIONS, &dir_context);
+    trans.directory()->GetDataTypeContext(&trans, PREFERENCES, &dir_context);
     EXPECT_EQ(old_context.SerializeAsString(), dir_context.SerializeAsString());
   }
 }
@@ -497,8 +477,7 @@
         password_worker_(new FakeModelWorker(GROUP_PASSWORD)),
         passive_worker_(new FakeModelWorker(GROUP_PASSIVE)),
         bookmarks_emitter_(BOOKMARKS, &type_observers_),
-        passwords_emitter_(PASSWORDS, &type_observers_),
-        articles_emitter_(DEPRECATED_ARTICLES, &type_observers_) {}
+        passwords_emitter_(PASSWORDS, &type_observers_) {}
 
   void SetUp() override {
     dir_maker_.SetUp();
@@ -524,10 +503,6 @@
     return passwords_emitter_.GetUpdateCounters();
   }
 
-  const UpdateCounters& GetArticlesUpdateCounters() {
-    return articles_emitter_.GetUpdateCounters();
-  }
-
   DirectoryCryptographer* GetCryptographer(
       const syncable::BaseTransaction* trans) {
     return dir_maker_.GetCryptographer(trans);
@@ -559,7 +534,6 @@
   base::ObserverList<TypeDebugInfoObserver>::Unchecked type_observers_;
   DirectoryTypeDebugInfoEmitter bookmarks_emitter_;
   DirectoryTypeDebugInfoEmitter passwords_emitter_;
-  DirectoryTypeDebugInfoEmitter articles_emitter_;
 
   std::map<ModelType, std::unique_ptr<UpdateHandler>> update_handler_map_;
 };
diff --git a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc
index c98d214..fc15fc63 100644
--- a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc
+++ b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity.cc
@@ -7,7 +7,7 @@
 #include "base/guid.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/engine_impl/loopback_server/persistent_permanent_entity.h"
 #include "components/sync/protocol/sync.pb.h"
 
@@ -65,7 +65,8 @@
     int64_t creation_time,
     int64_t last_modified_time) {
   ModelType model_type = GetModelTypeFromSpecifics(entity_specifics);
-  std::string client_tag_hash = GenerateSyncableHash(model_type, client_tag);
+  std::string client_tag_hash =
+      ClientTagHash::FromUnhashed(model_type, client_tag).value();
   std::string id = LoopbackServerEntity::CreateId(model_type, client_tag_hash);
   return std::make_unique<PersistentUniqueClientEntity>(
       id, model_type, 0, non_unique_name, client_tag_hash, entity_specifics,
diff --git a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity_unittest.cc b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity_unittest.cc
index f00c6aa..306ab12 100644
--- a/components/sync/engine_impl/loopback_server/persistent_unique_client_entity_unittest.cc
+++ b/components/sync/engine_impl/loopback_server/persistent_unique_client_entity_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "components/sync/engine_impl/loopback_server/persistent_unique_client_entity.h"
 
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/engine_impl/loopback_server/loopback_server_entity.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -45,10 +45,11 @@
   ASSERT_TRUE(entity);
   EXPECT_EQ(kNonUniqueName, entity->GetName());
   EXPECT_EQ(syncer::PREFERENCES, entity->GetModelType());
-  EXPECT_EQ(LoopbackServerEntity::CreateId(
-                syncer::PREFERENCES,
-                GenerateSyncableHash(syncer::PREFERENCES, kClientTag)),
-            entity->GetId());
+  EXPECT_EQ(
+      LoopbackServerEntity::CreateId(
+          syncer::PREFERENCES,
+          ClientTagHash::FromUnhashed(syncer::PREFERENCES, kClientTag).value()),
+      entity->GetId());
 }
 
 }  // namespace
diff --git a/components/sync/engine_impl/model_type_registry.cc b/components/sync/engine_impl/model_type_registry.cc
index 54140413..eb6ba28 100644
--- a/components/sync/engine_impl/model_type_registry.cc
+++ b/components/sync/engine_impl/model_type_registry.cc
@@ -135,19 +135,15 @@
     int migrated_entity_count = 0;
     if (uss_migrator_.Run(type, user_share_, worker_ptr,
                           &migrated_entity_count)) {
-      // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
       UMA_HISTOGRAM_ENUMERATION("Sync.USSMigrationSuccess",
-                                ModelTypeToHistogramInt(type),
-                                static_cast<int>(ModelType::NUM_ENTRIES));
+                                ModelTypeHistogramValue(type));
       // If we succesfully migrated, purge the directory of data for the type.
       // Purging removes the directory's local copy of the data only.
       directory()->PurgeEntriesWithTypeIn(ModelTypeSet(type), ModelTypeSet(),
                                           ModelTypeSet());
     } else {
-      // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
       UMA_HISTOGRAM_ENUMERATION("Sync.USSMigrationFailure",
-                                ModelTypeToHistogramInt(type),
-                                static_cast<int>(ModelType::NUM_ENTRIES));
+                                ModelTypeHistogramValue(type));
     }
 
     // Note that a partial failure may still contribute to the counts histogram.
diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
index fba61da..df14223 100644
--- a/components/sync/engine_impl/model_type_worker.cc
+++ b/components/sync/engine_impl/model_type_worker.cc
@@ -20,6 +20,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "components/sync/base/cancelation_signal.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/hash_util.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/time.h"
@@ -40,14 +41,14 @@
 }
 
 bool ContainsDuplicateClientTagHash(const UpdateResponseDataList& updates) {
-  std::vector<std::string> client_tag_hashes;
+  std::vector<std::string> raw_client_tag_hashes;
   for (const std::unique_ptr<UpdateResponseData>& update : updates) {
     DCHECK(update);
-    if (!update->entity->client_tag_hash.empty()) {
-      client_tag_hashes.push_back(update->entity->client_tag_hash);
+    if (!update->entity->client_tag_hash.value().empty()) {
+      raw_client_tag_hashes.push_back(update->entity->client_tag_hash.value());
     }
   }
-  return ContainsDuplicate(std::move(client_tag_hashes));
+  return ContainsDuplicate(std::move(raw_client_tag_hashes));
 }
 
 bool ContainsDuplicateServerID(const UpdateResponseDataList& updates) {
@@ -90,6 +91,104 @@
   base::UmaHistogramEnumeration("Sync.BookmarkGUIDSource", source);
 }
 
+void AdaptUniquePositionForBookmarks(const sync_pb::SyncEntity& update_entity,
+                                     syncer::EntityData* data) {
+  DCHECK(data);
+  bool has_position_scheme = false;
+  SyncPositioningScheme sync_positioning_scheme;
+  if (update_entity.has_unique_position()) {
+    data->unique_position = update_entity.unique_position();
+    has_position_scheme = true;
+    sync_positioning_scheme = SyncPositioningScheme::UNIQUE_POSITION;
+  } else if (update_entity.has_position_in_parent() ||
+             update_entity.has_insert_after_item_id()) {
+    bool missing_originator_fields = false;
+    if (!update_entity.has_originator_cache_guid() ||
+        !update_entity.has_originator_client_item_id()) {
+      DLOG(ERROR) << "Update is missing requirements for bookmark position.";
+      missing_originator_fields = true;
+    }
+
+    std::string suffix = missing_originator_fields
+                             ? UniquePosition::RandomSuffix()
+                             : GenerateSyncableBookmarkHash(
+                                   update_entity.originator_cache_guid(),
+                                   update_entity.originator_client_item_id());
+
+    if (update_entity.has_position_in_parent()) {
+      data->unique_position =
+          UniquePosition::FromInt64(update_entity.position_in_parent(), suffix)
+              .ToProto();
+      has_position_scheme = true;
+      sync_positioning_scheme = SyncPositioningScheme::POSITION_IN_PARENT;
+    } else {
+      // If update_entity has insert_after_item_id, use 0 index.
+      DCHECK(update_entity.has_insert_after_item_id());
+      data->unique_position = UniquePosition::FromInt64(0, suffix).ToProto();
+      has_position_scheme = true;
+      sync_positioning_scheme = SyncPositioningScheme::INSERT_AFTER_ITEM_ID;
+    }
+  } else if (SyncerProtoUtil::ShouldMaintainPosition(update_entity) &&
+             !update_entity.deleted()) {
+    DLOG(ERROR) << "Missing required position information in update.";
+    has_position_scheme = true;
+    sync_positioning_scheme = SyncPositioningScheme::MISSING;
+  }
+  if (has_position_scheme) {
+    UMA_HISTOGRAM_ENUMERATION("Sync.Entities.PositioningScheme",
+                              sync_positioning_scheme);
+  }
+}
+
+void AdaptTitleForBookmarks(const sync_pb::SyncEntity& update_entity,
+                            sync_pb::EntitySpecifics* specifics,
+                            bool specifics_were_encrypted) {
+  DCHECK(specifics);
+  if (specifics_were_encrypted || update_entity.deleted()) {
+    // If encrypted, the name field is never populated (unencrypted) for privacy
+    // reasons. Encryption was also introduced after moving the name out of
+    // SyncEntity so this hack is not needed at all.
+    return;
+  }
+  // Legacy clients populate the name field in the SyncEntity instead of the
+  // title field in the BookmarkSpecifics.
+  if (!specifics->bookmark().has_title() && !update_entity.name().empty()) {
+    specifics->mutable_bookmark()->set_title(update_entity.name());
+  }
+}
+
+void AdaptGuidForBookmarks(const sync_pb::SyncEntity& update_entity,
+                           sync_pb::EntitySpecifics* specifics) {
+  DCHECK(specifics);
+  if (update_entity.deleted()) {
+    return;
+  }
+  // Legacy clients don't populate the guid field in the BookmarkSpecifics, so
+  // we use the originator_client_item_id instead, if it is a valid GUID.
+  // Otherwise, we leave the field empty.
+  if (specifics->bookmark().has_guid()) {
+    LogGUIDSource(BookmarkGUIDSource::kSpecifics);
+  } else if (base::IsValidGUID(update_entity.originator_client_item_id())) {
+    specifics->mutable_bookmark()->set_guid(
+        update_entity.originator_client_item_id());
+    LogGUIDSource(BookmarkGUIDSource::kValidOCII);
+  } else {
+    LogGUIDSource(BookmarkGUIDSource::kLeftEmpty);
+  }
+}
+
+void AdaptUpdateForCompatibilityAfterDecryption(
+    ModelType model_type,
+    const sync_pb::SyncEntity& update_entity,
+    sync_pb::EntitySpecifics* specifics,
+    bool specifics_were_encrypted) {
+  DCHECK(specifics);
+  if (model_type == BOOKMARKS) {
+    AdaptTitleForBookmarks(update_entity, specifics, specifics_were_encrypted);
+    AdaptGuidForBookmarks(update_entity, specifics);
+  }
+}
+
 }  // namespace
 
 ModelTypeWorker::ModelTypeWorker(
@@ -257,7 +356,8 @@
   auto data = std::make_unique<syncer::EntityData>();
   // Prepare the message for the model thread.
   data->id = update_entity.id_string();
-  data->client_tag_hash = update_entity.client_defined_unique_tag();
+  data->client_tag_hash =
+      ClientTagHash::FromHashed(update_entity.client_defined_unique_tag());
   data->creation_time = ProtoTimeToTime(update_entity.ctime());
   data->modification_time = ProtoTimeToTime(update_entity.mtime());
   data->name = update_entity.name();
@@ -270,55 +370,12 @@
   data->originator_cache_guid = update_entity.originator_cache_guid();
   data->originator_client_item_id = update_entity.originator_client_item_id();
 
-  // Handle deprecated positioning fields. Relevant only for bookmarks.
-  if (model_type == syncer::BOOKMARKS) {
-    bool has_position_scheme = false;
-    SyncPositioningScheme sync_positioning_scheme;
-    if (update_entity.has_unique_position()) {
-      data->unique_position = update_entity.unique_position();
-      has_position_scheme = true;
-      sync_positioning_scheme = SyncPositioningScheme::UNIQUE_POSITION;
-    } else if (update_entity.has_position_in_parent() ||
-               update_entity.has_insert_after_item_id()) {
-      bool missing_originator_fields = false;
-      if (!update_entity.has_originator_cache_guid() ||
-          !update_entity.has_originator_client_item_id()) {
-        DLOG(ERROR) << "Update is missing requirements for bookmark position.";
-        missing_originator_fields = true;
-      }
-
-      std::string suffix =
-          missing_originator_fields
-              ? UniquePosition::RandomSuffix()
-              : GenerateSyncableHash(
-                    syncer::GetModelType(update_entity),
-                    /*client_tag=*/update_entity.originator_cache_guid() +
-                        update_entity.originator_client_item_id());
-
-      if (update_entity.has_position_in_parent()) {
-        data->unique_position = UniquePosition::FromInt64(
-                                    update_entity.position_in_parent(), suffix)
-                                    .ToProto();
-        has_position_scheme = true;
-        sync_positioning_scheme = SyncPositioningScheme::POSITION_IN_PARENT;
-      } else {
-        // If update_entity has insert_after_item_id, use 0 index.
-        DCHECK(update_entity.has_insert_after_item_id());
-        data->unique_position = UniquePosition::FromInt64(0, suffix).ToProto();
-        has_position_scheme = true;
-        sync_positioning_scheme = SyncPositioningScheme::INSERT_AFTER_ITEM_ID;
-      }
-    } else if (SyncerProtoUtil::ShouldMaintainPosition(update_entity) &&
-               !update_entity.deleted()) {
-      DLOG(ERROR) << "Missing required position information in update.";
-      has_position_scheme = true;
-      sync_positioning_scheme = SyncPositioningScheme::MISSING;
-    }
-    if (has_position_scheme) {
-      UMA_HISTOGRAM_ENUMERATION("Sync.Entities.PositioningScheme",
-                                sync_positioning_scheme);
-    }
+  // Adapt the update for compatibility (all except specifics that may need
+  // encryption).
+  if (model_type == BOOKMARKS) {
+    AdaptUniquePositionForBookmarks(update_entity, data.get());
   }
+  // TODO(crbug.com/881289): Generate client tag hash for wallet data.
 
   // Deleted entities must use the default instance of EntitySpecifics in
   // order for EntityData to correctly reflect that they are deleted.
@@ -346,6 +403,9 @@
                                   &data->specifics)) {
       return FAILED_TO_DECRYPT;
     }
+    AdaptUpdateForCompatibilityAfterDecryption(
+        model_type, update_entity, &data->specifics,
+        /*specifics_were_encrypted=*/true);
     response_data->entity = std::move(data);
     return SUCCESS;
   }
@@ -354,26 +414,9 @@
   if (!specifics.has_encrypted()) {
     // No encryption.
     data->specifics = specifics;
-    // Legacy clients populates the name field in the SyncEntity instead of the
-    // title field in the BookmarkSpecifics.
-    if (model_type == BOOKMARKS && !update_entity.deleted() &&
-        !specifics.bookmark().has_title() && !update_entity.name().empty()) {
-      data->specifics.mutable_bookmark()->set_title(update_entity.name());
-    }
-    // Legacy clients don't populate the guid field in the BookmarkSpecifics, so
-    // we use the originator_client_item_id instead, if it is a valid GUID.
-    // Otherwise, we leave the field empty.
-    if (model_type == BOOKMARKS && !update_entity.deleted()) {
-      if (specifics.bookmark().has_guid()) {
-        LogGUIDSource(BookmarkGUIDSource::kSpecifics);
-      } else if (base::IsValidGUID(update_entity.originator_client_item_id())) {
-        data->specifics.mutable_bookmark()->set_guid(
-            update_entity.originator_client_item_id());
-        LogGUIDSource(BookmarkGUIDSource::kValidOCII);
-      } else {
-        LogGUIDSource(BookmarkGUIDSource::kLeftEmpty);
-      }
-    }
+    AdaptUpdateForCompatibilityAfterDecryption(
+        model_type, update_entity, &data->specifics,
+        /*specifics_were_encrypted=*/false);
     response_data->entity = std::move(data);
     return SUCCESS;
   }
@@ -384,21 +427,9 @@
     if (!DecryptSpecifics(*cryptographer, specifics, &data->specifics)) {
       return FAILED_TO_DECRYPT;
     }
-
-    // Legacy clients don't populate the guid field in the BookmarkSpecifics, so
-    // we use the originator_client_item_id instead, if it is a valid GUID.
-    // Otherwise, we leave the field empty.
-    if (model_type == BOOKMARKS) {
-      if (data->specifics.bookmark().has_guid()) {
-        LogGUIDSource(BookmarkGUIDSource::kSpecifics);
-      } else if (base::IsValidGUID(update_entity.originator_client_item_id())) {
-        data->specifics.mutable_bookmark()->set_guid(
-            update_entity.originator_client_item_id());
-        LogGUIDSource(BookmarkGUIDSource::kValidOCII);
-      } else {
-        LogGUIDSource(BookmarkGUIDSource::kLeftEmpty);
-      }
-    }
+    AdaptUpdateForCompatibilityAfterDecryption(
+        model_type, update_entity, &data->specifics,
+        /*specifics_were_encrypted=*/true);
     response_data->entity = std::move(data);
     response_data->encryption_key_name = specifics.encrypted().key_name();
     return SUCCESS;
@@ -637,12 +668,18 @@
     decrypted_update->encryption_key_name = encryption_key_name;
     decrypted_update->entity = std::move(it->second->entity);
     decrypted_update->entity->specifics = std::move(specifics);
-    if (decrypted_update->entity->specifics.has_bookmark() &&
-        !decrypted_update->entity->specifics.bookmark().has_guid() &&
-        base::IsValidGUID(
-            decrypted_update->entity->originator_client_item_id)) {
-      decrypted_update->entity->specifics.mutable_bookmark()->set_guid(
-          decrypted_update->entity->originator_client_item_id);
+    // TODO(crbug.com/1007199): Reconcile with AdaptGuidForBookmarks().
+    if (decrypted_update->entity->specifics.has_bookmark()) {
+      if (decrypted_update->entity->specifics.bookmark().has_guid()) {
+        LogGUIDSource(BookmarkGUIDSource::kSpecifics);
+      } else if (base::IsValidGUID(
+                     decrypted_update->entity->originator_client_item_id)) {
+        decrypted_update->entity->specifics.mutable_bookmark()->set_guid(
+            decrypted_update->entity->originator_client_item_id);
+        LogGUIDSource(BookmarkGUIDSource::kValidOCII);
+      } else {
+        LogGUIDSource(BookmarkGUIDSource::kLeftEmpty);
+      }
     }
     pending_updates_.push_back(std::move(decrypted_update));
     it = entries_pending_decryption_.erase(it);
@@ -679,11 +716,11 @@
   UpdateResponseDataList candidates;
   pending_updates_.swap(candidates);
 
-  std::map<std::string, size_t> tag_to_index;
+  std::map<ClientTagHash, size_t> tag_to_index;
   for (std::unique_ptr<UpdateResponseData>& candidate : candidates) {
     DCHECK(candidate);
     // Items with empty client tag hash just get passed through.
-    if (candidate->entity->client_tag_hash.empty()) {
+    if (candidate->entity->client_tag_hash.value().empty()) {
       pending_updates_.push_back(std::move(candidate));
       continue;
     }
diff --git a/components/sync/engine_impl/model_type_worker_unittest.cc b/components/sync/engine_impl/model_type_worker_unittest.cc
index c0679f5..44be2fbf 100644
--- a/components/sync/engine_impl/model_type_worker_unittest.cc
+++ b/components/sync/engine_impl/model_type_worker_unittest.cc
@@ -15,7 +15,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread.h"
 #include "components/sync/base/cancelation_signal.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/unique_position.h"
 #include "components/sync/engine/model_type_processor.h"
 #include "components/sync/engine_impl/commit_contribution.h"
@@ -141,16 +141,16 @@
 // convenience functions so we can emulate server behavior.
 class ModelTypeWorkerTest : public ::testing::Test {
  protected:
-  static std::string GenerateTagHash(const std::string& tag) {
+  static ClientTagHash GenerateTagHash(const std::string& tag) {
     if (tag.empty()) {
-      return std::string();
+      return ClientTagHash();
     }
-    return GenerateSyncableHash(PREFERENCES, tag);
+    return ClientTagHash::FromUnhashed(PREFERENCES, tag);
   }
 
-  const std::string kHash1 = GenerateTagHash(kTag1);
-  const std::string kHash2 = GenerateTagHash(kTag2);
-  const std::string kHash3 = GenerateTagHash(kTag3);
+  const ClientTagHash kHash1 = GenerateTagHash(kTag1);
+  const ClientTagHash kHash2 = GenerateTagHash(kTag2);
+  const ClientTagHash kHash3 = GenerateTagHash(kTag3);
 
   explicit ModelTypeWorkerTest(ModelType model_type = PREFERENCES)
       : model_type_(model_type),
@@ -323,7 +323,7 @@
   }
 
   CommitRequestDataList GenerateCommitRequest(
-      const std::string& tag_hash,
+      const ClientTagHash& tag_hash,
       const EntitySpecifics& specifics) {
     CommitRequestDataList commit_request;
     commit_request.push_back(processor()->CommitRequest(tag_hash, specifics));
@@ -332,7 +332,7 @@
 
   CommitRequestDataList GenerateDeleteRequest(const std::string& tag) {
     CommitRequestDataList request;
-    const std::string tag_hash = GenerateTagHash(tag);
+    const ClientTagHash tag_hash = GenerateTagHash(tag);
     request.push_back(processor()->DeleteRequest(tag_hash));
     return request;
   }
@@ -562,7 +562,7 @@
   processor()->SetCommitRequest(GenerateCommitRequest(kTag1, kValue1));
   DoSuccessfulCommit();
 
-  const std::string& client_tag_hash = GenerateTagHash(kTag1);
+  const ClientTagHash client_tag_hash = GenerateTagHash(kTag1);
 
   // Exhaustively verify the SyncEntity sent in the commit message.
   ASSERT_EQ(1U, server()->GetNumCommitMessages());
@@ -574,7 +574,7 @@
   EXPECT_NE(0, entity.mtime());
   EXPECT_NE(0, entity.ctime());
   EXPECT_FALSE(entity.name().empty());
-  EXPECT_EQ(client_tag_hash, entity.client_defined_unique_tag());
+  EXPECT_EQ(client_tag_hash.value(), entity.client_defined_unique_tag());
   EXPECT_EQ(kTag1, entity.specifics().preference().name());
   EXPECT_FALSE(entity.deleted());
   EXPECT_EQ(kValue1, entity.specifics().preference().value());
@@ -630,7 +630,7 @@
   ASSERT_TRUE(server()->HasCommitEntity(kHash1));
   const SyncEntity& entity = server()->GetLastCommittedEntity(kHash1);
   EXPECT_FALSE(entity.id_string().empty());
-  EXPECT_EQ(GenerateTagHash(kTag1), entity.client_defined_unique_tag());
+  EXPECT_EQ(GenerateTagHash(kTag1).value(), entity.client_defined_unique_tag());
   EXPECT_EQ(base_version, entity.version());
   EXPECT_TRUE(entity.deleted());
 
@@ -647,7 +647,7 @@
 
   EXPECT_EQ(entity.id_string(), commit_response.id);
   EXPECT_EQ(entity.client_defined_unique_tag(),
-            commit_response.client_tag_hash);
+            commit_response.client_tag_hash.value());
   EXPECT_EQ(entity.version(), commit_response.response_version);
 }
 
@@ -716,7 +716,7 @@
   EXPECT_EQ(0, emitter()->GetUpdateCounters().num_non_initial_updates_received);
   EXPECT_EQ(0, emitter()->GetUpdateCounters().num_updates_applied);
 
-  const std::string& tag_hash = GenerateTagHash(kTag1);
+  const ClientTagHash tag_hash = GenerateTagHash(kTag1);
 
   TriggerUpdateFromServer(10, kTag1, kValue1);
 
@@ -881,7 +881,7 @@
       GenerateSpecifics("key2", "value2"));
   // Mimic a bug on the server by modifying the second entity to have the same
   // tag as the first one.
-  entity2.set_client_defined_unique_tag(GenerateTagHash(kTag1));
+  entity2.set_client_defined_unique_tag(GenerateTagHash(kTag1).value());
   worker()->ProcessGetUpdatesResponse(
       server()->GetProgress(), server()->GetContext(), {&entity1, &entity2},
       status_controller());
@@ -1226,7 +1226,7 @@
 
   // Manually create an update.
   SyncEntity entity;
-  entity.set_client_defined_unique_tag(GenerateTagHash(kTag1));
+  entity.set_client_defined_unique_tag(GenerateTagHash(kTag1).value());
   entity.set_id_string("SomeID");
   entity.set_version(1);
   entity.set_ctime(1000);
@@ -1350,7 +1350,7 @@
   EXPECT_FALSE(data.id.empty());
   EXPECT_FALSE(data.parent_id.empty());
   EXPECT_FALSE(data.is_folder);
-  EXPECT_EQ("CLIENT_TAG", data.client_tag_hash);
+  EXPECT_EQ("CLIENT_TAG", data.client_tag_hash.value());
   EXPECT_EQ("SERVER_TAG", data.server_defined_unique_tag);
   EXPECT_FALSE(data.is_deleted());
   EXPECT_EQ(kTag1, data.specifics.preference().name());
diff --git a/components/sync/engine_impl/net/server_connection_manager.cc b/components/sync/engine_impl/net/server_connection_manager.cc
index 57d3799..61a94376 100644
--- a/components/sync/engine_impl/net/server_connection_manager.cc
+++ b/components/sync/engine_impl/net/server_connection_manager.cc
@@ -119,21 +119,6 @@
   return true;
 }
 
-bool ServerConnectionManager::Connection::ReadDownloadResponse(
-    HttpResponse* response,
-    string* buffer_out) {
-  const int64_t bytes_read =
-      ReadResponse(buffer_out, static_cast<int>(response->content_length));
-
-  if (bytes_read != response->content_length) {
-    LOG(ERROR) << "Mismatched content lengths, server claimed "
-               << response->content_length << ", but sent " << bytes_read;
-    response->server_status = HttpResponse::IO_ERROR;
-    return false;
-  }
-  return true;
-}
-
 namespace {
 
 string StripTrailingSlash(const string& s) {
diff --git a/components/sync/engine_impl/net/server_connection_manager.h b/components/sync/engine_impl/net/server_connection_manager.h
index da79f0f..b10c1242 100644
--- a/components/sync/engine_impl/net/server_connection_manager.h
+++ b/components/sync/engine_impl/net/server_connection_manager.h
@@ -123,7 +123,6 @@
     bool ReadBufferResponse(std::string* buffer_out,
                             HttpResponse* response,
                             bool require_response);
-    bool ReadDownloadResponse(HttpResponse* response, std::string* buffer_out);
 
    protected:
     std::string MakeConnectionURL(const std::string& sync_server,
diff --git a/components/sync/engine_impl/non_blocking_type_commit_contribution.cc b/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
index 116afbd..49900af 100644
--- a/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
+++ b/components/sync/engine_impl/non_blocking_type_commit_contribution.cc
@@ -186,7 +186,8 @@
   // Populate client_defined_unique_tag only for non-bookmark and non-Nigori
   // data types.
   if (type != BOOKMARKS && type != NIGORI) {
-    commit_proto->set_client_defined_unique_tag(entity_data.client_tag_hash);
+    commit_proto->set_client_defined_unique_tag(
+        entity_data.client_tag_hash.value());
   }
   commit_proto->set_version(commit_entity.base_version);
   commit_proto->set_deleted(entity_data.is_deleted());
diff --git a/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc b/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc
index 9c6dee7..4f333d94 100644
--- a/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc
+++ b/components/sync/engine_impl/non_blocking_type_commit_contribution_unittest.cc
@@ -10,7 +10,7 @@
 
 #include "base/base64.h"
 #include "base/hash/sha1.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/unique_position.h"
 #include "components/sync/syncable/directory_cryptographer.h"
@@ -23,15 +23,15 @@
 using sync_pb::EntitySpecifics;
 using sync_pb::SyncEntity;
 
-const char kTag[] = "tag";
+const ClientTagHash kTag = ClientTagHash::FromHashed("tag");
 const char kValue[] = "value";
 const char kURL[] = "url";
 const char kTitle[] = "title";
 
-EntitySpecifics GeneratePreferenceSpecifics(const std::string& tag,
+EntitySpecifics GeneratePreferenceSpecifics(const ClientTagHash& tag,
                                             const std::string& value) {
   EntitySpecifics specifics;
-  specifics.mutable_preference()->set_name(tag);
+  specifics.mutable_preference()->set_name(tag.value());
   specifics.mutable_preference()->set_value(value);
   return specifics;
 }
@@ -80,7 +80,7 @@
   EXPECT_EQ(creation_time.ToJsTime(), entity.ctime());
   EXPECT_FALSE(entity.name().empty());
   EXPECT_FALSE(entity.client_defined_unique_tag().empty());
-  EXPECT_EQ(kTag, entity.specifics().preference().name());
+  EXPECT_EQ(kTag.value(), entity.specifics().preference().name());
   EXPECT_FALSE(entity.deleted());
   EXPECT_EQ(kValue, entity.specifics().preference().value());
   EXPECT_TRUE(entity.parent_id_string().empty());
@@ -187,7 +187,7 @@
   EXPECT_TRUE(entity.id_string().empty());
   EXPECT_EQ(7, entity.version());
   EXPECT_EQ("encrypted", entity.name());
-  EXPECT_EQ(kTag, entity.client_defined_unique_tag());
+  EXPECT_EQ(kTag.value(), entity.client_defined_unique_tag());
   EXPECT_FALSE(entity.deleted());
   EXPECT_FALSE(entity.specifics().has_encrypted());
   EXPECT_TRUE(entity.specifics().has_password());
@@ -248,7 +248,7 @@
   EXPECT_TRUE(entity.id_string().empty());
   EXPECT_EQ(7, entity.version());
   EXPECT_EQ("encrypted", entity.name());
-  EXPECT_EQ(kTag, entity.client_defined_unique_tag());
+  EXPECT_EQ(kTag.value(), entity.client_defined_unique_tag());
   EXPECT_FALSE(entity.deleted());
   EXPECT_FALSE(entity.specifics().has_encrypted());
   EXPECT_TRUE(entity.specifics().has_password());
diff --git a/components/sync/engine_impl/nudge_source.cc b/components/sync/engine_impl/nudge_source.cc
deleted file mode 100644
index 615370c..0000000
--- a/components/sync/engine_impl/nudge_source.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/sync/engine_impl/nudge_source.h"
-
-#include "base/logging.h"
-
-namespace syncer {
-
-#define ENUM_CASE(x) \
-  case x:            \
-    return #x;       \
-    break
-
-const char* GetNudgeSourceString(NudgeSource nudge_source) {
-  switch (nudge_source) {
-    ENUM_CASE(NUDGE_SOURCE_UNKNOWN);
-    ENUM_CASE(NUDGE_SOURCE_NOTIFICATION);
-    ENUM_CASE(NUDGE_SOURCE_LOCAL);
-    ENUM_CASE(NUDGE_SOURCE_LOCAL_REFRESH);
-  }
-  NOTREACHED();
-  return "";
-}
-
-#undef ENUM_CASE
-
-}  // namespace syncer
diff --git a/components/sync/engine_impl/nudge_source.h b/components/sync/engine_impl/nudge_source.h
index 0c95c769..9dd181e3 100644
--- a/components/sync/engine_impl/nudge_source.h
+++ b/components/sync/engine_impl/nudge_source.h
@@ -17,8 +17,6 @@
   NUDGE_SOURCE_LOCAL_REFRESH,
 };
 
-const char* GetNudgeSourceString(NudgeSource nudge_source);
-
 }  // namespace syncer
 
 #endif  // COMPONENTS_SYNC_ENGINE_IMPL_NUDGE_SOURCE_H_
diff --git a/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc b/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc
index a0f09ca..80c17edc 100644
--- a/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc
@@ -664,9 +664,7 @@
   EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
   ModelTypeSet encrypted_types =
       encryption_handler()->GetEncryptedTypesUnsafe();
-  EXPECT_EQ(
-      ModelTypeSet(PASSWORDS, DEPRECATED_WIFI_CREDENTIALS, WIFI_CONFIGURATIONS),
-      encrypted_types);
+  EXPECT_EQ(ModelTypeSet(PASSWORDS, WIFI_CONFIGURATIONS), encrypted_types);
 
   {
     WriteTransaction trans(FROM_HERE, user_share());
@@ -699,9 +697,7 @@
   EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
   ModelTypeSet encrypted_types =
       encryption_handler()->GetEncryptedTypesUnsafe();
-  EXPECT_EQ(
-      ModelTypeSet(PASSWORDS, DEPRECATED_WIFI_CREDENTIALS, WIFI_CONFIGURATIONS),
-      encrypted_types);
+  EXPECT_EQ(ModelTypeSet(PASSWORDS, WIFI_CONFIGURATIONS), encrypted_types);
 
   {
     WriteTransaction trans(FROM_HERE, user_share());
@@ -742,9 +738,7 @@
   EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
   ModelTypeSet encrypted_types =
       encryption_handler()->GetEncryptedTypesUnsafe();
-  EXPECT_EQ(
-      ModelTypeSet(PASSWORDS, DEPRECATED_WIFI_CREDENTIALS, WIFI_CONFIGURATIONS),
-      encrypted_types);
+  EXPECT_EQ(ModelTypeSet(PASSWORDS, WIFI_CONFIGURATIONS), encrypted_types);
 
   {
     WriteTransaction trans(FROM_HERE, user_share());
@@ -754,8 +748,7 @@
 
   EXPECT_FALSE(encryption_handler()->IsEncryptEverythingEnabled());
   encrypted_types = encryption_handler()->GetEncryptedTypesUnsafe();
-  EXPECT_EQ(ModelTypeSet(BOOKMARKS, PASSWORDS, DEPRECATED_WIFI_CREDENTIALS,
-                         WIFI_CONFIGURATIONS),
+  EXPECT_EQ(ModelTypeSet(BOOKMARKS, PASSWORDS, WIFI_CONFIGURATIONS),
             encrypted_types);
 }
 
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index 18ba58e5..3aa864ce 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -479,10 +479,6 @@
   return share_->directory.get();
 }
 
-const SyncScheduler* SyncManagerImpl::scheduler() const {
-  return scheduler_.get();
-}
-
 // static
 std::string SyncManagerImpl::GenerateCacheGUIDForTest() {
   return GenerateCacheGUID();
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h
index 82e8b79..150b3173 100644
--- a/components/sync/engine_impl/sync_manager_impl.h
+++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -171,8 +171,6 @@
   void NudgeForInitialDownload(ModelType type) override;
   void NudgeForCommit(ModelType type) override;
 
-  const SyncScheduler* scheduler() const;
-
   static std::string GenerateCacheGUIDForTest();
 
  protected:
@@ -197,8 +195,6 @@
     base::DictionaryValue* ToValue() const;
   };
 
-  base::TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type);
-
   using NotificationInfoMap = std::map<ModelType, NotificationInfo>;
 
   // Determine if the parents or predecessors differ between the old and new
diff --git a/components/sync/engine_impl/sync_manager_impl_unittest.cc b/components/sync/engine_impl/sync_manager_impl_unittest.cc
index 4923aab..c1fd8ba 100644
--- a/components/sync/engine_impl/sync_manager_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_manager_impl_unittest.cc
@@ -22,9 +22,9 @@
 #include "base/test/values_test_util.h"
 #include "base/values.h"
 #include "components/sync/base/cancelation_signal.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/extensions_activity.h"
 #include "components/sync/base/fake_encryptor.h"
-#include "components/sync/base/hash_util.h"
 #include "components/sync/base/mock_unrecoverable_error_handler.h"
 #include "components/sync/base/model_type_test_util.h"
 #include "components/sync/engine/engine_util.h"
@@ -91,6 +91,13 @@
 
 namespace {
 
+// Tests in this file covers directory behavior, so we did't migrate them to
+// use ClientTagHash.
+std::string GenerateSyncableHash(ModelType type,
+                                 const std::string& client_tag) {
+  return ClientTagHash::FromUnhashed(type, client_tag).value();
+}
+
 // Makes a child node under the type root folder.  Returns the id of the
 // newly-created node.
 int64_t MakeNode(UserShare* share,
diff --git a/components/sync/engine_impl/syncer_proto_util.cc b/components/sync/engine_impl/syncer_proto_util.cc
index 956f28d..8e6646a 100644
--- a/components/sync/engine_impl/syncer_proto_util.cc
+++ b/components/sync/engine_impl/syncer_proto_util.cc
@@ -346,9 +346,8 @@
           progress_marker.token();
       UMA_HISTOGRAM_ENUMERATION(
           "Sync.PostedDataTypeGetUpdatesRequest",
-          ModelTypeToHistogramInt(GetModelTypeFromSpecificsFieldNumber(
-              progress_marker.data_type_id())),
-          static_cast<int>(ModelType::NUM_ENTRIES));
+          ModelTypeHistogramValue(GetModelTypeFromSpecificsFieldNumber(
+              progress_marker.data_type_id())));
     }
   }
 
diff --git a/components/sync/engine_impl/syncer_proto_util.h b/components/sync/engine_impl/syncer_proto_util.h
index 3b75acc..a48fdea 100644
--- a/components/sync/engine_impl/syncer_proto_util.h
+++ b/components/sync/engine_impl/syncer_proto_util.h
@@ -19,7 +19,6 @@
 class ClientToServerResponse;
 class ClientToServerResponse_Error;
 class CommitResponse_EntryResponse;
-class EntitySpecifics;
 class SyncEntity;
 }
 
@@ -70,13 +69,6 @@
   static const std::string& NameFromCommitEntryResponse(
       const sync_pb::CommitResponse_EntryResponse& entry);
 
-  // EntitySpecifics is used as a filter for the GetUpdates message to tell
-  // the server which datatypes to send back.  This adds a datatype so that
-  // it's included in the filter.
-  static void AddToEntitySpecificDatatypesFilter(
-      ModelType datatype,
-      sync_pb::EntitySpecifics* filter);
-
   // Get a debug string representation of the client to server response.
   static std::string ClientToServerResponseDebugString(
       const sync_pb::ClientToServerResponse& response);
diff --git a/components/sync/engine_impl/test_entry_factory.cc b/components/sync/engine_impl/test_entry_factory.cc
index abbba1a..06dc2f9 100644
--- a/components/sync/engine_impl/test_entry_factory.cc
+++ b/components/sync/engine_impl/test_entry_factory.cc
@@ -4,7 +4,7 @@
 
 #include "components/sync/engine_impl/test_entry_factory.h"
 
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/syncable/directory.h"
 #include "components/sync/syncable/entry.h"
 #include "components/sync/syncable/model_neutral_mutable_entry.h"
@@ -311,7 +311,8 @@
   entry->PutId(item_id);
   entry->PutCtime(now);
   entry->PutMtime(now);
-  entry->PutUniqueClientTag(GenerateSyncableHash(model_type, name));
+  entry->PutUniqueClientTag(
+      ClientTagHash::FromUnhashed(model_type, name).value());
   entry->PutBaseVersion(version);
   entry->PutIsUnsynced(false);
   entry->PutNonUniqueName(name);
diff --git a/components/sync/engine_impl/update_applicator.h b/components/sync/engine_impl/update_applicator.h
index df01804..dd9e234 100644
--- a/components/sync/engine_impl/update_applicator.h
+++ b/components/sync/engine_impl/update_applicator.h
@@ -20,7 +20,6 @@
 
 namespace syncable {
 class WriteTransaction;
-class Entry;
 }
 
 // An UpdateApplicator is used to iterate over a number of unapplied updates,
@@ -48,9 +47,6 @@
   }
 
  private:
-  // If true, AttemptOneApplication will skip over |entry| and return true.
-  bool SkipUpdate(const syncable::Entry& entry);
-
   // Used to decrypt sensitive sync nodes.
   const Cryptographer* cryptographer_;
 
diff --git a/components/sync/engine_impl/uss_migrator_unittest.cc b/components/sync/engine_impl/uss_migrator_unittest.cc
index c22a177..70d0561 100644
--- a/components/sync/engine_impl/uss_migrator_unittest.cc
+++ b/components/sync/engine_impl/uss_migrator_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "components/sync/base/cancelation_signal.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/engine_impl/cycle/non_blocking_type_debug_info_emitter.h"
 #include "components/sync/engine_impl/model_type_worker.h"
 #include "components/sync/engine_impl/test_entry_factory.h"
@@ -30,11 +30,6 @@
 namespace {
 
 const ModelType kModelType = PREFERENCES;
-
-std::string GenerateTagHash(const std::string& tag) {
-  return GenerateSyncableHash(kModelType, tag);
-}
-
 const char kToken1[] = "token1";
 const char kTag1[] = "tag1";
 const char kTag2[] = "tag2";
@@ -42,7 +37,10 @@
 const char kValue1[] = "value1";
 const char kValue2[] = "value2";
 const char kValue3[] = "value3";
-const std::string kHash1(GenerateTagHash(kTag1));
+
+ClientTagHash GenerateTagHash(const std::string& tag) {
+  return ClientTagHash::FromUnhashed(kModelType, tag);
+}
 
 sync_pb::EntitySpecifics GenerateSpecifics(const std::string& tag,
                                            const std::string& value) {
@@ -140,7 +138,7 @@
   const EntityData& entity = *update->entity;
 
   EXPECT_FALSE(entity.id.empty());
-  EXPECT_EQ(kHash1, entity.client_tag_hash);
+  EXPECT_EQ(GenerateTagHash(kTag1), entity.client_tag_hash);
   EXPECT_EQ(1, update->response_version);
   EXPECT_EQ(ctime, entity.creation_time);
   EXPECT_EQ(ctime, entity.modification_time);
diff --git a/components/sync/model/data_type_error_handler_impl.cc b/components/sync/model/data_type_error_handler_impl.cc
index b05e98f..090edea 100644
--- a/components/sync/model/data_type_error_handler_impl.cc
+++ b/components/sync/model/data_type_error_handler_impl.cc
@@ -22,10 +22,8 @@
 void DataTypeErrorHandlerImpl::OnUnrecoverableError(const SyncError& error) {
   if (!dump_stack_.is_null())
     dump_stack_.Run();
-  // TODO(wychen): enum uma should be strongly typed. crbug.com/661401
   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures2",
-                            ModelTypeToHistogramInt(error.model_type()),
-                            static_cast<int>(ModelType::NUM_ENTRIES));
+                            ModelTypeHistogramValue(error.model_type()));
   ui_thread_->PostTask(error.location(), base::BindOnce(sync_callback_, error));
 }
 
diff --git a/components/sync/model/entity_data.cc b/components/sync/model/entity_data.cc
index 16ba2cd..19cdbf71 100644
--- a/components/sync/model/entity_data.cc
+++ b/components/sync/model/entity_data.cc
@@ -80,7 +80,7 @@
       std::make_unique<base::DictionaryValue>();
   dict->Set("SPECIFICS", EntitySpecificsToValue(specifics));
   ADD_TO_DICT(dict, id);
-  ADD_TO_DICT(dict, client_tag_hash);
+  ADD_TO_DICT(dict, client_tag_hash.value());
   ADD_TO_DICT(dict, originator_cache_guid);
   ADD_TO_DICT(dict, originator_client_item_id);
   ADD_TO_DICT(dict, server_defined_unique_tag);
diff --git a/components/sync/model/entity_data.h b/components/sync/model/entity_data.h
index 80b01b1..330f54c6 100644
--- a/components/sync/model/entity_data.h
+++ b/components/sync/model/entity_data.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/protocol/sync.pb.h"
 
 namespace syncer {
@@ -36,7 +37,7 @@
   // Used for various map lookups. Should always be available for all data types
   // except bookmarks. Sent to the server as
   // SyncEntity::client_defined_unique_tag.
-  std::string client_tag_hash;
+  ClientTagHash client_tag_hash;
 
   // A GUID that identifies the the sync client who initially committed this
   // entity. It's relevant only for bookmarks. See the definition in sync.proto
diff --git a/components/sync/model/fake_model_type_change_processor.cc b/components/sync/model/fake_model_type_change_processor.cc
index d585da0b..f8b18fc2 100644
--- a/components/sync/model/fake_model_type_change_processor.cc
+++ b/components/sync/model/fake_model_type_change_processor.cc
@@ -41,7 +41,7 @@
     const std::string& storage_key) {}
 
 void FakeModelTypeChangeProcessor::UntrackEntityForClientTagHash(
-    const std::string& client_tag_hash) {}
+    const ClientTagHash& client_tag_hash) {}
 
 bool FakeModelTypeChangeProcessor::IsEntityUnsynced(
     const std::string& storage_key) {
diff --git a/components/sync/model/fake_model_type_change_processor.h b/components/sync/model/fake_model_type_change_processor.h
index 6c2f4a3..347a72d9 100644
--- a/components/sync/model/fake_model_type_change_processor.h
+++ b/components/sync/model/fake_model_type_change_processor.h
@@ -37,7 +37,7 @@
                         MetadataChangeList* metadata_change_list) override;
   void UntrackEntityForStorageKey(const std::string& storage_key) override;
   void UntrackEntityForClientTagHash(
-      const std::string& client_tag_hash) override;
+      const ClientTagHash& client_tag_hash) override;
   bool IsEntityUnsynced(const std::string& storage_key) override;
   base::Time GetEntityCreationTime(
       const std::string& storage_key) const override;
diff --git a/components/sync/model/fake_model_type_sync_bridge.cc b/components/sync/model/fake_model_type_sync_bridge.cc
index 86fa9ce2..0c309c3 100644
--- a/components/sync/model/fake_model_type_sync_bridge.cc
+++ b/components/sync/model/fake_model_type_sync_bridge.cc
@@ -10,7 +10,7 @@
 #include "base/bind.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/conflict_resolution.h"
 #include "components/sync/model/model_type_store.h"
 #include "components/sync/model/mutable_data_batch.h"
@@ -65,9 +65,9 @@
 }
 
 // static
-std::string FakeModelTypeSyncBridge::TagHashFromKey(const std::string& key) {
-  return GenerateSyncableHash(PREFERENCES,
-                              FakeModelTypeSyncBridge::ClientTagFromKey(key));
+ClientTagHash FakeModelTypeSyncBridge::TagHashFromKey(const std::string& key) {
+  return ClientTagHash::FromUnhashed(
+      PREFERENCES, FakeModelTypeSyncBridge::ClientTagFromKey(key));
 }
 
 // static
diff --git a/components/sync/model/fake_model_type_sync_bridge.h b/components/sync/model/fake_model_type_sync_bridge.h
index f1c959a..e7ef5353 100644
--- a/components/sync/model/fake_model_type_sync_bridge.h
+++ b/components/sync/model/fake_model_type_sync_bridge.h
@@ -22,6 +22,8 @@
 
 namespace syncer {
 
+class ClientTagHash;
+
 // A basic, functional implementation of ModelTypeSyncBridge for testing
 // purposes. It uses the PREFERENCES type to provide a simple key/value
 // interface, and uses its own simple in-memory Store class.
@@ -31,7 +33,7 @@
   static std::string ClientTagFromKey(const std::string& key);
 
   // Generates the tag hash for a given key.
-  static std::string TagHashFromKey(const std::string& key);
+  static ClientTagHash TagHashFromKey(const std::string& key);
 
   // Generates entity specifics for the given key and value.
   static sync_pb::EntitySpecifics GenerateSpecifics(const std::string& key,
diff --git a/components/sync/model/mock_model_type_change_processor.cc b/components/sync/model/mock_model_type_change_processor.cc
index 0aa016d..cb437936d 100644
--- a/components/sync/model/mock_model_type_change_processor.cc
+++ b/components/sync/model/mock_model_type_change_processor.cc
@@ -45,7 +45,7 @@
   }
 
   void UntrackEntityForClientTagHash(
-      const std::string& client_tag_hash) override {
+      const ClientTagHash& client_tag_hash) override {
     other_->UntrackEntityForClientTagHash(client_tag_hash);
   }
 
diff --git a/components/sync/model/mock_model_type_change_processor.h b/components/sync/model/mock_model_type_change_processor.h
index 6594d33..f029094 100644
--- a/components/sync/model/mock_model_type_change_processor.h
+++ b/components/sync/model/mock_model_type_change_processor.h
@@ -33,7 +33,7 @@
   MOCK_METHOD1(UntrackEntityForStorageKey,
                void(const std::string& storage_key));
   MOCK_METHOD1(UntrackEntityForClientTagHash,
-               void(const std::string& client_tag_hash));
+               void(const ClientTagHash& client_tag_hash));
   MOCK_METHOD1(IsEntityUnsynced, bool(const std::string& storage_key));
   MOCK_CONST_METHOD1(GetEntityCreationTime,
                      base::Time(const std::string& storage_key));
diff --git a/components/sync/model/model_type_change_processor.h b/components/sync/model/model_type_change_processor.h
index bfc55b2..d9078d2 100644
--- a/components/sync/model/model_type_change_processor.h
+++ b/components/sync/model/model_type_change_processor.h
@@ -19,6 +19,7 @@
 
 namespace syncer {
 
+class ClientTagHash;
 class MetadataBatch;
 class MetadataChangeList;
 class ModelTypeSyncBridge;
@@ -65,7 +66,7 @@
   // datatypes that can't generate storage keys. The call is ignored if
   // |client_tag_hash| is unknown.
   virtual void UntrackEntityForClientTagHash(
-      const std::string& client_tag_hash) = 0;
+      const ClientTagHash& client_tag_hash) = 0;
 
   // Returns true if a tracked entity has local changes. A commit may or may not
   // be in progress at this time.
diff --git a/components/sync/model/model_type_store_service.h b/components/sync/model/model_type_store_service.h
index 0663f43..1203307 100644
--- a/components/sync/model/model_type_store_service.h
+++ b/components/sync/model/model_type_store_service.h
@@ -15,8 +15,6 @@
 
 namespace syncer {
 
-class BlockingModelTypeStore;
-
 // Handles the shared resources for ModelTypeStore and related classes,
 // including a shared background sequence runner.
 class ModelTypeStoreService : public KeyedService {
@@ -31,12 +29,6 @@
   virtual RepeatingModelTypeStoreFactory GetStoreFactory() = 0;
 
   virtual scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner() = 0;
-
-  // Creates a BlockingModelTypeStore. Must be called from the backend
-  // sequence as returned in GetBackendTaskRunner(). Can return null if there
-  // was an error.
-  virtual std::unique_ptr<BlockingModelTypeStore>
-  CreateBlockingStoreFromBackendSequence(ModelType type) = 0;
 };
 
 }  // namespace syncer
diff --git a/components/sync/model/recording_model_type_change_processor.cc b/components/sync/model/recording_model_type_change_processor.cc
index 8e54c6fa..0be034b8 100644
--- a/components/sync/model/recording_model_type_change_processor.cc
+++ b/components/sync/model/recording_model_type_change_processor.cc
@@ -44,7 +44,7 @@
 }
 
 void RecordingModelTypeChangeProcessor::UntrackEntityForClientTagHash(
-    const std::string& client_tag_hash) {
+    const ClientTagHash& client_tag_hash) {
   untrack_for_client_tag_hash_set_.insert(client_tag_hash);
 }
 
@@ -54,18 +54,13 @@
 }
 
 bool RecordingModelTypeChangeProcessor::IsTrackingMetadata() {
-  return is_tracking_metadata_;
+  return true;
 }
 
 std::string RecordingModelTypeChangeProcessor::TrackedAccountId() {
   return "";
 }
 
-void RecordingModelTypeChangeProcessor::SetIsTrackingMetadata(
-    bool is_tracking) {
-  is_tracking_metadata_ = is_tracking;
-}
-
 // static
 std::unique_ptr<ModelTypeChangeProcessor>
 RecordingModelTypeChangeProcessor::CreateProcessorAndAssignRawPointer(
diff --git a/components/sync/model/recording_model_type_change_processor.h b/components/sync/model/recording_model_type_change_processor.h
index a3aa2f7..e66f46d 100644
--- a/components/sync/model/recording_model_type_change_processor.h
+++ b/components/sync/model/recording_model_type_change_processor.h
@@ -10,6 +10,7 @@
 #include <set>
 #include <string>
 
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/model/fake_model_type_change_processor.h"
 #include "components/sync/model/model_type_sync_bridge.h"
 
@@ -33,13 +34,11 @@
                         MetadataChangeList* metadata_change_list) override;
   void UntrackEntityForStorageKey(const std::string& storage_key) override;
   void UntrackEntityForClientTagHash(
-      const std::string& client_tag_hash) override;
+      const ClientTagHash& client_tag_hash) override;
   void ModelReadyToSync(std::unique_ptr<MetadataBatch> batch) override;
   bool IsTrackingMetadata() override;
   std::string TrackedAccountId() override;
 
-  void SetIsTrackingMetadata(bool is_tracking);
-
   const std::multimap<std::string, std::unique_ptr<EntityData>>& put_multimap()
       const {
     return put_multimap_;
@@ -56,7 +55,7 @@
     return untrack_for_storage_key_set_;
   }
 
-  const std::set<std::string>& untrack_for_client_tag_hash_set() const {
+  const std::set<ClientTagHash>& untrack_for_client_tag_hash_set() const {
     return untrack_for_client_tag_hash_set_;
   }
 
@@ -74,9 +73,8 @@
   std::multimap<std::string, std::unique_ptr<EntityData>> update_multimap_;
   std::set<std::string> delete_set_;
   std::set<std::string> untrack_for_storage_key_set_;
-  std::set<std::string> untrack_for_client_tag_hash_set_;
+  std::set<ClientTagHash> untrack_for_client_tag_hash_set_;
   std::unique_ptr<MetadataBatch> metadata_;
-  bool is_tracking_metadata_ = true;
 };
 
 }  //  namespace syncer
diff --git a/components/sync/model/sync_change.cc b/components/sync/model/sync_change.cc
index a405d40..e182e4ef 100644
--- a/components/sync/model/sync_change.cc
+++ b/components/sync/model/sync_change.cc
@@ -6,6 +6,8 @@
 
 #include <ostream>
 
+#include "components/sync/protocol/sync.pb.h"
+
 namespace syncer {
 
 SyncChange::SyncChange() : change_type_(ACTION_INVALID) {}
@@ -28,8 +30,14 @@
     return IsRealDataType(sync_data_.GetDataType());
 
   // Local changes must always have a tag and specify a valid datatype.
-  if (SyncDataLocal(sync_data_).GetTag().empty() ||
-      !IsRealDataType(sync_data_.GetDataType())) {
+  if (SyncDataLocal(sync_data_).GetTag().empty())
+    return false;
+  // TODO(crbug.com/1007942): The ARTICLES data type has been removed and so is
+  // not considered a "real" data type anymore, but dom_distiller code still
+  // uses it for its local storage, and will DCHECK-fail if we don't consider
+  // article data as valid here.
+  if (!IsRealDataType(sync_data_.GetDataType()) &&
+      !sync_data_.GetSpecifics().has_article()) {
     return false;
   }
 
diff --git a/components/sync/model/sync_data.cc b/components/sync/model/sync_data.cc
index 865f19d..aa9a61c2 100644
--- a/components/sync/model/sync_data.cc
+++ b/components/sync/model/sync_data.cc
@@ -11,6 +11,7 @@
 #include "base/json/json_writer.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/protocol/proto_value_conversions.h"
 #include "components/sync/protocol/sync.pb.h"
 #include "components/sync/syncable/base_node.h"
@@ -153,7 +154,7 @@
   return id_;
 }
 
-const std::string& SyncDataRemote::GetClientTagHash() const {
+ClientTagHash SyncDataRemote::GetClientTagHash() const {
   // It seems that client_defined_unique_tag has a bit of an overloaded use,
   // holding onto the un-hashed tag while local, and then the hashed value when
   // communicating with the server. This usage is copying the latter of these
@@ -161,7 +162,8 @@
   // the server so we wouldn't be able to set this value anyways. The only way
   // to recreate an un-hashed tag is for the service to do so with a specifics.
   DCHECK(!immutable_entity_.Get().client_defined_unique_tag().empty());
-  return immutable_entity_.Get().client_defined_unique_tag();
+  return ClientTagHash::FromHashed(
+      immutable_entity_.Get().client_defined_unique_tag());
 }
 
 }  // namespace syncer
diff --git a/components/sync/model/sync_data.h b/components/sync/model/sync_data.h
index e0b5d4c4..f269774a 100644
--- a/components/sync/model/sync_data.h
+++ b/components/sync/model/sync_data.h
@@ -26,6 +26,7 @@
 
 namespace syncer {
 
+class ClientTagHash;
 class SyncDataLocal;
 class SyncDataRemote;
 
@@ -149,7 +150,7 @@
 
   // Returns the tag hash value. May not always be present, in which case an
   // empty string will be returned.
-  const std::string& GetClientTagHash() const;
+  ClientTagHash GetClientTagHash() const;
 
   // Deprecated: might not be populated in SyncableService API.
   // TODO(crbug.com/870624): Remove when directory is removed.
diff --git a/components/sync/model/sync_error.cc b/components/sync/model/sync_error.cc
index 44ef6e7..dbe35e93 100644
--- a/components/sync/model/sync_error.cc
+++ b/components/sync/model/sync_error.cc
@@ -154,8 +154,4 @@
       << message_;
 }
 
-void PrintTo(const SyncError& sync_error, std::ostream* os) {
-  *os << sync_error.ToString();
-}
-
 }  // namespace syncer
diff --git a/components/sync/model/sync_error.h b/components/sync/model/sync_error.h
index d07af6f..eaf1943 100644
--- a/components/sync/model/sync_error.h
+++ b/components/sync/model/sync_error.h
@@ -120,9 +120,6 @@
   ErrorType error_type_;
 };
 
-// gmock printer helper.
-void PrintTo(const SyncError& sync_error, std::ostream* os);
-
 }  // namespace syncer
 
 #endif  // COMPONENTS_SYNC_MODEL_SYNC_ERROR_H_
diff --git a/components/sync/model/test_model_type_store_service.cc b/components/sync/model/test_model_type_store_service.cc
index b75eb6a..277deef6 100644
--- a/components/sync/model/test_model_type_store_service.cc
+++ b/components/sync/model/test_model_type_store_service.cc
@@ -31,11 +31,4 @@
   return base::SequencedTaskRunnerHandle::Get();
 }
 
-std::unique_ptr<BlockingModelTypeStore>
-TestModelTypeStoreService::CreateBlockingStoreFromBackendSequence(
-    ModelType type) {
-  return std::make_unique<BlockingModelTypeStoreImpl>(type,
-                                                      store_backend_.get());
-}
-
 }  // namespace syncer
diff --git a/components/sync/model/test_model_type_store_service.h b/components/sync/model/test_model_type_store_service.h
index 977ac144..6d71a4b 100644
--- a/components/sync/model/test_model_type_store_service.h
+++ b/components/sync/model/test_model_type_store_service.h
@@ -28,8 +28,6 @@
   const base::FilePath& GetSyncDataPath() const override;
   RepeatingModelTypeStoreFactory GetStoreFactory() override;
   scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner() override;
-  std::unique_ptr<BlockingModelTypeStore>
-  CreateBlockingStoreFromBackendSequence(ModelType type) override;
 
  private:
   const scoped_refptr<ModelTypeStoreBackend> store_backend_;
diff --git a/components/sync/model_impl/blocking_model_type_store_impl.cc b/components/sync/model_impl/blocking_model_type_store_impl.cc
index 32ddbd78..71889f89 100644
--- a/components/sync/model_impl/blocking_model_type_store_impl.cc
+++ b/components/sync/model_impl/blocking_model_type_store_impl.cc
@@ -241,8 +241,7 @@
       static_cast<LevelDbWriteBatch*>(write_batch.release()));
   DCHECK_EQ(write_batch_impl->GetModelType(), type_);
   UMA_HISTOGRAM_ENUMERATION("Sync.ModelTypeStoreCommitCount",
-                            ModelTypeToHistogramInt(type_),
-                            static_cast<int>(ModelType::NUM_ENTRIES));
+                            ModelTypeHistogramValue(type_));
   return backend_->WriteModifications(
       LevelDbWriteBatch::ToLevelDbWriteBatch(std::move(write_batch_impl)));
 }
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 0c24ca9..beefb109 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
@@ -15,7 +15,6 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "components/sync/base/data_type_histogram.h"
-#include "components/sync/base/hash_util.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/commit_queue.h"
@@ -30,7 +29,7 @@
 namespace {
 
 int CountNonTombstoneEntries(
-    const std::map<std::string, std::unique_ptr<ProcessorEntity>>& entities) {
+    const std::map<ClientTagHash, std::unique_ptr<ProcessorEntity>>& entities) {
   int count = 0;
   for (const auto& kv : entities) {
     if (!kv.second->metadata().is_deleted()) {
@@ -130,9 +129,10 @@
       // to avoid copying.
       std::unique_ptr<ProcessorEntity> entity =
           ProcessorEntity::CreateFromMetadata(it->first, std::move(*metadata));
-      storage_key_to_tag_hash_[entity->storage_key()] =
-          entity->metadata().client_tag_hash();
-      entities_[entity->metadata().client_tag_hash()] = std::move(entity);
+      ClientTagHash client_tag_hash =
+          ClientTagHash::FromHashed(entity->metadata().client_tag_hash());
+      storage_key_to_tag_hash_[entity->storage_key()] = client_tag_hash;
+      entities_[client_tag_hash] = std::move(entity);
     }
     model_type_state_ = batch->GetModelTypeState();
   } else {
@@ -184,12 +184,8 @@
     // belong to the current syncing client.
     if (model_type_state_.progress_marker().data_type_id() !=
         GetSpecificsFieldNumberFromModelType(type_)) {
-      // This is not strongly typed because historically,
-      // ModelTypeToHistogramInt() defines quite a different order from the
-      // type_ enum.
       UMA_HISTOGRAM_ENUMERATION("Sync.PersistedModelTypeIdMismatch",
-                                ModelTypeToHistogramInt(type_),
-                                static_cast<int>(ModelType::NUM_ENTRIES));
+                                ModelTypeHistogramValue(type_));
     }
     ClearMetadataAndResetState();
 
@@ -391,14 +387,15 @@
   if (entity == nullptr) {
     // The bridge is creating a new entity. The bridge may or may not populate
     // |data->client_tag_hash|, so let's ask for the client tag if needed.
-    if (data->client_tag_hash.empty()) {
+    if (data->client_tag_hash.value().empty()) {
       data->client_tag_hash = GetClientTagHash(storage_key, *data);
     } else if (bridge_->SupportsGetClientTag()) {
       // If the Put() call already included the client tag, let's verify that
       // it's consistent with the bridge's regular GetClientTag() function (if
       // supported by the bridge).
-      DCHECK_EQ(data->client_tag_hash,
-                GenerateSyncableHash(type_, bridge_->GetClientTag(*data)));
+      DCHECK_EQ(
+          data->client_tag_hash,
+          ClientTagHash::FromUnhashed(type_, bridge_->GetClientTag(*data)));
     }
     // If another entity exists for the same client_tag_hash, it could be the
     // case that the bridge has deleted this entity but the tombstone hasn't
@@ -427,8 +424,7 @@
   } else if (entity->MatchesData(*data)) {
     // Ignore changes that don't actually change anything.
     UMA_HISTOGRAM_ENUMERATION("Sync.ModelTypeRedundantPut",
-                              ModelTypeToHistogramInt(type_),
-                              static_cast<int>(ModelType::NUM_ENTRIES));
+                              ModelTypeHistogramValue(type_));
     return;
   }
 
@@ -467,8 +463,8 @@
     const EntityData& entity_data,
     const std::string& storage_key,
     MetadataChangeList* metadata_change_list) {
-  const std::string& client_tag_hash = entity_data.client_tag_hash;
-  DCHECK(!client_tag_hash.empty());
+  const ClientTagHash& client_tag_hash = entity_data.client_tag_hash;
+  DCHECK(!client_tag_hash.value().empty());
   DCHECK(!storage_key.empty());
   DCHECK(!bridge_->SupportsGetStorageKey());
   DCHECK(model_type_state_.initial_sync_done());
@@ -501,9 +497,9 @@
 }
 
 void ClientTagBasedModelTypeProcessor::UntrackEntityForClientTagHash(
-    const std::string& client_tag_hash) {
+    const ClientTagHash& client_tag_hash) {
   DCHECK(model_type_state_.initial_sync_done());
-  DCHECK(!client_tag_hash.empty());
+  DCHECK(!client_tag_hash.value().empty());
   // Is a no-op if no entity for |client_tag_hash| is tracked.
   DCHECK(GetEntityForTagHash(client_tag_hash) == nullptr ||
          GetEntityForTagHash(client_tag_hash)->storage_key().empty());
@@ -675,8 +671,8 @@
       // wallet_data entity has a client tag.
       continue;
     }
-    update->entity->client_tag_hash =
-        GenerateSyncableHash(type, bridge->GetClientTag(*update->entity));
+    update->entity->client_tag_hash = ClientTagHash::FromUnhashed(
+        type, bridge->GetClientTag(*update->entity));
   }
 }
 
@@ -759,18 +755,18 @@
     EntityChangeList* entity_changes,
     std::string* storage_key_to_clear) {
   const EntityData& data = *update->entity;
-  const std::string& client_tag_hash = data.client_tag_hash;
+  const ClientTagHash& client_tag_hash = data.client_tag_hash;
 
   // Filter out updates without a client tag hash (including permanent nodes,
   // which have server tags instead).
-  if (client_tag_hash.empty()) {
+  if (client_tag_hash.value().empty()) {
     return nullptr;
   }
 
   // Filter out unexpected client tag hashes.
   if (!data.is_deleted() && bridge_->SupportsGetClientTag() &&
       client_tag_hash !=
-          GenerateSyncableHash(type_, bridge_->GetClientTag(data))) {
+          ClientTagHash::FromUnhashed(type_, bridge_->GetClientTag(data))) {
     DLOG(WARNING) << "Received unexpected client tag hash: " << client_tag_hash
                   << " for " << ModelTypeToString(type_);
     return nullptr;
@@ -1022,8 +1018,8 @@
 
   for (const std::unique_ptr<syncer::UpdateResponseData>& update : updates) {
     DCHECK(update);
-    const std::string& client_tag_hash = update->entity->client_tag_hash;
-    if (client_tag_hash.empty()) {
+    const ClientTagHash& client_tag_hash = update->entity->client_tag_hash;
+    if (client_tag_hash.value().empty()) {
       // Ignore updates missing a client tag hash (e.g. permanent nodes).
       continue;
     }
@@ -1034,7 +1030,7 @@
       continue;
     }
     if (bridge_->SupportsGetClientTag() &&
-        client_tag_hash != GenerateSyncableHash(
+        client_tag_hash != ClientTagHash::FromUnhashed(
                                type_, bridge_->GetClientTag(*update->entity))) {
       DLOG(WARNING) << "Received unexpected client tag hash: "
                     << client_tag_hash << " for " << ModelTypeToString(type_);
@@ -1126,7 +1122,8 @@
     if (entity->CanClearMetadata()) {
       metadata_changes->ClearMetadata(entity->storage_key());
       storage_key_to_tag_hash_.erase(entity->storage_key());
-      entities_.erase(entity->metadata().client_tag_hash());
+      entities_.erase(
+          ClientTagHash::FromHashed(entity->metadata().client_tag_hash()));
     } else {
       metadata_changes->UpdateMetadata(entity->storage_key(),
                                        entity->metadata());
@@ -1198,8 +1195,7 @@
     // had been called.
     storage_keys_to_untrack.push_back(storage_key);
     UMA_HISTOGRAM_ENUMERATION("Sync.ModelTypeOrphanMetadata",
-                              ModelTypeToHistogramInt(type_),
-                              static_cast<int>(ModelType::NUM_ENTRIES));
+                              ModelTypeHistogramValue(type_));
   }
 
   if (storage_keys_to_untrack.empty()) {
@@ -1240,13 +1236,13 @@
   std::move(callback).Run(std::move(commit_requests));
 }
 
-std::string ClientTagBasedModelTypeProcessor::GetClientTagHash(
+ClientTagHash ClientTagBasedModelTypeProcessor::GetClientTagHash(
     const std::string& storage_key,
     const EntityData& data) const {
   auto iter = storage_key_to_tag_hash_.find(storage_key);
   DCHECK(bridge_->SupportsGetClientTag());
   return iter == storage_key_to_tag_hash_.end()
-             ? GenerateSyncableHash(type_, bridge_->GetClientTag(data))
+             ? ClientTagHash::FromUnhashed(type_, bridge_->GetClientTag(data))
              : iter->second;
 }
 
@@ -1267,13 +1263,13 @@
 }
 
 ProcessorEntity* ClientTagBasedModelTypeProcessor::GetEntityForTagHash(
-    const std::string& tag_hash) {
+    const ClientTagHash& tag_hash) {
   auto it = entities_.find(tag_hash);
   return it != entities_.end() ? it->second.get() : nullptr;
 }
 
 const ProcessorEntity* ClientTagBasedModelTypeProcessor::GetEntityForTagHash(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   auto it = entities_.find(tag_hash);
   return it != entities_.end() ? it->second.get() : nullptr;
 }
@@ -1281,7 +1277,7 @@
 ProcessorEntity* ClientTagBasedModelTypeProcessor::CreateEntity(
     const std::string& storage_key,
     const EntityData& data) {
-  DCHECK(!data.client_tag_hash.empty());
+  DCHECK(!data.client_tag_hash.value().empty());
   DCHECK(entities_.find(data.client_tag_hash) == entities_.end());
   DCHECK(!bridge_->SupportsGetStorageKey() || !storage_key.empty());
   DCHECK(storage_key.empty() || storage_key_to_tag_hash_.find(storage_key) ==
@@ -1299,7 +1295,7 @@
     const EntityData& data) {
   if (bridge_->SupportsGetClientTag()) {
     DCHECK_EQ(data.client_tag_hash,
-              GenerateSyncableHash(type_, bridge_->GetClientTag(data)));
+              ClientTagHash::FromUnhashed(type_, bridge_->GetClientTag(data)));
   }
   std::string storage_key;
   if (bridge_->SupportsGetStorageKey())
@@ -1368,7 +1364,8 @@
     MetadataChangeList* metadata_change_list) {
   metadata_change_list->ClearMetadata(entity->storage_key());
   storage_key_to_tag_hash_.erase(entity->storage_key());
-  entities_.erase(entity->metadata().client_tag_hash());
+  entities_.erase(
+      ClientTagHash::FromHashed(entity->metadata().client_tag_hash()));
 }
 
 void ClientTagBasedModelTypeProcessor::ResetState(
@@ -1424,7 +1421,8 @@
       data->id = "s" + metadata.server_id();
       data->creation_time = ProtoTimeToTime(metadata.creation_time());
       data->modification_time = ProtoTimeToTime(metadata.modification_time());
-      data->client_tag_hash = metadata.client_tag_hash();
+      data->client_tag_hash =
+          ClientTagHash::FromHashed(metadata.client_tag_hash());
     }
 
     std::unique_ptr<base::DictionaryValue> node = data->ToDictionaryValue();
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.h b/components/sync/model_impl/client_tag_based_model_type_processor.h
index 67842cf..88ebbc3 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.h
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/sequence_checker.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/sync_stop_metadata_fate.h"
 #include "components/sync/engine/cycle/status_counters.h"
@@ -68,7 +69,7 @@
                         MetadataChangeList* metadata_change_list) override;
   void UntrackEntityForStorageKey(const std::string& storage_key) override;
   void UntrackEntityForClientTagHash(
-      const std::string& client_tag_hash) override;
+      const ClientTagHash& client_tag_hash) override;
   bool IsEntityUnsynced(const std::string& storage_key) override;
   base::Time GetEntityCreationTime(
       const std::string& storage_key) const override;
@@ -191,8 +192,8 @@
   // Looks up the client tag hash for the given |storage_key|, and regenerates
   // with |data| if the lookup finds nothing. Does not update the storage key to
   // client tag hash mapping.
-  std::string GetClientTagHash(const std::string& storage_key,
-                               const EntityData& data) const;
+  ClientTagHash GetClientTagHash(const std::string& storage_key,
+                                 const EntityData& data) const;
 
   // Gets the entity for the given storage key, or null if there isn't one.
   ProcessorEntity* GetEntityForStorageKey(const std::string& storage_key);
@@ -200,8 +201,9 @@
       const std::string& storage_key) const;
 
   // Gets the entity for the given tag hash, or null if there isn't one.
-  ProcessorEntity* GetEntityForTagHash(const std::string& tag_hash);
-  const ProcessorEntity* GetEntityForTagHash(const std::string& tag_hash) const;
+  ProcessorEntity* GetEntityForTagHash(const ClientTagHash& tag_hash);
+  const ProcessorEntity* GetEntityForTagHash(
+      const ClientTagHash& tag_hash) const;
 
   // Create an entity in the entity map for |storage_key| and return a pointer
   // to it.
@@ -291,7 +293,7 @@
   // A map of client tag hash to sync entities known to this processor. This
   // should contain entries and metadata for most everything, although the
   // entities may not always contain model type data/specifics.
-  std::map<std::string, std::unique_ptr<ProcessorEntity>> entities_;
+  std::map<ClientTagHash, std::unique_ptr<ProcessorEntity>> entities_;
 
   // The bridge wants to communicate entirely via storage keys that it is free
   // to define and can understand more easily. All of the sync machinery wants
@@ -302,7 +304,7 @@
   // GetStorageKey(). In this case the bridge is responsible for updating
   // storage key with UpdateStorageKey() call from within
   // MergeSyncData/ApplySyncChanges.
-  std::map<std::string, std::string> storage_key_to_tag_hash_;
+  std::map<std::string, ClientTagHash> storage_key_to_tag_hash_;
 
   // If the processor should behave as if |type_| is one of the commit only
   // model types. For this processor, being commit only means that on commit
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
index 9399271..071abf06 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -16,6 +16,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "base/threading/platform_thread.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/sync_mode.h"
 #include "components/sync/base/time.h"
@@ -42,11 +43,10 @@
 const char kValue1[] = "value1";
 const char kValue2[] = "value2";
 const char kValue3[] = "value3";
-const std::string kHash1(FakeModelTypeSyncBridge::TagHashFromKey(kKey1));
-const std::string kHash2(FakeModelTypeSyncBridge::TagHashFromKey(kKey2));
-const std::string kHash3(FakeModelTypeSyncBridge::TagHashFromKey(kKey3));
-const std::string kHash4(FakeModelTypeSyncBridge::TagHashFromKey(kKey4));
-const std::string kHash5(FakeModelTypeSyncBridge::TagHashFromKey(kKey5));
+
+ClientTagHash GetHash(const std::string& key) {
+  return FakeModelTypeSyncBridge::TagHashFromKey(key);
+}
 
 // Typically used for verification after a delete. The specifics given to the
 // worker/processor will not have been initialized and thus empty.
@@ -454,7 +454,7 @@
 
   EXPECT_EQ(0, bridge()->merge_call_count());
   // Initial sync with one server item.
-  worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey2, kValue2));
+  worker()->UpdateFromServer(GetHash(kKey2), GenerateSpecifics(kKey2, kValue2));
   EXPECT_EQ(1, bridge()->merge_call_count());
 
   // Now have data and metadata for both items, as well as a commit request for
@@ -464,7 +464,7 @@
   EXPECT_EQ(2U, ProcessorEntityCount());
   EXPECT_EQ(1, db()->GetMetadata(kKey1).sequence_number());
   EXPECT_EQ(0, db()->GetMetadata(kKey2).sequence_number());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 }
 
 // Test that an initial sync filters out tombstones in the processor.
@@ -475,7 +475,7 @@
   EXPECT_EQ(0, bridge()->merge_call_count());
   // Initial sync with a tombstone. The fake bridge checks that it doesn't get
   // any tombstones in its MergeSyncData function.
-  worker()->TombstoneFromServer(kHash1);
+  worker()->TombstoneFromServer(GetHash(kKey1));
   EXPECT_EQ(1, bridge()->merge_call_count());
 
   // Should still have no data, metadata, or commit requests.
@@ -511,7 +511,7 @@
 
   // Check that data coming from sync is treated as a normal GetUpdates.
   OnSyncStarting();
-  worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey2, kValue2));
+  worker()->UpdateFromServer(GetHash(kKey2), GenerateSpecifics(kKey2, kValue2));
   EXPECT_EQ(0, bridge()->merge_call_count());
   EXPECT_EQ(1, bridge()->apply_call_count());
   EXPECT_EQ(2U, db()->data_count());
@@ -579,7 +579,7 @@
   OnSyncStarting();
   OnCommitDataLoaded();
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics2});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics2});
 
   // Connect, data, put.
   EntitySpecifics specifics6 = ResetStateWriteItem(kKey1, kValue1);
@@ -588,8 +588,8 @@
   OnCommitDataLoaded();
   EntitySpecifics specifics7 = bridge()->WriteItem(kKey1, kValue2);
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics6});
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics7});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics6});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {specifics7});
 
   // Connect, put, data.
   EntitySpecifics specifics100 = ResetStateWriteItem(kKey1, kValue1);
@@ -598,7 +598,7 @@
   EntitySpecifics specifics8 = bridge()->WriteItem(kKey1, kValue2);
   OnCommitDataLoaded();
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics8});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics8});
   // GetData was launched as a result of GetLocalChanges call(). Since all data
   // are in memory, the 2nd pending commit should be empty.
   worker()->VerifyNthPendingCommit(1, {}, {});
@@ -610,7 +610,7 @@
   OnSyncStarting();
   EXPECT_FALSE(bridge()->GetDataCallback());
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics10});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics10});
 
   // Connect, data, delete.
   EntitySpecifics specifics12 = ResetStateWriteItem(kKey1, kValue1);
@@ -619,8 +619,8 @@
   OnCommitDataLoaded();
   bridge()->DeleteItem(kKey1);
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics12});
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics12});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {kEmptySpecifics});
 
   // Connect, delete, data.
   ResetStateWriteItem(kKey1, kValue1);
@@ -629,7 +629,7 @@
   bridge()->DeleteItem(kKey1);
   OnCommitDataLoaded();
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {kEmptySpecifics});
   // GetData was launched as a result of GetLocalChanges call(). Since all data
   // are in memory, the 2nd pending commit should be empty.
   worker()->VerifyNthPendingCommit(1, {}, {});
@@ -641,7 +641,7 @@
   OnSyncStarting();
   EXPECT_FALSE(bridge()->GetDataCallback());
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {kEmptySpecifics});
 }
 
 // Tests cases where pending data loads synchronously.
@@ -652,7 +652,7 @@
   InitializeToMetadataLoaded();
   OnSyncStarting();
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics1});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics1});
 
   // Sync, model.
   EntitySpecifics specifics2 = ResetStateWriteItem(kKey1, kValue1);
@@ -660,7 +660,7 @@
   bridge()->ExpectSynchronousDataCallback();
   InitializeToMetadataLoaded();
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics2});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics2});
 }
 
 // This test covers race conditions during loading a pending delete. All cases
@@ -679,7 +679,7 @@
   InitializeToMetadataLoaded();
   OnSyncStarting();
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {kEmptySpecifics});
 
   // Connect, put.
   ResetStateDeleteItem(kKey1, kValue1);
@@ -688,8 +688,8 @@
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
   EntitySpecifics specifics1 = bridge()->WriteItem(kKey1, kValue2);
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {kEmptySpecifics});
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics1});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {specifics1});
 
   // Put, connect.
   ResetStateDeleteItem(kKey1, kValue1);
@@ -697,7 +697,7 @@
   EntitySpecifics specifics2 = bridge()->WriteItem(kKey1, kValue2);
   OnSyncStarting();
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics2});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics2});
 
   // Connect, delete.
   ResetStateDeleteItem(kKey1, kValue1);
@@ -706,8 +706,8 @@
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
   bridge()->DeleteItem(kKey1);
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {kEmptySpecifics});
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {kEmptySpecifics});
 
   // Delete, connect.
   ResetStateDeleteItem(kKey1, kValue1);
@@ -715,7 +715,7 @@
   bridge()->DeleteItem(kKey1);
   OnSyncStarting();
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {kEmptySpecifics});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {kEmptySpecifics});
 }
 
 // Test that loading a committed item does not queue another commit.
@@ -746,9 +746,9 @@
             type_processor()->GetEntityModificationTime(kKey1));
 
   // Verify the commit request this operation has triggered.
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
   const CommitRequestData* tag1_request_data =
-      worker()->GetLatestPendingCommitForHash(kHash1);
+      worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
   ASSERT_TRUE(tag1_request_data);
   const EntityData& tag1_data = *tag1_request_data->entity;
 
@@ -808,24 +808,24 @@
   entity_data->specifics.mutable_preference()->set_value(kValue1);
 
   entity_data->name = kKey1;
-  entity_data->client_tag_hash = kHash1;
+  entity_data->client_tag_hash = GetHash(kKey1);
   entity_data->id = kId1;
   bridge()->WriteItem(kKey1, std::move(entity_data));
 
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  ASSERT_FALSE(worker()->HasPendingCommitForHash(kHash3));
-  ASSERT_TRUE(worker()->HasPendingCommitForHash(kHash1));
+  ASSERT_FALSE(worker()->HasPendingCommitForHash(GetHash(kKey3)));
+  ASSERT_TRUE(worker()->HasPendingCommitForHash(GetHash(kKey1)));
   EXPECT_EQ(1U, db()->metadata_count());
-  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(kHash1));
+  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetHash(kKey1)));
   const EntityData& out_entity1 =
-      *worker()->GetLatestPendingCommitForHash(kHash1)->entity;
+      *worker()->GetLatestPendingCommitForHash(GetHash(kKey1))->entity;
   const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
 
   EXPECT_EQ(kId1, out_entity1.id);
-  EXPECT_NE(kHash3, out_entity1.client_tag_hash);
+  EXPECT_NE(GetHash(kKey3), out_entity1.client_tag_hash);
   EXPECT_EQ(kValue1, out_entity1.specifics.preference().value());
   EXPECT_EQ(kId1, metadata_v1.server_id());
-  EXPECT_EQ(metadata_v1.client_tag_hash(), out_entity1.client_tag_hash);
+  EXPECT_EQ(metadata_v1.client_tag_hash(), out_entity1.client_tag_hash.value());
 
   entity_data = std::make_unique<EntityData>();
   // This is a sketchy move here, changing the name will change the generated
@@ -833,25 +833,25 @@
   entity_data->specifics.mutable_preference()->set_name(kKey2);
   entity_data->specifics.mutable_preference()->set_value(kValue2);
   entity_data->name = kKey2;
-  entity_data->client_tag_hash = kHash3;
+  entity_data->client_tag_hash = GetHash(kKey3);
   // Make sure ID isn't overwritten either.
   entity_data->id = kId2;
   bridge()->WriteItem(kKey1, std::move(entity_data));
 
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  ASSERT_FALSE(worker()->HasPendingCommitForHash(kHash3));
-  ASSERT_TRUE(worker()->HasPendingCommitForHash(kHash1));
+  ASSERT_FALSE(worker()->HasPendingCommitForHash(GetHash(kKey3)));
+  ASSERT_TRUE(worker()->HasPendingCommitForHash(GetHash(kKey1)));
   EXPECT_EQ(1U, db()->metadata_count());
-  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(kHash1));
+  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetHash(kKey1)));
   const EntityData& out_entity2 =
-      *worker()->GetLatestPendingCommitForHash(kHash1)->entity;
+      *worker()->GetLatestPendingCommitForHash(GetHash(kKey1))->entity;
   const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
 
   EXPECT_EQ(kValue2, out_entity2.specifics.preference().value());
   // Should still see old cid1 value, override is not respected on update.
   EXPECT_EQ(kId1, out_entity2.id);
   EXPECT_EQ(kId1, metadata_v2.server_id());
-  EXPECT_EQ(metadata_v2.client_tag_hash(), out_entity2.client_tag_hash);
+  EXPECT_EQ(metadata_v2.client_tag_hash(), out_entity2.client_tag_hash.value());
 
   // Specifics have changed so the hashes should not match.
   EXPECT_NE(metadata_v1.specifics_hash(), metadata_v2.specifics_hash());
@@ -864,14 +864,14 @@
 
   bridge()->WriteItem(kKey1, kValue1);
   ASSERT_EQ(1U, db()->metadata_count());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
   int64_t request_data_v1_sequence_number;
   {
     // request_data_v1 is valid only while the commit is still pending.
     const CommitRequestData* request_data_v1 =
-        worker()->GetLatestPendingCommitForHash(kHash1);
+        worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
     ASSERT_TRUE(request_data_v1);
     const EntityData& data_v1 = *request_data_v1->entity;
     EXPECT_EQ(data_v1.specifics.preference().value(), kValue1);
@@ -890,7 +890,7 @@
 
   bridge()->WriteItem(kKey1, kValue2);
   EXPECT_EQ(1U, db()->metadata_count());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   EXPECT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
   EXPECT_EQ(ctime, type_processor()->GetEntityCreationTime(kKey1));
@@ -898,7 +898,7 @@
   EXPECT_NE(ctime, mtime);
 
   const CommitRequestData* request_data_v2 =
-      worker()->GetLatestPendingCommitForHash(kHash1);
+      worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
   ASSERT_TRUE(request_data_v2);
   const EntityData& data_v2 = *request_data_v2->entity;
   const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
@@ -942,10 +942,10 @@
 
   bridge()->WriteItem(kKey1, kValue1);
   ASSERT_EQ(1U, db()->metadata_count());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   const CommitRequestData* request_data_v1 =
-      worker()->GetLatestPendingCommitForHash(kHash1);
+      worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
   ASSERT_TRUE(request_data_v1);
   const EntityData& data_v1 = *request_data_v1->entity;
   const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
@@ -961,7 +961,7 @@
 
   bridge()->WriteItem(kKey1, kValue2);
   EXPECT_EQ(1U, db()->metadata_count());
-  worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}, {GetHash(kKey1)}});
 
   EXPECT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
   EXPECT_EQ(ctime, type_processor()->GetEntityCreationTime(kKey1));
@@ -969,7 +969,7 @@
   EXPECT_NE(mtime, ctime);
 
   const CommitRequestData* request_data_v2 =
-      worker()->GetLatestPendingCommitForHash(kHash1);
+      worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
   ASSERT_TRUE(request_data_v2);
   const EntityData& data_v2 = *request_data_v2->entity;
   const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
@@ -1011,7 +1011,7 @@
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
   ASSERT_EQ(1U, db()->metadata_count());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   const base::Time ctime = type_processor()->GetEntityCreationTime(kKey1);
   const base::Time mtime = type_processor()->GetEntityModificationTime(kKey1);
@@ -1019,7 +1019,7 @@
   ASSERT_FALSE(mtime.is_null());
 
   bridge()->WriteItem(kKey1, kValue1);
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   EXPECT_EQ(ctime, type_processor()->GetEntityCreationTime(kKey1));
   EXPECT_EQ(mtime, type_processor()->GetEntityModificationTime(kKey1));
@@ -1028,7 +1028,7 @@
 // Thoroughly tests the data generated by a server item creation.
 TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldProcessRemoteCreation) {
   InitializeToReadyState();
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1));
   EXPECT_EQ(1U, db()->data_count());
   EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
@@ -1069,7 +1069,7 @@
 TEST_F(ClientTagBasedModelTypeProcessorTest,
        ShouldIgnoreRemoteUpdatesWithUnexpectedClientTagHash) {
   InitializeToReadyState();
-  worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey2), GenerateSpecifics(kKey1, kValue1));
   EXPECT_EQ(0U, db()->data_count());
   EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
@@ -1081,7 +1081,7 @@
   InitializeToReadyState();
   bridge()->ErrorOnNextCall();
   ExpectError();
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1));
 }
 
 // Thoroughly tests the data generated by a server item creation.
@@ -1094,12 +1094,12 @@
   EXPECT_EQ(2U, db()->metadata_change_count());
 
   // Redundant update from server doesn't write data but updates metadata.
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1));
   EXPECT_EQ(1U, db()->data_change_count());
   EXPECT_EQ(3U, db()->metadata_change_count());
 
   // A reflection (update already received) is ignored completely.
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1),
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1),
                              0 /* version_offset */);
   EXPECT_EQ(1U, db()->data_change_count());
   EXPECT_EQ(3U, db()->metadata_change_count());
@@ -1122,7 +1122,7 @@
   // Metadata is not removed until the commit response comes back.
   EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
   EXPECT_TRUE(metadata_v2.is_deleted());
@@ -1161,9 +1161,9 @@
        ShouldHandleLocalDeletionDuringLocalCreationCommit) {
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
   const CommitRequestData* data_v1 =
-      worker()->GetLatestPendingCommitForHash(kHash1);
+      worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
   ASSERT_TRUE(data_v1);
 
   const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
@@ -1176,10 +1176,10 @@
   EXPECT_EQ(0U, db()->data_count());
   EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
-  worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}, {GetHash(kKey1)}});
 
   const CommitRequestData* data_v2 =
-      worker()->GetLatestPendingCommitForHash(kHash1);
+      worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
   ASSERT_TRUE(data_v2);
   EXPECT_GT(data_v2->sequence_number, data_v1->sequence_number);
   EXPECT_TRUE(data_v2->entity->id.empty());
@@ -1218,7 +1218,7 @@
   EXPECT_EQ(1U, db()->data_count());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
-  worker()->TombstoneFromServer(kHash1);
+  worker()->TombstoneFromServer(GetHash(kKey1));
   // Delete from server should clear the data and all the metadata.
   EXPECT_EQ(0U, db()->data_count());
   EXPECT_EQ(0U, db()->metadata_count());
@@ -1251,7 +1251,7 @@
 TEST_F(ClientTagBasedModelTypeProcessorTest,
        ShouldIgnoreRemoteDeletionOfUnknownEntity) {
   InitializeToReadyState();
-  worker()->TombstoneFromServer(kHash1);
+  worker()->TombstoneFromServer(GetHash(kKey1));
   EXPECT_EQ(0U, db()->data_count());
   EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
@@ -1264,7 +1264,7 @@
        ShouldRetryCommitAfterServerError) {
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   // Entity is sent to server. Processor shouldn't include it in local changes.
   CommitRequestDataList commit_request;
@@ -1279,7 +1279,7 @@
       INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
   OnCommitDataLoaded();
   EXPECT_EQ(1U, commit_request.size());
-  EXPECT_EQ(kHash1, commit_request[0]->entity->client_tag_hash);
+  EXPECT_EQ(GetHash(kKey1), commit_request[0]->entity->client_tag_hash);
 }
 
 // Tests that GetLocalChanges honors max_entries parameter.
@@ -1308,7 +1308,7 @@
   const EntityMetadata metadata1 = db()->GetMetadata(kKey1);
 
   // There should be one commit request for this item only.
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 
   bridge()->WriteItem(kKey2, kValue2);
   EXPECT_EQ(2U, db()->data_count());
@@ -1316,7 +1316,7 @@
   const EntityMetadata metadata2 = db()->GetMetadata(kKey2);
 
   // The second write should trigger another single-item commit request.
-  worker()->VerifyPendingCommits({{kHash1}, {kHash2}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}, {GetHash(kKey2)}});
 
   EXPECT_FALSE(metadata1.is_deleted());
   EXPECT_EQ(1, metadata1.sequence_number());
@@ -1337,16 +1337,16 @@
   EXPECT_EQ(kValue1, db()->GetValue(kKey1));
   EXPECT_EQ(1U, db()->metadata_change_count());
   EXPECT_EQ(kUncommittedVersion, db()->GetMetadata(kKey1).server_version());
-  worker()->VerifyPendingCommits({{kHash1}});
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics});
 
   // Changes match doesn't call ResolveConflict.
-  worker()->UpdateFromServer(kHash1, specifics);
+  worker()->UpdateFromServer(GetHash(kKey1), specifics);
 
   // Updated metadata but not data; no new commit request.
   EXPECT_EQ(1U, db()->data_change_count());
   EXPECT_EQ(1, db()->GetMetadata(kKey1).server_version());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -1359,15 +1359,15 @@
   // Change value locally and at the same time simulate conflicting update from
   // server.
   EntitySpecifics specifics2 = bridge()->WriteItem(kKey1, kValue2);
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue3));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue3));
   OnCommitDataLoaded();
 
   // Updated metadata but not data; new commit request.
   EXPECT_EQ(2U, db()->data_change_count());
   EXPECT_EQ(4U, db()->metadata_change_count());
   EXPECT_EQ(2, db()->GetMetadata(kKey1).server_version());
-  worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics2});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}, {GetHash(kKey1)}});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {specifics2});
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -1377,14 +1377,15 @@
 
   bridge()->WriteItem(kKey1, kValue1);
   ASSERT_EQ(1U, worker()->GetNumPendingCommits());
-  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(kHash1));
-  ASSERT_TRUE(
-      worker()->GetLatestPendingCommitForHash(kHash1)->entity->id.empty());
+  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetHash(kKey1)));
+  ASSERT_TRUE(worker()
+                  ->GetLatestPendingCommitForHash(GetHash(kKey1))
+                  ->entity->id.empty());
 
   // The update from the server should be mostly ignored because local wins, but
   // the server ID should be updated.
   bridge()->SetConflictResolution(ConflictResolution::kUseLocal);
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue3));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue3));
   OnCommitDataLoaded();
   // In this test setup, the processor's nudge for commit immediately pulls
   // updates from the processor and list them as pending commits, so we should
@@ -1393,7 +1394,7 @@
 
   // Verify the commit request this operation has triggered.
   const CommitRequestData* tag1_request_data =
-      worker()->GetLatestPendingCommitForHash(kHash1);
+      worker()->GetLatestPendingCommitForHash(GetHash(kKey1));
   ASSERT_TRUE(tag1_request_data);
   const EntityData& tag1_data = *tag1_request_data->entity;
 
@@ -1427,15 +1428,16 @@
 
   bridge()->DeleteItem(kKey1);
   ASSERT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyPendingCommits({{kHash1}});
-  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(kHash1));
-  ASSERT_TRUE(
-      worker()->GetLatestPendingCommitForHash(kHash1)->entity->is_deleted());
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
+  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetHash(kKey1)));
+  ASSERT_TRUE(worker()
+                  ->GetLatestPendingCommitForHash(GetHash(kKey1))
+                  ->entity->is_deleted());
   ASSERT_EQ(2U, db()->data_change_count());
   ASSERT_EQ(3U, db()->metadata_change_count());
   ASSERT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1));
 
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue2));
 
   // Updated client data and metadata; no new commit request.
   EXPECT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1));
@@ -1443,7 +1445,7 @@
   EXPECT_EQ(kValue2, db()->GetValue(kKey1));
   EXPECT_EQ(4U, db()->metadata_change_count());
   EXPECT_EQ(2, db()->GetMetadata(kKey1).server_version());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -1455,15 +1457,16 @@
 
   bridge()->DeleteItem(kKey1);
   ASSERT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyPendingCommits({{kHash1}});
-  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(kHash1));
-  ASSERT_TRUE(
-      worker()->GetLatestPendingCommitForHash(kHash1)->entity->is_deleted());
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
+  ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(GetHash(kKey1)));
+  ASSERT_TRUE(worker()
+                  ->GetLatestPendingCommitForHash(GetHash(kKey1))
+                  ->entity->is_deleted());
   ASSERT_EQ(2U, db()->data_change_count());
   ASSERT_EQ(3U, db()->metadata_change_count());
   ASSERT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1));
 
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue2));
 
   // A new storage key should have been generated, which should replace the
   // previous when it comes to storing data and metadata.
@@ -1481,7 +1484,7 @@
   EXPECT_EQ(kValue2, db()->GetValue(new_storage_key));
   EXPECT_EQ(5U, db()->metadata_change_count());
   EXPECT_EQ(2, db()->GetMetadata(new_storage_key).server_version());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -1489,14 +1492,14 @@
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
   bridge()->SetConflictResolution(ConflictResolution::kUseRemote);
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue2));
 
   // Updated client data and metadata; no new commit request.
   EXPECT_EQ(2U, db()->data_change_count());
   EXPECT_EQ(kValue2, db()->GetValue(kKey1));
   EXPECT_EQ(2U, db()->metadata_change_count());
   EXPECT_EQ(1, db()->GetMetadata(kKey1).server_version());
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -1504,7 +1507,7 @@
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
   bridge()->SetConflictResolution(ConflictResolution::kUseRemote);
-  worker()->TombstoneFromServer(kHash1);
+  worker()->TombstoneFromServer(GetHash(kKey1));
 
   // Updated client data and metadata; no new commit request.
   EXPECT_EQ(0U, db()->data_count());
@@ -1525,7 +1528,7 @@
 
   // The second item has a commit request in progress.
   bridge()->WriteItem(kKey2, kValue2);
-  EXPECT_TRUE(worker()->HasPendingCommitForHash(kHash2));
+  EXPECT_TRUE(worker()->HasPendingCommitForHash(GetHash(kKey2)));
 
   DisconnectSync();
 
@@ -1539,13 +1542,13 @@
   EXPECT_EQ(2U, worker()->GetNthPendingCommit(0).size());
 
   // The first item was already in sync.
-  EXPECT_FALSE(worker()->HasPendingCommitForHash(kHash1));
+  EXPECT_FALSE(worker()->HasPendingCommitForHash(GetHash(kKey1)));
 
   // The second item's commit was interrupted and should be retried.
-  EXPECT_TRUE(worker()->HasPendingCommitForHash(kHash2));
+  EXPECT_TRUE(worker()->HasPendingCommitForHash(GetHash(kKey2)));
 
   // The third item's commit was not started until the reconnect.
-  EXPECT_TRUE(worker()->HasPendingCommitForHash(kHash3));
+  EXPECT_TRUE(worker()->HasPendingCommitForHash(GetHash(kKey3)));
 }
 
 // Test proper handling of stop (without disabling sync) and re-enable.
@@ -1560,7 +1563,7 @@
 
   // The second item has a commit request in progress.
   bridge()->WriteItem(kKey2, kValue2);
-  EXPECT_TRUE(worker()->HasPendingCommitForHash(kHash2));
+  EXPECT_TRUE(worker()->HasPendingCommitForHash(GetHash(kKey2)));
 
   type_processor()->OnSyncStopping(KEEP_METADATA);
   EXPECT_TRUE(type_processor()->IsTrackingMetadata());
@@ -1573,7 +1576,7 @@
   worker()->UpdateFromServer();
 
   // Once we're ready to commit, only the newest items should be committed.
-  worker()->VerifyPendingCommits({{kHash3}});
+  worker()->VerifyPendingCommits({{GetHash(kKey3)}});
 }
 
 // Test proper handling of disable and re-enable.
@@ -1588,7 +1591,7 @@
 
   // The second item has a commit request in progress.
   bridge()->WriteItem(kKey2, kValue2);
-  EXPECT_TRUE(worker()->HasPendingCommitForHash(kHash2));
+  EXPECT_TRUE(worker()->HasPendingCommitForHash(GetHash(kKey2)));
 
   type_processor()->OnSyncStopping(CLEAR_METADATA);
   EXPECT_FALSE(type_processor()->IsTrackingMetadata());
@@ -1603,7 +1606,8 @@
 
   // Once we're ready to commit, all three local items should consider
   // themselves uncommitted and pending for commit.
-  worker()->VerifyPendingCommits({{kHash1}, {kHash2}, {kHash3}});
+  worker()->VerifyPendingCommits(
+      {{GetHash(kKey1)}, {GetHash(kKey2)}, {GetHash(kKey3)}});
 }
 
 // Test proper handling of disable-sync before initial sync done.
@@ -1634,7 +1638,7 @@
   EntitySpecifics specifics1 = WriteItemAndAck(kKey1, kValue1);
   // Create another item and don't wait for its commit response.
   EntitySpecifics specifics2 = bridge()->WriteItem(kKey2, kValue2);
-  worker()->VerifyPendingCommits({{kHash2}});
+  worker()->VerifyPendingCommits({{GetHash(kKey2)}});
   EXPECT_EQ(1U, db()->GetMetadata(kKey1).sequence_number());
   EXPECT_EQ(1U, db()->GetMetadata(kKey2).sequence_number());
 
@@ -1646,7 +1650,7 @@
   OnCommitDataLoaded();
   // All data are in memory now.
   ASSERT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(1, {kHash1, kHash2},
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1), GetHash(kKey2)},
                                    {specifics1, specifics2});
   // Sequence numbers in the store are updated.
   EXPECT_EQ(2U, db()->GetMetadata(kKey1).sequence_number());
@@ -1669,36 +1673,38 @@
   InitializeToReadyState();
 
   // Receive an unencrypted update.
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1));
   ASSERT_EQ(0U, worker()->GetNumPendingCommits());
 
   UpdateResponseDataList update;
   // Receive an entity with old encryption as part of the update.
   update.push_back(worker()->GenerateUpdateData(
-      kHash2, GenerateSpecifics(kKey2, kValue2), 1, "k1"));
+      GetHash(kKey2), GenerateSpecifics(kKey2, kValue2), 1, "k1"));
   // Receive an entity with up-to-date encryption as part of the update.
   update.push_back(worker()->GenerateUpdateData(
-      kHash3, GenerateSpecifics(kKey3, kValue3), 1, "k2"));
+      GetHash(kKey3), GenerateSpecifics(kKey3, kValue3), 1, "k2"));
   // Set desired encryption key to k2 to force updates to some items.
   worker()->UpdateWithEncryptionKey("k2", std::move(update));
 
   OnCommitDataLoaded();
   // kKey1 needed data so once that's loaded, kKey1 and kKey2 are queued for
   // commit.
-  worker()->VerifyPendingCommits({{kHash1, kHash2}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1), GetHash(kKey2)}});
 
   // Receive a separate update that was encrypted with key k1.
-  worker()->UpdateFromServer(kHash4, GenerateSpecifics(kKey4, kValue1), 1,
-                             "k1");
+  worker()->UpdateFromServer(GetHash(kKey4), GenerateSpecifics(kKey4, kValue1),
+                             1, "k1");
   OnCommitDataLoaded();
   // Receipt of updates encrypted with old key also forces a re-encrypt commit.
-  worker()->VerifyPendingCommits({{kHash1, kHash2}, {kHash4}});
+  worker()->VerifyPendingCommits(
+      {{GetHash(kKey1), GetHash(kKey2)}, {GetHash(kKey4)}});
 
   // Receive an update that was encrypted with key k2.
-  worker()->UpdateFromServer(kHash5, GenerateSpecifics(kKey5, kValue1), 1,
-                             "k2");
+  worker()->UpdateFromServer(GetHash(kKey5), GenerateSpecifics(kKey5, kValue1),
+                             1, "k2");
   // That was the correct key, so no re-encryption is required.
-  worker()->VerifyPendingCommits({{kHash1, kHash2}, {kHash4}});
+  worker()->VerifyPendingCommits(
+      {{GetHash(kKey1), GetHash(kKey2)}, {GetHash(kKey4)}});
 }
 
 // Test that re-encrypting enqueues the right data for kUseLocal conflicts.
@@ -1711,16 +1717,17 @@
   OnCommitDataLoaded();
 
   EntitySpecifics specifics = bridge()->WriteItem(kKey1, kValue2);
-  worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}, {GetHash(kKey1)}});
 
   bridge()->SetConflictResolution(ConflictResolution::kUseLocal);
   // Unencrypted update needs to be re-commited with key k1.
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue3), 1, "");
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue3),
+                             1, "");
   OnCommitDataLoaded();
 
   // Ensure the re-commit has the correct value.
   EXPECT_EQ(3U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(2, {kHash1}, {specifics});
+  worker()->VerifyNthPendingCommit(2, {GetHash(kKey1)}, {specifics});
   EXPECT_EQ(kValue2, db()->GetValue(kKey1));
 }
 
@@ -1734,12 +1741,12 @@
   bridge()->SetConflictResolution(ConflictResolution::kUseRemote);
   // Unencrypted update needs to be re-commited with key k1.
   EntitySpecifics specifics = GenerateSpecifics(kKey1, kValue2);
-  worker()->UpdateFromServer(kHash1, specifics, 1, "");
+  worker()->UpdateFromServer(GetHash(kKey1), specifics, 1, "");
   OnCommitDataLoaded();
 
   // Ensure the re-commit has the correct value.
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {specifics});
   EXPECT_EQ(kValue2, db()->GetValue(kKey1));
 }
 
@@ -1755,12 +1762,12 @@
 
   // Unencrypted update needs to be re-commited with key k1.
   EntitySpecifics specifics = GenerateSpecifics(kKey1, kValue2);
-  worker()->UpdateFromServer(kHash1, specifics, 1, "");
+  worker()->UpdateFromServer(GetHash(kKey1), specifics, 1, "");
   OnCommitDataLoaded();
 
   // Ensure the re-commit has the correct value.
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {specifics});
   EXPECT_EQ(kValue2, db()->GetValue(kKey1));
 }
 
@@ -1772,9 +1779,9 @@
   worker()->UpdateWithEncryptionKey("k1");
   OnCommitDataLoaded();
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics});
 
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue2));
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
 }
 
@@ -1784,7 +1791,7 @@
   InitializeToReadyState();
   UpdateResponseDataList updates;
   updates.push_back(worker()->GenerateUpdateData(
-      /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
+      ClientTagHash(), GenerateSpecifics(kKey1, kValue1), 1, "k1"));
 
   worker()->UpdateFromServer(std::move(updates));
 
@@ -1804,7 +1811,7 @@
 
   UpdateResponseDataList updates;
   updates.push_back(worker()->GenerateUpdateData(
-      /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
+      ClientTagHash(), GenerateSpecifics(kKey1, kValue1), 1, "k1"));
   worker()->UpdateFromServer(std::move(updates));
 
   ASSERT_EQ(1, bridge()->merge_call_count());
@@ -1828,7 +1835,7 @@
 
   UpdateResponseDataList updates;
   updates.push_back(worker()->GenerateUpdateData(
-      /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
+      ClientTagHash(), GenerateSpecifics(kKey1, kValue1), 1, "k1"));
   worker()->UpdateFromServer(std::move(updates));
 
   ASSERT_EQ(1, bridge()->merge_call_count());
@@ -1856,9 +1863,9 @@
   InitializeToReadyState();
   UpdateResponseDataList updates;
   updates.push_back(worker()->GenerateUpdateData(
-      /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
+      ClientTagHash(), GenerateSpecifics(kKey1, kValue1), 1, "k1"));
   updates.push_back(worker()->GenerateUpdateData(
-      /*tag_hash=*/"", GenerateSpecifics(kKey2, kValue2), 2, "k2"));
+      ClientTagHash(), GenerateSpecifics(kKey2, kValue2), 2, "k2"));
 
   // Create 2 entries, one is version 3, another is version 1.
   sync_pb::GarbageCollectionDirective garbage_collection_directive;
@@ -1895,7 +1902,7 @@
 
   UpdateResponseDataList updates1;
   updates1.push_back(worker()->GenerateUpdateData(
-      /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
+      ClientTagHash(), GenerateSpecifics(kKey1, kValue1), 1, "k1"));
   sync_pb::GarbageCollectionDirective garbage_collection_directive;
   garbage_collection_directive.set_version_watermark(1);
 
@@ -1914,7 +1921,7 @@
   {
     UpdateResponseDataList updates2;
     updates2.push_back(worker()->GenerateUpdateData(
-        /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
+        ClientTagHash(), GenerateSpecifics(kKey1, kValue1), 1, "k1"));
     base::HistogramTester histogram_tester;
     // Send one more update with the same data.
     worker()->UpdateWithGarbageCollection(std::move(updates2),
@@ -1935,7 +1942,7 @@
   InitializeToReadyState();
 
   ExpectError();
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1));
 }
 
 // Tests that empty updates without a version GC are processed for types that
@@ -1972,7 +1979,7 @@
   ModelReadyToSync();
   OnSyncStarting();
 
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1));
 }
 
 class WalletDataClientTagBasedModelTypeProcessorTest
@@ -1992,7 +1999,7 @@
   // Commit an item.
   UpdateResponseDataList updates;
   updates.push_back(worker()->GenerateUpdateData(
-      /*tag_hash=*/"", GenerateSpecifics(kKey1, kValue1), 1, "k1"));
+      ClientTagHash(), GenerateSpecifics(kKey1, kValue1), 1, "k1"));
   sync_pb::GarbageCollectionDirective garbage_collection_directive;
   garbage_collection_directive.set_version_watermark(1);
   worker()->UpdateWithGarbageCollection(std::move(updates),
@@ -2011,13 +2018,14 @@
 
   EntitySpecifics specifics2 = bridge()->WriteItem(kKey1, kValue2);
   UpdateResponseDataList update;
-  update.push_back(worker()->GenerateUpdateData(kHash1, specifics1, 1, "k1"));
+  update.push_back(
+      worker()->GenerateUpdateData(GetHash(kKey1), specifics1, 1, "k1"));
   worker()->UpdateWithEncryptionKey("k1", std::move(update));
 
   OnCommitDataLoaded();
 
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics2});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {specifics2});
 }
 
 // Same as above but with two commit requests before one ack.
@@ -2031,16 +2039,17 @@
   worker()->AckOnePendingCommit();
   // kValue2 is now the base value.
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics2});
+  worker()->VerifyNthPendingCommit(0, {GetHash(kKey1)}, {specifics2});
 
   UpdateResponseDataList update;
-  update.push_back(worker()->GenerateUpdateData(kHash1, specifics1, 1, "k1"));
+  update.push_back(
+      worker()->GenerateUpdateData(GetHash(kKey1), specifics1, 1, "k1"));
   worker()->UpdateWithEncryptionKey("k1", std::move(update));
 
   OnCommitDataLoaded();
 
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
-  worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics2});
+  worker()->VerifyNthPendingCommit(1, {GetHash(kKey1)}, {specifics2});
 }
 
 // Tests that UpdateStorageKey propagates storage key to ProcessorEntity
@@ -2057,11 +2066,11 @@
 
   // Initial update from server should be handled by MergeSyncData.
   UpdateResponseDataList updates;
-  updates.push_back(
-      worker()->GenerateUpdateData(kHash1, GenerateSpecifics(kKey1, kValue1)));
+  updates.push_back(worker()->GenerateUpdateData(
+      GetHash(kKey1), GenerateSpecifics(kKey1, kValue1)));
   // Create update which will be ignored by bridge.
-  updates.push_back(
-      worker()->GenerateUpdateData(kHash3, GenerateSpecifics(kKey3, kValue3)));
+  updates.push_back(worker()->GenerateUpdateData(
+      GetHash(kKey3), GenerateSpecifics(kKey3, kValue3)));
   bridge()->AddValueToIgnore(kValue3);
   worker()->UpdateFromServer(std::move(updates));
   EXPECT_EQ(1, bridge()->merge_call_count());
@@ -2082,7 +2091,7 @@
 
   // Second update from server should be handled by ApplySyncChanges. Similarly
   // It should call UpdateStorageKey, not GetStorageKey.
-  worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey2, kValue2));
+  worker()->UpdateFromServer(GetHash(kKey2), GenerateSpecifics(kKey2, kValue2));
   EXPECT_EQ(1, bridge()->apply_call_count());
   const std::string storage_key2 = bridge()->GetLastGeneratedStorageKey();
   EXPECT_NE(storage_key1, storage_key2);
@@ -2102,10 +2111,10 @@
 
   UpdateResponseDataList update;
   update.push_back(worker()->GenerateUpdateData(
-      kHash1, GenerateSpecifics(kKey1, kValue1), 1, "ek1"));
+      GetHash(kKey1), GenerateSpecifics(kKey1, kValue1), 1, "ek1"));
   worker()->UpdateWithEncryptionKey("ek2", std::move(update));
   OnCommitDataLoaded();
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
 }
 
 // Tests that UntrackEntity won't propagate storage key to
@@ -2121,7 +2130,7 @@
   OnSyncStarting();
 
   // Initial update from server should be handled by MergeSyncData.
-  worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
+  worker()->UpdateFromServer(GetHash(kKey1), GenerateSpecifics(kKey1, kValue1));
   EXPECT_EQ(1, bridge()->merge_call_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
   // Metadata should not be written under kUntrackKey1. This means that
@@ -2138,7 +2147,7 @@
 TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldUntrackEntityForStorageKey) {
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
   worker()->AckOnePendingCommit();
 
   // Check the processor tracks the entity.
@@ -2248,7 +2257,7 @@
   OnSyncStarting();
   histogram_tester.ExpectBucketCount(
       "Sync.PersistedModelTypeIdMismatch",
-      /*bucket=*/ModelTypeToHistogramInt(GetModelType()), /*count=*/1);
+      /*bucket=*/ModelTypeHistogramValue(GetModelType()), /*count=*/1);
 
   // Model should still be ready to sync.
   ASSERT_TRUE(type_processor()->IsModelReadyToSyncForTest());
@@ -2282,7 +2291,7 @@
 
     histogram_tester.ExpectBucketCount(
         "Sync.ModelTypeOrphanMetadata",
-        /*bucket=*/ModelTypeToHistogramInt(GetModelType()), /*count=*/1);
+        /*bucket=*/ModelTypeHistogramValue(GetModelType()), /*count=*/1);
   }
 
   // Orphan metadata should have been deleted.
@@ -2303,7 +2312,7 @@
   // The processor should not report orphan again in UMA.
   histogram_tester.ExpectBucketCount(
       "Sync.ModelTypeOrphanMetadata",
-      /*bucket=*/ModelTypeToHistogramInt(GetModelType()), /*count=*/0);
+      /*bucket=*/ModelTypeHistogramValue(GetModelType()), /*count=*/0);
 }
 
 TEST_F(
@@ -2510,7 +2519,7 @@
   EXPECT_TRUE(db()->model_type_state().initial_sync_done());
 
   bridge()->WriteItem(kKey1, kValue1);
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
   EXPECT_EQ(1U, db()->data_count());
   EXPECT_EQ(1U, db()->metadata_count());
 
@@ -2526,17 +2535,17 @@
   InitializeToReadyState();
 
   bridge()->WriteItem(kKey1, kValue1);
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
   EXPECT_EQ(1U, db()->data_count());
   EXPECT_EQ(1U, db()->metadata_count());
 
   bridge()->WriteItem(kKey1, kValue2);
-  worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}, {GetHash(kKey1)}});
   EXPECT_EQ(1U, db()->data_count());
   EXPECT_EQ(1U, db()->metadata_count());
 
   worker()->AckOnePendingCommit();
-  worker()->VerifyPendingCommits({{kHash1}});
+  worker()->VerifyPendingCommits({{GetHash(kKey1)}});
   EXPECT_EQ(1U, db()->data_count());
   EXPECT_EQ(1U, db()->metadata_count());
 
diff --git a/components/sync/model_impl/model_type_store_service_impl.cc b/components/sync/model_impl/model_type_store_service_impl.cc
index c0039537..504c0262 100644
--- a/components/sync/model_impl/model_type_store_service_impl.cc
+++ b/components/sync/model_impl/model_type_store_service_impl.cc
@@ -124,15 +124,4 @@
   return backend_task_runner_;
 }
 
-std::unique_ptr<BlockingModelTypeStore>
-ModelTypeStoreServiceImpl::CreateBlockingStoreFromBackendSequence(
-    ModelType type) {
-  DCHECK(backend_task_runner_->RunsTasksInCurrentSequence());
-  if (!store_backend_) {
-    return nullptr;
-  }
-  return std::make_unique<BlockingModelTypeStoreImpl>(type,
-                                                      store_backend_.get());
-}
-
 }  // namespace syncer
diff --git a/components/sync/model_impl/model_type_store_service_impl.h b/components/sync/model_impl/model_type_store_service_impl.h
index 9daf0c0..6ef2371 100644
--- a/components/sync/model_impl/model_type_store_service_impl.h
+++ b/components/sync/model_impl/model_type_store_service_impl.h
@@ -32,13 +32,8 @@
   const base::FilePath& GetSyncDataPath() const override;
   RepeatingModelTypeStoreFactory GetStoreFactory() override;
   scoped_refptr<base::SequencedTaskRunner> GetBackendTaskRunner() override;
-  std::unique_ptr<BlockingModelTypeStore>
-  CreateBlockingStoreFromBackendSequence(ModelType type) override;
 
  private:
-  void CreateModelTypeStore(ModelType type,
-                            ModelTypeStore::InitCallback callback);
-
   // The path to the base directory under which sync should store its
   // information.
   const base::FilePath sync_path_;
diff --git a/components/sync/model_impl/processor_entity.cc b/components/sync/model_impl/processor_entity.cc
index a85ff9b..dd36b8e 100644
--- a/components/sync/model_impl/processor_entity.cc
+++ b/components/sync/model_impl/processor_entity.cc
@@ -13,6 +13,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_usage_estimator.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/sync_base_switches.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/non_blocking_sync_common.h"
@@ -38,12 +39,12 @@
 
 std::unique_ptr<ProcessorEntity> ProcessorEntity::CreateNew(
     const std::string& storage_key,
-    const std::string& client_tag_hash,
+    const ClientTagHash& client_tag_hash,
     const std::string& id,
     base::Time creation_time) {
   // Initialize metadata
   sync_pb::EntityMetadata metadata;
-  metadata.set_client_tag_hash(client_tag_hash);
+  metadata.set_client_tag_hash(client_tag_hash.value());
   if (!id.empty())
     metadata.set_server_id(id);
   metadata.set_sequence_number(0);
@@ -88,7 +89,8 @@
 void ProcessorEntity::SetCommitData(std::unique_ptr<EntityData> data) {
   DCHECK(data);
   // Update data's fields from metadata.
-  data->client_tag_hash = metadata_.client_tag_hash();
+  data->client_tag_hash =
+      ClientTagHash::FromHashed(metadata_.client_tag_hash());
   if (!metadata_.server_id().empty())
     data->id = metadata_.server_id();
   data->creation_time = ProtoTimeToTime(metadata_.creation_time());
@@ -105,7 +107,7 @@
 }
 
 bool ProcessorEntity::HasCommitData() const {
-  return commit_data_ && !commit_data_->client_tag_hash.empty();
+  return commit_data_ && !commit_data_->client_tag_hash.value().empty();
 }
 
 bool ProcessorEntity::MatchesData(const EntityData& data) const {
@@ -255,14 +257,16 @@
 void ProcessorEntity::InitializeCommitRequestData(CommitRequestData* request) {
   if (!metadata_.is_deleted()) {
     DCHECK(HasCommitData());
-    DCHECK_EQ(commit_data_->client_tag_hash, metadata_.client_tag_hash());
+    DCHECK_EQ(commit_data_->client_tag_hash.value(),
+              metadata_.client_tag_hash());
     DCHECK_EQ(commit_data_->id, metadata_.server_id());
     request->entity = std::move(commit_data_);
   } else {
     // Make an EntityData with empty specifics to indicate deletion. This is
     // done lazily here to simplify loading a pending deletion on startup.
     auto data = std::make_unique<syncer::EntityData>();
-    data->client_tag_hash = metadata_.client_tag_hash();
+    data->client_tag_hash =
+        ClientTagHash::FromHashed(metadata_.client_tag_hash());
     data->id = metadata_.server_id();
     data->creation_time = ProtoTimeToTime(metadata_.creation_time());
     data->modification_time = ProtoTimeToTime(metadata_.modification_time());
@@ -279,7 +283,7 @@
 void ProcessorEntity::ReceiveCommitResponse(const CommitResponseData& data,
                                             bool commit_only,
                                             ModelType type_for_uma) {
-  DCHECK_EQ(metadata_.client_tag_hash(), data.client_tag_hash);
+  DCHECK_EQ(metadata_.client_tag_hash(), data.client_tag_hash.value());
   DCHECK_GT(data.sequence_number, metadata_.acked_sequence_number());
   // Version is not valid for commit only types, as it's stripped before being
   // sent to the server, so it cannot behave correctly.
diff --git a/components/sync/model_impl/processor_entity.h b/components/sync/model_impl/processor_entity.h
index 2951d8bd..f3d24fe 100644
--- a/components/sync/model_impl/processor_entity.h
+++ b/components/sync/model_impl/processor_entity.h
@@ -18,6 +18,8 @@
 #include "components/sync/protocol/entity_metadata.pb.h"
 
 namespace syncer {
+
+class ClientTagHash;
 struct CommitRequestData;
 struct CommitResponseData;
 struct UpdateResponseData;
@@ -31,7 +33,7 @@
   // Construct an instance representing a new locally-created item.
   static std::unique_ptr<ProcessorEntity> CreateNew(
       const std::string& storage_key,
-      const std::string& client_tag_hash,
+      const ClientTagHash& client_tag_hash,
       const std::string& id,
       base::Time creation_time);
 
@@ -45,7 +47,6 @@
   const std::string& storage_key() const { return storage_key_; }
   const sync_pb::EntityMetadata& metadata() const { return metadata_; }
   const EntityData& commit_data() { return *commit_data_; }
-  base::Time unsynced_time() const { return unsynced_time_; }
 
   // Returns true if this data is out of sync with the server.
   // A commit may or may not be in progress at this time.
diff --git a/components/sync/model_impl/processor_entity_unittest.cc b/components/sync/model_impl/processor_entity_unittest.cc
index b56f33a6..810dae10 100644
--- a/components/sync/model_impl/processor_entity_unittest.cc
+++ b/components/sync/model_impl/processor_entity_unittest.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/test/metrics/histogram_tester.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/non_blocking_sync_common.h"
@@ -20,7 +21,7 @@
 namespace {
 
 const char kKey[] = "key";
-const char kHash[] = "hash";
+const ClientTagHash kHash = ClientTagHash::FromHashed("hash");
 const char kId[] = "id";
 const char kName[] = "name";
 const char kValue1[] = "value1";
@@ -36,7 +37,7 @@
   return specifics;
 }
 
-std::unique_ptr<EntityData> GenerateEntityData(const std::string& hash,
+std::unique_ptr<EntityData> GenerateEntityData(const ClientTagHash& hash,
                                                const std::string& name,
                                                const std::string& value) {
   std::unique_ptr<EntityData> entity_data(new EntityData());
@@ -48,7 +49,7 @@
 
 std::unique_ptr<UpdateResponseData> GenerateUpdate(
     const ProcessorEntity& entity,
-    const std::string& hash,
+    const ClientTagHash& hash,
     const std::string& id,
     const std::string& name,
     const std::string& value,
@@ -65,7 +66,7 @@
 
 std::unique_ptr<UpdateResponseData> GenerateTombstone(
     const ProcessorEntity& entity,
-    const std::string& hash,
+    const ClientTagHash& hash,
     const std::string& id,
     const std::string& name,
     const base::Time& mtime,
@@ -141,7 +142,7 @@
   std::unique_ptr<ProcessorEntity> entity = CreateNew();
 
   EXPECT_EQ(kKey, entity->storage_key());
-  EXPECT_EQ(kHash, entity->metadata().client_tag_hash());
+  EXPECT_EQ(kHash.value(), entity->metadata().client_tag_hash());
   EXPECT_EQ("", entity->metadata().server_id());
   EXPECT_FALSE(entity->metadata().is_deleted());
   EXPECT_EQ(0, entity->metadata().sequence_number());
diff --git a/components/sync/model_impl/syncable_service_based_bridge.cc b/components/sync/model_impl/syncable_service_based_bridge.cc
index eb7f418a..24c1e946 100644
--- a/components/sync/model_impl/syncable_service_based_bridge.cc
+++ b/components/sync/model_impl/syncable_service_based_bridge.cc
@@ -11,8 +11,8 @@
 #include "base/bind_helpers.h"
 #include "base/location.h"
 #include "base/trace_event/memory_usage_estimator.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/data_type_histogram.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"
@@ -28,9 +28,9 @@
 constexpr int64_t kInvalidNodeId = 0;
 
 std::unique_ptr<EntityData> ConvertPersistedToEntityData(
-    const std::string& client_tag_hash,
+    const ClientTagHash& client_tag_hash,
     sync_pb::PersistedEntityData data) {
-  DCHECK(!client_tag_hash.empty());
+  DCHECK(!client_tag_hash.value().empty());
   auto entity_data = std::make_unique<EntityData>();
 
   entity_data->name = std::move(*data.mutable_name());
@@ -156,8 +156,10 @@
           SyncDataLocal sync_data(change.sync_data());
           DCHECK(sync_data.IsValid())
               << " from " << change.location().ToString();
-          const std::string storage_key =
-              GenerateSyncableHash(type_, sync_data.GetTag());
+
+          const ClientTagHash client_tag_hash =
+              ClientTagHash::FromUnhashed(type_, sync_data.GetTag());
+          const std::string storage_key = client_tag_hash.value();
           DCHECK(!storage_key.empty());
 
           (*in_memory_store_)[storage_key] = sync_data.GetSpecifics();
@@ -165,11 +167,11 @@
               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());
+          other_->Put(storage_key,
+                      ConvertPersistedToEntityData(
+                          /*client_tag_hash=*/client_tag_hash,
+                          std::move(persisted_entity)),
+                      batch->GetMetadataChangeList());
           break;
         }
 
@@ -180,10 +182,11 @@
             SyncDataLocal sync_data(change.sync_data());
             DCHECK(sync_data.IsValid())
                 << " from " << change.location().ToString();
-            storage_key = GenerateSyncableHash(type_, sync_data.GetTag());
+            storage_key =
+                ClientTagHash::FromUnhashed(type_, sync_data.GetTag()).value();
           } else {
             SyncDataRemote sync_data(change.sync_data());
-            storage_key = sync_data.GetClientTagHash();
+            storage_key = sync_data.GetClientTagHash().value();
           }
 
           DCHECK(!storage_key.empty())
@@ -601,12 +604,13 @@
         // 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,
+            change->data(),
+            /*storage_key=*/change->data().client_tag_hash.value(),
             batch->GetMetadataChangeList());
         FALLTHROUGH;
 
       case EntityChange::ACTION_UPDATE: {
-        const std::string& storage_key = change->data().client_tag_hash;
+        const std::string& storage_key = change->data().client_tag_hash.value();
         DVLOG(1) << ModelTypeToString(type_)
                  << ": Processing add/update with key: " << storage_key;
 
@@ -614,7 +618,7 @@
             FROM_HERE, ConvertToSyncChangeType(change->type()),
             SyncData::CreateRemoteData(
                 /*id=*/kInvalidNodeId, change->data().specifics,
-                change->data().client_tag_hash));
+                change->data().client_tag_hash.value()));
 
         batch->WriteData(
             storage_key,
@@ -662,9 +666,9 @@
     }
 
     // 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)));
+    batch->Put(record.id, ConvertPersistedToEntityData(
+                              ClientTagHash::FromHashed(record.id),
+                              std::move(persisted_entity)));
   }
   std::move(callback).Run(std::move(batch));
 }
diff --git a/components/sync/model_impl/syncable_service_based_bridge_unittest.cc b/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
index 8f8b087..353d86f 100644
--- a/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
+++ b/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
@@ -12,7 +12,7 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.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"
@@ -156,8 +156,8 @@
   }
 
   const std::string kClientTag = "clienttag";
-  const std::string kClientTagHash =
-      GenerateSyncableHash(kModelType, kClientTag);
+  const ClientTagHash kClientTagHash =
+      ClientTagHash::FromUnhashed(kModelType, kClientTag);
 
   base::test::SingleThreadTaskEnvironment task_environment_;
   testing::NiceMock<MockSyncableService> syncable_service_;
@@ -207,7 +207,7 @@
                   kModelType, ElementsAre(SyncDataRemoteMatches("name1")),
                   NotNull(), NotNull()));
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
-  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash.value(), _)));
 }
 
 TEST_F(SyncableServiceBasedBridgeTest, ShouldWaitUntilModelReadyToSync) {
@@ -309,7 +309,7 @@
   // SyncableService being stopped.
   EXPECT_CALL(syncable_service_, StopSyncing(_)).Times(0);
   real_processor_->OnSyncStopping(KEEP_METADATA);
-  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash.value(), _)));
 
   // Since the SyncableService wasn't stopped, it shouldn't get restarted either
   // when Sync starts up again.
@@ -375,9 +375,10 @@
   InitializeBridge();
   StartSyncing();
 
-  EXPECT_CALL(mock_processor_, Put(kClientTagHash, NotNull(), NotNull()));
+  EXPECT_CALL(mock_processor_,
+              Put(kClientTagHash.value(), NotNull(), NotNull()));
   worker_->UpdateFromServer();
-  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash.value(), _)));
 }
 
 TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateLocalCreation) {
@@ -387,7 +388,8 @@
   ASSERT_THAT(start_syncing_sync_processor_, NotNull());
   ASSERT_THAT(GetAllData(), IsEmpty());
 
-  EXPECT_CALL(mock_processor_, Put(kClientTagHash, NotNull(), NotNull()));
+  EXPECT_CALL(mock_processor_,
+              Put(kClientTagHash.value(), NotNull(), NotNull()));
 
   SyncChangeList change_list;
   change_list.emplace_back(
@@ -396,7 +398,7 @@
   const SyncError error =
       start_syncing_sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
   EXPECT_FALSE(error.IsSet());
-  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash, _)));
+  EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash.value(), _)));
 }
 
 TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateLocalUpdate) {
@@ -405,10 +407,10 @@
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
   ASSERT_THAT(start_syncing_sync_processor_, NotNull());
   ASSERT_THAT(GetAllData(),
-              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+              ElementsAre(Pair(kClientTagHash.value(), HasName("name1"))));
 
-  EXPECT_CALL(mock_processor_, Put(GenerateSyncableHash(kModelType, kClientTag),
-                                   NotNull(), NotNull()));
+  EXPECT_CALL(mock_processor_,
+              Put(kClientTagHash.value(), NotNull(), NotNull()));
 
   SyncChangeList change_list;
   change_list.emplace_back(FROM_HERE, SyncChange::ACTION_UPDATE,
@@ -418,7 +420,7 @@
       start_syncing_sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
   EXPECT_FALSE(error.IsSet());
   EXPECT_THAT(GetAllData(),
-              ElementsAre(Pair(kClientTagHash, HasName("name2"))));
+              ElementsAre(Pair(kClientTagHash.value(), HasName("name2"))));
 }
 
 TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateLocalDeletion) {
@@ -427,10 +429,9 @@
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
   ASSERT_THAT(start_syncing_sync_processor_, NotNull());
   ASSERT_THAT(GetAllData(),
-              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+              ElementsAre(Pair(kClientTagHash.value(), HasName("name1"))));
 
-  EXPECT_CALL(mock_processor_,
-              Delete(GenerateSyncableHash(kModelType, kClientTag), NotNull()));
+  EXPECT_CALL(mock_processor_, Delete(kClientTagHash.value(), NotNull()));
 
   SyncChangeList change_list;
   change_list.emplace_back(FROM_HERE, SyncChange::ACTION_DELETE,
@@ -480,7 +481,7 @@
                                         SyncChange::ACTION_ADD, "name1"))));
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
   EXPECT_THAT(GetAllData(),
-              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+              ElementsAre(Pair(kClientTagHash.value(), HasName("name1"))));
 }
 
 TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateRemoteUpdates) {
@@ -489,14 +490,14 @@
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
   ASSERT_THAT(start_syncing_sync_processor_, NotNull());
   ASSERT_THAT(GetAllData(),
-              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+              ElementsAre(Pair(kClientTagHash.value(), 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"))));
+              ElementsAre(Pair(kClientTagHash.value(), HasName("name2"))));
 
   // A second update for the same entity.
   EXPECT_CALL(syncable_service_,
@@ -504,7 +505,7 @@
                                         SyncChange::ACTION_UPDATE, "name3"))));
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name3"));
   EXPECT_THAT(GetAllData(),
-              ElementsAre(Pair(kClientTagHash, HasName("name3"))));
+              ElementsAre(Pair(kClientTagHash.value(), HasName("name3"))));
 }
 
 TEST_F(SyncableServiceBasedBridgeTest, ShouldPropagateRemoteDeletion) {
@@ -513,7 +514,7 @@
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
   ASSERT_THAT(start_syncing_sync_processor_, NotNull());
   ASSERT_THAT(GetAllData(),
-              ElementsAre(Pair(kClientTagHash, HasName("name1"))));
+              ElementsAre(Pair(kClientTagHash.value(), HasName("name1"))));
 
   EXPECT_CALL(syncable_service_,
               ProcessSyncChanges(_, ElementsAre(SyncChangeMatches(
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index 846c3be..d817d119 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -5,6 +5,7 @@
 #include "components/sync/nigori/nigori_model_type_processor.h"
 
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/data_type_histogram.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/commit_queue.h"
@@ -21,7 +22,7 @@
 // TODO(mamir): remove those and adjust the code accordingly. Similarly in
 // tests.
 const char kNigoriStorageKey[] = "NigoriStorageKey";
-const char kNigoriClientTagHash[] = "NigoriClientTagHash";
+const char kRawNigoriClientTagHash[] = "NigoriClientTagHash";
 
 }  // namespace
 
@@ -131,8 +132,8 @@
     } else {
       DCHECK(!updates[0]->entity->is_deleted());
       entity_ = ProcessorEntity::CreateNew(
-          kNigoriStorageKey, kNigoriClientTagHash, updates[0]->entity->id,
-          updates[0]->entity->creation_time);
+          kNigoriStorageKey, ClientTagHash::FromHashed(kRawNigoriClientTagHash),
+          updates[0]->entity->id, updates[0]->entity->creation_time);
       entity_->RecordAcceptedUpdate(*updates[0]);
       error = bridge_->MergeSyncData(std::move(*updates[0]->entity));
     }
@@ -300,7 +301,7 @@
     model_type_state_ = std::move(nigori_metadata.model_type_state);
     sync_pb::EntityMetadata metadata =
         std::move(*nigori_metadata.entity_metadata);
-    metadata.set_client_tag_hash(kNigoriClientTagHash);
+    metadata.set_client_tag_hash(kRawNigoriClientTagHash);
     entity_ = ProcessorEntity::CreateFromMetadata(kNigoriStorageKey,
                                                   std::move(metadata));
   } else {
diff --git a/components/sync/nigori/nigori_model_type_processor_unittest.cc b/components/sync/nigori/nigori_model_type_processor_unittest.cc
index 27ba362..46a0995 100644
--- a/components/sync/nigori/nigori_model_type_processor_unittest.cc
+++ b/components/sync/nigori/nigori_model_type_processor_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/test/mock_callback.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/commit_queue.h"
 #include "components/sync/nigori/nigori_sync_bridge.h"
@@ -26,7 +27,7 @@
 using testing::NotNull;
 
 // TODO(mamir): remove those and adjust the code accordingly.
-const char kNigoriClientTagHash[] = "NigoriClientTagHash";
+const char kRawNigoriClientTagHash[] = "NigoriClientTagHash";
 
 const char kNigoriNonUniqueName[] = "nigori";
 const char kNigoriServerId[] = "nigori_server_id";
@@ -75,7 +76,8 @@
     int response_version) {
   CommitResponseData commit_response_data;
   commit_response_data.id = kNigoriServerId;
-  commit_response_data.client_tag_hash = kNigoriClientTagHash;
+  commit_response_data.client_tag_hash =
+      ClientTagHash::FromHashed(kRawNigoriClientTagHash);
   commit_response_data.sequence_number = commit_request_data.sequence_number;
   commit_response_data.response_version = response_version;
   commit_response_data.specifics_hash = commit_request_data.specifics_hash;
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
index d55b2343..9e11940 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -395,7 +395,7 @@
 void UpdateNigoriSpecificsFromEncryptedTypes(
     ModelTypeSet encrypted_types,
     sync_pb::NigoriSpecifics* specifics) {
-  static_assert(46 == ModelType::NUM_ENTRIES,
+  static_assert(39 == ModelType::NUM_ENTRIES,
                 "If adding an encryptable type, update handling below.");
   specifics->set_encrypt_bookmarks(encrypted_types.Has(BOOKMARKS));
   specifics->set_encrypt_preferences(encrypted_types.Has(PREFERENCES));
@@ -413,13 +413,10 @@
   specifics->set_encrypt_app_settings(encrypted_types.Has(APP_SETTINGS));
   specifics->set_encrypt_extension_settings(
       encrypted_types.Has(EXTENSION_SETTINGS));
-  specifics->set_encrypt_app_notifications(
-      encrypted_types.Has(DEPRECATED_APP_NOTIFICATIONS));
   specifics->set_encrypt_dictionary(encrypted_types.Has(DICTIONARY));
   specifics->set_encrypt_favicon_images(encrypted_types.Has(FAVICON_IMAGES));
   specifics->set_encrypt_favicon_tracking(
       encrypted_types.Has(FAVICON_TRACKING));
-  specifics->set_encrypt_articles(encrypted_types.Has(DEPRECATED_ARTICLES));
   specifics->set_encrypt_app_list(encrypted_types.Has(APP_LIST));
   specifics->set_encrypt_arc_package(encrypted_types.Has(ARC_PACKAGE));
   specifics->set_encrypt_printers(encrypted_types.Has(PRINTERS));
diff --git a/components/sync/protocol/app_notification_specifics.proto b/components/sync/protocol/app_notification_specifics.proto
deleted file mode 100644
index a3280d11..0000000
--- a/components/sync/protocol/app_notification_specifics.proto
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Sync protocol datatype extension for app notifications.
-
-// If you change or add any fields in this file, update proto_visitors.h and
-// potentially proto_enum_conversions.{h, cc}.
-
-syntax = "proto2";
-
-option java_multiple_files = true;
-option java_package = "org.chromium.components.sync.protocol";
-
-option optimize_for = LITE_RUNTIME;
-
-package sync_pb;
-
-// Properties of an app notification.
-
-// An App Notification, to be delivered from Chrome Apps to the
-// Chrome browser through the Notification API.
-message AppNotification {
-  // Globally unique id. This is more robust for uniquely identifying each
-  // notification and hence gives us flexibility in the future. In absence
-  // of this, unique id would be (app_id, creation_timestamp_ms). But that
-  // relies on creation_timestamp_ms being high resolution and is not
-  // globally unique - only unique for a given user.
-  optional string guid = 1;
-  // Metadata, not shown directly to the user.
-  // The unique App Id, as created by the webstore and used to
-  // delegate messages to the applications. This is defined as 32 characters
-  optional string app_id = 2;
-  // Timestamp when the message was created in milliseconds.
-  // This is seperate from ctime as this is only set by the application.
-  optional int64 creation_timestamp_ms = 3;
-
-  // Payload - these fields are visible to the user content is defined by the
-  // app. The fields are described in:
-  // chrome/browser/extensions/app_notification.h
-  optional string title = 4;
-  optional string body_text = 5;
-  optional string link_url = 6;
-  optional string link_text = 7;
-}
diff --git a/components/sync/protocol/managed_user_shared_setting_specifics.proto b/components/sync/protocol/managed_user_shared_setting_specifics.proto
deleted file mode 100644
index 82512ce..0000000
--- a/components/sync/protocol/managed_user_shared_setting_specifics.proto
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Sync protocol datatype extension for managed user shared settings.
-
-// If you change or add any fields in this file, update proto_visitors.h and
-// potentially proto_enum_conversions.{h, cc}.
-
-syntax = "proto2";
-
-option java_multiple_files = true;
-option java_package = "org.chromium.components.sync.protocol";
-
-option optimize_for = LITE_RUNTIME;
-
-package sync_pb;
-
-// Properties of managed user shared setting sync objects.
-message ManagedUserSharedSettingSpecifics {
-  // The MU ID for the managed user to whom the setting applies.
-  optional string mu_id = 1;
-  // The key of the setting.
-  optional string key = 2;
-  // The setting value. The setting is a JSON encoding of an arbitrary
-  // Javascript value.
-  optional string value = 3;
-  // This flag is set by the server to acknowledge that it has committed a
-  // change to a setting.
-  optional bool acknowledged = 4 [default = false];
-}
diff --git a/components/sync/protocol/managed_user_specifics.proto b/components/sync/protocol/managed_user_specifics.proto
deleted file mode 100644
index e36e8c66..0000000
--- a/components/sync/protocol/managed_user_specifics.proto
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Sync protocol datatype extension for managed user settings.
-
-// If you change or add any fields in this file, update proto_visitors.h and
-// potentially proto_enum_conversions.{h, cc}.
-
-syntax = "proto2";
-
-option java_multiple_files = true;
-option java_package = "org.chromium.components.sync.protocol";
-
-option optimize_for = LITE_RUNTIME;
-
-package sync_pb;
-
-// Properties of managed user sync objects.
-message ManagedUserSpecifics {
-  // A randomly-generated identifier for the managed user.
-  optional string id = 1;
-  // The human-visible name of the managed user
-  optional string name = 2;
-  // This flag is set by the server to acknowledge that it has committed a
-  // newly created managed user.
-  optional bool acknowledged = 3 [default = false];
-  // Master key for managed user cryptohome.
-  optional string master_key = 4;
-  // A string representing the index of the supervised user avatar on Chrome.
-  // It has the following format:
-  // "chrome-avatar-index:INDEX" where INDEX is an integer.
-  optional string chrome_avatar = 5;
-  // A string representing the index of the supervised user avatar on Chrome OS.
-  // It has the following format:
-  // "chromeos-avatar-index:INDEX" where INDEX is an integer.
-  optional string chromeos_avatar = 6;
-  // Key for signing supervised user's password.
-  optional string password_signature_key = 7;
-  // Key for encrypting supervised user's password.
-  optional string password_encryption_key = 8;
-}
diff --git a/components/sync/protocol/nigori_specifics.proto b/components/sync/protocol/nigori_specifics.proto
index e1e9085..5e294426 100644
--- a/components/sync/protocol/nigori_specifics.proto
+++ b/components/sync/protocol/nigori_specifics.proto
@@ -75,7 +75,10 @@
   optional bool encrypt_everything = 24;
 
   optional bool encrypt_extension_settings = 25;
-  optional bool encrypt_app_notifications = 26 [deprecated = true];
+
+  reserved 26;
+  reserved "encrypt_app_notifications";
+
   optional bool encrypt_app_settings = 27;
 
   // User device information. Contains information about each device that has a
@@ -132,8 +135,8 @@
   optional bool encrypt_favicon_images = 35;
   optional bool encrypt_favicon_tracking = 36;
 
-  // Boolean corresponding to whether articles should be encrypted.
-  optional bool encrypt_articles = 37;
+  reserved 37;
+  reserved "encrypt_articles";
 
   // Boolean corresponding to whether app list items should be encrypted.
   optional bool encrypt_app_list = 38;
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index f82837ca..2235a55 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -633,20 +633,6 @@
   return "";
 }
 
-const char* ProtoEnumToString(
-    sync_pb::WifiCredentialSpecifics::SecurityClass security_class) {
-  ASSERT_ENUM_BOUNDS(sync_pb::WifiCredentialSpecifics, SecurityClass,
-                     SECURITY_CLASS_INVALID, SECURITY_CLASS_PSK);
-  switch (security_class) {
-    ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_INVALID);
-    ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_NONE);
-    ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_WEP);
-    ENUM_CASE(sync_pb::WifiCredentialSpecifics, SECURITY_CLASS_PSK);
-  }
-  NOTREACHED();
-  return "";
-}
-
 #undef ASSERT_ENUM_BOUNDS
 #undef ENUM_CASE
 
diff --git a/components/sync/protocol/proto_enum_conversions.h b/components/sync/protocol/proto_enum_conversions.h
index 7ada191..a67e899 100644
--- a/components/sync/protocol/proto_enum_conversions.h
+++ b/components/sync/protocol/proto_enum_conversions.h
@@ -131,9 +131,6 @@
     sync_pb::WifiConfigurationSpecificsData::ProxyConfiguration::ProxyOption
         proxy_option);
 
-const char* ProtoEnumToString(
-    sync_pb::WifiCredentialSpecifics::SecurityClass security_class);
-
 }  // namespace syncer
 
 #endif  // COMPONENTS_SYNC_PROTOCOL_PROTO_ENUM_CONVERSIONS_H_
diff --git a/components/sync/protocol/proto_enum_conversions_unittest.cc b/components/sync/protocol/proto_enum_conversions_unittest.cc
index 127a084..f38ef68c 100644
--- a/components/sync/protocol/proto_enum_conversions_unittest.cc
+++ b/components/sync/protocol/proto_enum_conversions_unittest.cc
@@ -74,11 +74,6 @@
                              ProxyConfiguration::ProxyOption_MAX);
 }
 
-TEST_F(ProtoEnumConversionsTest, GetWifiCredentialSecurityClassString) {
-  TestEnumStringFunction(sync_pb::WifiCredentialSpecifics::SecurityClass_MIN,
-                         sync_pb::WifiCredentialSpecifics::SecurityClass_MAX);
-}
-
 TEST_F(ProtoEnumConversionsTest, GetUpdatesSourceString) {
   TestEnumStringFunction(sync_pb::GetUpdatesCallerInfo::GetUpdatesSource_MIN,
                          sync_pb::GetUpdatesCallerInfo::PERIODIC);
diff --git a/components/sync/protocol/proto_value_conversions.cc b/components/sync/protocol/proto_value_conversions.cc
index e0d1bb5..0e70dd51 100644
--- a/components/sync/protocol/proto_value_conversions.cc
+++ b/components/sync/protocol/proto_value_conversions.cc
@@ -300,12 +300,10 @@
   }
 
 IMPLEMENT_PROTO_TO_VALUE(AppListSpecifics)
-IMPLEMENT_PROTO_TO_VALUE(AppNotification)
 IMPLEMENT_PROTO_TO_VALUE(AppNotificationSettings)
 IMPLEMENT_PROTO_TO_VALUE(AppSettingSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(AppSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(ArcPackageSpecifics)
-IMPLEMENT_PROTO_TO_VALUE(ArticleSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(AutofillProfileSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(AutofillSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(AutofillWalletSpecifics)
@@ -328,8 +326,6 @@
 IMPLEMENT_PROTO_TO_VALUE(HistoryDeleteDirectiveSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(LinkedAppIconInfo)
 IMPLEMENT_PROTO_TO_VALUE(ManagedUserSettingSpecifics)
-IMPLEMENT_PROTO_TO_VALUE(ManagedUserSharedSettingSpecifics)
-IMPLEMENT_PROTO_TO_VALUE(ManagedUserSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(ManagedUserWhitelistSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(MountainShareSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(NavigationRedirect)
@@ -350,8 +346,6 @@
 IMPLEMENT_PROTO_TO_VALUE(SessionTab)
 IMPLEMENT_PROTO_TO_VALUE(SessionWindow)
 IMPLEMENT_PROTO_TO_VALUE(SyncCycleCompletedEventInfo)
-IMPLEMENT_PROTO_TO_VALUE(SyncedNotificationAppInfoSpecifics)
-IMPLEMENT_PROTO_TO_VALUE(SyncedNotificationSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(TabNavigation)
 IMPLEMENT_PROTO_TO_VALUE(ThemeSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(TimeRangeDirective)
@@ -364,7 +358,6 @@
 IMPLEMENT_PROTO_TO_VALUE(WalletPostalAddress)
 IMPLEMENT_PROTO_TO_VALUE(WebAppSpecifics)
 IMPLEMENT_PROTO_TO_VALUE(WifiConfigurationSpecifics)
-IMPLEMENT_PROTO_TO_VALUE(WifiCredentialSpecifics)
 
 IMPLEMENT_PROTO_TO_VALUE_INCLUDE_SPECIFICS(ClientToServerMessage)
 IMPLEMENT_PROTO_TO_VALUE_INCLUDE_SPECIFICS(ClientToServerResponse)
diff --git a/components/sync/protocol/proto_value_conversions.h b/components/sync/protocol/proto_value_conversions.h
index c8ef6867..1b0b276 100644
--- a/components/sync/protocol/proto_value_conversions.h
+++ b/components/sync/protocol/proto_value_conversions.h
@@ -13,12 +13,10 @@
 
 namespace sync_pb {
 class AppListSpecifics;
-class AppNotification;
 class AppNotificationSettings;
 class AppSettingSpecifics;
 class AppSpecifics;
 class ArcPackageSpecifics;
-class ArticleSpecifics;
 class AutofillProfileSpecifics;
 class AutofillSpecifics;
 class AutofillWalletSpecifics;
@@ -39,12 +37,9 @@
 class ExtensionSpecifics;
 class FaviconImageSpecifics;
 class FaviconTrackingSpecifics;
-class GlobalIdDirective;
 class HistoryDeleteDirectiveSpecifics;
 class LinkedAppIconInfo;
 class ManagedUserSettingSpecifics;
-class ManagedUserSharedSettingSpecifics;
-class ManagedUserSpecifics;
 class ManagedUserWhitelistSpecifics;
 class MountainShareSpecifics;
 class NavigationRedirect;
@@ -66,8 +61,6 @@
 class SessionWindow;
 class SyncCycleCompletedEventInfo;
 class SyncEntity;
-class SyncedNotificationAppInfoSpecifics;
-class SyncedNotificationSpecifics;
 class TabNavigation;
 class ThemeSpecifics;
 class TimeRangeDirective;
@@ -80,7 +73,6 @@
 class WalletPostalAddress;
 class WebAppSpecifics;
 class WifiConfigurationSpecifics;
-class WifiCredentialSpecifics;
 }  // namespace sync_pb
 
 // Keep this file in sync with the .proto files in this directory.
@@ -95,9 +87,6 @@
 std::unique_ptr<base::DictionaryValue> AppListSpecificsToValue(
     const sync_pb::AppListSpecifics& proto);
 
-std::unique_ptr<base::DictionaryValue> AppNotificationToValue(
-    const sync_pb::AppNotification& app_notification_specifics);
-
 std::unique_ptr<base::DictionaryValue> AppNotificationSettingsToValue(
     const sync_pb::AppNotificationSettings& app_notification_settings);
 
@@ -110,9 +99,6 @@
 std::unique_ptr<base::DictionaryValue> ArcPackageSpecificsToValue(
     const sync_pb::ArcPackageSpecifics& proto);
 
-std::unique_ptr<base::DictionaryValue> ArticleSpecificsToValue(
-    const sync_pb::ArticleSpecifics& article_specifics);
-
 std::unique_ptr<base::DictionaryValue> AutofillProfileSpecificsToValue(
     const sync_pb::AutofillProfileSpecifics& autofill_profile_specifics);
 
@@ -167,9 +153,6 @@
 std::unique_ptr<base::DictionaryValue> FaviconTrackingSpecificsToValue(
     const sync_pb::FaviconTrackingSpecifics& favicon_tracking_specifics);
 
-std::unique_ptr<base::DictionaryValue> GlobalIdDirectiveToValue(
-    const sync_pb::GlobalIdDirective& global_id_directive);
-
 std::unique_ptr<base::DictionaryValue> HistoryDeleteDirectiveSpecificsToValue(
     const sync_pb::HistoryDeleteDirectiveSpecifics&
         history_delete_directive_specifics);
@@ -180,13 +163,6 @@
 std::unique_ptr<base::DictionaryValue> ManagedUserSettingSpecificsToValue(
     const sync_pb::ManagedUserSettingSpecifics& managed_user_setting_specifics);
 
-std::unique_ptr<base::DictionaryValue> ManagedUserSharedSettingSpecificsToValue(
-    const sync_pb::ManagedUserSharedSettingSpecifics&
-        managed_user_shared_setting_specifics);
-
-std::unique_ptr<base::DictionaryValue> ManagedUserSpecificsToValue(
-    const sync_pb::ManagedUserSpecifics& managed_user_specifics);
-
 std::unique_ptr<base::DictionaryValue> ManagedUserWhitelistSpecificsToValue(
     const sync_pb::ManagedUserWhitelistSpecifics&
         managed_user_whitelist_specifics);
@@ -248,14 +224,6 @@
 std::unique_ptr<base::DictionaryValue> SyncCycleCompletedEventInfoToValue(
     const sync_pb::SyncCycleCompletedEventInfo& proto);
 
-std::unique_ptr<base::DictionaryValue>
-SyncedNotificationAppInfoSpecificsToValue(
-    const sync_pb::SyncedNotificationAppInfoSpecifics&
-        synced_notification_specifics);
-
-std::unique_ptr<base::DictionaryValue> SyncedNotificationSpecificsToValue(
-    const sync_pb::SyncedNotificationSpecifics& synced_notification_specifics);
-
 std::unique_ptr<base::DictionaryValue> TabNavigationToValue(
     const sync_pb::TabNavigation& tab_navigation);
 
@@ -292,9 +260,6 @@
 std::unique_ptr<base::DictionaryValue> WifiConfigurationSpecificsToValue(
     const sync_pb::WifiConfigurationSpecifics& wifi_configuration_specifics);
 
-std::unique_ptr<base::DictionaryValue> WifiCredentialSpecificsToValue(
-    const sync_pb::WifiCredentialSpecifics& wifi_credential_specifics);
-
 // ToValue functions that allow omitting specifics.
 
 std::unique_ptr<base::DictionaryValue> ClientToServerMessageToValue(
diff --git a/components/sync/protocol/proto_value_conversions_unittest.cc b/components/sync/protocol/proto_value_conversions_unittest.cc
index 72b3320..5ba9ca804 100644
--- a/components/sync/protocol/proto_value_conversions_unittest.cc
+++ b/components/sync/protocol/proto_value_conversions_unittest.cc
@@ -11,7 +11,6 @@
 #include "base/values.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/base/unique_position.h"
-#include "components/sync/protocol/app_notification_specifics.pb.h"
 #include "components/sync/protocol/app_setting_specifics.pb.h"
 #include "components/sync/protocol/app_specifics.pb.h"
 #include "components/sync/protocol/autofill_specifics.pb.h"
@@ -24,8 +23,6 @@
 #include "components/sync/protocol/favicon_image_specifics.pb.h"
 #include "components/sync/protocol/favicon_tracking_specifics.pb.h"
 #include "components/sync/protocol/managed_user_setting_specifics.pb.h"
-#include "components/sync/protocol/managed_user_shared_setting_specifics.pb.h"
-#include "components/sync/protocol/managed_user_specifics.pb.h"
 #include "components/sync/protocol/managed_user_whitelist_specifics.pb.h"
 #include "components/sync/protocol/nigori_specifics.pb.h"
 #include "components/sync/protocol/password_specifics.pb.h"
@@ -36,7 +33,6 @@
 #include "components/sync/protocol/sync.pb.h"
 #include "components/sync/protocol/theme_specifics.pb.h"
 #include "components/sync/protocol/typed_url_specifics.pb.h"
-#include "components/sync/protocol/wifi_credential_specifics.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
@@ -60,17 +56,15 @@
 
 DEFINE_SPECIFICS_TO_VALUE_TEST(encrypted)
 
-static_assert(46 == syncer::ModelType::NUM_ENTRIES,
+static_assert(39 == syncer::ModelType::NUM_ENTRIES,
               "When adding a new field, add a DEFINE_SPECIFICS_TO_VALUE_TEST "
               "for your field below, and optionally a test for the specific "
               "conversions.");
 
 DEFINE_SPECIFICS_TO_VALUE_TEST(app)
 DEFINE_SPECIFICS_TO_VALUE_TEST(app_list)
-DEFINE_SPECIFICS_TO_VALUE_TEST(app_notification)
 DEFINE_SPECIFICS_TO_VALUE_TEST(app_setting)
 DEFINE_SPECIFICS_TO_VALUE_TEST(arc_package)
-DEFINE_SPECIFICS_TO_VALUE_TEST(article)
 DEFINE_SPECIFICS_TO_VALUE_TEST(autofill)
 DEFINE_SPECIFICS_TO_VALUE_TEST(autofill_profile)
 DEFINE_SPECIFICS_TO_VALUE_TEST(autofill_wallet)
@@ -83,9 +77,7 @@
 DEFINE_SPECIFICS_TO_VALUE_TEST(favicon_image)
 DEFINE_SPECIFICS_TO_VALUE_TEST(favicon_tracking)
 DEFINE_SPECIFICS_TO_VALUE_TEST(history_delete_directive)
-DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user)
 DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_setting)
-DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_shared_setting)
 DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_whitelist)
 DEFINE_SPECIFICS_TO_VALUE_TEST(mountain_share)
 DEFINE_SPECIFICS_TO_VALUE_TEST(nigori)
@@ -98,8 +90,6 @@
 DEFINE_SPECIFICS_TO_VALUE_TEST(security_event)
 DEFINE_SPECIFICS_TO_VALUE_TEST(send_tab_to_self)
 DEFINE_SPECIFICS_TO_VALUE_TEST(session)
-DEFINE_SPECIFICS_TO_VALUE_TEST(synced_notification)
-DEFINE_SPECIFICS_TO_VALUE_TEST(synced_notification_app_info)
 DEFINE_SPECIFICS_TO_VALUE_TEST(theme)
 DEFINE_SPECIFICS_TO_VALUE_TEST(typed_url)
 DEFINE_SPECIFICS_TO_VALUE_TEST(user_consent)
@@ -107,7 +97,6 @@
 DEFINE_SPECIFICS_TO_VALUE_TEST(wallet_metadata)
 DEFINE_SPECIFICS_TO_VALUE_TEST(web_app)
 DEFINE_SPECIFICS_TO_VALUE_TEST(wifi_configuration)
-DEFINE_SPECIFICS_TO_VALUE_TEST(wifi_credential)
 
 TEST(ProtoValueConversionsTest, PasswordSpecifics) {
   sync_pb::PasswordSpecifics specifics;
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index b83e9de9..6ee25b2 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -7,7 +7,6 @@
 
 #include "components/sync/base/model_type.h"
 #include "components/sync/protocol/app_list_specifics.pb.h"
-#include "components/sync/protocol/app_notification_specifics.pb.h"
 #include "components/sync/protocol/app_setting_specifics.pb.h"
 #include "components/sync/protocol/app_specifics.pb.h"
 #include "components/sync/protocol/arc_package_specifics.pb.h"
@@ -101,16 +100,6 @@
   VISIT(item_pin_ordinal);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::AppNotification& proto) {
-  VISIT(guid);
-  VISIT(app_id);
-  VISIT(creation_timestamp_ms);
-  VISIT(title);
-  VISIT(body_text);
-  VISIT(link_url);
-  VISIT(link_text);
-}
-
 VISIT_PROTO_FIELDS(const sync_pb::AppNotificationSettings& proto) {
   VISIT(initial_setup_done);
   VISIT(disabled);
@@ -142,16 +131,6 @@
   VISIT(last_backup_time);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::ArticlePage& proto) {
-  VISIT(url);
-}
-
-VISIT_PROTO_FIELDS(const sync_pb::ArticleSpecifics& proto) {
-  VISIT(entry_id);
-  VISIT(title);
-  VISIT_REP(pages);
-}
-
 VISIT_PROTO_FIELDS(const sync_pb::AutofillCullingFlags& proto) {
   VISIT(enabled);
 }
@@ -383,16 +362,14 @@
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::EntitySpecifics& proto) {
-  static_assert(46 == ModelType::NUM_ENTRIES,
+  static_assert(39 == ModelType::NUM_ENTRIES,
                 "When adding a new protocol type, you will likely need to add "
                 "it here as well.");
   VISIT(encrypted);
   VISIT(app);
   VISIT(app_list);
-  VISIT(app_notification);
   VISIT(app_setting);
   VISIT(arc_package);
-  VISIT(article);
   VISIT(autofill);
   VISIT(autofill_profile);
   VISIT(autofill_wallet);
@@ -405,9 +382,7 @@
   VISIT(favicon_image);
   VISIT(favicon_tracking);
   VISIT(history_delete_directive);
-  VISIT(managed_user);
   VISIT(managed_user_setting);
-  VISIT(managed_user_shared_setting);
   VISIT(managed_user_whitelist);
   VISIT(mountain_share);
   VISIT(nigori);
@@ -420,8 +395,6 @@
   VISIT(security_event);
   VISIT(send_tab_to_self);
   VISIT(session);
-  VISIT(synced_notification);
-  VISIT(synced_notification_app_info);
   VISIT(theme);
   VISIT(typed_url);
   VISIT(user_consent);
@@ -429,7 +402,6 @@
   VISIT(wallet_metadata);
   VISIT(web_app);
   VISIT(wifi_configuration);
-  VISIT(wifi_credential);
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::ExperimentsSpecifics& proto) {
@@ -576,24 +548,6 @@
   VISIT(value);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::ManagedUserSharedSettingSpecifics& proto) {
-  VISIT(mu_id);
-  VISIT(key);
-  VISIT(value);
-  VISIT(acknowledged);
-}
-
-VISIT_PROTO_FIELDS(const sync_pb::ManagedUserSpecifics& proto) {
-  VISIT(id);
-  VISIT(name);
-  VISIT(acknowledged);
-  VISIT(master_key);
-  VISIT(chrome_avatar);
-  VISIT(chromeos_avatar);
-  VISIT(password_signature_key);
-  VISIT(password_encryption_key);
-}
-
 VISIT_PROTO_FIELDS(const sync_pb::ManagedUserWhitelistSpecifics& proto) {
   VISIT(id);
   VISIT(name);
@@ -675,7 +629,6 @@
   VISIT(encrypt_apps);
   VISIT(encrypt_search_engines);
   VISIT(encrypt_dictionary);
-  VISIT(encrypt_articles);
   VISIT(encrypt_app_list);
   VISIT(encrypt_arc_package);
   VISIT(encrypt_reading_list);
@@ -864,10 +817,6 @@
   VISIT_BYTES(ordinal_in_parent);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::SyncedNotificationAppInfoSpecifics& proto) {}
-
-VISIT_PROTO_FIELDS(const sync_pb::SyncedNotificationSpecifics& proto) {}
-
 VISIT_PROTO_FIELDS(const sync_pb::SecurityEventSpecifics& proto) {
   VISIT(gaia_password_reuse_event);
   VISIT(event_time_usec);
@@ -1137,12 +1086,6 @@
   VISIT(theme_color);
 }
 
-VISIT_PROTO_FIELDS(const sync_pb::WifiCredentialSpecifics& proto) {
-  VISIT_BYTES(ssid);
-  VISIT_ENUM(security_class);
-  VISIT_BYTES(passphrase);
-}
-
 VISIT_PROTO_FIELDS(const sync_pb::WifiConfigurationSpecifics& proto) {
   VISIT(encrypted);
   VISIT(client_only_encrypted_data);
diff --git a/components/sync/protocol/protocol_sources.gni b/components/sync/protocol/protocol_sources.gni
index 3f194ee..9735d55 100644
--- a/components/sync/protocol/protocol_sources.gni
+++ b/components/sync/protocol/protocol_sources.gni
@@ -3,7 +3,6 @@
 # found in the LICENSE file.
 
 sync_protocol_bases = [
-  "app_notification_specifics",
   "app_setting_specifics",
   "app_specifics",
   "app_list_specifics",
@@ -30,8 +29,6 @@
   "history_status",
   "loopback_server",
   "managed_user_setting_specifics",
-  "managed_user_shared_setting_specifics",
-  "managed_user_specifics",
   "managed_user_whitelist_specifics",
   "model_type_state",
   "model_type_store_schema_descriptor",
@@ -50,8 +47,6 @@
   "session_specifics",
   "sync",
   "sync_enums",
-  "synced_notification_app_info_specifics",
-  "synced_notification_specifics",
   "test",
   "theme_specifics",
   "typed_url_specifics",
@@ -61,7 +56,6 @@
   "user_event_specifics",
   "web_app_specifics",
   "wifi_configuration_specifics",
-  "wifi_credential_specifics",
 ]
 
 sync_protocol_sources = []
diff --git a/components/sync/protocol/sync.proto b/components/sync/protocol/sync.proto
index e213548..cbdd502 100644
--- a/components/sync/protocol/sync.proto
+++ b/components/sync/protocol/sync.proto
@@ -18,7 +18,6 @@
 package sync_pb;
 
 import "app_list_specifics.proto";
-import "app_notification_specifics.proto";
 import "app_setting_specifics.proto";
 import "app_specifics.proto";
 import "arc_package_specifics.proto";
@@ -38,8 +37,6 @@
 import "get_updates_caller_info.proto";
 import "history_delete_directive_specifics.proto";
 import "managed_user_setting_specifics.proto";
-import "managed_user_shared_setting_specifics.proto";
-import "managed_user_specifics.proto";
 import "managed_user_whitelist_specifics.proto";
 import "mountain_share_specifics.proto";
 import "nigori_specifics.proto";
@@ -53,8 +50,6 @@
 import "send_tab_to_self_specifics.proto";
 import "session_specifics.proto";
 import "sync_enums.proto";
-import "synced_notification_app_info_specifics.proto";
-import "synced_notification_specifics.proto";
 import "theme_specifics.proto";
 import "typed_url_specifics.proto";
 import "unique_position.proto";
@@ -62,7 +57,6 @@
 import "user_event_specifics.proto";
 import "web_app_specifics.proto";
 import "wifi_configuration_specifics.proto";
-import "wifi_credential_specifics.proto";
 
 // Used for inspecting how long we spent performing operations in different
 // backends. All times must be in millis.
@@ -118,7 +112,6 @@
     PreferenceSpecifics preference = 37702;
     TypedUrlSpecifics typed_url = 40781;
     ThemeSpecifics theme = 41210;
-    AppNotification app_notification = 45184;
     PasswordSpecifics password = 45873;
     NigoriSpecifics nigori = 47745;
     ExtensionSpecifics extension = 48119;
@@ -129,8 +122,6 @@
     ExtensionSettingSpecifics extension_setting = 96159;
     AppSettingSpecifics app_setting = 103656;
     HistoryDeleteDirectiveSpecifics history_delete_directive = 150251;
-    SyncedNotificationSpecifics synced_notification = 153108;
-    SyncedNotificationAppInfoSpecifics synced_notification_app_info = 235816;
     DeviceInfoSpecifics device_info = 154522;
     ExperimentsSpecifics experiments = 161496;
     PriorityPreferenceSpecifics priority_preference = 163425;
@@ -138,12 +129,11 @@
     FaviconTrackingSpecifics favicon_tracking = 181534;
     FaviconImageSpecifics favicon_image = 182019;
     ManagedUserSettingSpecifics managed_user_setting = 186662;
-    ManagedUserSpecifics managed_user = 194582;
-    ManagedUserSharedSettingSpecifics managed_user_shared_setting = 202026;
     ManagedUserWhitelistSpecifics managed_user_whitelist = 306060;
-    ArticleSpecifics article = 223759;
+    // TODO(crbug.com/1007942): |article| isn't used by Sync anymore, but it
+    // can't be removed because dom_distiller code uses it for local storage.
+    ArticleSpecifics article = 223759 [deprecated = true];
     AppListSpecifics app_list = 229170;
-    WifiCredentialSpecifics wifi_credential = 218175;
     AutofillWalletSpecifics autofill_wallet = 306270;
     WalletMetadataSpecifics wallet_metadata = 330441;
     ArcPackageSpecifics arc_package = 340906;
@@ -157,6 +147,18 @@
     WebAppSpecifics web_app = 673225;
     WifiConfigurationSpecifics wifi_configuration = 662827;
   }
+  reserved 45184;
+  reserved "app_notification";
+  reserved 153108;
+  reserved "synced_notification";
+  reserved 194582;
+  reserved "managed_user";
+  reserved 202026;
+  reserved "managed_user_shared_setting";
+  reserved 218175;
+  reserved "wifi_credential";
+  reserved 235816;
+  reserved "synced_notification_app_info";
 }
 
 message SyncEntity {
diff --git a/components/sync/protocol/synced_notification_app_info_specifics.proto b/components/sync/protocol/synced_notification_app_info_specifics.proto
deleted file mode 100644
index f8dea52b..0000000
--- a/components/sync/protocol/synced_notification_app_info_specifics.proto
+++ /dev/null
@@ -1,18 +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.
-//
-// Sync protocol datatype extension for synced notification app info objects.
-// DO NOT USE: This datatype is deprecated.
-
-syntax = "proto2";
-
-option java_multiple_files = true;
-option java_package = "org.chromium.components.sync.protocol";
-
-option optimize_for = LITE_RUNTIME;
-
-package sync_pb;
-
-// This message is kept around for backwards compatibility sake.
-message SyncedNotificationAppInfoSpecifics {}
diff --git a/components/sync/protocol/synced_notification_specifics.proto b/components/sync/protocol/synced_notification_specifics.proto
deleted file mode 100644
index c649f342..0000000
--- a/components/sync/protocol/synced_notification_specifics.proto
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-//
-// Sync protocol datatype extension for synced notifications.
-// DO NOT USE: This datatype is deprecated.
-
-syntax = "proto2";
-
-option java_multiple_files = true;
-option java_package = "org.chromium.components.sync.protocol";
-
-option optimize_for = LITE_RUNTIME;
-
-package sync_pb;
-
-// This message is kept around for backwards compatibility sake.
-message SyncedNotificationSpecifics {}
diff --git a/components/sync/protocol/wifi_credential_specifics.proto b/components/sync/protocol/wifi_credential_specifics.proto
deleted file mode 100644
index 25016f3..0000000
--- a/components/sync/protocol/wifi_credential_specifics.proto
+++ /dev/null
@@ -1,60 +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.
-//
-// Sync protocol datatype extension for WiFi credentials.
-
-// If you change or add any fields in this file, update proto_visitors.h and
-// potentially proto_enum_conversions.{h, cc}.
-
-syntax = "proto2";
-
-option java_multiple_files = true;
-option java_package = "org.chromium.components.sync.protocol";
-
-option optimize_for = LITE_RUNTIME;
-
-package sync_pb;
-
-// Properties of WiFi credential objects.
-message WifiCredentialSpecifics {
-  optional bytes ssid = 1;  // Not necessarily UTF-8. May contain NUL.
-
-  enum SecurityClass {
-    SECURITY_CLASS_INVALID = 0;
-    SECURITY_CLASS_NONE = 1;
-    SECURITY_CLASS_WEP = 2;
-    SECURITY_CLASS_PSK = 3;  // WPA-PSK or RSN-PSK
-    // 802.1X is omittted, as we do not support syncing 802.1X
-    // credentials.
-  }
-  optional SecurityClass security_class = 2;
-
-  // Network passphrase.
-  //
-  // For SECURITY_CLASS_NONE, the passphrase should be ignored.
-  //
-  // For SECURITY_CLASS_WEP, the passphrase should have one of the
-  // following formats:
-  // - WEP-40:
-  //   - 5 character ASCII string. Each character maps one byte of the key.
-  //   - 10 character hex string. The string maps to the WEP key by simple
-  //     hex decoding.
-  // - WEP-104:
-  //   - 13 character ASCII string. Each character maps one byte of the key.
-  //   - 26 character hex string. The string maps to the WEP key by simple
-  //     hex decoding.
-  //
-  // For SECURITY_CLASS_PSK, the passphrase should have one of the
-  // following two formats:
-  // - An 8-63 character ASCII string. The string maps to the
-  //   WPA/WPA-2 PSK as per IEEE 802.11i.
-  // - A 64 character hex string. The string maps to the PSK per
-  //   simple hex decoding.
-  //
-  // Note that, although the passphrase "should" contain only ASCII
-  // characters, we represent |passphrase| as |bytes| rather than
-  // |string|. This is to accomodate networks that use non-ASCII
-  // passphrases.
-  optional bytes passphrase = 3;
-}
diff --git a/components/sync/syncable/base_node.cc b/components/sync/syncable/base_node.cc
index ef8a5042..5be7a39 100644
--- a/components/sync/syncable/base_node.cc
+++ b/components/sync/syncable/base_node.cc
@@ -163,10 +163,6 @@
   return GetEntry()->GetMetahandle();
 }
 
-base::Time BaseNode::GetModificationTime() const {
-  return GetEntry()->GetMtime();
-}
-
 bool BaseNode::GetIsFolder() const {
   return GetEntry()->GetIsDir();
 }
@@ -266,11 +262,6 @@
   return *wifi_configuration_data_;
 }
 
-const sync_pb::TypedUrlSpecifics& BaseNode::GetTypedUrlSpecifics() const {
-  DCHECK_EQ(GetModelType(), TYPED_URLS);
-  return GetEntitySpecifics().typed_url();
-}
-
 const sync_pb::EntitySpecifics& BaseNode::GetEntitySpecifics() const {
   return GetUnencryptedSpecifics(GetEntry());
 }
diff --git a/components/sync/syncable/base_node.h b/components/sync/syncable/base_node.h
index 82d8b71..2925b44 100644
--- a/components/sync/syncable/base_node.h
+++ b/components/sync/syncable/base_node.h
@@ -30,7 +30,6 @@
 class EntitySpecifics;
 class NigoriSpecifics;
 class PasswordSpecificsData;
-class TypedUrlSpecifics;
 class WifiConfigurationSpecificsData;
 }  // namespace sync_pb
 
@@ -84,9 +83,6 @@
   // different ID value.
   virtual int64_t GetId() const;
 
-  // Returns the modification time of the object.
-  base::Time GetModificationTime() const;
-
   // Nodes are hierarchically arranged into a single-rooted tree.
   // InitByRootLookup on ReadNode allows access to the root. GetParentId is
   // how you find a node's parent.
@@ -126,10 +122,6 @@
   const sync_pb::WifiConfigurationSpecificsData& GetWifiConfigurationSpecifics()
       const;
 
-  // Getter specific to the TYPED_URLS datatype.  Returns protobuf
-  // data.  Can only be called if GetModelType() == TYPED_URLS.
-  const sync_pb::TypedUrlSpecifics& GetTypedUrlSpecifics() const;
-
   const sync_pb::EntitySpecifics& GetEntitySpecifics() const;
 
   // Returns the local external ID associated with the node.
diff --git a/components/sync/syncable/nigori_util.cc b/components/sync/syncable/nigori_util.cc
index 4d2fb24..a4d62e7 100644
--- a/components/sync/syncable/nigori_util.cc
+++ b/components/sync/syncable/nigori_util.cc
@@ -229,8 +229,7 @@
     DVLOG(2) << "Specifics of type " << ModelTypeToString(type)
              << " already match, dropping change.";
     UMA_HISTOGRAM_ENUMERATION("Sync.ModelTypeRedundantPut",
-                              ModelTypeToHistogramInt(type),
-                              static_cast<int>(ModelType::NUM_ENTRIES));
+                              ModelTypeHistogramValue(type));
     return true;
   }
 
@@ -266,7 +265,7 @@
                                     bool encrypt_everything,
                                     sync_pb::NigoriSpecifics* nigori) {
   nigori->set_encrypt_everything(encrypt_everything);
-  static_assert(46 == ModelType::NUM_ENTRIES,
+  static_assert(39 == ModelType::NUM_ENTRIES,
                 "If adding an encryptable type, update handling below.");
   nigori->set_encrypt_bookmarks(encrypted_types.Has(BOOKMARKS));
   nigori->set_encrypt_preferences(encrypted_types.Has(PREFERENCES));
@@ -283,12 +282,9 @@
   nigori->set_encrypt_app_settings(encrypted_types.Has(APP_SETTINGS));
   nigori->set_encrypt_extension_settings(
       encrypted_types.Has(EXTENSION_SETTINGS));
-  nigori->set_encrypt_app_notifications(
-      encrypted_types.Has(DEPRECATED_APP_NOTIFICATIONS));
   nigori->set_encrypt_dictionary(encrypted_types.Has(DICTIONARY));
   nigori->set_encrypt_favicon_images(encrypted_types.Has(FAVICON_IMAGES));
   nigori->set_encrypt_favicon_tracking(encrypted_types.Has(FAVICON_TRACKING));
-  nigori->set_encrypt_articles(encrypted_types.Has(DEPRECATED_ARTICLES));
   nigori->set_encrypt_app_list(encrypted_types.Has(APP_LIST));
   nigori->set_encrypt_arc_package(encrypted_types.Has(ARC_PACKAGE));
   nigori->set_encrypt_printers(encrypted_types.Has(PRINTERS));
@@ -304,7 +300,7 @@
     return ModelTypeSet::All();
 
   ModelTypeSet encrypted_types;
-  static_assert(46 == ModelType::NUM_ENTRIES,
+  static_assert(39 == ModelType::NUM_ENTRIES,
                 "If adding an encryptable type, update handling below.");
   if (nigori.encrypt_bookmarks())
     encrypted_types.Put(BOOKMARKS);
@@ -332,16 +328,12 @@
     encrypted_types.Put(APP_SETTINGS);
   if (nigori.encrypt_extension_settings())
     encrypted_types.Put(EXTENSION_SETTINGS);
-  if (nigori.encrypt_app_notifications())
-    encrypted_types.Put(DEPRECATED_APP_NOTIFICATIONS);
   if (nigori.encrypt_dictionary())
     encrypted_types.Put(DICTIONARY);
   if (nigori.encrypt_favicon_images())
     encrypted_types.Put(FAVICON_IMAGES);
   if (nigori.encrypt_favicon_tracking())
     encrypted_types.Put(FAVICON_TRACKING);
-  if (nigori.encrypt_articles())
-    encrypted_types.Put(DEPRECATED_ARTICLES);
   if (nigori.encrypt_app_list())
     encrypted_types.Put(APP_LIST);
   if (nigori.encrypt_arc_package())
diff --git a/components/sync/syncable/read_node.cc b/components/sync/syncable/read_node.cc
index 6f220cb4..d651ed1 100644
--- a/components/sync/syncable/read_node.cc
+++ b/components/sync/syncable/read_node.cc
@@ -5,7 +5,7 @@
 #include "components/sync/syncable/read_node.h"
 
 #include "base/logging.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/syncable/base_transaction.h"
 #include "components/sync/syncable/entry.h"
 #include "components/sync/syncable/syncable_base_transaction.h"
@@ -58,7 +58,7 @@
   if (tag.empty())
     return INIT_FAILED_PRECONDITION;
 
-  const std::string hash = GenerateSyncableHash(model_type, tag);
+  const std::string hash = ClientTagHash::FromUnhashed(model_type, tag).value();
 
   entry_ = new syncable::Entry(transaction_->GetWrappedTrans(),
                                syncable::GET_BY_CLIENT_TAG, hash);
diff --git a/components/sync/syncable/write_node.cc b/components/sync/syncable/write_node.cc
index aa0a3608f..507166b 100644
--- a/components/sync/syncable/write_node.cc
+++ b/components/sync/syncable/write_node.cc
@@ -9,7 +9,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/passphrase_enums.h"
 #include "components/sync/engine/engine_util.h"
 #include "components/sync/nigori/cryptographer.h"
@@ -271,7 +271,7 @@
   if (tag.empty())
     return INIT_FAILED_PRECONDITION;
 
-  const std::string hash = GenerateSyncableHash(model_type, tag);
+  const std::string hash = ClientTagHash::FromUnhashed(model_type, tag).value();
 
   entry_ = new syncable::MutableEntry(transaction_->GetWrappedWriteTrans(),
                                       syncable::GET_BY_CLIENT_TAG, hash);
@@ -362,7 +362,7 @@
     return INIT_FAILED_EMPTY_TAG;
   }
 
-  const std::string hash = GenerateSyncableHash(model_type, tag);
+  const std::string hash = ClientTagHash::FromUnhashed(model_type, tag).value();
 
   // Start out with a dummy name.  We expect
   // the caller to set a meaningful name after creation.
diff --git a/components/sync/test/engine/mock_model_type_processor.cc b/components/sync/test/engine/mock_model_type_processor.cc
index 1c46ef0..ea3a06f 100644
--- a/components/sync/test/engine/mock_model_type_processor.cc
+++ b/components/sync/test/engine/mock_model_type_processor.cc
@@ -9,6 +9,7 @@
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/hash/sha1.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/engine/commit_queue.h"
 
 namespace syncer {
@@ -68,7 +69,7 @@
 }
 
 std::unique_ptr<CommitRequestData> MockModelTypeProcessor::CommitRequest(
-    const std::string& tag_hash,
+    const ClientTagHash& tag_hash,
     const sync_pb::EntitySpecifics& specifics) {
   const int64_t base_version = GetBaseVersion(tag_hash);
 
@@ -86,7 +87,7 @@
   data->creation_time = base::Time::UnixEpoch() + base::TimeDelta::FromDays(1);
   data->modification_time =
       data->creation_time + base::TimeDelta::FromSeconds(base_version);
-  data->name = "Name: " + tag_hash;
+  data->name = "Name: " + tag_hash.value();
 
   auto request_data = std::make_unique<CommitRequestData>();
   request_data->entity = std::move(data);
@@ -99,7 +100,7 @@
 }
 
 std::unique_ptr<CommitRequestData> MockModelTypeProcessor::DeleteRequest(
-    const std::string& tag_hash) {
+    const ClientTagHash& tag_hash) {
   const int64_t base_version = GetBaseVersion(tag_hash);
 
   auto data = std::make_unique<syncer::EntityData>();
@@ -166,26 +167,26 @@
 }
 
 bool MockModelTypeProcessor::HasUpdateResponse(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   auto it = update_response_items_.find(tag_hash);
   return it != update_response_items_.end();
 }
 
 const UpdateResponseData& MockModelTypeProcessor::GetUpdateResponse(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   DCHECK(HasUpdateResponse(tag_hash));
   auto it = update_response_items_.find(tag_hash);
   return *it->second;
 }
 
 bool MockModelTypeProcessor::HasCommitResponse(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   auto it = commit_response_items_.find(tag_hash);
   return it != commit_response_items_.end();
 }
 
 CommitResponseData MockModelTypeProcessor::GetCommitResponse(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   DCHECK(HasCommitResponse(tag_hash));
   auto it = commit_response_items_.find(tag_hash);
   return it->second;
@@ -211,7 +212,7 @@
   received_commit_responses_.push_back(response_list);
   type_states_received_on_commit_.push_back(type_state);
   for (auto it = response_list.begin(); it != response_list.end(); ++it) {
-    const std::string tag_hash = it->client_tag_hash;
+    const ClientTagHash& tag_hash = it->client_tag_hash;
     commit_response_items_.insert(std::make_pair(tag_hash, *it));
 
     if (pending_deleted_hashes_.find(tag_hash) !=
@@ -235,7 +236,7 @@
     UpdateResponseDataList response_list) {
   type_states_received_on_update_.push_back(type_state);
   for (auto it = response_list.begin(); it != response_list.end(); ++it) {
-    const std::string client_tag_hash = (*it)->entity->client_tag_hash;
+    const ClientTagHash& client_tag_hash = (*it)->entity->client_tag_hash;
     // Server wins.  Set the model's base version.
     SetBaseVersion(client_tag_hash, (*it)->response_version);
     SetServerAssignedId(client_tag_hash, (*it)->entity->id);
@@ -247,7 +248,7 @@
 
 // Fetches the sequence number as of the most recent update request.
 int64_t MockModelTypeProcessor::GetCurrentSequenceNumber(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   auto it = sequence_numbers_.find(tag_hash);
   if (it == sequence_numbers_.end()) {
     return 0;
@@ -259,7 +260,7 @@
 // The model thread should be sending us items with strictly increasing
 // sequence numbers.  Here's where we emulate that behavior.
 int64_t MockModelTypeProcessor::GetNextSequenceNumber(
-    const std::string& tag_hash) {
+    const ClientTagHash& tag_hash) {
   int64_t sequence_number = GetCurrentSequenceNumber(tag_hash);
   sequence_number++;
   sequence_numbers_[tag_hash] = sequence_number;
@@ -267,7 +268,7 @@
 }
 
 int64_t MockModelTypeProcessor::GetBaseVersion(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   auto it = base_versions_.find(tag_hash);
   if (it == base_versions_.end()) {
     return kUncommittedVersion;
@@ -276,23 +277,23 @@
   }
 }
 
-void MockModelTypeProcessor::SetBaseVersion(const std::string& tag_hash,
+void MockModelTypeProcessor::SetBaseVersion(const ClientTagHash& tag_hash,
                                             int64_t version) {
   base_versions_[tag_hash] = version;
 }
 
 bool MockModelTypeProcessor::HasServerAssignedId(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   return assigned_ids_.find(tag_hash) != assigned_ids_.end();
 }
 
 const std::string& MockModelTypeProcessor::GetServerAssignedId(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   DCHECK(HasServerAssignedId(tag_hash));
   return assigned_ids_.find(tag_hash)->second;
 }
 
-void MockModelTypeProcessor::SetServerAssignedId(const std::string& tag_hash,
+void MockModelTypeProcessor::SetServerAssignedId(const ClientTagHash& tag_hash,
                                                  const std::string& id) {
   assigned_ids_[tag_hash] = id;
 }
diff --git a/components/sync/test/engine/mock_model_type_processor.h b/components/sync/test/engine/mock_model_type_processor.h
index dd9a755..d065cadb 100644
--- a/components/sync/test/engine/mock_model_type_processor.h
+++ b/components/sync/test/engine/mock_model_type_processor.h
@@ -69,9 +69,10 @@
   // return the value to the caller so the test framework can handle them as it
   // sees fit.
   std::unique_ptr<CommitRequestData> CommitRequest(
-      const std::string& tag_hash,
+      const ClientTagHash& tag_hash,
       const sync_pb::EntitySpecifics& specifics);
-  std::unique_ptr<CommitRequestData> DeleteRequest(const std::string& tag_hash);
+  std::unique_ptr<CommitRequestData> DeleteRequest(
+      const ClientTagHash& tag_hash);
 
   // Getters to access the log of received update responses.
   //
@@ -88,13 +89,13 @@
   sync_pb::ModelTypeState GetNthCommitState(size_t n) const;
 
   // Getters to access the lastest update response for a given tag_hash.
-  bool HasUpdateResponse(const std::string& tag_hash) const;
+  bool HasUpdateResponse(const ClientTagHash& tag_hash) const;
   const UpdateResponseData& GetUpdateResponse(
-      const std::string& tag_hash) const;
+      const ClientTagHash& tag_hash) const;
 
   // Getters to access the lastest commit response for a given tag_hash.
-  bool HasCommitResponse(const std::string& tag_hash) const;
-  CommitResponseData GetCommitResponse(const std::string& tag_hash) const;
+  bool HasCommitResponse(const ClientTagHash& tag_hash) const;
+  CommitResponseData GetCommitResponse(const ClientTagHash& tag_hash) const;
 
   void SetDisconnectCallback(const DisconnectCallback& callback);
 
@@ -117,17 +118,18 @@
                             UpdateResponseDataList response_list);
 
   // Getter and setter for per-item sequence number tracking.
-  int64_t GetCurrentSequenceNumber(const std::string& tag_hash) const;
-  int64_t GetNextSequenceNumber(const std::string& tag_hash);
+  int64_t GetCurrentSequenceNumber(const ClientTagHash& tag_hash) const;
+  int64_t GetNextSequenceNumber(const ClientTagHash& tag_hash);
 
   // Getter and setter for per-item base version tracking.
-  int64_t GetBaseVersion(const std::string& tag_hash) const;
-  void SetBaseVersion(const std::string& tag_hash, int64_t version);
+  int64_t GetBaseVersion(const ClientTagHash& tag_hash) const;
+  void SetBaseVersion(const ClientTagHash& tag_hash, int64_t version);
 
   // Getters and setter for server-assigned ID values.
-  bool HasServerAssignedId(const std::string& tag_hash) const;
-  const std::string& GetServerAssignedId(const std::string& tag_hash) const;
-  void SetServerAssignedId(const std::string& tag_hash, const std::string& id);
+  bool HasServerAssignedId(const ClientTagHash& tag_hash) const;
+  const std::string& GetServerAssignedId(const ClientTagHash& tag_hash) const;
+  void SetServerAssignedId(const ClientTagHash& tag_hash,
+                           const std::string& id);
 
   // State related to the implementation of deferred work.
   // See SetSynchronousExecution() for details.
@@ -142,17 +144,17 @@
   std::vector<sync_pb::ModelTypeState> type_states_received_on_commit_;
 
   // Latest responses received, indexed by tag_hash.
-  std::map<const std::string, CommitResponseData> commit_response_items_;
-  std::map<const std::string, const UpdateResponseData*> update_response_items_;
+  std::map<ClientTagHash, CommitResponseData> commit_response_items_;
+  std::map<ClientTagHash, const UpdateResponseData*> update_response_items_;
 
   // The per-item state maps.
-  std::map<const std::string, int64_t> sequence_numbers_;
-  std::map<const std::string, int64_t> base_versions_;
-  std::map<const std::string, std::string> assigned_ids_;
+  std::map<ClientTagHash, int64_t> sequence_numbers_;
+  std::map<ClientTagHash, int64_t> base_versions_;
+  std::map<ClientTagHash, std::string> assigned_ids_;
 
   // Set of tag hashes which were deleted with DeleteRequest but haven't yet
   // been confirmed by the server with OnCommitCompleted.
-  std::set<std::string> pending_deleted_hashes_;
+  std::set<ClientTagHash> pending_deleted_hashes_;
 
   // Callback which will be call during disconnection
   DisconnectCallback disconnect_callback_;
diff --git a/components/sync/test/engine/mock_model_type_worker.cc b/components/sync/test/engine/mock_model_type_worker.cc
index 6121812..a36e332 100644
--- a/components/sync/test/engine/mock_model_type_worker.cc
+++ b/components/sync/test/engine/mock_model_type_worker.cc
@@ -56,7 +56,7 @@
 }
 
 bool MockModelTypeWorker::HasPendingCommitForHash(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   for (const CommitRequestDataList& commit : pending_commits_) {
     for (const std::unique_ptr<CommitRequestData>& data : commit) {
       if (data && data->entity->client_tag_hash == tag_hash) {
@@ -68,7 +68,7 @@
 }
 
 const CommitRequestData* MockModelTypeWorker::GetLatestPendingCommitForHash(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   // Iterate backward through the sets of commit requests to find the most
   // recent one that applies to the specified tag_hash.
   for (auto rev_it = pending_commits_.rbegin();
@@ -85,7 +85,7 @@
 
 void MockModelTypeWorker::VerifyNthPendingCommit(
     size_t n,
-    const std::vector<std::string>& tag_hashes,
+    const std::vector<ClientTagHash>& tag_hashes,
     const std::vector<sync_pb::EntitySpecifics>& specifics_list) {
   ASSERT_EQ(tag_hashes.size(), specifics_list.size());
   std::vector<const CommitRequestData*> list = GetNthPendingCommit(n);
@@ -100,7 +100,7 @@
 }
 
 void MockModelTypeWorker::VerifyPendingCommits(
-    const std::vector<std::vector<std::string>>& tag_hashes) {
+    const std::vector<std::vector<ClientTagHash>>& tag_hashes) {
   ASSERT_EQ(tag_hashes.size(), GetNumPendingCommits());
   for (size_t i = 0; i < tag_hashes.size(); i++) {
     std::vector<const CommitRequestData*> commits = GetNthPendingCommit(i);
@@ -123,13 +123,13 @@
 }
 
 void MockModelTypeWorker::UpdateFromServer(
-    const std::string& tag_hash,
+    const ClientTagHash& tag_hash,
     const sync_pb::EntitySpecifics& specifics) {
   UpdateFromServer(tag_hash, specifics, 1);
 }
 
 void MockModelTypeWorker::UpdateFromServer(
-    const std::string& tag_hash,
+    const ClientTagHash& tag_hash,
     const sync_pb::EntitySpecifics& specifics,
     int64_t version_offset) {
   UpdateFromServer(tag_hash, specifics, version_offset,
@@ -137,7 +137,7 @@
 }
 
 void MockModelTypeWorker::UpdateFromServer(
-    const std::string& tag_hash,
+    const ClientTagHash& tag_hash,
     const sync_pb::EntitySpecifics& specifics,
     int64_t version_offset,
     const std::string& ekn) {
@@ -153,7 +153,7 @@
 
 std::unique_ptr<syncer::UpdateResponseData>
 MockModelTypeWorker::GenerateUpdateData(
-    const std::string& tag_hash,
+    const ClientTagHash& tag_hash,
     const sync_pb::EntitySpecifics& specifics,
     int64_t version_offset,
     const std::string& ekn) {
@@ -187,7 +187,7 @@
 
 std::unique_ptr<syncer::UpdateResponseData>
 MockModelTypeWorker::GenerateUpdateData(
-    const std::string& tag_hash,
+    const ClientTagHash& tag_hash,
     const sync_pb::EntitySpecifics& specifics) {
   return GenerateUpdateData(tag_hash, specifics, 1,
                             model_type_state_.encryption_key_name());
@@ -212,7 +212,7 @@
   return response_data;
 }
 
-void MockModelTypeWorker::TombstoneFromServer(const std::string& tag_hash) {
+void MockModelTypeWorker::TombstoneFromServer(const ClientTagHash& tag_hash) {
   int64_t old_version = GetServerVersion(tag_hash);
   int64_t version = old_version + 1;
   SetServerVersion(tag_hash, version);
@@ -262,7 +262,7 @@
     const CommitRequestData& request_data,
     int64_t version_offset) {
   const EntityData& entity = *request_data.entity;
-  const std::string& client_tag_hash = entity.client_tag_hash;
+  const ClientTagHash& client_tag_hash = entity.client_tag_hash;
 
   CommitResponseData response_data;
 
@@ -313,13 +313,12 @@
   processor_->OnUpdateReceived(model_type_state_, std::move(update));
 }
 
-std::string MockModelTypeWorker::GenerateId(const std::string& tag_hash) {
-  return "FakeId:" + tag_hash;
+std::string MockModelTypeWorker::GenerateId(const ClientTagHash& tag_hash) {
+  return "FakeId:" + tag_hash.value();
 }
 
-int64_t MockModelTypeWorker::GetServerVersion(const std::string& tag_hash) {
-  std::map<const std::string, int64_t>::const_iterator it;
-  it = server_versions_.find(tag_hash);
+int64_t MockModelTypeWorker::GetServerVersion(const ClientTagHash& tag_hash) {
+  auto it = server_versions_.find(tag_hash);
   if (it == server_versions_.end()) {
     return 0;
   } else {
@@ -327,7 +326,7 @@
   }
 }
 
-void MockModelTypeWorker::SetServerVersion(const std::string& tag_hash,
+void MockModelTypeWorker::SetServerVersion(const ClientTagHash& tag_hash,
                                            int64_t version) {
   server_versions_[tag_hash] = version;
 }
diff --git a/components/sync/test/engine/mock_model_type_worker.h b/components/sync/test/engine/mock_model_type_worker.h
index 7325607e..bc3faff 100644
--- a/components/sync/test/engine/mock_model_type_worker.h
+++ b/components/sync/test/engine/mock_model_type_worker.h
@@ -16,6 +16,7 @@
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/engine/commit_queue.h"
 #include "components/sync/engine/model_type_processor.h"
@@ -45,21 +46,21 @@
   // Getters to inspect the requests sent to this object.
   size_t GetNumPendingCommits() const;
   std::vector<const CommitRequestData*> GetNthPendingCommit(size_t n) const;
-  bool HasPendingCommitForHash(const std::string& tag_hash) const;
+  bool HasPendingCommitForHash(const ClientTagHash& tag_hash) const;
   const CommitRequestData* GetLatestPendingCommitForHash(
-      const std::string& tag_hash) const;
+      const ClientTagHash& tag_hash) const;
 
   // Verify that the |n|th commit request list has the corresponding commit
   // requests for |tag_hashes| with |value| set.
   void VerifyNthPendingCommit(
       size_t n,
-      const std::vector<std::string>& tag_hashes,
+      const std::vector<ClientTagHash>& tag_hashes,
       const std::vector<sync_pb::EntitySpecifics>& specificsList);
 
   // Verify the pending commits each contain a list of CommitRequestData and
   // they have the same hashes in the same order as |tag_hashes|.
   void VerifyPendingCommits(
-      const std::vector<std::vector<std::string>>& tag_hashes);
+      const std::vector<std::vector<ClientTagHash>>& tag_hashes);
 
   // Updates the model type state to be used in all future updates from server.
   void UpdateModelTypeState(const sync_pb::ModelTypeState& model_type_state);
@@ -68,12 +69,12 @@
   // descriptions. |version_offset| defaults to 1 and |ekn| defaults to the
   // current encryption key name the worker has.
   void UpdateFromServer();
-  void UpdateFromServer(const std::string& tag_hash,
+  void UpdateFromServer(const ClientTagHash& tag_hash,
                         const sync_pb::EntitySpecifics& specifics);
-  void UpdateFromServer(const std::string& tag_hash,
+  void UpdateFromServer(const ClientTagHash& tag_hash,
                         const sync_pb::EntitySpecifics& specifics,
                         int64_t version_offset);
-  void UpdateFromServer(const std::string& tag_hash,
+  void UpdateFromServer(const ClientTagHash& tag_hash,
                         const sync_pb::EntitySpecifics& specifics,
                         int64_t version_offset,
                         const std::string& ekn);
@@ -88,7 +89,7 @@
   //
   // |ekn| is the encryption key name this item will fake having.
   std::unique_ptr<syncer::UpdateResponseData> GenerateUpdateData(
-      const std::string& tag_hash,
+      const ClientTagHash& tag_hash,
       const sync_pb::EntitySpecifics& specifics,
       int64_t version_offset,
       const std::string& ekn);
@@ -96,7 +97,7 @@
   // Mostly same as GenerateUpdateData above, but set 1 as |version_offset|, and
   // use model_type_state_.encryption_key_name() as |ekn|.
   std::unique_ptr<syncer::UpdateResponseData> GenerateUpdateData(
-      const std::string& tag_hash,
+      const ClientTagHash& tag_hash,
       const sync_pb::EntitySpecifics& specifics);
 
   // Returns an UpdateResponseData representing an update received from
@@ -106,7 +107,7 @@
 
   // Triggers a server-side deletion of the entity with |tag_hash|; updates
   // server state accordingly.
-  void TombstoneFromServer(const std::string& tag_hash);
+  void TombstoneFromServer(const ClientTagHash& tag_hash);
 
   // Pops one pending commit from the front of the queue and send a commit
   // response to the processor for it.
@@ -132,7 +133,7 @@
 
  private:
   // Generate an ID string.
-  static std::string GenerateId(const std::string& tag_hash);
+  static std::string GenerateId(const ClientTagHash& tag_hash);
 
   // Returns a commit response that indicates a successful commit of the
   // given |request_data|. Updates server state accordingly.
@@ -141,8 +142,8 @@
       int64_t version_offset);
 
   // Retrieve or set the server version.
-  int64_t GetServerVersion(const std::string& tag_hash);
-  void SetServerVersion(const std::string& tag_hash, int64_t version);
+  int64_t GetServerVersion(const ClientTagHash& tag_hash);
+  void SetServerVersion(const ClientTagHash& tag_hash, int64_t version);
 
   sync_pb::ModelTypeState model_type_state_;
 
@@ -154,7 +155,7 @@
 
   // Map of versions by client tag hash.
   // This is an essential part of the mocked server state.
-  std::map<const std::string, int64_t> server_versions_;
+  std::map<ClientTagHash, int64_t> server_versions_;
 
   // WeakPtrFactory for this worker which will be sent to sync thread.
   base::WeakPtrFactory<MockModelTypeWorker> weak_ptr_factory_{this};
diff --git a/components/sync/test/engine/single_type_mock_server.cc b/components/sync/test/engine/single_type_mock_server.cc
index 72b4842..313f09a 100644
--- a/components/sync/test/engine/single_type_mock_server.cc
+++ b/components/sync/test/engine/single_type_mock_server.cc
@@ -34,7 +34,7 @@
 
 sync_pb::SyncEntity SingleTypeMockServer::UpdateFromServer(
     int64_t version_offset,
-    const std::string& tag_hash,
+    const ClientTagHash& tag_hash,
     const sync_pb::EntitySpecifics& specifics) {
   int64_t old_version = GetServerVersion(tag_hash);
   int64_t version = old_version + version_offset;
@@ -47,7 +47,7 @@
   entity.set_id_string(GenerateId(tag_hash));
   entity.set_parent_id_string(type_root_id_);
   entity.set_version(version);
-  entity.set_client_defined_unique_tag(tag_hash);
+  entity.set_client_defined_unique_tag(tag_hash.value());
   entity.set_deleted(false);
   entity.mutable_specifics()->CopyFrom(specifics);
 
@@ -56,14 +56,14 @@
   base::Time mtime = ctime + base::TimeDelta::FromSeconds(version);
   entity.set_ctime(TimeToProtoTime(ctime));
   entity.set_mtime(TimeToProtoTime(mtime));
-  entity.set_name("Name: " + tag_hash);
+  entity.set_name("Name: " + tag_hash.value());
 
   return entity;
 }
 
 sync_pb::SyncEntity SingleTypeMockServer::TombstoneFromServer(
     int64_t version_offset,
-    const std::string& tag_hash) {
+    const ClientTagHash& tag_hash) {
   int64_t old_version = GetServerVersion(tag_hash);
   int64_t version = old_version + version_offset;
   if (version > old_version) {
@@ -75,7 +75,7 @@
   entity.set_id_string(GenerateId(tag_hash));
   entity.set_parent_id_string(type_root_id_);
   entity.set_version(version);
-  entity.set_client_defined_unique_tag(tag_hash);
+  entity.set_client_defined_unique_tag(tag_hash.value());
   entity.set_deleted(false);
   AddDefaultFieldValue(type_, entity.mutable_specifics());
 
@@ -99,7 +99,8 @@
   const RepeatedPtrField<sync_pb::SyncEntity>& entries =
       message.commit().entries();
   for (const auto& entry : entries) {
-    const std::string tag_hash = entry.client_defined_unique_tag();
+    const ClientTagHash tag_hash =
+        ClientTagHash::FromHashed(entry.client_defined_unique_tag());
 
     committed_items_[tag_hash] = entry;
 
@@ -131,12 +132,13 @@
   return commit_messages_[n];
 }
 
-bool SingleTypeMockServer::HasCommitEntity(const std::string& tag_hash) const {
+bool SingleTypeMockServer::HasCommitEntity(
+    const ClientTagHash& tag_hash) const {
   return committed_items_.find(tag_hash) != committed_items_.end();
 }
 
 sync_pb::SyncEntity SingleTypeMockServer::GetLastCommittedEntity(
-    const std::string& tag_hash) const {
+    const ClientTagHash& tag_hash) const {
   DCHECK(HasCommitEntity(tag_hash));
   return committed_items_.find(tag_hash)->second;
 }
@@ -156,14 +158,13 @@
   progress_marker_token_ = token;
 }
 
-std::string SingleTypeMockServer::GenerateId(const std::string& tag_hash) {
-  return "FakeId:" + tag_hash;
+std::string SingleTypeMockServer::GenerateId(const ClientTagHash& tag_hash) {
+  return "FakeId:" + tag_hash.value();
 }
 
 int64_t SingleTypeMockServer::GetServerVersion(
-    const std::string& tag_hash) const {
-  std::map<const std::string, int64_t>::const_iterator it;
-  it = server_versions_.find(tag_hash);
+    const ClientTagHash& tag_hash) const {
+  auto it = server_versions_.find(tag_hash);
   // Server versions do not necessarily start at 1 or 0.
   if (it == server_versions_.end()) {
     return 2048;
@@ -172,7 +173,7 @@
   }
 }
 
-void SingleTypeMockServer::SetServerVersion(const std::string& tag_hash,
+void SingleTypeMockServer::SetServerVersion(const ClientTagHash& tag_hash,
                                             int64_t version) {
   server_versions_[tag_hash] = version;
 }
diff --git a/components/sync/test/engine/single_type_mock_server.h b/components/sync/test/engine/single_type_mock_server.h
index 72742fc9..2f06642 100644
--- a/components/sync/test/engine/single_type_mock_server.h
+++ b/components/sync/test/engine/single_type_mock_server.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/engine/non_blocking_sync_common.h"
 
@@ -40,13 +41,13 @@
   // updates, redeliveries, and genuine updates.
   sync_pb::SyncEntity UpdateFromServer(
       int64_t version_offset,
-      const std::string& tag_hash,
+      const ClientTagHash& tag_hash,
       const sync_pb::EntitySpecifics& specifics);
 
   // Generates a SyncEntity representing a server-delivered update to delete
   // an item.
   sync_pb::SyncEntity TombstoneFromServer(int64_t version_offset,
-                                          const std::string& tag_hash);
+                                          const ClientTagHash& tag_hash);
 
   // Generates a response to the specified commit message.
   //
@@ -66,8 +67,9 @@
 
   // Getters to return the most recently committed entities for a given
   // unique_client_tag hash.
-  bool HasCommitEntity(const std::string& tag_hash) const;
-  sync_pb::SyncEntity GetLastCommittedEntity(const std::string& tag_hash) const;
+  bool HasCommitEntity(const ClientTagHash& tag_hash) const;
+  sync_pb::SyncEntity GetLastCommittedEntity(
+      const ClientTagHash& tag_hash) const;
 
   // Getters that create realistic-looking progress markers and data type
   // context.
@@ -78,23 +80,23 @@
   void SetProgressMarkerToken(const std::string& token);
 
  private:
-  static std::string GenerateId(const std::string& tag_hash);
+  static std::string GenerateId(const ClientTagHash& tag_hash);
 
   // Get and set our emulated server state.
-  int64_t GetServerVersion(const std::string& tag_hash) const;
-  void SetServerVersion(const std::string& tag_hash, int64_t version);
+  int64_t GetServerVersion(const ClientTagHash& tag_hash) const;
+  void SetServerVersion(const ClientTagHash& tag_hash, int64_t version);
 
   const ModelType type_;
   const std::string type_root_id_;
 
   // Server version state maps.
-  std::map<const std::string, int64_t> server_versions_;
+  std::map<ClientTagHash, int64_t> server_versions_;
 
   // Log of messages sent to the server.
   std::vector<sync_pb::ClientToServerMessage> commit_messages_;
 
   // Map of most recent commits by tag_hash.
-  std::map<const std::string, sync_pb::SyncEntity> committed_items_;
+  std::map<ClientTagHash, sync_pb::SyncEntity> committed_items_;
 
   // The token that is used to generate the current progress marker.
   std::string progress_marker_token_;
diff --git a/components/sync_device_info/BUILD.gn b/components/sync_device_info/BUILD.gn
index e85e53e..be19364 100644
--- a/components/sync_device_info/BUILD.gn
+++ b/components/sync_device_info/BUILD.gn
@@ -15,6 +15,8 @@
     "device_info_prefs.h",
     "device_info_sync_bridge.cc",
     "device_info_sync_bridge.h",
+    "device_info_sync_client.cc",
+    "device_info_sync_client.h",
     "device_info_sync_service.cc",
     "device_info_sync_service.h",
     "device_info_sync_service_impl.cc",
diff --git a/components/sync_device_info/device_info_sync_client.cc b/components/sync_device_info/device_info_sync_client.cc
new file mode 100644
index 0000000..75ae6db1
--- /dev/null
+++ b/components/sync_device_info/device_info_sync_client.cc
@@ -0,0 +1,12 @@
+// Copyright 2019 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_device_info/device_info_sync_client.h"
+
+namespace syncer {
+
+DeviceInfoSyncClient::DeviceInfoSyncClient() = default;
+DeviceInfoSyncClient::~DeviceInfoSyncClient() = default;
+
+}  // namespace syncer
diff --git a/components/sync_device_info/device_info_sync_client.h b/components/sync_device_info/device_info_sync_client.h
new file mode 100644
index 0000000..386038d1
--- /dev/null
+++ b/components/sync_device_info/device_info_sync_client.h
@@ -0,0 +1,29 @@
+// Copyright 2019 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_DEVICE_INFO_DEVICE_INFO_SYNC_CLIENT_H_
+#define COMPONENTS_SYNC_DEVICE_INFO_DEVICE_INFO_SYNC_CLIENT_H_
+
+#include <string>
+
+#include "base/macros.h"
+
+namespace syncer {
+
+// Interface for clients of DeviceInfoSyncService.
+class DeviceInfoSyncClient {
+ public:
+  DeviceInfoSyncClient();
+  virtual ~DeviceInfoSyncClient();
+
+  virtual std::string GetSigninScopedDeviceId() const = 0;
+  virtual bool GetSendTabToSelfReceivingEnabled() const = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DeviceInfoSyncClient);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_DEVICE_INFO_DEVICE_INFO_SYNC_CLIENT_H_
diff --git a/components/sync_device_info/device_info_sync_service_impl.cc b/components/sync_device_info/device_info_sync_service_impl.cc
index a2136a05..a4a5da0 100644
--- a/components/sync_device_info/device_info_sync_service_impl.cc
+++ b/components/sync_device_info/device_info_sync_service_impl.cc
@@ -12,6 +12,7 @@
 #include "components/sync_device_info/device_info.h"
 #include "components/sync_device_info/device_info_prefs.h"
 #include "components/sync_device_info/device_info_sync_bridge.h"
+#include "components/sync_device_info/device_info_sync_client.h"
 #include "components/sync_device_info/device_info_tracker.h"
 #include "components/sync_device_info/local_device_info_provider.h"
 
@@ -20,9 +21,12 @@
 DeviceInfoSyncServiceImpl::DeviceInfoSyncServiceImpl(
     OnceModelTypeStoreFactory model_type_store_factory,
     std::unique_ptr<MutableLocalDeviceInfoProvider> local_device_info_provider,
-    std::unique_ptr<DeviceInfoPrefs> device_info_prefs) {
+    std::unique_ptr<DeviceInfoPrefs> device_info_prefs,
+    std::unique_ptr<DeviceInfoSyncClient> device_info_sync_client)
+    : device_info_sync_client_(std::move(device_info_sync_client)) {
   DCHECK(local_device_info_provider);
   DCHECK(device_info_prefs);
+  DCHECK(device_info_sync_client_);
 
   // Make a copy of the channel to avoid relying on argument evaluation order.
   const version_info::Channel channel =
diff --git a/components/sync_device_info/device_info_sync_service_impl.h b/components/sync_device_info/device_info_sync_service_impl.h
index 65cca99..32a084a 100644
--- a/components/sync_device_info/device_info_sync_service_impl.h
+++ b/components/sync_device_info/device_info_sync_service_impl.h
@@ -14,6 +14,7 @@
 namespace syncer {
 
 class DeviceInfoPrefs;
+class DeviceInfoSyncClient;
 class DeviceInfoSyncBridge;
 class MutableLocalDeviceInfoProvider;
 
@@ -21,10 +22,13 @@
  public:
   // |local_device_info_provider| must not be null.
   // |device_info_prefs| must not be null.
-  DeviceInfoSyncServiceImpl(OnceModelTypeStoreFactory model_type_store_factory,
-                            std::unique_ptr<MutableLocalDeviceInfoProvider>
-                                local_device_info_provider,
-                            std::unique_ptr<DeviceInfoPrefs> device_info_prefs);
+  // |device_info_sync_client| must not be null and must outlive this object.
+  DeviceInfoSyncServiceImpl(
+      OnceModelTypeStoreFactory model_type_store_factory,
+      std::unique_ptr<MutableLocalDeviceInfoProvider>
+          local_device_info_provider,
+      std::unique_ptr<DeviceInfoPrefs> device_info_prefs,
+      std::unique_ptr<DeviceInfoSyncClient> device_info_sync_client);
   ~DeviceInfoSyncServiceImpl() override;
 
   // DeviceInfoSyncService implementation.
@@ -33,6 +37,7 @@
   base::WeakPtr<ModelTypeControllerDelegate> GetControllerDelegate() override;
 
  private:
+  std::unique_ptr<DeviceInfoSyncClient> device_info_sync_client_;
   std::unique_ptr<DeviceInfoSyncBridge> bridge_;
 
   DISALLOW_COPY_AND_ASSIGN(DeviceInfoSyncServiceImpl);
diff --git a/components/sync_device_info/local_device_info_provider_impl.cc b/components/sync_device_info/local_device_info_provider_impl.cc
index de40505..aae8589 100644
--- a/components/sync_device_info/local_device_info_provider_impl.cc
+++ b/components/sync_device_info/local_device_info_provider_impl.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/driver/sync_util.h"
+#include "components/sync_device_info/device_info_sync_client.h"
 #include "components/sync_device_info/local_device_info_util.h"
 
 namespace syncer {
@@ -14,16 +15,9 @@
 LocalDeviceInfoProviderImpl::LocalDeviceInfoProviderImpl(
     version_info::Channel channel,
     const std::string& version,
-    const SigninScopedDeviceIdCallback& signin_scoped_device_id_callback,
-    const SendTabToSelfReceivingEnabledCallback&
-        send_tab_to_self_receiving_enabled_callback)
-    : channel_(channel),
-      version_(version),
-      signin_scoped_device_id_callback_(signin_scoped_device_id_callback),
-      send_tab_to_self_receiving_enabled_callback_(
-          send_tab_to_self_receiving_enabled_callback) {
-  DCHECK(signin_scoped_device_id_callback_);
-  DCHECK(send_tab_to_self_receiving_enabled_callback_);
+    const DeviceInfoSyncClient* sync_client)
+    : channel_(channel), version_(version), sync_client_(sync_client) {
+  DCHECK(sync_client);
 }
 
 LocalDeviceInfoProviderImpl::~LocalDeviceInfoProviderImpl() {
@@ -38,12 +32,12 @@
 const DeviceInfo* LocalDeviceInfoProviderImpl::GetLocalDeviceInfo() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  bool send_tab_to_self_receiving_enabled =
-      send_tab_to_self_receiving_enabled_callback_.Run();
-  if (local_device_info_) {
-    local_device_info_->set_send_tab_to_self_receiving_enabled(
-        send_tab_to_self_receiving_enabled);
+  if (!local_device_info_) {
+    return nullptr;
   }
+
+  local_device_info_->set_send_tab_to_self_receiving_enabled(
+      sync_client_->GetSendTabToSelfReceivingEnabled());
   return local_device_info_.get();
 }
 
@@ -64,9 +58,9 @@
   // the specifics when it will be synced up.
   local_device_info_ = std::make_unique<DeviceInfo>(
       cache_guid, session_name, version_, MakeUserAgentForSync(channel_),
-      GetLocalDeviceType(), signin_scoped_device_id_callback_.Run(),
+      GetLocalDeviceType(), sync_client_->GetSigninScopedDeviceId(),
       /*last_updated_timestamp=*/base::Time(),
-      send_tab_to_self_receiving_enabled_callback_.Run());
+      sync_client_->GetSendTabToSelfReceivingEnabled());
 
   // Notify observers.
   callback_list_.Notify();
diff --git a/components/sync_device_info/local_device_info_provider_impl.h b/components/sync_device_info/local_device_info_provider_impl.h
index e214c89..259dad5 100644
--- a/components/sync_device_info/local_device_info_provider_impl.h
+++ b/components/sync_device_info/local_device_info_provider_impl.h
@@ -18,17 +18,13 @@
 
 namespace syncer {
 
+class DeviceInfoSyncClient;
+
 class LocalDeviceInfoProviderImpl : public MutableLocalDeviceInfoProvider {
  public:
-  using SigninScopedDeviceIdCallback = base::RepeatingCallback<std::string()>;
-  using SendTabToSelfReceivingEnabledCallback = base::RepeatingCallback<bool()>;
-
-  LocalDeviceInfoProviderImpl(
-      version_info::Channel channel,
-      const std::string& version,
-      const SigninScopedDeviceIdCallback& signin_scoped_device_id_callback,
-      const SendTabToSelfReceivingEnabledCallback&
-          send_tab_to_self_receiving_enabled_callback);
+  LocalDeviceInfoProviderImpl(version_info::Channel channel,
+                              const std::string& version,
+                              const DeviceInfoSyncClient* sync_client);
   ~LocalDeviceInfoProviderImpl() override;
 
   // MutableLocalDeviceInfoProvider implementation.
@@ -47,9 +43,7 @@
   // The version string for the current client.
   const std::string version_;
 
-  const SigninScopedDeviceIdCallback signin_scoped_device_id_callback_;
-  const SendTabToSelfReceivingEnabledCallback
-      send_tab_to_self_receiving_enabled_callback_;
+  const DeviceInfoSyncClient* const sync_client_;
 
   std::unique_ptr<DeviceInfo> local_device_info_;
   base::CallbackList<void(void)> callback_list_;
diff --git a/components/sync_device_info/local_device_info_provider_impl_unittest.cc b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
index 6f0379e..aabd8f0 100644
--- a/components/sync_device_info/local_device_info_provider_impl_unittest.cc
+++ b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
@@ -4,8 +4,9 @@
 
 #include "components/sync_device_info/local_device_info_provider_impl.h"
 
-#include "base/test/mock_callback.h"
+#include "base/memory/ptr_util.h"
 #include "components/sync/driver/sync_util.h"
+#include "components/sync_device_info/device_info_sync_client.h"
 #include "components/version_info/version_string.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,6 +20,18 @@
 using testing::NotNull;
 using testing::Return;
 
+class MockDeviceInfoSyncClient : public DeviceInfoSyncClient {
+ public:
+  MockDeviceInfoSyncClient() = default;
+  ~MockDeviceInfoSyncClient() = default;
+
+  MOCK_CONST_METHOD0(GetSigninScopedDeviceId, std::string());
+  MOCK_CONST_METHOD0(GetSendTabToSelfReceivingEnabled, bool());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockDeviceInfoSyncClient);
+};
+
 class LocalDeviceInfoProviderImplTest : public testing::Test {
  public:
   LocalDeviceInfoProviderImplTest() {}
@@ -28,8 +41,7 @@
     provider_ = std::make_unique<LocalDeviceInfoProviderImpl>(
         version_info::Channel::UNKNOWN,
         version_info::GetVersionStringWithModifier("UNKNOWN"),
-        signin_scoped_device_id_callback_.Get(),
-        send_tab_to_self_receiving_enabled_callback_.Get());
+        &device_info_sync_client_);
   }
 
   void TearDown() override { provider_.reset(); }
@@ -41,12 +53,7 @@
     provider_->Initialize(guid, kLocalDeviceSessionName);
   }
 
-  testing::NiceMock<base::MockCallback<
-      LocalDeviceInfoProviderImpl::SigninScopedDeviceIdCallback>>
-      signin_scoped_device_id_callback_;
-  testing::NiceMock<base::MockCallback<
-      LocalDeviceInfoProviderImpl::SendTabToSelfReceivingEnabledCallback>>
-      send_tab_to_self_receiving_enabled_callback_;
+  testing::NiceMock<MockDeviceInfoSyncClient> device_info_sync_client_;
   std::unique_ptr<LocalDeviceInfoProviderImpl> provider_;
 };
 
@@ -69,7 +76,7 @@
 TEST_F(LocalDeviceInfoProviderImplTest, GetSigninScopedDeviceId) {
   const std::string kSigninScopedDeviceId = "device_id";
 
-  EXPECT_CALL(signin_scoped_device_id_callback_, Run())
+  EXPECT_CALL(device_info_sync_client_, GetSigninScopedDeviceId())
       .WillOnce(Return(kSigninScopedDeviceId));
 
   InitializeProvider();
@@ -80,7 +87,7 @@
 }
 
 TEST_F(LocalDeviceInfoProviderImplTest, SendTabToSelfReceivingEnabled) {
-  ON_CALL(send_tab_to_self_receiving_enabled_callback_, Run())
+  ON_CALL(device_info_sync_client_, GetSendTabToSelfReceivingEnabled())
       .WillByDefault(Return(true));
 
   InitializeProvider();
@@ -89,7 +96,7 @@
   EXPECT_TRUE(
       provider_->GetLocalDeviceInfo()->send_tab_to_self_receiving_enabled());
 
-  ON_CALL(send_tab_to_self_receiving_enabled_callback_, Run())
+  ON_CALL(device_info_sync_client_, GetSendTabToSelfReceivingEnabled())
       .WillByDefault(Return(false));
 
   ASSERT_THAT(provider_->GetLocalDeviceInfo(), NotNull());
diff --git a/components/sync_sessions/session_sync_bridge.cc b/components/sync_sessions/session_sync_bridge.cc
index a91a3b8..12136d6 100644
--- a/components/sync_sessions/session_sync_bridge.cc
+++ b/components/sync_sessions/session_sync_bridge.cc
@@ -17,7 +17,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/time.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/time.h"
 #include "components/sync/model/data_type_activation_request.h"
 #include "components/sync/model/entity_change.h"
@@ -235,8 +235,8 @@
 
         // Guaranteed by the processor.
         DCHECK_EQ(change->data().client_tag_hash,
-                  GenerateSyncableHash(syncer::SESSIONS,
-                                       SessionStore::GetClientTag(specifics)));
+                  syncer::ClientTagHash::FromUnhashed(
+                      syncer::SESSIONS, SessionStore::GetClientTag(specifics)));
 
         batch->PutAndUpdateTracker(specifics, change->data().modification_time);
         // If a favicon or favicon urls are present, load the URLs and visit
diff --git a/components/sync_sessions/session_sync_bridge_unittest.cc b/components/sync_sessions/session_sync_bridge_unittest.cc
index 5afc307..af019fd5 100644
--- a/components/sync_sessions/session_sync_bridge_unittest.cc
+++ b/components/sync_sessions/session_sync_bridge_unittest.cc
@@ -17,7 +17,7 @@
 #include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
 #include "components/prefs/testing_pref_service.h"
-#include "components/sync/base/hash_util.h"
+#include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/sync_prefs.h"
 #include "components/sync/model/data_batch.h"
 #include "components/sync/model/data_type_activation_request.h"
@@ -82,7 +82,7 @@
     const sync_pb::SessionSpecifics& specifics,
     base::Time mtime = base::Time::Now()) {
   auto data = std::make_unique<syncer::EntityData>();
-  data->client_tag_hash = syncer::GenerateSyncableHash(
+  data->client_tag_hash = syncer::ClientTagHash::FromUnhashed(
       syncer::SESSIONS, SessionStore::GetClientTag(specifics));
   *data->specifics.mutable_session() = specifics;
   data->modification_time = mtime;
@@ -111,7 +111,7 @@
   auto tombstone = std::make_unique<syncer::EntityData>();
 
   tombstone->client_tag_hash =
-      syncer::GenerateSyncableHash(syncer::SESSIONS, client_tag);
+      syncer::ClientTagHash::FromUnhashed(syncer::SESSIONS, client_tag);
 
   auto data = std::make_unique<syncer::UpdateResponseData>();
   data->entity = std::move(tombstone);
@@ -123,7 +123,7 @@
     const std::string& client_tag) {
   syncer::CommitResponseData response;
   response.client_tag_hash =
-      syncer::GenerateSyncableHash(syncer::SESSIONS, client_tag);
+      syncer::ClientTagHash::FromUnhashed(syncer::SESSIONS, client_tag);
   response.sequence_number = 1;
   return response;
 }
diff --git a/components/sync_sessions/synced_session_tracker.cc b/components/sync_sessions/synced_session_tracker.cc
index 4e2224c5..ee269b3 100644
--- a/components/sync_sessions/synced_session_tracker.cc
+++ b/components/sync_sessions/synced_session_tracker.cc
@@ -215,18 +215,6 @@
   return session ? session->tab_node_pool.GetAllTabNodeIds() : std::set<int>();
 }
 
-std::vector<const sessions::SessionTab*>
-SyncedSessionTracker::LookupUnmappedTabs(const std::string& session_tag) const {
-  const TrackedSession* session = LookupTrackedSession(session_tag);
-  std::vector<const sessions::SessionTab*> unmapped_tabs;
-  if (session) {
-    for (const auto& unmapped_tab_entry : session->unmapped_tabs) {
-      unmapped_tabs.push_back(unmapped_tab_entry.second.get());
-    }
-  }
-  return unmapped_tabs;
-}
-
 const SyncedSession* SyncedSessionTracker::LookupLocalSession() const {
   return LookupSession(local_session_tag_);
 }
diff --git a/components/sync_sessions/synced_session_tracker.h b/components/sync_sessions/synced_session_tracker.h
index e0585400..7422be7 100644
--- a/components/sync_sessions/synced_session_tracker.h
+++ b/components/sync_sessions/synced_session_tracker.h
@@ -67,10 +67,6 @@
   // session having tag |session_tag|.
   std::set<int> LookupTabNodeIds(const std::string& session_tag) const;
 
-  // Returns tabs that are unmapped for session with tag |session_tag|.
-  std::vector<const sessions::SessionTab*> LookupUnmappedTabs(
-      const std::string& session_tag) const;
-
   // Attempts to look up the session windows associatd with the session given
   // by |session_tag|. Ownership of SessionWindows stays within the
   // SyncedSessionTracker.
diff --git a/components/sync_sessions/synced_session_tracker_unittest.cc b/components/sync_sessions/synced_session_tracker_unittest.cc
index 98379746..ce62699 100644
--- a/components/sync_sessions/synced_session_tracker_unittest.cc
+++ b/components/sync_sessions/synced_session_tracker_unittest.cc
@@ -397,20 +397,6 @@
   EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), IsEmpty());
 }
 
-TEST_F(SyncedSessionTrackerTest, LookupUnmappedTabs) {
-  EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag), IsEmpty());
-
-  sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
-  ASSERT_THAT(tab, NotNull());
-
-  EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag), ElementsAre(tab));
-  EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag2), IsEmpty());
-
-  tracker_.PutWindowInSession(kTag, kWindow1);
-  tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
-  EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag), IsEmpty());
-}
-
 TEST_F(SyncedSessionTrackerTest, SessionTracking) {
   ASSERT_TRUE(tracker_.Empty());
 
diff --git a/components/sync_sessions/synced_tab_delegate.h b/components/sync_sessions/synced_tab_delegate.h
index e756fe06..bf48b062 100644
--- a/components/sync_sessions/synced_tab_delegate.h
+++ b/components/sync_sessions/synced_tab_delegate.h
@@ -35,11 +35,6 @@
   virtual SessionID GetSessionId() const = 0;
   virtual bool IsBeingDestroyed() const = 0;
 
-  // Get the tab id of the tab responsible for opening this tab, if applicable.
-  // Returns an invalid ID if no such tab relationship is known.
-  // TODO(mastiz): Rename to GetSourceTabSessionId().
-  virtual SessionID GetSourceTabID() const = 0;
-
   // Method derived from extensions TabHelper.
   virtual std::string GetExtensionAppId() const = 0;
 
diff --git a/components/sync_sessions/test_synced_window_delegates_getter.cc b/components/sync_sessions/test_synced_window_delegates_getter.cc
index af19b3c8..5e9db84 100644
--- a/components/sync_sessions/test_synced_window_delegates_getter.cc
+++ b/components/sync_sessions/test_synced_window_delegates_getter.cc
@@ -160,10 +160,6 @@
   return http_count > 0;
 }
 
-SessionID TestSyncedTabDelegate::GetSourceTabID() const {
-  return SessionID::InvalidValue();
-}
-
 int64_t TestSyncedTabDelegate::GetTaskIdForNavigationId(int nav_id) const {
   // Task IDs are currently not used in the tests. -1 signals an unknown Task
   // ID.
@@ -268,10 +264,6 @@
   return false;
 }
 
-SessionID PlaceholderTabDelegate::GetSourceTabID() const {
-  return SessionID::InvalidValue();
-}
-
 int64_t PlaceholderTabDelegate::GetTaskIdForNavigationId(int nav_id) const {
   // Task IDs are currently not used in the tests. -1 signals an unknown Task
   // ID.
diff --git a/components/sync_sessions/test_synced_window_delegates_getter.h b/components/sync_sessions/test_synced_window_delegates_getter.h
index 108df04..f373c00b 100644
--- a/components/sync_sessions/test_synced_window_delegates_getter.h
+++ b/components/sync_sessions/test_synced_window_delegates_getter.h
@@ -62,7 +62,6 @@
   GetBlockedNavigations() const override;
   bool IsPlaceholderTab() const override;
   bool ShouldSync(SyncSessionsClient* sessions_client) override;
-  SessionID GetSourceTabID() const override;
   int64_t GetTaskIdForNavigationId(int nav_id) const override;
   int64_t GetParentTaskIdForNavigationId(int nav_id) const override;
   int64_t GetRootTaskIdForNavigationId(int nav_id) const override;
@@ -113,7 +112,6 @@
   const std::vector<std::unique_ptr<const sessions::SerializedNavigationEntry>>*
   GetBlockedNavigations() const override;
   bool ShouldSync(SyncSessionsClient* sessions_client) override;
-  SessionID GetSourceTabID() const override;
   int64_t GetTaskIdForNavigationId(int nav_id) const override;
   int64_t GetParentTaskIdForNavigationId(int nav_id) const override;
   int64_t GetRootTaskIdForNavigationId(int nav_id) const override;
diff --git a/components/tracing/common/trace_startup_config.cc b/components/tracing/common/trace_startup_config.cc
index a5d7eb86..8720fe0c 100644
--- a/components/tracing/common/trace_startup_config.cc
+++ b/components/tracing/common/trace_startup_config.cc
@@ -79,14 +79,12 @@
 
 TraceStartupConfig::TraceStartupConfig() {
   auto* command_line = base::CommandLine::ForCurrentProcess();
-  if (!command_line->HasSwitch(switches::kDisablePerfetto)) {
-    const std::string value =
-        command_line->GetSwitchValueASCII(switches::kTraceStartupOwner);
-    if (value == "devtools") {
-      session_owner_ = SessionOwner::kDevToolsTracingHandler;
-    } else if (value == "system") {
-      session_owner_ = SessionOwner::kSystemTracing;
-    }
+  const std::string value =
+      command_line->GetSwitchValueASCII(switches::kTraceStartupOwner);
+  if (value == "devtools") {
+    session_owner_ = SessionOwner::kDevToolsTracingHandler;
+  } else if (value == "system") {
+    session_owner_ = SessionOwner::kSystemTracing;
   }
 
   if (EnableFromCommandLine()) {
diff --git a/components/tracing/common/tracing_switches.cc b/components/tracing/common/tracing_switches.cc
index bdd8bd5..0428622f 100644
--- a/components/tracing/common/tracing_switches.cc
+++ b/components/tracing/common/tracing_switches.cc
@@ -58,16 +58,6 @@
 // through the normal methods for stopping system traces.
 const char kTraceStartupOwner[] = "trace-startup-owner";
 
-// Disables the perfetto tracing backend. We need a separate command line
-// argument from the kTracingPerfettoBackend feature, because feature flags are
-// parsed too late during startup for early startup tracing support.
-const char kDisablePerfetto[] = "disable-perfetto";
-
-// Enables the perfetto tracing backend. We need a separate command line
-// argument from the kTracingPerfettoBackend feature, because feature flags are
-// parsed too late during startup for early startup tracing support.
-const char kEnablePerfetto[] = "enable-perfetto";
-
 // Repeat internable data for each TraceEvent in the perfetto proto format.
 const char kPerfettoDisableInterning[] = "perfetto-disable-interning";
 
diff --git a/components/tracing/common/tracing_switches.h b/components/tracing/common/tracing_switches.h
index b06520d6..09b7a20 100644
--- a/components/tracing/common/tracing_switches.h
+++ b/components/tracing/common/tracing_switches.h
@@ -16,8 +16,6 @@
 TRACING_EXPORT extern const char kTraceStartupFile[];
 TRACING_EXPORT extern const char kTraceStartupRecordMode[];
 TRACING_EXPORT extern const char kTraceStartupOwner[];
-TRACING_EXPORT extern const char kDisablePerfetto[];
-TRACING_EXPORT extern const char kEnablePerfetto[];
 TRACING_EXPORT extern const char kPerfettoDisableInterning[];
 TRACING_EXPORT extern const char kPerfettoOutputFile[];
 TRACING_EXPORT extern const char kTraceToConsole[];
diff --git a/components/ui_devtools/tracing_agent.cc b/components/ui_devtools/tracing_agent.cc
index e4c0d3b..e3faab2 100644
--- a/components/ui_devtools/tracing_agent.cc
+++ b/components/ui_devtools/tracing_agent.cc
@@ -380,14 +380,8 @@
 void TracingAgent::StartTracing(std::unique_ptr<StartCallback> callback) {
   trace_config_.SetProcessFilterConfig(CreateProcessFilterConfig(gpu_pid_));
 
-  if (tracing::TracingUsesPerfettoBackend()) {
-    perfetto_session_ =
-        std::make_unique<PerfettoTracingSession>(connector_.get());
-  } else {
-    callback->sendFailure(
-        Response::Error("Could not start tracing by Perfetto."));
-    return;
-  }
+  perfetto_session_ =
+      std::make_unique<PerfettoTracingSession>(connector_.get());
   perfetto_session_->EnableTracing(
       trace_config_,
       base::BindOnce(&TracingAgent::OnRecordingEnabled,
diff --git a/components/viz/host/host_display_client.cc b/components/viz/host/host_display_client.cc
index 76ed0dd..0108c892 100644
--- a/components/viz/host/host_display_client.cc
+++ b/components/viz/host/host_display_client.cc
@@ -59,7 +59,7 @@
 }
 #endif
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 void HostDisplayClient::DidCompleteSwapWithNewSize(const gfx::Size& size) {
   NOTIMPLEMENTED();
 }
diff --git a/components/viz/host/host_display_client.h b/components/viz/host/host_display_client.h
index 4619f19..af01fab0 100644
--- a/components/viz/host/host_display_client.h
+++ b/components/viz/host/host_display_client.h
@@ -42,7 +42,7 @@
       mojo::PendingReceiver<mojom::LayeredWindowUpdater> receiver) override;
 #endif
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   void DidCompleteSwapWithNewSize(const gfx::Size& size) override;
 #endif
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index c1a8f7ba..c87cdf65 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -930,7 +930,7 @@
 
 void SkiaOutputSurfaceImplOnGpu::CopyOutput(
     RenderPassId id,
-    const copy_output::RenderPassGeometry& geometry,
+    copy_output::RenderPassGeometry geometry,
     const gfx::ColorSpace& color_space,
     std::unique_ptr<CopyOutputRequest> request,
     base::OnceCallback<bool()> deferred_framebuffer_draw_closure) {
@@ -943,6 +943,40 @@
       base::BindOnce([](std::vector<std::unique_ptr<SkDeferredDisplayList>>) {},
                      std::move(destroy_after_swap_)));
 
+  bool use_gl_renderer_copier =
+      !is_using_vulkan() && !features::IsUsingSkiaForGLReadback();
+  // Lazy initialize GLRendererCopier before draw because
+  // DirectContextProvider ctor the backbuffer.
+  if (use_gl_renderer_copier && !copier_) {
+    if (!MakeCurrent(true /* need_fbo0 */))
+      return;
+    auto client = std::make_unique<DirectContextProviderDelegateImpl>(
+        gpu_preferences_, dependency_->GetGpuDriverBugWorkarounds(),
+        dependency_->GetGpuFeatureInfo(), context_state_.get(),
+        dependency_->GetMailboxManager(), dependency_->GetSharedImageManager(),
+        CreateSyncPointClientState(dependency_, sequence_id_));
+    context_provider_ = base::MakeRefCounted<DirectContextProvider>(
+        context_state_->context(), gl_surface_, supports_alpha_,
+        gpu_preferences_, feature_info_.get(), std::move(client));
+    auto result = context_provider_->BindToCurrentThread();
+    if (result != gpu::ContextResult::kSuccess) {
+      DLOG(ERROR) << "Couldn't initialize GLRendererCopier";
+      context_provider_ = nullptr;
+      return;
+    }
+    context_current_task_runner_ =
+        base::MakeRefCounted<ContextCurrentTaskRunner>(this);
+    texture_deleter_ =
+        std::make_unique<TextureDeleter>(context_current_task_runner_);
+    copier_ = std::make_unique<GLRendererCopier>(context_provider_,
+                                                 texture_deleter_.get());
+    copier_->set_async_gl_task_runner(context_current_task_runner_);
+
+    // DirectContextProvider changed GL state. Reset Skia state tracking
+    // for potential draw below.
+    gr_context()->resetContext();
+  }
+
   if (deferred_framebuffer_draw_closure) {
     // returns false if context not set to current, i.e lost
     if (!std::move(deferred_framebuffer_draw_closure).Run())
@@ -978,37 +1012,19 @@
     surface->flush();
   }
 
-  if (!is_using_vulkan() && !features::IsUsingSkiaForGLReadback()) {
-    // Lazy initialize GLRendererCopier.
-    if (!copier_) {
-      auto client = std::make_unique<DirectContextProviderDelegateImpl>(
-          gpu_preferences_, dependency_->GetGpuDriverBugWorkarounds(),
-          dependency_->GetGpuFeatureInfo(), context_state_.get(),
-          dependency_->GetMailboxManager(),
-          dependency_->GetSharedImageManager(),
-          CreateSyncPointClientState(dependency_, sequence_id_));
-      context_provider_ = base::MakeRefCounted<DirectContextProvider>(
-          context_state_->context(), gl_surface_, supports_alpha_,
-          gpu_preferences_, feature_info_.get(), std::move(client));
-      auto result = context_provider_->BindToCurrentThread();
-      if (result != gpu::ContextResult::kSuccess) {
-        DLOG(ERROR) << "Couldn't initialize GLRendererCopier";
-        context_provider_ = nullptr;
-        return;
-      }
-      context_current_task_runner_ =
-          base::MakeRefCounted<ContextCurrentTaskRunner>(this);
-      texture_deleter_ =
-          std::make_unique<TextureDeleter>(context_current_task_runner_);
-      copier_ = std::make_unique<GLRendererCopier>(context_provider_,
-                                                   texture_deleter_.get());
-      copier_->set_async_gl_task_runner(context_current_task_runner_);
-    }
+  if (use_gl_renderer_copier) {
     surface->flush();
 
     GLuint gl_id = 0;
     GLenum internal_format = supports_alpha_ ? GL_RGBA : GL_RGB;
     bool flipped = from_fbo0 ? !capabilities().flipped_output_surface : false;
+    // readback_offset is in window co-ordinate space and must take into account
+    // flipping.
+    if (flipped) {
+      geometry.readback_offset.set_y(
+          size_.height() -
+          (geometry.readback_offset.y() + geometry.result_selection.height()));
+    }
 
     base::Optional<ScopedSurfaceToTexture> texture_mapper;
     if (!from_fbo0 || dependency_->IsOffscreen()) {
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 59971434..eb9d790 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -141,7 +141,7 @@
   void RemoveRenderPassResource(
       std::vector<std::unique_ptr<ImageContextImpl>> image_contexts);
   void CopyOutput(RenderPassId id,
-                  const copy_output::RenderPassGeometry& geometry,
+                  copy_output::RenderPassGeometry geometry,
                   const gfx::ColorSpace& color_space,
                   std::unique_ptr<CopyOutputRequest> request,
                   base::OnceCallback<bool()> deferred_framebuffer_draw_closure);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
index ce80e8b..103db80 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_unittest.cc
@@ -187,19 +187,8 @@
   geometry.result_bounds = kSurfaceRect;
   geometry.result_selection = output_rect;
   geometry.sampling_bounds = kSurfaceRect;
+  geometry.readback_offset = gfx::Vector2d(0, 0);
 
-  if (gpu_service_holder_->is_vulkan_enabled()) {
-    // No flipping because Skia handles all co-ordinate transformation on the
-    // software readback path currently implemented for Vulkan.
-    geometry.readback_offset = geometry.readback_offset = gfx::Vector2d(0, 0);
-  } else {
-    // GLRendererCopier may need a vertical flip depending on output surface
-    // characteristics.
-    geometry.readback_offset =
-        output_surface_->capabilities().flipped_output_surface
-            ? geometry.readback_offset = gfx::Vector2d(0, 0)
-            : geometry.readback_offset = gfx::Vector2d(0, 90);
-  }
   output_surface_->CopyOutput(0, geometry, color_space, std::move(request));
   BlockMainThread();
 
diff --git a/components/viz/service/display_embedder/software_output_surface.cc b/components/viz/service/display_embedder/software_output_surface.cc
index 85a2040..65080f7 100644
--- a/components/viz/service/display_embedder/software_output_surface.cc
+++ b/components/viz/service/display_embedder/software_output_surface.cc
@@ -119,7 +119,7 @@
   base::TimeTicks now = base::TimeTicks::Now();
   base::TimeDelta interval_to_next_refresh =
       now.SnappedToNextTick(refresh_timebase_, refresh_interval_) - now;
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   if (needs_swap_size_notifications_)
     client_->DidSwapWithSize(pixel_size);
 #endif
@@ -148,7 +148,7 @@
   return gfx::OVERLAY_TRANSFORM_NONE;
 }
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 void SoftwareOutputSurface::SetNeedsSwapSizeNotifications(
     bool needs_swap_size_notifications) {
   needs_swap_size_notifications_ = needs_swap_size_notifications;
diff --git a/components/viz/service/display_embedder/software_output_surface.h b/components/viz/service/display_embedder/software_output_surface.h
index eb3ef42..c109013 100644
--- a/components/viz/service/display_embedder/software_output_surface.h
+++ b/components/viz/service/display_embedder/software_output_surface.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
 #include "components/viz/common/display/update_vsync_parameters_callback.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/service/display/output_surface.h"
@@ -49,7 +50,7 @@
       UpdateVSyncParametersCallback callback) override;
   void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
   gfx::OverlayTransform GetDisplayTransform() override;
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
 #endif
@@ -69,7 +70,7 @@
   std::queue<std::vector<ui::LatencyInfo>> stored_latency_info_;
   ui::LatencyTracker latency_tracker_;
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   bool needs_swap_size_notifications_ = false;
 #endif
 
diff --git a/components/viz/service/display_embedder/viz_process_context_provider.cc b/components/viz/service/display_embedder/viz_process_context_provider.cc
index 47e7214..41d744bd 100644
--- a/components/viz/service/display_embedder/viz_process_context_provider.cc
+++ b/components/viz/service/display_embedder/viz_process_context_provider.cc
@@ -138,6 +138,7 @@
     base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
         this);
   }
+  cache_controller_->SetGrContext(nullptr);
 }
 
 void VizProcessContextProvider::AddRef() const {
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index a20afa4..0807495 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -49,7 +49,7 @@
   output_surface->SetNeedsSwapSizeNotifications(
       params->send_swap_size_notifications);
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   // For X11, we need notify client about swap completion after resizing, so the
   // client can use it for synchronize with X11 WM.
   output_surface->SetNeedsSwapSizeNotifications(true);
@@ -358,7 +358,7 @@
 #if defined(OS_ANDROID)
   if (display_client_)
     display_client_->DidCompleteSwapWithSize(pixel_size);
-#elif defined(USE_X11)
+#elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
   if (display_client_ && pixel_size != last_swap_pixel_size_) {
     last_swap_pixel_size_ = pixel_size;
     display_client_->DidCompleteSwapWithNewSize(last_swap_pixel_size_);
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index 34e18572..1c9b6f5 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -135,7 +135,7 @@
   // to the BFS.
   std::unique_ptr<Display> display_;
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   gfx::Size last_swap_pixel_size_;
 #endif
 
diff --git a/components/viz/test/fake_output_surface.cc b/components/viz/test/fake_output_surface.cc
index 09db4f660..b245b40 100644
--- a/components/viz/test/fake_output_surface.cc
+++ b/components/viz/test/fake_output_surface.cc
@@ -107,7 +107,7 @@
   return gfx::OVERLAY_TRANSFORM_NONE;
 }
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 void FakeOutputSurface::SetNeedsSwapSizeNotifications(
     bool needs_swap_size_notifications) {}
 #endif
diff --git a/components/viz/test/fake_output_surface.h b/components/viz/test/fake_output_surface.h
index cb9a3b03..042a8d7 100644
--- a/components/viz/test/fake_output_surface.h
+++ b/components/viz/test/fake_output_surface.h
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/service/display/output_surface.h"
 #include "components/viz/service/display/output_surface_frame.h"
@@ -82,7 +83,7 @@
       UpdateVSyncParametersCallback callback) override;
   void SetDisplayTransformHint(gfx::OverlayTransform transform) override {}
   gfx::OverlayTransform GetDisplayTransform() override;
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
 #endif
diff --git a/components/webdata/common/web_database_migration_unittest.cc b/components/webdata/common/web_database_migration_unittest.cc
index 1db9a9ec..278a3605 100644
--- a/components/webdata/common/web_database_migration_unittest.cc
+++ b/components/webdata/common/web_database_migration_unittest.cc
@@ -1698,8 +1698,9 @@
     ASSERT_TRUE(s1.Step());
     // Note: This is the *wrong* ID for AUTOFILL, simulating the botched
     // migration in version 78. See crbug.com/895826.
-    ASSERT_EQ(syncer::ModelTypeToHistogramInt(syncer::AUTOFILL),
-              s1.ColumnInt(0));
+    ASSERT_EQ(
+        static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL)),
+        s1.ColumnInt(0));
     ASSERT_EQ("storage_key1", s1.ColumnString(1));
     ASSERT_EQ("blob1", s1.ColumnString(2));
 
@@ -1715,8 +1716,9 @@
         "SELECT model_type, value FROM autofill_model_type_state"));
     ASSERT_TRUE(s2.Step());
     // Like above: Bad value.
-    ASSERT_EQ(syncer::ModelTypeToHistogramInt(syncer::AUTOFILL),
-              s2.ColumnInt(0));
+    ASSERT_EQ(
+        static_cast<int>(syncer::ModelTypeHistogramValue(syncer::AUTOFILL)),
+        s2.ColumnInt(0));
     ASSERT_EQ("state1", s2.ColumnString(1));
     ASSERT_TRUE(s2.Step());
     // Good value.
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 53694424..e8d43be 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -826,8 +826,6 @@
     "download/save_types.h",
     "field_trial_recorder.cc",
     "field_trial_recorder.h",
-    "file_url_loader_factory.cc",
-    "file_url_loader_factory.h",
     "fileapi/browser_file_system_helper.cc",
     "fileapi/browser_file_system_helper.h",
     "fileapi/file_system_manager_impl.cc",
@@ -1087,6 +1085,8 @@
     "loader/data_pipe_to_source_stream.h",
     "loader/download_utils_impl.cc",
     "loader/download_utils_impl.h",
+    "loader/file_url_loader_factory.cc",
+    "loader/file_url_loader_factory.h",
     "loader/merkle_integrity_source_stream.cc",
     "loader/merkle_integrity_source_stream.h",
     "loader/navigation_loader_interceptor.cc",
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 0b4ee87..b106731 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -347,7 +347,6 @@
       service_manager::switches::kDisableInProcessStackTraces,
       switches::kDisableBestEffortTasks,
       switches::kDisableLogging,
-      switches::kDisablePerfetto,
       switches::kEnableLogging,
       switches::kIPCConnectionTimeout,
       switches::kLogBestEffortTasks,
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 0b77194..53cf5c2c 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/shared_worker_instance.h"
+#include "content/public/common/content_features.h"
 #include "device/gamepad/gamepad_monitor.h"
 #include "device/gamepad/public/mojom/gamepad.mojom.h"
 #include "media/capture/mojom/image_capture.mojom.h"
@@ -37,9 +38,11 @@
 #include "third_party/blink/public/mojom/idle/idle_manager.mojom.h"
 #include "third_party/blink/public/mojom/keyboard_lock/keyboard_lock.mojom.h"
 #include "third_party/blink/public/mojom/locks/lock_manager.mojom.h"
+#include "third_party/blink/public/mojom/payments/payment_app.mojom.h"
 #include "third_party/blink/public/mojom/permissions/permission.mojom.h"
 #include "third_party/blink/public/mojom/picture_in_picture/picture_in_picture.mojom.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom.h"
+#include "third_party/blink/public/mojom/sms/sms_receiver.mojom.h"
 #include "third_party/blink/public/mojom/speech/speech_synthesis.mojom.h"
 #include "third_party/blink/public/mojom/wake_lock/wake_lock.mojom.h"
 #include "third_party/blink/public/mojom/webaudio/audio_context_manager.mojom.h"
@@ -53,7 +56,6 @@
 #endif
 
 #if defined(OS_ANDROID)
-#include "content/public/common/content_features.h"
 #include "services/device/public/mojom/nfc.mojom.h"
 #endif
 
@@ -113,6 +115,11 @@
   map->Add<blink::mojom::ScreenEnumeration>(
       base::BindRepeating(&ScreenEnumerationImpl::Create));
 
+  if (base::FeatureList::IsEnabled(features::kSmsReceiver)) {
+    map->Add<blink::mojom::SmsReceiver>(base::BindRepeating(
+        &RenderFrameHostImpl::BindSmsReceiverReceiver, base::Unretained(host)));
+  }
+
   map->Add<blink::mojom::LockManager>(base::BindRepeating(
       &RenderFrameHostImpl::CreateLockManager, base::Unretained(host)));
 
@@ -139,6 +146,10 @@
   map->Add<media::mojom::ImageCapture>(
       base::BindRepeating(&ImageCaptureImpl::Create));
 
+  map->Add<payments::mojom::PaymentManager>(
+      base::BindRepeating(&RenderProcessHost::CreatePaymentManager,
+                          base::Unretained(host->GetProcess())));
+
   map->Add<blink::mojom::WebBluetoothService>(base::BindRepeating(
       &RenderFrameHostImpl::CreateWebBluetoothService, base::Unretained(host)));
 
@@ -196,14 +207,20 @@
       &DedicatedWorkerHost::CreateIdleManager, base::Unretained(host)));
   map->Add<blink::mojom::ScreenEnumeration>(
       base::BindRepeating(&ScreenEnumerationImpl::Create));
+  if (base::FeatureList::IsEnabled(features::kSmsReceiver)) {
+    map->Add<blink::mojom::SmsReceiver>(base::BindRepeating(
+        &DedicatedWorkerHost::BindSmsReceiverReceiver, base::Unretained(host)));
+  }
+  map->Add<payments::mojom::PaymentManager>(base::BindRepeating(
+      &DedicatedWorkerHost::CreatePaymentManager, base::Unretained(host)));
 }
 
 void PopulateBinderMapWithContext(
     DedicatedWorkerHost* host,
     service_manager::BinderMapWithContext<const url::Origin&>* map) {
-  map->Add<blink::mojom::LockManager>(
-      base::BindRepeating(&RenderProcessHost::CreateLockManager,
-                          base::Unretained(host->GetProcessHost())));
+  map->Add<blink::mojom::LockManager>(base::BindRepeating(
+      &RenderProcessHost::CreateLockManager,
+      base::Unretained(host->GetProcessHost()), MSG_ROUTING_NONE));
   map->Add<blink::mojom::PermissionService>(
       base::BindRepeating(&RenderProcessHost::CreatePermissionService,
                           base::Unretained(host->GetProcessHost())));
@@ -227,6 +244,8 @@
       &SharedWorkerHost::CreateAppCacheBackend, base::Unretained(host)));
   map->Add<blink::mojom::ScreenEnumeration>(
       base::BindRepeating(&ScreenEnumerationImpl::Create));
+  map->Add<payments::mojom::PaymentManager>(base::BindRepeating(
+      &SharedWorkerHost::CreatePaymentManager, base::Unretained(host)));
 }
 
 void PopulateBinderMapWithContext(
@@ -236,9 +255,9 @@
   map->Add<blink::mojom::FileSystemManager>(
       base::BindRepeating(&RenderProcessHost::BindFileSystemManager,
                           base::Unretained(host->GetProcessHost())));
-  map->Add<blink::mojom::LockManager>(
-      base::BindRepeating(&RenderProcessHost::CreateLockManager,
-                          base::Unretained(host->GetProcessHost())));
+  map->Add<blink::mojom::LockManager>(base::BindRepeating(
+      &RenderProcessHost::CreateLockManager,
+      base::Unretained(host->GetProcessHost()), MSG_ROUTING_NONE));
   map->Add<blink::mojom::PermissionService>(
       base::BindRepeating(&RenderProcessHost::CreatePermissionService,
                           base::Unretained(host->GetProcessHost())));
@@ -268,6 +287,9 @@
   map->Add<blink::mojom::PermissionService>(
       base::BindRepeating(&ServiceWorkerProviderHost::CreatePermissionService,
                           base::Unretained(host)));
+  map->Add<payments::mojom::PaymentManager>(
+      base::BindRepeating(&ServiceWorkerProviderHost::CreatePaymentManager,
+                          base::Unretained(host)));
 }
 
 void PopulateBinderMapWithContext(
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 4196b270..b25def1 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -114,7 +114,7 @@
   return content::BrowserMainLoop::GetInstance()->GetFrameSinkManager();
 }
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 class HostDisplayClient : public viz::HostDisplayClient {
  public:
   explicit HostDisplayClient(ui::Compositor* compositor)
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.cc b/content/browser/compositor/software_browser_compositor_output_surface.cc
index 31b6c038..b7938e45 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface.cc
+++ b/content/browser/compositor/software_browser_compositor_output_surface.cc
@@ -89,7 +89,7 @@
     const gfx::Size& pixel_size) {
   latency_tracker_.OnGpuSwapBuffersCompleted(latency_info);
   client_->DidReceiveSwapBuffersAck({swap_time, swap_time});
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   if (needs_swap_size_notifications_)
     client_->DidSwapWithSize(pixel_size);
 #endif
@@ -129,7 +129,7 @@
   return 0;
 }
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 void SoftwareBrowserCompositorOutputSurface::SetNeedsSwapSizeNotifications(
     bool needs_swap_size_notifications) {
   needs_swap_size_notifications_ = needs_swap_size_notifications;
diff --git a/content/browser/compositor/software_browser_compositor_output_surface.h b/content/browser/compositor/software_browser_compositor_output_surface.h
index 45fed95..164289b 100644
--- a/content/browser/compositor/software_browser_compositor_output_surface.h
+++ b/content/browser/compositor/software_browser_compositor_output_surface.h
@@ -39,7 +39,7 @@
   gfx::BufferFormat GetOverlayBufferFormat() const override;
   uint32_t GetFramebufferCopyTextureFormat() override;
   unsigned UpdateGpuFence() override;
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   void SetNeedsSwapSizeNotifications(
       bool needs_swap_size_notifications) override;
 #endif
@@ -55,7 +55,7 @@
   base::TimeDelta refresh_interval_;
   ui::LatencyTracker latency_tracker_;
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   bool needs_swap_size_notifications_ = false;
 #endif
 
diff --git a/content/browser/devtools/protocol/tracing_handler.cc b/content/browser/devtools/protocol/tracing_handler.cc
index 7730d55d..0cd0ca1 100644
--- a/content/browser/devtools/protocol/tracing_handler.cc
+++ b/content/browser/devtools/protocol/tracing_handler.cc
@@ -249,46 +249,6 @@
   DISALLOW_COPY_AND_ASSIGN(TracingSession);
 };
 
-class TracingHandler::LegacyTracingSession
-    : public TracingHandler::TracingSession {
- public:
-  void EnableTracing(const base::trace_event::TraceConfig& chrome_config,
-                     base::OnceClosure on_recording_enabled_callback) override {
-    DCHECK(!TracingController::GetInstance()->IsTracing());
-    TracingController::GetInstance()->StartTracing(
-        chrome_config, std::move(on_recording_enabled_callback));
-  }
-
-  void AdoptStartupTracingSession() override {
-    // Nothing to do for legacy tracing here (tracing is already active).
-    DCHECK(TracingController::GetInstance()->IsTracing());
-  }
-
-  void ChangeTraceConfig(
-      const base::trace_event::TraceConfig& chrome_config) override {
-    TracingController::GetInstance()->StartTracing(
-        chrome_config, TracingController::StartTracingDoneCallback());
-  }
-
-  void DisableTracing(bool use_proto_format,
-                      const std::string& agent_label,
-                      const scoped_refptr<TracingController::TraceDataEndpoint>&
-                          endpoint) override {
-    DCHECK(!use_proto_format);
-    TracingController::GetInstance()->StopTracing(endpoint, agent_label);
-  }
-
-  void GetBufferUsage(base::OnceCallback<void(float percent_full,
-                                              size_t approximate_event_count)>
-                          on_buffer_usage_callback) override {
-    TracingController::GetInstance()->GetTraceBufferUsage(
-        std::move(on_buffer_usage_callback));
-  }
-
-  bool HasTracingFailed() override { return false; }
-  bool HasDataLossOccurred() override { return false; }
-};
-
 class TracingHandler::PerfettoTracingSession
     : public TracingHandler::TracingSession,
       public tracing::mojom::TracingSessionClient,
@@ -586,7 +546,6 @@
     return;
   }
 
-  DCHECK(tracing::TracingUsesPerfettoBackend());
   session_ = std::make_unique<PerfettoTracingSession>();
   session_->AdoptStartupTracingSession();
   g_any_agent_tracing = true;
@@ -743,12 +702,6 @@
   bool proto_format =
       transfer_format.fromMaybe("") == Tracing::StreamFormatEnum::Proto;
 
-  if (proto_format && !tracing::TracingUsesPerfettoBackend()) {
-    callback->sendFailure(Response::Error(
-        "Proto format is only supported with the perfetto backend."));
-    return;
-  }
-
   if (proto_format && !return_as_stream) {
     callback->sendFailure(Response::Error(
         "Proto format is only supported when using stream transfer mode."));
@@ -821,11 +774,7 @@
 
   SetupProcessFilter(gpu_pid, nullptr);
 
-  if (tracing::TracingUsesPerfettoBackend()) {
-    session_ = std::make_unique<PerfettoTracingSession>();
-  } else {
-    session_ = std::make_unique<LegacyTracingSession>();
-  }
+  session_ = std::make_unique<PerfettoTracingSession>();
   session_->EnableTracing(
       trace_config_,
       base::BindOnce(&TracingHandler::OnRecordingEnabled,
@@ -884,16 +833,6 @@
 }
 
 Response TracingHandler::End() {
-  // Startup tracing triggered by --trace-config-file is a special case, where
-  // tracing is started automatically upon browser startup and can be stopped
-  // via DevTools.
-  // TODO(eseckler): Remove this when we remove the legacy tracing backend.
-  if (!tracing::TracingUsesPerfettoBackend() && IsStartupTracingActive()) {
-    DCHECK(!session_ && !did_initiate_recording_);
-    session_ = std::make_unique<LegacyTracingSession>();
-    session_->AdoptStartupTracingSession();
-  }
-
   if (!session_)
     return Response::Error("Tracing is not started");
 
@@ -972,20 +911,25 @@
 }
 
 void TracingHandler::RequestMemoryDump(
+    Maybe<bool> deterministic,
     std::unique_ptr<RequestMemoryDumpCallback> callback) {
   if (!IsTracing()) {
     callback->sendFailure(Response::Error("Tracing is not started"));
     return;
   }
 
+  auto determinism = deterministic.fromMaybe(false)
+                         ? base::trace_event::MemoryDumpDeterminism::FORCE_GC
+                         : base::trace_event::MemoryDumpDeterminism::NONE;
+
   auto on_memory_dump_finished =
       base::BindOnce(&TracingHandler::OnMemoryDumpFinished,
                      weak_factory_.GetWeakPtr(), std::move(callback));
+
   memory_instrumentation::MemoryInstrumentation::GetInstance()
       ->RequestGlobalDumpAndAppendToTrace(
           base::trace_event::MemoryDumpType::EXPLICITLY_TRIGGERED,
-          base::trace_event::MemoryDumpLevelOfDetail::DETAILED,
-          base::trace_event::MemoryDumpDeterminism::NONE,
+          base::trace_event::MemoryDumpLevelOfDetail::DETAILED, determinism,
           std::move(on_memory_dump_finished));
 }
 
diff --git a/content/browser/devtools/protocol/tracing_handler.h b/content/browser/devtools/protocol/tracing_handler.h
index c83b585..5dcfe36 100644
--- a/content/browser/devtools/protocol/tracing_handler.h
+++ b/content/browser/devtools/protocol/tracing_handler.h
@@ -73,6 +73,7 @@
   Response End() override;
   void GetCategories(std::unique_ptr<GetCategoriesCallback> callback) override;
   void RequestMemoryDump(
+      Maybe<bool> deterministic,
       std::unique_ptr<RequestMemoryDumpCallback> callback) override;
   Response RecordClockSyncMarker(const std::string& sync_id) override;
 
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 15989dd..17c3898a 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -40,8 +40,8 @@
 #include "content/browser/data_url_loader_factory.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/download/network_download_url_loader_factory_info.h"
-#include "content/browser/file_url_loader_factory.h"
 #include "content/browser/fileapi/file_system_url_loader_factory.h"
+#include "content/browser/loader/file_url_loader_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
diff --git a/content/browser/download/save_file_manager.cc b/content/browser/download/save_file_manager.cc
index 61dc065f..92c718d 100644
--- a/content/browser/download/save_file_manager.cc
+++ b/content/browser/download/save_file_manager.cc
@@ -15,8 +15,8 @@
 #include "content/browser/data_url_loader_factory.h"
 #include "content/browser/download/save_file.h"
 #include "content/browser/download/save_package.h"
-#include "content/browser/file_url_loader_factory.h"
 #include "content/browser/fileapi/file_system_url_loader_factory.h"
+#include "content/browser/loader/file_url_loader_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 6764afb..d85525b 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -45,7 +45,6 @@
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/dom_storage/dom_storage_context_wrapper.h"
 #include "content/browser/download/mhtml_generation_manager.h"
-#include "content/browser/file_url_loader_factory.h"
 #include "content/browser/fileapi/file_system_manager_impl.h"
 #include "content/browser/fileapi/file_system_url_loader_factory.h"
 #include "content/browser/frame_host/back_forward_cache_impl.h"
@@ -66,6 +65,7 @@
 #include "content/browser/generic_sensor/sensor_provider_proxy_impl.h"
 #include "content/browser/geolocation/geolocation_service_impl.h"
 #include "content/browser/interface_provider_filtering.h"
+#include "content/browser/loader/file_url_loader_factory.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
 #include "content/browser/loader/prefetch_url_loader_service.h"
 #include "content/browser/log_console_message.h"
@@ -1063,6 +1063,13 @@
   if (owned_render_widget_host_)
     owned_render_widget_host_->ShutdownAndDestroyWidget(false);
 
+  // TODO(https://crbug.com/1005077): There is no known reason for removing the
+  // RenderViewHostImpl here instead of automatically at the end of the
+  // destructor. In practise, not doing it here will prevent android WebView to
+  // display a new page after a long sequence of WebView creation / deletion.
+  // The real reason why this is needed needs to be investigated.
+  render_view_host_.reset();
+
   // If another frame is waiting for a beforeunload ACK from this frame,
   // simulate it now.
   RenderFrameHostImpl* beforeunload_initiator = GetBeforeUnloadInitiator();
@@ -4471,11 +4478,6 @@
   registry_->AddInterface(base::BindRepeating(
       &GetRestrictedCookieManager, base::Unretained(this),
       GetProcess()->GetID(), routing_id_, GetProcess()->GetStoragePartition()));
-
-  if (base::FeatureList::IsEnabled(features::kSmsReceiver)) {
-    registry_->AddInterface(base::BindRepeating(
-        &RenderFrameHostImpl::BindSmsReceiverReceiver, base::Unretained(this)));
-  }
 }
 
 void RenderFrameHostImpl::ResetWaitingState() {
@@ -6445,7 +6447,7 @@
 
 void RenderFrameHostImpl::CreateLockManager(
     mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
-  GetProcess()->CreateLockManager(GetLastCommittedOrigin(),
+  GetProcess()->CreateLockManager(GetRoutingID(), GetLastCommittedOrigin(),
                                   std::move(receiver));
 }
 
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 06792b6..91251c4 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1110,6 +1110,9 @@
   void BindNFCReceiver(mojo::PendingReceiver<device::mojom::NFC> receiver);
 #endif
 
+  void BindSmsReceiverReceiver(
+      mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver);
+
   // https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global
   network::mojom::CrossOriginEmbedderPolicy cross_origin_embedder_policy()
       const {
@@ -1611,9 +1614,6 @@
       mojo::PendingReceiver<blink::mojom::Authenticator> receiver);
 #endif
 
-  void BindSmsReceiverReceiver(
-      mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver);
-
   // service_manager::mojom::InterfaceProvider:
   void GetInterface(const std::string& interface_name,
                     mojo::ScopedMessagePipeHandle interface_pipe) override;
@@ -1892,7 +1892,7 @@
   //
   // TODO(creis): RenderViewHost will eventually go away and be replaced with
   // some form of page context.
-  const scoped_refptr<RenderViewHostImpl> render_view_host_;
+  scoped_refptr<RenderViewHostImpl> render_view_host_;
 
   RenderFrameHostDelegate* const delegate_;
 
diff --git a/content/browser/file_url_loader_factory.cc b/content/browser/loader/file_url_loader_factory.cc
similarity index 99%
rename from content/browser/file_url_loader_factory.cc
rename to content/browser/loader/file_url_loader_factory.cc
index 4fedf44..d5c926c 100644
--- a/content/browser/file_url_loader_factory.cc
+++ b/content/browser/loader/file_url_loader_factory.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 "content/browser/file_url_loader_factory.h"
+#include "content/browser/loader/file_url_loader_factory.h"
 
 #include <memory>
 #include <string>
@@ -491,6 +491,13 @@
       return;
     }
 
+    if (file_access_policy == FileAccessPolicy::kRestricted &&
+        !GetContentClient()->browser()->IsFileAccessAllowed(
+            path, base::MakeAbsoluteFilePath(path), profile_path)) {
+      OnClientComplete(net::ERR_ACCESS_DENIED, std::move(observer));
+      return;
+    }
+
 #if defined(OS_WIN)
     base::FilePath shortcut_target;
     if (link_following_policy == LinkFollowingPolicy::kFollow &&
@@ -529,13 +536,6 @@
     }
 #endif  // defined(OS_WIN)
 
-    if (file_access_policy == FileAccessPolicy::kRestricted &&
-        !GetContentClient()->browser()->IsFileAccessAllowed(
-            path, base::MakeAbsoluteFilePath(path), profile_path)) {
-      OnClientComplete(net::ERR_ACCESS_DENIED, std::move(observer));
-      return;
-    }
-
     mojo::DataPipe pipe(kDefaultFileUrlPipeSize);
     if (!pipe.consumer_handle.is_valid()) {
       OnClientComplete(net::ERR_FAILED, std::move(observer));
diff --git a/content/browser/file_url_loader_factory.h b/content/browser/loader/file_url_loader_factory.h
similarity index 93%
rename from content/browser/file_url_loader_factory.h
rename to content/browser/loader/file_url_loader_factory.h
index 90a1dbb..fb6f236b 100644
--- a/content/browser/file_url_loader_factory.h
+++ b/content/browser/loader/file_url_loader_factory.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 CONTENT_BROWSER_FILE_URL_LOADER_FACTORY_H_
-#define CONTENT_BROWSER_FILE_URL_LOADER_FACTORY_H_
+#ifndef CONTENT_BROWSER_LOADER_FILE_URL_LOADER_FACTORY_H_
+#define CONTENT_BROWSER_LOADER_FILE_URL_LOADER_FACTORY_H_
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
@@ -67,4 +67,4 @@
 
 }  // namespace content
 
-#endif  // CONTENT_BROWSER_FILE_URL_LOADER_FACTORY_H_
+#endif  // CONTENT_BROWSER_LOADER_FILE_URL_LOADER_FACTORY_H_
diff --git a/content/browser/loader/file_url_loader_factory_browsertest.cc b/content/browser/loader/file_url_loader_factory_browsertest.cc
new file mode 100644
index 0000000..f1a303c
--- /dev/null
+++ b/content/browser/loader/file_url_loader_factory_browsertest.cc
@@ -0,0 +1,316 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This must be before Windows headers
+#include "base/bind_helpers.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <objbase.h>
+#include <shlobj.h>
+#include <windows.h>
+#include <wrl/client.h>
+#endif
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/macros.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "content/shell/browser/shell.h"
+#include "content/test/test_content_browser_client.h"
+#include "net/base/filename_util.h"
+#include "net/base/net_errors.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/gtest_util.h"
+#include "url/gurl.h"
+
+namespace content {
+namespace {
+
+const char kSuccessTitle[] = "Title Of Awesomeness";
+const char kErrorTitle[] = "Error";
+
+base::FilePath TestFilePath() {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  return GetTestFilePath("", "title2.html");
+}
+
+base::FilePath AbsoluteFilePath(const base::FilePath& file_path) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  return base::MakeAbsoluteFilePath(file_path);
+}
+
+class TestFileAccessContentBrowserClient : public TestContentBrowserClient {
+ public:
+  struct FileAccessAllowedArgs {
+    base::FilePath path;
+    base::FilePath absolute_path;
+    base::FilePath profile_path;
+  };
+
+  TestFileAccessContentBrowserClient() {
+    old_content_browser_client_ = SetBrowserClientForTesting(this);
+  }
+
+  void set_blocked_path(const base::FilePath& blocked_path) {
+    blocked_path_ = AbsoluteFilePath(blocked_path);
+  }
+
+  ~TestFileAccessContentBrowserClient() override {
+    EXPECT_EQ(this, SetBrowserClientForTesting(old_content_browser_client_));
+  }
+
+  bool IsFileAccessAllowed(const base::FilePath& path,
+                           const base::FilePath& absolute_path,
+                           const base::FilePath& profile_path) override {
+    access_allowed_args_.push_back(
+        FileAccessAllowedArgs{path, absolute_path, profile_path});
+    return blocked_path_ != absolute_path;
+  }
+
+  // Returns a vector of arguments passed to each invocation of
+  // IsFileAccessAllowed().
+  const std::vector<FileAccessAllowedArgs>& access_allowed_args() const {
+    return access_allowed_args_;
+  }
+
+  void ClearAccessAllowedArgs() { access_allowed_args_.clear(); }
+
+ private:
+  ContentBrowserClient* old_content_browser_client_;
+
+  base::FilePath blocked_path_;
+
+  std::vector<FileAccessAllowedArgs> access_allowed_args_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestFileAccessContentBrowserClient);
+};
+
+// This class contains integration tests for file URLs.
+class FileURLLoaderFactoryBrowserTest : public ContentBrowserTest {
+ public:
+  FileURLLoaderFactoryBrowserTest() {
+    EXPECT_TRUE(embedded_test_server()->Start());
+  }
+
+  base::FilePath ProfilePath() const {
+    return shell()
+        ->web_contents()
+        ->GetSiteInstance()
+        ->GetBrowserContext()
+        ->GetPath();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, Basic) {
+  TestFileAccessContentBrowserClient test_browser_client;
+  EXPECT_TRUE(NavigateToURL(shell(), net::FilePathToFileURL(TestFilePath())));
+  EXPECT_EQ(base::ASCIIToUTF16(kSuccessTitle),
+            shell()->web_contents()->GetTitle());
+
+  ASSERT_EQ(1u, test_browser_client.access_allowed_args().size());
+  EXPECT_EQ(TestFilePath(), test_browser_client.access_allowed_args()[0].path);
+  EXPECT_EQ(AbsoluteFilePath(TestFilePath()),
+            test_browser_client.access_allowed_args()[0].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[0].profile_path);
+}
+
+IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, FileAccessNotAllowed) {
+  TestFileAccessContentBrowserClient test_browser_client;
+  test_browser_client.set_blocked_path(TestFilePath());
+
+  TestNavigationObserver navigation_observer(shell()->web_contents());
+  EXPECT_FALSE(NavigateToURL(shell(), net::FilePathToFileURL(TestFilePath())));
+  EXPECT_FALSE(navigation_observer.last_navigation_succeeded());
+  EXPECT_THAT(navigation_observer.last_net_error_code(),
+              net::test::IsError(net::ERR_ACCESS_DENIED));
+  EXPECT_EQ(net::FilePathToFileURL(TestFilePath()),
+            shell()->web_contents()->GetURL());
+  EXPECT_EQ(base::ASCIIToUTF16(kErrorTitle),
+            shell()->web_contents()->GetTitle());
+
+  ASSERT_EQ(1u, test_browser_client.access_allowed_args().size());
+  EXPECT_EQ(TestFilePath(), test_browser_client.access_allowed_args()[0].path);
+  EXPECT_EQ(AbsoluteFilePath(TestFilePath()),
+            test_browser_client.access_allowed_args()[0].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[0].profile_path);
+}
+
+#if defined(OS_POSIX)
+
+// Test symbolic links on POSIX platforms. These act like the contents of
+// the symbolic link are the same as the contents of the file it links to.
+IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, SymlinksToFiles) {
+  TestFileAccessContentBrowserClient test_browser_client;
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  // Get an absolute path since |temp_dir| can contain a symbolic link.
+  base::FilePath absolute_temp_dir = AbsoluteFilePath(temp_dir.GetPath());
+
+  // MIME sniffing uses the symbolic link's path, so this needs to end in
+  // ".html" for it to be sniffed as HTML.
+  base::FilePath sym_link = absolute_temp_dir.AppendASCII("link.html");
+  ASSERT_TRUE(
+      base::CreateSymbolicLink(AbsoluteFilePath(TestFilePath()), sym_link));
+
+  EXPECT_TRUE(NavigateToURL(shell(), net::FilePathToFileURL(sym_link)));
+  EXPECT_EQ(base::ASCIIToUTF16(kSuccessTitle),
+            shell()->web_contents()->GetTitle());
+
+  ASSERT_EQ(1u, test_browser_client.access_allowed_args().size());
+  EXPECT_EQ(sym_link, test_browser_client.access_allowed_args()[0].path);
+  EXPECT_EQ(AbsoluteFilePath(sym_link),
+            test_browser_client.access_allowed_args()[0].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[0].profile_path);
+
+  // Test the case where access to the destination URL is blocked. Note that
+  // this is the same as blocking the symbolic link URL - the
+  // IsFileAccessAllowed() is passed both the symbolic link path and the
+  // absolute path, so rejecting on looks just like rejecting the other.
+
+  test_browser_client.ClearAccessAllowedArgs();
+  test_browser_client.set_blocked_path(TestFilePath());
+
+  TestNavigationObserver navigation_observer3(shell()->web_contents());
+  EXPECT_FALSE(NavigateToURL(shell(), net::FilePathToFileURL(sym_link)));
+  EXPECT_FALSE(navigation_observer3.last_navigation_succeeded());
+  EXPECT_THAT(navigation_observer3.last_net_error_code(),
+              net::test::IsError(net::ERR_ACCESS_DENIED));
+  EXPECT_EQ(net::FilePathToFileURL(sym_link),
+            shell()->web_contents()->GetURL());
+  EXPECT_EQ(base::ASCIIToUTF16(kErrorTitle),
+            shell()->web_contents()->GetTitle());
+
+  ASSERT_EQ(1u, test_browser_client.access_allowed_args().size());
+  EXPECT_EQ(sym_link, test_browser_client.access_allowed_args()[0].path);
+  EXPECT_EQ(AbsoluteFilePath(sym_link),
+            test_browser_client.access_allowed_args()[0].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[0].profile_path);
+}
+
+#elif defined(OS_WIN)
+
+// Test shortcuts on Windows. These are treated as redirects.
+IN_PROC_BROWSER_TEST_F(FileURLLoaderFactoryBrowserTest, ResolveShortcutTest) {
+  TestFileAccessContentBrowserClient test_browser_client;
+
+  // Create an empty temp directory, to be sure there's no file in it.
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+  base::FilePath lnk_path =
+      temp_dir.GetPath().Append(FILE_PATH_LITERAL("foo.lnk"));
+
+  base::FilePath test = TestFilePath();
+
+  // Create a shortcut for the test.
+  {
+    Microsoft::WRL::ComPtr<IShellLink> shell;
+    ASSERT_TRUE(SUCCEEDED(::CoCreateInstance(
+        CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shell))));
+    Microsoft::WRL::ComPtr<IPersistFile> persist;
+    ASSERT_TRUE(SUCCEEDED(shell.As<IPersistFile>(&persist)));
+    EXPECT_TRUE(
+        SUCCEEDED(shell->SetPath(base::as_wcstr(TestFilePath().value()))));
+    EXPECT_TRUE(SUCCEEDED(shell->SetDescription(L"ResolveShortcutTest")));
+    base::string16 lnk_string = lnk_path.value();
+    EXPECT_TRUE(SUCCEEDED(persist->Save(base::as_wcstr(lnk_string), TRUE)));
+  }
+
+  EXPECT_TRUE(NavigateToURL(
+      shell(), net::FilePathToFileURL(lnk_path),
+      net::FilePathToFileURL(TestFilePath()) /* expect_commit_url */));
+  EXPECT_EQ(base::ASCIIToUTF16(kSuccessTitle),
+            shell()->web_contents()->GetTitle());
+
+  ASSERT_EQ(2u, test_browser_client.access_allowed_args().size());
+  EXPECT_EQ(lnk_path, test_browser_client.access_allowed_args()[0].path);
+  EXPECT_EQ(AbsoluteFilePath(lnk_path),
+            test_browser_client.access_allowed_args()[0].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[0].profile_path);
+
+  EXPECT_EQ(TestFilePath(), test_browser_client.access_allowed_args()[1].path);
+  EXPECT_EQ(AbsoluteFilePath(TestFilePath()),
+            test_browser_client.access_allowed_args()[1].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[1].profile_path);
+
+  // Test the case where access to the shortcut URL is blocked. Should display
+  // an error page at the shortcut's file URL.
+
+  test_browser_client.ClearAccessAllowedArgs();
+  test_browser_client.set_blocked_path(lnk_path);
+
+  TestNavigationObserver navigation_observer2(shell()->web_contents());
+  EXPECT_FALSE(NavigateToURL(shell(), net::FilePathToFileURL(lnk_path)));
+  EXPECT_FALSE(navigation_observer2.last_navigation_succeeded());
+  EXPECT_THAT(navigation_observer2.last_net_error_code(),
+              net::test::IsError(net::ERR_ACCESS_DENIED));
+  EXPECT_EQ(net::FilePathToFileURL(lnk_path),
+            shell()->web_contents()->GetURL());
+  EXPECT_EQ(base::ASCIIToUTF16(kErrorTitle),
+            shell()->web_contents()->GetTitle());
+
+  ASSERT_EQ(1u, test_browser_client.access_allowed_args().size());
+  EXPECT_EQ(lnk_path, test_browser_client.access_allowed_args()[0].path);
+  EXPECT_EQ(AbsoluteFilePath(lnk_path),
+            test_browser_client.access_allowed_args()[0].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[0].profile_path);
+
+  // Test the case where access to the destination URL is blocked. The redirect
+  // is followed, so this should end up at the shortcut destination, but
+  // displaying an error.
+
+  test_browser_client.ClearAccessAllowedArgs();
+  test_browser_client.set_blocked_path(TestFilePath());
+
+  TestNavigationObserver navigation_observer3(shell()->web_contents());
+  EXPECT_FALSE(NavigateToURL(shell(), net::FilePathToFileURL(lnk_path)));
+  EXPECT_FALSE(navigation_observer3.last_navigation_succeeded());
+  EXPECT_THAT(navigation_observer3.last_net_error_code(),
+              net::test::IsError(net::ERR_ACCESS_DENIED));
+  EXPECT_EQ(net::FilePathToFileURL(TestFilePath()),
+            shell()->web_contents()->GetURL());
+  EXPECT_EQ(base::ASCIIToUTF16(kErrorTitle),
+            shell()->web_contents()->GetTitle());
+
+  ASSERT_EQ(2u, test_browser_client.access_allowed_args().size());
+  EXPECT_EQ(lnk_path, test_browser_client.access_allowed_args()[0].path);
+  EXPECT_EQ(AbsoluteFilePath(lnk_path),
+            test_browser_client.access_allowed_args()[0].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[0].profile_path);
+
+  EXPECT_EQ(TestFilePath(), test_browser_client.access_allowed_args()[1].path);
+  EXPECT_EQ(AbsoluteFilePath(TestFilePath()),
+            test_browser_client.access_allowed_args()[1].absolute_path);
+  EXPECT_EQ(ProfilePath(),
+            test_browser_client.access_allowed_args()[1].profile_path);
+}
+
+#endif  // defined(OS_WIN)
+
+}  // namespace
+}  // namespace content
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 3d607d0..e40a0b3 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -24,10 +24,10 @@
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/data_url_loader_factory.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
-#include "content/browser/file_url_loader_factory.h"
 #include "content/browser/fileapi/file_system_url_loader_factory.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/navigation_request_info.h"
+#include "content/browser/loader/file_url_loader_factory.h"
 #include "content/browser/loader/navigation_loader_interceptor.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "content/browser/loader/prefetch_url_loader_service.h"
diff --git a/content/browser/locks/lock_manager.cc b/content/browser/locks/lock_manager.cc
index a927af3..d51862d 100644
--- a/content/browser/locks/lock_manager.cc
+++ b/content/browser/locks/lock_manager.cc
@@ -307,7 +307,9 @@
   LockManager* lock_manager_;
 };
 
-void LockManager::CreateService(
+void LockManager::BindReceiver(
+    int render_process_id,
+    int render_frame_id,
     const url::Origin& origin,
     mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -316,7 +318,8 @@
   // and be the same opaque string seen in Service Worker client ids.
   const std::string client_id = base::GenerateGUID();
 
-  receivers_.Add(this, std::move(receiver), {origin, client_id});
+  receivers_.Add(this, std::move(receiver),
+                 {client_id, render_process_id, render_frame_id, origin});
 }
 
 void LockManager::RequestLock(
diff --git a/content/browser/locks/lock_manager.h b/content/browser/locks/lock_manager.h
index af713df47..25ed9c95 100644
--- a/content/browser/locks/lock_manager.h
+++ b/content/browser/locks/lock_manager.h
@@ -26,8 +26,13 @@
  public:
   LockManager();
 
-  void CreateService(const url::Origin& origin,
-                     mojo::PendingReceiver<blink::mojom::LockManager> receiver);
+  // Binds |receiver| to this LockManager. |receiver| belongs to a frame or
+  // worker at |origin| hosted by |render_process_id|. If it belongs to a frame,
+  // |render_frame_id| identifies it, otherwise it is MSG_ROUTING_NONE.
+  void BindReceiver(int render_process_id,
+                    int render_frame_id,
+                    const url::Origin& origin,
+                    mojo::PendingReceiver<blink::mojom::LockManager> receiver);
 
   // Request a lock. When the lock is acquired, |callback| will be invoked with
   // a LockHandle.
@@ -56,8 +61,17 @@
 
   // State for each client held in |receivers_|.
   struct ReceiverState {
-    url::Origin origin;
     std::string client_id;
+
+    // Process owning this receiver.
+    int render_process_id;
+
+    // Frame owning this receiver. MSG_ROUTING_NONE if the receiver is owned by
+    // a worker.
+    int render_frame_id;
+
+    // Origin of the frame or worker owning this receiver.
+    url::Origin origin;
   };
 
   bool IsGrantable(const url::Origin& origin,
diff --git a/content/browser/payments/payment_manager.cc b/content/browser/payments/payment_manager.cc
index 9ffb7b6a..1739040 100644
--- a/content/browser/payments/payment_manager.cc
+++ b/content/browser/payments/payment_manager.cc
@@ -25,13 +25,13 @@
 
 PaymentManager::PaymentManager(
     PaymentAppContextImpl* payment_app_context,
-    mojo::InterfaceRequest<payments::mojom::PaymentManager> request)
+    mojo::PendingReceiver<payments::mojom::PaymentManager> receiver)
     : payment_app_context_(payment_app_context),
-      binding_(this, std::move(request)) {
+      receiver_(this, std::move(receiver)) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   DCHECK(payment_app_context);
 
-  binding_.set_connection_error_handler(base::BindOnce(
+  receiver_.set_disconnect_handler(base::BindOnce(
       &PaymentManager::OnConnectionError, weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -43,15 +43,15 @@
   scope_ = GURL(scope);
 
   if (!context_url_.is_valid()) {
-    binding_.CloseWithReason(0U, "Invalid context URL.");
+    receiver_.ResetWithReason(0U, "Invalid context URL.");
     return;
   }
   if (!scope_.is_valid()) {
-    binding_.CloseWithReason(1U, "Invalid scope URL.");
+    receiver_.ResetWithReason(1U, "Invalid scope URL.");
     return;
   }
   if (!url::IsSameOriginWith(context_url_, scope_)) {
-    binding_.CloseWithReason(
+    receiver_.ResetWithReason(
         2U, "Scope URL is not from the same origin of the context URL.");
     return;
   }
diff --git a/content/browser/payments/payment_manager.h b/content/browser/payments/payment_manager.h
index 3d0029f..1c778a36 100644
--- a/content/browser/payments/payment_manager.h
+++ b/content/browser/payments/payment_manager.h
@@ -10,7 +10,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "content/common/content_export.h"
-#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/mojom/payments/payment_app.mojom.h"
 #include "url/gurl.h"
 
@@ -22,7 +23,7 @@
  public:
   PaymentManager(
       PaymentAppContextImpl* payment_app_context,
-      mojo::InterfaceRequest<payments::mojom::PaymentManager> request);
+      mojo::PendingReceiver<payments::mojom::PaymentManager> receiver);
 
   ~PaymentManager() override;
 
@@ -52,7 +53,7 @@
       const std::vector<payments::mojom::PaymentDelegation>& delegations,
       EnableDelegationsCallback callback) override;
 
-  // Called when an error is detected on binding_.
+  // Called when an error is detected on receiver_.
   void OnConnectionError();
 
   void SetPaymentInstrumentIntermediateCallback(
@@ -65,7 +66,7 @@
   bool should_set_payment_app_info_;
   GURL context_url_;
   GURL scope_;
-  mojo::Binding<payments::mojom::PaymentManager> binding_;
+  mojo::Receiver<payments::mojom::PaymentManager> receiver_;
   base::WeakPtrFactory<PaymentManager> weak_ptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(PaymentManager);
 };
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 2e38480..82ecd362 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -33,6 +33,7 @@
 #include "cc/base/switches.h"
 #include "cc/input/input_handler.h"
 #include "cc/layers/layer.h"
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
 #include "cc/resources/ui_resource_manager.h"
 #include "cc/trees/layer_tree_host.h"
@@ -701,6 +702,11 @@
   root_window_->OnCompositingDidCommit();
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+CompositorImpl::GetBeginMainFrameMetrics() {
+  return nullptr;
+}
+
 void CompositorImpl::AttachLayerForReadback(scoped_refptr<cc::Layer> layer) {
   readback_layer_tree_->AddChild(layer);
 }
diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h
index a4ad3e64..c799299 100644
--- a/content/browser/renderer_host/compositor_impl_android.h
+++ b/content/browser/renderer_host/compositor_impl_android.h
@@ -135,6 +135,8 @@
       const gfx::PresentationFeedback& feedback) override {}
   void RecordStartOfFrameMetrics() override {}
   void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override {}
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics()
+      override;
 
   // LayerTreeHostSingleThreadClient implementation.
   void DidSubmitCompositorFrame() override;
diff --git a/content/browser/renderer_host/input/fling_controller.cc b/content/browser/renderer_host/input/fling_controller.cc
index 7776167..02df77f 100644
--- a/content/browser/renderer_host/input/fling_controller.cc
+++ b/content/browser/renderer_host/input/fling_controller.cc
@@ -96,17 +96,22 @@
 
 bool FlingController::ObserveAndMaybeConsumeGestureEvent(
     const GestureEventWithLatencyInfo& gesture_event) {
+  TRACE_EVENT0("input", "FlingController::ObserveAndMaybeConsumeGestureEvent");
   // FlingCancel events arrive when a finger is touched down regardless of
   // whether there is an ongoing fling. These can affect state so if there's no
   // on-going fling we should just discard these without letting the rest of
   // the fling system see it.
   if (gesture_event.event.GetType() == WebInputEvent::kGestureFlingCancel &&
       !fling_curve_) {
+    TRACE_EVENT_INSTANT0("input", "NoActiveFling", TRACE_EVENT_SCOPE_THREAD);
     return true;
   }
 
-  if (ObserveAndFilterForTapSuppression(gesture_event))
+  if (ObserveAndFilterForTapSuppression(gesture_event)) {
+    TRACE_EVENT_INSTANT0("input", "FilterTapSuppression",
+                         TRACE_EVENT_SCOPE_THREAD);
     return true;
+  }
 
   if (gesture_event.event.GetType() == WebInputEvent::kGestureScrollUpdate) {
     last_seen_scroll_update_ = gesture_event.event.TimeStamp();
diff --git a/content/browser/renderer_host/input/gesture_event_queue.cc b/content/browser/renderer_host/input/gesture_event_queue.cc
index 412bb16..d318665 100644
--- a/content/browser/renderer_host/input/gesture_event_queue.cc
+++ b/content/browser/renderer_host/input/gesture_event_queue.cc
@@ -56,7 +56,6 @@
 
 bool GestureEventQueue::PassToFlingController(
     const GestureEventWithLatencyInfo& gesture_event) {
-  TRACE_EVENT0("input", "GestureEventQueue::QueueEvent");
   return fling_controller_.ObserveAndMaybeConsumeGestureEvent(gesture_event);
 }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 93d8f72..23ebf96 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -1941,11 +1941,12 @@
 }
 
 void RenderProcessHostImpl::CreateLockManager(
+    int render_frame_id,
     const url::Origin& origin,
     mojo::PendingReceiver<blink::mojom::LockManager> receiver) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  storage_partition_impl_->GetLockManager()->CreateService(origin,
-                                                           std::move(receiver));
+  storage_partition_impl_->GetLockManager()->BindReceiver(
+      GetID(), render_frame_id, origin, std::move(receiver));
 }
 
 void RenderProcessHostImpl::CreatePermissionService(
@@ -1956,6 +1957,13 @@
                                                       std::move(receiver));
 }
 
+void RenderProcessHostImpl::CreatePaymentManager(
+    mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) {
+  static_cast<StoragePartitionImpl*>(GetStoragePartition())
+      ->GetPaymentAppContext()
+      ->CreatePaymentManager(std::move(receiver));
+}
+
 void RenderProcessHostImpl::CancelProcessShutdownDelayForUnload() {
   if (IsKeepAliveRefCountDisabled())
     return;
@@ -3000,7 +3008,6 @@
     switches::kEnableDeJelly,
     switches::kDisableOriginTrialControlledBlinkFeatures,
     switches::kDisablePepper3DImageChromium,
-    switches::kDisablePerfetto,
     switches::kDisablePermissionsAPI,
     switches::kDisablePresentationAPI,
     switches::kDisableRGBA4444Textures,
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index b92bf6c3..a826a1e 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -505,10 +505,14 @@
     return file_system_manager_impl_.get();
   }
 
-  // Binds |receiver| to the LockManager instance owned by
-  // |storage_partition_impl_|, and is used by frames and workers via
-  // BrowserInterfaceBroker.
+  // Binds |receiver| to the LockManager owned by |storage_partition_impl_|.
+  // |receiver| belongs to a frame or worker at |origin| hosted by this process.
+  // If it belongs to a frame, |render_frame_id| identifies it, otherwise it is
+  // MSG_ROUTING_NONE.
+  //
+  // Used by frames and workers via BrowserInterfaceBroker.
   void CreateLockManager(
+      int render_frame_id,
       const url::Origin& origin,
       mojo::PendingReceiver<blink::mojom::LockManager> receiver) override;
 
@@ -519,6 +523,12 @@
       const url::Origin& origin,
       mojo::PendingReceiver<blink::mojom::PermissionService> receiver) override;
 
+  // Binds |receiver| to the PaymentManager instance owned by
+  // |storage_partition_impl_|, and is used by workers via
+  // BrowserInterfaceBroker.
+  void CreatePaymentManager(
+      mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) override;
+
   // Adds a CORB (Cross-Origin Read Blocking) exception for |process_id|.  The
   // exception will be removed when the corresponding RenderProcessHostImpl is
   // destroyed (see |cleanup_corb_exception_for_plugin_upon_destruction_|).
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 64577267..3e006ec 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -636,8 +636,10 @@
 }
 
 gfx::Size RenderWidgetHostViewAndroid::GetVisibleViewportSize() {
+  int pinned_bottom_adjust_dps =
+      std::max(0, (int)(view_.GetViewportInsetBottom() / view_.GetDipScale()));
   gfx::Rect requested_rect(GetRequestedRendererSize());
-  requested_rect.Inset(gfx::Insets(0, 0, view_.GetViewportInsetBottom(), 0));
+  requested_rect.Inset(gfx::Insets(0, 0, pinned_bottom_adjust_dps, 0));
   return requested_rect.size();
 }
 
diff --git a/content/browser/renderer_interface_binders.cc b/content/browser/renderer_interface_binders.cc
index aad43961..7e58737 100644
--- a/content/browser/renderer_interface_binders.cc
+++ b/content/browser/renderer_interface_binders.cc
@@ -16,7 +16,6 @@
 #include "content/browser/gpu/gpu_process_host.h"
 #include "content/browser/native_file_system/native_file_system_manager_impl.h"
 #include "content/browser/notifications/platform_notification_context_impl.h"
-#include "content/browser/payments/payment_manager.h"
 #include "content/browser/permissions/permission_service_context.h"
 #include "content/browser/quota_dispatcher_host.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
@@ -160,13 +159,6 @@
   parameterized_binder_registry_.AddInterface(
       base::BindRepeating(CreateWebSocketConnector));
 
-  parameterized_binder_registry_.AddInterface(base::Bind(
-      [](mojo::PendingReceiver<payments::mojom::PaymentManager> receiver,
-         RenderProcessHost* host, const url::Origin& origin) {
-        static_cast<StoragePartitionImpl*>(host->GetStoragePartition())
-            ->GetPaymentAppContext()
-            ->CreatePaymentManager(std::move(receiver));
-      }));
   parameterized_binder_registry_.AddInterface(base::BindRepeating(
       [](mojo::PendingReceiver<blink::mojom::CacheStorage> receiver,
          RenderProcessHost* host, const url::Origin& origin) {
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index 18b1987..3bdb94b 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -720,8 +720,6 @@
 
   params->worker_devtools_agent_route_id = MSG_ROUTING_NONE;
   params->wait_for_debugger = false;
-  params->v8_cache_options = GetV8CacheOptions();
-
   params->subresource_loader_updater =
       subresource_loader_updater_.BindNewPipeAndPassReceiver();
 
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 234e700..36ec9013 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -96,7 +96,7 @@
   if (!process)
     return;
 
-  process->CreateLockManager(origin, std::move(receiver));
+  process->CreateLockManager(MSG_ROUTING_NONE, origin, std::move(receiver));
 }
 
 void CreatePermissionServiceImpl(
@@ -111,6 +111,17 @@
   process->CreatePermissionService(origin, std::move(receiver));
 }
 
+void CreatePaymentManagerImpl(
+    int process_id,
+    mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* process = RenderProcessHost::FromID(process_id);
+  if (!process)
+    return;
+
+  process->CreatePaymentManager(std::move(receiver));
+}
+
 ServiceWorkerMetrics::EventType PurposeToEventType(
     blink::mojom::ControllerServiceWorkerPurpose purpose) {
   switch (purpose) {
@@ -1424,6 +1435,16 @@
                      render_process_id_, std::move(receiver)));
 }
 
+void ServiceWorkerProviderHost::CreatePaymentManager(
+    mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) {
+  DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+  DCHECK(IsProviderForServiceWorker());
+  RunOrPostTaskOnThread(
+      FROM_HERE, BrowserThread::UI,
+      base::BindOnce(&CreatePaymentManagerImpl, render_process_id_,
+                     std::move(receiver)));
+}
+
 void ServiceWorkerProviderHost::SetExecutionReady() {
   DCHECK(!is_execution_ready());
   TransitionToClientPhase(ClientPhase::kExecutionReady);
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index df3f161..f896495 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -36,7 +36,8 @@
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
 #include "third_party/blink/public/mojom/locks/lock_manager.mojom-forward.h"
-#include "third_party/blink/public/mojom/permissions/permission.mojom.h"
+#include "third_party/blink/public/mojom/payments/payment_app.mojom-forward.h"
+#include "third_party/blink/public/mojom/permissions/permission.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_container.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
@@ -431,6 +432,11 @@
   void CreatePermissionService(
       mojo::PendingReceiver<blink::mojom::PermissionService> receiver);
 
+  // For service worker execution contexts. Forwards |receiver| to the process
+  // host on the UI thread.
+  void CreatePaymentManager(
+      mojo::PendingReceiver<payments::mojom::PaymentManager> receiver);
+
  private:
   // For service worker clients. The flow is kInitial -> kResponseCommitted ->
   // kExecutionReady.
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index ad160d5d6..bbc5fc0 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -104,15 +104,6 @@
  protected:
   using FetchHandlerExistence = blink::mojom::FetchHandlerExistence;
 
-  struct RunningStateListener : public ServiceWorkerVersion::Observer {
-    RunningStateListener() : last_status(EmbeddedWorkerStatus::STOPPED) {}
-    ~RunningStateListener() override {}
-    void OnRunningStateChanged(ServiceWorkerVersion* version) override {
-      last_status = version->running_status();
-    }
-    EmbeddedWorkerStatus last_status;
-  };
-
   struct CachedMetadataUpdateListener : public ServiceWorkerVersion::Observer {
     CachedMetadataUpdateListener() = default;
     ~CachedMetadataUpdateListener() override = default;
diff --git a/content/browser/tracing/background_tracing_active_scenario.cc b/content/browser/tracing/background_tracing_active_scenario.cc
index fb390be..5014f622 100644
--- a/content/browser/tracing/background_tracing_active_scenario.cc
+++ b/content/browser/tracing/background_tracing_active_scenario.cc
@@ -81,8 +81,7 @@
 #if !defined(OS_ANDROID)
     // TODO(crbug.com/941318): Re-enable startup tracing for Android once all
     // Perfetto-related deadlocks are resolved.
-    if (!TracingControllerImpl::GetInstance()->IsTracing() &&
-        tracing::TracingUsesPerfettoBackend()) {
+    if (!TracingControllerImpl::GetInstance()->IsTracing()) {
       tracing::TraceEventDataSource::GetInstance()->SetupStartupTracing(
           /*privacy_filtering_enabled=*/true);
     }
@@ -196,8 +195,7 @@
 #if !defined(OS_ANDROID)
     // TODO(crbug.com/941318): Re-enable startup tracing for Android once all
     // Perfetto-related deadlocks are resolved.
-    if (!TracingControllerImpl::GetInstance()->IsTracing() &&
-        tracing::TracingUsesPerfettoBackend()) {
+    if (!TracingControllerImpl::GetInstance()->IsTracing()) {
       tracing::TraceEventDataSource::GetInstance()->SetupStartupTracing(
           /*privacy_filtering_enabled=*/false);
     }
diff --git a/content/browser/tracing/background_tracing_manager_browsertest.cc b/content/browser/tracing/background_tracing_manager_browsertest.cc
index 390d9b1..fd1a1b96 100644
--- a/content/browser/tracing/background_tracing_manager_browsertest.cc
+++ b/content/browser/tracing/background_tracing_manager_browsertest.cc
@@ -1616,8 +1616,7 @@
  public:
   ProtoBackgroundTracingTest() {
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kTracingPerfettoBackend,
-                              features::kBackgroundTracingProtoOutput},
+        /*enabled_features=*/{features::kBackgroundTracingProtoOutput},
         /*disabled_features=*/{});
   }
 
diff --git a/content/browser/tracing/startup_tracing_browsertest.cc b/content/browser/tracing/startup_tracing_browsertest.cc
index 75fead8..f03ea41 100644
--- a/content/browser/tracing/startup_tracing_browsertest.cc
+++ b/content/browser/tracing/startup_tracing_browsertest.cc
@@ -94,8 +94,7 @@
  public:
   StartupTracingInProcessTest() {
     scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{features::kTracingPerfettoBackend,
-                              features::kTracingServiceInProcess},
+        /*enabled_features=*/{features::kTracingServiceInProcess},
         /*disabled_features=*/{});
   }
 
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index 0123f12..a1a4c59 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -234,16 +234,6 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto metadata_dict = std::make_unique<base::DictionaryValue>();
 
-  // trace_config_ can be null if the tracing controller finishes flushing
-  // traces before the Chrome tracing agent finishes flushing traces. Normally,
-  // this does not happen; however, if the service manager is teared down during
-  // tracing, e.g. at Chrome shutdown, tracing controller may finish flushing
-  // traces without waiting for tracing agents.
-  if (trace_config_ && !tracing::TracingUsesPerfettoBackend()) {
-    DCHECK(IsTracing());
-    metadata_dict->SetString("trace-config", trace_config_->ToString());
-  }
-
   metadata_dict->SetString("network-type", GetNetworkTypeString());
   metadata_dict->SetString("product-version",
                            GetContentClient()->browser()->GetProduct());
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index c0d8053..5c996179 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3667,13 +3667,15 @@
       render_view_host->GetRoutingID(), history_offset, history_length));
 }
 
-void WebContentsImpl::ReloadFocusedFrame(bool bypass_cache) {
+void WebContentsImpl::ReloadFocusedFrame() {
   RenderFrameHost* focused_frame = GetFocusedFrame();
   if (!focused_frame)
     return;
 
-  focused_frame->Send(new FrameMsg_Reload(
-      focused_frame->GetRoutingID(), bypass_cache));
+  // TODO(https://crbug.com/995428). This function is deprecated. Navigations
+  // are handled from the browser process. There is no need to send an IPC to
+  // the renderer process for this.
+  focused_frame->Send(new FrameMsg_Reload(focused_frame->GetRoutingID()));
 }
 
 std::vector<mojo::Remote<blink::mojom::PauseSubresourceLoadingHandle>>
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index d260705..72f7570 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -395,7 +395,7 @@
   void Stop() override;
   void SetPageFrozen(bool frozen) override;
   std::unique_ptr<WebContents> Clone() override;
-  void ReloadFocusedFrame(bool bypass_cache) override;
+  void ReloadFocusedFrame() override;
   void Undo() override;
   void Redo() override;
   void Cut() override;
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 57848544..232de7e2 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -426,6 +426,28 @@
       ->CreateService(std::move(receiver));
 }
 
+void DedicatedWorkerHost::CreatePaymentManager(
+    mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderProcessHost* worker_process_host = GetProcessHost();
+  if (!worker_process_host)
+    return;
+  worker_process_host->CreatePaymentManager(std::move(receiver));
+}
+
+void DedicatedWorkerHost::BindSmsReceiverReceiver(
+    mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver) {
+  RenderFrameHostImpl* ancestor_render_frame_host =
+      GetAncestorRenderFrameHost();
+  if (!ancestor_render_frame_host) {
+    // The ancestor frame may have already been closed. In that case, the worker
+    // will soon be terminated too, so abort the connection.
+    return;
+  }
+
+  ancestor_render_frame_host->BindSmsReceiverReceiver(std::move(receiver));
+}
+
 void DedicatedWorkerHost::ObserveNetworkServiceCrash(
     StoragePartitionImpl* storage_partition_impl) {
   auto params = network::mojom::URLLoaderFactoryParams::New();
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index bb0b7dc0..58c1d326 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -15,6 +15,8 @@
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom-forward.h"
 #include "third_party/blink/public/mojom/idle/idle_manager.mojom-forward.h"
+#include "third_party/blink/public/mojom/payments/payment_app.mojom-forward.h"
+#include "third_party/blink/public/mojom/sms/sms_receiver.mojom-forward.h"
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom-forward.h"
 #include "third_party/blink/public/mojom/websockets/websocket_connector.mojom-forward.h"
 #include "third_party/blink/public/mojom/worker/dedicated_worker_host.mojom.h"
@@ -68,6 +70,10 @@
       mojo::PendingReceiver<blink::mojom::FileSystemManager> receiver);
   void CreateIdleManager(
       mojo::PendingReceiver<blink::mojom::IdleManager> receiver);
+  void CreatePaymentManager(
+      mojo::PendingReceiver<payments::mojom::PaymentManager> receiver);
+  void BindSmsReceiverReceiver(
+      mojo::PendingReceiver<blink::mojom::SmsReceiver> receiver);
 
   // service_manager::mojom::InterfaceProvider:
   void GetInterface(const std::string& interface_name,
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index ee1860ef..7c6e733 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -347,6 +347,15 @@
       worker_process_host->GetID(), MSG_ROUTING_NONE, std::move(receiver));
 }
 
+void SharedWorkerHost::CreatePaymentManager(
+    mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  RenderProcessHost* worker_process_host = GetProcessHost();
+  if (!worker_process_host)
+    return;
+  worker_process_host->CreatePaymentManager(std::move(receiver));
+}
+
 void SharedWorkerHost::Destruct() {
   // Ask the service to destroy |this| which will terminate the worker.
   service_->DestroyHost(this);
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 1cb5ccfd..6ea3666 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -28,6 +28,7 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/service_manager/public/mojom/interface_provider.mojom.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
+#include "third_party/blink/public/mojom/payments/payment_app.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_client.mojom.h"
@@ -109,6 +110,8 @@
 
   void CreateAppCacheBackend(
       mojo::PendingReceiver<blink::mojom::AppCacheBackend> receiver);
+  void CreatePaymentManager(
+      mojo::PendingReceiver<payments::mojom::PaymentManager> receiver);
 
   // Causes this instance to be deleted, which will terminate the worker. May
   // be done based on a UI action.
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index 810415e..adaf93f 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -17,7 +17,7 @@
 #include "base/macros.h"
 #include "base/task/post_task.h"
 #include "content/browser/appcache/appcache_navigation_handle.h"
-#include "content/browser/file_url_loader_factory.h"
+#include "content/browser/loader/file_url_loader_factory.h"
 #include "content/browser/service_worker/service_worker_navigation_handle.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/url_loader_factory_getter.h"
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index ad639e3..634ecc39 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -16,9 +16,9 @@
 #include "base/task/post_task.h"
 #include "content/browser/appcache/appcache_navigation_handle.h"
 #include "content/browser/data_url_loader_factory.h"
-#include "content/browser/file_url_loader_factory.h"
 #include "content/browser/fileapi/file_system_url_loader_factory.h"
 #include "content/browser/loader/browser_initiated_resource_request.h"
+#include "content/browser/loader/file_url_loader_factory.h"
 #include "content/browser/navigation_subresource_loader_params.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_navigation_handle.h"
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index f6a5644..6b5e943 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -748,10 +748,9 @@
                     std::string /* message */,
                     bool /* discard_duplicates */)
 
-// Tells the renderer to reload the frame, optionally bypassing the cache while
-// doing so.
-IPC_MESSAGE_ROUTED1(FrameMsg_Reload,
-                    bool /* bypass_cache */)
+// TODO(https://crbug.com/995428): Deprecated.
+// Tells the renderer to reload the frame.
+IPC_MESSAGE_ROUTED0(FrameMsg_Reload)
 
 // Change the accessibility mode in the renderer process.
 IPC_MESSAGE_ROUTED1(FrameMsg_SetAccessibilityMode, ui::AXMode)
diff --git a/content/public/app/content_browser_manifest.cc b/content/public/app/content_browser_manifest.cc
index 4b6d406..b9c64b4 100644
--- a/content/public/app/content_browser_manifest.cc
+++ b/content/public/app/content_browser_manifest.cc
@@ -172,7 +172,6 @@
                   "blink.mojom.QuotaDispatcherHost",
                   "blink.mojom.WebSocketConnector",
                   "media.mojom.VideoDecodePerfHistory",
-                  "payments.mojom.PaymentManager",
                   "shape_detection.mojom.BarcodeDetectionProvider",
                   "shape_detection.mojom.FaceDetectionProvider",
                   "shape_detection.mojom.TextDetection"})
@@ -186,10 +185,9 @@
                   "blink.mojom.NotificationService",
                   "blink.mojom.QuotaDispatcherHost",
                   "blink.mojom.SerialService",
-                  "blink.mojom.WebUsbService", "blink.mojom.SmsReceiver",
+                  "blink.mojom.WebUsbService",
                   "blink.mojom.WebSocketConnector",
                   "media.mojom.VideoDecodePerfHistory",
-                  "payments.mojom.PaymentManager",
                   "shape_detection.mojom.BarcodeDetectionProvider",
                   "shape_detection.mojom.FaceDetectionProvider",
                   "shape_detection.mojom.TextDetection"})
@@ -204,7 +202,6 @@
                   "media.mojom.VideoDecodePerfHistory",
                   "network.mojom.RestrictedCookieManager",
                   "blink.mojom.WebSocketConnector",
-                  "payments.mojom.PaymentManager",
                   "shape_detection.mojom.BarcodeDetectionProvider",
                   "shape_detection.mojom.FaceDetectionProvider",
                   "shape_detection.mojom.TextDetection"})
@@ -231,7 +228,6 @@
                   "blink.mojom.QuotaDispatcherHost",
                   "blink.mojom.SerialService",
                   "blink.mojom.SharedWorkerConnector",
-                  "blink.mojom.SmsReceiver",
                   "blink.mojom.SpeechRecognizer",
                   "blink.mojom.TextSuggestionHost",
                   "blink.mojom.UnhandledTapNotifier",
@@ -252,7 +248,6 @@
                   "mojom.ProcessInternalsHandler",
                   "network.mojom.RestrictedCookieManager",
                   "blink.mojom.WebSocketConnector",
-                  "payments.mojom.PaymentManager",
                   "payments.mojom.PaymentRequest",
                   "resource_coordinator.mojom.DocumentCoordinationUnit",
                   "shape_detection.mojom.BarcodeDetectionProvider",
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index de63a79..1cdc600 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -497,6 +497,12 @@
 
   // Indicates whether a file path should be accessible via file URL given a
   // request from a browser context which lives within |profile_path|.
+  //
+  // On POSIX platforms, |absolute_path| is the path after resolving all
+  // symboling links. On Windows, if the file URL is a shortcut,
+  // IsFileAccessAllowed will be called twice: Once for the shortcut, which is
+  // treated like a redirect, and once for the destination path after following
+  // the shortcut, assuming access to the shortcut path was allowed.
   virtual bool IsFileAccessAllowed(const base::FilePath& path,
                                    const base::FilePath& absolute_path,
                                    const base::FilePath& profile_path);
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index 56eaf82..ae365a1e 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -35,7 +35,8 @@
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom-forward.h"
 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-forward.h"
 #include "third_party/blink/public/mojom/locks/lock_manager.mojom-forward.h"
-#include "third_party/blink/public/mojom/permissions/permission.mojom.h"
+#include "third_party/blink/public/mojom/payments/payment_app.mojom-forward.h"
+#include "third_party/blink/public/mojom/permissions/permission.mojom-forward.h"
 #include "ui/gfx/native_widget_types.h"
 
 #if defined(OS_ANDROID)
@@ -502,6 +503,7 @@
   // only, and is only exposed here to support MockRenderProcessHost usage in
   // tests.
   virtual void CreateLockManager(
+      int render_frame_id,
       const url::Origin& origin,
       mojo::PendingReceiver<blink::mojom::LockManager> receiver) = 0;
 
@@ -512,6 +514,12 @@
       const url::Origin& origin,
       mojo::PendingReceiver<blink::mojom::PermissionService> receiver) = 0;
 
+  // Binds |receiver| to an instance of PaymentManager. This is for internal
+  // use only, and is only exposed here to support MockRenderProcessHost usage
+  // in tests.
+  virtual void CreatePaymentManager(
+      mojo::PendingReceiver<payments::mojom::PaymentManager> receiver) = 0;
+
   // Returns the current number of active views in this process.  Excludes
   // any RenderViewHosts that are swapped out.
   size_t GetActiveViewCount();
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index dd1113e..6708e25 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -629,7 +629,7 @@
   virtual std::unique_ptr<WebContents> Clone() = 0;
 
   // Reloads the focused frame.
-  virtual void ReloadFocusedFrame(bool bypass_cache) = 0;
+  virtual void ReloadFocusedFrame() = 0;
 
   // Attains PauseSubresourceLoadingHandles for each frame in the web contents.
   // As long as these handles are not deleted, subresources will continue to be
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 1b6e80f4..1fc01f7 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -179,12 +179,16 @@
   void BindIndexedDB(mojo::PendingReceiver<blink::mojom::IDBFactory> receiver,
                      const url::Origin& origin) override;
   void CreateLockManager(
+      int render_frame_id,
       const url::Origin& origin,
       mojo::PendingReceiver<blink::mojom::LockManager> receiver) override {}
   void CreatePermissionService(
       const url::Origin& origin,
       mojo::PendingReceiver<blink::mojom::PermissionService> receiver)
       override {}
+  void CreatePaymentManager(
+      mojo::PendingReceiver<payments::mojom::PaymentManager> receiver)
+      override {}
   void CleanupCorbExceptionForPluginUponDestruction() override;
 
   // IPC::Sender via RenderProcessHost.
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index 1b4df2d..c6fac7a 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -267,6 +267,11 @@
   delegate_->RecordEndOfFrameMetrics(frame_begin_time);
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+LayerTreeView::GetBeginMainFrameMetrics() {
+  return delegate_->GetBeginMainFrameMetrics();
+}
+
 void LayerTreeView::DidSubmitCompositorFrame() {}
 
 void LayerTreeView::DidLoseLayerTreeFrameSink() {}
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index 07351680..fa20900 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -94,6 +94,8 @@
       const gfx::PresentationFeedback& feedback) override;
   void RecordStartOfFrameMetrics() override;
   void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override;
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics()
+      override;
 
   // cc::LayerTreeHostSingleThreadClient implementation.
   void DidSubmitCompositorFrame() override;
diff --git a/content/renderer/compositor/layer_tree_view_delegate.h b/content/renderer/compositor/layer_tree_view_delegate.h
index 667a4c0..028f472 100644
--- a/content/renderer/compositor/layer_tree_view_delegate.h
+++ b/content/renderer/compositor/layer_tree_view_delegate.h
@@ -14,6 +14,7 @@
 
 namespace cc {
 class LayerTreeFrameSink;
+struct BeginMainFrameMetrics;
 struct ElementId;
 }  // namespace cc
 
@@ -82,6 +83,14 @@
   // (at the same time Tracing measurements are taken).
   virtual void RecordStartOfFrameMetrics() = 0;
   virtual void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) = 0;
+  // Return metrics information for the stages of BeginMainFrame. This is
+  // ultimately implemented by Blink's LocalFrameUKMAggregator. It must be a
+  // distinct call from the FrameMetrics above because the BeginMainFrameMetrics
+  // for compositor latency must be gathered before the layer tree is
+  // committed to the compositor, which is before the call to
+  // RecordEndOfFrameMetrics.
+  virtual std::unique_ptr<cc::BeginMainFrameMetrics>
+  GetBeginMainFrameMetrics() = 0;
 
   // Notification of the beginning and end of LayerTreeHost::UpdateLayers, for
   // metrics collection.
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 488dcb4b..79cd638 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2943,9 +2943,8 @@
                                               msg_event);
 }
 
-void RenderFrameImpl::OnReload(bool bypass_cache) {
-  frame_->StartReload(bypass_cache ? WebFrameLoadType::kReloadBypassingCache
-                                   : WebFrameLoadType::kReload);
+void RenderFrameImpl::OnReload() {
+  frame_->StartReload(WebFrameLoadType::kReload);
 }
 
 bool RenderFrameImpl::RunJavaScriptDialog(JavaScriptDialogType type,
@@ -3140,12 +3139,6 @@
   return nullptr;
 }
 
-void RenderFrameImpl::LoadErrorPage(int reason) {
-  LoadNavigationErrorPage(frame_->GetDocumentLoader(),
-                          WebURLError(reason, frame_->GetDocument().Url()),
-                          base::nullopt, true /* replace_current_item */);
-}
-
 void RenderFrameImpl::ExecuteJavaScript(const base::string16& javascript) {
   JavaScriptExecuteRequest(javascript, false, base::DoNothing());
 }
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 676bdd48d..f1a995b 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -737,7 +737,6 @@
   void DownloadURL(const blink::WebURLRequest& request,
                    CrossOriginRedirects cross_origin_redirect_behavior,
                    mojo::ScopedMessagePipeHandle blob_url_token) override;
-  void LoadErrorPage(int reason) override;
   void BeginNavigation(std::unique_ptr<blink::WebNavigationInfo> info) override;
   void WillSendSubmitEvent(const blink::WebFormElement& form) override;
   void DidCreateDocumentLoader(
@@ -1138,7 +1137,8 @@
                              const std::string& message,
                              bool discard_duplicates);
   void OnVisualStateRequest(uint64_t key);
-  void OnReload(bool bypass_cache);
+  // TODO(https://crbug.com/995428): Deprecated.
+  void OnReload();
   void OnSetAccessibilityMode(ui::AXMode new_mode);
   void OnSnapshotAccessibilityTree(int callback_id, ui::AXMode ax_mode);
   void OnUpdateOpener(int opener_routing_id);
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index a27150f..32bac12f 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1545,6 +1545,13 @@
     GetWebWidget()->RecordEndOfFrameMetrics(frame_begin_time);
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+RenderWidget::GetBeginMainFrameMetrics() {
+  if (GetWebWidget())
+    return GetWebWidget()->GetBeginMainFrameMetrics();
+  return nullptr;
+}
+
 void RenderWidget::BeginUpdateLayers() {
   if (GetWebWidget())
     GetWebWidget()->BeginUpdateLayers();
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 12c3b27..82d0ab8 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -367,6 +367,9 @@
   void DidCompletePageScaleAnimation() override;
   void RecordStartOfFrameMetrics() override;
   void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override;
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics()
+      override;
+
   void BeginUpdateLayers() override;
   void EndUpdateLayers() override;
   void UpdateVisualState() override;
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
index 009e06d2..0141d4d 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -174,8 +174,6 @@
           ? blink::WebEmbeddedWorkerStartData::kWaitForDebugger
           : blink::WebEmbeddedWorkerStartData::kDontWaitForDebugger;
   start_data->devtools_worker_token = params.devtools_worker_token;
-  start_data->v8_cache_options =
-      static_cast<blink::WebSettings::V8CacheOptions>(params.v8_cache_options);
   start_data->privacy_preferences = blink::PrivacyPreferences(
       params.renderer_preferences->enable_do_not_track,
       params.renderer_preferences->enable_referrers);
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 41cea1a..223ed71 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -947,6 +947,7 @@
     "../browser/loader/cors_file_origin_browsertest.cc",
     "../browser/loader/cors_preflight_cache_browsertest.cc",
     "../browser/loader/cross_site_document_blocking_browsertest.cc",
+    "../browser/loader/file_url_loader_factory_browsertest.cc",
     "../browser/loader/loader_browsertest.cc",
     "../browser/loader/prefetch_browsertest.cc",
     "../browser/loader/prefetch_browsertest_base.cc",
diff --git a/content/test/data/media/peerconnection-call-data.html b/content/test/data/media/peerconnection-call-data.html
index 67f7e5c..6a458391 100644
--- a/content/test/data/media/peerconnection-call-data.html
+++ b/content/test/data/media/peerconnection-call-data.html
@@ -254,7 +254,12 @@
   }
 
   function negotiate() {
-    negotiateBetween(gFirstConnection, gSecondConnection);
+    Promise.all([
+      waitForConnectionToStabilizeIfNeeded(gFirstConnection),
+      waitForConnectionToStabilizeIfNeeded(gSecondConnection),
+    ]).then(() => {
+      negotiateBetween(gFirstConnection, gSecondConnection);
+    });
   }
 
   function onRemoteStream(e, target) {
diff --git a/content/test/data/media/webrtc_test_utilities.js b/content/test/data/media/webrtc_test_utilities.js
index 83f8fd8..328fa56 100644
--- a/content/test/data/media/webrtc_test_utilities.js
+++ b/content/test/data/media/webrtc_test_utilities.js
@@ -145,6 +145,16 @@
   });
 }
 
+function waitForConnectionToStabilizeIfNeeded(peerConnection) {
+  return new Promise((resolve, reject) => {
+    if (peerConnection.signalingState == 'stable') {
+      resolve();
+      return;
+    }
+    return waitForConnectionToStabilize(peerConnection).then(resolve);
+  });
+}
+
 // This very basic video verification algorithm will be satisfied if any
 // pixels are changed.
 function isVideoPlaying(pixels, previousPixels) {
diff --git a/content/test/stub_layer_tree_view_delegate.cc b/content/test/stub_layer_tree_view_delegate.cc
index aefecb90..77f96db 100644
--- a/content/test/stub_layer_tree_view_delegate.cc
+++ b/content/test/stub_layer_tree_view_delegate.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "cc/trees/swap_promise.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
@@ -17,4 +18,9 @@
   std::move(callback).Run(nullptr);
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+StubLayerTreeViewDelegate::GetBeginMainFrameMetrics() {
+  return nullptr;
+}
+
 }  // namespace content
diff --git a/content/test/stub_layer_tree_view_delegate.h b/content/test/stub_layer_tree_view_delegate.h
index b7365ae..07768b83 100644
--- a/content/test/stub_layer_tree_view_delegate.h
+++ b/content/test/stub_layer_tree_view_delegate.h
@@ -30,6 +30,8 @@
   void DidBeginMainFrame() override {}
   void RecordStartOfFrameMetrics() override {}
   void RecordEndOfFrameMetrics(base::TimeTicks) override {}
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics()
+      override;
   void BeginUpdateLayers() override {}
   void EndUpdateLayers() override {}
   void RequestNewLayerTreeFrameSink(
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index 50415246..7411ba6 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -344,9 +344,3 @@
 
   * Experimental percentage: 10
 
-* [linux-chromeos-coverage-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/linux-chromeos-coverage-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/linux-chromeos-coverage-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+linux-chromeos-coverage-rel))
-
-  https://crbug.com/1000367
-
-  * Experimental percentage: 3
-
diff --git a/docs/security/sheriff.md b/docs/security/sheriff.md
index a0d3e1f..97c4ba9 100644
--- a/docs/security/sheriff.md
+++ b/docs/security/sheriff.md
@@ -150,11 +150,11 @@
 * **If the report doesn't have enough information**, ask the reporter for more
   information, add the **Needs-Feedback** label and wait for 24 hours for a
   response.
-	* The [security bug template](https://bugs.chromium.org/p/chromium/issues/entry?template=Security+Bug)
-    asks reporters to attach files directly, not in zip or other archives, and
-    attach the source of any online demos they've created. If they've not done
-    so, please make sure all files needed to reproduce the issue are downloaded
-    and attached.
+* The [security bug template](https://bugs.chromium.org/p/chromium/issues/entry?template=Security+Bug)
+  asks reporters to **attach files directly**, not in zip or other archives, and
+  not hosted at an external resource (e.g. Google Cloud Storage). If the report
+  mentions an online demo hosted somewhere, make sure the reporters attach the
+  the source code for the demo as well.
 * **If the bug is a security bug, but is only applicable to Chrome OS**:
 	* The Chrome OS Security team now has their own sheriffing rotation. To get
     bugs into their triage queue, just set OS to the single value of "Chrome".
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn
index 60eefa0..051e5df 100644
--- a/gpu/ipc/common/BUILD.gn
+++ b/gpu/ipc/common/BUILD.gn
@@ -186,7 +186,7 @@
   ]
 }
 
-source_set("vulkan_ycbcr_info") {
+component("vulkan_ycbcr_info") {
   sources = [
     "vulkan_ycbcr_info.cc",
     "vulkan_ycbcr_info.h",
@@ -194,6 +194,7 @@
   deps = [
     "//base",
   ]
+  configs += [ "//gpu:gpu_implementation" ]
 }
 
 mojom("interfaces") {
diff --git a/gpu/ipc/common/vulkan_ycbcr_info.h b/gpu/ipc/common/vulkan_ycbcr_info.h
index 2c99091..91fa0348 100644
--- a/gpu/ipc/common/vulkan_ycbcr_info.h
+++ b/gpu/ipc/common/vulkan_ycbcr_info.h
@@ -6,11 +6,12 @@
 #define GPU_IPC_COMMON_VULKAN_YCBCR_INFO_H_
 
 #include <stdint.h>
+#include "gpu/gpu_export.h"
 
 namespace gpu {
 
 // Sampler Ycbcr conversion information.
-struct VulkanYCbCrInfo {
+struct GPU_EXPORT VulkanYCbCrInfo {
   VulkanYCbCrInfo();
   VulkanYCbCrInfo(uint32_t image_format,
                   uint64_t external_format,
diff --git a/gpu/vulkan/demo/vulkan_demo.h b/gpu/vulkan/demo/vulkan_demo.h
index 2f665be..3d8cd19 100644
--- a/gpu/vulkan/demo/vulkan_demo.h
+++ b/gpu/vulkan/demo/vulkan_demo.h
@@ -12,6 +12,7 @@
 #include "gpu/vulkan/vulkan_swap_chain.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
 
 class SkCanvas;
@@ -27,7 +28,6 @@
 
 namespace ui {
 class PlatformEventSource;
-class PlatformWindow;
 }  // namespace ui
 
 namespace gpu {
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index a91b9da7..2ae8d2df 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -212,8 +212,8 @@
 HEADLESS_PROTOCOL_TEST(VirtualTimeInterrupt,
                        "emulation/virtual-time-interrupt.js")
 
-// Flaky on Linux & Mac. TODO(crbug.com/930717): Re-enable.
-#if defined(OS_LINUX) || defined(OS_MACOSX)
+// Flaky on Linux, Mac & Win. TODO(crbug.com/930717): Re-enable.
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_WIN)
 #define MAYBE_VirtualTimeCrossProcessNavigation \
   DISABLED_VirtualTimeCrossProcessNavigation
 #else
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg
index 5f8813fb..f2e527fc 100644
--- a/infra/config/commit-queue.cfg
+++ b/infra/config/commit-queue.cfg
@@ -392,11 +392,6 @@
         name: "chromium/try/ios-simulator-xcode-clang"
         experiment_percentage: 10
       }
-      # https://crbug.com/1000367
-      builders {
-        name: "chromium/try/linux-chromeos-coverage-rel"
-        experiment_percentage: 3
-      }
 
       retry_config {
         single_quota: 1
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index 615b1ebd14..da421e2 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -4358,16 +4358,6 @@
       mixins: "clang-coverage"
       name: "linux-chromeos-rel"
     }
-    # This builder replicates linux-chromeos-rel for code coverage experiment.
-    # TODO(crbug.com/1000367): Remove once linux-chromeos-coverage-rel is folded
-    # into linux-chromeos-rel.
-    builders {
-      mixins: "chromeos-try"
-      mixins: "goma-j150"
-      mixins: "builderless"
-      mixins: "clang-coverage"
-      name: "linux-chromeos-coverage-rel"
-    }
 
     builders {
       mixins: "linux-try"
diff --git a/ios/BUILD.gn b/ios/BUILD.gn
index 93df8be..982619c 100644
--- a/ios/BUILD.gn
+++ b/ios/BUILD.gn
@@ -45,9 +45,5 @@
       "//ios/web_view:all_tests",
       "//ios/web_view/shell/test:all_tests",
     ]
-
-    if (checkout_ios_webkit) {
-      deps += [ "//ios/third_party/webkit" ]
-    }
   }
 }
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index e02dd67..9729dea 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -29,6 +29,7 @@
     "//components/security_state/core",
     "//components/send_tab_to_self",
     "//components/signin/core/browser",
+    "//components/signin/ios/browser",
     "//components/signin/public/base",
     "//components/strings:components_strings",
     "//components/sync/driver",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 0baa5b92..7c0d781 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -41,6 +41,7 @@
 #include "components/security_state/core/features.h"
 #include "components/send_tab_to_self/features.h"
 #include "components/signin/core/browser/account_reconcilor.h"
+#include "components/signin/ios/browser/features.h"
 #include "components/signin/public/base/signin_switches.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/sync/driver/sync_driver_switches.h"
@@ -546,6 +547,10 @@
     {"password-leak-detection", flag_descriptions::kPasswordLeakDetectionName,
      flag_descriptions::kPasswordLeakDetectionDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(password_manager::features::kLeakDetection)},
+    {"force-startup-signin-promo",
+     flag_descriptions::kForceStartupSigninPromoName,
+     flag_descriptions::kForceStartupSigninPromoDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(signin::kForceStartupSigninPromo)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 31b84dc..e48485be 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -335,6 +335,11 @@
 const char kSnapshotDrawViewDescription[] =
     "When enabled, snapshots will be taken using |-drawViewHierarchy:|.";
 
+const char kForceStartupSigninPromoName[] = "Display the startup sign-in promo";
+const char kForceStartupSigninPromoDescription[] =
+    "When enabled, the startup sign-in promo is always displayed when starting "
+    "Chrome.";
+
 const char kSyncSandboxName[] = "Use Chrome Sync sandbox";
 const char kSyncSandboxDescription[] =
     "Connects to the testing server for Chrome Sync.";
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index 3104ed5..076c4f9 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -286,6 +286,10 @@
 extern const char kSnapshotDrawViewName[];
 extern const char kSnapshotDrawViewDescription[];
 
+// Title and description for the flag to trigger the startup sign-in promo.
+extern const char kForceStartupSigninPromoName[];
+extern const char kForceStartupSigninPromoDescription[];
+
 // Title and description for the flag to control if Chrome Sync should use the
 // sandbox servers.
 extern const char kSyncSandboxName[];
diff --git a/ios/chrome/browser/main/browser_impl.h b/ios/chrome/browser/main/browser_impl.h
index 1b9aaec..30f7b69 100644
--- a/ios/chrome/browser/main/browser_impl.h
+++ b/ios/chrome/browser/main/browser_impl.h
@@ -13,6 +13,7 @@
 
 @class TabModel;
 class WebStateList;
+class WebStateListDelegate;
 
 namespace ios {
 class ChromeBrowserState;
@@ -37,13 +38,16 @@
   void RemoveObserver(BrowserObserver* observer) override;
 
  private:
-  // Exposed to allow unittests to pass in a mock TabModel.
+  // Exposed to allow unittests to inject a TabModel and WebStateList
   FRIEND_TEST_ALL_PREFIXES(BrowserImplTest, TestAccessors);
-  BrowserImpl(ios::ChromeBrowserState* browser_state, TabModel* tab_model);
+  BrowserImpl(ios::ChromeBrowserState* browser_state,
+              TabModel* tab_model,
+              std::unique_ptr<WebStateList> web_state_list);
 
   ios::ChromeBrowserState* browser_state_;
   __strong TabModel* tab_model_;
-  WebStateList* web_state_list_;
+  std::unique_ptr<WebStateListDelegate> web_state_list_delegate_;
+  std::unique_ptr<WebStateList> web_state_list_;
   base::ObserverList<BrowserObserver, /* check_empty= */ true> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(BrowserImpl);
diff --git a/ios/chrome/browser/main/browser_impl.mm b/ios/chrome/browser/main/browser_impl.mm
index c729d96..7b104c4b 100644
--- a/ios/chrome/browser/main/browser_impl.mm
+++ b/ios/chrome/browser/main/browser_impl.mm
@@ -8,8 +8,11 @@
 #include "base/memory/ptr_util.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/main/browser_observer.h"
+#import "ios/chrome/browser/main/browser_web_state_list_delegate.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -18,18 +21,25 @@
 BrowserImpl::BrowserImpl(ios::ChromeBrowserState* browser_state)
     : browser_state_(browser_state) {
   DCHECK(browser_state_);
+
+  web_state_list_delegate_ = std::make_unique<BrowserWebStateListDelegate>();
+  web_state_list_ =
+      std::make_unique<WebStateList>(web_state_list_delegate_.get());
+
   tab_model_ =
       [[TabModel alloc] initWithSessionService:[SessionServiceIOS sharedService]
-                                  browserState:browser_state_];
-  web_state_list_ = tab_model_.webStateList;
+                                  browserState:browser_state_
+                                  webStateList:web_state_list_.get()];
 }
 
 BrowserImpl::BrowserImpl(ios::ChromeBrowserState* browser_state,
-                         TabModel* tab_model)
-    : browser_state_(browser_state), tab_model_(tab_model) {
+                         TabModel* tab_model,
+                         std::unique_ptr<WebStateList> web_state_list)
+    : browser_state_(browser_state),
+      tab_model_(tab_model),
+      web_state_list_(std::move(web_state_list)) {
   DCHECK(browser_state_);
-  DCHECK(tab_model_);
-  web_state_list_ = tab_model_.webStateList;
+  DCHECK(tab_model.webStateList == web_state_list_.get());
 }
 
 BrowserImpl::~BrowserImpl() {
@@ -47,7 +57,7 @@
 }
 
 WebStateList* BrowserImpl::GetWebStateList() const {
-  return web_state_list_;
+  return web_state_list_.get();
 }
 
 void BrowserImpl::AddObserver(BrowserObserver* observer) {
diff --git a/ios/chrome/browser/main/browser_impl_unittest.mm b/ios/chrome/browser/main/browser_impl_unittest.mm
index 323aa90..a2dc35ea 100644
--- a/ios/chrome/browser/main/browser_impl_unittest.mm
+++ b/ios/chrome/browser/main/browser_impl_unittest.mm
@@ -19,29 +19,34 @@
 
 class BrowserImplTest : public PlatformTest {
  protected:
-  BrowserImplTest() : web_state_list_(&web_state_list_delegate_) {
+  BrowserImplTest()
+      : web_state_list_(
+            std::make_unique<WebStateList>(&web_state_list_delegate_)) {
     TestChromeBrowserState::Builder test_cbs_builder;
     chrome_browser_state_ = test_cbs_builder.Build();
 
     tab_model_ = [OCMockObject mockForClass:[TabModel class]];
-    OCMStub([tab_model_ webStateList]).andReturn(&web_state_list_);
+    OCMStub([tab_model_ webStateList]).andReturn(web_state_list_.get());
   }
 
   web::WebTaskEnvironment task_environment_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
 
   FakeWebStateListDelegate web_state_list_delegate_;
-  WebStateList web_state_list_;
+  // Unique ptr to the web_state_list_ to transfer into new Browser instances.
+  std::unique_ptr<WebStateList> web_state_list_;
   id tab_model_;
 };
 
 // Tests that the accessors return the expected values.
 TEST_F(BrowserImplTest, TestAccessors) {
-  BrowserImpl browser(chrome_browser_state_.get(), tab_model_);
+  WebStateList* web_state_list_weak_reference = web_state_list_.get();
+  BrowserImpl browser(chrome_browser_state_.get(), tab_model_,
+                      std::move(web_state_list_));
 
   EXPECT_EQ(chrome_browser_state_.get(), browser.GetBrowserState());
   EXPECT_EQ(tab_model_, browser.GetTabModel());
-  EXPECT_EQ(&web_state_list_, browser.GetWebStateList());
+  EXPECT_EQ(web_state_list_weak_reference, browser.GetWebStateList());
 }
 
 // Tests that the BrowserDestroyed() callback is sent when a browser is deleted.
@@ -53,7 +58,7 @@
   // |-browserStateDestroyed| is expected to be executed before the
   // TabModelList's destructor.
   // TODO(crbug.com/783777): Remove when TabModel is no longer used.
-  [browser->GetTabModel() browserStateDestroyed];
+  [browser->GetTabModel() disconnect];
   browser = nullptr;
   EXPECT_TRUE(observer.browser_destroyed());
 }
diff --git a/ios/chrome/browser/metrics/previous_session_info.mm b/ios/chrome/browser/metrics/previous_session_info.mm
index a8d73f4..53df9f658 100644
--- a/ios/chrome/browser/metrics/previous_session_info.mm
+++ b/ios/chrome/browser/metrics/previous_session_info.mm
@@ -120,21 +120,6 @@
 
 @implementation PreviousSessionInfo
 
-@synthesize availableDeviceStorage = _availableDeviceStorage;
-@synthesize deviceBatteryLevel = _deviceBatteryLevel;
-@synthesize deviceBatteryState = _deviceBatteryState;
-@synthesize deviceThermalState = _deviceThermalState;
-@synthesize deviceWasInLowPowerMode = _deviceWasInLowPowerMode;
-@synthesize didBeginRecordingCurrentSession = _didBeginRecordingCurrentSession;
-@synthesize didSeeMemoryWarningShortlyBeforeTerminating =
-    _didSeeMemoryWarningShortlyBeforeTerminating;
-@synthesize isFirstSessionAfterOSUpgrade = _isFirstSessionAfterOSUpgrade;
-@synthesize isFirstSessionAfterUpgrade = _isFirstSessionAfterUpgrade;
-@synthesize isFirstSessionAfterLanguageChange =
-    _isFirstSessionAfterLanguageChange;
-@synthesize OSVersion = _OSVersion;
-@synthesize sessionEndTime = _sessionEndTime;
-
 // Singleton PreviousSessionInfo.
 static PreviousSessionInfo* gSharedInstance = nil;
 
diff --git a/ios/chrome/browser/sessions/session_service_ios.mm b/ios/chrome/browser/sessions/session_service_ios.mm
index f4578b2..eac1ccd 100644
--- a/ios/chrome/browser/sessions/session_service_ios.mm
+++ b/ios/chrome/browser/sessions/session_service_ios.mm
@@ -211,6 +211,10 @@
   SessionIOSFactory factory = [_pendingSessions objectForKey:sessionPath];
   [_pendingSessions removeObjectForKey:sessionPath];
   SessionIOS* session = factory();
+  // Because the factory may be called asynchronously after the underlying
+  // web state list is destroyed, the session may be nil; if so, do nothing.
+  if (!session)
+    return;
 
   @try {
     NSError* error = nil;
diff --git a/ios/chrome/browser/snapshots/BUILD.gn b/ios/chrome/browser/snapshots/BUILD.gn
index 676edde68..2e1c6ae0 100644
--- a/ios/chrome/browser/snapshots/BUILD.gn
+++ b/ios/chrome/browser/snapshots/BUILD.gn
@@ -41,13 +41,13 @@
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web:tab_id_tab_helper",
     "//ios/chrome/browser/web_state_list",
+    "//ios/third_party/webkit",
     "//ios/web/public",
     "//ui/gfx",
   ]
   libs = [
     "QuartzCore.framework",
     "UIKit.framework",
-    "WebKit.framework",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/chrome/browser/sync/device_info_sync_service_factory.mm b/ios/chrome/browser/sync/device_info_sync_service_factory.mm
index df61478f..2a14001 100644
--- a/ios/chrome/browser/sync/device_info_sync_service_factory.mm
+++ b/ios/chrome/browser/sync/device_info_sync_service_factory.mm
@@ -14,6 +14,7 @@
 #include "components/signin/public/base/device_id_helper.h"
 #include "components/sync/model/model_type_store_service.h"
 #include "components/sync_device_info/device_info_prefs.h"
+#include "components/sync_device_info/device_info_sync_client.h"
 #include "components/sync_device_info/device_info_sync_service_impl.h"
 #include "components/sync_device_info/local_device_info_provider_impl.h"
 #include "ios/chrome/browser/application_context.h"
@@ -26,6 +27,29 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+class DeviceInfoSyncClient : public syncer::DeviceInfoSyncClient {
+ public:
+  explicit DeviceInfoSyncClient(PrefService* prefs) : prefs_(prefs) {}
+  ~DeviceInfoSyncClient() override = default;
+
+  // syncer::DeviceInfoSyncClient:
+  std::string GetSigninScopedDeviceId() const override {
+    return signin::GetSigninScopedDeviceId(prefs_);
+  }
+
+  // syncer::DeviceInfoSyncClient:
+  bool GetSendTabToSelfReceivingEnabled() const override {
+    return send_tab_to_self::IsReceivingEnabledByUserOnThisDevice(prefs_);
+  }
+
+ private:
+  PrefService* const prefs_;
+};
+
+}  // namespace
+
 // static
 syncer::DeviceInfoSyncService* DeviceInfoSyncServiceFactory::GetForBrowserState(
     ios::ChromeBrowserState* browser_state) {
@@ -74,21 +98,17 @@
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
 
+  auto device_info_sync_client =
+      std::make_unique<DeviceInfoSyncClient>(browser_state->GetPrefs());
   auto local_device_info_provider =
       std::make_unique<syncer::LocalDeviceInfoProviderImpl>(
-          ::GetChannel(), ::GetVersionString(),
-          /*signin_scoped_device_id_callback=*/
-          base::BindRepeating(&signin::GetSigninScopedDeviceId,
-                              browser_state->GetPrefs()),
-          /*send_tab_to_self_receiving_enabled_callback=*/
-          base::BindRepeating(
-              &send_tab_to_self::IsReceivingEnabledByUserOnThisDevice,
-              browser_state->GetPrefs()));
+          ::GetChannel(), ::GetVersionString(), device_info_sync_client.get());
   auto device_prefs =
       std::make_unique<syncer::DeviceInfoPrefs>(browser_state->GetPrefs());
 
   return std::make_unique<syncer::DeviceInfoSyncServiceImpl>(
       ModelTypeStoreServiceFactory::GetForBrowserState(browser_state)
           ->GetStoreFactory(),
-      std::move(local_device_info_provider), std::move(device_prefs));
+      std::move(local_device_info_provider), std::move(device_prefs),
+      std::move(device_info_sync_client));
 }
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.mm b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
index 93cde8a9..bac4d98 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.mm
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.mm
@@ -225,13 +225,6 @@
       NOTREACHED();
       return nullptr;
     }
-    case syncer::DEPRECATED_ARTICLES: {
-      // DomDistillerService is used in iOS ReadingList. The distilled articles
-      // are saved separately and must not be synced.
-      // Add a not reached to avoid having ARTICLES sync be enabled silently.
-      NOTREACHED();
-      return base::WeakPtr<syncer::SyncableService>();
-    }
     case syncer::PASSWORDS: {
       return password_store_ ? password_store_->GetPasswordSyncableService()
                              : base::WeakPtr<syncer::SyncableService>();
diff --git a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h
index 4ab66c9..206edce7 100644
--- a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h
+++ b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h
@@ -27,7 +27,6 @@
   SessionID GetWindowId() const override;
   SessionID GetSessionId() const override;
   bool IsBeingDestroyed() const override;
-  SessionID GetSourceTabID() const override;
   std::string GetExtensionAppId() const override;
   bool IsInitialBlankNavigation() const override;
   int GetCurrentEntryIndex() const override;
diff --git a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm
index 10973dc..50b85c5b 100644
--- a/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm
+++ b/ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.mm
@@ -55,12 +55,6 @@
   return web_state_->IsBeingDestroyed();
 }
 
-// todo(pnoland): add logic to store and return the source tab id on ios.
-// http://crbug/695241
-SessionID IOSChromeSyncedTabDelegate::GetSourceTabID() const {
-  return SessionID::InvalidValue();
-}
-
 std::string IOSChromeSyncedTabDelegate::GetExtensionAppId() const {
   return std::string();
 }
diff --git a/ios/chrome/browser/sync/profile_sync_service_factory_unittest.cc b/ios/chrome/browser/sync/profile_sync_service_factory_unittest.cc
index b9adb43..dd246bb 100644
--- a/ios/chrome/browser/sync/profile_sync_service_factory_unittest.cc
+++ b/ios/chrome/browser/sync/profile_sync_service_factory_unittest.cc
@@ -43,7 +43,7 @@
  protected:
   // Returns the collection of default datatypes.
   std::vector<syncer::ModelType> DefaultDatatypes() {
-    static_assert(46 == syncer::ModelType::NUM_ENTRIES,
+    static_assert(39 == syncer::ModelType::NUM_ENTRIES,
                   "When adding a new type, you probably want to add it here as "
                   "well (assuming it is already enabled).");
 
diff --git a/ios/chrome/browser/tabs/tab_lifecycle.md b/ios/chrome/browser/tabs/tab_lifecycle.md
index 4590a26..228fe8d 100644
--- a/ios/chrome/browser/tabs/tab_lifecycle.md
+++ b/ios/chrome/browser/tabs/tab_lifecycle.md
@@ -10,13 +10,13 @@
 AttachTabHelper(web_state.get());
 ````
 
-When a `WebState` is added to a `TabModel`'s `WebStateList`,
+When a `WebState` is added to a `Browser`'s `WebStateList`,
 `BrowserWebStateListDelegate` will invoke `AttachTabHelpers` if necessary.
 
 ```cpp
-TabModel* tab_model = ...;
+Browser* browser = ...;
 std::unique_ptr<web::WebState> web_state =  ...;
-[tab_model webStateList]->InsertWebState(0, std::move(web_state));
+browser->GetWebStateList()->InsertWebState(0, std::move(web_state));
 ```
 
 All Tab helpers are `WebStateUserData` thus they are destroyed after the
diff --git a/ios/chrome/browser/tabs/tab_model.h b/ios/chrome/browser/tabs/tab_model.h
index 157e8ef..9e0cdfa0 100644
--- a/ios/chrome/browser/tabs/tab_model.h
+++ b/ios/chrome/browser/tabs/tab_model.h
@@ -76,13 +76,16 @@
 // in order to display the views associated with the tabs. Waits until the views
 // are ready. |browserState| cannot be nil. |service| cannot be nil; this class
 // creates intermediate SessionWindowIOS objects which must be consumed by a
-// session service before they are deallocated. |window| can be nil to create
-// an empty TabModel. In that case no notification will be sent during object
-// creation.
+// session service before they are deallocated.
 - (instancetype)initWithSessionService:(SessionServiceIOS*)service
                           browserState:(ios::ChromeBrowserState*)browserState
+                          webStateList:(WebStateList*)webStateList
     NS_DESIGNATED_INITIALIZER;
 
+// Temporary backwards compatibility init which creates a webStateList.
+- (instancetype)initWithSessionService:(SessionServiceIOS*)service
+                          browserState:(ios::ChromeBrowserState*)browserState;
+
 - (instancetype)init NS_UNAVAILABLE;
 
 // Add/modify tabs.
@@ -129,9 +132,16 @@
 // Sets whether the user is primarily interacting with this tab model.
 - (void)setPrimary:(BOOL)primary;
 
-// Called when the browser state provided to this instance is being destroyed.
+// Tells the receiver to disconnect from the model object it depends on. This
+// should be called before destroying the browser state that the receiver was
+// initialized with.
+// It is safe to call this method multiple times.
 // At this point the tab model will no longer ever be active, and will likely be
-// deallocated soon.
+// deallocated soon. Calling any other methods or accessing any properties on
+// the tab model after this is called is unsafe.
+- (void)disconnect;
+
+// Legacy method name for -disconnect, will be deleted very soon.
 - (void)browserStateDestroyed;
 
 @end
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index d58b598..430bcbeb 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -215,11 +215,8 @@
 }  // anonymous namespace
 
 @interface TabModel ()<CRWWebStateObserver, WebStateListObserving> {
-  // Delegate for the WebStateList.
-  std::unique_ptr<WebStateListDelegate> _webStateListDelegate;
-
-  // Underlying shared model implementation.
-  std::unique_ptr<WebStateList> _webStateList;
+  // Weak reference to the underlying shared model implementation.
+  WebStateList* _webStateList;
 
   // WebStateListObservers reacting to modifications of the model (may send
   // notification, translate and forward events, update metrics, ...).
@@ -246,6 +243,10 @@
 
   // Used to observe owned Tabs' WebStates.
   std::unique_ptr<web::WebStateObserver> _webStateObserver;
+
+  // Legacy ivars for backwards compatibility with some tests
+  std::unique_ptr<WebStateListDelegate> _legacyWebStateListDelegate;
+  std::unique_ptr<WebStateList> _legacyOwnedWebStateList;
 }
 
 // Session window for the contents of the tab model.
@@ -262,12 +263,11 @@
 #pragma mark - Overriden
 
 - (void)dealloc {
-  // browserStateDestroyed should always have been called before destruction.
+  // -disconnect should always have been called before destruction.
   DCHECK(!_browserState);
 }
 
 #pragma mark - Public methods
-
 - (TabUsageRecorder*)tabUsageRecorder {
   return _tabUsageRecorder.get();
 }
@@ -287,15 +287,14 @@
 
 - (WebStateList*)webStateList {
   DCHECK(_webStateList);
-  return _webStateList.get();
+  return _webStateList;
 }
 
 - (instancetype)initWithSessionService:(SessionServiceIOS*)service
-                          browserState:(ios::ChromeBrowserState*)browserState {
+                          browserState:(ios::ChromeBrowserState*)browserState
+                          webStateList:(WebStateList*)webStateList {
   if ((self = [super init])) {
-    _webStateListDelegate = std::make_unique<BrowserWebStateListDelegate>();
-    _webStateList = std::make_unique<WebStateList>(_webStateListDelegate.get());
-
+    _webStateList = webStateList;
     _browserState = browserState;
     DCHECK(_browserState);
 
@@ -307,11 +306,12 @@
     if (!_browserState->IsOffTheRecord()) {
       // Set up the usage recorder before tabs are created.
       _tabUsageRecorder = std::make_unique<TabUsageRecorder>(
-          _webStateList.get(),
+          _webStateList,
           PrerenderServiceFactory::GetForBrowserState(browserState));
     }
+
     std::unique_ptr<TabModelSyncedWindowDelegate> syncedWindowDelegate =
-        std::make_unique<TabModelSyncedWindowDelegate>(_webStateList.get());
+        std::make_unique<TabModelSyncedWindowDelegate>(_webStateList);
 
     // Keep a weak ref to the the window delegate, which is then moved into
     // the web state list observers list.
@@ -376,6 +376,17 @@
   return self;
 }
 
+- (instancetype)initWithSessionService:(SessionServiceIOS*)service
+                          browserState:(ios::ChromeBrowserState*)browserState {
+  _legacyWebStateListDelegate = std::make_unique<BrowserWebStateListDelegate>();
+  _legacyOwnedWebStateList =
+      std::make_unique<WebStateList>(_legacyWebStateListDelegate.get());
+
+  return [self initWithSessionService:service
+                         browserState:browserState
+                         webStateList:_legacyOwnedWebStateList.get()];
+}
+
 - (web::WebState*)insertWebStateWithURL:(const GURL&)URL
                                referrer:(const web::Referrer&)referrer
                              transition:(ui::PageTransition)transition
@@ -476,7 +487,7 @@
 }
 
 // NOTE: This can be called multiple times, so must be robust against that.
-- (void)browserStateDestroyed {
+- (void)disconnect {
   if (!_browserState)
     return;
 
@@ -502,20 +513,23 @@
     _webStateList->RemoveObserver(webStateListObserver.get());
   _webStateListObservers.clear();
   _retainedWebStateListObservers = nil;
+  _webStateList = nullptr;
 
   _clearPoliciesTaskTracker.TryCancelAll();
   _tabUsageRecorder.reset();
   _webStateObserver.reset();
 }
 
+- (void)browserStateDestroyed {
+  [self disconnect];
+}
+
 #pragma mark - SessionWindowRestoring(public)
 
 - (void)saveSessionImmediately:(BOOL)immediately {
-  // Do nothing if there are tabs in the model but no selected tab. This is
-  // a transitional state.
-  if ((!_webStateList->GetActiveWebState() && _webStateList->count()) ||
-      !_browserState)
+  if (![self canSaveCurrentSession])
     return;
+
   NSString* statePath =
       base::SysUTF8ToNSString(_browserState->GetStatePath().AsUTF8Unsafe());
   __weak TabModel* weakSelf = self;
@@ -529,13 +543,28 @@
 
 #pragma mark - Private methods
 
+// YES if the current session can be saved.
+- (BOOL)canSaveCurrentSession {
+  // A session requires an active browser state and web state list.
+  if (!_browserState || !_webStateList)
+    return NO;
+  // Sessions where there's no active tab shouldn't be saved, unless the web
+  // state list is empty. This is a transitional state.
+  if (!_webStateList->empty() && !_webStateList->GetActiveWebState())
+    return NO;
+
+  return YES;
+}
+
 - (SessionIOS*)sessionForSaving {
+  if (![self canSaveCurrentSession])
+    return nil;
   // Build the array of sessions. Copy the session objects as the saving will
   // be done on a separate thread.
   // TODO(crbug.com/661986): This could get expensive especially since this
   // window may never be saved (if another call comes in before the delay).
   return [[SessionIOS alloc]
-      initWithWindows:@[ SerializeWebStateList(_webStateList.get()) ]];
+      initWithWindows:@[ SerializeWebStateList(_webStateList) ]];
 }
 
 - (BOOL)isWebUsageEnabled {
@@ -567,7 +596,7 @@
 
   web::WebState::CreateParams createParams(_browserState);
   DeserializeWebStateList(
-      _webStateList.get(), window,
+      _webStateList, window,
       base::BindRepeating(&web::WebState::CreateWithStorageSession,
                           createParams));
 
@@ -658,7 +687,7 @@
       &_clearPoliciesTaskTracker,
       base::CreateSingleThreadTaskRunner({web::WebThread::IO}),
       web::BrowserState::GetCertificatePolicyCache(_browserState),
-      _webStateList.get());
+      _webStateList);
 
   // Normally, the session is saved after some timer expires but since the app
   // is about to enter the background send YES to save the session immediately.
diff --git a/ios/chrome/browser/tabs/tab_model_list_unittest.mm b/ios/chrome/browser/tabs/tab_model_list_unittest.mm
index 83e7f60..6ab7936 100644
--- a/ios/chrome/browser/tabs/tab_model_list_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_list_unittest.mm
@@ -7,9 +7,11 @@
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
+#import "ios/chrome/browser/main/browser_web_state_list_delegate.h"
 #import "ios/chrome/browser/sessions/test_session_service.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/tabs/tab_model_list_observer.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
 #include "ios/web/public/test/web_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -40,18 +42,28 @@
   TabModelListTest()
       : scoped_browser_state_manager_(
             std::make_unique<TestChromeBrowserStateManager>(
-                TestChromeBrowserState::Builder().Build())) {}
+                TestChromeBrowserState::Builder().Build())),
+        web_state_list_delegate_(
+            std::make_unique<BrowserWebStateListDelegate>()),
+        web_state_list_(
+            std::make_unique<WebStateList>(web_state_list_delegate_.get())),
+        otr_web_state_list_delegate_(
+            std::make_unique<BrowserWebStateListDelegate>()),
+        otr_web_state_list_(
+            std::make_unique<WebStateList>(web_state_list_delegate_.get())) {}
 
   TabModel* CreateTabModel() {
     return [[TabModel alloc]
         initWithSessionService:[[TestSessionService alloc] init]
-                  browserState:browser_state()];
+                  browserState:browser_state()
+                  webStateList:web_state_list_.get()];
   }
 
   TabModel* CreateOffTheRecordTabModel() {
     return [[TabModel alloc]
         initWithSessionService:[[TestSessionService alloc] init]
-                  browserState:otr_browser_state()];
+                  browserState:otr_browser_state()
+                  webStateList:otr_web_state_list_.get()];
   }
 
   NSArray<TabModel*>* RegisteredTabModels() {
@@ -76,6 +88,12 @@
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
 
+  std::unique_ptr<WebStateListDelegate> web_state_list_delegate_;
+  std::unique_ptr<WebStateList> web_state_list_;
+
+  std::unique_ptr<WebStateListDelegate> otr_web_state_list_delegate_;
+  std::unique_ptr<WebStateList> otr_web_state_list_;
+
   DISALLOW_COPY_AND_ASSIGN(TabModelListTest);
 };
 
@@ -105,7 +123,7 @@
   EXPECT_CALL(observer, TabModelUnregisteredFromBrowserState(
                             Eq(tab_model), Eq(otr_browser_state())))
       .Times(0);
-  [tab_model browserStateDestroyed];
+  [tab_model disconnect];
   EXPECT_EQ([RegisteredTabModels() count], 0u);
 
   TabModelList::RemoveObserver(&observer);
@@ -137,7 +155,7 @@
   EXPECT_CALL(observer, TabModelUnregisteredFromBrowserState(
                             Eq(tab_model), Eq(browser_state())))
       .Times(0);
-  [tab_model browserStateDestroyed];
+  [tab_model disconnect];
   EXPECT_EQ([RegisteredOffTheRecordTabModels() count], 0u);
 
   TabModelList::RemoveObserver(&observer);
@@ -156,8 +174,8 @@
   EXPECT_NE([RegisteredTabModels() indexOfObject:tab_model2],
             static_cast<NSUInteger>(NSNotFound));
 
-  [tab_model1 browserStateDestroyed];
-  [tab_model2 browserStateDestroyed];
+  [tab_model1 disconnect];
+  [tab_model2 disconnect];
 
   EXPECT_EQ([RegisteredTabModels() count], 0u);
 }
@@ -175,8 +193,8 @@
   EXPECT_NE([RegisteredOffTheRecordTabModels() indexOfObject:tab_model2],
             static_cast<NSUInteger>(NSNotFound));
 
-  [tab_model1 browserStateDestroyed];
-  [tab_model2 browserStateDestroyed];
+  [tab_model1 disconnect];
+  [tab_model2 disconnect];
 
   EXPECT_EQ([RegisteredOffTheRecordTabModels() count], 0u);
 }
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm
index 9cc927d..1e76a11 100644
--- a/ios/chrome/browser/tabs/tab_model_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -13,6 +13,7 @@
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
+#import "ios/chrome/browser/main/browser_web_state_list_delegate.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper_delegate.h"
 #include "ios/chrome/browser/sessions/ios_chrome_session_tab_helper.h"
@@ -23,6 +24,7 @@
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/web_state_list/web_state_list_delegate.h"
 #import "ios/chrome/browser/web_state_list/web_state_opener.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler_factory.h"
@@ -67,7 +69,11 @@
   TabModelTest()
       : scoped_browser_state_manager_(
             std::make_unique<TestChromeBrowserStateManager>(base::FilePath())),
-        web_client_(std::make_unique<ChromeWebClient>()) {
+        web_client_(std::make_unique<ChromeWebClient>()),
+        web_state_list_delegate_(
+            std::make_unique<BrowserWebStateListDelegate>()),
+        web_state_list_(
+            std::make_unique<WebStateList>(web_state_list_delegate_.get())) {
     DCHECK_CURRENTLY_ON(web::WebThread::UI);
 
     if (GetParam() == NavigationManagerChoice::LEGACY) {
@@ -105,7 +111,7 @@
     if (tab_model_) {
       web_usage_enabler_->SetWebStateList(nullptr);
       @autoreleasepool {
-        [tab_model_ browserStateDestroyed];
+        [tab_model_ disconnect];
         tab_model_ = nil;
       }
     }
@@ -118,7 +124,8 @@
                            SessionWindowIOS* session_window) {
     TabModel* tab_model([[TabModel alloc]
         initWithSessionService:session_service
-                  browserState:chrome_browser_state_.get()]);
+                  browserState:chrome_browser_state_.get()
+                  webStateList:web_state_list_.get()]);
     [tab_model restoreSessionWindow:session_window forInitialRestore:YES];
     [tab_model setPrimary:YES];
     return tab_model;
@@ -141,6 +148,8 @@
   web::WebTaskEnvironment task_environment_;
   IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
   web::ScopedTestingWebClient web_client_;
+  std::unique_ptr<WebStateListDelegate> web_state_list_delegate_;
+  std::unique_ptr<WebStateList> web_state_list_;
   SessionWindowIOS* session_window_;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   WebStateListWebUsageEnabler* web_usage_enabler_;
@@ -183,8 +192,8 @@
                         openedByDOM:NO
                             atIndex:0
                        inBackground:NO];
-  [tab_model_ browserStateDestroyed];
-  [tab_model_ browserStateDestroyed];
+  [tab_model_ disconnect];
+  [tab_model_ disconnect];
 }
 
 TEST_P(TabModelTest, InsertUrlMultiple) {
diff --git a/ios/chrome/browser/test/BUILD.gn b/ios/chrome/browser/test/BUILD.gn
index d5138a31..95574d6 100644
--- a/ios/chrome/browser/test/BUILD.gn
+++ b/ios/chrome/browser/test/BUILD.gn
@@ -17,6 +17,7 @@
     "//ios/chrome/browser/autocomplete",
     "//ios/chrome/browser/bookmarks",
     "//ios/chrome/browser/browser_state:test_support",
+    "//ios/chrome/browser/main",
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/prerender",
     "//ios/chrome/browser/search_engines",
@@ -27,6 +28,7 @@
     "//ios/chrome/browser/ui/browser_view",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/web:web_internal",
+    "//ios/chrome/browser/web_state_list",
     "//ios/chrome/test:test_support",
     "//ios/chrome/test/base:perf_test_support",
     "//ios/public/provider/chrome/browser",
diff --git a/ios/chrome/browser/test/perf_test_with_bvc_ios.h b/ios/chrome/browser/test/perf_test_with_bvc_ios.h
index 8aa0bc3..e91d627 100644
--- a/ios/chrome/browser/test/perf_test_with_bvc_ios.h
+++ b/ios/chrome/browser/test/perf_test_with_bvc_ios.h
@@ -10,6 +10,8 @@
 #include <memory>
 
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/main/browser_web_state_list_delegate.h"
+#import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/chrome/test/base/perf_test_ios.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_provider.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
@@ -52,6 +54,11 @@
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
   std::unique_ptr<TestChromeBrowserState> incognito_chrome_browser_state_;
 
+  BrowserWebStateListDelegate web_state_list_delegate_;
+
+  WebStateList web_state_list_;
+  WebStateList otr_web_state_list_;
+
   TabModel* tab_model_;
   TabModel* otr_tab_model_;
 
diff --git a/ios/chrome/browser/test/perf_test_with_bvc_ios.mm b/ios/chrome/browser/test/perf_test_with_bvc_ios.mm
index 183bce9..5318107 100644
--- a/ios/chrome/browser/test/perf_test_with_bvc_ios.mm
+++ b/ios/chrome/browser/test/perf_test_with_bvc_ios.mm
@@ -46,7 +46,9 @@
       web_client_(std::make_unique<ChromeWebClient>()),
       provider_(ios::CreateChromeBrowserProvider()),
       browser_state_manager_(
-          std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {}
+          std::make_unique<TestChromeBrowserStateManager>(base::FilePath())),
+      web_state_list_(&web_state_list_delegate_),
+      otr_web_state_list_(&web_state_list_delegate_) {}
 
 PerfTestWithBVC::PerfTestWithBVC(std::string testGroup,
                                  std::string firstLabel,
@@ -65,7 +67,9 @@
       web_client_(std::make_unique<ChromeWebClient>()),
       provider_(ios::CreateChromeBrowserProvider()),
       browser_state_manager_(
-          std::make_unique<TestChromeBrowserStateManager>(base::FilePath())) {}
+          std::make_unique<TestChromeBrowserStateManager>(base::FilePath())),
+      web_state_list_(&web_state_list_delegate_),
+      otr_web_state_list_(&web_state_list_delegate_) {}
 
 PerfTestWithBVC::~PerfTestWithBVC() {}
 
@@ -102,13 +106,15 @@
   // view controller, which is created in OpenStackView().
   tab_model_ =
       [[TabModel alloc] initWithSessionService:[SessionServiceIOS sharedService]
-                                  browserState:chrome_browser_state_.get()];
+                                  browserState:chrome_browser_state_.get()
+                                  webStateList:&web_state_list_];
   [tab_model_ restoreSessionWindow:session.sessionWindows[0]
                  forInitialRestore:YES];
   otr_tab_model_ = [[TabModel alloc]
       initWithSessionService:[SessionServiceIOS sharedService]
                 browserState:chrome_browser_state_
-                                 ->GetOffTheRecordChromeBrowserState()];
+                                 ->GetOffTheRecordChromeBrowserState()
+                webStateList:&otr_web_state_list_];
   [otr_tab_model_ restoreSessionWindow:session.sessionWindows[0]
                      forInitialRestore:YES];
 
@@ -149,7 +155,7 @@
   bvc_ = nil;
   bvc_factory_ = nil;
   tab_model_ = nil;
-  [otr_tab_model_ browserStateDestroyed];
+  [otr_tab_model_ disconnect];
   otr_tab_model_ = nil;
 
   // The base class |TearDown| method calls the run loop so the
diff --git a/ios/chrome/browser/ui/badges/BUILD.gn b/ios/chrome/browser/ui/badges/BUILD.gn
index 4d5efa3..069fe26 100644
--- a/ios/chrome/browser/ui/badges/BUILD.gn
+++ b/ios/chrome/browser/ui/badges/BUILD.gn
@@ -17,8 +17,6 @@
   sources = [
     "badge_button.h",
     "badge_button.mm",
-    "badge_button_action_handler.h",
-    "badge_button_action_handler.mm",
     "badge_button_factory.h",
     "badge_button_factory.mm",
     "badge_consumer.h",
diff --git a/ios/chrome/browser/ui/badges/badge_button_action_handler.h b/ios/chrome/browser/ui/badges/badge_button_action_handler.h
deleted file mode 100644
index 96f7446..0000000
--- a/ios/chrome/browser/ui/badges/badge_button_action_handler.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_BADGES_BADGE_BUTTON_ACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_UI_BADGES_BADGE_BUTTON_ACTION_HANDLER_H_
-
-#import <UIKit/UIKit.h>
-
-@protocol InfobarCommands;
-@protocol BadgeDelegate;
-
-// Handler for the actions associated with the different badge buttons.
-@interface BadgeButtonActionHandler : NSObject
-
-// The dispatcher for badge button actions.
-@property(nonatomic, weak) id<InfobarCommands> dispatcher;
-
-// The command handler.
-@property(nonatomic, weak) id<BadgeDelegate> buttonActionDelegate;
-
-// Action when a Passwords badge is tapped.
-- (void)passwordsBadgeButtonTapped:(id)sender;
-
-// Action when the overflow badge is tapped.
-- (void)overflowBadgeButtonTapped:(id)sender;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_BADGES_BADGE_BUTTON_ACTION_HANDLER_H_
diff --git a/ios/chrome/browser/ui/badges/badge_button_action_handler.mm b/ios/chrome/browser/ui/badges/badge_button_action_handler.mm
deleted file mode 100644
index 5324e26..0000000
--- a/ios/chrome/browser/ui/badges/badge_button_action_handler.mm
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2019 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/chrome/browser/ui/badges/badge_button_action_handler.h"
-
-#include "base/mac/foundation_util.h"
-#include "base/metrics/user_metrics.h"
-#include "ios/chrome/browser/infobars/infobar_metrics_recorder.h"
-#import "ios/chrome/browser/infobars/infobar_type.h"
-#import "ios/chrome/browser/ui/badges/badge_button.h"
-#import "ios/chrome/browser/ui/badges/badge_delegate.h"
-#import "ios/chrome/browser/ui/commands/infobar_commands.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation BadgeButtonActionHandler
-
-- (void)passwordsBadgeButtonTapped:(id)sender {
-  BadgeButton* badgeButton = base::mac::ObjCCastStrict<BadgeButton>(sender);
-  MobileMessagesBadgeState state;
-  if (badgeButton.accepted) {
-    state = MobileMessagesBadgeState::Active;
-    base::RecordAction(
-        base::UserMetricsAction("MobileMessagesBadgeAcceptedTapped"));
-  } else {
-    state = MobileMessagesBadgeState::Inactive;
-    base::RecordAction(
-        base::UserMetricsAction("MobileMessagesBadgeNonAcceptedTapped"));
-  }
-  InfobarMetricsRecorder* metricsRecorder;
-  if (badgeButton.badgeType == BadgeType::kBadgeTypePasswordSave) {
-    metricsRecorder = [[InfobarMetricsRecorder alloc]
-        initWithType:InfobarType::kInfobarTypePasswordSave];
-    [self.dispatcher displayModalInfobar:InfobarType::kInfobarTypePasswordSave];
-  } else if (badgeButton.badgeType == BadgeType::kBadgeTypePasswordUpdate) {
-    metricsRecorder = [[InfobarMetricsRecorder alloc]
-        initWithType:InfobarType::kInfobarTypePasswordUpdate];
-    [self.dispatcher
-        displayModalInfobar:InfobarType::kInfobarTypePasswordUpdate];
-  }
-  [metricsRecorder recordBadgeTappedInState:state];
-}
-
-- (void)overflowBadgeButtonTapped:(id)sender {
-  [self.buttonActionDelegate showOverflowMenu];
-  // TODO(crbug.com/976901): Add metric for this action.
-}
-
-@end
diff --git a/ios/chrome/browser/ui/badges/badge_button_factory.h b/ios/chrome/browser/ui/badges/badge_button_factory.h
index cdbd3538..d003811 100644
--- a/ios/chrome/browser/ui/badges/badge_button_factory.h
+++ b/ios/chrome/browser/ui/badges/badge_button_factory.h
@@ -11,18 +11,18 @@
 
 @class BadgeButtonActionHandler;
 @class BadgeButton;
+@protocol BadgeDelegate;
 
 // BadgeButtonFactory Factory creates BadgButton objects with certain
 // styles and configurations, depending on its type.
 @interface BadgeButtonFactory : NSObject
 
-- (instancetype)initWithActionHandler:(BadgeButtonActionHandler*)actionHandler
-    NS_DESIGNATED_INITIALIZER;
-- (instancetype)init NS_UNAVAILABLE;
-
 // Yes if in Incognito mode.
 @property(nonatomic, assign) BOOL incognito;
 
+// Action handler delegate for the buttons.
+@property(nonatomic, weak) id<BadgeDelegate> delegate;
+
 // Returns a properly configured BadgButton associated with |badgeType|.
 - (BadgeButton*)getBadgeButtonForBadgeType:(BadgeType)badgeType;
 
diff --git a/ios/chrome/browser/ui/badges/badge_button_factory.mm b/ios/chrome/browser/ui/badges/badge_button_factory.mm
index c691bc8..1d3768c1 100644
--- a/ios/chrome/browser/ui/badges/badge_button_factory.mm
+++ b/ios/chrome/browser/ui/badges/badge_button_factory.mm
@@ -6,8 +6,8 @@
 
 #import "base/logging.h"
 #import "ios/chrome/browser/ui/badges/badge_button.h"
-#import "ios/chrome/browser/ui/badges/badge_button_action_handler.h"
 #import "ios/chrome/browser/ui/badges/badge_constants.h"
+#import "ios/chrome/browser/ui/badges/badge_delegate.h"
 #import "ios/chrome/common/colors/dynamic_color_util.h"
 #import "ios/chrome/common/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -17,23 +17,8 @@
 #error "This file requires ARC support."
 #endif
 
-@interface BadgeButtonFactory ()
-
-// Action handlers for the buttons.
-@property(nonatomic, strong) BadgeButtonActionHandler* actionHandler;
-
-@end
-
 @implementation BadgeButtonFactory
 
-- (instancetype)initWithActionHandler:(BadgeButtonActionHandler*)actionHandler {
-  self = [super init];
-  if (self) {
-    _actionHandler = actionHandler;
-  }
-  return self;
-}
-
 - (BadgeButton*)getBadgeButtonForBadgeType:(BadgeType)badgeType {
   switch (badgeType) {
     case BadgeType::kBadgeTypePasswordSave:
@@ -58,7 +43,7 @@
       [self createButtonForType:BadgeType::kBadgeTypePasswordSave
                      imageNamed:@"infobar_passwords_icon"
                   renderingMode:UIImageRenderingModeAlwaysTemplate];
-  [button addTarget:self.actionHandler
+  [button addTarget:self.delegate
                 action:@selector(passwordsBadgeButtonTapped:)
       forControlEvents:UIControlEventTouchUpInside];
   button.accessibilityIdentifier =
@@ -73,7 +58,7 @@
       [self createButtonForType:BadgeType::kBadgeTypePasswordUpdate
                      imageNamed:@"infobar_passwords_icon"
                   renderingMode:UIImageRenderingModeAlwaysTemplate];
-  [button addTarget:self.actionHandler
+  [button addTarget:self.delegate
                 action:@selector(passwordsBadgeButtonTapped:)
       forControlEvents:UIControlEventTouchUpInside];
   button.accessibilityIdentifier =
@@ -106,7 +91,7 @@
       [self createButtonForType:BadgeType::kBadgeTypeOverflow
                      imageNamed:@"wrench_badge"
                   renderingMode:UIImageRenderingModeAlwaysTemplate];
-  [button addTarget:self.actionHandler
+  [button addTarget:self.delegate
                 action:@selector(overflowBadgeButtonTapped:)
       forControlEvents:UIControlEventTouchUpInside];
   button.accessibilityIdentifier = kBadgeButtonOverflowAccessibilityIdentifier;
diff --git a/ios/chrome/browser/ui/badges/badge_constants.h b/ios/chrome/browser/ui/badges/badge_constants.h
index 85fcac6..99db012 100644
--- a/ios/chrome/browser/ui/badges/badge_constants.h
+++ b/ios/chrome/browser/ui/badges/badge_constants.h
@@ -13,4 +13,7 @@
 extern NSString* const kBadgeButtonIncognitoAccessibilityIdentifier;
 extern NSString* const kBadgeButtonOverflowAccessibilityIdentifier;
 
+// A11y identifier for the Badge Popup Menu Table View.
+extern NSString* const kBadgePopupMenuTableViewAccessibilityIdentifier;
+
 #endif  // IOS_CHROME_BROWSER_UI_BADGES_BADGE_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/badges/badge_constants.mm b/ios/chrome/browser/ui/badges/badge_constants.mm
index 1a917cac..803194f 100644
--- a/ios/chrome/browser/ui/badges/badge_constants.mm
+++ b/ios/chrome/browser/ui/badges/badge_constants.mm
@@ -19,3 +19,6 @@
 
 NSString* const kBadgeButtonOverflowAccessibilityIdentifier =
     @"badgeButtonOverflowAXID";
+
+NSString* const kBadgePopupMenuTableViewAccessibilityIdentifier =
+    @"badgePopupMenuOverflowAXID";
diff --git a/ios/chrome/browser/ui/badges/badge_delegate.h b/ios/chrome/browser/ui/badges/badge_delegate.h
index 9c71a7e..538eeb4f 100644
--- a/ios/chrome/browser/ui/badges/badge_delegate.h
+++ b/ios/chrome/browser/ui/badges/badge_delegate.h
@@ -5,10 +5,13 @@
 #ifndef IOS_CHROME_BROWSER_UI_BADGES_BADGE_DELEGATE_H_
 #define IOS_CHROME_BROWSER_UI_BADGES_BADGE_DELEGATE_H_
 
-// Protocol to communicate Badge actions to the coordinator.
+// Protocol to communicate Badge actions to the mediator.
 @protocol BadgeDelegate
-// Shows the badge overflow menu.
-- (void)showOverflowMenu;
+// Action when a Passwords badge is tapped.
+- (void)passwordsBadgeButtonTapped:(id)sender;
+
+// Action when the overflow badge is tapped.
+- (void)overflowBadgeButtonTapped:(id)sender;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_BADGES_BADGE_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.h b/ios/chrome/browser/ui/badges/badge_mediator.h
index 31739f3..81e8e67 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator.h
+++ b/ios/chrome/browser/ui/badges/badge_mediator.h
@@ -7,11 +7,16 @@
 
 #import <UIKit/UIKit.h>
 
+#import "ios/chrome/browser/ui/badges/badge_delegate.h"
+
 @protocol BadgeConsumer;
+@protocol BadgeItem;
+@protocol BrowserCoordinatorCommands;
+@protocol InfobarCommands;
 class WebStateList;
 
 // A mediator object that updates the consumer when the state of badges changes.
-@interface BadgeMediator : NSObject
+@interface BadgeMediator : NSObject <BadgeDelegate>
 
 - (instancetype)initWithConsumer:(id<BadgeConsumer>)consumer
                     webStateList:(WebStateList*)webStateList
@@ -21,6 +26,10 @@
 // Stops observing all objects.
 - (void)disconnect;
 
+// The dispatcher for badge related actions.
+@property(nonatomic, weak) id<InfobarCommands, BrowserCoordinatorCommands>
+    dispatcher;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_BADGES_BADGE_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.mm b/ios/chrome/browser/ui/badges/badge_mediator.mm
index e52367a..1180cb8 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator.mm
@@ -4,12 +4,20 @@
 
 #import "ios/chrome/browser/ui/badges/badge_mediator.h"
 
+#include "base/mac/foundation_util.h"
+#include "base/metrics/user_metrics.h"
 #include "ios/chrome/browser/infobars/infobar_badge_tab_helper.h"
 #include "ios/chrome/browser/infobars/infobar_badge_tab_helper_delegate.h"
+#include "ios/chrome/browser/infobars/infobar_metrics_recorder.h"
 #import "ios/chrome/browser/infobars/infobar_type.h"
+#import "ios/chrome/browser/ui/badges/badge_button.h"
 #import "ios/chrome/browser/ui/badges/badge_consumer.h"
 #import "ios/chrome/browser/ui/badges/badge_item.h"
 #import "ios/chrome/browser/ui/badges/badge_static_item.h"
+#import "ios/chrome/browser/ui/badges/badge_tappable_item.h"
+#import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h"
+#import "ios/chrome/browser/ui/commands/infobar_commands.h"
+#import "ios/chrome/browser/ui/list_model/list_model.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
 #include "ios/web/public/browser_state.h"
@@ -18,6 +26,14 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+// The number of Fullscreen badges
+const int kNumberOfFullScrenBadges = 1;
+// The minimum number of non-Fullscreen badges to display the overflow popup
+// menu.
+const int kMinimumNonFullScreenBadgesForOverflow = 2;
+}
+
 @interface BadgeMediator () <InfobarBadgeTabHelperDelegate,
                              WebStateListObserving> {
   std::unique_ptr<WebStateListObserverBridge> _webStateListObserver;
@@ -70,7 +86,7 @@
 
 - (void)addInfobarBadge:(id<BadgeItem>)badgeItem {
   if (!self.badges) {
-    self.badges = [[NSMutableArray alloc] init];
+    self.badges = [NSMutableArray array];
   }
   [self.badges addObject:badgeItem];
   [self updateBadgesShown];
@@ -96,6 +112,47 @@
   }
 }
 
+#pragma mark - BadgeDelegate
+
+- (void)passwordsBadgeButtonTapped:(id)sender {
+  BadgeButton* badgeButton = base::mac::ObjCCastStrict<BadgeButton>(sender);
+  MobileMessagesBadgeState state;
+  if (badgeButton.accepted) {
+    state = MobileMessagesBadgeState::Active;
+    base::RecordAction(
+        base::UserMetricsAction("MobileMessagesBadgeAcceptedTapped"));
+  } else {
+    state = MobileMessagesBadgeState::Inactive;
+    base::RecordAction(
+        base::UserMetricsAction("MobileMessagesBadgeNonAcceptedTapped"));
+  }
+  InfobarMetricsRecorder* metricsRecorder;
+  if (badgeButton.badgeType == BadgeType::kBadgeTypePasswordSave) {
+    metricsRecorder = [[InfobarMetricsRecorder alloc]
+        initWithType:InfobarType::kInfobarTypePasswordSave];
+    [self.dispatcher displayModalInfobar:InfobarType::kInfobarTypePasswordSave];
+  } else if (badgeButton.badgeType == BadgeType::kBadgeTypePasswordUpdate) {
+    metricsRecorder = [[InfobarMetricsRecorder alloc]
+        initWithType:InfobarType::kInfobarTypePasswordUpdate];
+    [self.dispatcher
+        displayModalInfobar:InfobarType::kInfobarTypePasswordUpdate];
+  }
+  [metricsRecorder recordBadgeTappedInState:state];
+}
+
+- (void)overflowBadgeButtonTapped:(id)sender {
+  NSMutableArray<id<BadgeItem>>* popupMenuBadges =
+      [[NSMutableArray alloc] init];
+  // Get all non-fullscreen badges.
+  for (id<BadgeItem> item in self.badges) {
+    if (![item isFullScreen]) {
+      [popupMenuBadges addObject:item];
+    }
+  }
+  [self.dispatcher displayPopupMenuWithBadgeItems:popupMenuBadges];
+  // TODO(crbug.com/976901): Add metric for this action.
+}
+
 #pragma mark - WebStateListObserver
 
 - (void)webStateList:(WebStateList*)webStateList
@@ -131,12 +188,20 @@
   id<BadgeItem> displayedBadge;
   id<BadgeItem> fullScreenBadge;
   for (id<BadgeItem> item in self.badges) {
-    if (item.isFullScreen) {
+    if ([item isFullScreen]) {
       fullScreenBadge = item;
     } else {
       displayedBadge = item;
     }
   }
+  NSInteger count = [self.badges count];
+  if (fullScreenBadge) {
+    count -= kNumberOfFullScrenBadges;
+  }
+  if (count >= kMinimumNonFullScreenBadgesForOverflow) {
+    displayedBadge = [[BadgeTappableItem alloc]
+        initWithBadgeType:BadgeType::kBadgeTypeOverflow];
+  }
   [self.consumer updateDisplayedBadge:displayedBadge
                       fullScreenBadge:fullScreenBadge];
 }
@@ -156,9 +221,11 @@
   self.badges = [[NSArray arrayWithObjects:&infobarBadges[0]
                                      count:infobarBadges.size()] mutableCopy];
   id<BadgeItem> displayedBadge;
-  // Set the last badge as the displayed badge, since there is currently only
-  // one Infobar with a badge.
-  if ([self.badges count] > 0) {
+  if ([self.badges count] > 1) {
+    // Show the overflow menu badge when there are multiple badges.
+    displayedBadge = [[BadgeTappableItem alloc]
+        initWithBadgeType:BadgeType::kBadgeTypeOverflow];
+  } else if ([self.badges count] == 1) {
     displayedBadge = [self.badges lastObject];
   }
   id<BadgeItem> fullScreenBadge;
diff --git a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
index a759447..12dc1a8 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
@@ -158,7 +158,7 @@
   AddInfobar();
   AddSecondInfobar();
   EXPECT_EQ(badge_consumer_.displayedBadge.badgeType,
-            BadgeType::kBadgeTypePasswordUpdate);
+            BadgeType::kBadgeTypeOverflow);
   RemoveInfobar();
   ASSERT_TRUE(badge_consumer_.displayedBadge);
   EXPECT_EQ(badge_consumer_.displayedBadge.badgeType,
diff --git a/ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.mm b/ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.mm
index 9aee30c5..5ee73b82 100644
--- a/ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.mm
+++ b/ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.mm
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #import "ios/chrome/browser/infobars/infobar_type.h"
+#import "ios/chrome/browser/ui/badges/badge_constants.h"
 #import "ios/chrome/browser/ui/badges/badge_item.h"
 #import "ios/chrome/browser/ui/badges/badge_popup_menu_item.h"
 #import "ios/chrome/browser/ui/popup_menu/public/cells/popup_menu_item.h"
@@ -45,6 +46,8 @@
   self.popupViewController = [[PopupMenuTableViewController alloc] init];
   self.popupViewController.baseViewController = self.baseViewController;
   self.popupViewController.delegate = self;
+  self.popupViewController.tableView.accessibilityIdentifier =
+      kBadgePopupMenuTableViewAccessibilityIdentifier;
   self.consumer = self.popupViewController;
   [self.consumer setPopupMenuItems:self.popupMenuItems];
   self.popupMenuPresenter = [[PopupMenuPresenter alloc] init];
diff --git a/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm b/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
index eab7ba6..23864cb 100644
--- a/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
+++ b/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
@@ -71,6 +71,7 @@
            withStyler:(ChromeTableViewStyler*)styler {
   [super configureCell:cell withStyler:styler];
   cell.titleLabel.text = self.title;
+  cell.accessibilityTraits = UIAccessibilityTraitButton;
   UIImage* badgeImage;
   switch (self.badgeType) {
     case BadgeType::kBadgeTypePasswordSave:
diff --git a/ios/chrome/browser/ui/badges/badge_view_controller.mm b/ios/chrome/browser/ui/badges/badge_view_controller.mm
index 73d239e..ccb00aa 100644
--- a/ios/chrome/browser/ui/badges/badge_view_controller.mm
+++ b/ios/chrome/browser/ui/badges/badge_view_controller.mm
@@ -169,10 +169,4 @@
   [self.stackView insertArrangedSubview:_fullScreenBadge atIndex:0];
 }
 
-#pragma mark - Helpers
-
-- (void)updateDisplayedBadges {
-  self.displayedBadge = [self.badges lastObject];
-}
-
 @end
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn
index 8ba80242..cfa78c6 100644
--- a/ios/chrome/browser/ui/browser_view/BUILD.gn
+++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -81,6 +81,7 @@
     "//ios/chrome/browser/ui/autofill/form_input_accessory",
     "//ios/chrome/browser/ui/autofill/manual_fill",
     "//ios/chrome/browser/ui/autofill/manual_fill:manual_fill_ui",
+    "//ios/chrome/browser/ui/badges:badges_popup_menu",
     "//ios/chrome/browser/ui/bookmarks",
     "//ios/chrome/browser/ui/browser_container",
     "//ios/chrome/browser/ui/browser_container:ui",
@@ -166,6 +167,7 @@
     "//ios/public/provider/chrome/browser/ui",
     "//ios/public/provider/chrome/browser/voice",
     "//ios/third_party/material_components_ios",
+    "//ios/third_party/webkit",
     "//ios/web",
     "//ios/web/common",
     "//ios/web/public",
@@ -183,7 +185,6 @@
     "MessageUI.framework",
     "Photos.framework",
     "UIKit.framework",
-    "WebKit.framework",
   ]
 }
 
@@ -267,6 +268,7 @@
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/third_party/earl_grey:earl_grey+link",
+    "//ios/third_party/webkit",
     "//ios/web:earl_grey_test_support",
     "//ios/web/public/test",
     "//ios/web/public/test/http_server",
@@ -275,7 +277,6 @@
   ]
   libs = [
     "UIKit.framework",
-    "WebKit.framework",
     "XCTest.framework",
   ]
 }
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 8762be02..0b69cf98 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/all_password_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h"
+#import "ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.h"
 #import "ios/chrome/browser/ui/browser_container/browser_container_coordinator.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller+private.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller.h"
@@ -29,6 +30,7 @@
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/browser_coordinator_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/ui/commands/infobar_commands.h"
 #import "ios/chrome/browser/ui/download/ar_quick_look_coordinator.h"
 #import "ios/chrome/browser/ui/download/pass_kit_coordinator.h"
 #import "ios/chrome/browser/ui/open_in/open_in_mediator.h"
@@ -90,6 +92,10 @@
 @property(nonatomic, strong)
     AutofillAddCreditCardCoordinator* addCreditCardCoordinator;
 
+// Coordinator for the badge popup menu.
+@property(nonatomic, strong)
+    BadgePopupMenuCoordinator* badgePopupMenuCoordinator;
+
 // Coordinator in charge of the presenting autofill options above the
 // keyboard.
 @property(nonatomic, strong)
@@ -213,6 +219,15 @@
                                           dismissOmnibox:dismissOmnibox];
 }
 
+- (void)displayPopupMenuWithBadgeItems:(NSArray<id<BadgeItem>>*)badgeItems {
+  self.badgePopupMenuCoordinator = [[BadgePopupMenuCoordinator alloc]
+      initWithBaseViewController:self.viewController];
+  self.badgePopupMenuCoordinator.dispatcher =
+      static_cast<id<InfobarCommands>>(self.dispatcher);
+  [self.badgePopupMenuCoordinator setBadgeItemsToShow:badgeItems];
+  [self.badgePopupMenuCoordinator start];
+}
+
 #pragma mark - Private
 
 // Instantiates a BrowserViewController.
diff --git a/ios/chrome/browser/ui/commands/BUILD.gn b/ios/chrome/browser/ui/commands/BUILD.gn
index 32b7034..c08a3b1 100644
--- a/ios/chrome/browser/ui/commands/BUILD.gn
+++ b/ios/chrome/browser/ui/commands/BUILD.gn
@@ -8,7 +8,6 @@
   sources = [
     "activity_service_commands.h",
     "application_commands.h",
-    "badge_commands.h",
     "browser_commands.h",
     "browser_coordinator_commands.h",
     "browsing_data_commands.h",
diff --git a/ios/chrome/browser/ui/commands/badge_commands.h b/ios/chrome/browser/ui/commands/badge_commands.h
deleted file mode 100644
index 778eda6..0000000
--- a/ios/chrome/browser/ui/commands/badge_commands.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_COMMANDS_BADGE_COMMANDS_H_
-#define IOS_CHROME_BROWSER_UI_COMMANDS_BADGE_COMMANDS_H_
-
-#import <Foundation/Foundation.h>
-
-@protocol BadgeItem;
-
-@protocol BadgeCommands <NSObject>
-// Displays the Badge popup menu showing |badgeItems|.
-- (void)displayPopupMenuWithBadgeItems:(NSArray<id<BadgeItem>>*)badgeItems;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_COMMANDS_BADGE_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/commands/browser_coordinator_commands.h b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
index 8aeaa92..84d6225 100644
--- a/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
+++ b/ios/chrome/browser/ui/commands/browser_coordinator_commands.h
@@ -7,6 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
+@protocol BadgeItem;
+
 // Protocol for commands that will be handled by the BrowserCoordinator.
 // TODO(crbug.com/906662) : Rename this protocol to one that is more descriptive
 // and representative of the contents.
@@ -24,6 +26,9 @@
 // Shows the AddCreditCard UI.
 - (void)showAddCreditCard;
 
+// Displays the Badge popup menu showing |badgeItems|.
+- (void)displayPopupMenuWithBadgeItems:(NSArray<id<BadgeItem>>*)badgeItems;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_COMMANDS_BROWSER_COORDINATOR_COMMANDS_H_
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index d7ddcd8a..d0729d66 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -23,7 +23,6 @@
 #import "ios/chrome/browser/ntp/new_tab_page_tab_helper.h"
 #import "ios/chrome/browser/overlays/public/overlay_presenter.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#import "ios/chrome/browser/ui/badges/badge_button_action_handler.h"
 #import "ios/chrome/browser/ui/badges/badge_button_factory.h"
 #import "ios/chrome/browser/ui/badges/badge_delegate.h"
 #import "ios/chrome/browser/ui/badges/badge_mediator.h"
@@ -73,8 +72,7 @@
 const int kLocationAuthorizationStatusCount = 5;
 }  // namespace
 
-@interface LocationBarCoordinator () <BadgeDelegate,
-                                      LoadQueryCommands,
+@interface LocationBarCoordinator () <LoadQueryCommands,
                                       LocationBarDelegate,
                                       LocationBarViewControllerDelegate,
                                       LocationBarConsumer> {
@@ -175,16 +173,9 @@
   self.omniboxPopupCoordinator.webStateList = self.webStateList;
   [self.omniboxPopupCoordinator start];
 
-  // Create an action handler that will handle the action to take for button
-  // taps.
-  BadgeButtonActionHandler* actionHandler =
-      [[BadgeButtonActionHandler alloc] init];
-  actionHandler.dispatcher = static_cast<id<InfobarCommands>>(self.dispatcher);
-  actionHandler.buttonActionDelegate = self;
   // Create button factory that wil be used by the ViewController to get
   // BadgeButtons for a BadgeType.
-  BadgeButtonFactory* buttonFactory =
-      [[BadgeButtonFactory alloc] initWithActionHandler:actionHandler];
+  BadgeButtonFactory* buttonFactory = [[BadgeButtonFactory alloc] init];
   buttonFactory.incognito = isIncognito;
   self.badgeViewController =
       [[BadgeViewController alloc] initWithButtonFactory:buttonFactory];
@@ -195,6 +186,10 @@
   self.badgeMediator =
       [[BadgeMediator alloc] initWithConsumer:self.badgeViewController
                                  webStateList:self.webStateList];
+  self.badgeMediator.dispatcher =
+      static_cast<id<InfobarCommands, BrowserCoordinatorCommands>>(
+          self.dispatcher);
+  buttonFactory.delegate = self.badgeMediator;
   _fullscreenBadgeObserver =
       std::make_unique<FullscreenUIUpdater>(self.badgeViewController);
   FullscreenControllerFactory::GetInstance()
@@ -262,13 +257,6 @@
   return self.omniboxCoordinator.animatee;
 }
 
-#pragma mark - BadgeDelegate
-
-- (void)showOverflowMenu {
-  // TODO(crbug.com/976901): Retrieve badges to show in overflow menu and send
-  // signal to BVC.
-}
-
 #pragma mark - LoadQueryCommands
 
 - (void)loadQuery:(NSString*)query immediately:(BOOL)immediately {
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler.mm b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
index c615f1a..61b9769 100644
--- a/ios/chrome/browser/ui/main/browser_view_wrangler.mm
+++ b/ios/chrome/browser/ui/main/browser_view_wrangler.mm
@@ -255,7 +255,7 @@
     WebStateList* webStateList = self.mainBrowser->GetWebStateList();
     breakpad::StopMonitoringTabStateForWebStateList(webStateList);
     breakpad::StopMonitoringURLsForWebStateList(webStateList);
-    [tabModel browserStateDestroyed];
+    [tabModel disconnect];
     _activeWebStateObservationForwarders[webStateList] = nullptr;
     webStateList->RemoveObserver(_webStateListObserver.get());
     webStateList->RemoveObserver(_webStateListForwardingObserver.get());
@@ -269,7 +269,7 @@
     TabModel* tabModel = self.otrBrowser->GetTabModel();
     WebStateList* webStateList = self.otrBrowser->GetWebStateList();
     breakpad::StopMonitoringTabStateForWebStateList(webStateList);
-    [tabModel browserStateDestroyed];
+    [tabModel disconnect];
     _activeWebStateObservationForwarders[webStateList] = nullptr;
     webStateList->RemoveObserver(_webStateListObserver.get());
     webStateList->RemoveObserver(_webStateListForwardingObserver.get());
diff --git a/ios/chrome/browser/ui/promos/BUILD.gn b/ios/chrome/browser/ui/promos/BUILD.gn
index b0499b10..7dfff4e 100644
--- a/ios/chrome/browser/ui/promos/BUILD.gn
+++ b/ios/chrome/browser/ui/promos/BUILD.gn
@@ -11,6 +11,7 @@
   ]
   deps = [
     "//base",
+    "//components/signin/ios/browser",
     "//components/signin/public/base",
     "//components/version_info",
     "//ios/chrome/app:tests_hook",
diff --git a/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm b/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm
index 0a52f43..882b212 100644
--- a/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm
+++ b/ios/chrome/browser/ui/promos/signin_promo_view_controller.mm
@@ -8,6 +8,7 @@
 #include "base/metrics/user_metrics.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/version.h"
+#include "components/signin/ios/browser/features.h"
 #include "components/signin/public/base/signin_metrics.h"
 #include "components/version_info/version_info.h"
 #include "ios/chrome/app/tests_hook.h"
@@ -178,6 +179,9 @@
 
 + (BOOL)shouldBePresentedForBrowserState:
     (ios::ChromeBrowserState*)browserState {
+  if (signin::ForceStartupSigninPromo())
+    return YES;
+
   if (tests_hook::DisableSigninRecallPromo())
     return NO;
 
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
index ffdf772..3cf3c75 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller.mm
@@ -245,7 +245,8 @@
   item.text =
       l10n_util::GetNSString(IDS_IOS_OPTIONS_ACCOUNTS_ADD_ACCOUNT_BUTTON);
   item.accessibilityIdentifier = kSettingsAccountsTableViewAddAccountCellId;
-  item.image = [UIImage imageNamed:@"settings_accounts_add_account"];
+  item.image = [[UIImage imageNamed:@"settings_accounts_add_account"]
+      imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
   return item;
 }
 
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn
index d2e5296..a50dfc2 100644
--- a/ios/chrome/browser/web/BUILD.gn
+++ b/ios/chrome/browser/web/BUILD.gn
@@ -420,10 +420,10 @@
     "//base/test:test_support",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/test/base:perf_test_support",
+    "//ios/third_party/webkit",
     "//ios/web/common:web_view_creation_util",
     "//ios/web/public/test",
   ]
-  libs = [ "WebKit.framework" ]
 }
 
 source_set("feature_flags") {
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 4f2fe8ee..a891341 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -266,6 +266,7 @@
     "//ios/chrome/test/app:test_support",
     "//ios/testing/earl_grey:earl_grey_support",
     "//ios/third_party/material_components_ios",
+    "//ios/third_party/webkit",
     "//ios/web",
     "//ios/web:earl_grey_test_support",
     "//ios/web/common",
@@ -290,7 +291,6 @@
 
   libs = [
     "OCHamcrest.framework",
-    "WebKit.framework",
     "XCTest.framework",
     "IOKit.framework",
   ]
diff --git a/ios/showcase/badges/BUILD.gn b/ios/showcase/badges/BUILD.gn
index fc484955..7fdc441 100644
--- a/ios/showcase/badges/BUILD.gn
+++ b/ios/showcase/badges/BUILD.gn
@@ -10,6 +10,8 @@
   deps = [
     "//ios/chrome/browser/infobars:badge",
     "//ios/chrome/browser/ui/badges",
+    "//ios/chrome/browser/ui/badges:badges_popup_menu",
+    "//ios/chrome/browser/ui/util",
     "//ios/chrome/common/ui_util",
     "//ios/showcase/common",
   ]
@@ -26,6 +28,7 @@
     "//ios/chrome/browser/infobars:badge",
     "//ios/chrome/browser/ui/badges:public",
     "//ios/chrome/test/earl_grey:test_support",
+    "//ios/showcase/badges",
     "//ios/showcase/test",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/showcase/badges/sc_badge_coordinator.h b/ios/showcase/badges/sc_badge_coordinator.h
index 59b8d5a..2625ab8 100644
--- a/ios/showcase/badges/sc_badge_coordinator.h
+++ b/ios/showcase/badges/sc_badge_coordinator.h
@@ -7,6 +7,10 @@
 
 #import "ios/showcase/common/navigation_coordinator.h"
 
+// A11y identifier for button that will replace the displayed badge with the
+// overflow badge button.
+extern NSString* const kSCDisplayedBadgeToggleButton;
+
 @interface SCBadgeCoordinator : NSObject <NavigationCoordinator>
 
 @end
diff --git a/ios/showcase/badges/sc_badge_coordinator.mm b/ios/showcase/badges/sc_badge_coordinator.mm
index 429081b..ba71dee7d 100644
--- a/ios/showcase/badges/sc_badge_coordinator.mm
+++ b/ios/showcase/badges/sc_badge_coordinator.mm
@@ -7,45 +7,91 @@
 #include "ios/chrome/browser/infobars/infobar_badge_model.h"
 #import "ios/chrome/browser/ui/badges/badge_button_factory.h"
 #import "ios/chrome/browser/ui/badges/badge_consumer.h"
+#import "ios/chrome/browser/ui/badges/badge_delegate.h"
+#import "ios/chrome/browser/ui/badges/badge_popup_menu_coordinator.h"
 #import "ios/chrome/browser/ui/badges/badge_static_item.h"
+#import "ios/chrome/browser/ui/badges/badge_tappable_item.h"
 #import "ios/chrome/browser/ui/badges/badge_view_controller.h"
+#import "ios/chrome/browser/ui/util/named_guide.h"
+#import "ios/chrome/browser/ui/util/named_guide_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+NSString* const kSCDisplayedBadgeToggleButton =
+    @"kSCDisplayedBadgeToggleButtonAXID";
+
 @interface BadgeContainerViewController : UIViewController
 @property(nonatomic, strong) BadgeViewController* centeredChildViewController;
+@property(nonatomic, weak) id<BadgeConsumer> consumer;
 @end
 
 @implementation BadgeContainerViewController
 - (void)viewDidLoad {
   [super viewDidLoad];
+
+  UIStackView* stackView = [[UIStackView alloc] init];
+  stackView.translatesAutoresizingMaskIntoConstraints = NO;
+  stackView.axis = UILayoutConstraintAxisHorizontal;
+  [self.view addSubview:stackView];
+  [NSLayoutConstraint activateConstraints:@[
+    [stackView.widthAnchor constraintEqualToConstant:400],
+    [stackView.heightAnchor constraintEqualToConstant:100]
+  ]];
+  AddSameCenterConstraints(stackView, self.view);
+
   [self addChildViewController:self.centeredChildViewController];
-  [self.view addSubview:self.centeredChildViewController.view];
+  [stackView addArrangedSubview:self.centeredChildViewController.view];
   [self didMoveToParentViewController:self.centeredChildViewController];
   self.centeredChildViewController.view
       .translatesAutoresizingMaskIntoConstraints = NO;
-  AddSameCenterConstraints(self.centeredChildViewController.view, self.view);
   [NSLayoutConstraint activateConstraints:@[
     [self.centeredChildViewController.view.widthAnchor
         constraintGreaterThanOrEqualToConstant:1],
     [self.centeredChildViewController.view.heightAnchor
         constraintEqualToConstant:100]
   ]];
+
+  UIButton* button = [UIButton buttonWithType:UIButtonTypeSystem];
+  button.accessibilityIdentifier = kSCDisplayedBadgeToggleButton;
+  [button setTitle:@"Show Overflow badge" forState:UIControlStateNormal];
+  [button addTarget:self
+                action:@selector(addSecondBadge:)
+      forControlEvents:UIControlEventTouchUpInside];
+  [stackView addArrangedSubview:button];
+
   UIView* containerView = self.view;
   containerView.backgroundColor = [UIColor whiteColor];
   self.title = @"Badges";
+  AddNamedGuidesToView(@[ kBadgeOverflowMenuGuide ], self.view);
+  BadgeStaticItem* incognitoItem = [[BadgeStaticItem alloc]
+      initWithBadgeType:BadgeType::kBadgeTypeIncognito];
+  InfobarBadgeModel* passwordBadgeItem = [[InfobarBadgeModel alloc]
+      initWithInfobarType:InfobarType::kInfobarTypePasswordSave];
+  [self.consumer setupWithDisplayedBadge:passwordBadgeItem
+                         fullScreenBadge:incognitoItem];
+}
+
+- (void)addSecondBadge:(id)sender {
+  BadgeStaticItem* incognitoItem = [[BadgeStaticItem alloc]
+      initWithBadgeType:BadgeType::kBadgeTypeIncognito];
+  BadgeTappableItem* displayedBadge = [[BadgeTappableItem alloc]
+      initWithBadgeType:BadgeType::kBadgeTypeOverflow];
+  [self.consumer setupWithDisplayedBadge:displayedBadge
+                         fullScreenBadge:incognitoItem];
 }
 
 @end
 
-@interface SCBadgeCoordinator ()
+@interface SCBadgeCoordinator () <BadgeDelegate>
 @property(nonatomic, strong)
     BadgeContainerViewController* containerViewController;
 @property(nonatomic, strong) BadgeViewController* badgeViewController;
 @property(nonatomic, weak) id<BadgeConsumer> consumer;
+@property(nonatomic, strong)
+    BadgePopupMenuCoordinator* badgePopupMenuCoordinator;
 @end
 
 @implementation SCBadgeCoordinator
@@ -53,21 +99,28 @@
 
 - (void)start {
   self.containerViewController = [[BadgeContainerViewController alloc] init];
-  BadgeButtonFactory* buttonFactory =
-      [[BadgeButtonFactory alloc] initWithActionHandler:nil];
+  BadgeButtonFactory* buttonFactory = [[BadgeButtonFactory alloc] init];
+  buttonFactory.delegate = self;
   self.badgeViewController =
       [[BadgeViewController alloc] initWithButtonFactory:buttonFactory];
   self.consumer = self.badgeViewController;
+  self.containerViewController.consumer = self.badgeViewController;
   self.containerViewController.centeredChildViewController =
       self.badgeViewController;
   [self.baseViewController pushViewController:self.containerViewController
                                      animated:YES];
-  BadgeStaticItem* incognitoItem = [[BadgeStaticItem alloc]
-      initWithBadgeType:BadgeType::kBadgeTypeIncognito];
-  InfobarBadgeModel* passwordBadgeItem = [[InfobarBadgeModel alloc]
-      initWithInfobarType:InfobarType::kInfobarTypePasswordSave];
-  [self.consumer setupWithDisplayedBadge:passwordBadgeItem
-                         fullScreenBadge:incognitoItem];
+}
+
+- (void)passwordsBadgeButtonTapped:(id)sender {
+}
+
+- (void)overflowBadgeButtonTapped:(id)sender {
+  self.badgePopupMenuCoordinator = [[BadgePopupMenuCoordinator alloc]
+      initWithBaseViewController:self.containerViewController];
+  NSArray* badgeItems = @[ [[InfobarBadgeModel alloc]
+      initWithInfobarType:InfobarType::kInfobarTypePasswordSave] ];
+  [self.badgePopupMenuCoordinator setBadgeItemsToShow:badgeItems];
+  [self.badgePopupMenuCoordinator start];
 }
 
 @end
diff --git a/ios/showcase/badges/sc_badge_egtest.mm b/ios/showcase/badges/sc_badge_egtest.mm
index 11f8c80f..f008253 100644
--- a/ios/showcase/badges/sc_badge_egtest.mm
+++ b/ios/showcase/badges/sc_badge_egtest.mm
@@ -6,6 +6,7 @@
 
 #import "ios/chrome/browser/ui/badges/badge_constants.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/showcase/badges/sc_badge_coordinator.h"
 #import "ios/showcase/test/showcase_eg_utils.h"
 #import "ios/showcase/test/showcase_test_case.h"
 
@@ -50,4 +51,32 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
+// Tests that the overflow badge presents and that the popup menu is presented
+// when it is tapped.
+- (void)testOverflowbadge {
+  // Tap on button to show the overflow badge.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                          kSCDisplayedBadgeToggleButton)]
+      performAction:grey_tap()];
+
+  // Assert that overflow badge is shown and tap on it.
+  [[EarlGrey selectElementWithMatcher:
+                 grey_allOf(grey_accessibilityID(
+                                kBadgeButtonOverflowAccessibilityIdentifier),
+                            grey_sufficientlyVisible(), nil)]
+      assertWithMatcher:grey_sufficientlyVisible()];
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(
+                                   kBadgeButtonOverflowAccessibilityIdentifier)]
+      performAction:grey_tap()];
+
+  // Assert that the badge overflow popup menu is being presented.
+  [[EarlGrey
+      selectElementWithMatcher:
+          grey_allOf(grey_accessibilityID(
+                         kBadgePopupMenuTableViewAccessibilityIdentifier),
+                     grey_sufficientlyVisible(), nil)]
+      assertWithMatcher:grey_sufficientlyVisible()];
+}
+
 @end
diff --git a/ios/third_party/earl_grey/BUILD.gn b/ios/third_party/earl_grey/BUILD.gn
index 710332d1..1c9a57a 100644
--- a/ios/third_party/earl_grey/BUILD.gn
+++ b/ios/third_party/earl_grey/BUILD.gn
@@ -290,6 +290,7 @@
   deps = [
     "//build/config/ios:xctest",
     "//ios/third_party/fishhook",
+    "//ios/third_party/webkit",
   ]
   public_deps = [
     "//ios/third_party/ochamcrest:ochamcrest+link",
@@ -306,7 +307,6 @@
     "IOKit.framework",
     "QuartzCore.framework",
     "UIKit.framework",
-    "WebKit.framework",
     "XCTest.framework",
   ]
   public_configs = [ ":config" ]
diff --git a/ios/third_party/earl_grey2/BUILD.gn b/ios/third_party/earl_grey2/BUILD.gn
index e5deb17..11190f93 100644
--- a/ios/third_party/earl_grey2/BUILD.gn
+++ b/ios/third_party/earl_grey2/BUILD.gn
@@ -386,6 +386,7 @@
     "//build/config/ios:xctest",
     "//ios/third_party/edo",
     "//ios/third_party/fishhook",
+    "//ios/third_party/webkit",
   ]
 
   public_deps = [
@@ -400,7 +401,6 @@
     "Foundation.framework",
     "IOKit.framework",
     "QuartzCore.framework",
-    "WebKit.framework",
   ]
 
   public_configs = [ ":config" ]
diff --git a/ios/third_party/webkit/BUILD.gn b/ios/third_party/webkit/BUILD.gn
index b3ccc594..c6fbbd5 100644
--- a/ios/third_party/webkit/BUILD.gn
+++ b/ios/third_party/webkit/BUILD.gn
@@ -5,8 +5,20 @@
 import("//build/config/gclient_args.gni")
 import("//build/config/ios/ios_sdk.gni")
 
+group("webkit") {
+  if (checkout_ios_webkit) {
+    deps = [
+      ":compile_webkit",
+    ]
+  }
+
+  all_dependent_configs = [ ":_webkit_config" ]
+}
+
 if (checkout_ios_webkit) {
-  action("webkit") {
+  action("compile_webkit") {
+    visibility = [ ":webkit" ]
+
     script = "build_webkit.py"
 
     inputs = [
@@ -52,3 +64,20 @@
     ]
   }
 }
+
+config("_webkit_config") {
+  if (checkout_ios_webkit) {
+    # From the ld documentation: "Directories specified with -F are searched in
+    # the order they appear on the command line and before the default search
+    # path."
+    common_flags = [
+      "-F",
+      rebase_path("$target_out_dir/Debug-iphonesimulator/", root_build_dir),
+    ]
+
+    cflags = common_flags
+    ldflags = common_flags
+  }
+
+  libs = [ "WebKit.framework" ]
+}
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index f4aea84..7f99aaab 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -39,6 +39,7 @@
     ":threads",
     "//base",
     "//components/leveldb_proto",
+    "//ios/third_party/webkit",
     "//ios/web/common",
     "//ios/web/download",
     "//ios/web/favicon",
@@ -66,8 +67,6 @@
     "web_client.mm",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index 1b118fd..357e0e6 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -21,7 +21,7 @@
     "CrashOnUnexpectedURLChange", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kHistoryClobberWorkaround{
-    "WKWebViewHistoryClobberWorkaround", base::FEATURE_ENABLED_BY_DEFAULT};
+    "WKWebViewHistoryClobberWorkaround", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kBlockUniversalLinksInOffTheRecordMode{
     "BlockUniversalLinksInOffTheRecord", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/ios/web/navigation/wk_based_navigation_manager_impl.mm b/ios/web/navigation/wk_based_navigation_manager_impl.mm
index ac3b2c1..10dc540 100644
--- a/ios/web/navigation/wk_based_navigation_manager_impl.mm
+++ b/ios/web/navigation/wk_based_navigation_manager_impl.mm
@@ -108,9 +108,19 @@
     restoration_timer_ = std::make_unique<base::ElapsedTimer>();
   } else if (!wk_navigation_util::IsRestoreSessionUrl(url)) {
     is_restore_session_in_progress_ = false;
-    DCHECK(restoration_timer_);
-    UMA_HISTOGRAM_TIMES(kRestoreNavigationTime, restoration_timer_->Elapsed());
-    restoration_timer_.reset();
+    // It's possible for there to be pending navigations for a session that is
+    // going to be restored (such as for the -ForwardHistoryClobber workaround).
+    // In this case, the pending navigation will start while the navigation
+    // manager is in restore mode.  There are other edges cases where a restore
+    // session finishes without trigger it's start, such as when restoring some
+    // with some app specific or blocked URLs, or when WKWebView's
+    // backForwardList state is out of sync. See crbug.com/1008026 for more
+    // details.
+    if (restoration_timer_) {
+      UMA_HISTOGRAM_TIMES(kRestoreNavigationTime,
+                          restoration_timer_->Elapsed());
+      restoration_timer_.reset();
+    }
 
     for (base::OnceClosure& callback : restore_session_completion_callbacks_) {
       std::move(callback).Run();
diff --git a/ios/web/net/cookies/BUILD.gn b/ios/web/net/cookies/BUILD.gn
index b0e418af..dfeed04 100644
--- a/ios/web/net/cookies/BUILD.gn
+++ b/ios/web/net/cookies/BUILD.gn
@@ -8,6 +8,7 @@
   deps = [
     "//base",
     "//ios/net",
+    "//ios/third_party/webkit",
     "//ios/web/common",
     "//ios/web/public",
     "//ios/web/web_state/ui:wk_web_view_configuration_provider",
@@ -22,7 +23,5 @@
     "wk_http_system_cookie_store.mm",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/web/public/BUILD.gn b/ios/web/public/BUILD.gn
index 49bb079..e5c556a 100644
--- a/ios/web/public/BUILD.gn
+++ b/ios/web/public/BUILD.gn
@@ -16,6 +16,7 @@
   ]
 
   deps = [
+    "//ios/third_party/webkit",
     "//ios/web/common",
     "//ios/web/common:user_agent",
     "//ios/web/public/deprecated",
@@ -32,8 +33,6 @@
     "web_state_user_data.h",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
diff --git a/ios/web/public/webui/BUILD.gn b/ios/web/public/webui/BUILD.gn
index 4f59e437..b824393 100644
--- a/ios/web/public/webui/BUILD.gn
+++ b/ios/web/public/webui/BUILD.gn
@@ -7,6 +7,7 @@
 source_set("webui") {
   deps = [
     "//base",
+    "//ios/third_party/webkit",
     "//net",
   ]
 
@@ -19,7 +20,5 @@
     "web_ui_ios_message_handler.h",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/web/test/fakes/BUILD.gn b/ios/web/test/fakes/BUILD.gn
index bbfc030..438b1cdb 100644
--- a/ios/web/test/fakes/BUILD.gn
+++ b/ios/web/test/fakes/BUILD.gn
@@ -8,6 +8,7 @@
 
   deps = [
     "//base",
+    "//ios/third_party/webkit",
     "//ios/web/navigation",
     "//ios/web/navigation:core",
     "//ios/web/public",
@@ -38,6 +39,4 @@
     "mock_interstitial_delegate.h",
     "mock_interstitial_delegate.mm",
   ]
-
-  libs = [ "WebKit.framework" ]
 }
diff --git a/ios/web/web_state/BUILD.gn b/ios/web/web_state/BUILD.gn
index 23abe86..33c4adc 100644
--- a/ios/web/web_state/BUILD.gn
+++ b/ios/web/web_state/BUILD.gn
@@ -9,6 +9,7 @@
     ":context_menu",
     ":web_state_impl_header",
     "//base",
+    "//ios/third_party/webkit",
     "//ios/web/common",
     "//ios/web/js_messaging",
     "//ios/web/navigation",
@@ -43,8 +44,6 @@
     "web_state_policy_decider_bridge.mm",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index 0b66a6af..94898ff8 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -10,6 +10,7 @@
     ":crw_web_view_navigation_proxy",
     "//base",
     "//ios/net",
+    "//ios/third_party/webkit",
     "//ios/web:core",
     "//ios/web/browsing_data",
     "//ios/web/common",
@@ -65,8 +66,6 @@
     "js_window_error_manager.mm",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
@@ -75,7 +74,9 @@
     "crw_web_view_navigation_proxy.h",
   ]
 
-  libs = [ "WebKit.framework" ]
+  deps = [
+    "//ios/third_party/webkit",
+  ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
 }
@@ -104,6 +105,7 @@
 source_set("wk_web_view_configuration_provider") {
   deps = [
     "//base",
+    "//ios/third_party/webkit",
     "//ios/web/common",
     "//ios/web/js_messaging",
     "//ios/web/public",
@@ -117,7 +119,5 @@
     "wk_web_view_configuration_provider_observer.h",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/web/web_state/ui/controller/BUILD.gn b/ios/web/web_state/ui/controller/BUILD.gn
index ebb6a6ee..c38eac6 100644
--- a/ios/web/web_state/ui/controller/BUILD.gn
+++ b/ios/web/web_state/ui/controller/BUILD.gn
@@ -10,6 +10,7 @@
   ]
 
   deps = [
+    "//ios/third_party/webkit",
     "//ios/web/navigation:core",
     "//ios/web/net",
     "//ios/web/public",
@@ -19,7 +20,5 @@
     "//url",
   ]
 
-  libs = [ "WebKit.framework" ]
-
   configs += [ "//build/config/compiler:enable_arc" ]
 }
diff --git a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
index 94f359a..10c4447 100644
--- a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
+++ b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
@@ -12,6 +12,7 @@
 #include "components/signin/public/base/device_id_helper.h"
 #include "components/sync/model/model_type_store_service.h"
 #include "components/sync_device_info/device_info_prefs.h"
+#include "components/sync_device_info/device_info_sync_client.h"
 #include "components/sync_device_info/device_info_sync_service_impl.h"
 #include "components/sync_device_info/local_device_info_provider_impl.h"
 #include "components/version_info/version_info.h"
@@ -22,6 +23,27 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+class DeviceInfoSyncClient : public syncer::DeviceInfoSyncClient {
+ public:
+  explicit DeviceInfoSyncClient(PrefService* prefs) : prefs_(prefs) {}
+  ~DeviceInfoSyncClient() override = default;
+
+  // syncer::DeviceInfoSyncClient:
+  std::string GetSigninScopedDeviceId() const override {
+    return signin::GetSigninScopedDeviceId(prefs_);
+  }
+
+  // syncer::DeviceInfoSyncClient:
+  bool GetSendTabToSelfReceivingEnabled() const override { return false; }
+
+ private:
+  PrefService* const prefs_;
+};
+
+}  // namespace
+
 namespace ios_web_view {
 
 // static
@@ -52,22 +74,21 @@
     web::BrowserState* context) const {
   WebViewBrowserState* browser_state =
       WebViewBrowserState::FromBrowserState(context);
+
+  auto device_info_sync_client =
+      std::make_unique<DeviceInfoSyncClient>(browser_state->GetPrefs());
   auto local_device_info_provider =
       std::make_unique<syncer::LocalDeviceInfoProviderImpl>(
           version_info::Channel::STABLE, version_info::GetVersionNumber(),
-          /*signin_scoped_device_id_callback=*/
-          base::BindRepeating(&signin::GetSigninScopedDeviceId,
-                              browser_state->GetPrefs()),
-          /*send_tab_to_self_receiving_enabled_callback=*/
-          base::BindRepeating([]() { return false; }));
-
+          device_info_sync_client.get());
   auto device_prefs =
       std::make_unique<syncer::DeviceInfoPrefs>(browser_state->GetPrefs());
 
   return std::make_unique<syncer::DeviceInfoSyncServiceImpl>(
       WebViewModelTypeStoreServiceFactory::GetForBrowserState(browser_state)
           ->GetStoreFactory(),
-      std::move(local_device_info_provider), std::move(device_prefs));
+      std::move(local_device_info_provider), std::move(device_prefs),
+      std::move(device_info_sync_client));
 }
 
 }  // namespace ios_web_view
diff --git a/ios/web_view/shell/BUILD.gn b/ios/web_view/shell/BUILD.gn
index 362d975a..d8c6c6a1 100644
--- a/ios/web_view/shell/BUILD.gn
+++ b/ios/web_view/shell/BUILD.gn
@@ -105,6 +105,7 @@
   deps = [
     ":shell_auth_service_interface",
     ":shell_risk_data_loader_interface",
+    "//ios/third_party/webkit",
     "//ios/web_view:web_view+link",
     ios_web_view_shell_auth_service,
     ios_web_view_shell_risk_data_loader,
@@ -121,7 +122,6 @@
     "Security.framework",
     "SystemConfiguration.framework",
     "UIKit.framework",
-    "WebKit.framework",
     "resolv",
   ]
 
diff --git a/ios/web_view/shell/test/BUILD.gn b/ios/web_view/shell/test/BUILD.gn
index f11dc763..945fb76 100644
--- a/ios/web_view/shell/test/BUILD.gn
+++ b/ios/web_view/shell/test/BUILD.gn
@@ -38,6 +38,7 @@
   deps = [
     "//base",
     "//base/test:test_support",
+    "//ios/third_party/webkit",
     "//ios/web_view/shell",
   ]
 
@@ -46,10 +47,7 @@
     "//ios/third_party/earl_grey:earl_grey+link",
   ]
 
-  libs = [
-    "WebKit.framework",
-    "XCTest.framework",
-  ]
+  libs = [ "XCTest.framework" ]
 
   sources = [
     "earl_grey/web_view_shell_matchers.h",
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 7d2cb05..cdd05c7c 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -111,6 +111,8 @@
     "channel_mixer.h",
     "channel_mixing_matrix.cc",
     "channel_mixing_matrix.h",
+    "color_plane_layout.cc",
+    "color_plane_layout.h",
     "container_names.cc",
     "container_names.h",
     "content_decryption_module.cc",
diff --git a/media/base/color_plane_layout.cc b/media/base/color_plane_layout.cc
new file mode 100644
index 0000000..da4fbdc
--- /dev/null
+++ b/media/base/color_plane_layout.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/color_plane_layout.h"
+
+namespace media {
+
+ColorPlaneLayout::ColorPlaneLayout() = default;
+
+ColorPlaneLayout::ColorPlaneLayout(int32_t stride, size_t offset, size_t size)
+    : stride(stride), offset(offset), size(size) {}
+
+ColorPlaneLayout::~ColorPlaneLayout() = default;
+
+bool ColorPlaneLayout::operator==(const ColorPlaneLayout& rhs) const {
+  return stride == rhs.stride && offset == rhs.offset && size == rhs.size;
+}
+
+bool ColorPlaneLayout::operator!=(const ColorPlaneLayout& rhs) const {
+  return !(*this == rhs);
+}
+
+std::ostream& operator<<(std::ostream& ostream, const ColorPlaneLayout& plane) {
+  ostream << "(" << plane.stride << ", " << plane.offset << ", " << plane.size
+          << ")";
+  return ostream;
+}
+
+}  // namespace media
diff --git a/media/base/color_plane_layout.h b/media/base/color_plane_layout.h
new file mode 100644
index 0000000..922cc1b
--- /dev/null
+++ b/media/base/color_plane_layout.h
@@ -0,0 +1,43 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_COLOR_PLANE_LAYOUT_H_
+#define MEDIA_BASE_COLOR_PLANE_LAYOUT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <ostream>
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Encapsulates a color plane's memory layout: (stride, offset, size)
+// stride: in bytes of a plane. Note that stride can be negative if the image
+//         layout is bottom-up.
+// offset: in bytes of a plane, which stands for the offset of a start point of
+//         a color plane from a buffer FD.
+// size:   in bytes of a plane. This |size| bytes data must contain all the data
+//         a decoder will access (e.g. visible area and padding).
+struct MEDIA_EXPORT ColorPlaneLayout {
+  ColorPlaneLayout();
+  ColorPlaneLayout(int32_t stride, size_t offset, size_t size);
+  ~ColorPlaneLayout();
+
+  bool operator==(const ColorPlaneLayout& rhs) const;
+  bool operator!=(const ColorPlaneLayout& rhs) const;
+
+  int32_t stride = 0;
+  size_t offset = 0;
+  size_t size = 0;
+};
+
+// Outputs ColorPlaneLayout to stream.
+MEDIA_EXPORT std::ostream& operator<<(std::ostream& ostream,
+                                      const ColorPlaneLayout& plane);
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_COLOR_PLANE_LAYOUT_H_
diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc
index 0eb03fde..e6957b81 100644
--- a/media/base/video_frame.cc
+++ b/media/base/video_frame.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/limits.h"
 #include "media/base/timestamp_constants.h"
@@ -141,7 +142,7 @@
 static base::Optional<VideoFrameLayout> GetDefaultLayout(
     VideoPixelFormat format,
     const gfx::Size& coded_size) {
-  std::vector<VideoFrameLayout::Plane> planes;
+  std::vector<ColorPlaneLayout> planes;
 
   switch (format) {
     case PIXEL_FORMAT_I420: {
@@ -149,22 +150,21 @@
       int uv_height = (coded_size.height() + 1) / 2;
       int uv_stride = uv_width;
       int uv_size = uv_stride * uv_height;
-      planes = std::vector<VideoFrameLayout::Plane>{
-          VideoFrameLayout::Plane(coded_size.width(), 0, coded_size.GetArea()),
-          VideoFrameLayout::Plane(uv_stride, coded_size.GetArea(), uv_size),
-          VideoFrameLayout::Plane(uv_stride, coded_size.GetArea() + uv_size,
-                                  uv_size),
+      planes = std::vector<ColorPlaneLayout>{
+          ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
+          ColorPlaneLayout(uv_stride, coded_size.GetArea(), uv_size),
+          ColorPlaneLayout(uv_stride, coded_size.GetArea() + uv_size, uv_size),
       };
       break;
     }
 
     case PIXEL_FORMAT_Y16:
-      planes = std::vector<VideoFrameLayout::Plane>{VideoFrameLayout::Plane(
+      planes = std::vector<ColorPlaneLayout>{ColorPlaneLayout(
           coded_size.width() * 2, 0, coded_size.GetArea() * 2)};
       break;
 
     case PIXEL_FORMAT_ARGB:
-      planes = std::vector<VideoFrameLayout::Plane>{VideoFrameLayout::Plane(
+      planes = std::vector<ColorPlaneLayout>{ColorPlaneLayout(
           coded_size.width() * 4, 0, coded_size.GetArea() * 4)};
       break;
 
@@ -173,9 +173,9 @@
       int uv_height = (coded_size.height() + 1) / 2;
       int uv_stride = uv_width * 2;
       int uv_size = uv_stride * uv_height;
-      planes = std::vector<VideoFrameLayout::Plane>{
-          VideoFrameLayout::Plane(coded_size.width(), 0, coded_size.GetArea()),
-          VideoFrameLayout::Plane(uv_stride, coded_size.GetArea(), uv_size),
+      planes = std::vector<ColorPlaneLayout>{
+          ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
+          ColorPlaneLayout(uv_stride, coded_size.GetArea(), uv_size),
       };
       break;
     }
diff --git a/media/base/video_frame_layout.cc b/media/base/video_frame_layout.cc
index 4700132..eec8480d 100644
--- a/media/base/video_frame_layout.cc
+++ b/media/base/video_frame_layout.cc
@@ -29,9 +29,9 @@
   return result.str();
 }
 
-std::vector<VideoFrameLayout::Plane> PlanesFromStrides(
+std::vector<ColorPlaneLayout> PlanesFromStrides(
     const std::vector<int32_t> strides) {
-  std::vector<VideoFrameLayout::Plane> planes(strides.size());
+  std::vector<ColorPlaneLayout> planes(strides.size());
   for (size_t i = 0; i < strides.size(); i++) {
     planes[i].stride = strides[i];
   }
@@ -101,7 +101,7 @@
 base::Optional<VideoFrameLayout> VideoFrameLayout::CreateWithPlanes(
     VideoPixelFormat format,
     const gfx::Size& coded_size,
-    std::vector<Plane> planes,
+    std::vector<ColorPlaneLayout> planes,
     size_t buffer_addr_align,
     uint64_t modifier) {
   // NOTE: Even if format is UNKNOWN, it is valid if coded_sizes is not Empty().
@@ -117,7 +117,7 @@
 base::Optional<VideoFrameLayout> VideoFrameLayout::CreateMultiPlanar(
     VideoPixelFormat format,
     const gfx::Size& coded_size,
-    std::vector<Plane> planes,
+    std::vector<ColorPlaneLayout> planes,
     size_t buffer_addr_align,
     uint64_t modifier) {
   // NOTE: Even if format is UNKNOWN, it is valid if coded_sizes is not Empty().
@@ -132,7 +132,7 @@
 
 VideoFrameLayout::VideoFrameLayout(VideoPixelFormat format,
                                    const gfx::Size& coded_size,
-                                   std::vector<Plane> planes,
+                                   std::vector<ColorPlaneLayout> planes,
                                    bool is_multi_planar,
                                    size_t buffer_addr_align,
                                    uint64_t modifier)
@@ -149,23 +149,6 @@
 VideoFrameLayout& VideoFrameLayout::operator=(const VideoFrameLayout&) =
     default;
 
-std::ostream& operator<<(std::ostream& ostream,
-                         const VideoFrameLayout::Plane& plane) {
-  ostream << "(" << plane.stride << ", " << plane.offset << ", " << plane.size
-          << ")";
-  return ostream;
-}
-
-bool VideoFrameLayout::Plane::operator==(
-    const VideoFrameLayout::Plane& rhs) const {
-  return stride == rhs.stride && offset == rhs.offset && size == rhs.size;
-}
-
-bool VideoFrameLayout::Plane::operator!=(
-    const VideoFrameLayout::Plane& rhs) const {
-  return !(*this == rhs);
-}
-
 bool VideoFrameLayout::operator==(const VideoFrameLayout& rhs) const {
   return format_ == rhs.format_ && coded_size_ == rhs.coded_size_ &&
          planes_ == rhs.planes_ && is_multi_planar_ == rhs.is_multi_planar_ &&
diff --git a/media/base/video_frame_layout.h b/media/base/video_frame_layout.h
index 086eb95..c7f59083 100644
--- a/media/base/video_frame_layout.h
+++ b/media/base/video_frame_layout.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "base/optional.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/media_export.h"
 #include "media/base/video_types.h"
 #include "ui/gfx/geometry/size.h"
@@ -36,27 +37,6 @@
   // without inspecting av_frame_get_buffer() first.
   static constexpr size_t kBufferAddressAlignment = 32;
 
-  struct Plane {
-    Plane() = default;
-    Plane(int32_t stride, size_t offset, size_t size)
-        : stride(stride), offset(offset), size(size) {}
-
-    bool operator==(const Plane& rhs) const;
-    bool operator!=(const Plane& rhs) const;
-
-    // Strides in bytes of a plane. Note that stride can be negative if the
-    // image layout is bottom-up.
-    int32_t stride = 0;
-
-    // Offset in bytes of a plane, which stands for the offset of a start point
-    // of a color plane from a buffer fd.
-    size_t offset = 0;
-
-    // Size in bytes of a plane. This |size| bytes data must contain all the
-    // data a decoder will access (e.g. visible area and padding).
-    size_t size = 0;
-  };
-
   // Factory functions.
   // |format| and |coded_size| must be specified.
   // |is_single_planar| is optional. It describes planes can be stored (although
@@ -84,7 +64,7 @@
   static base::Optional<VideoFrameLayout> CreateWithPlanes(
       VideoPixelFormat format,
       const gfx::Size& coded_size,
-      std::vector<Plane> planes,
+      std::vector<ColorPlaneLayout> planes,
       size_t buffer_addr_align = kBufferAddressAlignment,
       uint64_t modifier = gfx::NativePixmapHandle::kNoModifier);
 
@@ -94,7 +74,7 @@
   static base::Optional<VideoFrameLayout> CreateMultiPlanar(
       VideoPixelFormat format,
       const gfx::Size& coded_size,
-      std::vector<Plane> planes,
+      std::vector<ColorPlaneLayout> planes,
       size_t buffer_addr_align = kBufferAddressAlignment,
       uint64_t modifier = gfx::NativePixmapHandle::kNoModifier);
 
@@ -112,7 +92,7 @@
   // Returns number of planes. Note that num_planes >= num_buffers.
   size_t num_planes() const { return planes_.size(); }
 
-  const std::vector<Plane>& planes() const { return planes_; }
+  const std::vector<ColorPlaneLayout>& planes() const { return planes_; }
 
   bool operator==(const VideoFrameLayout& rhs) const;
   bool operator!=(const VideoFrameLayout& rhs) const;
@@ -128,7 +108,7 @@
  private:
   VideoFrameLayout(VideoPixelFormat format,
                    const gfx::Size& coded_size,
-                   std::vector<Plane> planes,
+                   std::vector<ColorPlaneLayout> planes,
                    bool is_multi_planar,
                    size_t buffer_addr_align,
                    uint64_t modifier);
@@ -143,7 +123,7 @@
   gfx::Size coded_size_;
 
   // Layout property for each color planes, e.g. stride and buffer offset.
-  std::vector<Plane> planes_;
+  std::vector<ColorPlaneLayout> planes_;
 
   // Set to true when a format uses multiple backing buffers to store its
   // planes. Used by code for V4L2 API at the moment.
@@ -160,10 +140,6 @@
   uint64_t modifier_;
 };
 
-// Outputs VideoFrameLayout::Plane to stream.
-MEDIA_EXPORT std::ostream& operator<<(std::ostream& ostream,
-                                      const VideoFrameLayout::Plane& plane);
-
 // Outputs VideoFrameLayout to stream.
 MEDIA_EXPORT std::ostream& operator<<(std::ostream& ostream,
                                       const VideoFrameLayout& layout);
diff --git a/media/base/video_frame_layout_unittest.cc b/media/base/video_frame_layout_unittest.cc
index 297f234..17ad2a9c 100644
--- a/media/base/video_frame_layout_unittest.cc
+++ b/media/base/video_frame_layout_unittest.cc
@@ -22,12 +22,11 @@
 
 namespace {
 
-std::vector<VideoFrameLayout::Plane> CreatePlanes(
-    const std::vector<int32_t>& strides,
-    const std::vector<size_t>& offsets,
-    const std::vector<size_t>& sizes) {
+std::vector<ColorPlaneLayout> CreatePlanes(const std::vector<int32_t>& strides,
+                                           const std::vector<size_t>& offsets,
+                                           const std::vector<size_t>& sizes) {
   LOG_ASSERT(strides.size() == offsets.size());
-  std::vector<VideoFrameLayout::Plane> planes(strides.size());
+  std::vector<ColorPlaneLayout> planes(strides.size());
   for (size_t i = 0; i < strides.size(); i++) {
     planes[i].stride = strides[i];
     planes[i].offset = offsets[i];
diff --git a/media/base/video_frame_unittest.cc b/media/base/video_frame_unittest.cc
index c7a0816..a7f3008 100644
--- a/media/base/video_frame_unittest.cc
+++ b/media/base/video_frame_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/simple_sync_token_client.h"
 #include "media/video/fake_gpu_memory_buffer.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -420,7 +421,7 @@
   std::vector<int32_t> strides = {384, 192, 192};
   std::vector<size_t> offsets = {0, 100, 200};
   std::vector<size_t> sizes = {100, 50, 50};
-  std::vector<VideoFrameLayout::Plane> planes(strides.size());
+  std::vector<ColorPlaneLayout> planes(strides.size());
 
   for (size_t i = 0; i < planes.size(); i++) {
     planes[i].stride = strides[i];
diff --git a/media/capture/video/chromeos/mojom/BUILD.gn b/media/capture/video/chromeos/mojom/BUILD.gn
index 23dc55e..f98d89e 100644
--- a/media/capture/video/chromeos/mojom/BUILD.gn
+++ b/media/capture/video/chromeos/mojom/BUILD.gn
@@ -15,7 +15,6 @@
   ]
 
   deps = [
-    "//components/arc/mojom:camera_intent",
     "//components/chromeos_camera/common",
     "//media/capture/mojom:image_capture",
     "//media/mojo/mojom",
diff --git a/media/capture/video/chromeos/mojom/camera_app.mojom b/media/capture/video/chromeos/mojom/camera_app.mojom
index 0a81a4f..b58b594f 100644
--- a/media/capture/video/chromeos/mojom/camera_app.mojom
+++ b/media/capture/video/chromeos/mojom/camera_app.mojom
@@ -4,7 +4,6 @@
 
 module cros.mojom;
 
-import "components/arc/mojom/camera_intent.mojom";
 import "media/capture/mojom/image_capture.mojom";
 import "media/capture/video/chromeos/mojom/camera_common.mojom";
 import "media/capture/video/chromeos/mojom/camera_metadata.mojom";
@@ -56,22 +55,6 @@
   IsSupported() => (bool is_supported);
 };
 
-// Interface for communication between Chrome Camera App (Remote) and Chrome
-// (Receiver).
-interface CameraAppHelper {
-  // Sends the captured result |data| for corresponding intent recognized by
-  // |intent_id| back to ARC. The handler should handle |data| and may notify
-  // the intent caller according to the intention of the |action|. |is_success|
-  // will be set to true if the ARC received the result and set to false for
-  // invalid input.
-  HandleCameraResult(uint32 intent_id, arc.mojom.CameraIntentAction action,
-                     array<uint8> data)
-      => (bool is_success);
-
-  // Checks if device is under tablet mode currently.
-  IsTabletMode() => (bool is_tablet_mode);
-};
-
 // Inner interface that used to communicate between browser process (Remote) and
 // the Video Capture service (Receiver).
 interface CameraAppDeviceBridge {
diff --git a/media/gpu/linux/platform_video_frame_utils.cc b/media/gpu/linux/platform_video_frame_utils.cc
index 453c328..4c060197 100644
--- a/media/gpu/linux/platform_video_frame_utils.cc
+++ b/media/gpu/linux/platform_video_frame_utils.cc
@@ -8,6 +8,7 @@
 #include "base/bind_helpers.h"
 #include "base/files/scoped_file.h"
 #include "build/build_config.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/scopedfd_helper.h"
 #include "media/base/video_frame_layout.h"
@@ -48,7 +49,7 @@
     return nullptr;
 
   const size_t num_planes = VideoFrame::NumPlanes(pixel_format);
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<ColorPlaneLayout> planes(num_planes);
   for (size_t i = 0; i < num_planes; ++i) {
     planes[i].stride = pixmap->GetDmaBufPitch(i);
     planes[i].offset = pixmap->GetDmaBufOffset(i);
diff --git a/media/gpu/linux/platform_video_frame_utils_unittest.cc b/media/gpu/linux/platform_video_frame_utils_unittest.cc
index ccaf0ad..67ca7b3 100644
--- a/media/gpu/linux/platform_video_frame_utils_unittest.cc
+++ b/media/gpu/linux/platform_video_frame_utils_unittest.cc
@@ -17,6 +17,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/optional.h"
 #include "base/time/time.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
@@ -89,7 +90,7 @@
   const size_t num_planes = video_frame->layout().num_planes();
   ASSERT_EQ(native_pixmap->ExportHandle().planes.size(), num_planes);
   for (size_t i = 0; i < num_planes; i++) {
-    const VideoFrameLayout::Plane& plane = video_frame->layout().planes()[i];
+    const ColorPlaneLayout& plane = video_frame->layout().planes()[i];
     // The original and duplicated FDs should be different.
     EXPECT_NE(native_pixmap->GetDmaBufFd(i), video_frame->DmabufFds()[i].get());
     EXPECT_EQ(native_pixmap->GetDmaBufPitch(i),
diff --git a/media/gpu/test/video_frame_helpers.cc b/media/gpu/test/video_frame_helpers.cc
index 38e86ef..31aa838f3 100644
--- a/media/gpu/test/video_frame_helpers.cc
+++ b/media/gpu/test/video_frame_helpers.cc
@@ -10,6 +10,7 @@
 #include "base/bind_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/buildflags.h"
@@ -318,7 +319,7 @@
                                                         const gfx::Size& size) {
   const size_t num_planes = VideoFrame::NumPlanes(format);
 
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<ColorPlaneLayout> planes(num_planes);
   const auto strides = VideoFrame::ComputeStrides(format, size);
   size_t offset = 0;
   for (size_t i = 0; i < num_planes; ++i) {
diff --git a/media/gpu/v4l2/BUILD.gn b/media/gpu/v4l2/BUILD.gn
index 4e63c6e..33f9efb0 100644
--- a/media/gpu/v4l2/BUILD.gn
+++ b/media/gpu/v4l2/BUILD.gn
@@ -121,6 +121,7 @@
   ]
   deps = [
     ":v4l2",
+    "//media:test_support",
     "//testing/gtest",
     "//ui/gfx:test_support",
     "//ui/gl",
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index 6a6d6db..6385b207 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -18,6 +18,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "build/build_config.h"
 #include "media/base/bind_to_current_loop.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/video_types.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/generic_v4l2_device.h"
@@ -1555,7 +1556,7 @@
     return base::nullopt;
   }
   // Reserve capacity in advance to prevent unnecessary vector reallocation.
-  std::vector<VideoFrameLayout::Plane> planes;
+  std::vector<ColorPlaneLayout> planes;
   planes.reserve(num_color_planes);
   for (size_t i = 0; i < num_buffers; ++i) {
     const v4l2_plane_pix_format& plane_format = pix_mp.plane_fmt[i];
diff --git a/media/gpu/v4l2/v4l2_device_unittest.cc b/media/gpu/v4l2/v4l2_device_unittest.cc
index 7888b33..3d91c38a 100644
--- a/media/gpu/v4l2/v4l2_device_unittest.cc
+++ b/media/gpu/v4l2/v4l2_device_unittest.cc
@@ -8,6 +8,7 @@
 #include <sstream>
 #include <vector>
 
+#include "media/base/color_plane_layout.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/native_pixmap_handle.h"
 
@@ -78,7 +79,7 @@
   ASSERT_TRUE(layout.has_value());
   EXPECT_EQ(PIXEL_FORMAT_NV12, layout->format());
   EXPECT_EQ(gfx::Size(300, 180), layout->coded_size());
-  std::vector<VideoFrameLayout::Plane> expected_planes(
+  std::vector<ColorPlaneLayout> expected_planes(
       {{320, 0u, 86400u}, {320, 57600u, 28800u}});
   EXPECT_EQ(expected_planes, layout->planes());
   EXPECT_EQ(layout->is_multi_planar(), false);
@@ -103,7 +104,7 @@
   ASSERT_TRUE(layout.has_value());
   EXPECT_EQ(PIXEL_FORMAT_NV12, layout->format());
   EXPECT_EQ(gfx::Size(300, 180), layout->coded_size());
-  std::vector<VideoFrameLayout::Plane> expected_planes(
+  std::vector<ColorPlaneLayout> expected_planes(
       {{320, 0u, 57600u}, {320, 0u, 28800u}});
   EXPECT_EQ(expected_planes, layout->planes());
   EXPECT_EQ(layout->is_multi_planar(), true);
@@ -128,7 +129,7 @@
   ASSERT_TRUE(layout.has_value());
   EXPECT_EQ(PIXEL_FORMAT_I420, layout->format());
   EXPECT_EQ(gfx::Size(300, 180), layout->coded_size());
-  std::vector<VideoFrameLayout::Plane> expected_planes(
+  std::vector<ColorPlaneLayout> expected_planes(
       {{320, 0u, 86400}, {160, 57600u, 14400u}, {160, 72000u, 14400u}});
   EXPECT_EQ(expected_planes, layout->planes());
   std::ostringstream ostream;
diff --git a/media/gpu/v4l2/v4l2_slice_video_decoder.cc b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
index b2d3de0..63b0c41 100644
--- a/media/gpu/v4l2/v4l2_slice_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_slice_video_decoder.cc
@@ -55,7 +55,7 @@
     return false;
   }
 
-  const std::vector<VideoFrameLayout::Plane>& planes = frame->layout().planes();
+  const auto& planes = frame->layout().planes();
   for (size_t i = frame->DmabufFds().size() - 1; i >= target_num_fds; --i) {
     // Assume that an fd is a duplicate of a previous plane's fd if offset != 0.
     // Otherwise, if offset == 0, return error as surface_it may be pointing to
diff --git a/media/gpu/v4l2/v4l2_vda_helpers.cc b/media/gpu/v4l2/v4l2_vda_helpers.cc
index 5d9743f9..ddf47621 100644
--- a/media/gpu/v4l2/v4l2_vda_helpers.cc
+++ b/media/gpu/v4l2/v4l2_vda_helpers.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/v4l2/v4l2_vda_helpers.h"
 
+#include "media/base/color_plane_layout.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_device.h"
 #include "media/gpu/v4l2/v4l2_image_processor.h"
@@ -25,8 +26,7 @@
     case V4L2_PIX_FMT_MM21:
       num_planes = 2;
       return VideoFrameLayout::CreateMultiPlanar(
-          PIXEL_FORMAT_NV12, size,
-          std::vector<VideoFrameLayout::Plane>(num_planes));
+          PIXEL_FORMAT_NV12, size, std::vector<ColorPlaneLayout>(num_planes));
 
     default:
       VideoPixelFormat pixel_format =
@@ -38,8 +38,7 @@
         return VideoFrameLayout::Create(pixel_format, size);
       else
         return VideoFrameLayout::CreateMultiPlanar(
-            pixel_format, size,
-            std::vector<VideoFrameLayout::Plane>(num_planes));
+            pixel_format, size, std::vector<ColorPlaneLayout>(num_planes));
       break;
   }
 }
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 3c8138d..ee49c63 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -27,6 +27,7 @@
 #include "base/trace_event/trace_event.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/bitstream_buffer.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/scopedfd_helper.h"
 #include "media/base/unaligned_shared_memory.h"
 #include "media/base/video_frame_layout.h"
@@ -243,7 +244,7 @@
     // TODO(hiroh): Decide the appropriate planar in some way.
     auto input_layout = VideoFrameLayout::CreateMultiPlanar(
         config.input_format, visible_size_,
-        std::vector<VideoFrameLayout::Plane>(
+        std::vector<ColorPlaneLayout>(
             VideoFrame::NumPlanes(config.input_format)));
     if (!input_layout) {
       VLOGF(1) << "Invalid image processor input layout";
@@ -658,7 +659,7 @@
     // TODO(hiroh): Decide the appropriate planar in some way.
     auto input_layout = VideoFrameLayout::CreateMultiPlanar(
         format, new_frame_size,
-        std::vector<VideoFrameLayout::Plane>(VideoFrame::NumPlanes(format)));
+        std::vector<ColorPlaneLayout>(VideoFrame::NumPlanes(format)));
     if (!input_layout) {
       VLOGF(1) << "Invalid image processor input layout";
       return false;
diff --git a/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
index 4341b83..a77a545 100644
--- a/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
+++ b/media/gpu/vaapi/vaapi_dmabuf_video_frame_mapper.cc
@@ -8,6 +8,7 @@
 #include "base/bind_helpers.h"
 #include "base/memory/ptr_util.h"
 #include "build/build_config.h"
+#include "media/base/color_plane_layout.h"
 #include "media/gpu/linux/platform_video_frame_utils.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/vaapi/vaapi_utils.h"
@@ -42,7 +43,7 @@
   }
 
   // All the planes are stored in the same buffer, VAImage.va_buffer.
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<ColorPlaneLayout> planes(num_planes);
   std::vector<uint8_t*> addrs(num_planes, nullptr);
   for (size_t i = 0; i < num_planes; i++) {
     planes[i].stride = va_image->image()->pitches[i];
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index 9736e629..df09d3b6 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -47,6 +47,7 @@
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/cdm_context.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/media.h"
 #include "media/base/media_switches.h"
@@ -2113,7 +2114,7 @@
   CHECK_LE(num_planes, 3u);
 
   uint8_t* frame_data[3] = {};
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<ColorPlaneLayout> planes(num_planes);
   size_t offset = position;
   // All the planes are stored in the same buffer, aligned_in_file_data[0].
   for (size_t i = 0; i < num_planes; i++) {
@@ -2671,7 +2672,7 @@
   CHECK_LE(num_planes, 3u);
   std::vector<char, AlignedAllocator<char, kPlatformBufferAlignment>>
       aligned_data[3];
-  std::vector<VideoFrameLayout::Plane> planes(num_planes);
+  std::vector<ColorPlaneLayout> planes(num_planes);
   std::vector<size_t> buffer_sizes(num_planes);
   uint8_t* frame_data[3] = {};
   // This VideoFrame is dummy. Each plane is stored in a separate buffer and
diff --git a/media/mojo/mojom/video_frame_mojom_traits.cc b/media/mojo/mojom/video_frame_mojom_traits.cc
index d1ec35e..799aafe 100644
--- a/media/mojo/mojom/video_frame_mojom_traits.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits.cc
@@ -10,6 +10,7 @@
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 #include "mojo/public/cpp/base/time_mojom_traits.h"
@@ -173,7 +174,7 @@
     if (num_planes != dmabuf_fds_data.size())
       return false;
 
-    std::vector<media::VideoFrameLayout::Plane> planes(num_planes);
+    std::vector<media::ColorPlaneLayout> planes(num_planes);
     for (size_t i = 0; i < num_planes; i++) {
       planes[i].stride = strides[i];
       planes[i].offset = 0;
diff --git a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
index 945097e..e743a74 100644
--- a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
@@ -11,6 +11,7 @@
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "gpu/command_buffer/common/sync_token.h"
+#include "media/base/color_plane_layout.h"
 #include "media/base/video_frame.h"
 #include "media/mojo/common/mojo_shared_buffer_video_frame.h"
 #include "media/mojo/mojom/traits_test_service.mojom.h"
@@ -107,8 +108,8 @@
   std::vector<size_t> sizes = {1280 * 720, 1280 * 720 / 2};
   auto layout = media::VideoFrameLayout::CreateWithPlanes(
       PIXEL_FORMAT_NV12, gfx::Size(1280, 720),
-      {media::VideoFrameLayout::Plane(strides[0], 0, sizes[0]),
-       media::VideoFrameLayout::Plane(strides[1], 0, sizes[1])});
+      {media::ColorPlaneLayout(strides[0], 0, sizes[0]),
+       media::ColorPlaneLayout(strides[1], 0, sizes[1])});
 
   // DMABUF needs device to create, use file fd instead.
   std::vector<int> fake_fds = {open("/dev/null", O_RDWR),
diff --git a/net/cert/cert_verify_proc_builtin.cc b/net/cert/cert_verify_proc_builtin.cc
index 68a28d86..5984834 100644
--- a/net/cert/cert_verify_proc_builtin.cc
+++ b/net/cert/cert_verify_proc_builtin.cc
@@ -43,6 +43,9 @@
 constexpr base::TimeDelta kMaxVerificationTime =
     base::TimeDelta::FromSeconds(60);
 
+constexpr base::TimeDelta kPerAttemptMinVerificationTimeLimit =
+    base::TimeDelta::FromSeconds(5);
+
 DEFINE_CERT_ERROR_ID(kPathLacksEVPolicy, "Path does not have an EV policy");
 
 RevocationPolicy NoRevocationChecking() {
@@ -589,13 +592,8 @@
 // This implementation is simplistic, and looks only for the presence of the
 // kUnacceptableSignatureAlgorithm error somewhere among the built paths.
 bool CanTryAgainWithWeakerDigestPolicy(const CertPathBuilder::Result& result) {
-  for (const auto& path : result.paths) {
-    if (path->errors.ContainsError(
-            cert_errors::kUnacceptableSignatureAlgorithm))
-      return true;
-  }
-
-  return false;
+  return result.AnyPathContainsError(
+      cert_errors::kUnacceptableSignatureAlgorithm);
 }
 
 int CertVerifyProcBuiltin::VerifyInternal(
@@ -672,6 +670,12 @@
     const auto& cur_attempt = attempts[cur_attempt_index];
     verification_type = cur_attempt.verification_type;
 
+    // If a previous attempt used up most/all of the deadline, extend the
+    // deadline a little bit to give this verification attempt a chance at
+    // success.
+    deadline = std::max(
+        deadline, base::TimeTicks::Now() + kPerAttemptMinVerificationTimeLimit);
+
     // Run the attempt through the path builder.
     result = TryBuildPath(
         target, &intermediates, ssl_trust_store.get(), verification_time,
@@ -679,9 +683,23 @@
         flags, ocsp_response, crl_set, net_fetcher_.get(), ev_metadata,
         &checked_revocation_for_some_path);
 
-    if (result.HasValidPath() || result.exceeded_deadline)
+    if (result.HasValidPath())
       break;
 
+    if (result.exceeded_deadline) {
+      if (verification_type == VerificationType::kEV &&
+          result.AnyPathContainsError(cert_errors::kUnableToCheckRevocation)) {
+        // EV verification failed due to deadline exceeded and unable to check
+        // revocation. Try the non-EV attempt even though the deadline has been
+        // reached, since a revocation checking failure on EV should be a
+        // soft-fail. (Since the non-EV attempt generally will not be using
+        // revocation checking it hopefully won't hit the deadline too.)
+        continue;
+      }
+      // Otherwise, stop immediately if an attempt exceeds the deadline.
+      break;
+    }
+
     // If this path building attempt (may have) failed due to the chain using a
     // weak signature algorithm, enqueue a similar attempt but with weaker
     // signature algorithms (SHA1) permitted.
diff --git a/net/cert/cert_verify_proc_builtin_unittest.cc b/net/cert/cert_verify_proc_builtin_unittest.cc
index cb43e260..3b939fc 100644
--- a/net/cert/cert_verify_proc_builtin_unittest.cc
+++ b/net/cert/cert_verify_proc_builtin_unittest.cc
@@ -10,6 +10,7 @@
 #include "net/base/test_completion_callback.h"
 #include "net/cert/cert_verify_proc.h"
 #include "net/cert/crl_set.h"
+#include "net/cert/ev_root_ca_metadata.h"
 #include "net/cert/internal/system_trust_store.h"
 #include "net/cert_net/cert_net_fetcher_impl.h"
 #include "net/log/net_log_with_source.h"
@@ -296,4 +297,77 @@
   EXPECT_THAT(error, IsOk());
 }
 
+#if defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
+// Tests that if the verification deadline is exceeded during EV revocation
+// checking, the certificate is verified as non-EV.
+TEST_F(CertVerifyProcBuiltinTest, EVRevocationCheckDeadline) {
+  std::unique_ptr<CertBuilder> leaf, intermediate, root;
+  CreateChain(&leaf, &intermediate, &root);
+  ASSERT_TRUE(leaf && intermediate && root);
+
+  // Add test EV policy to leaf and intermediate.
+  static const char kEVTestCertPolicy[] = "1.2.3.4";
+  leaf->SetCertificatePolicies({kEVTestCertPolicy});
+  intermediate->SetCertificatePolicies({kEVTestCertPolicy});
+
+  const base::TimeDelta timeout_increment =
+      CertNetFetcherImpl::GetDefaultTimeoutForTesting() +
+      base::TimeDelta::FromMilliseconds(1);
+  const int expected_request_count =
+      GetCertVerifyProcBuiltinTimeLimitForTesting() / timeout_increment + 1;
+
+  EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTP);
+  ASSERT_TRUE(test_server.InitializeAndListen());
+
+  // Set up the test intermediate to have enough OCSP urls that if all the
+  // requests hang the deadline will be exceeded.
+  std::vector<GURL> ocsp_urls;
+  std::vector<base::RunLoop> runloops(expected_request_count);
+  for (int i = 0; i < expected_request_count; ++i) {
+    std::string path = base::StringPrintf("/hung/%i", i);
+    ocsp_urls.emplace_back(test_server.GetURL(path));
+    test_server.RegisterRequestHandler(
+        base::BindRepeating(&test_server::HandlePrefixedRequest, path,
+                            base::BindRepeating(&HangRequestAndCallback,
+                                                runloops[i].QuitClosure())));
+  }
+  intermediate->SetCaIssuersAndOCSPUrls({}, ocsp_urls);
+
+  test_server.StartAcceptingConnections();
+
+  // Consider the root of the test chain a valid EV root for the test policy.
+  ScopedTestEVPolicy scoped_test_ev_policy(
+      EVRootCAMetadata::GetInstance(),
+      X509Certificate::CalculateFingerprint256(root->GetCertBuffer()),
+      kEVTestCertPolicy);
+
+  scoped_refptr<X509Certificate> chain = leaf->GetX509CertificateChain();
+  ASSERT_TRUE(chain.get());
+
+  CertVerifyResult verify_result;
+  TestCompletionCallback verify_callback;
+  Verify(chain.get(), "www.example.com",
+         /*flags=*/0,
+         /*additional_trust_anchors=*/{root->GetX509Certificate()},
+         &verify_result, verify_callback.callback());
+
+  for (int i = 0; i < expected_request_count; i++) {
+    // Wait for request #|i| to be made.
+    runloops[i].Run();
+    // Advance virtual time to cause the timeout task to become runnable.
+    task_environment().AdvanceClock(timeout_increment);
+  }
+
+  // Once |expected_request_count| requests have been made and timed out, the
+  // overall deadline should be reached, causing the EV verification attempt to
+  // fail.
+  int error = verify_callback.WaitForResult();
+  // EV uses soft-fail revocation checking, therefore verification result
+  // should be OK but not EV.
+  EXPECT_THAT(error, IsOk());
+  EXPECT_FALSE(verify_result.cert_status & CERT_STATUS_IS_EV);
+  EXPECT_TRUE(verify_result.cert_status & CERT_STATUS_REV_CHECKING_ENABLED);
+}
+#endif  // defined(PLATFORM_USES_CHROMIUM_EV_METADATA)
+
 }  // namespace net
diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
index 8da4222..e67fc240 100644
--- a/net/cert/internal/path_builder.cc
+++ b/net/cert/internal/path_builder.cc
@@ -525,6 +525,15 @@
   return GetBestValidPath() != nullptr;
 }
 
+bool CertPathBuilder::Result::AnyPathContainsError(CertErrorId error_id) const {
+  for (const auto& path : paths) {
+    if (path->errors.ContainsError(error_id))
+      return true;
+  }
+
+  return false;
+}
+
 const CertPathBuilderResultPath* CertPathBuilder::Result::GetBestValidPath()
     const {
   const CertPathBuilderResultPath* result_path = GetBestPathPossiblyInvalid();
diff --git a/net/cert/internal/path_builder.h b/net/cert/internal/path_builder.h
index 7dba3e5..5f3dbb7 100644
--- a/net/cert/internal/path_builder.h
+++ b/net/cert/internal/path_builder.h
@@ -116,6 +116,9 @@
     // Returns true if there was a valid path.
     bool HasValidPath() const;
 
+    // Returns true if any of the attempted paths contain |error_id|.
+    bool AnyPathContainsError(CertErrorId error_id) const;
+
     // Returns the CertPathBuilderResultPath for the best valid path, or nullptr
     // if there was none.
     const CertPathBuilderResultPath* GetBestValidPath() const;
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index d03f76da..1dc154e 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -429,6 +429,7 @@
     case CONNECTION_INFO_QUIC_46:
     case CONNECTION_INFO_QUIC_47:
     case CONNECTION_INFO_QUIC_48:
+    case CONNECTION_INFO_QUIC_49:
     case CONNECTION_INFO_QUIC_99:
     case CONNECTION_INFO_QUIC_999:
       return true;
@@ -497,6 +498,8 @@
       return "http/2+quic/47";
     case CONNECTION_INFO_QUIC_48:
       return "http/2+quic/48";
+    case CONNECTION_INFO_QUIC_49:
+      return "http/2+quic/49";
     case CONNECTION_INFO_QUIC_99:
       return "http/2+quic/99";
     case CONNECTION_INFO_HTTP0_9:
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index ff80d89..68087a5 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -64,6 +64,7 @@
     CONNECTION_INFO_QUIC_47 = 26,
     CONNECTION_INFO_QUIC_999 = 27,
     CONNECTION_INFO_QUIC_48 = 28,
+    CONNECTION_INFO_QUIC_49 = 29,
     NUM_OF_CONNECTION_INFOS,
   };
 
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc
index fb873fe..65a5b24 100644
--- a/net/http/http_stream_factory_unittest.cc
+++ b/net/http/http_stream_factory_unittest.cc
@@ -3296,7 +3296,7 @@
 
   scoped_refptr<HttpResponseHeaders> headers(
       base::MakeRefCounted<HttpResponseHeaders>(""));
-  headers->AddHeader("alt-svc: quic=\":443\"; v=\"99,48,47,46,43,39\"");
+  headers->AddHeader("alt-svc: quic=\":443\"; v=\"99,49,48,47,46,43,39\"");
 
   session_->http_stream_factory()->ProcessAlternativeServices(
       session_.get(), network_isolation_key, headers.get(), origin);
@@ -3329,7 +3329,8 @@
       base::MakeRefCounted<HttpResponseHeaders>(""));
   headers->AddHeader(
       "alt-svc: "
-      "h3-Q099=\":443\",h3-Q048=\":443\",h3-Q047=\":443\",h3-Q043=\":443\",h3-"
+      "h3-Q099=\":443\",h3-Q049=\":443\",h3-Q048=\":443\",h3-Q047=\":443\",h3-"
+      "Q043=\":443\",h3-"
       "Q039=\":443\"");
 
   session_->http_stream_factory()->ProcessAlternativeServices(
@@ -3337,6 +3338,7 @@
 
   quic::ParsedQuicVersionVector versions = {
       {quic::PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_99},
+      {quic::PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_49},
       {quic::PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_48},
       {quic::PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_47},
       {quic::PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_43},
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index e49323c..9d9dac6 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -3265,9 +3265,17 @@
 //  }
 EVENT_TYPE(COOKIE_SET_BLOCKED_BY_NETWORK_DELEGATE)
 
-//
+// Event emitted when cookies are associated with domain but not sent
+// and received but not stored
+//  {
+//    "exclusion_reason": <Exclusion flags>
+//  }
+EVENT_TYPE(COOKIE_INCLUSION_STATUS)
+
+// -----------------------------------------------------------------------------
 // HTTP/3 events.
-//
+// -----------------------------------------------------------------------------
+
 // Event emitted when peer created control stream type is received.
 EVENT_TYPE(HTTP3_PEER_CONTROL_STREAM_CREATED)
 
diff --git a/net/quic/mock_decrypter.cc b/net/quic/mock_decrypter.cc
index 9576ea7..809650d 100644
--- a/net/quic/mock_decrypter.cc
+++ b/net/quic/mock_decrypter.cc
@@ -76,6 +76,10 @@
   return 0;
 }
 
+size_t MockDecrypter::GetNoncePrefixSize() const {
+  return 0;
+}
+
 size_t MockDecrypter::GetIVSize() const {
   return 0;
 }
diff --git a/net/quic/mock_decrypter.h b/net/quic/mock_decrypter.h
index 5b7f0e22..acd7ee0 100644
--- a/net/quic/mock_decrypter.h
+++ b/net/quic/mock_decrypter.h
@@ -41,6 +41,7 @@
                      size_t* output_length,
                      size_t max_output_length) override;
   size_t GetKeySize() const override;
+  size_t GetNoncePrefixSize() const override;
   size_t GetIVSize() const override;
   quic::QuicStringPiece GetKey() const override;
   quic::QuicStringPiece GetNoncePrefix() const override;
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index f2a1ebd..da86be66 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -83,7 +83,7 @@
 QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_unified_iw_options, false)
 
 // Number of packets that the pacing sender allows in bursts during pacing.
-QUIC_FLAG(int32_t, FLAGS_quic_lumpy_pacing_size, 1)
+QUIC_FLAG(int32_t, FLAGS_quic_lumpy_pacing_size, 2)
 
 // Congestion window fraction that the pacing sender allows in bursts during
 // pacing.
@@ -173,6 +173,9 @@
     FLAGS_quic_reloadable_flag_send_quic_fallback_server_config_on_leto_error,
     false)
 
+// If true, enable QUIC version 49.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_49, false)
+
 // If true, GFE will not request private keys when fetching QUIC ServerConfigs
 // from Leto.
 QUIC_FLAG(bool,
@@ -205,13 +208,6 @@
 // If true, use predictable version negotiation versions.
 QUIC_FLAG(bool, FLAGS_quic_disable_version_negotiation_grease_randomness, false)
 
-// If true and --quic_lumpy_pacing_size is 1, QUIC will use a lumpy size of two
-// for pacing.
-QUIC_FLAG(
-    bool,
-    FLAGS_quic_reloadable_flag_quic_change_default_lumpy_pacing_size_to_two,
-    true)
-
 // If true, do not add connection ID of packets with unknown connection ID
 // and no version to time wait list, instead, send appropriate responses
 // depending on the packets' sizes and drop them.
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index ab1a31f..6cd7318 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -99,6 +99,8 @@
       return HttpResponseInfo::CONNECTION_INFO_QUIC_47;
     case quic::QUIC_VERSION_48:
       return HttpResponseInfo::CONNECTION_INFO_QUIC_48;
+    case quic::QUIC_VERSION_49:
+      return HttpResponseInfo::CONNECTION_INFO_QUIC_49;
     case quic::QUIC_VERSION_99:
       return HttpResponseInfo::CONNECTION_INFO_QUIC_99;
     case quic::QUIC_VERSION_RESERVED_FOR_NEGOTIATION:
diff --git a/net/quic/quic_test_packet_maker.cc b/net/quic/quic_test_packet_maker.cc
index 8ab3faa..8fa8bc8 100644
--- a/net/quic/quic_test_packet_maker.cc
+++ b/net/quic/quic_test_packet_maker.cc
@@ -12,6 +12,7 @@
 #include "net/quic/mock_crypto_client_stream.h"
 #include "net/quic/quic_chromium_client_session.h"
 #include "net/quic/quic_http_utils.h"
+#include "net/third_party/quiche/src/quic/core/crypto/null_encrypter.h"
 #include "net/third_party/quiche/src/quic/core/http/http_constants.h"
 #include "net/third_party/quiche/src/quic/core/quic_framer.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
@@ -163,6 +164,13 @@
   quic::QuicFramer framer(quic::test::SupportedVersions(version_),
                           clock_->Now(), perspective_,
                           quic::kQuicDefaultConnectionIdLength);
+  framer.SetInitialObfuscators(perspective_ == quic::Perspective::IS_CLIENT
+                                   ? header_.destination_connection_id
+                                   : header_.source_connection_id);
+  if (encryption_level_ != quic::ENCRYPTION_INITIAL) {
+    framer.SetEncrypter(encryption_level_,
+                        std::make_unique<quic::NullEncrypter>(perspective_));
+  }
   size_t max_plaintext_size =
       framer.GetMaxPlaintextSize(quic::kDefaultMaxPacketSize);
   char buffer[quic::kDefaultMaxPacketSize];
@@ -185,7 +193,7 @@
                                             payloads, true, encryption_level_);
   }
   size_t encrypted_size = framer.EncryptInPlace(
-      quic::ENCRYPTION_INITIAL, header_.packet_number,
+      encryption_level_, header_.packet_number,
       GetStartOfEncryptedData(framer.transport_version(), header_), length,
       quic::kDefaultMaxPacketSize, buffer);
   EXPECT_EQ(quic::kDefaultMaxPacketSize, encrypted_size);
@@ -618,6 +626,13 @@
   quic::QuicFramer framer(quic::test::SupportedVersions(version_),
                           clock_->Now(), perspective_,
                           quic::kQuicDefaultConnectionIdLength);
+  framer.SetInitialObfuscators(perspective_ == quic::Perspective::IS_CLIENT
+                                   ? header_.destination_connection_id
+                                   : header_.source_connection_id);
+  if (encryption_level_ != quic::ENCRYPTION_INITIAL) {
+    framer.SetEncrypter(encryption_level_,
+                        std::make_unique<quic::NullEncrypter>(perspective_));
+  }
   quic::QuicFrames frames;
   quic::QuicFrame ack_frame(&ack);
   frames.push_back(ack_frame);
@@ -638,8 +653,8 @@
       quic::test::BuildUnsizedDataPacket(&framer, header_, frames));
   char buffer[quic::kMaxOutgoingPacketSize];
   size_t encrypted_size =
-      framer.EncryptPayload(quic::ENCRYPTION_INITIAL, header_.packet_number,
-                            *packet, buffer, quic::kMaxOutgoingPacketSize);
+      framer.EncryptPayload(encryption_level_, header_.packet_number, *packet,
+                            buffer, quic::kMaxOutgoingPacketSize);
   EXPECT_NE(0u, encrypted_size);
   quic::QuicReceivedPacket encrypted(buffer, encrypted_size, clock_->Now(),
                                      false);
@@ -1086,6 +1101,13 @@
   quic::QuicFramer framer(quic::test::SupportedVersions(version_),
                           clock_->Now(), perspective_,
                           quic::kQuicDefaultConnectionIdLength);
+  framer.SetInitialObfuscators(perspective_ == quic::Perspective::IS_CLIENT
+                                   ? header_.destination_connection_id
+                                   : header_.source_connection_id);
+  if (encryption_level_ != quic::ENCRYPTION_INITIAL) {
+    framer.SetEncrypter(encryption_level_,
+                        std::make_unique<quic::NullEncrypter>(perspective_));
+  }
   if (data_producer != nullptr) {
     framer.set_data_producer(data_producer);
   }
@@ -1117,8 +1139,8 @@
       &framer, header, frames_copy, max_plaintext_size));
   char buffer[quic::kMaxOutgoingPacketSize];
   size_t encrypted_size =
-      framer.EncryptPayload(quic::ENCRYPTION_INITIAL, header.packet_number,
-                            *packet, buffer, quic::kMaxOutgoingPacketSize);
+      framer.EncryptPayload(encryption_level_, header.packet_number, *packet,
+                            buffer, quic::kMaxOutgoingPacketSize);
   EXPECT_NE(0u, encrypted_size);
   quic::QuicReceivedPacket encrypted(buffer, encrypted_size, clock_->Now(),
                                      false);
diff --git a/net/test/cert_builder.cc b/net/test/cert_builder.cc
index b61df8db..74c0bea6 100644
--- a/net/test/cert_builder.cc
+++ b/net/test/cert_builder.cc
@@ -291,6 +291,36 @@
   SetExtension(SubjectAltNameOid(), FinishCBB(cbb.get()));
 }
 
+void CertBuilder::SetCertificatePolicies(
+    const std::vector<std::string>& policy_oids) {
+  // From RFC 5280:
+  //    certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
+  //
+  //    PolicyInformation ::= SEQUENCE {
+  //         policyIdentifier   CertPolicyId,
+  //         policyQualifiers   SEQUENCE SIZE (1..MAX) OF
+  //                                 PolicyQualifierInfo OPTIONAL }
+  //
+  //    CertPolicyId ::= OBJECT IDENTIFIER
+  bssl::ScopedCBB cbb;
+  CBB certificate_policies;
+  ASSERT_TRUE(CBB_init(cbb.get(), 64));
+  ASSERT_TRUE(
+      CBB_add_asn1(cbb.get(), &certificate_policies, CBS_ASN1_SEQUENCE));
+  for (const auto& oid : policy_oids) {
+    CBB policy_information, policy_identifier;
+    ASSERT_TRUE(CBB_add_asn1(&certificate_policies, &policy_information,
+                             CBS_ASN1_SEQUENCE));
+    ASSERT_TRUE(
+        CBB_add_asn1(&policy_information, &policy_identifier, CBS_ASN1_OBJECT));
+    ASSERT_TRUE(
+        CBB_add_asn1_oid_from_text(&policy_identifier, oid.data(), oid.size()));
+    ASSERT_TRUE(CBB_flush(&certificate_policies));
+  }
+
+  SetExtension(CertificatePoliciesOid(), FinishCBB(cbb.get()));
+}
+
 void CertBuilder::SetValidity(base::Time not_before, base::Time not_after) {
   // From RFC 5280:
   //   Validity ::= SEQUENCE {
diff --git a/net/test/cert_builder.h b/net/test/cert_builder.h
index 153e739..e8a3d35 100644
--- a/net/test/cert_builder.h
+++ b/net/test/cert_builder.h
@@ -74,6 +74,10 @@
   // Sets the SAN for the certificate to a single dNSName.
   void SetSubjectAltName(const std::string& dns_name);
 
+  // Sets the certificatePolicies extension with the specified policyIdentifier
+  // OIDs, which must be specified in dotted string notation (e.g. "1.2.3.4").
+  void SetCertificatePolicies(const std::vector<std::string>& policy_oids);
+
   void SetValidity(base::Time not_before, base::Time not_after);
 
   // Sets the signature algorithm for the certificate to either
diff --git a/net/test/spawned_test_server/base_test_server.cc b/net/test/spawned_test_server/base_test_server.cc
index 2f62512..dbe05b6 100644
--- a/net/test/spawned_test_server/base_test_server.cc
+++ b/net/test/spawned_test_server/base_test_server.cc
@@ -331,7 +331,7 @@
   return host_port_pair_;
 }
 
-const base::DictionaryValue& BaseTestServer::server_data() const {
+const base::Value& BaseTestServer::server_data() const {
   DCHECK(server_data_);
   return *server_data_;
 }
@@ -513,19 +513,22 @@
                                            int* port) {
   VLOG(1) << "Server data: " << server_data;
   base::JSONReader json_reader;
-  std::unique_ptr<base::Value> value(
-      json_reader.ReadToValueDeprecated(server_data));
-  if (!value.get() || !value->is_dict()) {
+  base::Optional<base::Value> value(json_reader.ReadToValue(server_data));
+  if (!value || !value->is_dict()) {
     LOG(ERROR) << "Could not parse server data: "
                << json_reader.GetErrorMessage();
     return false;
   }
 
-  server_data_.reset(static_cast<base::DictionaryValue*>(value.release()));
-  if (!server_data_->GetInteger("port", port)) {
+  server_data_ = std::move(value);
+
+  base::Optional<int> port_value = server_data_->FindIntKey("port");
+  if (!port_value) {
     LOG(ERROR) << "Could not find port value";
     return false;
   }
+
+  *port = *port_value;
   if ((*port <= 0) || (*port > std::numeric_limits<uint16_t>::max())) {
     LOG(ERROR) << "Invalid port value: " << port;
     return false;
diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h
index c21e0e0..a10f786 100644
--- a/net/test/spawned_test_server/base_test_server.h
+++ b/net/test/spawned_test_server/base_test_server.h
@@ -19,15 +19,13 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "base/values.h"
 #include "net/base/host_port_pair.h"
 #include "net/ssl/ssl_client_cert_type.h"
 
 class GURL;
 
-namespace base {
-class DictionaryValue;
-}
-
 namespace net {
 
 class AddressList;
@@ -407,7 +405,7 @@
   const HostPortPair& host_port_pair() const;
 
   const base::FilePath& document_root() const { return document_root_; }
-  const base::DictionaryValue& server_data() const;
+  const base::Value& server_data() const;
   std::string GetScheme() const;
   bool GetAddressList(AddressList* address_list) const WARN_UNUSED_RESULT;
 
@@ -513,7 +511,7 @@
   HostPortPair host_port_pair_;
 
   // Holds the data sent from the server (e.g., port number).
-  std::unique_ptr<base::DictionaryValue> server_data_;
+  base::Optional<base::Value> server_data_;
 
   // If |type_| is TYPE_HTTPS or TYPE_WSS, the TLS settings to use for the test
   // server.
diff --git a/net/test/spawned_test_server/remote_test_server.cc b/net/test/spawned_test_server/remote_test_server.cc
index d29e762..302f390f 100644
--- a/net/test/spawned_test_server/remote_test_server.cc
+++ b/net/test/spawned_test_server/remote_test_server.cc
@@ -150,10 +150,9 @@
   }
 
   if (ocsp_proxy_) {
-    const base::Value* ocsp_port_value = server_data().FindKey("ocsp_port");
-    if (ocsp_port_value && ocsp_port_value->is_int()) {
-      ocsp_proxy_->Start(
-          IPEndPoint(config_.address(), ocsp_port_value->GetInt()));
+    base::Optional<int> ocsp_port_value = server_data().FindIntKey("ocsp_port");
+    if (ocsp_port_value) {
+      ocsp_proxy_->Start(IPEndPoint(config_.address(), *ocsp_port_value));
     } else {
       LOG(WARNING) << "testserver.py didn't return ocsp_port.";
     }
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index ff03d8c..7590b1a 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -58,6 +58,7 @@
 #include "net/http/http_util.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
+#include "net/log/net_log_values.h"
 #include "net/log/net_log_with_source.h"
 #include "net/nqe/network_quality_estimator.h"
 #include "net/proxy_resolution/proxy_info.h"
@@ -671,6 +672,13 @@
       status.AddExclusionReason(
           CanonicalCookie::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
     }
+    if (!status.IsInclude()) {
+      request_->net_log().AddEvent(
+          NetLogEventType::COOKIE_INCLUSION_STATUS, [&] {
+            return NetLogParamsWithString("exclusion_reason",
+                                          status.GetDebugString());
+          });
+    }
     maybe_sent_cookies.push_back({cookie_with_status.cookie, status});
   }
 
@@ -749,6 +757,11 @@
           CanonicalCookie::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
     }
     if (!returned_status.IsInclude()) {
+      request_->net_log().AddEvent(
+          NetLogEventType::COOKIE_INCLUSION_STATUS, [&] {
+            return NetLogParamsWithString("exclusion_reason",
+                                          returned_status.GetDebugString());
+          });
       OnSetCookieResult(options, cookie_to_return, std::move(cookie_string),
                         returned_status);
       continue;
diff --git a/services/tracing/public/cpp/trace_event_agent.cc b/services/tracing/public/cpp/trace_event_agent.cc
index 53ee5d0..41f5fe4e 100644
--- a/services/tracing/public/cpp/trace_event_agent.cc
+++ b/services/tracing/public/cpp/trace_event_agent.cc
@@ -81,7 +81,7 @@
 void TraceEventAgent::StartTracing(const std::string& config,
                                    base::TimeTicks coordinator_time,
                                    StartTracingCallback callback) {
-  DCHECK(!IsBoundForTesting() || !TracingUsesPerfettoBackend());
+  DCHECK(!IsBoundForTesting());
   DCHECK(!recorder_);
 #if defined(__native_client__)
   // NaCl and system times are offset by a bit, so subtract some time from
@@ -101,7 +101,7 @@
 
 void TraceEventAgent::StopAndFlush(
     mojo::PendingRemote<mojom::Recorder> recorder) {
-  DCHECK(!IsBoundForTesting() || !TracingUsesPerfettoBackend());
+  DCHECK(!IsBoundForTesting());
   DCHECK(!recorder_);
 
   recorder_.Bind(std::move(recorder));
@@ -120,7 +120,7 @@
 
 void TraceEventAgent::RequestBufferStatus(
     RequestBufferStatusCallback callback) {
-  DCHECK(!IsBoundForTesting() || !TracingUsesPerfettoBackend());
+  DCHECK(!IsBoundForTesting());
   base::trace_event::TraceLogStatus status =
       base::trace_event::TraceLog::GetInstance()->GetStatus();
   std::move(callback).Run(status.event_capacity, status.event_count);
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc
index f38c451..2f22b0c 100644
--- a/services/tracing/public/cpp/trace_startup.cc
+++ b/services/tracing/public/cpp/trace_startup.cc
@@ -45,7 +45,6 @@
 
   if (startup_config->IsEnabled()) {
     const TraceConfig& trace_config = startup_config->GetTraceConfig();
-    if (TracingUsesPerfettoBackend()) {
       if (trace_config.IsCategoryGroupEnabled(
               TRACE_DISABLED_BY_DEFAULT("cpu_profiler"))) {
         TracingSamplerProfiler::SetupStartupTracing();
@@ -53,7 +52,6 @@
       TraceEventDataSource::GetInstance()->SetupStartupTracing(
           startup_config->GetSessionOwner() ==
           TraceStartupConfig::SessionOwner::kBackgroundTracing);
-    }
 
     uint8_t modes = TraceLog::RECORDING_MODE;
     if (!trace_config.event_filters().empty())
@@ -66,9 +64,8 @@
     LOG(ERROR) << "Start " << switches::kTraceToConsole
                << " with CategoryFilter '"
                << trace_config.ToCategoryFilterString() << "'.";
-    if (TracingUsesPerfettoBackend())
-      TraceEventDataSource::GetInstance()->SetupStartupTracing(
-          /*privacy_filtering_enabled=*/false);
+    TraceEventDataSource::GetInstance()->SetupStartupTracing(
+        /*privacy_filtering_enabled=*/false);
     trace_log->SetEnabled(trace_config, TraceLog::RECORDING_MODE);
   }
 }
diff --git a/services/tracing/public/cpp/tracing_features.cc b/services/tracing/public/cpp/tracing_features.cc
index df6c26c..439d971 100644
--- a/services/tracing/public/cpp/tracing_features.cc
+++ b/services/tracing/public/cpp/tracing_features.cc
@@ -18,18 +18,6 @@
 
 namespace features {
 
-// Enables the perfetto tracing backend. For startup tracing, pass the
-// --enable-perfetto flag instead.
-const base::Feature kTracingPerfettoBackend {
-  "TracingPerfettoBackend",
-#if defined(IS_CHROMECAST)
-
-      base::FEATURE_DISABLED_BY_DEFAULT
-#else
-      base::FEATURE_ENABLED_BY_DEFAULT
-#endif
-};
-
 // Causes the BackgroundTracingManager to upload proto messages via UMA,
 // rather than JSON via the crash frontend.
 const base::Feature kBackgroundTracingProtoOutput{
@@ -62,28 +50,6 @@
 
 namespace tracing {
 
-bool TracingUsesPerfettoBackend() {
-  // This is checked early at startup, so feature list may not be initialized.
-  // So, for startup tracing cases there is no way to control the backend using
-  // feature list.
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisablePerfetto)) {
-    return false;
-  }
-
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnablePerfetto)) {
-    return true;
-  }
-
-  if (base::FeatureList::GetInstance()) {
-    return base::FeatureList::IsEnabled(features::kTracingPerfettoBackend);
-  }
-
-  return features::kTracingPerfettoBackend.default_state ==
-         base::FEATURE_ENABLED_BY_DEFAULT;
-}
-
 bool ShouldSetupSystemTracing() {
 #if defined(OS_ANDROID)
   if (base::android::BuildInfo::GetInstance()->is_debug_android()) {
@@ -91,7 +57,7 @@
   }
 #endif  // defined(OS_ANDROID)
   if (base::FeatureList::GetInstance()) {
-    return base::FeatureList::IsEnabled(features::kTracingPerfettoBackend);
+    return base::FeatureList::IsEnabled(features::kEnablePerfettoSystemTracing);
   }
   return features::kEnablePerfettoSystemTracing.default_state ==
          base::FEATURE_ENABLED_BY_DEFAULT;
diff --git a/services/tracing/public/cpp/tracing_features.h b/services/tracing/public/cpp/tracing_features.h
index e53a4918..9bd1a12 100644
--- a/services/tracing/public/cpp/tracing_features.h
+++ b/services/tracing/public/cpp/tracing_features.h
@@ -16,9 +16,6 @@
 // The features should be documented alongside the definition of their values
 // in the .cc file.
 extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature
-    kTracingPerfettoBackend;
-
-extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature
     kTracingServiceInProcess;
 
 extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature
@@ -34,8 +31,6 @@
 
 namespace tracing {
 
-bool COMPONENT_EXPORT(TRACING_CPP) TracingUsesPerfettoBackend();
-
 // Returns true if the system tracing Perfetto producer should be setup. This
 // can be influenced by the feature above or other situations (like debug
 // android builds).
diff --git a/services/tracing/tracing_service.cc b/services/tracing/tracing_service.cc
index 444890cc..09bd16e 100644
--- a/services/tracing/tracing_service.cc
+++ b/services/tracing/tracing_service.cc
@@ -179,25 +179,13 @@
 void TracingService::OnStart() {
   tracing_agent_registry_ = std::make_unique<AgentRegistry>();
 
-  if (TracingUsesPerfettoBackend()) {
-    auto perfetto_coordinator = std::make_unique<PerfettoTracingCoordinator>(
-        tracing_agent_registry_.get(),
-        base::BindRepeating(&TracingService::OnCoordinatorConnectionClosed,
-                            base::Unretained(this)));
-    registry_.AddInterface(
-        base::BindRepeating(&PerfettoTracingCoordinator::BindCoordinatorRequest,
-                            base::Unretained(perfetto_coordinator.get())));
-    tracing_coordinator_ = std::move(perfetto_coordinator);
-  } else {
-    auto tracing_coordinator = std::make_unique<Coordinator>(
-        tracing_agent_registry_.get(),
-        base::BindRepeating(&TracingService::OnCoordinatorConnectionClosed,
-                            base::Unretained(this)));
-    registry_.AddInterface(
-        base::BindRepeating(&Coordinator::BindCoordinatorRequest,
-                            base::Unretained(tracing_coordinator.get())));
-    tracing_coordinator_ = std::move(tracing_coordinator);
-  }
+  tracing_coordinator_ = std::make_unique<PerfettoTracingCoordinator>(
+      tracing_agent_registry_.get(),
+      base::BindRepeating(&TracingService::OnCoordinatorConnectionClosed,
+                          base::Unretained(this)));
+  registry_.AddInterface(
+      base::BindRepeating(&PerfettoTracingCoordinator::BindCoordinatorRequest,
+                          base::Unretained(tracing_coordinator_.get())));
 
   registry_.AddInterface(
       base::BindRepeating(&ConsumerHost::BindConsumerRequest,
diff --git a/services/tracing/tracing_service.h b/services/tracing/tracing_service.h
index 0ff7bbfa3..65697fc8 100644
--- a/services/tracing/tracing_service.h
+++ b/services/tracing/tracing_service.h
@@ -17,7 +17,7 @@
 #include "services/service_manager/public/cpp/service_binding.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
 #include "services/tracing/agent_registry.h"
-#include "services/tracing/coordinator.h"
+#include "services/tracing/perfetto/perfetto_tracing_coordinator.h"
 
 namespace tracing {
 
@@ -45,7 +45,7 @@
       const service_manager::BindSourceInfo&>
       registry_;
   std::unique_ptr<tracing::AgentRegistry> tracing_agent_registry_;
-  std::unique_ptr<Coordinator> tracing_coordinator_;
+  std::unique_ptr<PerfettoTracingCoordinator> tracing_coordinator_;
 
   std::unique_ptr<ServiceListener> service_listener_;
 
diff --git a/services/viz/privileged/mojom/compositing/display_private.mojom b/services/viz/privileged/mojom/compositing/display_private.mojom
index de364997..1e456c1 100644
--- a/services/viz/privileged/mojom/compositing/display_private.mojom
+++ b/services/viz/privileged/mojom/compositing/display_private.mojom
@@ -93,7 +93,7 @@
   DidCompleteSwapWithSize(gfx.mojom.Size size);
 
   // Notifies that a swap has occurred with a new size.
-  [EnableIf=use_x11]
+  [EnableIf=is_linux]
   DidCompleteSwapWithNewSize(gfx.mojom.Size size);
 
   // Notifies the client of the result of context creation attempt. On Android we can't
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 9c6d529..6bc847a 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -708,1616 +708,6 @@
       }
     ]
   },
-  "linux-chromeos-coverage-rel-dummy": {
-    "additional_compile_targets": [
-      "gn_all"
-    ],
-    "gtest_tests": [
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "accessibility_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "app_list_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "app_shell_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ash_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "aura_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "base_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "base_util_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_common_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_fuzzer_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "blink_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "boringssl_crypto_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "boringssl_ssl_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--disable-features=VizDisplayCompositor"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "non_viz_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "browser_tests"
-      },
-      {
-        "args": [
-          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webui_html_imports_polyfill_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "browser_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "cast_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "chrome_app_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "chromedriver_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "chromeos_components_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "chromeos_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "color_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "components_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "compositor_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 6
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--disable-features=VizDisplayCompositor"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "non_viz_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "args": [
-          "--disable-features=VizDisplayCompositor"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "non_viz_content_unittests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "content_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "dbus_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "device_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "display_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "events_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "exo_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "extensions_browsertests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "extensions_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "filesystem_service_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gl_unittests_ozone"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "gwp_asan_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "shards": 3
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "args": [
-          "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_interactive_ui_tests.filter"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webui_html_imports_polyfill_interactive_ui_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "interactive_ui_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "keyboard_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "leveldb_service_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "libjingle_xmpp_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "media_service_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "media_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "message_center_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "mojo_core_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "mojo_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "nacl_helper_nonsfi_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "nacl_loader_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "native_theme_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "net_unittests"
-      },
-      {
-        "args": [
-          "--ozone-platform=headless"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ozone_gl_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ozone_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ozone_x11_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "pdf_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "perfetto_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ppapi_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "printing_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "remoting_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "service_manager_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "services_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "shell_dialogs_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "snapshot_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "storage_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "sync_integration_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "traffic_annotation_auditor_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ui_chromeos_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "unit_tests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "url_unittests"
-      },
-      {
-        "experiment_percentage": 100,
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "usage_time_limit_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "views_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "viz_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "wayland_client_perftests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "wm_unittests"
-      },
-      {
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04"
-            }
-          ]
-        },
-        "test": "wtf_unittests"
-      }
-    ]
-  },
   "linux-chromeos-dbg": {
     "gtest_tests": [
       {
@@ -3819,6 +2209,7 @@
     ],
     "gtest_tests": [
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3834,6 +2225,7 @@
         "test": "accessibility_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3849,6 +2241,7 @@
         "test": "angle_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3864,6 +2257,7 @@
         "test": "app_list_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3879,6 +2273,7 @@
         "test": "app_shell_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3894,6 +2289,7 @@
         "test": "ash_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3909,6 +2305,7 @@
         "test": "aura_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3924,6 +2321,7 @@
         "test": "base_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3939,6 +2337,7 @@
         "test": "base_util_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3954,6 +2353,7 @@
         "test": "blink_common_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3969,6 +2369,7 @@
         "test": "blink_fuzzer_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3984,6 +2385,7 @@
         "test": "blink_heap_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -3999,6 +2401,7 @@
         "test": "blink_platform_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4015,6 +2418,7 @@
         "test": "blink_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4030,6 +2434,7 @@
         "test": "boringssl_crypto_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4045,6 +2450,7 @@
         "test": "boringssl_ssl_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4064,6 +2470,7 @@
         "args": [
           "--disable-features=VizDisplayCompositor"
         ],
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4085,6 +2492,7 @@
           "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
           "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_browser_tests.filter"
         ],
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4101,6 +2509,7 @@
         "test": "browser_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4119,6 +2528,7 @@
         "args": [
           "--gtest_filter=-*UsingRealWebcam*"
         ],
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4134,6 +2544,7 @@
         "test": "capture_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4149,6 +2560,7 @@
         "test": "cast_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4164,6 +2576,7 @@
         "test": "cc_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4179,6 +2592,7 @@
         "test": "chrome_app_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4194,6 +2608,7 @@
         "test": "chromedriver_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4209,6 +2624,7 @@
         "test": "chromeos_components_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4224,6 +2640,7 @@
         "test": "chromeos_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4239,6 +2656,7 @@
         "test": "color_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4254,6 +2672,7 @@
         "test": "components_browsertests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4269,6 +2688,7 @@
         "test": "components_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4284,6 +2704,7 @@
         "test": "compositor_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4303,6 +2724,7 @@
         "args": [
           "--disable-features=VizDisplayCompositor"
         ],
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4320,6 +2742,7 @@
         "test": "content_browsertests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4338,6 +2761,7 @@
         "args": [
           "--disable-features=VizDisplayCompositor"
         ],
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4354,6 +2778,7 @@
         "test": "content_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4369,6 +2794,7 @@
         "test": "crypto_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4384,6 +2810,7 @@
         "test": "dbus_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4399,6 +2826,7 @@
         "test": "device_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4414,6 +2842,7 @@
         "test": "display_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4429,6 +2858,7 @@
         "test": "events_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4444,6 +2874,7 @@
         "test": "exo_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4459,6 +2890,7 @@
         "test": "extensions_browsertests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4474,6 +2906,7 @@
         "test": "extensions_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4489,6 +2922,7 @@
         "test": "filesystem_service_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4504,6 +2938,7 @@
         "test": "gcm_unit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4519,6 +2954,7 @@
         "test": "gfx_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4534,6 +2970,7 @@
         "test": "gin_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4549,6 +2986,7 @@
         "test": "gl_unittests_ozone"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4564,6 +3002,7 @@
         "test": "google_apis_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4579,6 +3018,7 @@
         "test": "gpu_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4594,6 +3034,7 @@
         "test": "gwp_asan_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4614,6 +3055,7 @@
           "--disable-blink-features=HTMLImports,ShadowDOMV0,CustomElementsV0",
           "--test-launcher-filter-file=../../testing/buildbot/filters/webui_html_imports_polyfill_interactive_ui_tests.filter"
         ],
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4630,6 +3072,7 @@
         "test": "interactive_ui_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4645,6 +3088,7 @@
         "test": "ipc_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4660,6 +3104,7 @@
         "test": "jingle_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4675,6 +3120,7 @@
         "test": "keyboard_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4690,6 +3136,7 @@
         "test": "latency_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4705,6 +3152,7 @@
         "test": "leveldb_service_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4720,6 +3168,7 @@
         "test": "libjingle_xmpp_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4735,6 +3184,7 @@
         "test": "media_blink_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4750,6 +3200,7 @@
         "test": "media_service_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4765,6 +3216,7 @@
         "test": "media_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4780,6 +3232,7 @@
         "test": "message_center_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4795,6 +3248,7 @@
         "test": "midi_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4810,6 +3264,7 @@
         "test": "mojo_core_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4825,6 +3280,7 @@
         "test": "mojo_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4840,6 +3296,7 @@
         "test": "nacl_helper_nonsfi_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4855,6 +3312,7 @@
         "test": "nacl_loader_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4870,6 +3328,7 @@
         "test": "native_theme_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4888,6 +3347,7 @@
         "args": [
           "--ozone-platform=headless"
         ],
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4903,6 +3363,7 @@
         "test": "ozone_gl_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4918,6 +3379,7 @@
         "test": "ozone_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4933,6 +3395,7 @@
         "test": "ozone_x11_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4948,6 +3411,7 @@
         "test": "pdf_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4963,6 +3427,7 @@
         "test": "perfetto_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4978,6 +3443,7 @@
         "test": "ppapi_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -4993,6 +3459,7 @@
         "test": "printing_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5008,6 +3475,7 @@
         "test": "remoting_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5023,6 +3491,7 @@
         "test": "sandbox_linux_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5038,6 +3507,7 @@
         "test": "service_manager_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5053,6 +3523,7 @@
         "test": "services_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5068,6 +3539,7 @@
         "test": "shell_dialogs_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5083,6 +3555,7 @@
         "test": "skia_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5098,6 +3571,7 @@
         "test": "snapshot_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5113,6 +3587,7 @@
         "test": "sql_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5128,6 +3603,7 @@
         "test": "storage_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5143,6 +3619,7 @@
         "test": "sync_integration_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5158,6 +3635,7 @@
         "test": "traffic_annotation_auditor_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5173,6 +3651,7 @@
         "test": "ui_base_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5188,6 +3667,7 @@
         "test": "ui_chromeos_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5203,6 +3683,7 @@
         "test": "ui_touch_selection_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5218,6 +3699,7 @@
         "test": "unit_tests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5234,6 +3716,7 @@
       },
       {
         "experiment_percentage": 100,
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5249,6 +3732,7 @@
         "test": "usage_time_limit_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5264,6 +3748,7 @@
         "test": "views_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5279,6 +3764,7 @@
         "test": "viz_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5294,6 +3780,7 @@
         "test": "wayland_client_perftests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -5309,6 +3796,7 @@
         "test": "wm_unittests"
       },
       {
+        "isolate_coverage_data": true,
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py
index 64370c6..7218462 100755
--- a/testing/buildbot/generate_buildbot_json.py
+++ b/testing/buildbot/generate_buildbot_json.py
@@ -1049,8 +1049,6 @@
       'win32-dbg',
       'win-archive-dbg',
       'win32-archive-dbg',
-      # code coverage, see https://crbug.com/1000367.
-      'linux-chromeos-coverage-rel-dummy',
     ]
 
   def get_internal_waterfalls(self):
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index bfdaf5e..56001b981 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -781,21 +781,6 @@
         },
         'os_type': 'chromeos',
       },
-      # Replicate linux-chromeos-rel for code coverage experiment.
-      # TODO(crbug.com/1000367): Remove once linux-chromeos-coverage-rel is
-      # folded into linux-chromeos-rel.
-      'linux-chromeos-coverage-rel-dummy': {
-        'mixins': [
-          'linux-xenial',
-          'code-coverage',
-        ],
-        'additional_compile_targets': [
-          'gn_all',
-        ],
-        'test_suites': {
-          'gtest_tests': 'linux_chromeos_gtests',
-        },
-      },
       'linux-chromeos-dbg': {
         'mixins': [
           'linux-xenial',
@@ -807,6 +792,7 @@
       'linux-chromeos-rel': {
         'mixins': [
           'linux-xenial',
+          'code-coverage',
         ],
         'additional_compile_targets': [
           'gn_all',
diff --git a/third_party/.gitignore b/third_party/.gitignore
index a0870189..9b8f5b31 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -73,6 +73,7 @@
 /emoji-segmenter/src
 /errorprone/lib
 /espresso/lib/
+/expat/src
 /eyesfree/src
 /feed/src
 /ffmpeg
diff --git a/third_party/blink/common/indexeddb/indexeddb_key_path.cc b/third_party/blink/common/indexeddb/indexeddb_key_path.cc
index ec2b618e..552d541 100644
--- a/third_party/blink/common/indexeddb/indexeddb_key_path.cc
+++ b/third_party/blink/common/indexeddb/indexeddb_key_path.cc
@@ -18,12 +18,12 @@
     : type_(mojom::IDBKeyPathType::Array), array_(array) {}
 
 IndexedDBKeyPath::IndexedDBKeyPath(const IndexedDBKeyPath& other) = default;
-IndexedDBKeyPath::IndexedDBKeyPath(IndexedDBKeyPath&& other) noexcept = default;
+IndexedDBKeyPath::IndexedDBKeyPath(IndexedDBKeyPath&& other) = default;
 IndexedDBKeyPath::~IndexedDBKeyPath() = default;
 IndexedDBKeyPath& IndexedDBKeyPath::operator=(const IndexedDBKeyPath& other) =
     default;
-IndexedDBKeyPath& IndexedDBKeyPath::operator=(
-    IndexedDBKeyPath&& other) noexcept = default;
+IndexedDBKeyPath& IndexedDBKeyPath::operator=(IndexedDBKeyPath&& other) =
+    default;
 
 const std::vector<base::string16>& IndexedDBKeyPath::array() const {
   DCHECK(type_ == blink::mojom::IDBKeyPathType::Array);
diff --git a/third_party/blink/common/indexeddb/indexeddb_metadata.cc b/third_party/blink/common/indexeddb/indexeddb_metadata.cc
index 50d5845..93497320 100644
--- a/third_party/blink/common/indexeddb/indexeddb_metadata.cc
+++ b/third_party/blink/common/indexeddb/indexeddb_metadata.cc
@@ -23,15 +23,15 @@
 
 IndexedDBIndexMetadata::IndexedDBIndexMetadata(
     const IndexedDBIndexMetadata& other) = default;
-IndexedDBIndexMetadata::IndexedDBIndexMetadata(
-    IndexedDBIndexMetadata&& other) noexcept = default;
+IndexedDBIndexMetadata::IndexedDBIndexMetadata(IndexedDBIndexMetadata&& other) =
+    default;
 
 IndexedDBIndexMetadata::~IndexedDBIndexMetadata() = default;
 
 IndexedDBIndexMetadata& IndexedDBIndexMetadata::operator=(
     const IndexedDBIndexMetadata& other) = default;
 IndexedDBIndexMetadata& IndexedDBIndexMetadata::operator=(
-    IndexedDBIndexMetadata&& other) noexcept = default;
+    IndexedDBIndexMetadata&& other) = default;
 
 bool IndexedDBIndexMetadata::operator==(
     const IndexedDBIndexMetadata& other) const {
@@ -56,14 +56,14 @@
 IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata(
     const IndexedDBObjectStoreMetadata& other) = default;
 IndexedDBObjectStoreMetadata::IndexedDBObjectStoreMetadata(
-    IndexedDBObjectStoreMetadata&& other) noexcept = default;
+    IndexedDBObjectStoreMetadata&& other) = default;
 
 IndexedDBObjectStoreMetadata::~IndexedDBObjectStoreMetadata() = default;
 
 IndexedDBObjectStoreMetadata& IndexedDBObjectStoreMetadata::operator=(
     const IndexedDBObjectStoreMetadata& other) = default;
 IndexedDBObjectStoreMetadata& IndexedDBObjectStoreMetadata::operator=(
-    IndexedDBObjectStoreMetadata&& other) noexcept = default;
+    IndexedDBObjectStoreMetadata&& other) = default;
 
 bool IndexedDBObjectStoreMetadata::operator==(
     const IndexedDBObjectStoreMetadata& other) const {
@@ -87,14 +87,14 @@
 IndexedDBDatabaseMetadata::IndexedDBDatabaseMetadata(
     const IndexedDBDatabaseMetadata& other) = default;
 IndexedDBDatabaseMetadata::IndexedDBDatabaseMetadata(
-    IndexedDBDatabaseMetadata&& other) noexcept = default;
+    IndexedDBDatabaseMetadata&& other) = default;
 
 IndexedDBDatabaseMetadata::~IndexedDBDatabaseMetadata() = default;
 
 IndexedDBDatabaseMetadata& IndexedDBDatabaseMetadata::operator=(
     const IndexedDBDatabaseMetadata& other) = default;
 IndexedDBDatabaseMetadata& IndexedDBDatabaseMetadata::operator=(
-    IndexedDBDatabaseMetadata&& other) noexcept = default;
+    IndexedDBDatabaseMetadata&& other) = default;
 
 bool IndexedDBDatabaseMetadata::operator==(
     const IndexedDBDatabaseMetadata& other) const {
diff --git a/third_party/blink/common/mediastream/media_devices.cc b/third_party/blink/common/mediastream/media_devices.cc
index 93df201..3661b4b 100644
--- a/third_party/blink/common/mediastream/media_devices.cc
+++ b/third_party/blink/common/mediastream/media_devices.cc
@@ -13,8 +13,7 @@
 WebMediaDeviceInfo::WebMediaDeviceInfo(const WebMediaDeviceInfo& other) =
     default;
 
-WebMediaDeviceInfo::WebMediaDeviceInfo(WebMediaDeviceInfo&& other) noexcept =
-    default;
+WebMediaDeviceInfo::WebMediaDeviceInfo(WebMediaDeviceInfo&& other) = default;
 
 WebMediaDeviceInfo::WebMediaDeviceInfo(const std::string& device_id,
                                        const std::string& label,
@@ -36,8 +35,8 @@
 WebMediaDeviceInfo& WebMediaDeviceInfo::operator=(
     const WebMediaDeviceInfo& other) = default;
 
-WebMediaDeviceInfo& WebMediaDeviceInfo::operator=(
-    WebMediaDeviceInfo&& other) noexcept = default;
+WebMediaDeviceInfo& WebMediaDeviceInfo::operator=(WebMediaDeviceInfo&& other) =
+    default;
 
 bool operator==(const WebMediaDeviceInfo& first,
                 const WebMediaDeviceInfo& second) {
diff --git a/third_party/blink/common/messaging/cloneable_message.cc b/third_party/blink/common/messaging/cloneable_message.cc
index 2984fd1..5c3ee7e8 100644
--- a/third_party/blink/common/messaging/cloneable_message.cc
+++ b/third_party/blink/common/messaging/cloneable_message.cc
@@ -12,9 +12,8 @@
 namespace blink {
 
 CloneableMessage::CloneableMessage() = default;
-CloneableMessage::CloneableMessage(CloneableMessage&&) noexcept = default;
-CloneableMessage& CloneableMessage::operator=(CloneableMessage&&) noexcept =
-    default;
+CloneableMessage::CloneableMessage(CloneableMessage&&) = default;
+CloneableMessage& CloneableMessage::operator=(CloneableMessage&&) = default;
 CloneableMessage::~CloneableMessage() = default;
 
 CloneableMessage CloneableMessage::ShallowClone() const {
diff --git a/third_party/blink/common/messaging/transferable_message.cc b/third_party/blink/common/messaging/transferable_message.cc
index 07ac9bbc..4a69d0aa 100644
--- a/third_party/blink/common/messaging/transferable_message.cc
+++ b/third_party/blink/common/messaging/transferable_message.cc
@@ -9,10 +9,9 @@
 namespace blink {
 
 TransferableMessage::TransferableMessage() = default;
-TransferableMessage::TransferableMessage(TransferableMessage&&) noexcept =
+TransferableMessage::TransferableMessage(TransferableMessage&&) = default;
+TransferableMessage& TransferableMessage::operator=(TransferableMessage&&) =
     default;
-TransferableMessage& TransferableMessage::operator=(
-    TransferableMessage&&) noexcept = default;
 TransferableMessage::~TransferableMessage() = default;
 
 }  // namespace blink
diff --git a/third_party/blink/public/common/indexeddb/indexeddb_key_path.h b/third_party/blink/public/common/indexeddb/indexeddb_key_path.h
index 8ebe10c..9f5422c8 100644
--- a/third_party/blink/public/common/indexeddb/indexeddb_key_path.h
+++ b/third_party/blink/public/common/indexeddb/indexeddb_key_path.h
@@ -22,10 +22,10 @@
   explicit IndexedDBKeyPath(const base::string16&);
   explicit IndexedDBKeyPath(const std::vector<base::string16>&);
   IndexedDBKeyPath(const IndexedDBKeyPath& other);
-  IndexedDBKeyPath(IndexedDBKeyPath&& other) noexcept;
+  IndexedDBKeyPath(IndexedDBKeyPath&& other);
   ~IndexedDBKeyPath();
   IndexedDBKeyPath& operator=(const IndexedDBKeyPath& other);
-  IndexedDBKeyPath& operator=(IndexedDBKeyPath&& other) noexcept;
+  IndexedDBKeyPath& operator=(IndexedDBKeyPath&& other);
 
   bool IsNull() const { return type_ == blink::mojom::IDBKeyPathType::Null; }
   bool operator==(const IndexedDBKeyPath& other) const;
diff --git a/third_party/blink/public/common/indexeddb/indexeddb_metadata.h b/third_party/blink/public/common/indexeddb/indexeddb_metadata.h
index 15663f60..7bb37c9e 100644
--- a/third_party/blink/public/common/indexeddb/indexeddb_metadata.h
+++ b/third_party/blink/public/common/indexeddb/indexeddb_metadata.h
@@ -26,10 +26,10 @@
                          bool unique,
                          bool multi_entry);
   IndexedDBIndexMetadata(const IndexedDBIndexMetadata& other);
-  IndexedDBIndexMetadata(IndexedDBIndexMetadata&& other) noexcept;
+  IndexedDBIndexMetadata(IndexedDBIndexMetadata&& other);
   ~IndexedDBIndexMetadata();
   IndexedDBIndexMetadata& operator=(const IndexedDBIndexMetadata& other);
-  IndexedDBIndexMetadata& operator=(IndexedDBIndexMetadata&& other) noexcept;
+  IndexedDBIndexMetadata& operator=(IndexedDBIndexMetadata&& other);
   bool operator==(const IndexedDBIndexMetadata& other) const;
 
   base::string16 name;
@@ -50,12 +50,11 @@
                                bool auto_increment,
                                int64_t max_index_id);
   IndexedDBObjectStoreMetadata(const IndexedDBObjectStoreMetadata& other);
-  IndexedDBObjectStoreMetadata(IndexedDBObjectStoreMetadata&& other) noexcept;
+  IndexedDBObjectStoreMetadata(IndexedDBObjectStoreMetadata&& other);
   ~IndexedDBObjectStoreMetadata();
   IndexedDBObjectStoreMetadata& operator=(
       const IndexedDBObjectStoreMetadata& other);
-  IndexedDBObjectStoreMetadata& operator=(
-      IndexedDBObjectStoreMetadata&& other) noexcept;
+  IndexedDBObjectStoreMetadata& operator=(IndexedDBObjectStoreMetadata&& other);
   bool operator==(const IndexedDBObjectStoreMetadata& other) const;
 
   base::string16 name;
@@ -77,11 +76,10 @@
                             int64_t version,
                             int64_t max_object_store_id);
   IndexedDBDatabaseMetadata(const IndexedDBDatabaseMetadata& other);
-  IndexedDBDatabaseMetadata(IndexedDBDatabaseMetadata&& other) noexcept;
+  IndexedDBDatabaseMetadata(IndexedDBDatabaseMetadata&& other);
   ~IndexedDBDatabaseMetadata();
   IndexedDBDatabaseMetadata& operator=(const IndexedDBDatabaseMetadata& other);
-  IndexedDBDatabaseMetadata& operator=(
-      IndexedDBDatabaseMetadata&& other) noexcept;
+  IndexedDBDatabaseMetadata& operator=(IndexedDBDatabaseMetadata&& other);
   bool operator==(const IndexedDBDatabaseMetadata& other) const;
 
   base::string16 name;
diff --git a/third_party/blink/public/common/mediastream/media_devices.h b/third_party/blink/public/common/mediastream/media_devices.h
index 0d63e575..d87affeb 100644
--- a/third_party/blink/public/common/mediastream/media_devices.h
+++ b/third_party/blink/public/common/mediastream/media_devices.h
@@ -27,7 +27,7 @@
 struct BLINK_COMMON_EXPORT WebMediaDeviceInfo {
   WebMediaDeviceInfo();
   WebMediaDeviceInfo(const WebMediaDeviceInfo& other);
-  WebMediaDeviceInfo(WebMediaDeviceInfo&& other) noexcept;
+  WebMediaDeviceInfo(WebMediaDeviceInfo&& other);
   WebMediaDeviceInfo(
       const std::string& device_id,
       const std::string& label,
@@ -37,7 +37,7 @@
       const media::VideoCaptureDeviceDescriptor& descriptor);
   ~WebMediaDeviceInfo();
   WebMediaDeviceInfo& operator=(const WebMediaDeviceInfo& other);
-  WebMediaDeviceInfo& operator=(WebMediaDeviceInfo&& other) noexcept;
+  WebMediaDeviceInfo& operator=(WebMediaDeviceInfo&& other);
 
   std::string device_id;
   std::string label;
diff --git a/third_party/blink/public/common/messaging/cloneable_message.h b/third_party/blink/public/common/messaging/cloneable_message.h
index de9952d..8dbd9106 100644
--- a/third_party/blink/public/common/messaging/cloneable_message.h
+++ b/third_party/blink/public/common/messaging/cloneable_message.h
@@ -21,8 +21,8 @@
 // This type can be serialized as a blink::mojom::CloneableMessage struct.
 struct BLINK_COMMON_EXPORT CloneableMessage {
   CloneableMessage();
-  CloneableMessage(CloneableMessage&&) noexcept;
-  CloneableMessage& operator=(CloneableMessage&&) noexcept;
+  CloneableMessage(CloneableMessage&&);
+  CloneableMessage& operator=(CloneableMessage&&);
   ~CloneableMessage();
 
   // Returns a shallow clone of this message. |encoded_message| in the clone
diff --git a/third_party/blink/public/common/messaging/transferable_message.h b/third_party/blink/public/common/messaging/transferable_message.h
index 5eef63c..2d87363 100644
--- a/third_party/blink/public/common/messaging/transferable_message.h
+++ b/third_party/blink/public/common/messaging/transferable_message.h
@@ -22,8 +22,8 @@
 // type can be serialized as a blink::mojom::TransferableMessage struct.
 struct BLINK_COMMON_EXPORT TransferableMessage : public CloneableMessage {
   TransferableMessage();
-  TransferableMessage(TransferableMessage&&) noexcept;
-  TransferableMessage& operator=(TransferableMessage&&) noexcept;
+  TransferableMessage(TransferableMessage&&);
+  TransferableMessage& operator=(TransferableMessage&&);
   ~TransferableMessage();
 
   // Any ports being transferred as part of this message.
diff --git a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
index f05400c..9433fee 100644
--- a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
@@ -21,7 +21,6 @@
 import "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom";
-import "third_party/blink/public/mojom/v8_cache_options.mojom";
 import "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom";
 import "third_party/blink/public/mojom/web_feature/web_feature.mojom";
 import "url/mojom/url.mojom";
@@ -77,9 +76,6 @@
   // True if this service worker has been installed.
   bool is_installed;
 
-  // Determines how eagerly V8 creates the code cache.
-  V8CacheOptions v8_cache_options;
-
   // Used to set up fetch requests.
   RendererPreferences renderer_preferences;
 
diff --git a/third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h b/third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h
index 75f3ae16..81edd69 100644
--- a/third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h
+++ b/third_party/blink/public/platform/scheduler/web_scoped_virtual_time_pauser.h
@@ -42,9 +42,8 @@
   WebScopedVirtualTimePauser();
   ~WebScopedVirtualTimePauser();
 
-  WebScopedVirtualTimePauser(WebScopedVirtualTimePauser&& other) noexcept;
-  WebScopedVirtualTimePauser& operator=(
-      WebScopedVirtualTimePauser&& other) noexcept;
+  WebScopedVirtualTimePauser(WebScopedVirtualTimePauser&& other);
+  WebScopedVirtualTimePauser& operator=(WebScopedVirtualTimePauser&& other);
 
   WebScopedVirtualTimePauser(const WebScopedVirtualTimePauser&) = delete;
   WebScopedVirtualTimePauser& operator=(const WebScopedVirtualTimePauser&) =
diff --git a/third_party/blink/public/platform/web_string.h b/third_party/blink/public/platform/web_string.h
index e4fda4f6..ffd1da0 100644
--- a/third_party/blink/public/platform/web_string.h
+++ b/third_party/blink/public/platform/web_string.h
@@ -102,10 +102,10 @@
   BLINK_PLATFORM_EXPORT WebString(const WebUChar* data, size_t len);
 
   BLINK_PLATFORM_EXPORT WebString(const WebString&);
-  BLINK_PLATFORM_EXPORT WebString(WebString&&) noexcept;
+  BLINK_PLATFORM_EXPORT WebString(WebString&&);
 
   BLINK_PLATFORM_EXPORT WebString& operator=(const WebString&);
-  BLINK_PLATFORM_EXPORT WebString& operator=(WebString&&) noexcept;
+  BLINK_PLATFORM_EXPORT WebString& operator=(WebString&&);
 
   BLINK_PLATFORM_EXPORT void Reset();
 
diff --git a/third_party/blink/public/web/DEPS b/third_party/blink/public/web/DEPS
index 58f3066d..e2f15930 100644
--- a/third_party/blink/public/web/DEPS
+++ b/third_party/blink/public/web/DEPS
@@ -14,6 +14,7 @@
     "+cc/input/overscroll_behavior.h",
     "+cc/input/layer_selection_bound.h",
     "+cc/layers/layer.h",
+    "+cc/metrics/begin_main_frame_metrics.h",
     "+cc/paint/element_id.h",
     "+cc/paint/paint_canvas.h",
     "+cc/paint/paint_flags.h",
diff --git a/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h b/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h
index 99f47f5..8451b86 100644
--- a/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h
+++ b/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util.h
@@ -73,8 +73,8 @@
 
   VideoCaptureSettings(const VideoCaptureSettings& other);
   VideoCaptureSettings& operator=(const VideoCaptureSettings& other);
-  VideoCaptureSettings(VideoCaptureSettings&& other) noexcept;
-  VideoCaptureSettings& operator=(VideoCaptureSettings&& other) noexcept;
+  VideoCaptureSettings(VideoCaptureSettings&& other);
+  VideoCaptureSettings& operator=(VideoCaptureSettings&& other);
   ~VideoCaptureSettings();
 
   bool HasValue() const { return !failed_constraint_name_; }
@@ -193,8 +193,8 @@
       const AudioProcessingProperties& audio_processing_properties);
   AudioCaptureSettings(const AudioCaptureSettings& other);
   AudioCaptureSettings& operator=(const AudioCaptureSettings& other);
-  AudioCaptureSettings(AudioCaptureSettings&& other) noexcept;
-  AudioCaptureSettings& operator=(AudioCaptureSettings&& other) noexcept;
+  AudioCaptureSettings(AudioCaptureSettings&& other);
+  AudioCaptureSettings& operator=(AudioCaptureSettings&& other);
 
   bool HasValue() const { return !failed_constraint_name_; }
 
diff --git a/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_sets.h b/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_sets.h
index 199bdeb2..c89e6e9 100644
--- a/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_sets.h
+++ b/third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_sets.h
@@ -159,8 +159,8 @@
 
   DiscreteSet(const DiscreteSet& other) = default;
   DiscreteSet& operator=(const DiscreteSet& other) = default;
-  DiscreteSet(DiscreteSet&& other) noexcept = default;
-  DiscreteSet& operator=(DiscreteSet&& other) noexcept = default;
+  DiscreteSet(DiscreteSet&& other) = default;
+  DiscreteSet& operator=(DiscreteSet&& other) = default;
   ~DiscreteSet() = default;
 
   bool Contains(const T& value) const {
diff --git a/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h b/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h
index 5e50d1b..ed5967f 100644
--- a/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h
+++ b/third_party/blink/public/web/modules/mediastream/media_stream_video_source.h
@@ -285,8 +285,8 @@
         const VideoTrackFormatCallback& format_callback,
         std::unique_ptr<VideoTrackAdapterSettings> adapter_settings,
         const ConstraintsCallback& callback);
-    PendingTrackInfo(PendingTrackInfo&& other) noexcept;
-    PendingTrackInfo& operator=(PendingTrackInfo&& other) noexcept;
+    PendingTrackInfo(PendingTrackInfo&& other);
+    PendingTrackInfo& operator=(PendingTrackInfo&& other);
     ~PendingTrackInfo();
 
     MediaStreamVideoTrack* track;
diff --git a/third_party/blink/public/web/web_embedded_worker_start_data.h b/third_party/blink/public/web/web_embedded_worker_start_data.h
index c4e3d06..be3cf43 100644
--- a/third_party/blink/public/web/web_embedded_worker_start_data.h
+++ b/third_party/blink/public/web/web_embedded_worker_start_data.h
@@ -38,7 +38,6 @@
 #include "third_party/blink/public/platform/web_content_security_policy.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/web/web_settings.h"
 
 namespace blink {
 
@@ -54,15 +53,12 @@
   // Unique worker token used by DevTools to attribute different instrumentation
   // to the same worker.
   base::UnguessableToken devtools_worker_token;
-  WebSettings::V8CacheOptions v8_cache_options;
 
   network::mojom::IPAddressSpace address_space;
 
   PrivacyPreferences privacy_preferences;
 
-  WebEmbeddedWorkerStartData()
-      : wait_for_debugger_mode(kDontWaitForDebugger),
-        v8_cache_options(WebSettings::V8CacheOptions::kDefault) {}
+  WebEmbeddedWorkerStartData() : wait_for_debugger_mode(kDontWaitForDebugger) {}
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 31837ad0..7e5ee23 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -353,9 +353,6 @@
                            CrossOriginRedirects cross_origin_redirect_behavior,
                            mojo::ScopedMessagePipeHandle blob_url_token) {}
 
-  // The client should load an error page in the current frame.
-  virtual void LoadErrorPage(int reason) {}
-
   // Navigational queries ------------------------------------------------
 
   // Requests the client to begin a navigation for this frame.
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index bc31142..700d7448 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -34,6 +34,7 @@
 #include "base/callback.h"
 #include "base/time/time.h"
 #include "cc/input/browser_controls_state.h"
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/paint/element_id.h"
 #include "cc/trees/layer_tree_host_client.h"
 #include "third_party/blink/public/platform/web_common.h"
@@ -103,6 +104,17 @@
   // any metrics that depend upon the main frame total time.
   virtual void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) {}
 
+  // Return metrics information for the stages of BeginMainFrame. This is
+  // ultimately implemented by Blink's LocalFrameUKMAggregator. It must be a
+  // distinct call from the FrameMetrics above because the BeginMainFrameMetrics
+  // for compositor latency must be gathered before the layer tree is
+  // committed to the compositor, which is before the call to
+  // RecordEndOfFrameMetrics.
+  virtual std::unique_ptr<cc::BeginMainFrameMetrics>
+  GetBeginMainFrameMetrics() {
+    return nullptr;
+  }
+
   // Methods called to mark the beginning and end of input processing work
   // before rAF scripts are executed. Only called when gathering main frame
   // UMA and UKM. That is, when RecordStartOfFrameMetrics has been called, and
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni
index 3ba7f441..0b9a7778 100644
--- a/third_party/blink/renderer/bindings/bindings.gni
+++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -210,6 +210,7 @@
           "core/v8/v8_binding_for_testing.cc",
           "core/v8/v8_binding_for_testing.h",
           "core/v8/window_proxy_test.cc",
+          "core/v8/world_safe_v8_reference_test.cc",
           "core/v8/v8_object_builder_test.cc",
           "core/v8/v8_script_runner_test.cc",
           "core/v8/serialization/serialized_script_value_test.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.cc b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.cc
index 0f2accb..8d3b1b2 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.cc
@@ -12,9 +12,8 @@
 
 namespace blink {
 
-namespace {
-
-std::unique_ptr<DummyPageHolder> CreateDummyPageHolder(const KURL& url) {
+std::unique_ptr<DummyPageHolder> V8TestingScope::CreateDummyPageHolder(
+    const KURL& url) {
   std::unique_ptr<DummyPageHolder> holder = std::make_unique<DummyPageHolder>();
   if (url.IsValid()) {
     holder->GetFrame().Loader().CommitNavigation(
@@ -25,8 +24,6 @@
   return holder;
 }
 
-}  // namespace
-
 V8TestingScope::V8TestingScope(const KURL& url)
     : holder_(CreateDummyPageHolder(url)),
       handle_scope_(GetIsolate()),
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h
index d22214d6..ba8125b 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h
@@ -27,6 +27,9 @@
   STACK_ALLOCATED();
 
  public:
+  // TODO(keishi): Define CreateDummyPageHolder in DummyPageHolder.
+  static std::unique_ptr<DummyPageHolder> CreateDummyPageHolder(
+      const KURL& url);
   explicit V8TestingScope(const KURL& url = KURL());
   ScriptState* GetScriptState() const;
   ExecutionContext* GetExecutionContext() const;
diff --git a/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.cc b/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.cc
index 6bc84e4..7ee67ac 100644
--- a/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.cc
+++ b/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.cc
@@ -40,9 +40,6 @@
     v8::Local<v8::Value> value) {
   if (!value->IsObject())
     return;
-  // TODO(crbug.com/v8/9713): Let JSModuleNamespaceObject have CreationContext.
-  if (value->IsModuleNamespaceObject())
-    return;
 
   ScriptState* script_state =
       ScriptState::From(value.As<v8::Object>()->CreationContext());
diff --git a/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.h b/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.h
index f3bb51bc..92ee8ca 100644
--- a/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.h
+++ b/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference.h
@@ -47,40 +47,57 @@
 // provides accessors that check whether the value is accessed in the same world
 // or not, also provides an accessor that clones the value when accessed across
 // worlds.
+//
+// TODO(crbug.com/1008765): Allow WorldSafeV8Reference created/set not in
+// context.
 template <typename V8Type>
 class WorldSafeV8Reference final {
   DISALLOW_NEW();
 
  public:
   WorldSafeV8Reference() = default;
+
   explicit WorldSafeV8Reference(v8::Isolate* isolate, v8::Local<V8Type> value)
-      : v8_reference_(isolate, value),
-        world_(&DOMWrapperWorld::Current(isolate)) {
-    WorldSafeV8ReferenceInternal::MaybeCheckCreationContextWorld(*world_.get(),
-                                                                 value);
+      : v8_reference_(isolate, value) {
+    // Basically, |world_| is a world when this V8 reference is created.
+    // However, when this V8 reference isn't created in context and value is
+    // object, we set |world_| to a value's creation cotext's world.
+    if (isolate->InContext()) {
+      world_ = &DOMWrapperWorld::Current(isolate);
+      WorldSafeV8ReferenceInternal::MaybeCheckCreationContextWorld(
+          *world_.get(), value);
+    } else if (value->IsObject()) {
+      ScriptState* script_state =
+          ScriptState::From(value.template As<v8::Object>()->CreationContext());
+      world_ = &script_state->World();
+    }
   }
   ~WorldSafeV8Reference() = default;
 
-  // Returns the V8 reference.  Crashes if |target_script_state|'s world is
-  // different from this V8 reference's world.
+  // Returns the V8 reference.  Crashes if |world_| is set and it is
+  // different from |target_script_state|'s world.
   v8::Local<V8Type> Get(ScriptState* target_script_state) const {
     DCHECK(!v8_reference_.IsEmpty());
-    CHECK_EQ(world_.get(), &target_script_state->World());
+    if (world_) {
+      CHECK_EQ(world_.get(), &target_script_state->World());
+    }
     return v8_reference_.NewLocal(target_script_state->GetIsolate());
   }
 
   // Returns a V8 reference that is safe to access in |target_script_state|.
   // The return value may be a cloned object.
   v8::Local<V8Type> GetAcrossWorld(ScriptState* target_script_state) const {
+    CHECK(world_);
     return WorldSafeV8ReferenceInternal::ToWorldSafeValue(
                target_script_state, v8_reference_, *world_.get())
         .template As<V8Type>();
   }
 
-  // Sets a new V8 reference.  Crashes if |new_value|'s world is different from
-  // this V8 reference's world.
+  // Sets a new V8 reference.  Crashes if |world_| is set and it is
+  // different from |new_value|'s world.
   void Set(v8::Isolate* isolate, v8::Local<V8Type> new_value) {
     DCHECK(!new_value.IsEmpty());
+    CHECK(isolate->InContext());
     const DOMWrapperWorld& new_world = DOMWrapperWorld::Current(isolate);
     WorldSafeV8ReferenceInternal::MaybeCheckCreationContextWorld(new_world,
                                                                  new_value);
@@ -93,6 +110,7 @@
   // world of this V8 reference will be |new_value|'s world.
   void SetAcrossWorld(v8::Isolate* isolate, v8::Local<V8Type> new_value) {
     DCHECK(!new_value.IsEmpty());
+    CHECK(isolate->InContext());
     const DOMWrapperWorld& new_world = DOMWrapperWorld::Current(isolate);
     v8_reference_.Set(isolate, new_value);
     world_ = WrapRefCounted(&new_world);
diff --git a/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference_test.cc b/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference_test.cc
new file mode 100644
index 0000000..8f16674
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/world_safe_v8_reference_test.cc
@@ -0,0 +1,63 @@
+// Copyright 2019 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/bindings/core/v8/world_safe_v8_reference.h"
+
+#include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
+#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class DummyPageHolder;
+class KURL;
+
+namespace {
+
+class IsolateOnlyV8TestingScope {
+  STACK_ALLOCATED();
+
+ public:
+  IsolateOnlyV8TestingScope(const KURL& url = KURL())
+      : holder_(V8TestingScope::CreateDummyPageHolder(url)),
+        handle_scope_(GetIsolate()) {}
+
+  v8::Isolate* GetIsolate() const {
+    return ToScriptStateForMainWorld(holder_->GetDocument().GetFrame())
+        ->GetIsolate();
+  }
+
+ private:
+  std::unique_ptr<DummyPageHolder> holder_;
+  v8::HandleScope handle_scope_;
+};
+
+// http://crbug.com/1007504, http://crbug.com/1008425
+TEST(WorldSafeV8ReferenceTest, CreatedWhenNotInContext) {
+  WorldSafeV8Reference<v8::Value> v8_reference;
+  v8::Local<v8::Value> value;
+  {
+    IsolateOnlyV8TestingScope scope1;
+    v8::Isolate* isolate = scope1.GetIsolate();
+    CHECK(isolate);
+    CHECK(!isolate->InContext());
+
+    value = v8::Null(isolate);
+    v8_reference = WorldSafeV8Reference<v8::Value>(isolate, value);
+    EXPECT_FALSE(v8_reference.IsEmpty());
+  }
+  V8TestingScope scope2;
+  ScriptState* script_state = scope2.GetScriptState();
+  EXPECT_EQ(v8_reference.Get(script_state), value);
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.h.tmpl b/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.h.tmpl
index 52ad7d9..f33388f 100644
--- a/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.h.tmpl
+++ b/third_party/blink/renderer/build/scripts/core/css/templates/style_property_shorthand.h.tmpl
@@ -64,7 +64,7 @@
 const StylePropertyShorthand& transitionShorthandForParsing();
 
 // Returns an empty list if the property is not a shorthand.
-const StylePropertyShorthand& shorthandForProperty(CSSPropertyID);
+CORE_EXPORT const StylePropertyShorthand& shorthandForProperty(CSSPropertyID);
 
 // Return the list of shorthands for a given longhand.
 // The client must pass in an empty result vector.
diff --git a/third_party/blink/renderer/core/DEPS b/third_party/blink/renderer/core/DEPS
index 61297fef..b196a99b 100644
--- a/third_party/blink/renderer/core/DEPS
+++ b/third_party/blink/renderer/core/DEPS
@@ -44,6 +44,7 @@
     "+cc/layers/picture_layer.h",
     "+cc/layers/scrollbar_layer_interface.h",
     "+cc/layers/surface_layer.h",
+    "+cc/metrics/begin_main_frame_metrics.h",
     "+cc/paint/display_item_list.h",
     "+cc/paint/paint_canvas.h",
     "+cc/paint/paint_flags.h",
diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn
index a048dcf..824fe595 100644
--- a/third_party/blink/renderer/core/animation/BUILD.gn
+++ b/third_party/blink/renderer/core/animation/BUILD.gn
@@ -160,6 +160,8 @@
     "inert_effect.h",
     "interpolable_length.cc",
     "interpolable_length.h",
+    "interpolable_shadow.cc",
+    "interpolable_shadow.h",
     "interpolable_value.cc",
     "interpolable_value.h",
     "interpolated_svg_path_source.h",
@@ -202,8 +204,6 @@
     "scroll_timeline.h",
     "scroll_timeline_util.cc",
     "scroll_timeline_util.h",
-    "shadow_interpolation_functions.cc",
-    "shadow_interpolation_functions.h",
     "side_index.h",
     "size_interpolation_functions.cc",
     "size_interpolation_functions.h",
diff --git a/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
index fa454dd..fb5cbf7 100644
--- a/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.cc
@@ -365,6 +365,8 @@
   ListInterpolationFunctions::Composite(
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
+      WTF::BindRepeating(
+          ListInterpolationFunctions::InterpolableValuesKnownCompatible),
       WTF::BindRepeating(NonInterpolableSidesAreCompatible),
       WTF::BindRepeating(CompositeSide));
 }
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
index 86b713ad..1a787a8 100644
--- a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
@@ -127,6 +127,8 @@
   ListInterpolationFunctions::Composite(
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
+      WTF::BindRepeating(
+          ListInterpolationFunctions::InterpolableValuesKnownCompatible),
       GetNonInterpolableValuesAreCompatibleCallback(),
       WTF::BindRepeating(composite_callback,
                          WTF::Unretained(inner_interpolation_type_.get()),
diff --git a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
index f26a25a..07864c91 100644
--- a/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_length_list_interpolation_type.cc
@@ -148,6 +148,8 @@
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
       WTF::BindRepeating(
+          ListInterpolationFunctions::InterpolableValuesKnownCompatible),
+      WTF::BindRepeating(
           ListInterpolationFunctions::VerifyNoNonInterpolableValues),
       WTF::BindRepeating([](UnderlyingValue& underlying_value,
                             double underlying_fraction,
diff --git a/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
index 463cf6f6..32e5a11 100644
--- a/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_shadow_list_interpolation_type.cc
@@ -8,8 +8,8 @@
 #include <utility>
 
 #include "base/memory/ptr_util.h"
+#include "third_party/blink/renderer/core/animation/interpolable_shadow.h"
 #include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
-#include "third_party/blink/renderer/core/animation/shadow_interpolation_functions.h"
 #include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -44,8 +44,8 @@
   const ShadowDataVector& shadows = shadow_list->Shadows();
   return ListInterpolationFunctions::CreateList(
       shadows.size(), [&shadows, zoom](wtf_size_t index) {
-        return ShadowInterpolationFunctions::ConvertShadowData(shadows[index],
-                                                               zoom);
+        return InterpolationValue(
+            InterpolableShadow::Create(shadows[index], zoom));
       });
 }
 
@@ -116,8 +116,8 @@
   const auto& value_list = To<CSSValueList>(value);
   return ListInterpolationFunctions::CreateList(
       value_list.length(), [&value_list](wtf_size_t index) {
-        return ShadowInterpolationFunctions::MaybeConvertCSSValue(
-            value_list.Item(index));
+        return InterpolationValue(
+            InterpolableShadow::MaybeConvertCSSValue(value_list.Item(index)));
       });
 }
 
@@ -127,7 +127,12 @@
   return ListInterpolationFunctions::MaybeMergeSingles(
       std::move(start), std::move(end),
       ListInterpolationFunctions::LengthMatchingStrategy::kPadToLargest,
-      WTF::BindRepeating(ShadowInterpolationFunctions::MaybeMergeSingles));
+      WTF::BindRepeating(
+          [](InterpolationValue&& start_item, InterpolationValue&& end_item) {
+            return InterpolableShadow::MaybeMergeSingles(
+                std::move(start_item.interpolable_value),
+                std::move(end_item.interpolable_value));
+          }));
 }
 
 InterpolationValue
@@ -145,9 +150,10 @@
   ListInterpolationFunctions::Composite(
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kPadToLargest,
+      WTF::BindRepeating(InterpolableShadow::CompatibleForCompositing),
       WTF::BindRepeating(
-          ShadowInterpolationFunctions::NonInterpolableValuesAreCompatible),
-      WTF::BindRepeating(ShadowInterpolationFunctions::Composite));
+          ListInterpolationFunctions::VerifyNoNonInterpolableValues),
+      WTF::BindRepeating(InterpolableShadow::Composite));
 }
 
 static scoped_refptr<ShadowList> CreateShadowList(
@@ -159,12 +165,11 @@
   wtf_size_t length = interpolable_list.length();
   if (length == 0)
     return nullptr;
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*non_interpolable_value);
   ShadowDataVector shadows;
-  for (wtf_size_t i = 0; i < length; i++)
-    shadows.push_back(ShadowInterpolationFunctions::CreateShadowData(
-        *interpolable_list.Get(i), non_interpolable_list.Get(i), state));
+  for (wtf_size_t i = 0; i < length; i++) {
+    shadows.push_back(To<InterpolableShadow>(interpolable_list.Get(i))
+                          ->CreateShadowData(state));
+  }
   return ShadowList::Adopt(shadows);
 }
 
diff --git a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
index 181f65e..7ae4e510 100644
--- a/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_size_list_interpolation_type.cc
@@ -165,6 +165,8 @@
       underlying_value_owner, underlying_fraction, *this, value,
       ListInterpolationFunctions::LengthMatchingStrategy::kLowestCommonMultiple,
       WTF::BindRepeating(
+          ListInterpolationFunctions::InterpolableValuesKnownCompatible),
+      WTF::BindRepeating(
           SizeInterpolationFunctions::NonInterpolableValuesAreCompatible),
       WTF::BindRepeating(SizeInterpolationFunctions::Composite));
 }
diff --git a/third_party/blink/renderer/core/animation/filter_interpolation_functions.cc b/third_party/blink/renderer/core/animation/filter_interpolation_functions.cc
index dd4a2b4..6355d93 100644
--- a/third_party/blink/renderer/core/animation/filter_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/filter_interpolation_functions.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 #include "third_party/blink/renderer/core/animation/interpolable_length.h"
-#include "third_party/blink/renderer/core/animation/shadow_interpolation_functions.h"
+#include "third_party/blink/renderer/core/animation/interpolable_shadow.h"
 #include "third_party/blink/renderer/core/css/css_function_value.h"
 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
 #include "third_party/blink/renderer/core/css/resolver/filter_operation_resolver.h"
@@ -106,11 +106,10 @@
       break;
     }
 
-    case FilterOperation::DROP_SHADOW: {
-      result =
-          ShadowInterpolationFunctions::MaybeConvertCSSValue(filter.Item(0));
+    case FilterOperation::DROP_SHADOW:
+      result.interpolable_value =
+          InterpolableShadow::MaybeConvertCSSValue(filter.Item(0));
       break;
-    }
 
     default:
       NOTREACHED();
@@ -152,11 +151,10 @@
           To<BlurFilterOperation>(filter).StdDeviation(), zoom));
       break;
 
-    case FilterOperation::DROP_SHADOW: {
-      result = ShadowInterpolationFunctions::ConvertShadowData(
+    case FilterOperation::DROP_SHADOW:
+      result.interpolable_value = InterpolableShadow::Create(
           To<DropShadowFilterOperation>(filter).Shadow(), zoom);
       break;
-    }
 
     case FilterOperation::REFERENCE:
       return nullptr;
@@ -194,7 +192,7 @@
       return InterpolableLength::CreateNeutral();
 
     case FilterOperation::DROP_SHADOW:
-      return ShadowInterpolationFunctions::CreateNeutralInterpolableValue();
+      return InterpolableShadow::CreateNeutral();
 
     default:
       NOTREACHED();
@@ -247,9 +245,8 @@
     }
 
     case FilterOperation::DROP_SHADOW: {
-      ShadowData shadow_data = ShadowInterpolationFunctions::CreateShadowData(
-          interpolable_value, non_interpolable_value.TypeNonInterpolableValue(),
-          state);
+      ShadowData shadow_data =
+          To<InterpolableShadow>(interpolable_value).CreateShadowData(state);
       if (shadow_data.GetColor().IsCurrentColor())
         shadow_data.OverrideColor(Color::kBlack);
       return DropShadowFilterOperation::Create(shadow_data);
diff --git a/third_party/blink/renderer/core/animation/interpolable_length.cc b/third_party/blink/renderer/core/animation/interpolable_length.cc
index 66925c6a..30f9484 100644
--- a/third_party/blink/renderer/core/animation/interpolable_length.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_length.cc
@@ -142,8 +142,8 @@
   expression_ = &expression;
 }
 
-std::unique_ptr<InterpolableValue> InterpolableLength::Clone() const {
-  return std::make_unique<InterpolableLength>(*this);
+InterpolableLength* InterpolableLength::RawClone() const {
+  return new InterpolableLength(*this);
 }
 
 bool InterpolableLength::HasPercentage() const {
diff --git a/third_party/blink/renderer/core/animation/interpolable_length.h b/third_party/blink/renderer/core/animation/interpolable_length.h
index 3f0948b2..071d604 100644
--- a/third_party/blink/renderer/core/animation/interpolable_length.h
+++ b/third_party/blink/renderer/core/animation/interpolable_length.h
@@ -49,25 +49,31 @@
   bool HasPercentage() const;
   void SubtractFromOneHundredPercent();
 
+  std::unique_ptr<InterpolableLength> Clone() const {
+    return std::unique_ptr<InterpolableLength>(RawClone());
+  }
+  std::unique_ptr<InterpolableLength> CloneAndZero() const {
+    return std::unique_ptr<InterpolableLength>(RawCloneAndZero());
+  }
+
   // InterpolableValue:
+  void Interpolate(const InterpolableValue& to,
+                   const double progress,
+                   InterpolableValue& result) const final;
   bool IsLength() const final { return true; }
   bool Equals(const InterpolableValue& other) const final {
     NOTREACHED();
     return false;
   }
-  std::unique_ptr<InterpolableValue> Clone() const final;
-  std::unique_ptr<InterpolableValue> CloneAndZero() const final {
-    return std::make_unique<InterpolableLength>(CSSLengthArray());
-  }
   void Scale(double scale) final;
   void ScaleAndAdd(double scale, const InterpolableValue& other) final;
   void AssertCanInterpolateWith(const InterpolableValue& other) const final;
 
  private:
-  // InterpolableValue:
-  void Interpolate(const InterpolableValue& to,
-                   const double progress,
-                   InterpolableValue& result) const final;
+  InterpolableLength* RawClone() const final;
+  InterpolableLength* RawCloneAndZero() const final {
+    return new InterpolableLength(CSSLengthArray());
+  }
 
   bool IsLengthArray() const { return type_ == Type::kLengthArray; }
   bool IsExpression() const { return type_ == Type::kExpression; }
diff --git a/third_party/blink/renderer/core/animation/interpolable_shadow.cc b/third_party/blink/renderer/core/animation/interpolable_shadow.cc
new file mode 100644
index 0000000..9aa7e20
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/interpolable_shadow.cc
@@ -0,0 +1,203 @@
+// Copyright 2019 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/animation/interpolable_shadow.h"
+
+#include <memory>
+#include "third_party/blink/renderer/core/animation/css_color_interpolation_type.h"
+#include "third_party/blink/renderer/core/animation/underlying_value.h"
+#include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_shadow_value.h"
+#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
+
+namespace blink {
+namespace {
+std::unique_ptr<InterpolableLength> MaybeConvertLength(
+    const CSSPrimitiveValue* value) {
+  if (value)
+    return InterpolableLength::MaybeConvertCSSValue(*value);
+  return InterpolableLength::CreatePixels(0);
+}
+
+std::unique_ptr<InterpolableValue> MaybeConvertColor(const CSSValue* value) {
+  if (value)
+    return CSSColorInterpolationType::MaybeCreateInterpolableColor(*value);
+  return CSSColorInterpolationType::CreateInterpolableColor(
+      StyleColor::CurrentColor());
+}
+}  // namespace
+
+InterpolableShadow::InterpolableShadow(
+    std::unique_ptr<InterpolableLength> x,
+    std::unique_ptr<InterpolableLength> y,
+    std::unique_ptr<InterpolableLength> blur,
+    std::unique_ptr<InterpolableLength> spread,
+    std::unique_ptr<InterpolableValue> color,
+    ShadowStyle shadow_style)
+    : x_(std::move(x)),
+      y_(std::move(y)),
+      blur_(std::move(blur)),
+      spread_(std::move(spread)),
+      color_(std::move(color)),
+      shadow_style_(shadow_style) {
+  DCHECK(x_);
+  DCHECK(y_);
+  DCHECK(blur_);
+  DCHECK(spread_);
+  DCHECK(color_);
+}
+
+// static
+std::unique_ptr<InterpolableShadow> InterpolableShadow::Create(
+    const ShadowData& shadow_data,
+    double zoom) {
+  return std::make_unique<InterpolableShadow>(
+      InterpolableLength::CreatePixels(shadow_data.X() / zoom),
+      InterpolableLength::CreatePixels(shadow_data.Y() / zoom),
+      InterpolableLength::CreatePixels(shadow_data.Blur() / zoom),
+      InterpolableLength::CreatePixels(shadow_data.Spread() / zoom),
+      CSSColorInterpolationType::CreateInterpolableColor(
+          shadow_data.GetColor()),
+      shadow_data.Style());
+}
+
+// static
+std::unique_ptr<InterpolableShadow> InterpolableShadow::CreateNeutral() {
+  return Create(ShadowData::NeutralValue(), 1);
+}
+
+// static
+std::unique_ptr<InterpolableShadow> InterpolableShadow::MaybeConvertCSSValue(
+    const CSSValue& value) {
+  const auto* shadow = DynamicTo<CSSShadowValue>(value);
+  if (!shadow)
+    return nullptr;
+
+  ShadowStyle shadow_style = kNormal;
+  if (shadow->style) {
+    if (shadow->style->GetValueID() != CSSValueID::kInset)
+      return nullptr;
+    shadow_style = kInset;
+  }
+
+  std::unique_ptr<InterpolableLength> x = MaybeConvertLength(shadow->x.Get());
+  std::unique_ptr<InterpolableLength> y = MaybeConvertLength(shadow->y.Get());
+  std::unique_ptr<InterpolableLength> blur =
+      MaybeConvertLength(shadow->blur.Get());
+  std::unique_ptr<InterpolableLength> spread =
+      MaybeConvertLength(shadow->spread.Get());
+  std::unique_ptr<InterpolableValue> color = MaybeConvertColor(shadow->color);
+
+  // If any of the conversations failed, we can't represent this CSSValue.
+  if (!x || !y || !blur || !spread || !color)
+    return nullptr;
+
+  return std::make_unique<InterpolableShadow>(
+      std::move(x), std::move(y), std::move(blur), std::move(spread),
+      std::move(color), shadow_style);
+}
+
+// static
+PairwiseInterpolationValue InterpolableShadow::MaybeMergeSingles(
+    std::unique_ptr<InterpolableValue> start,
+    std::unique_ptr<InterpolableValue> end) {
+  if (To<InterpolableShadow>(start.get())->shadow_style_ !=
+      To<InterpolableShadow>(end.get())->shadow_style_)
+    return nullptr;
+  return PairwiseInterpolationValue(std::move(start), std::move(end));
+}
+
+//  static
+bool InterpolableShadow::CompatibleForCompositing(const InterpolableValue* from,
+                                                  const InterpolableValue* to) {
+  return To<InterpolableShadow>(from)->shadow_style_ ==
+         To<InterpolableShadow>(to)->shadow_style_;
+}
+
+// static
+void InterpolableShadow::Composite(UnderlyingValue& underlying_value,
+                                   double underlying_fraction,
+                                   const InterpolableValue& interpolable_value,
+                                   const NonInterpolableValue*) {
+  InterpolableShadow& underlying_shadow =
+      To<InterpolableShadow>(underlying_value.MutableInterpolableValue());
+  const InterpolableShadow& interpolable_shadow =
+      To<InterpolableShadow>(interpolable_value);
+  DCHECK_EQ(underlying_shadow.shadow_style_, interpolable_shadow.shadow_style_);
+  underlying_shadow.ScaleAndAdd(underlying_fraction, interpolable_shadow);
+}
+
+ShadowData InterpolableShadow::CreateShadowData(
+    const StyleResolverState& state) const {
+  const CSSToLengthConversionData& conversion_data =
+      state.CssToLengthConversionData();
+  Length shadow_x = x_->CreateLength(conversion_data, kValueRangeAll);
+  Length shadow_y = y_->CreateLength(conversion_data, kValueRangeAll);
+  Length shadow_blur =
+      blur_->CreateLength(conversion_data, kValueRangeNonNegative);
+  Length shadow_spread = spread_->CreateLength(conversion_data, kValueRangeAll);
+  DCHECK(shadow_x.IsFixed() && shadow_y.IsFixed() && shadow_blur.IsFixed() &&
+         shadow_spread.IsFixed());
+  return ShadowData(
+      FloatPoint(shadow_x.Value(), shadow_y.Value()), shadow_blur.Value(),
+      shadow_spread.Value(), shadow_style_,
+      CSSColorInterpolationType::ResolveInterpolableColor(*color_, state));
+}
+
+InterpolableShadow* InterpolableShadow::RawClone() const {
+  return new InterpolableShadow(x_->Clone(), y_->Clone(), blur_->Clone(),
+                                spread_->Clone(), color_->Clone(),
+                                shadow_style_);
+}
+
+InterpolableShadow* InterpolableShadow::RawCloneAndZero() const {
+  return new InterpolableShadow(x_->CloneAndZero(), y_->CloneAndZero(),
+                                blur_->CloneAndZero(), spread_->CloneAndZero(),
+                                color_->CloneAndZero(), shadow_style_);
+}
+
+void InterpolableShadow::Scale(double scale) {
+  x_->Scale(scale);
+  y_->Scale(scale);
+  blur_->Scale(scale);
+  spread_->Scale(scale);
+  color_->Scale(scale);
+}
+
+void InterpolableShadow::ScaleAndAdd(double scale,
+                                     const InterpolableValue& other) {
+  const InterpolableShadow& other_shadow = To<InterpolableShadow>(other);
+  x_->ScaleAndAdd(scale, *other_shadow.x_);
+  y_->ScaleAndAdd(scale, *other_shadow.y_);
+  blur_->ScaleAndAdd(scale, *other_shadow.blur_);
+  spread_->ScaleAndAdd(scale, *other_shadow.spread_);
+  color_->ScaleAndAdd(scale, *other_shadow.color_);
+}
+
+void InterpolableShadow::AssertCanInterpolateWith(
+    const InterpolableValue& other) const {
+  const InterpolableShadow& other_shadow = To<InterpolableShadow>(other);
+  DCHECK_EQ(shadow_style_, other_shadow.shadow_style_);
+  x_->AssertCanInterpolateWith(*other_shadow.x_);
+  y_->AssertCanInterpolateWith(*other_shadow.y_);
+  blur_->AssertCanInterpolateWith(*other_shadow.blur_);
+  spread_->AssertCanInterpolateWith(*other_shadow.spread_);
+  color_->AssertCanInterpolateWith(*other_shadow.color_);
+}
+
+void InterpolableShadow::Interpolate(const InterpolableValue& to,
+                                     const double progress,
+                                     InterpolableValue& result) const {
+  const InterpolableShadow& to_shadow = To<InterpolableShadow>(to);
+  InterpolableShadow& result_shadow = To<InterpolableShadow>(result);
+
+  x_->Interpolate(*to_shadow.x_, progress, *result_shadow.x_);
+  y_->Interpolate(*to_shadow.y_, progress, *result_shadow.y_);
+  blur_->Interpolate(*to_shadow.blur_, progress, *result_shadow.blur_);
+  spread_->Interpolate(*to_shadow.spread_, progress, *result_shadow.spread_);
+  color_->Interpolate(*to_shadow.color_, progress, *result_shadow.color_);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/interpolable_shadow.h b/third_party/blink/renderer/core/animation/interpolable_shadow.h
new file mode 100644
index 0000000..6bcc378
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/interpolable_shadow.h
@@ -0,0 +1,94 @@
+// Copyright 2019 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_ANIMATION_INTERPOLABLE_SHADOW_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_SHADOW_H_
+
+#include <memory>
+#include "third_party/blink/renderer/core/animation/interpolable_length.h"
+#include "third_party/blink/renderer/core/animation/interpolable_value.h"
+#include "third_party/blink/renderer/core/animation/interpolation_value.h"
+#include "third_party/blink/renderer/core/animation/pairwise_interpolation_value.h"
+#include "third_party/blink/renderer/core/style/shadow_data.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
+
+namespace blink {
+
+class CSSValue;
+class StyleResolverState;
+class UnderlyingValue;
+
+// Represents a CSS shadow value (such as [0] or [1]), converted into a form
+// that can be interpolated from/to.
+//
+// [0]: https://drafts.csswg.org/css-backgrounds-3/#typedef-shadow
+// [1]: https://drafts.fxtf.org/filter-effects/#funcdef-filter-drop-shadow
+class InterpolableShadow : public InterpolableValue {
+ public:
+  InterpolableShadow(std::unique_ptr<InterpolableLength> x,
+                     std::unique_ptr<InterpolableLength> y,
+                     std::unique_ptr<InterpolableLength> blur,
+                     std::unique_ptr<InterpolableLength> spread,
+                     std::unique_ptr<InterpolableValue> color,
+                     ShadowStyle);
+
+  static std::unique_ptr<InterpolableShadow> Create(const ShadowData&,
+                                                    double zoom);
+  static std::unique_ptr<InterpolableShadow> CreateNeutral();
+
+  static std::unique_ptr<InterpolableShadow> MaybeConvertCSSValue(
+      const CSSValue&);
+
+  // Helpers for CSSListInterpolationFunctions.
+  static PairwiseInterpolationValue MaybeMergeSingles(
+      std::unique_ptr<InterpolableValue> start,
+      std::unique_ptr<InterpolableValue> end);
+  static bool CompatibleForCompositing(const InterpolableValue*,
+                                       const InterpolableValue*);
+  static void Composite(UnderlyingValue&,
+                        double underlying_fraction,
+                        const InterpolableValue&,
+                        const NonInterpolableValue*);
+
+  // Convert this InterpolableShadow back into a ShadowData class, usually to be
+  // applied to the style after interpolating it.
+  ShadowData CreateShadowData(const StyleResolverState&) const;
+
+  // InterpolableValue implementation:
+  void Interpolate(const InterpolableValue& to,
+                   const double progress,
+                   InterpolableValue& result) const final;
+  bool IsShadow() const final { return true; }
+  bool Equals(const InterpolableValue& other) const final {
+    NOTREACHED();
+    return false;
+  }
+  void Scale(double scale) final;
+  void ScaleAndAdd(double scale, const InterpolableValue& other) final;
+  void AssertCanInterpolateWith(const InterpolableValue& other) const final;
+
+ private:
+  InterpolableShadow* RawClone() const final;
+  InterpolableShadow* RawCloneAndZero() const final;
+
+  // The interpolable components of a shadow. These should all be non-null.
+  std::unique_ptr<InterpolableLength> x_;
+  std::unique_ptr<InterpolableLength> y_;
+  std::unique_ptr<InterpolableLength> blur_;
+  std::unique_ptr<InterpolableLength> spread_;
+  std::unique_ptr<InterpolableValue> color_;
+
+  ShadowStyle shadow_style_;
+};
+
+template <>
+struct DowncastTraits<InterpolableShadow> {
+  static bool AllowFrom(const InterpolableValue& interpolable_value) {
+    return interpolable_value.IsShadow();
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_SHADOW_H_
diff --git a/third_party/blink/renderer/core/animation/interpolable_value.cc b/third_party/blink/renderer/core/animation/interpolable_value.cc
index 1f3c0ebc..504f7aa 100644
--- a/third_party/blink/renderer/core/animation/interpolable_value.cc
+++ b/third_party/blink/renderer/core/animation/interpolable_value.cc
@@ -63,11 +63,11 @@
   }
 }
 
-std::unique_ptr<InterpolableValue> InterpolableList::CloneAndZero() const {
-  auto result = std::make_unique<InterpolableList>(length());
+InterpolableList* InterpolableList::RawCloneAndZero() const {
+  auto* result = new InterpolableList(length());
   for (wtf_size_t i = 0; i < length(); i++)
     result->Set(i, values_[i]->CloneAndZero());
-  return std::move(result);
+  return result;
 }
 
 void InterpolableNumber::Scale(double scale) {
diff --git a/third_party/blink/renderer/core/animation/interpolable_value.h b/third_party/blink/renderer/core/animation/interpolable_value.h
index 8531e99..65915fa 100644
--- a/third_party/blink/renderer/core/animation/interpolable_value.h
+++ b/third_party/blink/renderer/core/animation/interpolable_value.h
@@ -23,59 +23,74 @@
  public:
   virtual ~InterpolableValue() = default;
 
+  // Interpolates from |this| InterpolableValue towards |to| at the given
+  // |progress|, placing the output in |result|. That is:
+  //
+  //   result = this * (1 - progress) + to * progress
+  //
+  // Callers must make sure that |this|, |to|, and |result| are all of the same
+  // concrete subclass.
+  virtual void Interpolate(const InterpolableValue& to,
+                           const double progress,
+                           InterpolableValue& result) const = 0;
+
   virtual bool IsNumber() const { return false; }
   virtual bool IsBool() const { return false; }
   virtual bool IsList() const { return false; }
   virtual bool IsLength() const { return false; }
+  virtual bool IsShadow() const { return false; }
 
   // TODO(alancutter): Remove Equals().
   virtual bool Equals(const InterpolableValue&) const = 0;
-  virtual std::unique_ptr<InterpolableValue> Clone() const = 0;
-  virtual std::unique_ptr<InterpolableValue> CloneAndZero() const = 0;
   virtual void Scale(double scale) = 0;
   virtual void ScaleAndAdd(double scale, const InterpolableValue& other) = 0;
   virtual void AssertCanInterpolateWith(
       const InterpolableValue& other) const = 0;
 
+  // Clone this value, optionally zeroing out the components at the same time.
+  // These are not virtual to allow for covariant return types; see
+  // documentation on RawClone/RawCloneAndZero.
+  std::unique_ptr<InterpolableValue> Clone() const {
+    return std::unique_ptr<InterpolableValue>(RawClone());
+  }
+  std::unique_ptr<InterpolableValue> CloneAndZero() const {
+    return std::unique_ptr<InterpolableValue>(RawCloneAndZero());
+  }
+
  private:
-  virtual void Interpolate(const InterpolableValue& to,
-                           const double progress,
-                           InterpolableValue& result) const = 0;
-
-  friend class LegacyStyleInterpolation;
-  friend class TransitionInterpolation;
-  friend class PairwisePrimitiveInterpolation;
-
-  // Keep interpolate private, but allow calls within the hierarchy without
-  // knowledge of type.
-  friend class InterpolableNumber;
-  friend class InterpolableList;
-
-  friend class AnimationInterpolableValueTest;
+  // Helper methods to allow covariant Clone/CloneAndZero methods. Concrete
+  // subclasses should not expose these methods publically, but instead should
+  // declare their own version of Clone/CloneAndZero with a concrete return type
+  // if it is useful for their clients.
+  virtual InterpolableValue* RawClone() const = 0;
+  virtual InterpolableValue* RawCloneAndZero() const = 0;
 };
 
 class CORE_EXPORT InterpolableNumber final : public InterpolableValue {
  public:
   explicit InterpolableNumber(double value) : value_(value) {}
 
-  bool IsNumber() const final { return true; }
   double Value() const { return value_; }
-  bool Equals(const InterpolableValue& other) const final;
-  std::unique_ptr<InterpolableValue> Clone() const final {
-    return std::make_unique<InterpolableNumber>(value_);
-  }
-  std::unique_ptr<InterpolableValue> CloneAndZero() const final {
-    return std::make_unique<InterpolableNumber>(0);
-  }
-  void Scale(double scale) final;
-  void ScaleAndAdd(double scale, const InterpolableValue& other) final;
   void Set(double value) { value_ = value; }
-  void AssertCanInterpolateWith(const InterpolableValue& other) const final;
 
- private:
+  // InterpolableValue
   void Interpolate(const InterpolableValue& to,
                    const double progress,
                    InterpolableValue& result) const final;
+  bool IsNumber() const final { return true; }
+  bool Equals(const InterpolableValue& other) const final;
+  void Scale(double scale) final;
+  void ScaleAndAdd(double scale, const InterpolableValue& other) final;
+  void AssertCanInterpolateWith(const InterpolableValue& other) const final;
+
+ private:
+  InterpolableNumber* RawClone() const final {
+    return new InterpolableNumber(value_);
+  }
+  InterpolableNumber* RawCloneAndZero() const final {
+    return new InterpolableNumber(0);
+  }
+
   double value_;
 };
 
@@ -96,10 +111,6 @@
       Set(i, other.values_[i]->Clone());
   }
 
-  bool IsList() const final { return true; }
-  void Set(wtf_size_t position, std::unique_ptr<InterpolableValue> value) {
-    values_[position] = std::move(value);
-  }
   const InterpolableValue* Get(wtf_size_t position) const {
     return values_[position].get();
   }
@@ -107,19 +118,25 @@
     return values_[position];
   }
   wtf_size_t length() const { return values_.size(); }
-  bool Equals(const InterpolableValue& other) const final;
-  std::unique_ptr<InterpolableValue> Clone() const final {
-    return std::make_unique<InterpolableList>(*this);
+  void Set(wtf_size_t position, std::unique_ptr<InterpolableValue> value) {
+    values_[position] = std::move(value);
   }
-  std::unique_ptr<InterpolableValue> CloneAndZero() const final;
+
+  // InterpolableValue
+  void Interpolate(const InterpolableValue& to,
+                   const double progress,
+                   InterpolableValue& result) const final;
+  bool IsList() const final { return true; }
+  bool Equals(const InterpolableValue& other) const final;
   void Scale(double scale) final;
   void ScaleAndAdd(double scale, const InterpolableValue& other) final;
   void AssertCanInterpolateWith(const InterpolableValue& other) const final;
 
  private:
-  void Interpolate(const InterpolableValue& to,
-                   const double progress,
-                   InterpolableValue& result) const final;
+  InterpolableList* RawClone() const final {
+    return new InterpolableList(*this);
+  }
+  InterpolableList* RawCloneAndZero() const final;
 
   Vector<std::unique_ptr<InterpolableValue>> values_;
 };
diff --git a/third_party/blink/renderer/core/animation/interpolation_value.h b/third_party/blink/renderer/core/animation/interpolation_value.h
index 75c5d35..17ccf02 100644
--- a/third_party/blink/renderer/core/animation/interpolation_value.h
+++ b/third_party/blink/renderer/core/animation/interpolation_value.h
@@ -27,11 +27,11 @@
 
   InterpolationValue(std::nullptr_t) {}
 
-  InterpolationValue(InterpolationValue&& other) noexcept
+  InterpolationValue(InterpolationValue&& other)
       : interpolable_value(std::move(other.interpolable_value)),
         non_interpolable_value(std::move(other.non_interpolable_value)) {}
 
-  void operator=(InterpolationValue&& other) noexcept {
+  void operator=(InterpolationValue&& other) {
     interpolable_value = std::move(other.interpolable_value);
     non_interpolable_value = std::move(other.non_interpolable_value);
   }
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
index c23539d..146f840 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.cc
@@ -281,6 +281,27 @@
       NonInterpolableList::Create(std::move(new_non_interpolable_values));
 }
 
+static bool InterpolableListsAreCompatible(
+    const InterpolableList& a,
+    const InterpolableList& b,
+    wtf_size_t length,
+    ListInterpolationFunctions::LengthMatchingStrategy length_matching_strategy,
+    ListInterpolationFunctions::InterpolableValuesAreCompatibleCallback
+        interpolable_values_are_compatible) {
+  for (wtf_size_t i = 0; i < length; i++) {
+    if (length_matching_strategy ==
+            ListInterpolationFunctions::LengthMatchingStrategy::
+                kLowestCommonMultiple ||
+        (i < a.length() && i < b.length())) {
+      if (!interpolable_values_are_compatible.Run(a.Get(i % a.length()),
+                                                  b.Get(i % b.length()))) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 static bool NonInterpolableListsAreCompatible(
     const NonInterpolableList& a,
     const NonInterpolableList& b,
@@ -315,6 +336,7 @@
     const InterpolationType& type,
     const InterpolationValue& value,
     LengthMatchingStrategy length_matching_strategy,
+    InterpolableValuesAreCompatibleCallback interpolable_values_are_compatible,
     NonInterpolableValuesAreCompatibleCallback
         non_interpolable_values_are_compatible,
     CompositeItemCallback composite_item) {
@@ -346,10 +368,20 @@
     return;
   }
 
-  const NonInterpolableList& non_interpolable_list =
-      ToNonInterpolableList(*value.non_interpolable_value);
   const wtf_size_t final_length =
       MatchLengths(underlying_length, value_length, length_matching_strategy);
+
+  if (!InterpolableListsAreCompatible(
+          ToInterpolableList(
+              *underlying_value_owner.Value().interpolable_value),
+          interpolable_list, final_length, length_matching_strategy,
+          interpolable_values_are_compatible)) {
+    underlying_value_owner.Set(type, value);
+    return;
+  }
+
+  const NonInterpolableList& non_interpolable_list =
+      ToNonInterpolableList(*value.non_interpolable_value);
   if (!NonInterpolableListsAreCompatible(
           ToNonInterpolableList(
               *underlying_value_owner.Value().non_interpolable_value),
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions.h b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
index a254bc4..1cd58ff 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions.h
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions.h
@@ -49,6 +49,9 @@
                           const InterpolationValue&,
                           EqualNonInterpolableValuesCallback);
 
+  using InterpolableValuesAreCompatibleCallback =
+      base::RepeatingCallback<bool(const InterpolableValue*,
+                                   const InterpolableValue*)>;
   using NonInterpolableValuesAreCompatibleCallback =
       base::RepeatingCallback<bool(const NonInterpolableValue*,
                                    const NonInterpolableValue*)>;
@@ -62,9 +65,16 @@
                         const InterpolationType&,
                         const InterpolationValue&,
                         LengthMatchingStrategy,
+                        InterpolableValuesAreCompatibleCallback,
                         NonInterpolableValuesAreCompatibleCallback,
                         CompositeItemCallback);
 
+  // Used when the interpolable values are known to always be compatible.
+  static bool InterpolableValuesKnownCompatible(const InterpolableValue* a,
+                                                const InterpolableValue* b) {
+    return true;
+  }
+
   // We are moving towards elimination of |NonInterpolableValue|, and expect
   // more clients to assert no more usage with this function.
   static bool VerifyNoNonInterpolableValues(const NonInterpolableValue* a,
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
index 01929f8..23ac9fa3 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
@@ -105,6 +105,27 @@
       });
 }
 
+// A simple helper to specify which InterpolableValues in an InterpolableList
+// should be considered compatible.
+class InterpolableValuesCompatibilityHelper {
+ public:
+  // The input |answers| vector must be at least as large as the
+  // InterpolableList being tested, or |AreCompatible| will DCHECK.
+  InterpolableValuesCompatibilityHelper(Vector<bool> answers)
+      : answers_(answers), current_index_(0) {}
+
+  // Callers should pass a reference to this function to
+  // ListInterpolationFunctions::Composite.
+  bool AreCompatible(const InterpolableValue*, const InterpolableValue*) {
+    DCHECK(current_index_ < answers_.size());
+    return answers_.at(current_index_++);
+  }
+
+ private:
+  Vector<bool> answers_;
+  wtf_size_t current_index_;
+};
+
 bool NonInterpolableValuesAreCompatible(const NonInterpolableValue* a,
                                         const NonInterpolableValue* b) {
   return (a ? ToTestNonInterpolableValue(*a).GetValue() : 0) ==
@@ -193,6 +214,8 @@
   ListInterpolationFunctions::Composite(
       owner, 1.0, interpolation_type, list2,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
+      WTF::BindRepeating(
+          ListInterpolationFunctions::InterpolableValuesKnownCompatible),
       WTF::BindRepeating(NonInterpolableValuesAreCompatible),
       WTF::BindRepeating(Composite));
 
@@ -218,6 +241,8 @@
   ListInterpolationFunctions::Composite(
       owner, 1.0, interpolation_type, list2,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
+      WTF::BindRepeating(
+          ListInterpolationFunctions::InterpolableValuesKnownCompatible),
       WTF::BindRepeating(NonInterpolableValuesAreCompatible),
       WTF::BindRepeating(Composite));
 
@@ -230,7 +255,39 @@
 
 // If one (or more) of the element pairs are incompatible, the list as a whole
 // is non-interpolable. We expect the underlying value to be replaced.
-TEST(ListInterpolationFunctionsTest, EqualCompositeIncompatibleValues) {
+TEST(ListInterpolationFunctionsTest,
+     EqualCompositeIncompatibleInterpolableValues) {
+  auto list1 = CreateInterpolableList({1.0, 2.0, 3.0});
+  auto list2 = CreateInterpolableList({4.0, 5.0, 6.0});
+
+  InterpolableValuesCompatibilityHelper compatibility_helper(
+      {true, false, true});
+
+  PropertyHandle property_handle(GetCSSPropertyZIndex());
+  CSSNumberInterpolationType interpolation_type(property_handle);
+  UnderlyingValueOwner owner;
+  owner.Set(interpolation_type, std::move(list1));
+
+  ListInterpolationFunctions::Composite(
+      owner, 1.0, interpolation_type, list2,
+      ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
+      WTF::BindRepeating(&InterpolableValuesCompatibilityHelper::AreCompatible,
+                         WTF::Unretained(&compatibility_helper)),
+      WTF::BindRepeating(NonInterpolableValuesAreCompatible),
+      WTF::BindRepeating(Composite));
+
+  const auto& result = ToInterpolableList(*owner.Value().interpolable_value);
+
+  ASSERT_EQ(result.length(), 3u);
+  EXPECT_EQ(ToInterpolableNumber(result.Get(0))->Value(), 4.0);
+  EXPECT_EQ(ToInterpolableNumber(result.Get(1))->Value(), 5.0);
+  EXPECT_EQ(ToInterpolableNumber(result.Get(2))->Value(), 6.0);
+}
+
+// If one (or more) of the element pairs are incompatible, the list as a whole
+// is non-interpolable. We expect the underlying value to be replaced.
+TEST(ListInterpolationFunctionsTest,
+     EqualCompositeIncompatibleNonInterpolableValues) {
   auto list1 = CreateInterpolableList({{1.0, 1}, {2.0, 2}, {3.0, 3}});
   auto list2 = CreateInterpolableList({{4.0, 1}, {5.0, 4}, {6.0, 3}});
 
@@ -242,6 +299,8 @@
   ListInterpolationFunctions::Composite(
       owner, 1.0, interpolation_type, list2,
       ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
+      WTF::BindRepeating(
+          ListInterpolationFunctions::InterpolableValuesKnownCompatible),
       WTF::BindRepeating(NonInterpolableValuesAreCompatible),
       WTF::BindRepeating(Composite));
 
diff --git a/third_party/blink/renderer/core/animation/pairwise_interpolation_value.h b/third_party/blink/renderer/core/animation/pairwise_interpolation_value.h
index 39e6c57..87bb431 100644
--- a/third_party/blink/renderer/core/animation/pairwise_interpolation_value.h
+++ b/third_party/blink/renderer/core/animation/pairwise_interpolation_value.h
@@ -28,7 +28,7 @@
 
   PairwiseInterpolationValue(std::nullptr_t) {}
 
-  PairwiseInterpolationValue(PairwiseInterpolationValue&& other) noexcept
+  PairwiseInterpolationValue(PairwiseInterpolationValue&& other)
       : start_interpolable_value(std::move(other.start_interpolable_value)),
         end_interpolable_value(std::move(other.end_interpolable_value)),
         non_interpolable_value(std::move(other.non_interpolable_value)) {}
diff --git a/third_party/blink/renderer/core/animation/shadow_interpolation_functions.cc b/third_party/blink/renderer/core/animation/shadow_interpolation_functions.cc
deleted file mode 100644
index 5274b9f..0000000
--- a/third_party/blink/renderer/core/animation/shadow_interpolation_functions.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/animation/shadow_interpolation_functions.h"
-
-#include <memory>
-#include "third_party/blink/renderer/core/animation/css_color_interpolation_type.h"
-#include "third_party/blink/renderer/core/animation/interpolable_length.h"
-#include "third_party/blink/renderer/core/animation/interpolation_value.h"
-#include "third_party/blink/renderer/core/animation/non_interpolable_value.h"
-#include "third_party/blink/renderer/core/animation/underlying_value.h"
-#include "third_party/blink/renderer/core/css/css_identifier_value.h"
-#include "third_party/blink/renderer/core/css/css_shadow_value.h"
-#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
-#include "third_party/blink/renderer/core/style/shadow_data.h"
-#include "third_party/blink/renderer/platform/geometry/float_point.h"
-
-namespace blink {
-
-enum ShadowComponentIndex : unsigned {
-  kShadowX,
-  kShadowY,
-  kShadowBlur,
-  kShadowSpread,
-  kShadowColor,
-  kShadowComponentIndexCount,
-};
-
-class ShadowNonInterpolableValue : public NonInterpolableValue {
- public:
-  ~ShadowNonInterpolableValue() final = default;
-
-  static scoped_refptr<ShadowNonInterpolableValue> Create(
-      ShadowStyle shadow_style) {
-    return base::AdoptRef(new ShadowNonInterpolableValue(shadow_style));
-  }
-
-  ShadowStyle Style() const { return style_; }
-
-  DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
-
- private:
-  ShadowNonInterpolableValue(ShadowStyle shadow_style) : style_(shadow_style) {}
-
-  ShadowStyle style_;
-};
-
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE(ShadowNonInterpolableValue);
-DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(ShadowNonInterpolableValue);
-
-bool ShadowInterpolationFunctions::NonInterpolableValuesAreCompatible(
-    const NonInterpolableValue* a,
-    const NonInterpolableValue* b) {
-  return ToShadowNonInterpolableValue(*a).Style() ==
-         ToShadowNonInterpolableValue(*b).Style();
-}
-
-PairwiseInterpolationValue ShadowInterpolationFunctions::MaybeMergeSingles(
-    InterpolationValue&& start,
-    InterpolationValue&& end) {
-  if (!NonInterpolableValuesAreCompatible(start.non_interpolable_value.get(),
-                                          end.non_interpolable_value.get()))
-    return nullptr;
-  return PairwiseInterpolationValue(std::move(start.interpolable_value),
-                                    std::move(end.interpolable_value),
-                                    std::move(start.non_interpolable_value));
-}
-
-InterpolationValue ShadowInterpolationFunctions::ConvertShadowData(
-    const ShadowData& shadow_data,
-    double zoom) {
-  auto interpolable_list =
-      std::make_unique<InterpolableList>(kShadowComponentIndexCount);
-  interpolable_list->Set(
-      kShadowX, InterpolableLength::CreatePixels(shadow_data.X() / zoom));
-  interpolable_list->Set(
-      kShadowY, InterpolableLength::CreatePixels(shadow_data.Y() / zoom));
-  interpolable_list->Set(
-      kShadowBlur, InterpolableLength::CreatePixels(shadow_data.Blur() / zoom));
-  interpolable_list->Set(kShadowSpread, InterpolableLength::CreatePixels(
-                                            shadow_data.Spread() / zoom));
-  interpolable_list->Set(kShadowColor,
-                         CSSColorInterpolationType::CreateInterpolableColor(
-                             shadow_data.GetColor()));
-  return InterpolationValue(
-      std::move(interpolable_list),
-      ShadowNonInterpolableValue::Create(shadow_data.Style()));
-}
-
-InterpolationValue ShadowInterpolationFunctions::MaybeConvertCSSValue(
-    const CSSValue& value) {
-  const auto* shadow = DynamicTo<CSSShadowValue>(value);
-  if (!shadow)
-    return nullptr;
-
-  ShadowStyle style = kNormal;
-  if (shadow->style) {
-    if (shadow->style->GetValueID() == CSSValueID::kInset)
-      style = kInset;
-    else
-      return nullptr;
-  }
-
-  auto interpolable_list =
-      std::make_unique<InterpolableList>(kShadowComponentIndexCount);
-  static_assert(kShadowX == 0, "Enum ordering check.");
-  static_assert(kShadowY == 1, "Enum ordering check.");
-  static_assert(kShadowBlur == 2, "Enum ordering check.");
-  static_assert(kShadowSpread == 3, "Enum ordering check.");
-  const CSSPrimitiveValue* lengths[] = {
-      shadow->x.Get(),
-      shadow->y.Get(),
-      shadow->blur.Get(),
-      shadow->spread.Get(),
-  };
-  for (wtf_size_t i = 0; i < base::size(lengths); i++) {
-    if (lengths[i]) {
-      InterpolationValue length_field(
-          InterpolableLength::MaybeConvertCSSValue(*lengths[i]));
-      if (!length_field)
-        return nullptr;
-      DCHECK(!length_field.non_interpolable_value);
-      interpolable_list->Set(i, std::move(length_field.interpolable_value));
-    } else {
-      interpolable_list->Set(i, InterpolableLength::CreatePixels(0));
-    }
-  }
-
-  if (shadow->color) {
-    std::unique_ptr<InterpolableValue> interpolable_color =
-        CSSColorInterpolationType::MaybeCreateInterpolableColor(*shadow->color);
-    if (!interpolable_color)
-      return nullptr;
-    interpolable_list->Set(kShadowColor, std::move(interpolable_color));
-  } else {
-    interpolable_list->Set(kShadowColor,
-                           CSSColorInterpolationType::CreateInterpolableColor(
-                               StyleColor::CurrentColor()));
-  }
-
-  return InterpolationValue(std::move(interpolable_list),
-                            ShadowNonInterpolableValue::Create(style));
-}
-
-std::unique_ptr<InterpolableValue>
-ShadowInterpolationFunctions::CreateNeutralInterpolableValue() {
-  return ConvertShadowData(ShadowData::NeutralValue(), 1).interpolable_value;
-}
-
-void ShadowInterpolationFunctions::Composite(
-    UnderlyingValue& underlying_value,
-    double underlying_fraction,
-    const InterpolableValue& interpolable_value,
-    const NonInterpolableValue* non_interpolable_value) {
-  DCHECK(NonInterpolableValuesAreCompatible(
-      underlying_value.GetNonInterpolableValue(), non_interpolable_value));
-  InterpolableList& underlying_interpolable_list =
-      ToInterpolableList(underlying_value.MutableInterpolableValue());
-  const InterpolableList& interpolable_list =
-      ToInterpolableList(interpolable_value);
-  underlying_interpolable_list.ScaleAndAdd(underlying_fraction,
-                                           interpolable_list);
-}
-
-ShadowData ShadowInterpolationFunctions::CreateShadowData(
-    const InterpolableValue& interpolable_value,
-    const NonInterpolableValue* non_interpolable_value,
-    const StyleResolverState& state) {
-  const InterpolableList& interpolable_list =
-      ToInterpolableList(interpolable_value);
-  const ShadowNonInterpolableValue& shadow_non_interpolable_value =
-      ToShadowNonInterpolableValue(*non_interpolable_value);
-  const CSSToLengthConversionData& conversion_data =
-      state.CssToLengthConversionData();
-  Length shadow_x = To<InterpolableLength>(*interpolable_list.Get(kShadowX))
-                        .CreateLength(conversion_data, kValueRangeAll);
-  Length shadow_y = To<InterpolableLength>(*interpolable_list.Get(kShadowY))
-                        .CreateLength(conversion_data, kValueRangeAll);
-  Length shadow_blur =
-      To<InterpolableLength>(*interpolable_list.Get(kShadowBlur))
-          .CreateLength(conversion_data, kValueRangeNonNegative);
-  Length shadow_spread =
-      To<InterpolableLength>(*interpolable_list.Get(kShadowSpread))
-          .CreateLength(conversion_data, kValueRangeAll);
-  DCHECK(shadow_x.IsFixed() && shadow_y.IsFixed() && shadow_blur.IsFixed() &&
-         shadow_spread.IsFixed());
-  return ShadowData(FloatPoint(shadow_x.Value(), shadow_y.Value()),
-                    shadow_blur.Value(), shadow_spread.Value(),
-                    shadow_non_interpolable_value.Style(),
-                    CSSColorInterpolationType::ResolveInterpolableColor(
-                        *interpolable_list.Get(kShadowColor), state));
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/shadow_interpolation_functions.h b/third_party/blink/renderer/core/animation/shadow_interpolation_functions.h
deleted file mode 100644
index 7745390..0000000
--- a/third_party/blink/renderer/core/animation/shadow_interpolation_functions.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SHADOW_INTERPOLATION_FUNCTIONS_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SHADOW_INTERPOLATION_FUNCTIONS_H_
-
-#include <memory>
-#include "third_party/blink/renderer/core/animation/interpolation_value.h"
-#include "third_party/blink/renderer/core/animation/pairwise_interpolation_value.h"
-
-namespace blink {
-
-class ShadowData;
-class CSSValue;
-class StyleResolverState;
-class UnderlyingValue;
-
-class ShadowInterpolationFunctions {
- public:
-  static InterpolationValue ConvertShadowData(const ShadowData&, double zoom);
-  static InterpolationValue MaybeConvertCSSValue(const CSSValue&);
-  static std::unique_ptr<InterpolableValue> CreateNeutralInterpolableValue();
-  static bool NonInterpolableValuesAreCompatible(const NonInterpolableValue*,
-                                                 const NonInterpolableValue*);
-  static PairwiseInterpolationValue MaybeMergeSingles(
-      InterpolationValue&& start,
-      InterpolationValue&& end);
-  static void Composite(UnderlyingValue&,
-                        double underlying_fraction,
-                        const InterpolableValue&,
-                        const NonInterpolableValue*);
-  static ShadowData CreateShadowData(const InterpolableValue&,
-                                     const NonInterpolableValue*,
-                                     const StyleResolverState&);
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SHADOW_INTERPOLATION_FUNCTIONS_H_
diff --git a/third_party/blink/renderer/core/css/BUILD.gn b/third_party/blink/renderer/core/css/BUILD.gn
index 1cf96b76b..afd5ae1 100644
--- a/third_party/blink/renderer/core/css/BUILD.gn
+++ b/third_party/blink/renderer/core/css/BUILD.gn
@@ -616,6 +616,7 @@
     "cssom/css_unit_value_test.cc",
     "cssom/css_unparsed_value_test.cc",
     "cssom/css_unsupported_color_value_test.cc",
+    "cssom/inline_style_property_map_test.cc",
     "cssom/paint_worklet_style_property_map_test.cc",
     "cssom/prepopulated_computed_style_property_map_test.cc",
     "drag_update_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_selector_list.h b/third_party/blink/renderer/core/css/css_selector_list.h
index 4d38b09..51f354b 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.h
+++ b/third_party/blink/renderer/core/css/css_selector_list.h
@@ -67,8 +67,7 @@
  public:
   CSSSelectorList() : selector_array_(nullptr) {}
 
-  CSSSelectorList(CSSSelectorList&& o) noexcept
-      : selector_array_(o.selector_array_) {
+  CSSSelectorList(CSSSelectorList&& o) : selector_array_(o.selector_array_) {
     o.selector_array_ = nullptr;
   }
 
@@ -82,7 +81,7 @@
   bool HasPseudoWhere() const;
   bool RequiresExpansion() const;
 
-  CSSSelectorList& operator=(CSSSelectorList&& o) noexcept {
+  CSSSelectorList& operator=(CSSSelectorList&& o) {
     DCHECK(this != &o);
     DeleteSelectorsIfNeeded();
     selector_array_ = o.selector_array_;
diff --git a/third_party/blink/renderer/core/css/cssom/css_position_value.cc b/third_party/blink/renderer/core/css/cssom/css_position_value.cc
index cff142e..552b408 100644
--- a/third_party/blink/renderer/core/css/cssom/css_position_value.cc
+++ b/third_party/blink/renderer/core/css/cssom/css_position_value.cc
@@ -95,10 +95,11 @@
 }
 
 CSSPositionValue* CSSPositionValue::FromCSSValue(const CSSValue& value) {
-  const auto& pair = To<CSSValuePair>(value);
-
-  CSSNumericValue* x = FromSingleValue(pair.First());
-  CSSNumericValue* y = FromSingleValue(pair.Second());
+  const auto* pair = DynamicTo<CSSValuePair>(&value);
+  if (!pair)
+    return nullptr;
+  CSSNumericValue* x = FromSingleValue(pair->First());
+  CSSNumericValue* y = FromSingleValue(pair->Second());
   DCHECK(x);
   DCHECK(y);
 
diff --git a/third_party/blink/renderer/core/css/cssom/inline_style_property_map_test.cc b/third_party/blink/renderer/core/css/cssom/inline_style_property_map_test.cc
new file mode 100644
index 0000000..87412c2
--- /dev/null
+++ b/third_party/blink/renderer/core/css/cssom/inline_style_property_map_test.cc
@@ -0,0 +1,39 @@
+// Copyright 2019 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/cssom/inline_style_property_map.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/properties/css_property.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+#include "third_party/blink/renderer/core/style_property_shorthand.h"
+
+namespace blink {
+
+TEST(InlineStylePropertyMapTest, PendingSubstitutionValueCrash) {
+  // Test that trying to reify any longhands with a CSSPendingSubstitutionValue
+  // does not cause a crash.
+
+  Document* document = MakeGarbageCollected<Document>();
+  Element* div = document->CreateRawElement(html_names::kDivTag);
+  InlineStylePropertyMap map(div);
+
+  // For each shorthand, create a declaration with a var() reference and try
+  // reifying all longhands.
+  for (CSSPropertyID property_id : CSSPropertyIDList()) {
+    const CSSProperty& shorthand = CSSProperty::Get(property_id);
+    if (!shorthand.IsShorthand())
+      continue;
+    div->SetInlineStyleProperty(property_id, "var(--dummy)");
+    const StylePropertyShorthand& longhands = shorthandForProperty(property_id);
+    for (unsigned i = 0; i < longhands.length(); i++) {
+      map.get(document,
+              longhands.properties()[i]->GetCSSPropertyName().ToAtomicString(),
+              ASSERT_NO_EXCEPTION);
+    }
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
index 56a30af..f9c4384 100644
--- a/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
+++ b/third_party/blink/renderer/core/css/cssom/style_value_factory.cc
@@ -131,10 +131,11 @@
       return CreateStyleValue(value);
     }
     case CSSPropertyID::kGridAutoFlow: {
-      const auto& value_list = To<CSSValueList>(value);
-      // Only single keywords are supported in level 1.
-      if (value_list.length() == 1U)
-        return CreateStyleValue(value_list.Item(0));
+      if (const auto* value_list = DynamicTo<CSSValueList>(value)) {
+        // Only single keywords are supported in level 1.
+        if (value_list->length() == 1U)
+          return CreateStyleValue(value_list->Item(0));
+      }
       return nullptr;
     }
     case CSSPropertyID::kTransform:
@@ -150,10 +151,11 @@
     case CSSPropertyID::kTransformOrigin:
       return CSSPositionValue::FromCSSValue(value);
     case CSSPropertyID::kOffsetRotate: {
-      const auto& value_list = To<CSSValueList>(value);
-      // Only single keywords are supported in level 1.
-      if (value_list.length() == 1U)
-        return CreateStyleValue(value_list.Item(0));
+      if (const auto* value_list = DynamicTo<CSSValueList>(&value)) {
+        // Only single keywords are supported in level 1.
+        if (value_list->length() == 1U)
+          return CreateStyleValue(value_list->Item(0));
+      }
       return nullptr;
     }
     case CSSPropertyID::kAlignItems: {
@@ -171,10 +173,11 @@
       if (value.IsIdentifierValue())
         return CreateStyleValue(value);
 
-      const auto& value_list = To<CSSValueList>(value);
-      // Only single keywords are supported in level 1.
-      if (value_list.length() == 1U)
-        return CreateStyleValue(value_list.Item(0));
+      if (const auto* value_list = DynamicTo<CSSValueList>(&value)) {
+        // Only single keywords are supported in level 1.
+        if (value_list->length() == 1U)
+          return CreateStyleValue(value_list->Item(0));
+      }
       return nullptr;
     }
     case CSSPropertyID::kTextIndent: {
@@ -189,10 +192,11 @@
     }
     case CSSPropertyID::kTransitionProperty:
     case CSSPropertyID::kTouchAction: {
-      const auto& value_list = To<CSSValueList>(value);
-      // Only single values are supported in level 1.
-      if (value_list.length() == 1U)
-        return CreateStyleValue(value_list.Item(0));
+      if (const auto* value_list = DynamicTo<CSSValueList>(value)) {
+        // Only single values are supported in level 1.
+        if (value_list->length() == 1U)
+          return CreateStyleValue(value_list->Item(0));
+      }
       return nullptr;
     }
     case CSSPropertyID::kWillChange: {
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 20ea659..ca3b3f1 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -1136,6 +1136,7 @@
         ordered_named_auto_repeat_grid_lines_(
             is_row_axis ? style.AutoRepeatOrderedNamedGridColumnLines()
                         : style.AutoRepeatOrderedNamedGridRowLines()) {}
+  virtual ~OrderedNamedLinesCollector() = default;
 
   bool IsEmpty() const {
     return ordered_named_grid_lines_.IsEmpty() &&
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 3d2b4c0..c64cb15 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -1030,7 +1030,7 @@
     : context_(context) {}
 
 DisplayLockContext::ScopedForcedUpdate::ScopedForcedUpdate(
-    ScopedForcedUpdate&& other) noexcept
+    ScopedForcedUpdate&& other)
     : context_(other.context_) {
   other.context_ = nullptr;
 }
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 0a330de..aea4ef0 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -76,7 +76,7 @@
     DISALLOW_NEW();
 
    public:
-    ScopedForcedUpdate(ScopedForcedUpdate&&) noexcept;
+    ScopedForcedUpdate(ScopedForcedUpdate&&);
     ~ScopedForcedUpdate();
 
    private:
diff --git a/third_party/blink/renderer/core/editing/layout_selection.cc b/third_party/blink/renderer/core/editing/layout_selection.cc
index 00040d2..73cfe0f 100644
--- a/third_party/blink/renderer/core/editing/layout_selection.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection.cc
@@ -178,7 +178,7 @@
  public:
   OldSelectedNodes()
       : paint_range(MakeGarbageCollected<SelectionPaintRange>()) {}
-  OldSelectedNodes(OldSelectedNodes&& other) noexcept {
+  OldSelectedNodes(OldSelectedNodes&& other) {
     paint_range = other.paint_range;
     selected_map = std::move(other.selected_map);
   }
@@ -205,8 +205,7 @@
       HeapHashSet<Member<const Node>>&& passed_selected_objects)
       : paint_range(passed_paint_range),
         selected_objects(std::move(passed_selected_objects)) {}
-  NewPaintRangeAndSelectedNodes(
-      NewPaintRangeAndSelectedNodes&& other) noexcept {
+  NewPaintRangeAndSelectedNodes(NewPaintRangeAndSelectedNodes&& other) {
     paint_range = other.paint_range;
     selected_objects = std::move(other.selected_objects);
   }
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 26522457..e6721f5 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -663,11 +663,6 @@
       blob_url_token.PassPipe());
 }
 
-void LocalFrameClientImpl::LoadErrorPage(int reason) {
-  if (web_frame_->Client())
-    web_frame_->Client()->LoadErrorPage(reason);
-}
-
 bool LocalFrameClientImpl::NavigateBackForward(int offset) const {
   WebViewImpl* webview = web_frame_->ViewImpl();
   DCHECK(webview->Client());
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index a7e1a80..600bd620 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -142,7 +142,6 @@
   void ForwardResourceTimingToParent(const WebResourceTimingInfo&) override;
   void DownloadURL(const ResourceRequest&,
                    DownloadCrossOriginRedirects) override;
-  void LoadErrorPage(int reason) override;
   bool NavigateBackForward(int offset) const override;
   void DidAccessInitialDocument() override;
   void DidDisplayInsecureContent() override;
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 834be89..a6645451 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1584,6 +1584,18 @@
       .RecordEndOfFrameMetrics(frame_begin_time, base::TimeTicks::Now());
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+WebViewImpl::GetBeginMainFrameMetrics() {
+  if (!MainFrameImpl())
+    return nullptr;
+
+  return MainFrameImpl()
+      ->GetFrame()
+      ->View()
+      ->EnsureUkmAggregator()
+      .GetBeginMainFrameMetrics();
+}
+
 void WebViewImpl::UpdateLifecycle(WebWidget::LifecycleUpdate requested_update,
                                   WebWidget::LifecycleUpdateReason reason) {
   TRACE_EVENT0("blink", "WebViewImpl::updateAllLifecyclePhases");
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index 5be09c8..7c28228 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -69,6 +69,7 @@
 
 namespace cc {
 class Layer;
+struct BeginMainFrameMetrics;
 class ScopedDeferMainFrameUpdate;
 }
 
@@ -455,6 +456,7 @@
   void EndCommitCompositorFrame();
   void RecordStartOfFrameMetrics();
   void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time);
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics();
   void UpdateLifecycle(WebWidget::LifecycleUpdate requested_update,
                        WebWidget::LifecycleUpdateReason reason);
   void ThemeChanged();
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index 075f067..cce38835 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -192,7 +192,6 @@
 
   virtual void DownloadURL(const ResourceRequest&,
                            DownloadCrossOriginRedirects) = 0;
-  virtual void LoadErrorPage(int reason) = 0;
 
   virtual bool NavigateBackForward(int offset) const = 0;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
index f0ac3c4..b90e9d64 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.cc
@@ -7,6 +7,7 @@
 #include "base/format_macros.h"
 #include "base/rand_util.h"
 #include "base/time/default_tick_clock.h"
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -24,7 +25,7 @@
       start_time_(clock_->NowTicks()) {}
 
 LocalFrameUkmAggregator::ScopedUkmHierarchicalTimer::ScopedUkmHierarchicalTimer(
-    ScopedUkmHierarchicalTimer&& other) noexcept
+    ScopedUkmHierarchicalTimer&& other)
     : aggregator_(other.aggregator_),
       metric_index_(other.metric_index_),
       clock_(other.clock_),
@@ -124,6 +125,48 @@
   in_main_frame_update_ = true;
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+LocalFrameUkmAggregator::GetBeginMainFrameMetrics() {
+  DCHECK(InMainFrame());
+
+  // Use the main_frame_percentage_records_ because they are the ones that
+  // only count time between the Begin and End of a main frame update.
+  std::unique_ptr<cc::BeginMainFrameMetrics> metrics_data =
+      std::make_unique<cc::BeginMainFrameMetrics>();
+  metrics_data->handle_input_events =
+      main_frame_percentage_records_[static_cast<unsigned>(
+                                         MetricId::kHandleInputEvents)]
+          .interval_duration;
+  metrics_data->animate =
+      main_frame_percentage_records_[static_cast<unsigned>(MetricId::kAnimate)]
+          .interval_duration;
+  metrics_data->style_update =
+      main_frame_percentage_records_[static_cast<unsigned>(MetricId::kStyle)]
+          .interval_duration;
+  metrics_data->layout_update =
+      main_frame_percentage_records_[static_cast<unsigned>(MetricId::kLayout)]
+          .interval_duration;
+  metrics_data->prepaint =
+      main_frame_percentage_records_[static_cast<unsigned>(MetricId::kPrePaint)]
+          .interval_duration;
+  metrics_data->composite =
+      main_frame_percentage_records_[static_cast<unsigned>(
+                                         MetricId::kCompositing)]
+          .interval_duration;
+  metrics_data->paint =
+      main_frame_percentage_records_[static_cast<unsigned>(MetricId::kPaint)]
+          .interval_duration;
+  metrics_data->scrolling_coordinator =
+      main_frame_percentage_records_[static_cast<unsigned>(
+                                         MetricId::kScrollingCoordinator)]
+          .interval_duration;
+  metrics_data->composite_commit =
+      main_frame_percentage_records_[static_cast<unsigned>(
+                                         MetricId::kCompositingCommit)]
+          .interval_duration;
+  return metrics_data;
+}
+
 void LocalFrameUkmAggregator::SetTickClockForTesting(
     const base::TickClock* clock) {
   clock_ = clock;
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
index 44c3f87..e3421225 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h
@@ -14,6 +14,10 @@
 class TickClock;
 }
 
+namespace cc {
+struct BeginMainFrameMetrics;
+}
+
 namespace ukm {
 class UkmRecorder;
 }
@@ -195,7 +199,7 @@
     STACK_ALLOCATED();
 
    public:
-    ScopedUkmHierarchicalTimer(ScopedUkmHierarchicalTimer&&) noexcept;
+    ScopedUkmHierarchicalTimer(ScopedUkmHierarchicalTimer&&);
     ~ScopedUkmHierarchicalTimer();
 
    private:
@@ -238,6 +242,12 @@
 
   bool InMainFrame() { return in_main_frame_update_; }
 
+  // Populate a BeginMainFrameMetrics structure with the latency numbers for
+  // the most recent frame. Must be called when within a main frame update.
+  // That is, after calling BeginMainFrame and before calling
+  // RecordEndOfFrameMetrics.
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics();
+
   // The caller is the owner of the |clock|. The |clock| must outlive the
   // LocalFrameUkmAggregator.
   void SetTickClockForTesting(const base::TickClock* clock);
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
index a9bef6df..458fa838 100644
--- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_ukm_aggregator.h"
 
 #include "base/test/test_mock_time_task_runner.h"
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -246,4 +247,43 @@
                 expected_percentage);
 }
 
+TEST_F(LocalFrameUkmAggregatorTest, LatencyDataIsPopulated) {
+  // Although the tests use a mock clock, the UKM aggregator checks if the
+  // system has a high resolution clock before recording results. As a result,
+  // the tests will fail if the system does not have a high resolution clock.
+  if (!base::TimeTicks::IsHighResolution())
+    return;
+
+  // The initial interval is always zero, so we should see one set of metrics
+  // for the initial frame, regardless of the initial interval.
+  FramesToNextEventForTest(1);
+  unsigned millisecond_for_step = 1;
+  aggregator().BeginMainFrame();
+  for (int i = 0; i < LocalFrameUkmAggregator::kCount; ++i) {
+    auto timer =
+        aggregator().GetScopedTimer(i % LocalFrameUkmAggregator::kCount);
+    test_task_runner_->FastForwardBy(
+        base::TimeDelta::FromMilliseconds(millisecond_for_step));
+  }
+
+  // Need to populate before the end of the frame.
+  std::unique_ptr<cc::BeginMainFrameMetrics> metrics_data =
+      aggregator().GetBeginMainFrameMetrics();
+  EXPECT_EQ(metrics_data->handle_input_events.InMillisecondsF(),
+            millisecond_for_step);
+  EXPECT_EQ(metrics_data->animate.InMillisecondsF(), millisecond_for_step);
+  EXPECT_EQ(metrics_data->style_update.InMillisecondsF(), millisecond_for_step);
+  EXPECT_EQ(metrics_data->layout_update.InMillisecondsF(),
+            millisecond_for_step);
+  EXPECT_EQ(metrics_data->prepaint.InMillisecondsF(), millisecond_for_step);
+  EXPECT_EQ(metrics_data->composite.InMillisecondsF(), millisecond_for_step);
+  EXPECT_EQ(metrics_data->paint.InMillisecondsF(), millisecond_for_step);
+  EXPECT_EQ(metrics_data->scrolling_coordinator.InMillisecondsF(),
+            millisecond_for_step);
+  EXPECT_EQ(metrics_data->composite_commit.InMillisecondsF(),
+            millisecond_for_step);
+  // Do not check the value in metrics_data.update_layers because it
+  // is not set by the aggregator.
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 7d3faaac..e4f78f3 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -350,6 +350,18 @@
       .RecordEndOfFrameMetrics(frame_begin_time, base::TimeTicks::Now());
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+WebFrameWidgetImpl::GetBeginMainFrameMetrics() {
+  if (!LocalRootImpl())
+    return nullptr;
+
+  return LocalRootImpl()
+      ->GetFrame()
+      ->View()
+      ->EnsureUkmAggregator()
+      .GetBeginMainFrameMetrics();
+}
+
 void WebFrameWidgetImpl::UpdateLifecycle(LifecycleUpdate requested_update,
                                          LifecycleUpdateReason reason) {
   TRACE_EVENT0("blink", "WebFrameWidgetImpl::updateAllLifecyclePhases");
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index e111f8d7..43da8465 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -52,6 +52,7 @@
 
 namespace cc {
 class Layer;
+struct BeginMainFrameMetrics;
 }
 
 namespace blink {
@@ -89,6 +90,8 @@
   void EndCommitCompositorFrame() override;
   void RecordStartOfFrameMetrics() override;
   void RecordEndOfFrameMetrics(base::TimeTicks) override;
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics()
+      override;
   void UpdateLifecycle(LifecycleUpdate requested_update,
                        LifecycleUpdateReason reason) override;
   void ThemeChanged() override;
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index 950529614..7d1b83e 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -91,6 +91,11 @@
   web_view_->RecordEndOfFrameMetrics(frame_begin_time);
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+WebViewFrameWidget::GetBeginMainFrameMetrics() {
+  return web_view_->GetBeginMainFrameMetrics();
+}
+
 void WebViewFrameWidget::UpdateLifecycle(LifecycleUpdate requested_update,
                                          LifecycleUpdateReason reason) {
   web_view_->UpdateLifecycle(requested_update, reason);
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.h b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
index 5024fa08..4b58d5d8 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.h
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
@@ -58,6 +58,8 @@
   void EndCommitCompositorFrame() override;
   void RecordStartOfFrameMetrics() override;
   void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override;
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics()
+      override;
   void UpdateLifecycle(LifecycleUpdate requested_update,
                        LifecycleUpdateReason reason) override;
   void ThemeChanged() override;
diff --git a/third_party/blink/renderer/core/html/forms/resources/.clang-format b/third_party/blink/renderer/core/html/forms/resources/.clang-format
index ea647be..ce154cc8 100644
--- a/third_party/blink/renderer/core/html/forms/resources/.clang-format
+++ b/third_party/blink/renderer/core/html/forms/resources/.clang-format
@@ -1,7 +1,6 @@
 ---
 BasedOnStyle: Chromium
 Language: JavaScript
-ColumnLimit: 120
 CommentPragmas: .*\@.*
 AllowShortBlocksOnASingleLine: false
 AllowShortFunctionsOnASingleLine: None
diff --git a/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py b/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py
new file mode 100644
index 0000000..f284658
--- /dev/null
+++ b/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py
@@ -0,0 +1,14 @@
+# Copyright 2019 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.
+
+def _CheckChangeOnUploadOrCommit(input_api, output_api):
+  return input_api.canned_checks.CheckPatchFormatted(input_api, output_api,
+                                                     check_js=True)
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _CheckChangeOnUploadOrCommit(input_api, output_api)
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  return _CheckChangeOnUploadOrCommit(input_api, output_api)
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 7ef248a8..006f968 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
@@ -33,7 +33,15 @@
 /**
  * @enum {number}
  */
-var WeekDay = {Sunday: 0, Monday: 1, Tuesday: 2, Wednesday: 3, Thursday: 4, Friday: 5, Saturday: 6};
+var WeekDay = {
+  Sunday: 0,
+  Monday: 1,
+  Tuesday: 2,
+  Wednesday: 3,
+  Thursday: 4,
+  Friday: 5,
+  Saturday: 6
+};
 
 /**
  * @type {Object}
@@ -45,7 +53,10 @@
     weekStartDay: WeekDay.Sunday,
     dayLabels: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
     ampmLabels: ['AM', 'PM'],
-    shortMonthLabels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
+    shortMonthLabels: [
+      'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct',
+      'Nov', 'Dec'
+    ],
     isLocaleRTL: false,
     isFormControlsRefreshEnabled: false,
     mode: 'date',
@@ -109,14 +120,15 @@
   if (year <= 1867 || year == 1868 && month <= 9)
     return '';
   if (!japaneseEraFormatter) {
-    japaneseEraFormatter = new Intl.DateTimeFormat('ja-JP-u-ca-japanese',
-                                                   {era: 'long'});
+    japaneseEraFormatter =
+        new Intl.DateTimeFormat('ja-JP-u-ca-japanese', {era: 'long'});
   }
   // Produce the era for day 16 because it's almost the midpoint of a month.
   // 275760-09-13 is the last valid date in ECMAScript. We apply day 7 in that
   // case because it's the midpoint between 09-01 and 09-13.
   let sampleDay = year == 275760 && month == 8 ? 7 : 16;
-  let sampleDayString = japaneseEraFormatter.format(new Date(year, month, sampleDay));
+  let sampleDayString =
+      japaneseEraFormatter.format(new Date(year, month, sampleDay));
   let nenIndex = sampleDayString.indexOf('\u5e74');
   if (nenIndex == -1)
     return '';
@@ -271,7 +283,8 @@
  * @return {!boolean}
  */
 Day.prototype.equals = function(other) {
-  return other instanceof Day && this.year === other.year && this.month === other.month && this.date === other.date;
+  return other instanceof Day && this.year === other.year &&
+      this.month === other.month && this.date === other.date;
 };
 
 /**
@@ -350,7 +363,8 @@
   var yearString = String(this.year);
   if (yearString.length < 4)
     yearString = ('000' + yearString).substr(-4, 4);
-  return yearString + '-' + ('0' + (this.month + 1)).substr(-2, 2) + '-' + ('0' + this.date).substr(-2, 2);
+  return yearString + '-' + ('0' + (this.month + 1)).substr(-2, 2) + '-' +
+      ('0' + this.date).substr(-2, 2);
 };
 
 /**
@@ -358,8 +372,13 @@
  */
 Day.prototype.format = function() {
   if (!Day.formatter) {
-    Day.formatter = new Intl.DateTimeFormat(
-        getLocale(), {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC'});
+    Day.formatter = new Intl.DateTimeFormat(getLocale(), {
+      weekday: 'long',
+      year: 'numeric',
+      month: 'long',
+      day: 'numeric',
+      timeZone: 'UTC'
+    });
   }
   return Day.formatter.format(this.startDate());
 };
@@ -390,7 +409,8 @@
    */
   this.week = week;
   // Number of years per year is either 52 or 53.
-  if (this.week < 1 || (this.week > 52 && this.week > Week.numberOfWeeksInYear(this.year))) {
+  if (this.week < 1 ||
+      (this.week > 52 && this.week > Week.numberOfWeeksInYear(this.year))) {
     var normalizedWeek = Week.createFromDay(this.firstDay());
     this.year = normalizedWeek.year;
     this.week = normalizedWeek.week;
@@ -438,11 +458,14 @@
   if (isNaN(date.valueOf()))
     throw 'Invalid date';
   var year = date.getUTCFullYear();
-  if (year <= Week.Maximum.year && Week.weekOneStartDateForYear(year + 1).getTime() <= date.getTime())
+  if (year <= Week.Maximum.year &&
+      Week.weekOneStartDateForYear(year + 1).getTime() <= date.getTime())
     year++;
-  else if (year > 1 && Week.weekOneStartDateForYear(year).getTime() > date.getTime())
+  else if (
+      year > 1 && Week.weekOneStartDateForYear(year).getTime() > date.getTime())
     year--;
-  var week = 1 + Week._numberOfWeeksSinceDate(Week.weekOneStartDateForYear(year), date);
+  var week = 1 +
+      Week._numberOfWeeksSinceDate(Week.weekOneStartDateForYear(year), date);
   return new Week(year, week);
 };
 
@@ -456,7 +479,10 @@
     year++;
   else if (year > 1 && Week.weekOneStartDayForYear(year) > day)
     year--;
-  var week = Math.floor(1 + (day.valueOf() - Week.weekOneStartDayForYear(year).valueOf()) / MillisecondsPerWeek);
+  var week = Math.floor(
+      1 +
+      (day.valueOf() - Week.weekOneStartDayForYear(year).valueOf()) /
+          MillisecondsPerWeek);
   return new Week(year, week);
 };
 
@@ -465,7 +491,8 @@
  */
 Week.createFromToday = function() {
   var now = new Date();
-  return Week.createFromDate(createUTCDate(now.getFullYear(), now.getMonth(), now.getDate()));
+  return Week.createFromDate(
+      createUTCDate(now.getFullYear(), now.getMonth(), now.getDate()));
 };
 
 /**
@@ -501,7 +528,9 @@
     return 0;
   else if (year === Week.Maximum.year)
     return Week.Maximum.week;
-  return Week._numberOfWeeksSinceDate(Week.weekOneStartDateForYear(year), Week.weekOneStartDateForYear(year + 1));
+  return Week._numberOfWeeksSinceDate(
+      Week.weekOneStartDateForYear(year),
+      Week.weekOneStartDateForYear(year + 1));
 };
 
 /**
@@ -510,7 +539,8 @@
  * @return {!number}
  */
 Week._numberOfWeeksSinceDate = function(baseDate, date) {
-  return Math.floor((date.getTime() - baseDate.getTime()) / MillisecondsPerWeek);
+  return Math.floor(
+      (date.getTime() - baseDate.getTime()) / MillisecondsPerWeek);
 };
 
 /**
@@ -518,7 +548,8 @@
  * @return {!boolean}
  */
 Week.prototype.equals = function(other) {
-  return other instanceof Week && this.year === other.year && this.week === other.week;
+  return other instanceof Week && this.year === other.year &&
+      this.week === other.week;
 };
 
 /**
@@ -616,7 +647,9 @@
    * @type {number}
    * @const
    */
-  this.month = month % MonthsPerYear < 0 ? month % MonthsPerYear + MonthsPerYear : month % MonthsPerYear;
+  this.month = month % MonthsPerYear < 0 ?
+      month % MonthsPerYear + MonthsPerYear :
+      month % MonthsPerYear;
 };
 
 Month.ISOStringRegExp = /^(\d+)-(\d+)$/;
@@ -688,7 +721,8 @@
  * @return {!boolean}
  */
 Month.prototype.equals = function(other) {
-  return other instanceof Month && this.year === other.year && this.month === other.month;
+  return other instanceof Month && this.year === other.year &&
+      this.month === other.month;
 };
 
 /**
@@ -772,8 +806,9 @@
  */
 Month.prototype.toLocaleString = function() {
   if (global.params.locale === 'ja')
-    return '' + this.year + '\u5e74' + formatJapaneseImperialEra(this.year, this.month) + ' ' + (this.month + 1) +
-        '\u6708';
+    return '' + this.year + '\u5e74' +
+        formatJapaneseImperialEra(this.year, this.month) + ' ' +
+        (this.month + 1) + '\u6708';
   return window.pagePopupController.formatMonth(this.year, this.month);
 };
 
@@ -932,7 +967,8 @@
  * @return {!boolean}
  */
 AnimationManager.prototype._needsTimer = function() {
-  return this._runningAnimatorCount > 0 || this.hasListener(AnimationManager.EventTypeAnimationFrameWillFinish);
+  return this._runningAnimatorCount > 0 ||
+      this.hasListener(AnimationManager.EventTypeAnimationFrameWillFinish);
 };
 
 /**
@@ -1093,7 +1129,8 @@
   this.progress += (now - this._lastStepTime) / this.duration;
   this.progress = Math.min(1.0, this.progress);
   this._lastStepTime = now;
-  this.currentValue = this.timingFunction(this.progress) * this._delta + this._from;
+  this.currentValue =
+      this.timingFunction(this.progress) * this._delta + this._from;
   this.step(this);
   if (this.progress === 1.0) {
     this.stop();
@@ -1155,15 +1192,16 @@
  * @param {!number} t
  */
 FlingGestureAnimator.prototype._valueAtTime = function(t) {
-  return FlingGestureAnimator._P0 * Math.exp(-FlingGestureAnimator._P2 * t) - FlingGestureAnimator._P1 * t -
-      FlingGestureAnimator._P0;
+  return FlingGestureAnimator._P0 * Math.exp(-FlingGestureAnimator._P2 * t) -
+      FlingGestureAnimator._P1 * t - FlingGestureAnimator._P0;
 };
 
 /**
  * @param {!number} t
  */
 FlingGestureAnimator.prototype._velocityAtTime = function(t) {
-  return -FlingGestureAnimator._P0 * FlingGestureAnimator._P2 * Math.exp(-FlingGestureAnimator._P2 * t) -
+  return -FlingGestureAnimator._P0 * FlingGestureAnimator._P2 *
+      Math.exp(-FlingGestureAnimator._P2 * t) -
       FlingGestureAnimator._P1;
 };
 
@@ -1171,7 +1209,9 @@
  * @param {!number} v
  */
 FlingGestureAnimator.prototype._timeAtVelocity = function(v) {
-  return -Math.log((v + FlingGestureAnimator._P1) / (-FlingGestureAnimator._P0 * FlingGestureAnimator._P2)) /
+  return -Math.log(
+             (v + FlingGestureAnimator._P1) /
+             (-FlingGestureAnimator._P0 * FlingGestureAnimator._P2)) /
       FlingGestureAnimator._P2;
 };
 
@@ -1190,7 +1230,8 @@
     this.stop();
     return;
   }
-  var position = this._valueAtTime(this._elapsedTime + this._timeOffset) - this._positionOffset;
+  var position = this._valueAtTime(this._elapsedTime + this._timeOffset) -
+      this._positionOffset;
   if (this.initialVelocity < 0)
     position = -position;
   this.currentValue = position + this.initialValue;
@@ -1271,7 +1312,8 @@
    * @type {Element}
    * @const
    */
-  this.contentElement = createElement('div', ScrollView.ClassNameScrollViewContent);
+  this.contentElement =
+      createElement('div', ScrollView.ClassNameScrollViewContent);
   this.element.appendChild(this.contentElement);
   /**
    * @type {number}
@@ -1370,7 +1412,8 @@
  */
 ScrollView.prototype.onWindowTouchEnd = function(event) {
   if (Math.abs(this._lastTouchVelocity) > 0.01) {
-    this._scrollAnimator = new FlingGestureAnimator(this._lastTouchVelocity, this._contentOffset);
+    this._scrollAnimator =
+        new FlingGestureAnimator(this._lastTouchVelocity, this._contentOffset);
     this._scrollAnimator.step = this.onFlingGestureAnimatorStep;
     this._scrollAnimator.start();
   }
@@ -1487,7 +1530,9 @@
  */
 ScrollView.prototype.setContentOffset = function(value) {
   console.assert(isFinite(value));
-  value = Math.min(this.maximumContentOffset - this._height, Math.max(this.minimumContentOffset, Math.floor(value)));
+  value = Math.min(
+      this.maximumContentOffset - this._height,
+      Math.max(this.minimumContentOffset, Math.floor(value)));
   if (this._contentOffset === value)
     return;
   this._contentOffset = value;
@@ -1497,11 +1542,12 @@
 };
 
 ScrollView.prototype._updateScrollContent = function() {
-  var newPartitionNumber = Math.floor(this._contentOffset / ScrollView.PartitionHeight);
+  var newPartitionNumber =
+      Math.floor(this._contentOffset / ScrollView.PartitionHeight);
   var partitionChanged = this._partitionNumber !== newPartitionNumber;
   this._partitionNumber = newPartitionNumber;
-  this.contentElement.style.webkitTransform =
-      'translate(0, ' + (-this.contentPositionForContentOffset(this._contentOffset)) + 'px)';
+  this.contentElement.style.webkitTransform = 'translate(0, ' +
+      (-this.contentPositionForContentOffset(this._contentOffset)) + 'px)';
   if (this.delegate && partitionChanged)
     this.delegate.scrollViewDidChangePartition(this);
 };
@@ -1554,14 +1600,17 @@
  * @return {!Array} An array to keep thrown away cells.
  */
 ListCell.prototype._recycleBin = function() {
-  console.assert(false, 'NOT REACHED: ListCell.prototype._recycleBin needs to be overridden.');
+  console.assert(
+      false,
+      'NOT REACHED: ListCell.prototype._recycleBin needs to be overridden.');
   return [];
 };
 
 ListCell.prototype.throwAway = function() {
   this.hide();
-  var limit = typeof this.constructor.RecycleBinLimit === 'undefined' ? ListCell.DefaultRecycleBinLimit :
-                                                                        this.constructor.RecycleBinLimit;
+  var limit = typeof this.constructor.RecycleBinLimit === 'undefined' ?
+      ListCell.DefaultRecycleBinLimit :
+      this.constructor.RecycleBinLimit;
   var recycleBin = this._recycleBin();
   if (recycleBin.length < limit)
     recycleBin.push(this);
@@ -1684,10 +1733,13 @@
     return;
   this._needsUpdateCells = needsUpdateCells;
   if (this._needsUpdateCells)
-    AnimationManager.shared.on(AnimationManager.EventTypeAnimationFrameWillFinish, this.onAnimationFrameWillFinish);
+    AnimationManager.shared.on(
+        AnimationManager.EventTypeAnimationFrameWillFinish,
+        this.onAnimationFrameWillFinish);
   else
     AnimationManager.shared.removeListener(
-        AnimationManager.EventTypeAnimationFrameWillFinish, this.onAnimationFrameWillFinish);
+        AnimationManager.EventTypeAnimationFrameWillFinish,
+        this.onAnimationFrameWillFinish);
 };
 
 /**
@@ -1703,7 +1755,9 @@
  * @return {!number}
  */
 ListView.prototype.rowAtScrollOffset = function(offset) {
-  console.assert(false, 'NOT REACHED: ListView.prototype.rowAtScrollOffset needs to be overridden.');
+  console.assert(
+      false,
+      'NOT REACHED: ListView.prototype.rowAtScrollOffset needs to be overridden.');
   return 0;
 };
 
@@ -1712,7 +1766,9 @@
  * @return {!number} Scroll offset in pixels.
  */
 ListView.prototype.scrollOffsetForRow = function(row) {
-  console.assert(false, 'NOT REACHED: ListView.prototype.scrollOffsetForRow needs to be overridden.');
+  console.assert(
+      false,
+      'NOT REACHED: ListView.prototype.scrollOffsetForRow needs to be overridden.');
   return 0;
 };
 
@@ -1727,7 +1783,8 @@
   cell = this.prepareNewCell(row);
   cell.attachTo(this.scrollView.contentElement);
   cell.setWidth(this._width);
-  cell.setPosition(this.scrollView.contentPositionForContentOffset(this.scrollOffsetForRow(row)));
+  cell.setPosition(this.scrollView.contentPositionForContentOffset(
+      this.scrollOffsetForRow(row)));
   this._cells[row] = cell;
   return cell;
 };
@@ -1737,7 +1794,9 @@
  * @return {!ListCell}
  */
 ListView.prototype.prepareNewCell = function(row) {
-  console.assert(false, 'NOT REACHED: ListView.prototype.prepareNewCell should be overridden.');
+  console.assert(
+      false,
+      'NOT REACHED: ListView.prototype.prepareNewCell should be overridden.');
   return new ListCell();
 };
 
@@ -1760,7 +1819,8 @@
  * @return {!number}
  */
 ListView.prototype.lastVisibleRow = function() {
-  return this.rowAtScrollOffset(this.scrollView.contentOffset() + this.scrollView.height() - 1);
+  return this.rowAtScrollOffset(
+      this.scrollView.contentOffset() + this.scrollView.height() - 1);
 };
 
 /**
@@ -1796,7 +1856,8 @@
   for (var i = firstVisibleRow; i <= lastVisibleRow; ++i) {
     var cell = this._cells[i];
     if (cell)
-      cell.setPosition(this.scrollView.contentPositionForContentOffset(this.scrollOffsetForRow(cell.row)));
+      cell.setPosition(this.scrollView.contentPositionForContentOffset(
+          this.scrollOffsetForRow(cell.row)));
     else
       this.addCellIfNecessary(i);
   }
@@ -1843,7 +1904,8 @@
  * @param {?Event} event
  */
 ListView.prototype.onClick = function(event) {
-  var clickedCellElement = enclosingNodeOrSelfWithClass(event.target, ListCell.ClassNameListCell);
+  var clickedCellElement =
+      enclosingNodeOrSelfWithClass(event.target, ListCell.ClassNameListCell);
   if (!clickedCellElement)
     return;
   var clickedCell = clickedCellElement.$view;
@@ -1889,13 +1951,15 @@
  * @param {!ScrollView} scrollView
  */
 function ScrubbyScrollBar(scrollView) {
-  View.call(this, createElement('div', ScrubbyScrollBar.ClassNameScrubbyScrollBar));
+  View.call(
+      this, createElement('div', ScrubbyScrollBar.ClassNameScrubbyScrollBar));
 
   /**
    * @type {!Element}
    * @const
    */
-  this.thumb = createElement('div', ScrubbyScrollBar.ClassNameScrubbyScrollThumb);
+  this.thumb =
+      createElement('div', ScrubbyScrollBar.ClassNameScrubbyScrollThumb);
   this.element.appendChild(this.thumb);
 
   /**
@@ -1955,7 +2019,8 @@
   this._setThumbPositionFromEventPosition(touch.clientY);
   if (this._thumbStyleTopAnimator)
     this._thumbStyleTopAnimator.stop();
-  this._timer = setInterval(this.onScrollTimer, ScrubbyScrollBar.ScrollInterval);
+  this._timer =
+      setInterval(this.onScrollTimer, ScrubbyScrollBar.ScrollInterval);
   window.addEventListener('touchmove', this.onWindowTouchMove, false);
   window.addEventListener('touchend', this.onWindowTouchEnd, false);
   event.stopPropagation();
@@ -1980,7 +2045,8 @@
   this._thumbStyleTopAnimator.step = this.onThumbStyleTopAnimationStep;
   this._thumbStyleTopAnimator.setFrom(this.thumb.offsetTop);
   this._thumbStyleTopAnimator.setTo((this._height - this._thumbHeight) / 2);
-  this._thumbStyleTopAnimator.timingFunction = AnimationTimingFunction.EaseInOut;
+  this._thumbStyleTopAnimator.timingFunction =
+      AnimationTimingFunction.EaseInOut;
   this._thumbStyleTopAnimator.duration = 100;
   this._thumbStyleTopAnimator.start();
 
@@ -2023,15 +2089,19 @@
 /**
  * @param {number} position
  */
-ScrubbyScrollBar.prototype._setThumbPositionFromEventPosition = function(position) {
+ScrubbyScrollBar.prototype._setThumbPositionFromEventPosition = function(
+    position) {
   var thumbMin = ScrubbyScrollBar.ThumbMargin;
-  var thumbMax = this._height - this._thumbHeight - ScrubbyScrollBar.ThumbMargin * 2;
-  var y = position - this.element.getBoundingClientRect().top - this.element.clientTop + this.element.scrollTop;
+  var thumbMax =
+      this._height - this._thumbHeight - ScrubbyScrollBar.ThumbMargin * 2;
+  var y = position - this.element.getBoundingClientRect().top -
+      this.element.clientTop + this.element.scrollTop;
   var thumbPosition = y - this._thumbHeight / 2;
   thumbPosition = Math.max(thumbPosition, thumbMin);
   thumbPosition = Math.min(thumbPosition, thumbMax);
   this.thumb.style.top = thumbPosition + 'px';
-  this._thumbPosition = 1.0 - (thumbPosition - thumbMin) / (thumbMax - thumbMin) * 2;
+  this._thumbPosition =
+      1.0 - (thumbPosition - thumbMin) / (thumbMax - thumbMin) * 2;
 };
 
 /**
@@ -2044,7 +2114,8 @@
   window.addEventListener('mouseup', this.onWindowMouseUp, false);
   if (this._thumbStyleTopAnimator)
     this._thumbStyleTopAnimator.stop();
-  this._timer = setInterval(this.onScrollTimer, ScrubbyScrollBar.ScrollInterval);
+  this._timer =
+      setInterval(this.onScrollTimer, ScrubbyScrollBar.ScrollInterval);
   event.stopPropagation();
   event.preventDefault();
 };
@@ -2064,7 +2135,8 @@
   this._thumbStyleTopAnimator.step = this.onThumbStyleTopAnimationStep;
   this._thumbStyleTopAnimator.setFrom(this.thumb.offsetTop);
   this._thumbStyleTopAnimator.setTo((this._height - this._thumbHeight) / 2);
-  this._thumbStyleTopAnimator.timingFunction = AnimationTimingFunction.EaseInOut;
+  this._thumbStyleTopAnimator.timingFunction =
+      AnimationTimingFunction.EaseInOut;
   this._thumbStyleTopAnimator.duration = 100;
   this._thumbStyleTopAnimator.start();
 
@@ -2103,21 +2175,26 @@
    */
   this.label = createElement('div', YearListCell.ClassNameLabel, '----');
   this.element.appendChild(this.label);
-  this.label.style.height = (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
-  this.label.style.lineHeight = (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
+  this.label.style.height =
+      (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
+  this.label.style.lineHeight =
+      (YearListCell.GetHeight() - YearListCell.BorderBottomWidth) + 'px';
 
   /**
    * @type {!Array} Array of the 12 month button elements.
    * @const
    */
   this.monthButtons = [];
-  var monthChooserElement = createElement('div', YearListCell.ClassNameMonthChooser);
+  var monthChooserElement =
+      createElement('div', YearListCell.ClassNameMonthChooser);
   for (var r = 0; r < YearListCell.ButtonRows; ++r) {
-    var buttonsRow = createElement('div', YearListCell.ClassNameMonthButtonsRow);
+    var buttonsRow =
+        createElement('div', YearListCell.ClassNameMonthButtonsRow);
     buttonsRow.setAttribute('role', 'row');
     for (var c = 0; c < YearListCell.ButtonColumns; ++c) {
       var month = c + r * YearListCell.ButtonColumns;
-      var button = createElement('div', YearListCell.ClassNameMonthButton, shortMonthLabels[month]);
+      var button = createElement(
+          'div', YearListCell.ClassNameMonthButton, shortMonthLabels[month]);
       button.setAttribute('role', 'gridcell');
       button.dataset.month = month;
       buttonsRow.appendChild(button);
@@ -2148,7 +2225,7 @@
     return YearListCell._HeightRefresh;
   }
   return YearListCell._Height;
-}
+};
 YearListCell.BorderBottomWidth = 1;
 YearListCell.ButtonRows = 3;
 YearListCell.ButtonColumns = 4;
@@ -2159,7 +2236,7 @@
     return YearListCell._SelectedHeightRefresh;
   }
   return YearListCell._SelectedHeight;
-}
+};
 YearListCell.ClassNameYearListCell = 'year-list-cell';
 YearListCell.ClassNameLabel = 'label';
 YearListCell.ClassNameMonthChooser = 'month-chooser';
@@ -2241,9 +2318,11 @@
    */
   this._maximumMonth = maximumMonth;
 
-  this.scrollView.minimumContentOffset = (this._minimumMonth.year - 1) * YearListCell.GetHeight();
+  this.scrollView.minimumContentOffset =
+      (this._minimumMonth.year - 1) * YearListCell.GetHeight();
   this.scrollView.maximumContentOffset =
-      (this._maximumMonth.year - 1) * YearListCell.GetHeight() + YearListCell.GetSelectedHeight();
+      (this._maximumMonth.year - 1) * YearListCell.GetHeight() +
+      YearListCell.GetSelectedHeight();
 
   /**
    * @type {!Object}
@@ -2280,13 +2359,14 @@
 
 YearListView._Height = YearListCell._SelectedHeight - 1;
 YearListView._VisibleYearsRefresh = 4;
-YearListView._HeightRefresh = YearListCell._SelectedHeightRefresh - 1 + YearListView._VisibleYearsRefresh * YearListCell._HeightRefresh;
+YearListView._HeightRefresh = YearListCell._SelectedHeightRefresh - 1 +
+    YearListView._VisibleYearsRefresh * YearListCell._HeightRefresh;
 YearListView.GetHeight = function() {
   if (global.params.isFormControlsRefreshEnabled) {
     return YearListView._HeightRefresh;
   }
   return YearListView._Height;
-}
+};
 YearListView.EventTypeYearListViewDidHide = 'yearListViewDidHide';
 YearListView.EventTypeYearListViewDidSelectMonth = 'yearListViewDidSelectMonth';
 
@@ -2295,24 +2375,30 @@
  */
 YearListView.prototype.onTouchStart = function(event) {
   var touch = event.touches[0];
-  var monthButtonElement = enclosingNodeOrSelfWithClass(touch.target, YearListCell.ClassNameMonthButton);
+  var monthButtonElement = enclosingNodeOrSelfWithClass(
+      touch.target, YearListCell.ClassNameMonthButton);
   if (!monthButtonElement)
     return;
-  var cellElement = enclosingNodeOrSelfWithClass(monthButtonElement, YearListCell.ClassNameYearListCell);
+  var cellElement = enclosingNodeOrSelfWithClass(
+      monthButtonElement, YearListCell.ClassNameYearListCell);
   var cell = cellElement.$view;
-  this.highlightMonth(new Month(cell.row + 1, parseInt(monthButtonElement.dataset.month, 10)));
+  this.highlightMonth(
+      new Month(cell.row + 1, parseInt(monthButtonElement.dataset.month, 10)));
 };
 
 /**
  * @param {?Event} event
  */
 YearListView.prototype.onMouseOver = function(event) {
-  var monthButtonElement = enclosingNodeOrSelfWithClass(event.target, YearListCell.ClassNameMonthButton);
+  var monthButtonElement = enclosingNodeOrSelfWithClass(
+      event.target, YearListCell.ClassNameMonthButton);
   if (!monthButtonElement)
     return;
-  var cellElement = enclosingNodeOrSelfWithClass(monthButtonElement, YearListCell.ClassNameYearListCell);
+  var cellElement = enclosingNodeOrSelfWithClass(
+      monthButtonElement, YearListCell.ClassNameYearListCell);
   var cell = cellElement.$view;
-  this.highlightMonth(new Month(cell.row + 1, parseInt(monthButtonElement.dataset.month, 10)));
+  this.highlightMonth(
+      new Month(cell.row + 1, parseInt(monthButtonElement.dataset.month, 10)));
   this._ignoreMouseOutUntillNextMouseOver = false;
 };
 
@@ -2322,7 +2408,8 @@
 YearListView.prototype.onMouseOut = function(event) {
   if (this._ignoreMouseOutUntillNextMouseOver)
     return;
-  var monthButtonElement = enclosingNodeOrSelfWithClass(event.target, YearListCell.ClassNameMonthButton);
+  var monthButtonElement = enclosingNodeOrSelfWithClass(
+      event.target, YearListCell.ClassNameMonthButton);
   if (!monthButtonElement) {
     this.dehighlightMonth();
   }
@@ -2333,7 +2420,8 @@
  * @override
  */
 YearListView.prototype.setWidth = function(width) {
-  ListView.prototype.setWidth.call(this, width - this.scrubbyScrollBar.element.offsetWidth);
+  ListView.prototype.setWidth.call(
+      this, width - this.scrubbyScrollBar.element.offsetWidth);
   this.element.style.width = width + 'px';
 };
 
@@ -2359,8 +2447,9 @@
  * @param {!YearListView.RowAnimationDirection} direction
  */
 YearListView.prototype._animateRow = function(row, direction) {
-  var fromValue =
-      direction === YearListView.RowAnimationDirection.Closing ? YearListCell.GetSelectedHeight() : YearListCell.GetHeight();
+  var fromValue = direction === YearListView.RowAnimationDirection.Closing ?
+      YearListCell.GetSelectedHeight() :
+      YearListCell.GetHeight();
   var oldAnimator = this._runningAnimators[row];
   if (oldAnimator) {
     oldAnimator.stop();
@@ -2371,11 +2460,14 @@
   animator.step = this.onCellHeightAnimatorStep;
   animator.setFrom(fromValue);
   animator.setTo(
-      direction === YearListView.RowAnimationDirection.Opening ? YearListCell.GetSelectedHeight() : YearListCell.GetHeight());
+      direction === YearListView.RowAnimationDirection.Opening ?
+          YearListCell.GetSelectedHeight() :
+          YearListCell.GetHeight());
   animator.timingFunction = AnimationTimingFunction.EaseInOut;
   animator.duration = 300;
   animator.row = row;
-  animator.on(Animator.EventTypeDidAnimationStop, this.onCellHeightAnimatorDidStop);
+  animator.on(
+      Animator.EventTypeDidAnimationStop, this.onCellHeightAnimatorDidStop);
   this._runningAnimators[row] = animator;
   this._animatingRows.push(row);
   this._animatingRows.sort();
@@ -2410,14 +2502,19 @@
   var year = this.selectedRow + 1;
   if (this.selectedRow !== oldSelectedRow) {
     var month = this.highlightedMonth ? this.highlightedMonth.month : 0;
-    this.dispatchEvent(YearListView.EventTypeYearListViewDidSelectMonth, this, new Month(year, month));
+    this.dispatchEvent(
+        YearListView.EventTypeYearListViewDidSelectMonth, this,
+        new Month(year, month));
     this.scrollView.scrollTo(this.selectedRow * YearListCell.GetHeight(), true);
   } else {
-    var monthButton = enclosingNodeOrSelfWithClass(event.target, YearListCell.ClassNameMonthButton);
+    var monthButton = enclosingNodeOrSelfWithClass(
+        event.target, YearListCell.ClassNameMonthButton);
     if (!monthButton || monthButton.getAttribute('aria-disabled') == 'true')
       return;
     var month = parseInt(monthButton.dataset.month, 10);
-    this.dispatchEvent(YearListView.EventTypeYearListViewDidSelectMonth, this, new Month(year, month));
+    this.dispatchEvent(
+        YearListView.EventTypeYearListViewDidSelectMonth, this,
+        new Month(year, month));
     this.hide();
   }
 };
@@ -2438,9 +2535,12 @@
   for (var i = 0; i < rowsWithIrregularHeight.length; ++i) {
     var row = rowsWithIrregularHeight[i];
     var animator = this._runningAnimators[row];
-    var rowHeight = animator ? animator.currentValue : YearListCell.GetSelectedHeight();
-    if (remainingOffset <= (row - lastAnimatingRow) * YearListCell.GetHeight()) {
-      return lastAnimatingRow + Math.floor(remainingOffset / YearListCell.GetHeight());
+    var rowHeight =
+        animator ? animator.currentValue : YearListCell.GetSelectedHeight();
+    if (remainingOffset <=
+        (row - lastAnimatingRow) * YearListCell.GetHeight()) {
+      return lastAnimatingRow +
+          Math.floor(remainingOffset / YearListCell.GetHeight());
     }
     remainingOffset -= (row - lastAnimatingRow) * YearListCell.GetHeight();
     if (remainingOffset <= (rowHeight - YearListCell.GetHeight()))
@@ -2448,7 +2548,8 @@
     remainingOffset -= rowHeight - YearListCell.GetHeight();
     lastAnimatingRow = row;
   }
-  return lastAnimatingRow + Math.floor(remainingOffset / YearListCell.GetHeight());
+  return lastAnimatingRow +
+      Math.floor(remainingOffset / YearListCell.GetHeight());
 };
 
 /**
@@ -2465,7 +2566,8 @@
     var animator = this._runningAnimators[animatingRow];
     scrollOffset += animator.currentValue - YearListCell.GetHeight();
   }
-  if (this.selectedRow > -1 && this.selectedRow < row && !this._runningAnimators[this.selectedRow]) {
+  if (this.selectedRow > -1 && this.selectedRow < row &&
+      !this._runningAnimators[this.selectedRow]) {
     scrollOffset += YearListCell.GetSelectedHeight() - YearListCell.GetHeight();
   }
   return scrollOffset;
@@ -2477,14 +2579,17 @@
  * @override
  */
 YearListView.prototype.prepareNewCell = function(row) {
-  var cell = YearListCell._recycleBin.pop() || new YearListCell(global.params.shortMonthLabels);
+  var cell = YearListCell._recycleBin.pop() ||
+      new YearListCell(global.params.shortMonthLabels);
   cell.reset(row);
   cell.setSelected(this.selectedRow === row);
   for (var i = 0; i < cell.monthButtons.length; ++i) {
     var month = new Month(row + 1, i);
     cell.monthButtons[i].id = month.toString();
     cell.monthButtons[i].setAttribute(
-        'aria-disabled', this._minimumMonth > month || this._maximumMonth < month ? 'true' : 'false');
+        'aria-disabled',
+        this._minimumMonth > month || this._maximumMonth < month ? 'true' :
+                                                                   'false');
     cell.monthButtons[i].setAttribute('aria-label', month.toLocaleString());
   }
   if (this.highlightedMonth && row === this.highlightedMonth.year - 1) {
@@ -2532,7 +2637,8 @@
   for (var i = firstVisibleRow; i <= lastVisibleRow; ++i) {
     var cell = this._cells[i];
     if (cell)
-      cell.setPosition(this.scrollView.contentPositionForContentOffset(this.scrollOffsetForRow(cell.row)));
+      cell.setPosition(this.scrollView.contentPositionForContentOffset(
+          this.scrollOffsetForRow(cell.row)));
     else
       this.addCellIfNecessary(i);
   }
@@ -2548,7 +2654,8 @@
   var selectedCell = this._cells[this.selectedRow];
   if (selectedCell)
     selectedCell.setSelected(false);
-  this._animateRow(this.selectedRow, YearListView.RowAnimationDirection.Closing);
+  this._animateRow(
+      this.selectedRow, YearListView.RowAnimationDirection.Closing);
   this.selectedRow = ListView.NoSelection;
   this.setNeedsUpdateCells(true);
 };
@@ -2578,7 +2685,8 @@
   this.selectedRow = row;
   if (this.selectedRow !== ListView.NoSelection) {
     var selectedCell = this._cells[this.selectedRow];
-    this._animateRow(this.selectedRow, YearListView.RowAnimationDirection.Opening);
+    this._animateRow(
+        this.selectedRow, YearListView.RowAnimationDirection.Opening);
     if (selectedCell)
       selectedCell.setSelected(true);
     var month = this.highlightedMonth ? this.highlightedMonth.month : 0;
@@ -2653,14 +2761,14 @@
 
 YearListView.prototype.setSelectedMonth = function(month) {
   this._selectedMonth = month;
-}
+};
 
 YearListView.prototype.showSelectedMonth = function() {
   var monthButton = this.buttonForMonth(this._selectedMonth);
   if (monthButton) {
     monthButton.classList.add(YearListCell.ClassNameSelected);
   }
-}
+};
 
 /**
  * @param {!Month} month
@@ -2685,7 +2793,8 @@
   this.highlightMonth(month);
   this.select(this.highlightedMonth.year - 1);
 
-  this.dispatchEvent(YearListView.EventTypeYearListViewDidSelectMonth, this, month);
+  this.dispatchEvent(
+      YearListView.EventTypeYearListViewDidSelectMonth, this, month);
   this.scrollView.scrollTo(this.selectedRow * YearListCell.GetHeight(), true);
   return true;
 };
@@ -2702,17 +2811,24 @@
     if (global.params.isLocaleRTL ? key == 'ArrowRight' : key == 'ArrowLeft')
       eventHandled = this._moveHighlightTo(this.highlightedMonth.previous());
     else if (key == 'ArrowUp')
-      eventHandled = this._moveHighlightTo(this.highlightedMonth.previous(YearListCell.ButtonColumns));
-    else if (global.params.isLocaleRTL ? key == 'ArrowLeft' : key == 'ArrowRight')
+      eventHandled = this._moveHighlightTo(
+          this.highlightedMonth.previous(YearListCell.ButtonColumns));
+    else if (
+        global.params.isLocaleRTL ? key == 'ArrowLeft' : key == 'ArrowRight')
       eventHandled = this._moveHighlightTo(this.highlightedMonth.next());
     else if (key == 'ArrowDown')
-      eventHandled = this._moveHighlightTo(this.highlightedMonth.next(YearListCell.ButtonColumns));
+      eventHandled = this._moveHighlightTo(
+          this.highlightedMonth.next(YearListCell.ButtonColumns));
     else if (key == 'PageUp')
-      eventHandled = this._moveHighlightTo(this.highlightedMonth.previous(MonthsPerYear));
+      eventHandled =
+          this._moveHighlightTo(this.highlightedMonth.previous(MonthsPerYear));
     else if (key == 'PageDown')
-      eventHandled = this._moveHighlightTo(this.highlightedMonth.next(MonthsPerYear));
+      eventHandled =
+          this._moveHighlightTo(this.highlightedMonth.next(MonthsPerYear));
     else if (key == 'Enter') {
-      this.dispatchEvent(YearListView.EventTypeYearListViewDidSelectMonth, this, this.highlightedMonth);
+      this.dispatchEvent(
+          YearListView.EventTypeYearListViewDidSelectMonth, this,
+          this.highlightedMonth);
       this.hide();
       eventHandled = true;
     }
@@ -2801,21 +2917,25 @@
  * @param {!number} maxWidth Maximum width in pixels.
  */
 function MonthPopupButton(maxWidth) {
-  View.call(this, createElement('button', MonthPopupButton.ClassNameMonthPopupButton));
+  View.call(
+      this,
+      createElement('button', MonthPopupButton.ClassNameMonthPopupButton));
   this.element.setAttribute('aria-label', global.params.axShowMonthSelector);
 
   /**
    * @type {!Element}
    * @const
    */
-  this.labelElement = createElement('span', MonthPopupButton.ClassNameMonthPopupButtonLabel, '-----');
+  this.labelElement = createElement(
+      'span', MonthPopupButton.ClassNameMonthPopupButtonLabel, '-----');
   this.element.appendChild(this.labelElement);
 
   /**
    * @type {!Element}
    * @const
    */
-  this.disclosureTriangleIcon = createElement('span', MonthPopupButton.ClassNameDisclosureTriangle);
+  this.disclosureTriangleIcon =
+      createElement('span', MonthPopupButton.ClassNameDisclosureTriangle);
   this.disclosureTriangleIcon.innerHTML =
       '<svg width=\'7\' height=\'5\'><polygon points=\'0,1 7,1 3.5,5\' style=\'fill:#000000;\' /></svg>';
   this.element.appendChild(this.disclosureTriangleIcon);
@@ -2858,7 +2978,9 @@
  * @param {!Month} month
  */
 MonthPopupButton.prototype.setCurrentMonth = function(month) {
-  this.labelElement.textContent = this._useShortMonth ? month.toShortLocaleString() : month.toLocaleString();
+  this.labelElement.textContent = this._useShortMonth ?
+      month.toShortLocaleString() :
+      month.toLocaleString();
 };
 
 /**
@@ -2873,15 +2995,21 @@
  * @extends View
  */
 function CalendarNavigationButton() {
-  View.call(this, createElement('button', CalendarNavigationButton.ClassNameCalendarNavigationButton));
+  View.call(
+      this,
+      createElement(
+          'button',
+          CalendarNavigationButton.ClassNameCalendarNavigationButton));
   /**
    * @type {number} Threshold for starting repeating clicks in milliseconds.
    */
-  this.repeatingClicksStartingThreshold = CalendarNavigationButton.DefaultRepeatingClicksStartingThreshold;
+  this.repeatingClicksStartingThreshold =
+      CalendarNavigationButton.DefaultRepeatingClicksStartingThreshold;
   /**
    * @type {number} Interval between reapeating clicks in milliseconds.
    */
-  this.reapeatingClicksInterval = CalendarNavigationButton.DefaultRepeatingClicksInterval;
+  this.reapeatingClicksInterval =
+      CalendarNavigationButton.DefaultRepeatingClicksInterval;
   /**
    * @type {?number} The ID for the timeout that triggers the repeating clicks.
    */
@@ -2897,7 +3025,8 @@
 CalendarNavigationButton.DefaultRepeatingClicksInterval = 300;
 CalendarNavigationButton.LeftMargin = 4;
 CalendarNavigationButton.Width = 24;
-CalendarNavigationButton.ClassNameCalendarNavigationButton = 'calendar-navigation-button';
+CalendarNavigationButton.ClassNameCalendarNavigationButton =
+    'calendar-navigation-button';
 CalendarNavigationButton.EventTypeButtonClick = 'buttonClick';
 CalendarNavigationButton.EventTypeRepeatingButtonClick = 'repeatingButtonClick';
 
@@ -2921,7 +3050,8 @@
 CalendarNavigationButton.prototype.onTouchStart = function(event) {
   if (this._timer !== null)
     return;
-  this._timer = setTimeout(this.onRepeatingClick, this.repeatingClicksStartingThreshold);
+  this._timer =
+      setTimeout(this.onRepeatingClick, this.repeatingClicksStartingThreshold);
   window.addEventListener('touchend', this.onWindowTouchEnd, false);
 };
 
@@ -2942,7 +3072,8 @@
 CalendarNavigationButton.prototype.onMouseDown = function(event) {
   if (this._timer !== null)
     return;
-  this._timer = setTimeout(this.onRepeatingClick, this.repeatingClicksStartingThreshold);
+  this._timer =
+      setTimeout(this.onRepeatingClick, this.repeatingClicksStartingThreshold);
   window.addEventListener('mouseup', this.onWindowMouseUp, false);
 };
 
@@ -2961,8 +3092,10 @@
  * @param {?Event} event
  */
 CalendarNavigationButton.prototype.onRepeatingClick = function(event) {
-  this.dispatchEvent(CalendarNavigationButton.EventTypeRepeatingButtonClick, this);
-  this._timer = setTimeout(this.onRepeatingClick, this.reapeatingClicksInterval);
+  this.dispatchEvent(
+      CalendarNavigationButton.EventTypeRepeatingButtonClick, this);
+  this._timer =
+      setTimeout(this.onRepeatingClick, this.reapeatingClicksInterval);
 };
 
 /**
@@ -2971,19 +3104,25 @@
  * @param {!CalendarPicker} calendarPicker
  */
 function CalendarHeaderView(calendarPicker) {
-  View.call(this, createElement('div', CalendarHeaderView.ClassNameCalendarHeaderView));
+  View.call(
+      this,
+      createElement('div', CalendarHeaderView.ClassNameCalendarHeaderView));
   this.calendarPicker = calendarPicker;
-  this.calendarPicker.on(CalendarPicker.EventTypeCurrentMonthChanged, this.onCurrentMonthChanged);
+  this.calendarPicker.on(
+      CalendarPicker.EventTypeCurrentMonthChanged, this.onCurrentMonthChanged);
 
-  var titleElement = createElement('div', CalendarHeaderView.ClassNameCalendarTitle);
+  var titleElement =
+      createElement('div', CalendarHeaderView.ClassNameCalendarTitle);
   this.element.appendChild(titleElement);
 
   /**
    * @type {!MonthPopupButton}
    */
   this.monthPopupButton = new MonthPopupButton(
-      this.calendarPicker.calendarTableView.width() - CalendarTableView.GetBorderWidth() * 2 -
-      CalendarNavigationButton.Width * 3 - CalendarNavigationButton.LeftMargin * 2);
+      this.calendarPicker.calendarTableView.width() -
+      CalendarTableView.GetBorderWidth() * 2 -
+      CalendarNavigationButton.Width * 3 -
+      CalendarNavigationButton.LeftMargin * 2);
   this.monthPopupButton.attachTo(titleElement);
 
   /**
@@ -2992,9 +3131,14 @@
    */
   this._previousMonthButton = new CalendarNavigationButton();
   this._previousMonthButton.attachTo(this);
-  this._previousMonthButton.on(CalendarNavigationButton.EventTypeButtonClick, this.onNavigationButtonClick);
-  this._previousMonthButton.on(CalendarNavigationButton.EventTypeRepeatingButtonClick, this.onNavigationButtonClick);
-  this._previousMonthButton.element.setAttribute('aria-label', global.params.axShowPreviousMonth);
+  this._previousMonthButton.on(
+      CalendarNavigationButton.EventTypeButtonClick,
+      this.onNavigationButtonClick);
+  this._previousMonthButton.on(
+      CalendarNavigationButton.EventTypeRepeatingButtonClick,
+      this.onNavigationButtonClick);
+  this._previousMonthButton.element.setAttribute(
+      'aria-label', global.params.axShowPreviousMonth);
 
   if (!global.params.isFormControlsRefreshEnabled) {
     /**
@@ -3003,13 +3147,17 @@
      */
     this._todayButton = new CalendarNavigationButton();
     this._todayButton.attachTo(this);
-    this._todayButton.on(CalendarNavigationButton.EventTypeButtonClick, this.onNavigationButtonClick);
-    this._todayButton.element.classList.add(CalendarHeaderView.GetClassNameTodayButton());
+    this._todayButton.on(
+        CalendarNavigationButton.EventTypeButtonClick,
+        this.onNavigationButtonClick);
+    this._todayButton.element.classList.add(
+        CalendarHeaderView.GetClassNameTodayButton());
     var monthContainingToday = Month.createFromToday();
     this._todayButton.setDisabled(
         monthContainingToday < this.calendarPicker.minimumMonth ||
         monthContainingToday > this.calendarPicker.maximumMonth);
-    this._todayButton.element.setAttribute('aria-label', global.params.todayLabel);
+    this._todayButton.element.setAttribute(
+        'aria-label', global.params.todayLabel);
   }
 
   /**
@@ -3018,16 +3166,25 @@
    */
   this._nextMonthButton = new CalendarNavigationButton();
   this._nextMonthButton.attachTo(this);
-  this._nextMonthButton.on(CalendarNavigationButton.EventTypeButtonClick, this.onNavigationButtonClick);
-  this._nextMonthButton.on(CalendarNavigationButton.EventTypeRepeatingButtonClick, this.onNavigationButtonClick);
-  this._nextMonthButton.element.setAttribute('aria-label', global.params.axShowNextMonth);
+  this._nextMonthButton.on(
+      CalendarNavigationButton.EventTypeButtonClick,
+      this.onNavigationButtonClick);
+  this._nextMonthButton.on(
+      CalendarNavigationButton.EventTypeRepeatingButtonClick,
+      this.onNavigationButtonClick);
+  this._nextMonthButton.element.setAttribute(
+      'aria-label', global.params.axShowNextMonth);
 
   if (global.params.isLocaleRTL) {
-    this._nextMonthButton.element.innerHTML = CalendarHeaderView.GetBackwardTriangle();
-    this._previousMonthButton.element.innerHTML = CalendarHeaderView.GetForwardTriangle();
+    this._nextMonthButton.element.innerHTML =
+        CalendarHeaderView.GetBackwardTriangle();
+    this._previousMonthButton.element.innerHTML =
+        CalendarHeaderView.GetForwardTriangle();
   } else {
-    this._nextMonthButton.element.innerHTML = CalendarHeaderView.GetForwardTriangle();
-    this._previousMonthButton.element.innerHTML = CalendarHeaderView.GetBackwardTriangle();
+    this._nextMonthButton.element.innerHTML =
+        CalendarHeaderView.GetForwardTriangle();
+    this._previousMonthButton.element.innerHTML =
+        CalendarHeaderView.GetBackwardTriangle();
   }
 }
 
@@ -3046,7 +3203,7 @@
     return CalendarHeaderView._ForwardTriangleRefresh;
   }
   return CalendarHeaderView._ForwardTriangle;
-}
+};
 CalendarHeaderView._BackwardTriangle =
     '<svg width=\'4\' height=\'7\'><polygon points=\'0,3.5 4,7 4,0\' style=\'fill:#6e6e6e;\' /></svg>';
 CalendarHeaderView._BackwardTriangleRefresh =
@@ -3058,7 +3215,7 @@
     return CalendarHeaderView._BackwardTriangleRefresh;
   }
   return CalendarHeaderView._BackwardTriangle;
-}
+};
 CalendarHeaderView.ClassNameCalendarHeaderView = 'calendar-header-view';
 CalendarHeaderView.ClassNameCalendarTitle = 'calendar-title';
 CalendarHeaderView.ClassNameTodayButton = 'today-button';
@@ -3068,23 +3225,27 @@
     return CalendarHeaderView.ClassNameTodayButtonRefresh;
   }
   return CalendarHeaderView.ClassNameTodayButton;
-}
+};
 
 CalendarHeaderView.prototype.onCurrentMonthChanged = function() {
   this.monthPopupButton.setCurrentMonth(this.calendarPicker.currentMonth());
   this._previousMonthButton.setDisabled(
-      this.disabled || this.calendarPicker.currentMonth() <= this.calendarPicker.minimumMonth);
+      this.disabled ||
+      this.calendarPicker.currentMonth() <= this.calendarPicker.minimumMonth);
   this._nextMonthButton.setDisabled(
-      this.disabled || this.calendarPicker.currentMonth() >= this.calendarPicker.maximumMonth);
+      this.disabled ||
+      this.calendarPicker.currentMonth() >= this.calendarPicker.maximumMonth);
 };
 
 CalendarHeaderView.prototype.onNavigationButtonClick = function(sender) {
   if (sender === this._previousMonthButton)
     this.calendarPicker.setCurrentMonth(
-        this.calendarPicker.currentMonth().previous(), CalendarPicker.NavigationBehavior.WithAnimation);
+        this.calendarPicker.currentMonth().previous(),
+        CalendarPicker.NavigationBehavior.WithAnimation);
   else if (sender === this._nextMonthButton)
     this.calendarPicker.setCurrentMonth(
-        this.calendarPicker.currentMonth().next(), CalendarPicker.NavigationBehavior.WithAnimation);
+        this.calendarPicker.currentMonth().next(),
+        CalendarPicker.NavigationBehavior.WithAnimation);
   else
     this.calendarPicker.selectRangeContainingDay(Day.createFromToday());
 };
@@ -3095,19 +3256,24 @@
 CalendarHeaderView.prototype.setDisabled = function(disabled) {
   this.disabled = disabled;
   if (global.params.isFormControlsRefreshEnabled) {
-    this._previousMonthButton.element.style.visibility = this.disabled ? 'hidden' : 'visible';
-    this._nextMonthButton.element.style.visibility = this.disabled ? 'hidden' : 'visible';
+    this._previousMonthButton.element.style.visibility =
+        this.disabled ? 'hidden' : 'visible';
+    this._nextMonthButton.element.style.visibility =
+        this.disabled ? 'hidden' : 'visible';
   }
 
   this.monthPopupButton.element.disabled = this.disabled;
   this._previousMonthButton.setDisabled(
-      this.disabled || this.calendarPicker.currentMonth() <= this.calendarPicker.minimumMonth);
+      this.disabled ||
+      this.calendarPicker.currentMonth() <= this.calendarPicker.minimumMonth);
   this._nextMonthButton.setDisabled(
-      this.disabled || this.calendarPicker.currentMonth() >= this.calendarPicker.maximumMonth);
+      this.disabled ||
+      this.calendarPicker.currentMonth() >= this.calendarPicker.maximumMonth);
   if (this._todayButton) {
     var monthContainingToday = Month.createFromToday();
     this._todayButton.setDisabled(
-        this.disabled || monthContainingToday < this.calendarPicker.minimumMonth ||
+        this.disabled ||
+        monthContainingToday < this.calendarPicker.minimumMonth ||
         monthContainingToday > this.calendarPicker.maximumMonth);
   }
 };
@@ -3121,7 +3287,8 @@
   this.element.classList.add(DayCell.ClassNameDayCell);
   this.element.style.width = DayCell.GetWidth() + 'px';
   this.element.style.height = DayCell.GetHeight() + 'px';
-  this.element.style.lineHeight = (DayCell.GetHeight() - DayCell.PaddingSize * 2) + 'px';
+  this.element.style.lineHeight =
+      (DayCell.GetHeight() - DayCell.PaddingSize * 2) + 'px';
   this.element.setAttribute('role', 'gridcell');
   /**
    * @type {?Day}
@@ -3138,7 +3305,7 @@
     return DayCell._WidthRefresh;
   }
   return DayCell._Width;
-}
+};
 DayCell._Height = hasInaccuratePointingDevice() ? 34 : 20;
 DayCell._HeightRefresh = 28;
 DayCell.GetHeight = function() {
@@ -3146,7 +3313,7 @@
     return DayCell._HeightRefresh;
   }
   return DayCell._Height;
-}
+};
 DayCell.PaddingSize = 1;
 DayCell.ClassNameDayCell = 'day-cell';
 DayCell.ClassNameHighlighted = 'highlighted';
@@ -3237,9 +3404,11 @@
 function WeekNumberCell() {
   ListCell.call(this);
   this.element.classList.add(WeekNumberCell.ClassNameWeekNumberCell);
-  this.element.style.width = (WeekNumberCell.Width - WeekNumberCell.SeparatorWidth) + 'px';
+  this.element.style.width =
+      (WeekNumberCell.Width - WeekNumberCell.SeparatorWidth) + 'px';
   this.element.style.height = WeekNumberCell.GetHeight() + 'px';
-  this.element.style.lineHeight = (WeekNumberCell.GetHeight() - WeekNumberCell.PaddingSize * 2) + 'px';
+  this.element.style.lineHeight =
+      (WeekNumberCell.GetHeight() - WeekNumberCell.PaddingSize * 2) + 'px';
   /**
    * @type {?Week}
    */
@@ -3256,7 +3425,7 @@
     return WeekNumberCell._HeightRefresh;
   }
   return WeekNumberCell._Height;
-}
+};
 WeekNumberCell.SeparatorWidth = 1;
 WeekNumberCell.PaddingSize = 1;
 WeekNumberCell.ClassNameWeekNumberCell = 'week-number-cell';
@@ -3288,7 +3457,9 @@
   this.element.id = week.toString();
   this.element.setAttribute('role', 'gridcell');
   this.element.setAttribute(
-      'aria-label', window.pagePopupController.formatWeek(week.year, week.week, week.firstDay().format()));
+      'aria-label',
+      window.pagePopupController.formatWeek(
+          week.year, week.week, week.firstDay().format()));
   this.element.textContent = localizeNumber(this.week.week.toString());
   this.show();
 };
@@ -3326,13 +3497,15 @@
 function CalendarTableHeaderView(hasWeekNumberColumn) {
   View.call(this, createElement('div', 'calendar-table-header-view'));
   if (hasWeekNumberColumn) {
-    var weekNumberLabelElement = createElement('div', 'week-number-label', global.params.weekLabel);
+    var weekNumberLabelElement =
+        createElement('div', 'week-number-label', global.params.weekLabel);
     weekNumberLabelElement.style.width = WeekNumberCell.Width + 'px';
     this.element.appendChild(weekNumberLabelElement);
   }
   for (var i = 0; i < DaysPerWeek; ++i) {
     var weekDayNumber = (global.params.weekStartDay + i) % DaysPerWeek;
-    var labelElement = createElement('div', 'week-day-label', global.params.dayLabels[weekDayNumber]);
+    var labelElement = createElement(
+        'div', 'week-day-label', global.params.dayLabels[weekDayNumber]);
     labelElement.style.width = DayCell.GetWidth() + 'px';
     this.element.appendChild(labelElement);
     if (getLanguage() === 'ja') {
@@ -3353,7 +3526,7 @@
     return CalendarTableHeaderView._HeightRefresh;
   }
   return CalendarTableHeaderView._Height;
-}
+};
 
 /**
  * @constructor
@@ -3389,7 +3562,7 @@
     return CalendarRowCell._HeightRefresh;
   }
   return CalendarRowCell._Height;
-}
+};
 CalendarRowCell.ClassNameCalendarRowCell = 'calendar-row-cell';
 
 CalendarRowCell._recycleBin = [];
@@ -3432,7 +3605,8 @@
   ListCell.prototype.throwAway.call(this);
   if (this.weekNumberCell)
     this.calendarTableView.throwAwayWeekNumberCell(this.weekNumberCell);
-  this._dayCells.forEach(this.calendarTableView.throwAwayDayCell, this.calendarTableView);
+  this._dayCells.forEach(
+      this.calendarTableView.throwAwayDayCell, this.calendarTableView);
   this._dayCells.length = 0;
 };
 
@@ -3471,9 +3645,11 @@
      */
     var todayButton = new CalendarNavigationButton();
     todayButton.attachTo(this);
-    todayButton.on(CalendarNavigationButton.EventTypeButtonClick, this.onTodayButtonClick);
+    todayButton.on(
+        CalendarNavigationButton.EventTypeButtonClick, this.onTodayButtonClick);
     todayButton.element.textContent = global.params.todayLabel;
-    todayButton.element.classList.add(CalendarHeaderView.GetClassNameTodayButton());
+    todayButton.element.classList.add(
+        CalendarHeaderView.GetClassNameTodayButton());
     var monthContainingToday = Month.createFromToday();
     todayButton.setDisabled(
         monthContainingToday < this.calendarPicker.minimumMonth ||
@@ -3504,9 +3680,11 @@
   this.element.addEventListener('mouseout', this.onMouseOut, false);
 
   // You shouldn't be able to use the mouse wheel to scroll.
-  this.scrollView.element.removeEventListener('mousewheel', this.scrollView.onMouseWheel, false);
+  this.scrollView.element.removeEventListener(
+      'mousewheel', this.scrollView.onMouseWheel, false);
   // You shouldn't be able to do gesture scroll.
-  this.scrollView.element.removeEventListener('touchstart', this.scrollView.onTouchStart, false);
+  this.scrollView.element.removeEventListener(
+      'touchstart', this.scrollView.onTouchStart, false);
 }
 
 CalendarTableView.prototype = Object.create(ListView.prototype);
@@ -3518,7 +3696,7 @@
     return CalendarTableView._BorderWidthRefresh;
   }
   return CalendarTableView._BorderWidth;
-}
+};
 CalendarTableView._TodayButtonHeight = 0;
 CalendarTableView._TodayButtonHeightRefresh = 28;
 CalendarTableView.GetTodayButtonHeight = function() {
@@ -3526,7 +3704,7 @@
     return CalendarTableView._TodayButtonHeightRefresh;
   }
   return CalendarTableView._TodayButtonHeight;
-}
+};
 CalendarTableView.ClassNameCalendarTableView = 'calendar-table-view';
 
 /**
@@ -3550,14 +3728,17 @@
  */
 CalendarTableView.prototype.onClick = function(event) {
   if (this.hasWeekNumberColumn) {
-    var weekNumberCellElement = enclosingNodeOrSelfWithClass(event.target, WeekNumberCell.ClassNameWeekNumberCell);
+    var weekNumberCellElement = enclosingNodeOrSelfWithClass(
+        event.target, WeekNumberCell.ClassNameWeekNumberCell);
     if (weekNumberCellElement) {
       var weekNumberCell = weekNumberCellElement.$view;
-      this.calendarPicker.selectRangeContainingDay(weekNumberCell.week.firstDay());
+      this.calendarPicker.selectRangeContainingDay(
+          weekNumberCell.week.firstDay());
       return;
     }
   }
-  var dayCellElement = enclosingNodeOrSelfWithClass(event.target, DayCell.ClassNameDayCell);
+  var dayCellElement =
+      enclosingNodeOrSelfWithClass(event.target, DayCell.ClassNameDayCell);
   if (!dayCellElement)
     return;
   var dayCell = dayCellElement.$view;
@@ -3566,22 +3747,25 @@
 
 CalendarTableView.prototype.onTodayButtonClick = function(sender) {
   this.calendarPicker.selectRangeContainingDay(Day.createFromToday());
-}
+};
 
 /**
  * @param {?Event} event
  */
 CalendarTableView.prototype.onMouseOver = function(event) {
   if (this.hasWeekNumberColumn) {
-    var weekNumberCellElement = enclosingNodeOrSelfWithClass(event.target, WeekNumberCell.ClassNameWeekNumberCell);
+    var weekNumberCellElement = enclosingNodeOrSelfWithClass(
+        event.target, WeekNumberCell.ClassNameWeekNumberCell);
     if (weekNumberCellElement) {
       var weekNumberCell = weekNumberCellElement.$view;
-      this.calendarPicker.highlightRangeContainingDay(weekNumberCell.week.firstDay());
+      this.calendarPicker.highlightRangeContainingDay(
+          weekNumberCell.week.firstDay());
       this._ignoreMouseOutUntillNextMouseOver = false;
       return;
     }
   }
-  var dayCellElement = enclosingNodeOrSelfWithClass(event.target, DayCell.ClassNameDayCell);
+  var dayCellElement =
+      enclosingNodeOrSelfWithClass(event.target, DayCell.ClassNameDayCell);
   if (!dayCellElement)
     return;
   var dayCell = dayCellElement.$view;
@@ -3595,7 +3779,8 @@
 CalendarTableView.prototype.onMouseOut = function(event) {
   if (this._ignoreMouseOutUntillNextMouseOver)
     return;
-  var dayCellElement = enclosingNodeOrSelfWithClass(event.target, DayCell.ClassNameDayCell);
+  var dayCellElement =
+      enclosingNodeOrSelfWithClass(event.target, DayCell.ClassNameDayCell);
   if (!dayCellElement) {
     this.calendarPicker.highlightRangeContainingDay(null);
   }
@@ -3615,14 +3800,19 @@
  * @return {!number} Height in pixels.
  */
 CalendarTableView.prototype.height = function() {
-  return this.scrollView.height() + CalendarTableHeaderView.GetHeight() + CalendarTableView.GetBorderWidth() * 2 + CalendarTableView.GetTodayButtonHeight();
+  return this.scrollView.height() + CalendarTableHeaderView.GetHeight() +
+      CalendarTableView.GetBorderWidth() * 2 +
+      CalendarTableView.GetTodayButtonHeight();
 };
 
 /**
  * @param {!number} height Height in pixels.
  */
 CalendarTableView.prototype.setHeight = function(height) {
-  this.scrollView.setHeight(height - CalendarTableHeaderView.GetHeight() - CalendarTableView.GetBorderWidth() * 2 - CalendarTableView.GetTodayButtonHeight());
+  this.scrollView.setHeight(
+      height - CalendarTableHeaderView.GetHeight() -
+      CalendarTableView.GetBorderWidth() * 2 -
+      CalendarTableView.GetTodayButtonHeight());
   if (global.params.isFormControlsRefreshEnabled) {
     this.element.style.height = height + 'px';
   }
@@ -3634,7 +3824,8 @@
  */
 CalendarTableView.prototype.scrollToMonth = function(month, animate) {
   var rowForFirstDayInMonth = this.columnAndRowForDay(month.firstDay()).row;
-  this.scrollView.scrollTo(this.scrollOffsetForRow(rowForFirstDayInMonth), animate);
+  this.scrollView.scrollTo(
+      this.scrollOffsetForRow(rowForFirstDayInMonth), animate);
 };
 
 /**
@@ -3643,8 +3834,11 @@
  * @return {!Day}
  */
 CalendarTableView.prototype.dayAtColumnAndRow = function(column, row) {
-  var daysSinceMinimum = row * DaysPerWeek + column + global.params.weekStartDay - CalendarTableView._MinimumDayWeekDay;
-  return Day.createFromValue(daysSinceMinimum * MillisecondsPerDay + CalendarTableView._MinimumDayValue);
+  var daysSinceMinimum = row * DaysPerWeek + column +
+      global.params.weekStartDay - CalendarTableView._MinimumDayWeekDay;
+  return Day.createFromValue(
+      daysSinceMinimum * MillisecondsPerDay +
+      CalendarTableView._MinimumDayValue);
 };
 
 CalendarTableView._MinimumDayValue = Day.Minimum.valueOf();
@@ -3655,8 +3849,10 @@
  * @return {!Object} Object with properties column and row.
  */
 CalendarTableView.prototype.columnAndRowForDay = function(day) {
-  var daysSinceMinimum = (day.valueOf() - CalendarTableView._MinimumDayValue) / MillisecondsPerDay;
-  var offset = daysSinceMinimum + CalendarTableView._MinimumDayWeekDay - global.params.weekStartDay;
+  var daysSinceMinimum =
+      (day.valueOf() - CalendarTableView._MinimumDayValue) / MillisecondsPerDay;
+  var offset = daysSinceMinimum + CalendarTableView._MinimumDayWeekDay -
+      global.params.weekStartDay;
   var row = Math.floor(offset / DaysPerWeek);
   var column = offset - row * DaysPerWeek;
   return {column: column, row: row};
@@ -3693,16 +3889,19 @@
     var dayCell = this._dayCells[dayString];
     var day = dayCell.day;
     dayCell.setIsToday(Day.createFromToday().equals(day));
-    dayCell.setSelected(day >= firstDayInSelection && day <= lastDayInSelection);
+    dayCell.setSelected(
+        day >= firstDayInSelection && day <= lastDayInSelection);
     var isHighlighted = day >= firstDayInHighlight && day <= lastDayInHighlight;
     dayCell.setHighlighted(isHighlighted);
     if (isHighlighted) {
       if (firstDayInHighlight == lastDayInHighlight)
         activeCell = dayCell;
-      else if (this.calendarPicker.type == 'month' && day == firstDayInHighlight)
+      else if (
+          this.calendarPicker.type == 'month' && day == firstDayInHighlight)
         activeCell = dayCell;
     }
-    dayCell.setIsInCurrentMonth(day >= firstDayInCurrentMonth && day <= lastDayInCurrentMonth);
+    dayCell.setIsInCurrentMonth(
+        day >= firstDayInCurrentMonth && day <= lastDayInCurrentMonth);
     dayCell.setDisabled(!this.calendarPicker.isValidDay(day));
   }
   if (this.hasWeekNumberColumn) {
@@ -3734,7 +3933,8 @@
   var dayCell = DayCell.recycleOrCreate();
   dayCell.reset(day);
   if (this.calendarPicker.type == 'month')
-    dayCell.element.setAttribute('aria-label', Month.createFromDay(day).toLocaleString());
+    dayCell.element.setAttribute(
+        'aria-label', Month.createFromDay(day).toLocaleString());
   this._dayCells[dayCell.day.toString()] = dayCell;
   return dayCell;
 };
@@ -3815,15 +4015,19 @@
    * @const
    */
   this.calendarHeaderView = new CalendarHeaderView(this);
-  this.calendarHeaderView.monthPopupButton.on(MonthPopupButton.EventTypeButtonClick, this.onMonthPopupButtonClick);
+  this.calendarHeaderView.monthPopupButton.on(
+      MonthPopupButton.EventTypeButtonClick, this.onMonthPopupButtonClick);
   /**
    * @type {!MonthPopupView}
    * @const
    */
-  this.monthPopupView = new MonthPopupView(this.minimumMonth, this.maximumMonth);
+  this.monthPopupView =
+      new MonthPopupView(this.minimumMonth, this.maximumMonth);
   this.monthPopupView.yearListView.on(
-      YearListView.EventTypeYearListViewDidSelectMonth, this.onYearListViewDidSelectMonth);
-  this.monthPopupView.yearListView.on(YearListView.EventTypeYearListViewDidHide, this.onYearListViewDidHide);
+      YearListView.EventTypeYearListViewDidSelectMonth,
+      this.onYearListViewDidSelectMonth);
+  this.monthPopupView.yearListView.on(
+      YearListView.EventTypeYearListViewDidHide, this.onYearListViewDidHide);
   this.calendarHeaderView.attachTo(this);
   this.calendarTableView.attachTo(this);
   /**
@@ -3841,7 +4045,8 @@
    * @protected
    */
   this._highlight = null;
-  this.calendarTableView.element.addEventListener('keydown', this.onCalendarTableKeyDown, false);
+  this.calendarTableView.element.addEventListener(
+      'keydown', this.onCalendarTableKeyDown, false);
   document.body.addEventListener('keydown', this.onBodyKeyDown, false);
 
   window.addEventListener('resize', this.onWindowResize, false);
@@ -3854,10 +4059,13 @@
 
   var initialSelection = parseDateString(config.currentValue);
   if (initialSelection) {
-    this.setCurrentMonth(Month.createFromDay(initialSelection.middleDay()), CalendarPicker.NavigationBehavior.None);
+    this.setCurrentMonth(
+        Month.createFromDay(initialSelection.middleDay()),
+        CalendarPicker.NavigationBehavior.None);
     this.setSelection(initialSelection);
   } else
-    this.setCurrentMonth(Month.createFromToday(), CalendarPicker.NavigationBehavior.None);
+    this.setCurrentMonth(
+        Month.createFromToday(), CalendarPicker.NavigationBehavior.None);
 }
 
 CalendarPicker.prototype = Object.create(View.prototype);
@@ -3895,7 +4103,8 @@
  * @param {!YearListView} sender
  * @param {!Month} month
  */
-CalendarPicker.prototype.onYearListViewDidSelectMonth = function(sender, month) {
+CalendarPicker.prototype.onYearListViewDidSelectMonth = function(
+    sender, month) {
   this.setCurrentMonth(month, CalendarPicker.NavigationBehavior.None);
 };
 
@@ -3911,7 +4120,8 @@
 
 CalendarPicker.prototype.cleanup = function() {
   window.removeEventListener('resize', this.onWindowResize, false);
-  this.calendarTableView.element.removeEventListener('keydown', this.onBodyKeyDown, false);
+  this.calendarTableView.element.removeEventListener(
+      'keydown', this.onBodyKeyDown, false);
   // Month popup view might be attached to document.body.
   this.monthPopupView.hide();
 };
@@ -3922,7 +4132,8 @@
 CalendarPicker.prototype.onMonthPopupButtonClick = function(sender) {
   var clientRect = this.calendarTableView.element.getBoundingClientRect();
   var calendarTableRect = new Rectangle(
-      clientRect.left + document.body.scrollLeft, clientRect.top + document.body.scrollTop, clientRect.width,
+      clientRect.left + document.body.scrollLeft,
+      clientRect.top + document.body.scrollTop, clientRect.width,
       clientRect.height);
   this.monthPopupView.show(this.currentMonth(), calendarTableRect);
   this.calendarHeaderView.setDisabled(true);
@@ -3934,15 +4145,20 @@
 };
 
 CalendarPicker.prototype._setConfig = function(config) {
-  this.config.minimum = (typeof config.min !== 'undefined' && config.min) ? parseDateString(config.min) :
-                                                                            this._dateTypeConstructor.Minimum;
-  this.config.maximum = (typeof config.max !== 'undefined' && config.max) ? parseDateString(config.max) :
-                                                                            this._dateTypeConstructor.Maximum;
+  this.config.minimum = (typeof config.min !== 'undefined' && config.min) ?
+      parseDateString(config.min) :
+      this._dateTypeConstructor.Minimum;
+  this.config.maximum = (typeof config.max !== 'undefined' && config.max) ?
+      parseDateString(config.max) :
+      this._dateTypeConstructor.Maximum;
   this.config.minimumValue = this.config.minimum.valueOf();
   this.config.maximumValue = this.config.maximum.valueOf();
-  this.config.step = (typeof config.step !== undefined) ? Number(config.step) : this._dateTypeConstructor.DefaultStep;
-  this.config.stepBase =
-      (typeof config.stepBase !== 'undefined') ? Number(config.stepBase) : this._dateTypeConstructor.DefaultStepBase;
+  this.config.step = (typeof config.step !== undefined) ?
+      Number(config.step) :
+      this._dateTypeConstructor.DefaultStep;
+  this.config.stepBase = (typeof config.stepBase !== 'undefined') ?
+      Number(config.stepBase) :
+      this._dateTypeConstructor.DefaultStepBase;
 };
 
 /**
@@ -3973,21 +4189,34 @@
     return;
   this._currentMonth = month;
   this.calendarTableView.scrollToMonth(
-      this._currentMonth, behavior === CalendarPicker.NavigationBehavior.WithAnimation);
+      this._currentMonth,
+      behavior === CalendarPicker.NavigationBehavior.WithAnimation);
   this.adjustHeight();
   this.calendarTableView.setNeedsUpdateCells(true);
-  this.dispatchEvent(CalendarPicker.EventTypeCurrentMonthChanged, {target: this});
+  this.dispatchEvent(
+      CalendarPicker.EventTypeCurrentMonthChanged, {target: this});
 };
 
 CalendarPicker.prototype.adjustHeight = function() {
-  var rowForFirstDayInMonth = this.calendarTableView.columnAndRowForDay(this._currentMonth.firstDay()).row;
-  var rowForLastDayInMonth = this.calendarTableView.columnAndRowForDay(this._currentMonth.lastDay()).row;
-  var numberOfRows = global.params.isFormControlsRefreshEnabled ? CalendarPicker.VisibleRowsRefresh : rowForLastDayInMonth - rowForFirstDayInMonth + 1;
-  var calendarTableViewHeight =
-      CalendarTableHeaderView.GetHeight() + numberOfRows * DayCell.GetHeight() + CalendarTableView.GetBorderWidth() * 2 + CalendarTableView.GetTodayButtonHeight();
-  var height = (this.monthPopupView.isVisible && !global.params.isFormControlsRefreshEnabled ? YearListView.GetHeight() : calendarTableViewHeight) +
-      CalendarHeaderView.Height + CalendarHeaderView.BottomMargin + CalendarPicker.Padding * 2 +
-      CalendarPicker.BorderWidth * 2;
+  var rowForFirstDayInMonth =
+      this.calendarTableView.columnAndRowForDay(this._currentMonth.firstDay())
+          .row;
+  var rowForLastDayInMonth =
+      this.calendarTableView.columnAndRowForDay(this._currentMonth.lastDay())
+          .row;
+  var numberOfRows = global.params.isFormControlsRefreshEnabled ?
+      CalendarPicker.VisibleRowsRefresh :
+      rowForLastDayInMonth - rowForFirstDayInMonth + 1;
+  var calendarTableViewHeight = CalendarTableHeaderView.GetHeight() +
+      numberOfRows * DayCell.GetHeight() +
+      CalendarTableView.GetBorderWidth() * 2 +
+      CalendarTableView.GetTodayButtonHeight();
+  var height = (this.monthPopupView.isVisible &&
+                        !global.params.isFormControlsRefreshEnabled ?
+                    YearListView.GetHeight() :
+                    calendarTableViewHeight) +
+      CalendarHeaderView.Height + CalendarHeaderView.BottomMargin +
+      CalendarPicker.Padding * 2 + CalendarPicker.BorderWidth * 2;
   this.setHeight(height);
 };
 
@@ -4003,8 +4232,11 @@
  * @return {!Day}
  */
 CalendarPicker.prototype.firstVisibleDay = function() {
-  var firstVisibleRow = this.calendarTableView.columnAndRowForDay(this.currentMonth().firstDay()).row;
-  var firstVisibleDay = this.calendarTableView.dayAtColumnAndRow(0, firstVisibleRow);
+  var firstVisibleRow =
+      this.calendarTableView.columnAndRowForDay(this.currentMonth().firstDay())
+          .row;
+  var firstVisibleDay =
+      this.calendarTableView.dayAtColumnAndRow(0, firstVisibleRow);
   if (!firstVisibleDay)
     firstVisibleDay = Day.Minimum;
   return firstVisibleDay;
@@ -4014,11 +4246,17 @@
  * @return {!Day}
  */
 CalendarPicker.prototype.lastVisibleDay = function() {
-  var lastVisibleRow = this.calendarTableView.columnAndRowForDay(this.currentMonth().lastDay()).row;
+  var lastVisibleRow =
+      this.calendarTableView.columnAndRowForDay(this.currentMonth().lastDay())
+          .row;
   if (global.params.isFormControlsRefreshEnabled) {
-    lastVisibleRow = this.calendarTableView.columnAndRowForDay(this.currentMonth().firstDay()).row + CalendarPicker.VisibleRowsRefresh - 1;
+    lastVisibleRow = this.calendarTableView
+                         .columnAndRowForDay(this.currentMonth().firstDay())
+                         .row +
+        CalendarPicker.VisibleRowsRefresh - 1;
   }
-  var lastVisibleDay = this.calendarTableView.dayAtColumnAndRow(DaysPerWeek - 1, lastVisibleRow);
+  var lastVisibleDay =
+      this.calendarTableView.dayAtColumnAndRow(DaysPerWeek - 1, lastVisibleRow);
   if (!lastVisibleDay)
     lastVisibleDay = Day.Maximum;
   return lastVisibleDay;
@@ -4052,24 +4290,40 @@
   var firstDayInSelection = dayOrWeekOrMonth.firstDay();
   var lastDayInSelection = dayOrWeekOrMonth.lastDay();
   var candidateCurrentMonth = Month.createFromDay(firstDayInSelection);
-  if (this.firstVisibleDay() > lastDayInSelection || this.lastVisibleDay() < firstDayInSelection) {
+  if (this.firstVisibleDay() > lastDayInSelection ||
+      this.lastVisibleDay() < firstDayInSelection) {
     // Change current month if the selection is not visible at all.
-    this.setCurrentMonth(candidateCurrentMonth, CalendarPicker.NavigationBehavior.WithAnimation);
-  } else if (this.firstVisibleDay() < firstDayInSelection || this.lastVisibleDay() > lastDayInSelection) {
+    this.setCurrentMonth(
+        candidateCurrentMonth, CalendarPicker.NavigationBehavior.WithAnimation);
+  } else if (
+      this.firstVisibleDay() < firstDayInSelection ||
+      this.lastVisibleDay() > lastDayInSelection) {
     // If the selection is partly visible, only change the current month if
     // doing so will make the whole selection visible.
-    var firstVisibleRow = this.calendarTableView.columnAndRowForDay(candidateCurrentMonth.firstDay()).row;
-    var firstVisibleDay = this.calendarTableView.dayAtColumnAndRow(0, firstVisibleRow);
-    var lastVisibleRow = this.calendarTableView.columnAndRowForDay(candidateCurrentMonth.lastDay()).row;
-    var lastVisibleDay = this.calendarTableView.dayAtColumnAndRow(DaysPerWeek - 1, lastVisibleRow);
-    if (firstDayInSelection >= firstVisibleDay && lastDayInSelection <= lastVisibleDay)
-      this.setCurrentMonth(candidateCurrentMonth, CalendarPicker.NavigationBehavior.WithAnimation);
+    var firstVisibleRow =
+        this.calendarTableView
+            .columnAndRowForDay(candidateCurrentMonth.firstDay())
+            .row;
+    var firstVisibleDay =
+        this.calendarTableView.dayAtColumnAndRow(0, firstVisibleRow);
+    var lastVisibleRow =
+        this.calendarTableView
+            .columnAndRowForDay(candidateCurrentMonth.lastDay())
+            .row;
+    var lastVisibleDay = this.calendarTableView.dayAtColumnAndRow(
+        DaysPerWeek - 1, lastVisibleRow);
+    if (firstDayInSelection >= firstVisibleDay &&
+        lastDayInSelection <= lastVisibleDay)
+      this.setCurrentMonth(
+          candidateCurrentMonth,
+          CalendarPicker.NavigationBehavior.WithAnimation);
   }
   this._setHighlight(dayOrWeekOrMonth);
   if (!this.isValid(dayOrWeekOrMonth))
     return;
   this._selection = dayOrWeekOrMonth;
-  this.monthPopupView.yearListView.setSelectedMonth(Month.createFromDay(dayOrWeekOrMonth.middleDay()));
+  this.monthPopupView.yearListView.setSelectedMonth(
+      Month.createFromDay(dayOrWeekOrMonth.middleDay()));
   this.calendarTableView.setNeedsUpdateCells(true);
 };
 
@@ -4116,7 +4370,9 @@
  */
 CalendarPicker.prototype._stepMismatch = function(value) {
   var nextAllowedValue =
-      Math.ceil((value - this.config.stepBase) / this.config.step) * this.config.step + this.config.stepBase;
+      Math.ceil((value - this.config.stepBase) / this.config.step) *
+          this.config.step +
+      this.config.stepBase;
   return nextAllowedValue >= value + this._dateTypeConstructor.DefaultStep;
 };
 
@@ -4134,8 +4390,8 @@
  */
 CalendarPicker.prototype.isValid = function(dayOrWeekOrMonth) {
   var value = dayOrWeekOrMonth.valueOf();
-  return dayOrWeekOrMonth instanceof this._dateTypeConstructor && !this._outOfRange(value) &&
-      !this._stepMismatch(value);
+  return dayOrWeekOrMonth instanceof this._dateTypeConstructor &&
+      !this._outOfRange(value) && !this._stepMismatch(value);
 };
 
 /**
@@ -4155,8 +4411,11 @@
     return false;
   if (this._outOfRange(dateRange.valueOf()))
     return false;
-  if (this.firstVisibleDay() > dateRange.middleDay() || this.lastVisibleDay() < dateRange.middleDay())
-    this.setCurrentMonth(Month.createFromDay(dateRange.middleDay()), CalendarPicker.NavigationBehavior.WithAnimation);
+  if (this.firstVisibleDay() > dateRange.middleDay() ||
+      this.lastVisibleDay() < dateRange.middleDay())
+    this.setCurrentMonth(
+        Month.createFromDay(dateRange.middleDay()),
+        CalendarPicker.NavigationBehavior.WithAnimation);
   this._setHighlight(dateRange);
   return true;
 };
@@ -4173,28 +4432,35 @@
   } else if (key == 'PageUp') {
     var previousMonth = this.currentMonth().previous();
     if (previousMonth && previousMonth >= this.config.minimumValue) {
-      this.setCurrentMonth(previousMonth, CalendarPicker.NavigationBehavior.WithAnimation);
+      this.setCurrentMonth(
+          previousMonth, CalendarPicker.NavigationBehavior.WithAnimation);
       eventHandled = true;
     }
   } else if (key == 'PageDown') {
     var nextMonth = this.currentMonth().next();
     if (nextMonth && nextMonth >= this.config.minimumValue) {
-      this.setCurrentMonth(nextMonth, CalendarPicker.NavigationBehavior.WithAnimation);
+      this.setCurrentMonth(
+          nextMonth, CalendarPicker.NavigationBehavior.WithAnimation);
       eventHandled = true;
     }
   } else if (this._highlight) {
     if (global.params.isLocaleRTL ? key == 'ArrowRight' : key == 'ArrowLeft') {
       eventHandled = this._moveHighlight(this._highlight.previous());
     } else if (key == 'ArrowUp') {
-      eventHandled = this._moveHighlight(this._highlight.previous(this.type === 'date' ? DaysPerWeek : 1));
-    } else if (global.params.isLocaleRTL ? key == 'ArrowLeft' : key == 'ArrowRight') {
+      eventHandled = this._moveHighlight(
+          this._highlight.previous(this.type === 'date' ? DaysPerWeek : 1));
+    } else if (
+        global.params.isLocaleRTL ? key == 'ArrowLeft' : key == 'ArrowRight') {
       eventHandled = this._moveHighlight(this._highlight.next());
     } else if (key == 'ArrowDown') {
-      eventHandled = this._moveHighlight(this._highlight.next(this.type === 'date' ? DaysPerWeek : 1));
+      eventHandled = this._moveHighlight(
+          this._highlight.next(this.type === 'date' ? DaysPerWeek : 1));
     } else if (key == 'Enter') {
       this.setSelectionAndCommit(this._highlight);
     }
-  } else if (key == 'ArrowLeft' || key == 'ArrowUp' || key == 'ArrowRight' || key == 'ArrowDown') {
+  } else if (
+      key == 'ArrowLeft' || key == 'ArrowUp' || key == 'ArrowRight' ||
+      key == 'ArrowDown') {
     // Highlight range near the middle.
     this.highlightRangeContainingDay(this.currentMonth().middleDay());
     eventHandled = true;
@@ -4211,7 +4477,9 @@
  */
 CalendarPicker.prototype.width = function() {
   return this.calendarTableView.width() +
-      (CalendarTableView.GetBorderWidth() + CalendarPicker.BorderWidth + CalendarPicker.Padding) * 2;
+      (CalendarTableView.GetBorderWidth() + CalendarPicker.BorderWidth +
+       CalendarPicker.Padding) *
+      2;
 };
 
 /**
@@ -4230,7 +4498,8 @@
   this._height = height;
   resizeWindow(this.width(), this._height);
   this.calendarTableView.setHeight(
-      this._height - CalendarHeaderView.Height - CalendarHeaderView.BottomMargin - CalendarPicker.Padding * 2 -
+      this._height - CalendarHeaderView.Height -
+      CalendarHeaderView.BottomMargin - CalendarPicker.Padding * 2 -
       CalendarPicker.BorderWidth * 2);
 };
 
@@ -4255,15 +4524,22 @@
     case 'd':
     case 'D':
       offset = offset || MonthsPerYear * 10;
-      var oldFirstVisibleRow = this.calendarTableView.columnAndRowForDay(this.currentMonth().firstDay()).row;
+      var oldFirstVisibleRow =
+          this.calendarTableView
+              .columnAndRowForDay(this.currentMonth().firstDay())
+              .row;
       this.setCurrentMonth(
-          event.shiftKey ? this.currentMonth().previous(offset) : this.currentMonth().next(offset),
+          event.shiftKey ? this.currentMonth().previous(offset) :
+                           this.currentMonth().next(offset),
           CalendarPicker.NavigationBehavior.WithAnimation);
-      var newFirstVisibleRow = this.calendarTableView.columnAndRowForDay(this.currentMonth().firstDay()).row;
+      var newFirstVisibleRow =
+          this.calendarTableView
+              .columnAndRowForDay(this.currentMonth().firstDay())
+              .row;
       if (this._highlight) {
         var highlightMiddleDay = this._highlight.middleDay();
-        this.highlightRangeContainingDay(
-            highlightMiddleDay.next((newFirstVisibleRow - oldFirstVisibleRow) * DaysPerWeek));
+        this.highlightRangeContainingDay(highlightMiddleDay.next(
+            (newFirstVisibleRow - oldFirstVisibleRow) * DaysPerWeek));
       }
       eventHandled = true;
       break;
diff --git a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js
index 15c60eb0..43698c0 100644
--- a/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.js
@@ -35,7 +35,7 @@
  */
 function validateColorSuggestionPickerArguments(args) {
   if (!args.shouldShowColorSuggestionPicker)
-    return 'Should not be showing the color suggestion picker.'
+    return 'Should not be showing the color suggestion picker.';
   if (!args.values)
     return 'No values.';
   if (!args.otherColorLabel)
@@ -70,7 +70,8 @@
 
 ColorSuggestionPicker.prototype._layout = function() {
   var container = createElement('div', 'color-swatch-container');
-  container.addEventListener('click', this._handleSwatchClick.bind(this), false);
+  container.addEventListener(
+      'click', this._handleSwatchClick.bind(this), false);
   for (var i = 0; i < this._config.values.length; ++i) {
     var swatch = createElement('button', 'color-swatch');
     swatch.dataset.index = i;
@@ -85,8 +86,10 @@
   container.style.width = containerWidth + 'px';
   container.style.maxHeight = (SwatchBorderBoxHeight * SwatchesMaxRow) + 'px';
   this._element.appendChild(container);
-  var otherButton = createElement('button', 'other-color', this._config.otherColorLabel);
-  otherButton.addEventListener('click', this._onOtherButtonClick.bind(this), false);
+  var otherButton =
+      createElement('button', 'other-color', this._config.otherColorLabel);
+  otherButton.addEventListener(
+      'click', this._onOtherButtonClick.bind(this), false);
   this._element.appendChild(otherButton);
   this._container = container;
   this._otherButton = otherButton;
@@ -109,7 +112,7 @@
   } else {
     this.chooseOtherColor();
   }
-}
+};
 
 ColorSuggestionPicker.prototype.selectColorAtIndex = function(index) {
   index = Math.max(Math.min(this._container.childNodes.length - 1, index), 0);
@@ -131,7 +134,9 @@
   var key = event.key;
   if (key === 'Escape')
     this.handleCancel();
-  else if (key == 'ArrowLeft' || key == 'ArrowUp' || key == 'ArrowRight' || key == 'ArrowDown') {
+  else if (
+      key == 'ArrowLeft' || key == 'ArrowUp' || key == 'ArrowRight' ||
+      key == 'ArrowDown') {
     var selectedElement = document.activeElement;
     var index = 0;
     if (selectedElement.classList.contains('other-color')) {
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.js b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
index 8b7ce346..c09fad5 100644
--- a/third_party/blink/renderer/core/html/forms/resources/color_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
@@ -23,7 +23,7 @@
  */
 function validateColorPickerArguments(args) {
   if (args.shouldShowColorSuggestionPicker)
-    return 'Should be showing the color suggestion picker.'
+    return 'Should be showing the color suggestion picker.';
   if (!args.selectedColor)
     return 'No selectedColor.';
   return null;
@@ -83,15 +83,17 @@
         colorStringOrFormat = colorStringOrFormat.replace(/\s+/g, '');
         [this.rValue_, this.gValue_, this.bValue_] =
             colorStringOrFormat.substring(4, colorStringOrFormat.length - 1)
-            .split(',').map(Number);
+                .split(',')
+                .map(Number);
       } else if (colorStringOrFormat.startsWith('hsl')) {
         colorStringOrFormat = colorStringOrFormat.replace(/%|\s+/g, '');
         [this.hValue_, this.sValue_, this.lValue_] =
             colorStringOrFormat.substring(4, colorStringOrFormat.length - 1)
-            .split(',').map(Number);
+                .split(',')
+                .map(Number);
       }
     } else {
-      switch(colorStringOrFormat) {
+      switch (colorStringOrFormat) {
         case ColorFormat.HEX:
           this.hexValue_ = colorValues[0].toLowerCase();
           break;
@@ -124,11 +126,9 @@
     if (this.hexValue_ !== undefined) {
       // Already computed.
     } else if (this.rValue_ !== undefined) {
-      this.hexValue_ =
-          Color.rgbToHex(this.rValue_, this.gValue_, this.bValue_);
+      this.hexValue_ = Color.rgbToHex(this.rValue_, this.gValue_, this.bValue_);
     } else if (this.hValue_ !== undefined) {
-      this.hexValue_ =
-          Color.hslToHex(this.hValue_, this.sValue_, this.lValue_);
+      this.hexValue_ = Color.hslToHex(this.hValue_, this.sValue_, this.lValue_);
     }
   }
 
@@ -231,7 +231,9 @@
   static hexToRGB(hexValue) {
     // Ex. 'ffffff' => '[255,255,255]'
     const colorValue = parseInt(hexValue, 16);
-    return [(colorValue >> 16) & 255, (colorValue >> 8) & 255, colorValue & 255];
+    return [
+      (colorValue >> 16) & 255, (colorValue >> 8) & 255, colorValue & 255
+    ];
   }
 
   /**
@@ -242,7 +244,7 @@
     // Ex. '[255,255,255]' => 'ffffff'
     return rgbValues.reduce((cumulativeHexValue, rgbValue) => {
       let hexValue = Number(rgbValue).toString(16);
-      if(hexValue.length == 1) {
+      if (hexValue.length == 1) {
         hexValue = '0' + hexValue;
       }
       return (cumulativeHexValue + hexValue);
@@ -357,9 +359,10 @@
    * Both color triples must be of the same color format.
    */
   static distance(colorTripleA, colorTripleB) {
-    return Math.sqrt(Math.pow(colorTripleA[0] - colorTripleB[0], 2)
-        + Math.pow(colorTripleA[1] - colorTripleB[1], 2)
-        + Math.pow(colorTripleA[2] - colorTripleB[2], 2));
+    return Math.sqrt(
+        Math.pow(colorTripleA[0] - colorTripleB[0], 2) +
+        Math.pow(colorTripleA[1] - colorTripleB[1], 2) +
+        Math.pow(colorTripleA[2] - colorTripleB[2], 2));
   }
 }
 
@@ -425,15 +428,14 @@
 
     this.visualColorPicker_ = new VisualColorPicker(initialColor);
     this.manualColorPicker_ = new ManualColorPicker(initialColor);
-    this.submissionControls_ =
-        new SubmissionControls(this.onSubmitButtonClick_,
-                               this.onCancelButtonClick_);
-    this.append(this.visualColorPicker_,
-                this.manualColorPicker_,
-                this.submissionControls_);
+    this.submissionControls_ = new SubmissionControls(
+        this.onSubmitButtonClick_, this.onCancelButtonClick_);
+    this.append(
+        this.visualColorPicker_, this.manualColorPicker_,
+        this.submissionControls_);
 
-    this.visualColorPicker_.addEventListener('visual-color-picker-initialized',
-        this.initializeListeners_);
+    this.visualColorPicker_.addEventListener(
+        'visual-color-picker-initialized', this.initializeListeners_);
   }
 
   initializeListeners_ = () => {
@@ -519,11 +521,11 @@
     window.setTimeout(function() {
       window.pagePopupController.setValueAndClosePopup(0, selectedValue);
     }, ColorPicker.COMMIT_DELAY_MS);
-  }
+  };
 
   onCancelButtonClick_ = () => {
     window.pagePopupController.closePopup();
-  }
+  };
 }
 window.customElements.define('color-picker', ColorPicker);
 
@@ -543,9 +545,8 @@
     this.eyeDropper_ = new EyeDropper();
     this.colorViewer_ = new ColorViewer(initialColor);
     this.hueSlider_ = new HueSlider(initialColor);
-    visualColorPickerStrip.append(this.eyeDropper_,
-                                  this.colorViewer_,
-                                  this.hueSlider_);
+    visualColorPickerStrip.append(
+        this.eyeDropper_, this.colorViewer_, this.hueSlider_);
     this.append(visualColorPickerStrip);
 
     this.colorWell_ = new ColorWell(initialColor);
@@ -671,7 +672,7 @@
  *             implementation.)
  * TODO(http://crbug.com/992297): Implement eye dropper
  */
-class EyeDropper extends HTMLElement { }
+class EyeDropper extends HTMLElement {}
 window.customElements.define('eye-dropper', EyeDropper);
 
 /**
@@ -716,8 +717,10 @@
     this.append(this.colorPalette_, this.colorSelectionRing_);
     this.initialized_ = false;
 
-    this.colorSelectionRing_.addEventListener('focus', this.onColorSelectionRingFocus_);
-    this.colorSelectionRing_.addEventListener('blur', this.onColorSelectionRingBlur_);
+    this.colorSelectionRing_.addEventListener(
+        'focus', this.onColorSelectionRingFocus_);
+    this.colorSelectionRing_.addEventListener(
+        'blur', this.onColorSelectionRingBlur_);
   }
 
   get initialized() {
@@ -726,11 +729,11 @@
 
   onColorSelectionRingFocus_ = () => {
     this.focused_ = true;
-  }
+  };
 
   onColorSelectionRingBlur_ = () => {
     this.focused_ = false;
-  }
+  };
 
   /**
    * @param {!Point} point
@@ -800,14 +803,15 @@
 
   get hslImageData() {
     if (this.pendingColorChange_) {
-      const rgbaImageData = this.renderingContext
-          .getImageData(0, 0, this.width, this.height).data;
-      this.hslImageData_ = rgbaImageData
-          .reduce((hslArray, {}, currentIndex, rgbaArray) => {
+      const rgbaImageData =
+          this.renderingContext.getImageData(0, 0, this.width, this.height)
+              .data;
+      this.hslImageData_ =
+          rgbaImageData.reduce((hslArray, {}, currentIndex, rgbaArray) => {
             if ((currentIndex % 4) === 0) {
-              hslArray.push(...Color.rgbToHSL(rgbaArray[currentIndex],
-                                              rgbaArray[currentIndex + 1],
-                                              rgbaArray[currentIndex + 2]));
+              hslArray.push(...Color.rgbToHSL(
+                  rgbaArray[currentIndex], rgbaArray[currentIndex + 1],
+                  rgbaArray[currentIndex + 2]));
             }
             return hslArray;
           }, []);
@@ -832,8 +836,9 @@
   colorAtPoint(point) {
     const hslImageDataAtPoint =
         this.hslImageDataAtPoint_(point.x - this.left, point.y - this.top);
-    return new Color(ColorFormat.HSL, hslImageDataAtPoint[0],
-        hslImageDataAtPoint[1], hslImageDataAtPoint[2]);
+    return new Color(
+        ColorFormat.HSL, hslImageDataAtPoint[0], hslImageDataAtPoint[1],
+        hslImageDataAtPoint[2]);
   }
 
   /**
@@ -866,8 +871,9 @@
    * @param {!Color} color
    */
   fillHue(color) {
-    this.fillColor_ = new Color(ColorFormat.HSL, color.hValue,
-        this.fillColor_.sValue, this.fillColor_.lValue);
+    this.fillColor_ = new Color(
+        ColorFormat.HSL, color.hValue, this.fillColor_.sValue,
+        this.fillColor_.lValue);
     this.fillColorAndGradients_();
     this.pendingHueChange_ = true;
   }
@@ -937,9 +943,8 @@
     return Math.ceil(this.getBoundingClientRect().bottom - 1);
   }
 }
-window.customElements.define('color-palette',
-                             ColorPalette,
-                             { extends: 'canvas' });
+window.customElements.define(
+    'color-palette', ColorPalette, {extends: 'canvas'});
 
 /**
  * ColorSelectionRing: Provides movement and color selection functionality to
@@ -1023,20 +1028,20 @@
 
   setElementPosition_() {
     if (this.height > this.backingColorPalette_.height) {
-      this.style.top = this.top
-          - (this.height - this.backingColorPalette_.height) / 2
-          - this.backingColorPalette_.top + 'px';
+      this.style.top = this.top -
+          (this.height - this.backingColorPalette_.height) / 2 -
+          this.backingColorPalette_.top + 'px';
     } else {
-      this.style.top = this.top - this.radius
-          - this.backingColorPalette_.top + 'px';
+      this.style.top =
+          this.top - this.radius - this.backingColorPalette_.top + 'px';
     }
     if (this.width > this.backingColorPalette_.width) {
-      this.style.left = this.left
-          - (this.width - this.backingColorPalette_.width) / 2
-          - this.backingColorPalette_.left + 'px';
+      this.style.left = this.left -
+          (this.width - this.backingColorPalette_.width) / 2 -
+          this.backingColorPalette_.left + 'px';
     } else {
-      this.style.left = this.left - this.radius
-          - this.backingColorPalette_.left + 'px';
+      this.style.left =
+          this.left - this.radius - this.backingColorPalette_.left + 'px';
     }
   }
 
@@ -1072,9 +1077,9 @@
    * @param {bool} accelerated
    */
   move(direction, accelerated) {
-    let shiftFactor = accelerated
-        ? ColorSelectionRing.ACCELERATED_MOVE_DISTANCE
-        : ColorSelectionRing.MOVE_DISTANCE;
+    let shiftFactor = accelerated ?
+        ColorSelectionRing.ACCELERATED_MOVE_DISTANCE :
+        ColorSelectionRing.MOVE_DISTANCE;
     if ((direction === Direction.UP) || (direction === Direction.LEFT)) {
       shiftFactor *= -1;
     }
@@ -1087,13 +1092,13 @@
         }
       } else {
         // direction === Direction.RIGHT
-        if (this.position_.x + shiftFactor >
-            this.backingColorPalette_.right) {
+        if (this.position_.x + shiftFactor > this.backingColorPalette_.right) {
           newX = this.backingColorPalette_.right;
         }
       }
       this.setX(newX);
-    } else if (this.canMoveVertically_ &&
+    } else if (
+        this.canMoveVertically_ &&
         ((direction === Direction.UP) || (direction === Direction.DOWN))) {
       let newY = this.position_.y + shiftFactor;
       if (direction === Direction.UP) {
@@ -1102,8 +1107,7 @@
         }
       } else {
         // direction === Direction.DOWN
-        if (this.position_.y + shiftFactor >
-            this.backingColorPalette_.bottom) {
+        if (this.position_.y + shiftFactor > this.backingColorPalette_.bottom) {
           newY = this.backingColorPalette_.bottom;
         }
       }
@@ -1159,20 +1163,22 @@
     this.selectedColor_ = initialColor;
 
     this.resizeObserver_ = new ResizeObserver(() => {
-      let whiteGradient = this.colorPalette_.renderingContext
-          .createLinearGradient(0, 0, this.colorPalette_.offsetWidth, 0);
+      let whiteGradient =
+          this.colorPalette_.renderingContext.createLinearGradient(
+              0, 0, this.colorPalette_.offsetWidth, 0);
       whiteGradient.addColorStop(0.01, 'hsla(0, 0%, 100%, 1)');
       whiteGradient.addColorStop(0.99, 'hsla(0, 0%, 100%, 0)');
-      let blackGradient = this.colorPalette_.renderingContext
-          .createLinearGradient(0, this.colorPalette_.offsetHeight, 0, 0);
+      let blackGradient =
+          this.colorPalette_.renderingContext.createLinearGradient(
+              0, this.colorPalette_.offsetHeight, 0, 0);
       blackGradient.addColorStop(0.01, 'hsla(0, 0%, 0%, 1)');
       blackGradient.addColorStop(0.99, 'hsla(0, 0%, 0%, 0)');
       this.colorPalette_.initialize(whiteGradient, blackGradient);
       this.colorPalette_.fillHue(this.fillColor_);
       this.colorSelectionRing_.initialize();
 
-      this.colorSelectionRing_.addEventListener('color-selection-ring-update',
-          this.onColorSelectionRingUpdate_);
+      this.colorSelectionRing_.addEventListener(
+          'color-selection-ring-update', this.onColorSelectionRingUpdate_);
 
       this.moveColorSelectionRingTo_(this.selectedColor_);
 
@@ -1194,26 +1200,30 @@
           this.colorPalette_.nearestPointOnColorPalette(newPositionOrColor);
       this.colorSelectionRing_.moveTo(point);
     } else {
-      const closestHSLValueIndex = this.colorPalette_.hslImageData
-          .reduce((closestSoFar, {}, index, array) => {
+      const closestHSLValueIndex = this.colorPalette_.hslImageData.reduce(
+          (closestSoFar, {}, index, array) => {
             if ((index % 3) === 0) {
-              const currentHSLValueDistance = Color.distance([array[index],
-                  array[index + 1], array[index + 2]],
-                    newPositionOrColor.hslValues());
-              const closestHSLValueDistance =
-                  Color.distance([array[closestSoFar], array[closestSoFar + 1],
-                    array[closestSoFar + 2]], newPositionOrColor.hslValues());
+              const currentHSLValueDistance = Color.distance(
+                  [array[index], array[index + 1], array[index + 2]],
+                  newPositionOrColor.hslValues());
+              const closestHSLValueDistance = Color.distance(
+                  [
+                    array[closestSoFar], array[closestSoFar + 1],
+                    array[closestSoFar + 2]
+                  ],
+                  newPositionOrColor.hslValues());
               if (currentHSLValueDistance < closestHSLValueDistance) {
                 return index;
               }
             }
             return closestSoFar;
-          }, 0);
+          },
+          0);
       const offsetX = (closestHSLValueIndex / 3) % this.colorPalette_.width;
       const offsetY =
           Math.floor((closestHSLValueIndex / 3) / this.colorPalette_.width);
-      this.colorSelectionRing_.set(this.colorPalette_.left + offsetX,
-                                  this.colorPalette_.top + offsetY);
+      this.colorSelectionRing_.set(
+          this.colorPalette_.left + offsetX, this.colorPalette_.top + offsetY);
     }
   }
 
@@ -1248,12 +1258,9 @@
 
   onColorSelectionRingUpdate_ = () => {
     this.selectedColor_ = this.colorSelectionRing_.color;
-    this.dispatchEvent(new CustomEvent('visual-color-change', {
-      bubbles: true,
-      detail: {
-        color: this.selectedColor
-      }
-    }));
+    this.dispatchEvent(new CustomEvent(
+        'visual-color-change',
+        {bubbles: true, detail: {color: this.selectedColor}}));
   }
 }
 window.customElements.define('color-well', ColorWell);
@@ -1271,8 +1278,9 @@
     this.color_ = new Color(ColorFormat.HSL, initialColor.hValue, 100, 50);
 
     this.resizeObserver_ = new ResizeObserver(() => {
-      let hueSliderPaletteGradient = this.colorPalette_.renderingContext
-          .createLinearGradient(0, 0, this.colorPalette_.offsetWidth, 0);
+      let hueSliderPaletteGradient =
+          this.colorPalette_.renderingContext.createLinearGradient(
+              0, 0, this.colorPalette_.offsetWidth, 0);
       hueSliderPaletteGradient.addColorStop(0.01, 'hsl(0, 100%, 50%)');
       hueSliderPaletteGradient.addColorStop(0.17, 'hsl(300, 100%, 50%)');
       hueSliderPaletteGradient.addColorStop(0.33, 'hsl(240, 100%, 50%)');
@@ -1283,8 +1291,8 @@
       this.colorPalette_.initialize(hueSliderPaletteGradient);
       this.colorSelectionRing_.initialize();
 
-      this.colorSelectionRing_.addEventListener('color-selection-ring-update',
-          this.onColorSelectionRingUpdate_);
+      this.colorSelectionRing_.addEventListener(
+          'color-selection-ring-update', this.onColorSelectionRingUpdate_);
 
       this.moveColorSelectionRingTo_(this.color_);
 
@@ -1308,15 +1316,16 @@
     } else {
       const targetHValue = newPositionOrColor.hValue;
       if (targetHValue !== this.colorSelectionRing_.color.hValue) {
-        const closestHValueIndex = this.colorPalette_.hslImageData
-            .reduce((closestHValueIndexSoFar, currentHValue, index, array) => {
+        const closestHValueIndex = this.colorPalette_.hslImageData.reduce(
+            (closestHValueIndexSoFar, currentHValue, index, array) => {
               if ((index % 3 === 0) &&
                   (Math.abs(currentHValue - targetHValue) <
-                    Math.abs(array[closestHValueIndexSoFar] - targetHValue))) {
+                   Math.abs(array[closestHValueIndexSoFar] - targetHValue))) {
                 return index;
               }
               return closestHValueIndexSoFar;
-        }, 0);
+            },
+            0);
         const offsetX = (closestHValueIndex / 3) % this.colorPalette_.width;
         this.colorSelectionRing_.setX(this.colorPalette_.left + offsetX);
       }
@@ -1339,9 +1348,7 @@
 
   onColorSelectionRingUpdate_ = () => {
     this.color_ = this.colorSelectionRing_.color;
-    this.dispatchEvent(new CustomEvent('hue-slider-update', {
-      bubbles: true
-    }));
+    this.dispatchEvent(new CustomEvent('hue-slider-update', {bubbles: true}));
   }
 }
 window.customElements.define('hue-slider', HueSlider);
@@ -1357,12 +1364,12 @@
   constructor(initialColor) {
     super();
 
-    this.hexValueContainer_ = new ColorValueContainer(ColorChannel.HEX,
-                                                      initialColor);
-    this.rgbValueContainer_ = new ColorValueContainer(ColorFormat.RGB,
-                                                      initialColor);
-    this.hslValueContainer_ = new ColorValueContainer(ColorFormat.HSL,
-                                                      initialColor);
+    this.hexValueContainer_ =
+        new ColorValueContainer(ColorChannel.HEX, initialColor);
+    this.rgbValueContainer_ =
+        new ColorValueContainer(ColorFormat.RGB, initialColor);
+    this.hslValueContainer_ =
+        new ColorValueContainer(ColorFormat.HSL, initialColor);
     this.colorValueContainers_ = [
       this.hexValueContainer_,
       this.rgbValueContainer_,
@@ -1373,8 +1380,7 @@
     this.formatToggler_ = new FormatToggler(this.currentColorFormat_);
     this.append(...this.colorValueContainers_, this.formatToggler_);
 
-    this.formatToggler_
-    .addEventListener('format-change', this.onFormatChange_);
+    this.formatToggler_.addEventListener('format-change', this.onFormatChange_);
 
     this.addEventListener('manual-color-change', this.onManualColorChange_);
   }
@@ -1408,8 +1414,8 @@
    * @param {!Color} newColor
    */
   set color(newColor) {
-    this.colorValueContainers_.forEach((colorValueContainer) =>
-        colorValueContainer.color = newColor);
+    this.colorValueContainers_.forEach(
+        (colorValueContainer) => colorValueContainer.color = newColor);
   }
 }
 window.customElements.define('manual-color-picker', ManualColorPicker);
@@ -1429,35 +1435,33 @@
     this.colorFormat_ = colorFormat;
     this.channelValueContainers_ = [];
     if (this.colorFormat_ === ColorFormat.HEX) {
-      const hexValueContainer = new ChannelValueContainer(ColorChannel.HEX,
-                                                          initialColor);
+      const hexValueContainer =
+          new ChannelValueContainer(ColorChannel.HEX, initialColor);
       this.channelValueContainers_.push(hexValueContainer);
     } else if (this.colorFormat_ === ColorFormat.RGB) {
-      const rValueContainer = new ChannelValueContainer(ColorChannel.R,
-                                                        initialColor);
-      const gValueContainer = new ChannelValueContainer(ColorChannel.G,
-                                                        initialColor);
-      const bValueContainer = new ChannelValueContainer(ColorChannel.B,
-                                                        initialColor);
-      this.channelValueContainers_.push(rValueContainer,
-                                        gValueContainer,
-                                        bValueContainer);
+      const rValueContainer =
+          new ChannelValueContainer(ColorChannel.R, initialColor);
+      const gValueContainer =
+          new ChannelValueContainer(ColorChannel.G, initialColor);
+      const bValueContainer =
+          new ChannelValueContainer(ColorChannel.B, initialColor);
+      this.channelValueContainers_.push(
+          rValueContainer, gValueContainer, bValueContainer);
     } else if (this.colorFormat_ === ColorFormat.HSL) {
-      const hValueContainer = new ChannelValueContainer(ColorChannel.H,
-                                                        initialColor);
-      const sValueContainer = new ChannelValueContainer(ColorChannel.S,
-                                                        initialColor);
-      const lValueContainer = new ChannelValueContainer(ColorChannel.L,
-                                                        initialColor);
-      this.channelValueContainers_.push(hValueContainer,
-                                        sValueContainer,
-                                        lValueContainer);
+      const hValueContainer =
+          new ChannelValueContainer(ColorChannel.H, initialColor);
+      const sValueContainer =
+          new ChannelValueContainer(ColorChannel.S, initialColor);
+      const lValueContainer =
+          new ChannelValueContainer(ColorChannel.L, initialColor);
+      this.channelValueContainers_.push(
+          hValueContainer, sValueContainer, lValueContainer);
     }
     this.append(...this.channelValueContainers_);
 
-    this.channelValueContainers_.forEach((channelValueContainer) =>
-        channelValueContainer.addEventListener('input',
-            this.onChannelValueChange_));
+    this.channelValueContainers_.forEach(
+        (channelValueContainer) => channelValueContainer.addEventListener(
+            'input', this.onChannelValueChange_));
   }
 
   get colorFormat() {
@@ -1465,17 +1469,18 @@
   }
 
   get color() {
-    return new Color(this.colorFormat_,
-        ...this.channelValueContainers_.map((channelValueContainer) =>
-            channelValueContainer.channelValue));
+    return new Color(
+        this.colorFormat_,
+        ...this.channelValueContainers_.map(
+            (channelValueContainer) => channelValueContainer.channelValue));
   }
 
   /**
    * @param {!Color} color
    */
   set color(color) {
-    this.channelValueContainers_.forEach((channelValueContainer) =>
-        channelValueContainer.setValue(color));
+    this.channelValueContainers_.forEach(
+        (channelValueContainer) => channelValueContainer.setValue(color));
   }
 
   show() {
@@ -1487,12 +1492,8 @@
   }
 
   onChannelValueChange_ = () => {
-    this.dispatchEvent(new CustomEvent('manual-color-change', {
-      bubbles: true,
-      detail: {
-        color: this.color
-      }
-    }));
+    this.dispatchEvent(new CustomEvent(
+        'manual-color-change', {bubbles: true, detail: {color: this.color}}));
   }
 }
 window.customElements.define('color-value-container', ColorValueContainer);
@@ -1511,7 +1512,7 @@
 
     this.setAttribute('type', 'text');
     this.colorChannel_ = colorChannel;
-    switch(colorChannel) {
+    switch (colorChannel) {
       case ColorChannel.HEX:
         this.setAttribute('id', 'hexValueContainer');
         this.setAttribute('maxlength', '7');
@@ -1556,7 +1557,7 @@
    * @param {!Color} color
    */
   setValue(color) {
-    switch(this.colorChannel_) {
+    switch (this.colorChannel_) {
       case ColorChannel.HEX:
         if (this.channelValue_ !== color.hexValue) {
           this.channelValue_ = color.hexValue;
@@ -1606,7 +1607,7 @@
     // Set this.channelValue_ based on the element's new value.
     let value = this.value;
     if (value) {
-      switch(this.colorChannel_) {
+      switch (this.colorChannel_) {
         case ColorChannel.HEX:
           if (value.startsWith('#')) {
             value = value.substr(1).toLowerCase();
@@ -1642,9 +1643,8 @@
     }
   }
 }
-window.customElements.define('channel-value-container',
-                             ChannelValueContainer,
-                             { extends: 'input' });
+window.customElements.define(
+    'channel-value-container', ChannelValueContainer, {extends: 'input'});
 
 /**
  * FormatToggler: Button that powers switching between different color formats.
@@ -1690,9 +1690,8 @@
    */
   updateColorFormat_(choosePreviousFormat) {
     const numFormats = Object.keys(ColorFormat).length;
-    const newValue = choosePreviousFormat
-        ? this.currentColorFormat_ - 1
-        : this.currentColorFormat_ + 1;
+    const newValue = choosePreviousFormat ? this.currentColorFormat_ - 1 :
+                                            this.currentColorFormat_ + 1;
     const newColorFormatKey = Object.keys(ColorFormat).filter((key) => {
       return ColorFormat[key] ===
           (((newValue % numFormats) + numFormats) % numFormats);
@@ -1701,11 +1700,8 @@
 
     this.adjustFormatLabelVisibility_();
 
-    this.dispatchEvent(new CustomEvent('format-change', {
-      detail: {
-        colorFormat: this.currentColorFormat_
-      }
-    }));
+    this.dispatchEvent(new CustomEvent(
+        'format-change', {detail: {colorFormat: this.currentColorFormat_}}));
   }
 
   adjustFormatLabelVisibility_() {
@@ -1721,13 +1717,13 @@
   onClick_ = () => {
     this.focus();
     this.updateColorFormat_(false);
-  }
+  };
 
   /**
    * @param {!Event} event
    */
   onKeyDown_ = (event) => {
-    switch(event.key) {
+    switch (event.key) {
       case 'ArrowUp':
         this.updateColorFormat_(true);
         break;
@@ -1758,16 +1754,14 @@
       this.rChannelLabel_ = new ChannelLabel(ColorChannel.R);
       this.gChannelLabel_ = new ChannelLabel(ColorChannel.G);
       this.bChannelLabel_ = new ChannelLabel(ColorChannel.B);
-      this.append(this.rChannelLabel_,
-                  this.gChannelLabel_,
-                  this.bChannelLabel_);
+      this.append(
+          this.rChannelLabel_, this.gChannelLabel_, this.bChannelLabel_);
     } else if (colorFormat === ColorFormat.HSL) {
       this.hChannelLabel_ = new ChannelLabel(ColorChannel.H);
       this.sChannelLabel_ = new ChannelLabel(ColorChannel.S);
       this.lChannelLabel_ = new ChannelLabel(ColorChannel.L);
-      this.append(this.hChannelLabel_,
-                  this.sChannelLabel_,
-                  this.lChannelLabel_);
+      this.append(
+          this.hChannelLabel_, this.sChannelLabel_, this.lChannelLabel_);
     }
   }
 
@@ -1829,20 +1823,20 @@
     padding.setAttribute('id', 'submission-controls-padding');
     this.append(padding);
 
-    this.submitButton_ = new SubmissionButton(submitCallback,
+    this.submitButton_ = new SubmissionButton(
+        submitCallback,
         '<svg width="14" height="10" viewBox="0 0 14 10" fill="none" ' +
-        'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' +
-        '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' +
-        '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="WindowText"/></svg>'
-    );
-    this.cancelButton_ = new SubmissionButton(cancelCallback,
+            'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' +
+            '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' +
+            '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="WindowText"/></svg>');
+    this.cancelButton_ = new SubmissionButton(
+        cancelCallback,
         '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" ' +
-        'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' +
-        '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' +
-        '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' +
-        '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' +
-        'fill="WindowText"/></svg>'
-    );
+            'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' +
+            '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' +
+            '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' +
+            '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' +
+            'fill="WindowText"/></svg>');
     this.append(this.submitButton_, this.cancelButton_);
   }
 
@@ -1874,4 +1868,4 @@
     this.addEventListener('click', clickCallback);
   }
 }
-window.customElements.define('submission-button', SubmissionButton);
\ No newline at end of file
+window.customElements.define('submission-button', SubmissionButton);
diff --git a/third_party/blink/renderer/core/html/forms/resources/listPicker.js b/third_party/blink/renderer/core/html/forms/resources/listPicker.js
index 59af8767..8294f0b 100644
--- a/third_party/blink/renderer/core/html/forms/resources/listPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/listPicker.js
@@ -44,16 +44,24 @@
   this._delayedChildrenConfig = null;
   this._delayedChildrenConfigIndex = 0;
   this._layout();
-  this._selectElement.addEventListener('mouseup', this._handleMouseUp.bind(this), false);
-  this._selectElement.addEventListener('touchstart', this._handleTouchStart.bind(this), false);
-  this._selectElement.addEventListener('keydown', this._handleKeyDown.bind(this), false);
-  this._selectElement.addEventListener('change', this._handleChange.bind(this), false);
-  window.addEventListener('message', this._handleWindowMessage.bind(this), false);
-  window.addEventListener('mousemove', this._handleWindowMouseMove.bind(this), false);
-  window.addEventListener('mouseover', this._handleWindowMouseOver.bind(this), false);
+  this._selectElement.addEventListener(
+      'mouseup', this._handleMouseUp.bind(this), false);
+  this._selectElement.addEventListener(
+      'touchstart', this._handleTouchStart.bind(this), false);
+  this._selectElement.addEventListener(
+      'keydown', this._handleKeyDown.bind(this), false);
+  this._selectElement.addEventListener(
+      'change', this._handleChange.bind(this), false);
+  window.addEventListener(
+      'message', this._handleWindowMessage.bind(this), false);
+  window.addEventListener(
+      'mousemove', this._handleWindowMouseMove.bind(this), false);
+  window.addEventListener(
+      'mouseover', this._handleWindowMouseOver.bind(this), false);
   this._handleWindowTouchMoveBound = this._handleWindowTouchMove.bind(this);
   this._handleWindowTouchEndBound = this._handleWindowTouchEnd.bind(this);
-  this._handleTouchSelectModeScrollBound = this._handleTouchSelectModeScroll.bind(this);
+  this._handleTouchSelectModeScrollBound =
+      this._handleTouchSelectModeScroll.bind(this);
   this.lastMousePositionX = Infinity;
   this.lastMousePositionY = Infinity;
   this._selectionSetByMouseHover = false;
@@ -68,7 +76,8 @@
 
 ListPicker.prototype._handleWindowDidHide = function() {
   this._fixWindowSize();
-  var selectedOption = this._selectElement.options[this._selectElement.selectedIndex];
+  var selectedOption =
+      this._selectElement.options[this._selectElement.selectedIndex];
   if (selectedOption)
     selectedOption.scrollIntoView(false);
   window.removeEventListener('didHide', this._handleWindowDidHideBound, false);
@@ -80,10 +89,14 @@
     this._config.baseStyle = window.updateData.baseStyle;
     this._config.children = window.updateData.children;
     this._update();
-    if (this._config.anchorRectInScreen.x !== window.updateData.anchorRectInScreen.x ||
-        this._config.anchorRectInScreen.y !== window.updateData.anchorRectInScreen.y ||
-        this._config.anchorRectInScreen.width !== window.updateData.anchorRectInScreen.width ||
-        this._config.anchorRectInScreen.height !== window.updateData.anchorRectInScreen.height) {
+    if (this._config.anchorRectInScreen.x !==
+            window.updateData.anchorRectInScreen.x ||
+        this._config.anchorRectInScreen.y !==
+            window.updateData.anchorRectInScreen.y ||
+        this._config.anchorRectInScreen.width !==
+            window.updateData.anchorRectInScreen.width ||
+        this._config.anchorRectInScreen.height !==
+            window.updateData.anchorRectInScreen.height) {
       // TODO(tkent): Don't fix window size here due to a bug of Aura or
       // compositor. crbug.com/863770
       if (!navigator.platform.startsWith('Win')) {
@@ -101,7 +114,8 @@
 
 ListPicker.prototype._handleWindowMouseMove = function(event) {
   var visibleTop = ListPicker.ListboxSelectBorder;
-  var visibleBottom = this._selectElement.offsetHeight - ListPicker.ListboxSelectBorder;
+  var visibleBottom =
+      this._selectElement.offsetHeight - ListPicker.ListboxSelectBorder;
   var optionBounds = event.target.getBoundingClientRect();
   if (optionBounds.height >= 1.0) {
     // If the height of the visible part of event.target is less than 1px,
@@ -128,7 +142,8 @@
 ListPicker.prototype._handleMouseUp = function(event) {
   if (event.target.tagName !== 'OPTION')
     return;
-  window.pagePopupController.setValueAndClosePopup(0, this._selectElement.value);
+  window.pagePopupController.setValueAndClosePopup(
+      0, this._selectElement.value);
 };
 
 ListPicker.prototype._handleTouchStart = function(event) {
@@ -140,7 +155,8 @@
   this._trackingTouchId = touch.identifier;
   this._highlightOption(touch.target);
   this._selectionSetByMouseHover = false;
-  this._selectElement.addEventListener('scroll', this._handleTouchSelectModeScrollBound, false);
+  this._selectElement.addEventListener(
+      'scroll', this._handleTouchSelectModeScrollBound, false);
   window.addEventListener('touchmove', this._handleWindowTouchMoveBound, false);
   window.addEventListener('touchend', this._handleWindowTouchEndBound, false);
 };
@@ -151,9 +167,12 @@
 
 ListPicker.prototype._exitTouchSelectMode = function(event) {
   this._trackingTouchId = null;
-  this._selectElement.removeEventListener('scroll', this._handleTouchSelectModeScrollBound, false);
-  window.removeEventListener('touchmove', this._handleWindowTouchMoveBound, false);
-  window.removeEventListener('touchend', this._handleWindowTouchEndBound, false);
+  this._selectElement.removeEventListener(
+      'scroll', this._handleTouchSelectModeScrollBound, false);
+  window.removeEventListener(
+      'touchmove', this._handleWindowTouchMoveBound, false);
+  window.removeEventListener(
+      'touchend', this._handleWindowTouchEndBound, false);
 };
 
 ListPicker.prototype._handleWindowTouchMove = function(event) {
@@ -162,7 +181,8 @@
   var touch = this._getTouchForId(event.touches, this._trackingTouchId);
   if (!touch)
     return;
-  this._highlightOption(document.elementFromPoint(touch.clientX, touch.clientY));
+  this._highlightOption(
+      document.elementFromPoint(touch.clientX, touch.clientY));
   this._selectionSetByMouseHover = false;
 };
 
@@ -174,7 +194,8 @@
     return;
   var target = document.elementFromPoint(touch.clientX, touch.clientY);
   if (target.tagName === 'OPTION' && !target.disabled)
-    window.pagePopupController.setValueAndClosePopup(0, this._selectElement.value);
+    window.pagePopupController.setValueAndClosePopup(
+        0, this._selectElement.value);
   this._exitTouchSelectMode();
 };
 
@@ -207,7 +228,8 @@
     window.pagePopupController.closePopup();
     event.preventDefault();
   } else if (key === 'Tab' || key === 'Enter') {
-    window.pagePopupController.setValueAndClosePopup(0, this._selectElement.value);
+    window.pagePopupController.setValueAndClosePopup(
+        0, this._selectElement.value);
     event.preventDefault();
   } else if (event.altKey && (key === 'ArrowDown' || key === 'ArrowUp')) {
     // We need to add a delay here because, if we do it immediately the key
@@ -224,7 +246,8 @@
   this._selectElement.style.height = '';
   var scale = this._config.scaleFactor;
   var maxHeight = this._selectElement.offsetHeight;
-  var noScrollHeight = (this._calculateScrollHeight() + ListPicker.ListboxSelectBorder * 2);
+  var noScrollHeight =
+      (this._calculateScrollHeight() + ListPicker.ListboxSelectBorder * 2);
   var scrollbarWidth = getScrollbarWidth();
   var elementOffsetWidth = this._selectElement.offsetWidth;
   var desiredWindowHeight = noScrollHeight;
@@ -241,13 +264,17 @@
     expectingScrollbar = true;
   }
   // Screen coordinate for anchorRectInScreen and windowRect is DIP.
-  desiredWindowWidth = Math.max(this._config.anchorRectInScreen.width * scale, desiredWindowWidth);
-  var windowRect =
-      adjustWindowRect(desiredWindowWidth / scale, desiredWindowHeight / scale, elementOffsetWidth / scale, 0);
+  desiredWindowWidth = Math.max(
+      this._config.anchorRectInScreen.width * scale, desiredWindowWidth);
+  var windowRect = adjustWindowRect(
+      desiredWindowWidth / scale, desiredWindowHeight / scale,
+      elementOffsetWidth / scale, 0);
   // If the available screen space is smaller than maxHeight, we will get an unexpected scrollbar.
   if (!expectingScrollbar && windowRect.height < noScrollHeight / scale) {
     desiredWindowWidth = windowRect.width * scale + scrollbarWidth;
-    windowRect = adjustWindowRect(desiredWindowWidth / scale, windowRect.height, windowRect.width, windowRect.height);
+    windowRect = adjustWindowRect(
+        desiredWindowWidth / scale, windowRect.height, windowRect.width,
+        windowRect.height);
   }
   this._selectElement.style.width = (windowRect.width * scale) + 'px';
   this._selectElement.style.height = (windowRect.height * scale) + 'px';
@@ -279,11 +306,14 @@
 ListPicker.prototype._layout = function() {
   if (this._config.isRTL)
     this._element.classList.add('rtl');
-  this._selectElement.style.backgroundColor = this._config.baseStyle.backgroundColor;
+  this._selectElement.style.backgroundColor =
+      this._config.baseStyle.backgroundColor;
   this._selectElement.style.color = this._config.baseStyle.color;
-  this._selectElement.style.textTransform = this._config.baseStyle.textTransform;
+  this._selectElement.style.textTransform =
+      this._config.baseStyle.textTransform;
   this._selectElement.style.fontSize = this._config.baseStyle.fontSize + 'px';
-  this._selectElement.style.fontFamily = this._config.baseStyle.fontFamily.map(s => '"' + s + '"').join(',');
+  this._selectElement.style.fontFamily =
+      this._config.baseStyle.fontFamily.map(s => '"' + s + '"').join(',');
   this._selectElement.style.fontStyle = this._config.baseStyle.fontStyle;
   this._selectElement.style.fontVariant = this._config.baseStyle.fontVariant;
   this._updateChildren(this._selectElement, this._config);
@@ -297,7 +327,8 @@
   this._selectElement.scrollTop = scrollPosition;
   var optionUnderMouse = null;
   if (this._selectionSetByMouseHover) {
-    var elementUnderMouse = document.elementFromPoint(this.lastMousePositionX, this.lastMousePositionY);
+    var elementUnderMouse = document.elementFromPoint(
+        this.lastMousePositionX, this.lastMousePositionY);
     optionUnderMouse = elementUnderMouse && elementUnderMouse.closest('option');
   }
   if (optionUnderMouse)
@@ -319,13 +350,15 @@
   var fragment = null;
   var inGroup = parent.tagName === 'OPTGROUP';
   var lastListIndex = -1;
-  var limit = Math.max(this._config.selectedIndex, ListPicker.DelayedLayoutThreshold);
+  var limit =
+      Math.max(this._config.selectedIndex, ListPicker.DelayedLayoutThreshold);
   var i;
   for (i = 0; i < config.children.length; ++i) {
     if (!inGroup && lastListIndex >= limit)
       break;
     var childConfig = config.children[i];
-    var item = this._findReusableItem(parent, childConfig, outOfDateIndex) || this._createItemElement(childConfig);
+    var item = this._findReusableItem(parent, childConfig, outOfDateIndex) ||
+        this._createItemElement(childConfig);
     this._configureItem(item, childConfig, inGroup);
     lastListIndex = item.value ? Number(item.value) : -1;
     if (outOfDateIndex < parent.children.length) {
@@ -361,8 +394,10 @@
     return;
   var fragment = document.createDocumentFragment();
   var startIndex = this._delayedChildrenConfigIndex;
-  for (; this._delayedChildrenConfigIndex < this._delayedChildrenConfig.length; ++this._delayedChildrenConfigIndex) {
-    var childConfig = this._delayedChildrenConfig[this._delayedChildrenConfigIndex];
+  for (; this._delayedChildrenConfigIndex < this._delayedChildrenConfig.length;
+       ++this._delayedChildrenConfigIndex) {
+    var childConfig =
+        this._delayedChildrenConfig[this._delayedChildrenConfigIndex];
     var item = this._createItemElement(childConfig);
     this._configureItem(item, childConfig, false);
     fragment.appendChild(item);
@@ -409,13 +444,17 @@
   style.direction = styleConfig.direction ? styleConfig.direction : '';
   style.unicodeBidi = styleConfig.unicodeBidi ? styleConfig.unicodeBidi : '';
   style.color = styleConfig.color ? styleConfig.color : '';
-  style.backgroundColor = styleConfig.backgroundColor ? styleConfig.backgroundColor : '';
+  style.backgroundColor =
+      styleConfig.backgroundColor ? styleConfig.backgroundColor : '';
   style.fontSize = styleConfig.fontSize ? styleConfig.fontSize + 'px' : '';
   style.fontWeight = styleConfig.fontWeight ? styleConfig.fontWeight : '';
-  style.fontFamily = styleConfig.fontFamily ? styleConfig.fontFamily.map(s => '"' + s + '"').join(',') : '';
+  style.fontFamily = styleConfig.fontFamily ?
+      styleConfig.fontFamily.map(s => '"' + s + '"').join(',') :
+      '';
   style.fontStyle = styleConfig.fontStyle ? styleConfig.fontStyle : '';
   style.fontVariant = styleConfig.fontVariant ? styleConfig.fontVariant : '';
-  style.textTransform = styleConfig.textTransform ? styleConfig.textTransform : '';
+  style.textTransform =
+      styleConfig.textTransform ? styleConfig.textTransform : '';
 };
 
 ListPicker.prototype._configureItem = function(element, config, inGroup) {
diff --git a/third_party/blink/renderer/core/html/forms/resources/month_picker.js b/third_party/blink/renderer/core/html/forms/resources/month_picker.js
index bd001c8..47cf5ee 100644
--- a/third_party/blink/renderer/core/html/forms/resources/month_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/month_picker.js
@@ -28,7 +28,8 @@
 
     this.initializeFromConfig_(config);
 
-    this.yearListView_ = new YearListView(this.minimumMonth_, this.maximumMonth_);
+    this.yearListView_ =
+        new YearListView(this.minimumMonth_, this.maximumMonth_);
     this.append(this.yearListView_.element);
     this.initializeYearListView_();
 
@@ -40,14 +41,19 @@
   }
 
   initializeFromConfig_ = (config) => {
-    const minimum =(typeof config.min !== 'undefined' && config.min) ? parseDateString(config.min) : Month.Minimum;
-    const maximum = (typeof config.max !== 'undefined' && config.max) ? parseDateString(config.max) : Month.Maximum;
+    const minimum = (typeof config.min !== 'undefined' && config.min) ?
+        parseDateString(config.min) :
+        Month.Minimum;
+    const maximum = (typeof config.max !== 'undefined' && config.max) ?
+        parseDateString(config.max) :
+        Month.Maximum;
     this.minimumMonth_ = Month.createFromDay(minimum.firstDay());
     this.maximumMonth_ = Month.createFromDay(maximum.lastDay());
 
     const initialSelection = parseDateString(config.currentValue);
-    const initialSelectedMonth = initialSelection ? Month.createFromDay(initialSelection.middleDay())
-                                           : Month.createFromToday();
+    const initialSelectedMonth = initialSelection ?
+        Month.createFromDay(initialSelection.middleDay()) :
+        Month.createFromToday();
     this.initialValidSelection_ = false;
     if (initialSelectedMonth < this.minimumMonth_) {
       this.selectedMonth_ = this.minimumMonth_;
@@ -57,60 +63,68 @@
       this.selectedMonth_ = initialSelectedMonth;
       this.initialValidSelection_ = initialSelection != null;
     }
-  }
+  };
 
   initializeYearListView_ = () => {
     this.yearListView_.setWidth(MonthPicker.YearWidth);
     this.yearListView_.setHeight(MonthPicker.YearHeight);
     if (global.params.isLocaleRTL) {
       this.yearListView_.element.style.right = MonthPicker.YearPadding + 'px';
-      this.yearListView_.scrubbyScrollBar.element.style.right = MonthPicker.YearWidth + 'px';
+      this.yearListView_.scrubbyScrollBar.element.style.right =
+          MonthPicker.YearWidth + 'px';
     } else {
       this.yearListView_.element.style.left = MonthPicker.YearPadding + 'px';
-      this.yearListView_.scrubbyScrollBar.element.style.left = MonthPicker.YearWidth + 'px';
+      this.yearListView_.scrubbyScrollBar.element.style.left =
+          MonthPicker.YearWidth + 'px';
     }
     this.yearListView_.element.style.top = MonthPicker.YearPadding + 'px';
     if (this.initialValidSelection_) {
       this.yearListView_.setSelectedMonth(this.selectedMonth_);
     }
-    this.yearListView_.show(this.selectedMonth_)
-    this.yearListView_.on(YearListView.EventTypeYearListViewDidSelectMonth, this.onYearListViewDidSelectMonth_);
-    this.yearListView_.on(YearListView.EventTypeYearListViewDidHide, this.onYearListViewDidHide_);
-  }
+    this.yearListView_.show(this.selectedMonth_);
+    this.yearListView_.on(
+        YearListView.EventTypeYearListViewDidSelectMonth,
+        this.onYearListViewDidSelectMonth_);
+    this.yearListView_.on(
+        YearListView.EventTypeYearListViewDidHide, this.onYearListViewDidHide_);
+  };
 
   onYearListViewDidHide_ = (sender) => {
     const selectedValue = this.selectedMonth_.toString();
     window.setTimeout(function() {
       window.pagePopupController.setValueAndClosePopup(0, selectedValue);
     }, 100);
-  }
+  };
 
   onYearListViewDidSelectMonth_ = (sender, month) => {
     this.selectedMonth_ = month;
-  }
+  };
 
   initializeTodayButton_ = () => {
     this.todayButton_.element.textContent = global.params.todayLabel;
-    this.todayButton_.element.setAttribute('aria-label', global.params.todayLabel)
+    this.todayButton_.element.setAttribute(
+        'aria-label', global.params.todayLabel);
     this.todayButton_.element.classList.add(MonthPicker.ClassNameTodayButton);
     const monthContainingToday = Month.createFromToday();
     this.todayButton_.setDisabled(
         monthContainingToday < this.minimumMonth_ ||
         monthContainingToday > this.maximumMonth_);
-    this.todayButton_.on(CalendarNavigationButton.EventTypeButtonClick, this.onTodayButtonClick_);
-  }
+    this.todayButton_.on(
+        CalendarNavigationButton.EventTypeButtonClick,
+        this.onTodayButtonClick_);
+  };
 
   onTodayButtonClick_ = (sender) => {
     const selectedValue = Month.createFromToday().toString();
     window.setTimeout(function() {
       window.pagePopupController.setValueAndClosePopup(0, selectedValue);
     }, 100);
-  }
+  };
 
   onWindowResize_ = (event) => {
     window.removeEventListener('resize', this.onWindowResize_);
     this.yearListView_.element.focus();
-  }
+  };
 }
 MonthPicker.Width = 232;
 MonthPicker.YearWidth = 194;
@@ -118,4 +132,4 @@
 MonthPicker.YearPadding = 12;
 MonthPicker.Height = 182;
 MonthPicker.ClassNameTodayButton = 'today-button-refresh';
-window.customElements.define('month-picker', MonthPicker);
\ No newline at end of file
+window.customElements.define('month-picker', MonthPicker);
diff --git a/third_party/blink/renderer/core/html/forms/resources/pickerCommon.js b/third_party/blink/renderer/core/html/forms/resources/pickerCommon.js
index b5071055..ae3504d 100644
--- a/third_party/blink/renderer/core/html/forms/resources/pickerCommon.js
+++ b/third_party/blink/renderer/core/html/forms/resources/pickerCommon.js
@@ -74,7 +74,8 @@
     return this.y + this.height;
   },
   toString: function() {
-    return 'Rectangle(' + this.x + ',' + this.y + ',' + this.width + ',' + this.height + ')';
+    return 'Rectangle(' + this.x + ',' + this.y + ',' + this.width + ',' +
+        this.height + ')';
   }
 };
 
@@ -101,7 +102,8 @@
  */
 function resizeWindow(width, height) {
   var zoom = global.params.zoomFactor ? global.params.zoomFactor : 1;
-  setWindowRect(adjustWindowRect(width * zoom, height * zoom, width * zoom, height * zoom));
+  setWindowRect(adjustWindowRect(
+      width * zoom, height * zoom, width * zoom, height * zoom));
 }
 
 /**
@@ -124,7 +126,8 @@
 
   var anchorRect = new Rectangle(global.params.anchorRectInScreen);
   var availRect = new Rectangle(
-      window.screen.availLeft, window.screen.availTop, window.screen.availWidth, window.screen.availHeight);
+      window.screen.availLeft, window.screen.availTop, window.screen.availWidth,
+      window.screen.availHeight);
 
   _adjustWindowRectVertically(windowRect, availRect, anchorRect, minHeight);
   _adjustWindowRectHorizontally(windowRect, availRect, anchorRect, minWidth);
@@ -135,12 +138,15 @@
 /**
  * Arguments are DIPs.
  */
-function _adjustWindowRectVertically(windowRect, availRect, anchorRect, minHeight) {
+function _adjustWindowRectVertically(
+    windowRect, availRect, anchorRect, minHeight) {
   var availableSpaceAbove = anchorRect.y - availRect.y;
-  availableSpaceAbove = Math.max(0, Math.min(availRect.height, availableSpaceAbove));
+  availableSpaceAbove =
+      Math.max(0, Math.min(availRect.height, availableSpaceAbove));
 
   var availableSpaceBelow = availRect.maxY - anchorRect.maxY;
-  availableSpaceBelow = Math.max(0, Math.min(availRect.height, availableSpaceBelow));
+  availableSpaceBelow =
+      Math.max(0, Math.min(availRect.height, availableSpaceBelow));
 
   // In some situations, there may be no space available.  This can happen on
   // Linux when using a buggy window manager (https://crbug.com/774232).  When
@@ -148,7 +154,9 @@
   if (availableSpaceAbove == 0 && availableSpaceBelow == 0) {
     windowRect.height = Math.max(minHeight, windowRect.height);
     windowRect.y = anchorRect.maxY;
-  } else if (windowRect.height > availableSpaceBelow && availableSpaceBelow < availableSpaceAbove) {
+  } else if (
+      windowRect.height > availableSpaceBelow &&
+      availableSpaceBelow < availableSpaceAbove) {
     windowRect.height = Math.min(windowRect.height, availableSpaceAbove);
     windowRect.height = Math.max(windowRect.height, minHeight);
     windowRect.y = anchorRect.y - windowRect.height;
@@ -162,7 +170,8 @@
 /**
  * Arguments are DIPs.
  */
-function _adjustWindowRectHorizontally(windowRect, availRect, anchorRect, minWidth) {
+function _adjustWindowRectHorizontally(
+    windowRect, availRect, anchorRect, minWidth) {
   if (anchorRect.maxX <= availRect.x || availRect.maxX <= anchorRect.x) {
     windowRect.width = Math.max(minWidth, windowRect.width);
     windowRect.x = anchorRect.x
@@ -174,7 +183,8 @@
   // If we are getting clipped, we want to switch alignment to the right side
   // of the anchor rect as long as doing so will make the popup not clipped.
   var rightAlignedX = windowRect.x + anchorRect.width - windowRect.width;
-  if (rightAlignedX >= availRect.x && (windowRect.maxX > availRect.maxX || global.params.isRTL))
+  if (rightAlignedX >= availRect.x &&
+      (windowRect.maxX > availRect.maxX || global.params.isRTL))
     windowRect.x = rightAlignedX;
 }
 
@@ -186,7 +196,8 @@
     window.frameElement.style.width = rect.width + 'px';
     window.frameElement.style.height = rect.height + 'px';
   } else {
-    window.pagePopupController.setWindowRect(rect.x, rect.y, rect.width, rect.height);
+    window.pagePopupController.setWindowRect(
+        rect.x, rect.y, rect.width, rect.height);
   }
 }
 
@@ -233,8 +244,10 @@
  * @return {?Element}
  */
 function enclosingNodeOrSelfWithClass(selfNode, className) {
-  for (var node = selfNode; node && node !== selfNode.ownerDocument; node = node.parentNode) {
-    if (node.nodeType === Node.ELEMENT_NODE && node.classList.contains(className))
+  for (var node = selfNode; node && node !== selfNode.ownerDocument;
+       node = node.parentNode) {
+    if (node.nodeType === Node.ELEMENT_NODE &&
+        node.classList.contains(className))
       return node;
   }
   return null;
@@ -243,7 +256,8 @@
 /**
  * @constructor
  */
-function EventEmitter(){};
+function EventEmitter() {
+}
 
 /**
  * @param {!string} type
@@ -333,7 +347,8 @@
 };
 
 Picker.prototype.chooseOtherColor = function() {
-  window.pagePopupController.setValueAndClosePopup(Picker.Actions.ChooseOtherColor, '');
+  window.pagePopupController.setValueAndClosePopup(
+      Picker.Actions.ChooseOtherColor, '');
 };
 
 Picker.prototype.cleanup = function() {};
diff --git a/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js b/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js
index e56b93ea..b6a85fd 100644
--- a/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js
@@ -38,7 +38,8 @@
   this._fixWindowSize();
   this._handleBodyKeyDownBound = this._handleBodyKeyDown.bind(this);
   document.body.addEventListener('keydown', this._handleBodyKeyDownBound);
-  this._element.addEventListener('mouseout', this._handleMouseOut.bind(this), false);
+  this._element.addEventListener(
+      'mouseout', this._handleMouseOut.bind(this), false);
 }
 SuggestionPicker.prototype = Object.create(Picker.prototype);
 
@@ -58,9 +59,11 @@
     return 'No otherDateLabel.';
   if (config.suggestionHighlightColor && !config.suggestionHighlightColor)
     return 'No suggestionHighlightColor.';
-  if (config.suggestionHighlightTextColor && !config.suggestionHighlightTextColor)
+  if (config.suggestionHighlightTextColor &&
+      !config.suggestionHighlightTextColor)
     return 'No suggestionHighlightTextColor.';
-  if (config.suggestionValues.length !== config.localizedSuggestionValues.length)
+  if (config.suggestionValues.length !==
+      config.localizedSuggestionValues.length)
     return 'localizedSuggestionValues.length must equal suggestionValues.length.';
   if (config.suggestionValues.length !== config.suggestionLabels.length)
     return 'suggestionLabels.length must equal suggestionValues.length.';
@@ -76,12 +79,14 @@
         color: ' +
       this._config.suggestionHighlightTextColor + '; }';
   text += '.' + SuggestionPicker.ListEntryClass +
-      ':focus .label { color: ' + this._config.suggestionHighlightTextColor + '; }';
+      ':focus .label { color: ' + this._config.suggestionHighlightTextColor +
+      '; }';
   document.head.appendChild(createElement('style', null, text));
 };
 
 SuggestionPicker.prototype.cleanup = function() {
-  document.body.removeEventListener('keydown', this._handleBodyKeyDownBound, false);
+  document.body.removeEventListener(
+      'keydown', this._handleBodyKeyDownBound, false);
 };
 
 /**
@@ -90,7 +95,8 @@
  * @param {!string} value
  * @return {!Element}
  */
-SuggestionPicker.prototype._createSuggestionEntryElement = function(title, label, value) {
+SuggestionPicker.prototype._createSuggestionEntryElement = function(
+    title, label, value) {
   var entryElement = createElement('li', SuggestionPicker.ListEntryClass);
   entryElement.tabIndex = 0;
   entryElement.dataset.value = value;
@@ -102,7 +108,8 @@
     var labelElement = createElement('span', 'label', label);
     content.appendChild(labelElement);
   }
-  entryElement.addEventListener('mouseover', this._handleEntryMouseOver.bind(this), false);
+  entryElement.addEventListener(
+      'mouseover', this._handleEntryMouseOver.bind(this), false);
   return entryElement;
 };
 
@@ -111,7 +118,8 @@
  * @param {!string} actionName
  * @return {!Element}
  */
-SuggestionPicker.prototype._createActionEntryElement = function(title, actionName) {
+SuggestionPicker.prototype._createActionEntryElement = function(
+    title, actionName) {
   var entryElement = createElement('li', SuggestionPicker.ListEntryClass);
   entryElement.tabIndex = 0;
   entryElement.dataset.action = actionName;
@@ -119,7 +127,8 @@
   entryElement.appendChild(content);
   var titleElement = createElement('span', 'title', title);
   content.appendChild(titleElement);
-  entryElement.addEventListener('mouseover', this._handleEntryMouseOver.bind(this), false);
+  entryElement.addEventListener(
+      'mouseover', this._handleEntryMouseOver.bind(this), false);
   return entryElement;
 };
 
@@ -131,9 +140,11 @@
   // left aligns all the content including label.
   this._containerElement.classList.add('measuring-width');
   var maxContentWidth = 0;
-  var contentElements = this._containerElement.getElementsByClassName('content');
+  var contentElements =
+      this._containerElement.getElementsByClassName('content');
   for (var i = 0; i < contentElements.length; ++i) {
-    maxContentWidth = Math.max(maxContentWidth, contentElements[i].getBoundingClientRect().width);
+    maxContentWidth = Math.max(
+        maxContentWidth, contentElements[i].getBoundingClientRect().width);
   }
   this._containerElement.classList.remove('measuring-width');
   return maxContentWidth;
@@ -153,7 +164,8 @@
     if (node.classList.contains(SuggestionPicker.ListEntryClass))
       entryCount++;
     totalHeight += node.offsetHeight;
-    if (maxHeight === 0 && entryCount == SuggestionPicker.NumberOfVisibleEntries)
+    if (maxHeight === 0 &&
+        entryCount == SuggestionPicker.NumberOfVisibleEntries)
       maxHeight = totalHeight;
   }
   var desiredWindowHeight = totalHeight * zoom;
@@ -163,8 +175,10 @@
     desiredWindowHeight = maxHeight * zoom;
     this._containerElement.style.overflowY = 'scroll';
   }
-  var windowRect = adjustWindowRect(desiredWindowWidth, desiredWindowHeight, desiredWindowWidth, 0);
-  this._containerElement.style.height = (windowRect.height / zoom - ListBorder) + 'px';
+  var windowRect = adjustWindowRect(
+      desiredWindowWidth, desiredWindowHeight, desiredWindowWidth, 0);
+  this._containerElement.style.height =
+      (windowRect.height / zoom - ListBorder) + 'px';
   setWindowRect(windowRect);
 };
 
@@ -174,10 +188,12 @@
   if (this._config.isLocaleRTL)
     this._element.classList.add('locale-rtl');
   this._containerElement = createElement('ul', 'suggestion-list');
-  this._containerElement.addEventListener('click', this._handleEntryClick.bind(this), false);
+  this._containerElement.addEventListener(
+      'click', this._handleEntryClick.bind(this), false);
   for (var i = 0; i < this._config.suggestionValues.length; ++i) {
     this._containerElement.appendChild(this._createSuggestionEntryElement(
-        this._config.localizedSuggestionValues[i], this._config.suggestionLabels[i], this._config.suggestionValues[i]));
+        this._config.localizedSuggestionValues[i],
+        this._config.suggestionLabels[i], this._config.suggestionValues[i]));
   }
   if (this._config.showOtherDateEntry) {
     // Add separator
@@ -185,8 +201,9 @@
     this._containerElement.appendChild(separator);
 
     // Add "Other..." entry
-    var otherEntry =
-        this._createActionEntryElement(this._config.otherDateLabel, SuggestionPicker.ActionNames.OpenCalendarPicker);
+    var otherEntry = this._createActionEntryElement(
+        this._config.otherDateLabel,
+        SuggestionPicker.ActionNames.OpenCalendarPicker);
     this._containerElement.appendChild(otherEntry);
   }
   this._element.appendChild(this._containerElement);
@@ -198,8 +215,11 @@
 SuggestionPicker.prototype.selectEntry = function(entry) {
   if (typeof entry.dataset.value !== 'undefined') {
     this.submitValue(entry.dataset.value);
-  } else if (entry.dataset.action === SuggestionPicker.ActionNames.OpenCalendarPicker) {
-    window.addEventListener('didHide', SuggestionPicker._handleWindowDidHide, false);
+  } else if (
+      entry.dataset.action ===
+      SuggestionPicker.ActionNames.OpenCalendarPicker) {
+    window.addEventListener(
+        'didHide', SuggestionPicker._handleWindowDidHide, false);
     hideWindow();
   }
 };
@@ -213,7 +233,8 @@
  * @param {!Event} event
  */
 SuggestionPicker.prototype._handleEntryClick = function(event) {
-  var entry = enclosingNodeOrSelfWithClass(event.target, SuggestionPicker.ListEntryClass);
+  var entry = enclosingNodeOrSelfWithClass(
+      event.target, SuggestionPicker.ListEntryClass);
   if (!entry)
     return;
   this.selectEntry(entry);
@@ -228,9 +249,11 @@
   var childNodes = this._containerElement.childNodes;
   for (var i = 0; i < childNodes.length; ++i) {
     var node = childNodes[i];
-    if (node.nodeType !== Node.ELEMENT_NODE || !node.classList.contains(SuggestionPicker.ListEntryClass))
+    if (node.nodeType !== Node.ELEMENT_NODE ||
+        !node.classList.contains(SuggestionPicker.ListEntryClass))
       continue;
-    if (node.offsetTop + node.offsetHeight - scrollTop > SuggestionPicker.VisibleEntryThresholdHeight)
+    if (node.offsetTop + node.offsetHeight - scrollTop >
+        SuggestionPicker.VisibleEntryThresholdHeight)
       return node;
   }
   return null;
@@ -240,13 +263,16 @@
  * @return {?Element}
  */
 SuggestionPicker.prototype._findLastVisibleEntry = function() {
-  var scrollBottom = this._containerElement.scrollTop + this._containerElement.offsetHeight;
+  var scrollBottom =
+      this._containerElement.scrollTop + this._containerElement.offsetHeight;
   var childNodes = this._containerElement.childNodes;
   for (var i = childNodes.length - 1; i >= 0; --i) {
     var node = childNodes[i];
-    if (node.nodeType !== Node.ELEMENT_NODE || !node.classList.contains(SuggestionPicker.ListEntryClass))
+    if (node.nodeType !== Node.ELEMENT_NODE ||
+        !node.classList.contains(SuggestionPicker.ListEntryClass))
       continue;
-    if (scrollBottom - node.offsetTop > SuggestionPicker.VisibleEntryThresholdHeight)
+    if (scrollBottom - node.offsetTop >
+        SuggestionPicker.VisibleEntryThresholdHeight)
       return node;
   }
   return null;
@@ -262,8 +288,11 @@
     this.handleCancel();
     eventHandled = true;
   } else if (key == 'ArrowUp') {
-    if (document.activeElement && document.activeElement.classList.contains(SuggestionPicker.ListEntryClass)) {
-      for (var node = document.activeElement.previousElementSibling; node; node = node.previousElementSibling) {
+    if (document.activeElement &&
+        document.activeElement.classList.contains(
+            SuggestionPicker.ListEntryClass)) {
+      for (var node = document.activeElement.previousElementSibling; node;
+           node = node.previousElementSibling) {
         if (node.classList.contains(SuggestionPicker.ListEntryClass)) {
           this._isFocusByMouse = false;
           node.focus();
@@ -271,12 +300,17 @@
         }
       }
     } else {
-      this._element.querySelector('.' + SuggestionPicker.ListEntryClass + ':last-child').focus();
+      this._element
+          .querySelector('.' + SuggestionPicker.ListEntryClass + ':last-child')
+          .focus();
     }
     eventHandled = true;
   } else if (key == 'ArrowDown') {
-    if (document.activeElement && document.activeElement.classList.contains(SuggestionPicker.ListEntryClass)) {
-      for (var node = document.activeElement.nextElementSibling; node; node = node.nextElementSibling) {
+    if (document.activeElement &&
+        document.activeElement.classList.contains(
+            SuggestionPicker.ListEntryClass)) {
+      for (var node = document.activeElement.nextElementSibling; node;
+           node = node.nextElementSibling) {
         if (node.classList.contains(SuggestionPicker.ListEntryClass)) {
           this._isFocusByMouse = false;
           node.focus();
@@ -284,7 +318,9 @@
         }
       }
     } else {
-      this._element.querySelector('.' + SuggestionPicker.ListEntryClass + ':first-child').focus();
+      this._element
+          .querySelector('.' + SuggestionPicker.ListEntryClass + ':first-child')
+          .focus();
     }
     eventHandled = true;
   } else if (key === 'Enter') {
@@ -311,7 +347,8 @@
  * @param {!Event} event
  */
 SuggestionPicker.prototype._handleEntryMouseOver = function(event) {
-  var entry = enclosingNodeOrSelfWithClass(event.target, SuggestionPicker.ListEntryClass);
+  var entry = enclosingNodeOrSelfWithClass(
+      event.target, SuggestionPicker.ListEntryClass);
   if (!entry)
     return;
   this._isFocusByMouse = true;
@@ -323,7 +360,8 @@
  * @param {!Event} event
  */
 SuggestionPicker.prototype._handleMouseOut = function(event) {
-  if (!document.activeElement.classList.contains(SuggestionPicker.ListEntryClass))
+  if (!document.activeElement.classList.contains(
+          SuggestionPicker.ListEntryClass))
     return;
   this._isFocusByMouse = false;
   document.activeElement.blur();
diff --git a/third_party/blink/renderer/core/html/forms/resources/time_picker.js b/third_party/blink/renderer/core/html/forms/resources/time_picker.js
index b45bc86..625a1ce 100644
--- a/third_party/blink/renderer/core/html/forms/resources/time_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/time_picker.js
@@ -59,10 +59,11 @@
       case TimeColumnType.MILLISECOND:
         // TODO(https://crbug.com/1008294): Use increments of 1 instead of 100 for milliseconds.
         // support 100, 200, 300... for milliseconds
-        this.millisecond_ = (Math.round(this.millisecond_ / 100) * 100 + 100) % 1000;
+        this.millisecond_ =
+            (Math.round(this.millisecond_ / 100) * 100 + 100) % 1000;
         break;
     }
-  }
+  };
 
   value = (columnType) => {
     switch (columnType) {
@@ -75,10 +76,11 @@
       case TimeColumnType.MILLISECOND:
         return this.millisecond_.toString().padStart(3, '0');
     }
-  }
+  };
 
   toString = (hasSecond, hasMillisecond) => {
-    let value = `${this.value(TimeColumnType.HOUR)}:${this.value(TimeColumnType.MINUTE)}`;
+    let value = `${this.value(TimeColumnType.HOUR)}:${
+        this.value(TimeColumnType.MINUTE)}`;
     if (hasSecond) {
       value += `:${this.value(TimeColumnType.SECOND)}`;
     }
@@ -86,7 +88,7 @@
       value += `.${this.value(TimeColumnType.MILLISECOND)}`;
     }
     return value;
-  }
+  };
 
   static parse = (str) => {
     var match = Time.ISOStringRegExp.exec(str);
@@ -101,13 +103,14 @@
     if (match[4])
       millisecond = parseInt(match[4], 10);
     return new Time(hour, minute, second, millisecond);
-  }
+  };
 
   static currentTime = () => {
     var currentDate = new Date();
-    return new Time(currentDate.getHours(), currentDate.getMinutes(),
-                    currentDate.getSeconds(), currentDate.getMilliseconds());
-  }
+    return new Time(
+        currentDate.getHours(), currentDate.getMinutes(),
+        currentDate.getSeconds(), currentDate.getMilliseconds());
+  };
 
   static numberOfValues = (columnType) => {
     switch (columnType) {
@@ -120,7 +123,7 @@
       case TimeColumnType.MILLISECOND:
         return Time.MILLISECOND_VALUES;
     }
-  }
+  };
 }
 // See platform/date_components.h.
 Time.Minimum = new Time(0, 0, 0, 0);
@@ -145,9 +148,8 @@
     this.initializeFromConfig_(config);
 
     this.timeColumns_ = new TimeColumns(this);
-    this.submissionControls_ =
-        new SubmissionControls(this.onSubmitButtonClick_,
-                               this.onCancelButtonClick_);
+    this.submissionControls_ = new SubmissionControls(
+        this.onSubmitButtonClick_, this.onCancelButtonClick_);
     this.append(this.timeColumns_, this.submissionControls_);
   }
 
@@ -223,8 +225,8 @@
       this.width_ += TimePicker.ColumnWidth;
     }
     if (timePicker.hasMillisecond) {
-      this.millisecondColumn_ = new TimeColumn(TimeColumnType.MILLISECOND,
-        timePicker);
+      this.millisecondColumn_ =
+          new TimeColumn(TimeColumnType.MILLISECOND, timePicker);
       this.append(this.millisecondColumn_);
       this.width_ += TimePicker.ColumnWidth;
     }
@@ -237,10 +239,12 @@
   selectedValue = () => {
     const hour = parseInt(this.hourColumn_.selectedTimeCell.value, 10);
     const minute = parseInt(this.minuteColumn_.selectedTimeCell.value, 10);
-    const second = this.secondColumn_ ? parseInt(this.secondColumn_.selectedTimeCell.value, 10)
-                                      : 0;
-    const millisecond = this.millisecondColumn_ ? parseInt(this.millisecondColumn_.selectedTimeCell.value, 10)
-                                                : 0;
+    const second = this.secondColumn_ ?
+        parseInt(this.secondColumn_.selectedTimeCell.value, 10) :
+        0;
+    const millisecond = this.millisecondColumn_ ?
+        parseInt(this.millisecondColumn_.selectedTimeCell.value, 10) :
+        0;
     return new Time(hour, minute, second, millisecond);
   }
 }
@@ -291,7 +295,7 @@
   }
 }
 TimeColumn.ClassName = 'time-column';
-window.customElements.define('time-column', TimeColumn, { extends: "ul" });
+window.customElements.define('time-column', TimeColumn, {extends: 'ul'});
 
 /**
  * TimeCell: List item with a custom look that displays a time value.
@@ -306,7 +310,7 @@
   }
 }
 TimeCell.ClassName = 'time-cell';
-window.customElements.define('time-cell', TimeCell, { extends: "li" });
+window.customElements.define('time-cell', TimeCell, {extends: 'li'});
 
 /**
  * SubmissionControls: Provides functionality to submit or discard a change.
@@ -319,22 +323,22 @@
     padding.setAttribute('id', 'submission-controls-padding');
     this.append(padding);
 
-    this.className = SubmissionControls.ClassName
+    this.className = SubmissionControls.ClassName;
 
-    this.submitButton_ = new SubmissionButton(submitCallback,
+    this.submitButton_ = new SubmissionButton(
+        submitCallback,
         '<svg width="14" height="10" viewBox="0 0 14 10" fill="none" ' +
-        'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' +
-        '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' +
-        '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="black"/></svg>'
-    );
-    this.cancelButton_ = new SubmissionButton(cancelCallback,
+            'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' +
+            '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' +
+            '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="black"/></svg>');
+    this.cancelButton_ = new SubmissionButton(
+        cancelCallback,
         '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" ' +
-        'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' +
-        '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' +
-        '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' +
-        '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' +
-        'fill="black"/></svg>'
-    );
+            'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' +
+            '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' +
+            '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' +
+            '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' +
+            'fill="black"/></svg>');
     this.append(this.submitButton_, this.cancelButton_);
   }
 
@@ -357,10 +361,11 @@
   constructor(clickCallback, htmlString) {
     super();
 
-    this.className = SubmissionButton.ClassName
+    this.className = SubmissionButton.ClassName;
     this.innerHTML = htmlString;
     this.addEventListener('click', clickCallback);
   }
 }
 SubmissionButton.ClassName = 'submission-button';
-window.customElements.define('submission-button', SubmissionButton, { extends: 'button' });
\ No newline at end of file
+window.customElements.define(
+    'submission-button', SubmissionButton, {extends: 'button'});
diff --git a/third_party/blink/renderer/core/html/resources/forced_colors.css b/third_party/blink/renderer/core/html/resources/forced_colors.css
index 6d3cdce0..4e83f4f 100644
--- a/third_party/blink/renderer/core/html/resources/forced_colors.css
+++ b/third_party/blink/renderer/core/html/resources/forced_colors.css
@@ -120,6 +120,11 @@
     color: GrayText;
   }
 
+  /* same color as hyperlinks */
+  details summary {
+    color: LinkText;
+  }
+
   select:-internal-list-box {
     background-color: Window !important;
     border-color: ButtonText;
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 898ec97c..7f49d8d 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -6803,6 +6803,9 @@
 
   # Request a global memory dump.
   command requestMemoryDump
+    parameters
+      # Enables more deterministic results by forcing garbage collection
+      optional boolean deterministic
     returns
       # GUID of the resulting global memory dump.
       string dumpGuid
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
index 5cad314..13180f9 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.cc
@@ -73,9 +73,10 @@
     if (target_->isConnected() &&
         !observer_data->IsTargetOfImplicitRootObserver() &&
         !observer_data->IsRoot()) {
-      target_->GetDocument()
-          .EnsureIntersectionObserverController()
-          .RemoveTrackedElement(*target_);
+      IntersectionObserverController* controller =
+          target_->GetDocument().GetIntersectionObserverController();
+      if (controller)
+        controller->RemoveTrackedElement(*target_);
     }
   }
   entries_.clear();
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
index 9d6b822e..b658a7a4c 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
@@ -28,7 +28,7 @@
 
 static constexpr base::TimeDelta kTimerDelay =
     base::TimeDelta::FromMilliseconds(500);
-static const float kMovementThreshold = 3.0;  // CSS pixels.
+static const float kMovementThreshold = 1.0;  // CSS pixels.
 
 static FloatPoint LogicalStart(const FloatRect& rect,
                                const LayoutObject& object) {
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
index 4b9287b7..ec88c25b 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker_test.cc
@@ -92,33 +92,6 @@
   EXPECT_FLOAT_EQ(20.0, GetLayoutShiftTracker().OverallMaxDistance());
 }
 
-TEST_F(LayoutShiftTrackerTest, SmallMovementIgnored) {
-  SetBodyInnerHTML(R"HTML(
-    <style>
-      #j { position: relative; width: 300px; height: 100px; }
-    </style>
-    <div id='j'></div>
-  )HTML");
-  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
-                                                  AtomicString("top: 2px"));
-  UpdateAllLifecyclePhases();
-  EXPECT_EQ(0.0, GetLayoutShiftTracker().Score());
-}
-
-TEST_F(LayoutShiftTrackerTest, SmallMovementIgnoredWithZoom) {
-  GetDocument().GetFrame()->SetPageZoomFactor(2);
-  SetBodyInnerHTML(R"HTML(
-    <style>
-      #j { position: relative; width: 300px; height: 100px; }
-    </style>
-    <div id='j'></div>
-  )HTML");
-  GetDocument().getElementById("j")->setAttribute(html_names::kStyleAttr,
-                                                  AtomicString("top: 2px"));
-  UpdateAllLifecyclePhases();
-  EXPECT_EQ(0.0, GetLayoutShiftTracker().Score());
-}
-
 TEST_F(LayoutShiftTrackerTest, IgnoreAfterInput) {
   SetBodyInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
index 2af19ca..498e42e 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -181,7 +181,7 @@
 
    public:
     ChildList() = default;
-    void operator=(ChildList&& other) noexcept {
+    void operator=(ChildList&& other) {
       children_ = std::move(other.children_);
     }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index b12baeb..4333b4d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -1156,12 +1156,12 @@
       container_builder_.Size().inline_size, ConstraintSpace().Direction());
 
   if (ConstraintSpace().HasBlockFragmentation()) {
-    bool is_pushed_by_floats =
-        child_margin_got_separated ||
+    bool has_container_separation =
+        has_processed_first_child_ || child_margin_got_separated ||
         child_bfc_offset.block_offset > child_bfc_offset_estimate ||
         layout_result->IsPushedByFloats();
     if (BreakBeforeChild(child, *layout_result, previous_inflow_position,
-                         logical_offset.block_offset, is_pushed_by_floats))
+                         logical_offset.block_offset, has_container_separation))
       return true;
     EBreakBetween break_after = JoinFragmentainerBreakValues(
         layout_result->FinalBreakAfter(), child.Style().BreakAfter());
@@ -1642,9 +1642,14 @@
       fragment, layout_result->BfcLineOffset(), child_bfc_block_offset);
 
   if (ConstraintSpace().HasBlockFragmentation()) {
+    // Floats only cause container separation for the outermost block child that
+    // gets pushed down (the container and the child may have adjoining
+    // block-start margins).
+    bool has_container_separation =
+        has_processed_first_child_ || (layout_result->IsPushedByFloats() &&
+                                       !container_builder_.IsPushedByFloats());
     if (BreakBeforeChild(child, *layout_result, previous_inflow_position,
-                         logical_offset.block_offset,
-                         layout_result->IsPushedByFloats()))
+                         logical_offset.block_offset, has_container_separation))
       return true;
     EBreakBetween break_after = JoinFragmentainerBreakValues(
         layout_result->FinalBreakAfter(), child.Style().BreakAfter());
@@ -2002,10 +2007,10 @@
     const NGLayoutResult& layout_result,
     NGPreviousInflowPosition* previous_inflow_position,
     LayoutUnit block_offset,
-    bool is_pushed_by_floats) {
+    bool has_container_separation) {
   DCHECK(ConstraintSpace().HasBlockFragmentation());
   BreakType break_type = BreakTypeBeforeChild(
-      child, layout_result, block_offset, is_pushed_by_floats);
+      child, layout_result, block_offset, has_container_separation);
   if (break_type == NoBreak)
     return false;
 
@@ -2091,14 +2096,10 @@
     }
   }
 
-  if (!has_processed_first_child_ &&
-      (container_builder_.IsPushedByFloats() || !is_pushed_by_floats)) {
+  if (!has_container_separation) {
     // We're breaking before the first piece of in-flow content inside this
     // block, even if it's not a valid class C break point [1] in this case. We
-    // really don't want to break here, if we can find something better. A class
-    // C break point occurs if a first child has been pushed by floats, but this
-    // only applies to the outermost block that gets pushed (in case this parent
-    // and the child have adjoining top margins).
+    // really don't want to break here, if we can find something better.
     //
     // [1] https://www.w3.org/TR/css-break-3/#possible-breaks
     container_builder_.SetHasLastResortBreak();
@@ -2133,7 +2134,7 @@
     NGLayoutInputNode child,
     const NGLayoutResult& layout_result,
     LayoutUnit block_offset,
-    bool is_pushed_by_floats) const {
+    bool has_container_separation) const {
   if (!container_builder_.BfcBlockOffset().has_value())
     return NoBreak;
 
@@ -2157,9 +2158,11 @@
   EBreakBetween break_between =
       container_builder_.JoinedBreakBetweenValue(break_before);
   if (IsForcedBreakValue(ConstraintSpace(), break_between)) {
-    // There should be a forced break before this child, and if we're not at the
-    // first in-flow child, just go ahead and break.
-    if (has_processed_first_child_)
+    // There should be a forced break before this child, and if we're at a valid
+    // breakpoint (class A or C), just go ahead and break. If we're not at a
+    // valid breakpoint, the forced break will be propagated upwards until we
+    // find a valid breakpoint.
+    if (has_container_separation)
       return ForcedBreak;
   }
 
@@ -2186,7 +2189,7 @@
     // content progression.
     //
     // [1] https://www.w3.org/TR/css-break-3/#possible-breaks
-    if (has_processed_first_child_ || is_pushed_by_floats) {
+    if (has_container_separation) {
       // This is a valid break point, and we can resolve the last-resort
       // situation.
       return SoftBreak;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
index 7e144a1..e6893cdd 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.h
@@ -227,11 +227,21 @@
   // Insert a fragmentainer break before the child if necessary.
   // Update previous in-flow position and return true if a break was inserted.
   // Otherwise return false.
+  // If |has_container_separation| is true, it means that we're at a valid
+  // breakpoint. We obviously prefer valid breakpoints, but sometimes we need to
+  // break at undesirable locations. Class A breakpoints occur between block
+  // siblings. Class B breakpoints between line boxes. Both these breakpoint
+  // classes imply that we're already past the first in-flow child in the
+  // container, but there's also another way of achieving container separation:
+  // class C breakpoints. Those occur if there's a positive gap between the
+  // block-start content edge of the container and the block-start margin edge
+  // of the first in-flow child. This can happen when in-flow content is pushed
+  // down by floats. https://www.w3.org/TR/css-break-3/#possible-breaks
   bool BreakBeforeChild(NGLayoutInputNode child,
                         const NGLayoutResult&,
                         NGPreviousInflowPosition*,
                         LayoutUnit block_offset,
-                        bool is_pushed_by_floats);
+                        bool has_container_separation);
 
   enum BreakType { NoBreak, SoftBreak, ForcedBreak };
 
@@ -240,7 +250,7 @@
   BreakType BreakTypeBeforeChild(NGLayoutInputNode child,
                                  const NGLayoutResult&,
                                  LayoutUnit block_offset,
-                                 bool is_pushed_by_floats) const;
+                                 bool has_container_separation) const;
 
   // Final adjustments before fragment creation. We need to prevent the fragment
   // from crossing fragmentainer boundaries, and rather create a break token if
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
index 310a86f..91d8a52 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm_test.cc
@@ -3228,6 +3228,43 @@
   EXPECT_EQ(expectation, dump);
 }
 
+TEST_F(NGColumnLayoutAlgorithmTest, ForcedBreakAtClassCBreakPoint) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      #parent {
+        columns: 3;
+        column-gap: 10px;
+        column-fill: auto;
+        width: 320px;
+        height:100px;
+      }
+    </style>
+    <div id="container">
+      <div id="parent">
+        <div style="width:50px; height:50px;"></div>
+        <div style="float:left; width:100%; height:40px;"></div>
+        <div style="width:55px;">
+          <div style="display:flow-root; break-before:column; width:44px; height:20px;"></div>
+        </div>
+      </div>
+    </div>
+  )HTML");
+
+  String dump = DumpFragmentTree(GetElementById("container"));
+  String expectation = R"DUMP(.:: LayoutNG Physical Fragment Tree ::.
+  offset:unplaced size:1000x100
+    offset:0,0 size:320x100
+      offset:0,0 size:100x100
+        offset:0,0 size:50x50
+        offset:0,50 size:100x40
+        offset:0,50 size:55x50
+      offset:110,0 size:100x20
+        offset:0,0 size:55x20
+          offset:0,0 size:44x20
+)DUMP";
+  EXPECT_EQ(expectation, dump);
+}
+
 TEST_F(NGColumnLayoutAlgorithmTest, Nested) {
   SetBodyInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
index 8f77b90..833b0d23 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -86,7 +86,7 @@
     else
       bfc_offset_ = other.bfc_offset_;
   }
-  NGConstraintSpace(NGConstraintSpace&& other) noexcept
+  NGConstraintSpace(NGConstraintSpace&& other)
       : available_size_(other.available_size_),
         exclusion_space_(std::move(other.exclusion_space_)),
         bitfields_(other.bitfields_) {
@@ -110,7 +110,7 @@
     bitfields_ = other.bitfields_;
     return *this;
   }
-  NGConstraintSpace& operator=(NGConstraintSpace&& other) noexcept {
+  NGConstraintSpace& operator=(NGConstraintSpace&& other) {
     available_size_ = other.available_size_;
     if (HasRareData())
       delete rare_data_;
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index 22c752ec..7fe4716c 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -318,7 +318,6 @@
 
   void DownloadURL(const ResourceRequest&,
                    DownloadCrossOriginRedirects) override {}
-  void LoadErrorPage(int reason) override {}
 
   DocumentLoader* CreateDocumentLoader(
       LocalFrame*,
diff --git a/third_party/blink/renderer/core/messaging/blink_cloneable_message.cc b/third_party/blink/renderer/core/messaging/blink_cloneable_message.cc
index b6c8768..74239307 100644
--- a/third_party/blink/renderer/core/messaging/blink_cloneable_message.cc
+++ b/third_party/blink/renderer/core/messaging/blink_cloneable_message.cc
@@ -9,9 +9,8 @@
 BlinkCloneableMessage::BlinkCloneableMessage() = default;
 BlinkCloneableMessage::~BlinkCloneableMessage() = default;
 
-BlinkCloneableMessage::BlinkCloneableMessage(BlinkCloneableMessage&&) noexcept =
-    default;
+BlinkCloneableMessage::BlinkCloneableMessage(BlinkCloneableMessage&&) = default;
 BlinkCloneableMessage& BlinkCloneableMessage::operator=(
-    BlinkCloneableMessage&&) noexcept = default;
+    BlinkCloneableMessage&&) = default;
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/messaging/blink_cloneable_message.h b/third_party/blink/renderer/core/messaging/blink_cloneable_message.h
index 337b0f8..a51e888 100644
--- a/third_party/blink/renderer/core/messaging/blink_cloneable_message.h
+++ b/third_party/blink/renderer/core/messaging/blink_cloneable_message.h
@@ -21,8 +21,8 @@
   BlinkCloneableMessage();
   ~BlinkCloneableMessage();
 
-  BlinkCloneableMessage(BlinkCloneableMessage&&) noexcept;
-  BlinkCloneableMessage& operator=(BlinkCloneableMessage&&) noexcept;
+  BlinkCloneableMessage(BlinkCloneableMessage&&);
+  BlinkCloneableMessage& operator=(BlinkCloneableMessage&&);
 
   scoped_refptr<blink::SerializedScriptValue> message;
   v8_inspector::V8StackTraceId sender_stack_trace_id;
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
index 909ddb0..b5cdfda 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
@@ -16,10 +16,10 @@
 BlinkTransferableMessage::BlinkTransferableMessage() = default;
 BlinkTransferableMessage::~BlinkTransferableMessage() = default;
 
-BlinkTransferableMessage::BlinkTransferableMessage(
-    BlinkTransferableMessage&&) noexcept = default;
+BlinkTransferableMessage::BlinkTransferableMessage(BlinkTransferableMessage&&) =
+    default;
 BlinkTransferableMessage& BlinkTransferableMessage::operator=(
-    BlinkTransferableMessage&&) noexcept = default;
+    BlinkTransferableMessage&&) = default;
 
 scoped_refptr<blink::StaticBitmapImage> ToStaticBitmapImage(
     const SkBitmap& sk_bitmap) {
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.h b/third_party/blink/renderer/core/messaging/blink_transferable_message.h
index edf4fab..3957bed 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.h
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.h
@@ -24,8 +24,8 @@
   BlinkTransferableMessage();
   ~BlinkTransferableMessage();
 
-  BlinkTransferableMessage(BlinkTransferableMessage&&) noexcept;
-  BlinkTransferableMessage& operator=(BlinkTransferableMessage&&) noexcept;
+  BlinkTransferableMessage(BlinkTransferableMessage&&);
+  BlinkTransferableMessage& operator=(BlinkTransferableMessage&&);
 
   Vector<MessagePortChannel> ports;
 
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 8c7ec85a..64c67d12 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -217,7 +217,6 @@
   }
 
   UpdateOverflowControlsLayers(false, false, false);
-  UpdateChildTransformLayer(false);
   UpdateForegroundLayer(false);
   UpdateMaskLayer(false);
   UpdateScrollingLayers(false);
@@ -257,7 +256,6 @@
 
   graphics_layer_ = nullptr;
   foreground_layer_ = nullptr;
-  child_transform_layer_ = nullptr;
   mask_layer_ = nullptr;
 
   scrolling_layer_ = nullptr;
@@ -504,11 +502,6 @@
                                    RequiresScrollCornerLayer()))
     layer_config_changed = true;
 
-  bool has_perspective = style.HasPerspective();
-  bool needs_child_transform_layer = has_perspective && layout_object.IsBox();
-  if (UpdateChildTransformLayer(needs_child_transform_layer))
-    layer_config_changed = true;
-
   if (UpdateSquashingLayers(!squashed_layers_.IsEmpty()))
     layer_config_changed = true;
 
@@ -870,8 +863,6 @@
       snapped_offset_from_composited_ancestor, squashed_layers_,
       layers_needing_paint_invalidation);
 
-  UpdateChildTransformLayerGeometry();
-
   UpdateMaskLayerGeometry();
   UpdateTransformGeometry(snapped_offset_from_composited_ancestor,
                           relative_compositing_bounds);
@@ -1033,18 +1024,6 @@
   overflow_controls_host_layer_->SetMasksToBounds(true);
 }
 
-void CompositedLayerMapping::UpdateChildTransformLayerGeometry() {
-  if (!child_transform_layer_)
-    return;
-
-  PhysicalRect border_box =
-      ToLayoutBox(owning_layer_.GetLayoutObject()).PhysicalBorderBoxRect();
-  border_box.Move(ContentOffsetInCompositingLayer());
-  child_transform_layer_->SetSize(gfx::Size(border_box.PixelSnappedSize()));
-  child_transform_layer_->SetOffsetFromLayoutObject(IntSize());
-  child_transform_layer_->SetPosition(FloatPoint(border_box.offset));
-}
-
 void CompositedLayerMapping::UpdateMaskLayerGeometry() {
   if (!mask_layer_)
     return;
@@ -1102,11 +1081,8 @@
   // When a m_childTransformLayer exists, local content offsets for the
   // m_scrollingLayer have already been applied. Otherwise, we apply them here.
   IntSize local_content_offset(0, 0);
-  if (!child_transform_layer_) {
-    local_content_offset =
-        RoundedIntPoint(owning_layer_.SubpixelAccumulation()) -
-        local_compositing_bounds.Location();
-  }
+  local_content_offset = RoundedIntPoint(owning_layer_.SubpixelAccumulation()) -
+                         local_compositing_bounds.Location();
   scrolling_layer_->SetPosition(
       FloatPoint(overflow_clip_rect.Location() + local_content_offset));
 
@@ -1215,7 +1191,6 @@
     }
   };
 
-  update_bottom_layer(child_transform_layer_.get());
   update_bottom_layer(scrolling_layer_.get());
 
   // Now constructing the subtree for the overflow controls.
@@ -1348,35 +1323,9 @@
 }
 
 void CompositedLayerMapping::UpdateChildrenTransform() {
-  if (GraphicsLayer* child_transform_layer = ChildTransformLayer()) {
-    child_transform_layer->SetTransform(OwningLayer().PerspectiveTransform());
-    child_transform_layer->SetTransformOrigin(
-        OwningLayer().PerspectiveOrigin());
-  }
-
   UpdateShouldFlattenTransform();
 }
 
-bool CompositedLayerMapping::UpdateChildTransformLayer(
-    bool needs_child_transform_layer) {
-  bool layers_changed = false;
-
-  if (needs_child_transform_layer) {
-    if (!child_transform_layer_) {
-      child_transform_layer_ =
-          CreateGraphicsLayer(CompositingReason::kLayerForPerspective);
-      child_transform_layer_->SetDrawsContent(false);
-      layers_changed = true;
-    }
-  } else if (child_transform_layer_) {
-    child_transform_layer_->RemoveFromParent();
-    child_transform_layer_ = nullptr;
-    layers_changed = true;
-  }
-
-  return layers_changed;
-}
-
 bool CompositedLayerMapping::ToggleScrollbarLayerIfNeeded(
     std::unique_ptr<GraphicsLayer>& layer,
     bool needs_layer,
@@ -1546,10 +1495,6 @@
   DCHECK(mode);
 
   if (((mode & kApplyToLayersAffectedByPreserve3D) ||
-       (mode & kApplyToChildContainingLayers)) &&
-      mapping->ChildTransformLayer())
-    f(mapping->ChildTransformLayer());
-  if (((mode & kApplyToLayersAffectedByPreserve3D) ||
        (mode & kApplyToContentLayers) ||
        (mode & kApplyToNonScrollingContentLayers)) &&
       mapping->MainGraphicsLayer())
@@ -1606,13 +1551,11 @@
   // invalidation for this flag. See crbug.com/783614.
   bool is_flat = !owning_layer_.ShouldPreserve3D();
 
-  if (GraphicsLayer* layer = ChildTransformLayer())
-    layer->SetShouldFlattenTransform(false);
   if (GraphicsLayer* layer = ScrollingLayer())
     layer->SetShouldFlattenTransform(false);
   graphics_layer_->SetShouldFlattenTransform(is_flat && !HasScrollingLayer());
   if (GraphicsLayer* layer = ScrollingContentsLayer())
-    layer->SetShouldFlattenTransform(is_flat && !HasChildTransformLayer());
+    layer->SetShouldFlattenTransform(is_flat);
   if (GraphicsLayer* layer = ForegroundLayer())
     layer->SetShouldFlattenTransform(is_flat);
 }
@@ -2077,9 +2020,6 @@
   if (scrolling_contents_layer_)
     return scrolling_contents_layer_.get();
 
-  if (child_transform_layer_)
-    return child_transform_layer_.get();
-
   return graphics_layer_.get();
 }
 
@@ -2797,8 +2737,6 @@
            ")";
   } else if (graphics_layer == foreground_layer_.get()) {
     name = owning_layer_.DebugName() + " (foreground) Layer";
-  } else if (graphics_layer == child_transform_layer_.get()) {
-    name = "Child Transform Layer";
   } else if (graphics_layer == mask_layer_.get()) {
     name = "Mask Layer";
   } else if (graphics_layer == layer_for_horizontal_scrollbar_.get()) {
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
index 40dd6e3a..dc151f2 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
@@ -134,11 +134,6 @@
   GraphicsLayer* ChildForSuperlayers() const;
   void SetSublayers(const GraphicsLayerVector&);
 
-  bool HasChildTransformLayer() const { return child_transform_layer_.get(); }
-  GraphicsLayer* ChildTransformLayer() const {
-    return child_transform_layer_.get();
-  }
-
   GraphicsLayer* SquashingContainmentLayer() const {
     return squashing_containment_layer_.get();
   }
@@ -360,7 +355,6 @@
 
   void UpdateInternalHierarchy();
   void UpdatePaintingPhases();
-  bool UpdateChildTransformLayer(bool needs_child_transform_layer);
   bool UpdateOverflowControlsLayers(bool needs_horizontal_scrollbar_layer,
                                     bool needs_vertical_scrollbar_layer,
                                     bool needs_scroll_corner_layer);
@@ -446,8 +440,7 @@
   // looks like this:
   //
   //    + graphics_layer_
-  //      + child_transform_layer_ [OPTIONAL]
-  //      |   (scrolling_layer_ + scrolling_contents_layer_) [OPTIONAL]
+  //      + (scrolling_layer_ + scrolling_contents_layer_) [OPTIONAL]
   //      | + overflow_controls_host_layer_ [OPTIONAL]
   //      |   + layer_for_vertical_scrollbar_ [OPTIONAL]
   //      |   + layer_for_horizontal_scrollbar_ [OPTIONAL]
@@ -458,9 +451,6 @@
 
   std::unique_ptr<GraphicsLayer> graphics_layer_;
 
-  // Only used if we have perspective.
-  std::unique_ptr<GraphicsLayer> child_transform_layer_;
-
   // Only used if the layer is using composited scrolling.
   std::unique_ptr<GraphicsLayer> scrolling_layer_;
 
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
index 785130b..daddc94 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
@@ -1178,89 +1178,6 @@
   EXPECT_FALSE(mapping->BackgroundPaintsOntoScrollingContentsLayer());
 }
 
-TEST_F(CompositedLayerMappingTest,
-       ScrollingLayerWithPerspectivePositionedCorrectly) {
-  // Test positioning of a scrolling layer within an offset parent, both with
-  // and without perspective.
-  //
-  // When a box shadow is used, the main graphics layer position is offset by
-  // the shadow. The scrolling contents then need to be offset in the other
-  // direction to compensate.  To make this a little clearer, for the first
-  // example here the layer positions are calculated as:
-  //
-  //   graphics_layer_ x = left_pos - shadow_spread + shadow_x_offset
-  //                     = 50 - 10 - 10
-  //                     = 30
-  //
-  //   graphics_layer_ y = top_pos - shadow_spread + shadow_y_offset
-  //                     = 50 - 10 + 0
-  //                     = 40
-  //
-  //   contents x = 50 - graphics_layer_ x = 50 - 30 = 20
-  //   contents y = 50 - graphics_layer_ y = 50 - 40 = 10
-  //
-  // The reason that perspective matters is that it affects which 'contents'
-  // layer is offset; child_transform_layer_ when using perspective, or
-  // scrolling_layer_ when there is no perspective.
-
-  SetBodyInnerHTML(R"HTML(
-    <div id='scroller' style='position: absolute; top: 50px; left: 50px;
-        width: 400px; height: 245px; overflow: auto; will-change: transform;
-        box-shadow: -10px 0 0 10px; perspective: 1px;'>
-      <div style='position: absolute; top: 50px; bottom: 0; width: 200px;
-          height: 200px;'></div>
-      </div>
-    <div id='scroller2' style='position: absolute; top: 400px; left: 50px;
-        width: 400px; height: 245px; overflow: auto; will-change: transform;
-        box-shadow: -10px 0 0 10px;'>
-      <div style='position: absolute; top: 50px; bottom: 0; width: 200px;
-          height: 200px;'></div>
-    </div>
-  )HTML");
-
-  auto* mapping = To<LayoutBlock>(GetLayoutObjectByElementId("scroller"))
-                      ->Layer()
-                      ->GetCompositedLayerMapping();
-
-  auto* mapping2 = To<LayoutBlock>(GetLayoutObjectByElementId("scroller2"))
-                       ->Layer()
-                       ->GetCompositedLayerMapping();
-
-  ASSERT_TRUE(mapping);
-  ASSERT_TRUE(mapping2);
-
-  // The perspective scroller should have a child transform containing the
-  // positional offset, and a scrolling layer that has no offset.
-
-  GraphicsLayer* scrolling_layer = mapping->ScrollingLayer();
-  GraphicsLayer* child_transform_layer = mapping->ChildTransformLayer();
-  GraphicsLayer* main_graphics_layer = mapping->MainGraphicsLayer();
-
-  ASSERT_TRUE(scrolling_layer);
-  ASSERT_TRUE(child_transform_layer);
-
-  EXPECT_FLOAT_EQ(30, main_graphics_layer->GetPosition().x());
-  EXPECT_FLOAT_EQ(40, main_graphics_layer->GetPosition().y());
-  EXPECT_FLOAT_EQ(0, scrolling_layer->GetPosition().x());
-  EXPECT_FLOAT_EQ(0, scrolling_layer->GetPosition().y());
-  EXPECT_FLOAT_EQ(20, child_transform_layer->GetPosition().x());
-  EXPECT_FLOAT_EQ(10, child_transform_layer->GetPosition().y());
-
-  // The non-perspective scroller should have no child transform and the
-  // offset on the scroller layer directly.
-
-  GraphicsLayer* scrolling_layer2 = mapping2->ScrollingLayer();
-  GraphicsLayer* main_graphics_layer2 = mapping2->MainGraphicsLayer();
-
-  ASSERT_TRUE(scrolling_layer2);
-  ASSERT_FALSE(mapping2->ChildTransformLayer());
-
-  EXPECT_FLOAT_EQ(30, main_graphics_layer2->GetPosition().x());
-  EXPECT_FLOAT_EQ(390, main_graphics_layer2->GetPosition().y());
-  EXPECT_FLOAT_EQ(20, scrolling_layer2->GetPosition().x());
-  EXPECT_FLOAT_EQ(10, scrolling_layer2->GetPosition().y());
-}
-
 TEST_F(CompositedLayerMappingTest, StickyPositionMainThreadOffset) {
   SetBodyInnerHTML(R"HTML(
     <style>.composited { backface-visibility: hidden; }
@@ -1349,10 +1266,8 @@
       sticky->Layer()->GetCompositedLayerMapping();
   ASSERT_TRUE(mapping);
   GraphicsLayer* main_graphics_layer = mapping->MainGraphicsLayer();
-  GraphicsLayer* child_transform_layer = mapping->ChildTransformLayer();
 
   ASSERT_TRUE(main_graphics_layer);
-  ASSERT_TRUE(child_transform_layer);
 
   auto* scroller =
       To<LayoutBlock>(GetLayoutObjectByElementId("scroller"))->Layer();
@@ -1369,11 +1284,6 @@
   // removed so that the compositor can take care of it.
   EXPECT_FLOAT_EQ(0, main_graphics_layer->GetPosition().x());
   EXPECT_FLOAT_EQ(0, main_graphics_layer->GetPosition().y());
-
-  // The child transform layer for the perspective shifting should also not be
-  // moved by the sticky offset.
-  EXPECT_FLOAT_EQ(0, child_transform_layer->GetPosition().x());
-  EXPECT_FLOAT_EQ(0, child_transform_layer->GetPosition().y());
 }
 
 TEST_F(CompositedLayerMappingTest,
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
index af5e46ce..82f5f20 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.cc
@@ -515,7 +515,7 @@
     ParseBeginOrEnd(value.GetString(), kBegin);
     if (isConnected()) {
       ConnectConditions();
-      InstanceListChanged(kBegin, Elapsed());
+      InstanceListChanged(kBegin);
       if (time_container_)
         time_container_->NotifyIntervalsChanged();
     }
@@ -528,7 +528,7 @@
     ParseBeginOrEnd(value.GetString(), kEnd);
     if (isConnected()) {
       ConnectConditions();
-      InstanceListChanged(kEnd, Elapsed());
+      InstanceListChanged(kEnd);
       if (time_container_)
         time_container_->NotifyIntervalsChanged();
     }
@@ -724,9 +724,6 @@
 void SVGSMILElement::AddInstanceTime(BeginOrEnd begin_or_end,
                                      SMILTime time,
                                      SMILTimeOrigin origin) {
-  SMILTime current_presentation_time =
-      time_container_ ? time_container_->CurrentDocumentTime() : SMILTime();
-  DCHECK(!current_presentation_time.IsUnresolved());
   // Ignore new instance times for 'end' if the element is not active
   // and the origin is script.
   if (begin_or_end == kEnd && GetActiveState() == kInactive &&
@@ -736,7 +733,7 @@
       begin_or_end == kBegin ? begin_times_ : end_times_;
   InsertSortedAndUnique(list, SMILTimeWithOrigin(time, origin));
 
-  InstanceListChanged(begin_or_end, current_presentation_time);
+  InstanceListChanged(begin_or_end);
   if (time_container_)
     time_container_->NotifyIntervalsChanged();
 }
@@ -870,16 +867,19 @@
                   FindInstanceTime(kBegin, presentation_time, false));
 }
 
-void SVGSMILElement::InstanceListChanged(BeginOrEnd begin_or_end,
-                                         SMILTime event_time) {
+void SVGSMILElement::InstanceListChanged(BeginOrEnd begin_or_end) {
   if (is_waiting_for_first_interval_) {
     ResolveFirstInterval();
     return;
   }
+  SMILTime current_presentation_time =
+      time_container_ ? time_container_->CurrentDocumentTime() : SMILTime();
+  DCHECK(!current_presentation_time.IsUnresolved());
   if (begin_or_end == kEnd) {
     // If we have no current interval, or the current interval ends before the
     // indicated time, then a new 'end' instance time will have no effect.
-    if (!interval_.IsResolved() || interval_.EndsBefore(event_time))
+    if (!interval_.IsResolved() ||
+        interval_.EndsBefore(current_presentation_time))
       return;
     SMILTime new_end = FindInstanceTime(kEnd, interval_.begin, false);
     if (interval_.EndsBefore(new_end))
@@ -894,21 +894,25 @@
   // Never resolve more than one interval when restart is 'never'.
   if (GetRestart() == kRestartNever)
     return;
-  SMILTime new_begin = FindInstanceTime(kBegin, event_time, true);
+  SMILTime new_begin =
+      FindInstanceTime(kBegin, current_presentation_time, true);
   if (!new_begin.IsFinite())
     return;
   // If the current interval is active and contains the new begin time, then we
   // will pick up a potentially new interval during the regular interval
   // update.
-  if (interval_.BeginsBefore(new_begin) && interval_.EndsAfter(event_time))
+  if (interval_.BeginsBefore(new_begin) &&
+      interval_.EndsAfter(current_presentation_time))
     return;
   // Begin time changed, re-resolve the interval.
   SMILTime old_begin = interval_.begin;
-  interval_ = ResolveInterval(event_time, event_time);
+  interval_ =
+      ResolveInterval(current_presentation_time, current_presentation_time);
   DCHECK(interval_.IsResolved());
   if (interval_.begin != old_begin) {
-    if (GetActiveState() == kActive && interval_.BeginsAfter(event_time)) {
-      active_state_ = DetermineActiveState(event_time);
+    if (GetActiveState() == kActive &&
+        interval_.BeginsAfter(current_presentation_time)) {
+      active_state_ = DetermineActiveState(current_presentation_time);
       if (GetActiveState() != kActive)
         EndedActiveInterval();
     }
diff --git a/third_party/blink/renderer/core/svg/animation/svg_smil_element.h b/third_party/blink/renderer/core/svg/animation/svg_smil_element.h
index 132a30ca..5dc47929 100644
--- a/third_party/blink/renderer/core/svg/animation/svg_smil_element.h
+++ b/third_party/blink/renderer/core/svg/animation/svg_smil_element.h
@@ -183,7 +183,7 @@
   SMILTime RepeatingDuration() const;
   const SMILInterval& GetActiveInterval(SMILTime elapsed) const;
 
-  void InstanceListChanged(BeginOrEnd, SMILTime event_time);
+  void InstanceListChanged(BeginOrEnd);
 
   // This represents conditions on elements begin or end list that need to be
   // resolved on runtime, for example
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn
index f95ebbf..734379d9 100644
--- a/third_party/blink/renderer/devtools/BUILD.gn
+++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -95,6 +95,19 @@
   "front_end/browser_debugger/xhrBreakpointsSidebarPane.css",
   "front_end/browser_sdk/LogManager.js",
   "front_end/browser_sdk/module.json",
+  "front_end/css_overview/cssOverview.css",
+  "front_end/css_overview/cssOverviewStartView.css",
+  "front_end/css_overview/cssOverviewProcessingView.css",
+  "front_end/css_overview/cssOverviewCompletedView.css",
+  "front_end/css_overview/CSSOverviewController.js",
+  "front_end/css_overview/CSSOverviewStartView.js",
+  "front_end/css_overview/CSSOverviewProcessingView.js",
+  "front_end/css_overview/CSSOverviewCompletedView.js",
+  "front_end/css_overview/CSSOverviewModel.js",
+  "front_end/css_overview/CSSOverviewSidebarPanel.js",
+  "front_end/css_overview/cssOverviewSidebarPanel.css",
+  "front_end/css_overview/CSSOverviewPanel.js",
+  "front_end/css_overview/module.json",
   "front_end/changes/ChangesHighlighter.js",
   "front_end/changes/changesView.css",
   "front_end/changes/ChangesView.js",
@@ -538,6 +551,7 @@
   "front_end/resources/clearStorageView.css",
   "front_end/resources/ClearStorageView.js",
   "front_end/resources/CookieItemsView.js",
+  "front_end/resources/cookieItemsView.css",
   "front_end/resources/DatabaseModel.js",
   "front_end/resources/DatabaseQueryView.js",
   "front_end/resources/DatabaseTableView.js",
@@ -1219,6 +1233,7 @@
   "$resources_out_dir/browser_debugger/browser_debugger_module.js",
   "$resources_out_dir/changes/changes_module.js",
   "$resources_out_dir/protocol_monitor/protocol_monitor_module.js",
+  "$resources_out_dir/css_overview/css_overview_module.js",
   "$resources_out_dir/cm/cm_module.js",
   "$resources_out_dir/color_picker/color_picker_module.js",
   "$resources_out_dir/console/console_module.js",
diff --git a/third_party/blink/renderer/devtools/front_end/Tests.js b/third_party/blink/renderer/devtools/front_end/Tests.js
index 33dc3a0..f61b3bf 100644
--- a/third_party/blink/renderer/devtools/front_end/Tests.js
+++ b/third_party/blink/renderer/devtools/front_end/Tests.js
@@ -735,9 +735,9 @@
       this.assertEquals(0, event.data.modifiers);
       this.assertEquals('', event.data.code);
       InspectorFrontendHost.events.removeEventListener(
-          InspectorFrontendHostAPI.Events.KeyEventUnhandled, onKeyEventUnhandledKeyDown, this);
+          Host.InspectorFrontendHostAPI.Events.KeyEventUnhandled, onKeyEventUnhandledKeyDown, this);
       InspectorFrontendHost.events.addEventListener(
-          InspectorFrontendHostAPI.Events.KeyEventUnhandled, onKeyEventUnhandledKeyUp, this);
+          Host.InspectorFrontendHostAPI.Events.KeyEventUnhandled, onKeyEventUnhandledKeyUp, this);
       SDK.targetManager.mainTarget().inputAgent().invoke_dispatchKeyEvent(
           {type: 'keyUp', key: 'F8', code: 'F8', windowsVirtualKeyCode: 119, nativeVirtualKeyCode: 119});
     }
@@ -751,7 +751,7 @@
     }
     this.takeControl();
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.KeyEventUnhandled, onKeyEventUnhandledKeyDown, this);
+        Host.InspectorFrontendHostAPI.Events.KeyEventUnhandled, onKeyEventUnhandledKeyDown, this);
     SDK.targetManager.mainTarget().inputAgent().invoke_dispatchKeyEvent(
         {type: 'rawKeyDown', key: 'F8', windowsVirtualKeyCode: 119, nativeVirtualKeyCode: 119});
   };
diff --git a/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp b/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp
index 488ce52..132daa2b 100644
--- a/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/audits/audits_strings.grdp
@@ -13,7 +13,7 @@
     💡 <ph name="THIS__FASTFACTSQUEUED_FASTFACTINDEX_">$1s<ex>75% of global mobile users in 2016 were on 2G or 3G [Source: GSMA Mobile]</ex></ph>
   </message>
   <message name="IDS_DEVTOOLS_1076f1bac69647c846ac3b822b133518" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    The average user device costs less than 200 USD. [Source: International Data Corporation]
+    The average user device costs less than 200 USD. [Source: <ph name="LOCKED_1">International Data Corporation</ph>]
   </message>
   <message name="IDS_DEVTOOLS_1421c82f3616812789ac346b1638e39d" desc="Text of audits start button in Audits Start View">
     Run audits
@@ -25,25 +25,25 @@
     View Trace
   </message>
   <message name="IDS_DEVTOOLS_169ece87d3c6b79c2c8d69892927857c" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    19 seconds is the average time a mobile web page takes to load on a 3G connection [Source: Google DoubleClick blog]
+    19 seconds is the average time a mobile web page takes to load on a 3G connection [Source: <ph name="LOCKED_1">Google DoubleClick blog</ph>]
   </message>
   <message name="IDS_DEVTOOLS_17de62900ceac9101c8814b4c4874328" desc="Text in Audits Controller">
     Apply mobile emulation during auditing
   </message>
   <message name="IDS_DEVTOOLS_193c4edb3ab576449959f6710e017a49" desc="Text in Audits Controller">
-    Typical DevTools throttling, with actual traffic shaping and CPU slowdown applied
+    Typical <ph name="LOCKED_1">DevTools</ph> throttling, with actual traffic shaping and CPU slowdown applied
   </message>
   <message name="IDS_DEVTOOLS_2046d43f4cc1f59c06f764da2ca2355a" desc="Status text in the Audits panel">
     The print popup window is open. Please close it to continue.
   </message>
   <message name="IDS_DEVTOOLS_24e8ca2483129261096ba8ba0fbcd247" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    70% of mobile pages take nearly 7 seconds for the visual content above the fold to display on the screen. [Source: Think with Google]
+    70% of mobile pages take nearly 7 seconds for the visual content above the fold to display on the screen. [Source: <ph name="LOCKED_1">Think with Google</ph>]
   </message>
   <message name="IDS_DEVTOOLS_2a96c07a6c11ca7add2b1ffde86efe25" desc="Text in Audits Controller">
-    Can only audit HTTP/HTTPS pages and Chrome extensions. Navigate to a different page to start an audit.
+    Can only audit HTTP/HTTPS pages and <ph name="LOCKED_1">Chrome</ph> extensions. Navigate to a different page to start an audit.
   </message>
   <message name="IDS_DEVTOOLS_39680b3025b1f8d881f9d983623bf071" desc="Text in Audits Status View">
-    Lighthouse is loading your page
+    <ph name="LOCKED_1">Lighthouse</ph> is loading your page
   </message>
   <message name="IDS_DEVTOOLS_3b7e043f46813f1d8ffbd462eb77fb6c" desc="Text in Audits Controller">
     How long does this app take to show content and become usable
@@ -52,13 +52,13 @@
     Applied Slow 4G, 4x CPU Slowdown
   </message>
   <message name="IDS_DEVTOOLS_47e2dbd37d8d524f5c4105a96db27b59" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    75% of global mobile users in 2016 were on 2G or 3G [Source: GSMA Mobile]
+    75% of global mobile users in 2016 were on 2G or 3G [Source: <ph name="LOCKED_1">GSMA Mobile</ph>]
   </message>
   <message name="IDS_DEVTOOLS_48d24d11c7c71282366fe2007d4cee3b" desc="Text in Audits Controller">
     Throttling is simulated, resulting in faster audit runs with similar measurement accuracy
   </message>
   <message name="IDS_DEVTOOLS_4ab5093468559edd5940ac59f1c6ac02" desc="Text in Audits Status View">
-    Try to navigate to the URL in a fresh Chrome profile without any other tabs or extensions open and try again.
+    Try to navigate to the URL in a fresh <ph name="LOCKED_1">Chrome</ph> profile without any other tabs or extensions open and try again.
   </message>
   <message name="IDS_DEVTOOLS_55f562701dc2cc775f9adc0478644b2a" desc="Tooltip text that appears when hovering over the largeicon add button in the Audits Panel">
     Perform an audit…
@@ -67,13 +67,13 @@
     Cancelling
   </message>
   <message name="IDS_DEVTOOLS_56846b99d193dbbd252ddca10d29aff4" desc="Text when lighthouse is loading the page in the Audits panel">
-    Lighthouse is loading your page with throttling to measure performance on a mobile device on 3G.
+    <ph name="LOCKED_1">Lighthouse</ph> is loading your page with throttling to measure performance on a mobile device on 3G.
   </message>
   <message name="IDS_DEVTOOLS_5a73519018eaaacb7882caf8f0971ac1" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    1MB takes a minimum of 5 seconds to download on a typical 3G connection [Source: WebPageTest and DevTools 3G definition].
+    1MB takes a minimum of 5 seconds to download on a typical 3G connection [Source: <ph name="LOCKED_1">WebPageTest</ph> and <ph name="LOCKED_2">DevTools</ph> 3G definition].
   </message>
   <message name="IDS_DEVTOOLS_5f0c08bc61549e7455c9e8b46bfe9aa9" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    70% of mobile pages weigh over 1MB, 36% over 2MB, and 12% over 4MB. [Source: Think with Google]
+    70% of mobile pages weigh over 1MB, 36% over 2MB, and 12% over 4MB. [Source: <ph name="LOCKED_1">Think with Google</ph>]
   </message>
   <message name="IDS_DEVTOOLS_64a64ca70e7a909a5a96c07082070bfc" desc="Text of a DOM element in Audits Status View">
     Ah, sorry! We ran into an error.
@@ -91,22 +91,22 @@
     Multiple tabs are being controlled by the same service worker. Close your other tabs on the same origin to audit this page.
   </message>
   <message name="IDS_DEVTOOLS_6e394d18cb68d551f58fa35f4dfc70bb" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    As page load time increases from one second to seven seconds, the probability of a mobile site visitor bouncing increases 113%. [Source: Think with Google]
+    As page load time increases from one second to seven seconds, the probability of a mobile site visitor bouncing increases 113%. [Source: <ph name="LOCKED_1">Think with Google</ph>]
   </message>
   <message name="IDS_DEVTOOLS_72c4a2e545d220fd8c9ead876a59a89d" desc="Text in Audits Controller">
     Is this page optimized for search engine results ranking
   </message>
   <message name="IDS_DEVTOOLS_73351c316c7866b1c5730af9c82b5ab3" desc="Text when lighthouse is loading the page in the Audits panel">
-    Lighthouse is loading your page with throttling to measure performance on a slow desktop on 3G.
+    <ph name="LOCKED_1">Lighthouse</ph> is loading your page with throttling to measure performance on a slow desktop on 3G.
   </message>
   <message name="IDS_DEVTOOLS_7387b82b5b5cbc317147961ff45142c5" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    Walmart saw a 1% increase in revenue for every 100ms improvement in page load [Source: WPO Stats]
+    Walmart saw a 1% increase in revenue for every 100ms improvement in page load [Source: <ph name="LOCKED_1">WPO Stats</ph>]
   </message>
   <message name="IDS_DEVTOOLS_77d52e376f0c781e4f0d908f6d0b7ff2" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    Lighthouse only simulates mobile performance; to measure performance on a real device, try WebPageTest.org [Source: Lighthouse team]
+    <ph name="LOCKED_1">Lighthouse</ph> only simulates mobile performance; to measure performance on a real device, try WebPageTest.org [Source: <ph name="LOCKED_2">Lighthouse</ph> team]
   </message>
   <message name="IDS_DEVTOOLS_78b8123c78d60b80d6a09618ad56c2cb" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    Rebuilding Pinterest pages for performance increased conversion rates by 15% [Source: WPO Stats]
+    Rebuilding Pinterest pages for performance increased conversion rates by 15% [Source: <ph name="LOCKED_1">WPO Stats</ph>]
   </message>
   <message name="IDS_DEVTOOLS_7bd1e4f7363173e2c8329551ca4d38cc" desc="New audit item label in Audits Report Selector">
     (new audit)
@@ -115,22 +115,22 @@
     Auditing <ph name="PAGEHOST">$1s<ex>github.com</ex></ph>
   </message>
   <message name="IDS_DEVTOOLS_8eb7115e5b4ba55f56931ec24c789f9e" desc="Text in Audits Status View">
-    Lighthouse is warming up…
+    <ph name="LOCKED_1">Lighthouse</ph> is warming up…
   </message>
   <message name="IDS_DEVTOOLS_8fe967af0fb77d0261a6ead91bcd607f" desc="Text in Audits Controller">
     Reset storage (localStorage, IndexedDB, etc) before auditing. (Good for performance &amp; PWA testing)
   </message>
   <message name="IDS_DEVTOOLS_9103edac10b1749db57ca27386ff29dc" desc="Text when lighthouse is loading the page in the Audits panel">
-    Lighthouse is loading your page.
+    <ph name="LOCKED_1">Lighthouse</ph> is loading your page.
   </message>
   <message name="IDS_DEVTOOLS_93c09fb3e06297a9b9504113fa5915ea" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    BBC has seen a loss of 10% of their users for every extra second of page load [Source: WPO Stats]
+    BBC has seen a loss of 10% of their users for every extra second of page load [Source: <ph name="LOCKED_1">WPO Stats</ph>]
   </message>
   <message name="IDS_DEVTOOLS_a3456af9cc3d7cf0c2e288f889f050b7" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    If a site takes &gt;1 second to become interactive, users lose attention, and their perception of completing the page task is broken [Source: Google Developers Blog]
+    If a site takes &gt;1 second to become interactive, users lose attention, and their perception of completing the page task is broken [Source: <ph name="LOCKED_1">Google Developers Blog</ph>]
   </message>
   <message name="IDS_DEVTOOLS_a99169b94755be554b65f8dcc2c2b842" desc="Text in the pop-up dialog when lighthouse is gathering information in the Audits panel">
-    Lighthouse is gathering information about the page to compute your score.
+    <ph name="LOCKED_1">Lighthouse</ph> is gathering information about the page to compute your score.
   </message>
   <message name="IDS_DEVTOOLS_b002a564ceda05dbdf012a48a145d0d3" desc="Status header in the Audits panel">
     Printing
@@ -142,7 +142,7 @@
     Does this page meet the standard of a Progressive Web App
   </message>
   <message name="IDS_DEVTOOLS_b29bb7427a05d89f1dae6ab5459e2d5c" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    By reducing the response size of JSON needed for displaying comments, Instagram saw increased impressions [Source: WPO Stats]
+    By reducing the response size of JSON needed for displaying comments, Instagram saw increased impressions [Source: <ph name="LOCKED_1">WPO Stats</ph>]
   </message>
   <message name="IDS_DEVTOOLS_bcf2457dd80557dd7ebdc1504b6149e6" desc="Text in Audits Controller">
     Does this page follow best practices for modern web development
@@ -154,7 +154,7 @@
     Reports
   </message>
   <message name="IDS_DEVTOOLS_d2f618fd63c7c2028cd527f60e761bc6" desc="Text when lighthouse is loading the page in the Audits panel">
-    Lighthouse is loading your page with mobile emulation.
+    <ph name="LOCKED_1">Lighthouse</ph> is loading your page with mobile emulation.
   </message>
   <message name="IDS_DEVTOOLS_d88946b678e4c2f251d4e292e8142291" desc="Text in Audits Controller">
     SEO
@@ -163,22 +163,22 @@
     Identify and fix common problems that affect your site&apos;s performance, accessibility, and user experience.
   </message>
   <message name="IDS_DEVTOOLS_e339ef3bcd3e92466fa83dc21e690a5b" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    As the number of elements on a page increases from 400 to 6,000, the probability of conversion drops 95%. [Source: Think with Google]
+    As the number of elements on a page increases from 400 to 6,000, the probability of conversion drops 95%. [Source: <ph name="LOCKED_1">Think with Google</ph>]
   </message>
   <message name="IDS_DEVTOOLS_e499f5109f685235e6280179fe22beec" desc="Text that appears when user drag and drop something (for example, a file) in Audits Panel">
     Drop audit file here
   </message>
   <message name="IDS_DEVTOOLS_e8e1638b0449b103a9fdcee6c12d700c" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    14 seconds is the average time a mobile web page takes to load on a 4G connection [Source: Google DoubleClick blog]
+    14 seconds is the average time a mobile web page takes to load on a 4G connection [Source: <ph name="LOCKED_1">Google DoubleClick blog</ph>]
   </message>
   <message name="IDS_DEVTOOLS_e9fe69c2de7c38bbe442e38780d20081" desc="Fast fact in the pop-up dialog when lighthouse is running in the Audits panel">
-    53% of all site visits are abandoned if page load takes more than 3 seconds [Source: Google DoubleClick blog]
+    53% of all site visits are abandoned if page load takes more than 3 seconds [Source: <ph name="LOCKED_1">Google DoubleClick blog</ph>]
   </message>
   <message name="IDS_DEVTOOLS_ea60ba1496b05c6a5816fb75d0665c8d" desc="Text of a DOM element in Audits Status View">
-    If this issue is reproducible, please report it at the Lighthouse GitHub repo.
+    If this issue is reproducible, please report it at the <ph name="LOCKED_1">Lighthouse</ph> <ph name="LOCKED_2">GitHub</ph> repo.
   </message>
   <message name="IDS_DEVTOOLS_f3ddea8173e7a06f7a6a5dadc234fd3d" desc="Text in the pop-up dialog when lighthouse is auditing in the Audits panel">
-    Almost there! Lighthouse is now generating your report.
+    Almost there! <ph name="LOCKED_1">Lighthouse</ph> is now generating your report.
   </message>
   <message name="IDS_DEVTOOLS_fad0ce221c826eede253cb0956ca0700" desc="A search term referring to the linting application named Lighthouse that can be entered in the command menu">
     <ph name="LOCKED_1">lighthouse</ph>
diff --git a/third_party/blink/renderer/devtools/front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js b/third_party/blink/renderer/devtools/front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js
index a6e75d1..6910f2a 100644
--- a/third_party/blink/renderer/devtools/front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/bindings_test_runner/IsolatedFilesystemTestRunner.js
@@ -43,7 +43,7 @@
     BindingsTestRunner.TestFileSystem._instances[this.fileSystemPath] = this;
 
     InspectorFrontendHost.events.dispatchEventToListeners(
-        InspectorFrontendHostAPI.Events.FileSystemAdded,
+        Host.InspectorFrontendHostAPI.Events.FileSystemAdded,
         {fileSystem: {fileSystemPath: this.fileSystemPath, fileSystemName: this.fileSystemPath, type}});
 
     Persistence.isolatedFileSystemManager.addEventListener(
@@ -64,7 +64,7 @@
   reportRemoved: function() {
     delete BindingsTestRunner.TestFileSystem._instances[this.fileSystemPath];
     InspectorFrontendHost.events.dispatchEventToListeners(
-        InspectorFrontendHostAPI.Events.FileSystemRemoved, this.fileSystemPath);
+        Host.InspectorFrontendHostAPI.Events.FileSystemRemoved, this.fileSystemPath);
   },
 
   addFile: function(path, content, lastModified) {
@@ -124,7 +124,7 @@
     child.parent = null;
 
     InspectorFrontendHost.events.dispatchEventToListeners(
-        InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
+        Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
         {changed: [], added: [], removed: [fullPath]});
 
     success();
@@ -149,7 +149,7 @@
     const fullPath = this._fileSystem.fileSystemPath + child.fullPath;
 
     InspectorFrontendHost.events.dispatchEventToListeners(
-        InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
+        Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
         {changed: [], added: [fullPath], removed: []});
 
     return child;
@@ -162,7 +162,7 @@
     const fullPath = this._fileSystem.fileSystemPath + this.fullPath;
 
     InspectorFrontendHost.events.dispatchEventToListeners(
-        InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
+        Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
         {changed: [fullPath], added: [], removed: []});
   },
 
diff --git a/third_party/blink/renderer/devtools/front_end/color_picker/ContrastDetails.js b/third_party/blink/renderer/devtools/front_end/color_picker/ContrastDetails.js
index 18cd246..5cd8415 100644
--- a/third_party/blink/renderer/devtools/front_end/color_picker/ContrastDetails.js
+++ b/third_party/blink/renderer/devtools/front_end/color_picker/ContrastDetails.js
@@ -252,10 +252,10 @@
     InspectorFrontendHost.setEyeDropperActive(enabled);
     if (enabled) {
       InspectorFrontendHost.events.addEventListener(
-          InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._bgColorPickedBound);
+          Host.InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._bgColorPickedBound);
     } else {
       InspectorFrontendHost.events.removeEventListener(
-          InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._bgColorPickedBound);
+          Host.InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._bgColorPickedBound);
     }
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js b/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
index 098433e..65b86456 100644
--- a/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
+++ b/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
@@ -889,10 +889,10 @@
     InspectorFrontendHost.setEyeDropperActive(enabled);
     if (enabled) {
       InspectorFrontendHost.events.addEventListener(
-          InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._colorPickedBound);
+          Host.InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._colorPickedBound);
     } else {
       InspectorFrontendHost.events.removeEventListener(
-          InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._colorPickedBound);
+          Host.InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._colorPickedBound);
     }
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
index 320cd87..be696a7 100644
--- a/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/console/console_strings.grdp
@@ -61,7 +61,7 @@
     [Violation] <ph name="MESSAGETEXT">$1s<ex>console.log(1)</ex></ph>
   </message>
   <message name="IDS_DEVTOOLS_375855385115b292f160b5131495003d" desc="Message element text content in Console View Message of the Console panel">
-    console.clear() was prevented due to &apos;Preserve log&apos;
+    <ph name="LOCKED_1">console.clear()</ph> was prevented due to &apos;Preserve log&apos;
   </message>
   <message name="IDS_DEVTOOLS_383b1deb90603a79d86f3ae82e55a9e2" desc="Text in Console View of the Console panel">
     <ph name="THIS__HIDDENBYFILTERCOUNT">$1s<ex>3</ex></ph> hidden
@@ -235,7 +235,7 @@
     JavaScript context: <ph name="THIS_TITLEFOR_ITEM_">$1s<ex>top</ex></ph>
   </message>
   <message name="IDS_DEVTOOLS_eacaf2bd1c1414e5086cf04573e154ef" desc="Text in Console View of the Console panel">
-    e.g. /event\d/ -cdn url:a.com
+    e.g. <ph name="LOCKED_1">/event\d/ -cdn url:a.com</ph>
   </message>
   <message name="IDS_DEVTOOLS_ebfab4df1bb91688c62d4523eba870a5" desc="Title of a setting under the Console category in Settings">
     Evaluate triggers user activation
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewCompletedView.js b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewCompletedView.js
new file mode 100644
index 0000000..60e9a35
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewCompletedView.js
@@ -0,0 +1,167 @@
+// Copyright 2019 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.
+
+/**
+ * @unrestricted
+ */
+CssOverview.CSSOverviewCompletedView = class extends UI.PanelWithSidebar {
+  constructor(controller) {
+    super('css_overview_completed_view');
+    this.registerRequiredCSS('css_overview/cssOverviewCompletedView.css');
+
+    this._controller = controller;
+    this._formatter = new Intl.NumberFormat('en-US');
+    this._mainContainer = new UI.VBox();
+
+    this._sideBar = new CssOverview.CSSOverviewSidebarPanel();
+    this.splitWidget().setSidebarWidget(this._sideBar);
+    this.splitWidget().setMainWidget(this._mainContainer);
+
+    this._sideBar.addItem(ls`Overview summary`, 'summary');
+    this._sideBar.addItem(ls`Colors`, 'colors');
+    this._sideBar.select('summary');
+
+    this._sideBar.addEventListener(CssOverview.SidebarEvents.ItemSelected, this._sideBarItemSelected, this);
+    this._sideBar.addEventListener(CssOverview.SidebarEvents.Reset, this._sideBarReset, this);
+    this._controller.addEventListener(CssOverview.Events.Reset, this._reset, this);
+    this._render({});
+  }
+
+  _sideBarItemSelected(event) {
+    const section = this._fragment.$(event.data);
+    if (!section)
+      return;
+
+    section.scrollIntoView();
+  }
+
+  _sideBarReset() {
+    this._controller.dispatchEventToListeners(CssOverview.Events.Reset);
+  }
+
+  _reset() {
+    this._mainContainer.element.removeChildren();
+  }
+
+  _render(data) {
+    if (!(data && ('textColors' in data) && ('backgroundColors' in data)))
+      return;
+
+    const {elementStyleStats, elementCount, backgroundColors, textColors, globalStyleStats} = data;
+
+    // Convert rgb values from the computed styles to either undefined or HEX(A) strings.
+    const nonTransparentBackgroundColors = this._getNonTransparentColorStrings(backgroundColors);
+    const nonTransparentTextColors = this._getNonTransparentColorStrings(textColors);
+
+    this._fragment = UI.Fragment.build`
+    <div class="vbox overview-completed-view">
+      <div $="summary" class="results-section summary">
+        <h1>${ls`Overview summary`}</h1>
+
+        <ul>
+          <li>
+            <div class="label">${ls`Elements processed`}</div>
+            <div class="value">${this._formatter.format(elementCount)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`External stylesheets`}</div>
+            <div class="value">${this._formatter.format(globalStyleStats.externalSheets)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Inline style elements`}</div>
+            <div class="value">${this._formatter.format(globalStyleStats.inlineStyles)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Style rules`}</div>
+            <div class="value">${this._formatter.format(globalStyleStats.styleRules)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Media rules`}</div>
+            <div class="value">${this._formatter.format(globalStyleStats.mediaRules)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Type selectors`}</div>
+            <div class="value">${this._formatter.format(elementStyleStats.type.size)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`ID selectors`}</div>
+            <div class="value">${this._formatter.format(elementStyleStats.id.size)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Class selectors`}</div>
+            <div class="value">${this._formatter.format(elementStyleStats.class.size)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Universal selectors`}</div>
+            <div class="value">${this._formatter.format(elementStyleStats.universal.size)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Attribute selectors`}</div>
+            <div class="value">${this._formatter.format(elementStyleStats.attribute.size)}</div>
+          </li>
+          <li>
+            <div class="label">${ls`Non-simple selectors`}</div>
+            <div class="value">${this._formatter.format(elementStyleStats.nonSimple.size)}</div>
+          </li>
+        </ul>
+      </div>
+
+      <div $="colors" class="results-section colors">
+        <h1>${ls`Colors`}</h1>
+        <h2>${ls`Unique background colors: ${nonTransparentBackgroundColors.length}`}</h2>
+        <ul>
+          ${nonTransparentBackgroundColors.map(this._colorsToFragment)}
+        </ul>
+
+        <h2>${ls`Unique text colors: ${nonTransparentTextColors.length}`}</h2>
+        <ul>
+          ${nonTransparentTextColors.map(this._colorsToFragment)}
+        </ul>
+      </div>
+    </div>`;
+
+    this._mainContainer.element.appendChild(this._fragment.element());
+  }
+
+  _colorsToFragment(color) {
+    const colorFormatted =
+        color.hasAlpha() ? color.asString(Common.Color.Format.HEXA) : color.asString(Common.Color.Format.HEX);
+    const blockFragment = UI.Fragment.build`<li>
+      <div class="block" $="color"></div>
+      <div class="block-title">${colorFormatted}</div>
+    </li>`;
+
+    const block = blockFragment.$('color');
+    block.style.backgroundColor = colorFormatted;
+
+    let [h, s, l] = color.hsla();
+    h = Math.round(h * 360);
+    s = Math.round(s * 100);
+    l = Math.round(l * 100);
+
+    // Reduce the lightness of the border to make sure that there's always a visible outline.
+    l = Math.max(0, l - 15);
+
+    const borderString = `1px solid hsl(${h}, ${s}%, ${l}%)`;
+    block.style.border = borderString;
+
+    return blockFragment;
+  }
+
+  _getNonTransparentColorStrings(colors) {
+    return Array.from(colors)
+        .map(colorText => {
+          const color = Common.Color.parse(colorText);
+          if (color.rgba()[3] === 0)
+            return;
+
+          return color;
+        })
+        .filter(color => !!color);
+  }
+
+  setOverviewData(data) {
+    this._render(data);
+  }
+};
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewController.js b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewController.js
new file mode 100644
index 0000000..3ff6f29
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewController.js
@@ -0,0 +1,25 @@
+// Copyright 2019 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.
+
+/**
+ * @unrestricted
+ */
+CssOverview.OverviewController = class extends Common.Object {
+  constructor() {
+    super();
+
+    SDK.targetManager.addEventListener(SDK.TargetManager.Events.InspectedURLChanged, this._reset, this);
+  }
+
+  _reset() {
+    this.dispatchEventToListeners(CssOverview.Events.Reset);
+  }
+};
+
+CssOverview.Events = {
+  RequestOverviewStart: Symbol('RequestOverviewStart'),
+  RequestOverviewCancel: Symbol('RequestOverviewCancel'),
+  OverviewCompleted: Symbol('OverviewCompleted'),
+  Reset: Symbol('Reset'),
+};
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewModel.js b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewModel.js
new file mode 100644
index 0000000..6f64b33
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewModel.js
@@ -0,0 +1,130 @@
+// Copyright 2019 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.
+
+/**
+ * @unrestricted
+ */
+CssOverview.CSSOverviewModel = class extends SDK.SDKModel {
+  /**
+   * @param {!SDK.Target} target
+   */
+  constructor(target) {
+    super(target);
+
+    this._runtimeAgent = target.runtimeAgent();
+    this._cssAgent = target.cssAgent();
+    this._domAgent = target.domAgent();
+  }
+
+  getFlattenedDocument() {
+    return this._domAgent.getFlattenedDocument(-1, true);
+  }
+
+  getComputedStyleForNode(nodeId) {
+    return this._cssAgent.getComputedStyleForNode(nodeId);
+  }
+
+  async getGlobalStylesheetStats() {
+    // There are no ways to pull CSSOM values directly today, due to its unserializable format,
+    // so instead we execute some JS within the page that extracts the relevant data and send that instead.
+    const expression = `(function() {
+      let styleRules = 0;
+      let mediaRules = 0;
+      let inlineStyles = 0;
+      let externalSheets = 0;
+      for (const { rules, href } of document.styleSheets) {
+        if (href) {
+          externalSheets++;
+        } else {
+          inlineStyles++;
+        }
+
+        for (const rule of rules) {
+          if ('selectorText' in rule) {
+            styleRules++;
+          } else if ('conditionText' in rule) {
+            mediaRules++;
+          }
+        }
+      }
+
+      return {
+        styleRules,
+        mediaRules,
+        inlineStyles,
+        externalSheets
+      }
+    })()`;
+    const {result} = await this._runtimeAgent.invoke_evaluate({expression, returnByValue: true});
+
+    // TODO(paullewis): Handle errors properly.
+    if (result.type !== 'object')
+      return;
+
+    return result.value;
+  }
+
+  async getStylesStatsForNode(nodeId) {
+    const stats = {
+      // Simple.
+      type: new Set(),
+      class: new Set(),
+      id: new Set(),
+      universal: new Set(),
+      attribute: new Set(),
+
+      // Non-simple.
+      nonSimple: new Set()
+    };
+
+    const matches = await this._cssAgent.invoke_getMatchedStylesForNode({nodeId});
+    if (!matches || !matches.matchedCSSRules || !matches.matchedCSSRules.length)
+      return;
+
+    matches.matchedCSSRules.forEach(cssRule => {
+      const {matchingSelectors} = cssRule;
+      const {origin, selectorList} = cssRule.rule;
+      const isExternalSheet = origin === 'regular';
+      if (!isExternalSheet || !selectorList)
+        return;
+
+
+      const selectors = matchingSelectors.map(idx => selectorList.selectors[idx]);
+
+      // Each group of selectors, e.g. foo.baz, foo .bar, foo { ... }
+      for (const {text} of selectors) {
+        // Each group that was used.
+        for (const selectorGroup of text.split(',')) {
+          // Each selector in the group.
+          for (const selector of selectorGroup.split(/[\t\n\f\r ]+/g)) {
+            if (selector.startsWith('.')) {
+              // Class.
+              stats.class.add(selector);
+            } else if (selector.startsWith('#')) {
+              // Id.
+              stats.id.add(selector);
+            } else if (selector.startsWith('*')) {
+              // Universal.
+              stats.universal.add(selector);
+            } else if (selector.startsWith('[')) {
+              // Attribute.
+              stats.attribute.add(selector);
+            } else {
+              // Type or non-simple selector.
+              const specialChars = /[#\.:\[\]|\+>~]/;
+              if (specialChars.test(selector))
+                stats.nonSimple.add(selector);
+              else
+                stats.type.add(selector);
+            }
+          }
+        }
+      }
+    });
+
+    return stats;
+  }
+};
+
+SDK.SDKModel.register(CssOverview.CSSOverviewModel, SDK.Target.Capability.DOM, false);
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewPanel.js b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewPanel.js
new file mode 100644
index 0000000..0520b58
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewPanel.js
@@ -0,0 +1,159 @@
+// Copyright 2019 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.
+
+/**
+ * @unrestricted
+ */
+CssOverview.CSSOverviewPanel = class extends UI.Panel {
+  constructor() {
+    super('css_overview');
+    this.registerRequiredCSS('css_overview/cssOverview.css');
+    this.element.classList.add('css-overview-panel');
+
+    this._controller = new CssOverview.OverviewController();
+    this._startView = new CssOverview.CSSOverviewStartView(this._controller);
+    this._processingView = new CssOverview.CSSOverviewProcessingView(this._controller);
+    this._completedView = new CssOverview.CSSOverviewCompletedView(this._controller);
+
+    const [model] = SDK.targetManager.models(CssOverview.CSSOverviewModel);
+    this._model = model;
+    this._controller.addEventListener(CssOverview.Events.RequestOverviewStart, this._startOverview, this);
+    this._controller.addEventListener(CssOverview.Events.RequestOverviewCancel, this._cancelOverview, this);
+    this._controller.addEventListener(CssOverview.Events.OverviewCompleted, this._overviewCompleted, this);
+    this._controller.addEventListener(CssOverview.Events.Reset, this._reset, this);
+
+    this._reset();
+  }
+
+  _reset() {
+    this._backgroundColors = new Set();
+    this._textColors = new Set();
+    this._fontSizes = new Map();
+    this._elementCount = 0;
+    this._elementStyleStats = {
+      // Simple.
+      type: new Set(),
+      class: new Set(),
+      id: new Set(),
+      universal: new Set(),
+      attribute: new Set(),
+
+      // Non-simple.
+      nonSimple: new Set()
+    };
+    this._cancelled = false;
+    this._globalStyleStats = {styleRules: 0, mediaRules: 0, inlineStyles: 0, externalSheets: 0};
+    this._renderInitialView();
+  }
+
+  _renderInitialView() {
+    this._processingView.hideWidget();
+    this._completedView.hideWidget();
+
+    this._startView.show(this.contentElement);
+  }
+
+  _renderOverviewStartedView(elementsHandled = 0, total = 0) {
+    this._startView.hideWidget();
+    this._completedView.hideWidget();
+
+    this._processingView.show(this.contentElement);
+    this._processingView.setElementsHandled(elementsHandled, total);
+  }
+
+  _renderOverviewCompletedView() {
+    this._startView.hideWidget();
+    this._processingView.hideWidget();
+
+    this._completedView.show(this.contentElement);
+    this._completedView.setOverviewData({
+      backgroundColors: this._backgroundColors,
+      textColors: this._textColors,
+      globalStyleStats: this._globalStyleStats,
+      elementStyleStats: this._elementStyleStats,
+      fontSizes: this._fontSizes,
+      elementCount: this._elementCount
+    });
+  }
+
+  async _startOverview() {
+    this._renderOverviewStartedView();
+
+    const document = await this._model.getFlattenedDocument();
+    if (this._cancelled) {
+      this._reset();
+      return;
+    }
+
+    // 1. Get the global style stats.
+    const globalStyleStats = await this._model.getGlobalStylesheetStats();
+    if (globalStyleStats)
+      this._globalStyleStats = globalStyleStats;
+
+    // 2. Get the total element count.
+    this._elementCount = document.length;
+
+    // 3. Process every element in the doc.
+    for (let idx = 0; idx < document.length; idx++) {
+      if (this._cancelled) {
+        this._reset();
+        return;
+      }
+
+      const node = document[idx];
+      const [computedStyles, styleStats] = await Promise.all(
+          [this._model.getComputedStyleForNode(node.nodeId), this._model.getStylesStatsForNode(node.nodeId)]);
+
+      // 3a. Capture any colors from the computed styles.
+      if (computedStyles) {
+        const backgroundColor = this._getStyleValue(computedStyles, 'background-color');
+        if (backgroundColor)
+          this._backgroundColors.add(backgroundColor);
+
+        if (node.nodeType === Node.TEXT_NODE) {
+          const textColor = this._getStyleValue(computedStyles, 'color');
+          this._textColors.add(textColor);
+
+          const fontSize = this._getStyleValue(computedStyles, 'font-size');
+          if (!this._fontSizes.has(fontSize))
+            this._fontSizes.set(fontSize, 0);
+
+          this._fontSizes.set(fontSize, this._fontSizes.get(fontSize) + 1);
+        }
+      }
+
+      // 3b. Tally the selector stats.
+      if (styleStats) {
+        for (const section of Object.keys(this._elementStyleStats)) {
+          if (!styleStats[section])
+            continue;
+
+          for (const value of styleStats[section])
+            this._elementStyleStats[section].add(value);
+        }
+      }
+
+      this._renderOverviewStartedView(idx + 1, document.length);
+    }
+
+    // 4. Finish.
+    this._controller.dispatchEventToListeners(CssOverview.Events.OverviewCompleted);
+  }
+
+  _getStyleValue(styles, name) {
+    const item = styles.filter(style => style.name === name);
+    if (!item.length)
+      return;
+
+    return item[0].value;
+  }
+
+  _cancelOverview() {
+    this._cancelled = true;
+  }
+
+  _overviewCompleted() {
+    this._renderOverviewCompletedView();
+  }
+};
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewProcessingView.js b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewProcessingView.js
new file mode 100644
index 0000000..169c363
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewProcessingView.js
@@ -0,0 +1,44 @@
+// Copyright 2019 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.
+
+/**
+ * @unrestricted
+ */
+CssOverview.CSSOverviewProcessingView = class extends UI.Widget {
+  constructor(controller) {
+    super();
+    this.registerRequiredCSS('css_overview/cssOverviewProcessingView.css');
+
+    this._formatter = new Intl.NumberFormat('en-US');
+    this._controller = controller;
+    this._render();
+  }
+
+  _render() {
+    const cancelButton = UI.createTextButton(
+        ls`Cancel`, () => this._controller.dispatchEventToListeners(CssOverview.Events.RequestOverviewCancel), '',
+        true /* primary */);
+    this.setDefaultFocusedElement(cancelButton);
+
+    this.fragment = UI.Fragment.build`
+      <div class="vbox overview-processing-view">
+        <h1>Processing page</h1>
+        <div>${cancelButton}</div>
+
+        <h2 $="processed"></h2>
+      </div>
+    `;
+
+    this.contentElement.appendChild(this.fragment.element());
+    this.contentElement.style.overflow = 'auto';
+  }
+
+  setElementsHandled(handled = 0, total = 0) {
+    // TODO(aerotwist): We might want to switch this to using Intl.PluralRules in the future
+    // @see https://v8.dev/features/intl-pluralrules
+    const elementsTotal = total > 0 ? ls`document elements` : ls`document element`;
+    this.fragment.$('processed').textContent =
+        ls`Processed ${this._formatter.format(handled)} of ${this._formatter.format(total)} ${elementsTotal}.`;
+  }
+};
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewSidebarPanel.js b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewSidebarPanel.js
new file mode 100644
index 0000000..3955421
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewSidebarPanel.js
@@ -0,0 +1,73 @@
+// Copyright 2019 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.
+
+CssOverview.CSSOverviewSidebarPanel = class extends UI.VBox {
+  static get ITEM_CLASS_NAME() {
+    return 'overview-sidebar-panel-item';
+  }
+
+  static get SELECTED() {
+    return 'selected';
+  }
+
+  constructor() {
+    super(true);
+
+    this.registerRequiredCSS('css_overview/cssOverviewSidebarPanel.css');
+    this.contentElement.classList.add('overview-sidebar-panel');
+    this.contentElement.addEventListener('click', this._onItemClick.bind(this));
+
+    // Clear overview.
+    const clearResultsButton = new UI.ToolbarButton(ls`Clear overview`, 'largeicon-clear');
+    clearResultsButton.addEventListener(UI.ToolbarButton.Events.Click, this._reset, this);
+
+    // Toolbar.
+    const toolbarElement = this.contentElement.createChild('div', 'overview-toolbar');
+    const toolbar = new UI.Toolbar('', toolbarElement);
+    toolbar.appendToolbarItem(clearResultsButton);
+  }
+
+  addItem(name, id) {
+    const item = this.contentElement.createChild('div', CssOverview.CSSOverviewSidebarPanel.ITEM_CLASS_NAME);
+    item.textContent = name;
+    item.dataset.id = id;
+  }
+
+  _reset() {
+    this.dispatchEventToListeners(CssOverview.SidebarEvents.Reset);
+  }
+
+  _deselectAllItems() {
+    const items = this.contentElement.querySelectorAll(`.${CssOverview.CSSOverviewSidebarPanel.ITEM_CLASS_NAME}`);
+    for (const item of items)
+      item.classList.remove(CssOverview.CSSOverviewSidebarPanel.SELECTED);
+  }
+
+  _onItemClick(event) {
+    const target = event.path[0];
+    if (!target.classList.contains(CssOverview.CSSOverviewSidebarPanel.ITEM_CLASS_NAME))
+      return;
+
+    const {id} = target.dataset;
+    this.select(id);
+    this.dispatchEventToListeners(CssOverview.SidebarEvents.ItemSelected, id);
+  }
+
+  select(id) {
+    const target = this.contentElement.querySelector(`[data-id=${CSS.escape(id)}]`);
+    if (!target)
+      return;
+
+    if (target.classList.contains(CssOverview.CSSOverviewSidebarPanel.SELECTED))
+      return;
+
+    this._deselectAllItems();
+    target.classList.add(CssOverview.CSSOverviewSidebarPanel.SELECTED);
+  }
+};
+
+CssOverview.SidebarEvents = {
+  ItemSelected: Symbol('ItemSelected'),
+  Reset: Symbol('Reset')
+};
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewStartView.js b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewStartView.js
new file mode 100644
index 0000000..d19cc9e
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/CSSOverviewStartView.js
@@ -0,0 +1,34 @@
+// Copyright 2019 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.
+
+/**
+ * @unrestricted
+ */
+CssOverview.CSSOverviewStartView = class extends UI.Widget {
+  constructor(controller) {
+    super();
+    this.registerRequiredCSS('css_overview/cssOverviewStartView.css');
+
+    this._controller = controller;
+    this._render();
+  }
+
+  _render() {
+    const startButton = UI.createTextButton(
+        ls`Capture overview`, () => this._controller.dispatchEventToListeners(CssOverview.Events.RequestOverviewStart),
+        '', true /* primary */);
+
+    this.setDefaultFocusedElement(startButton);
+
+    const fragment = UI.Fragment.build`
+      <div class="vbox overview-start-view">
+        <h1>${ls`CSS Overview`}</h1>
+        <div>${startButton}</div>
+      </div>
+    `;
+
+    this.contentElement.appendChild(fragment.element());
+    this.contentElement.style.overflow = 'auto';
+  }
+};
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/cssOverview.css b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverview.css
new file mode 100644
index 0000000..944bafacf
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverview.css
@@ -0,0 +1,9 @@
+/**
+ * Copyright 2019 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.
+ */
+
+.css-overview-panel {
+  overflow: hidden;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewCompletedView.css b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewCompletedView.css
new file mode 100644
index 0000000..fa6f0d4
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewCompletedView.css
@@ -0,0 +1,81 @@
+/**
+ * Copyright 2019 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.
+ */
+
+.overview-completed-view {
+  overflow: auto;
+}
+
+.overview-completed-view .summary ul,
+.overview-completed-view .colors ul {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.overview-completed-view .summary ul {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, 140px);
+  grid-gap: 16px;
+}
+
+.overview-completed-view .colors ul li {
+  display: inline-block;
+  padding: 0;
+  margin: 0 0 16px 0;
+}
+
+.overview-completed-view .summary ul li {
+  display: flex;
+  flex-direction: column;
+  grid-column-start: auto;
+}
+
+.overview-completed-view li .label {
+  font-size: 12px;
+  padding-bottom: 2px;
+}
+
+.overview-completed-view li .value {
+  font-size: 17px;
+}
+
+.overview-completed-view ul li span {
+  font-weight: bold;
+}
+
+.block {
+  width: 65px;
+  height: 25px;
+  border-radius: 3px;
+  margin-right: 16px;
+}
+
+.block-title {
+  padding-top: 4px;
+  font-size: 12px;
+  color: #303942;
+  text-transform: uppercase;
+  letter-spacing: 0;
+}
+
+.results-section {
+  flex-shrink: 0;
+  padding: 28px;
+  border-bottom: 1px solid #E6E6E6;
+}
+
+.results-section h1 {
+  font-size: 15px;
+  font-weight: normal;
+  padding: 0;
+  margin: 0 0 20px 0;
+}
+
+.results-section.colors h2 {
+  margin-top: 20px;
+  font-size: 13px;
+  font-weight: normal;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewProcessingView.css b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewProcessingView.css
new file mode 100644
index 0000000..a91165b1
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewProcessingView.css
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2019 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.
+ */
+
+.overview-processing-view {
+  overflow: hidden;
+}
+
+.overview-processing-view {
+  overflow: hidden;
+  padding: 16px;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+}
+
+.overview-processing-view h1 {
+  font-size: 16px;
+  text-align: center;
+  font-weight: normal;
+  margin: 0;
+  padding: 8px;
+}
+
+.overview-processing-view h2 {
+  font-size: 12px;
+  text-align: center;
+  font-weight: normal;
+  margin: 0;
+  padding-top: 32px;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewSidebarPanel.css b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewSidebarPanel.css
new file mode 100644
index 0000000..45025dc
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewSidebarPanel.css
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2019 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.
+ */
+
+.overview-sidebar-panel {
+  overflow: auto;
+  display: flex;
+  background: #F3F3F3;
+}
+
+.overview-sidebar-panel-item {
+  height: 30px;
+  padding-left: 30px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+}
+
+.overview-sidebar-panel-item:hover,
+.overview-sidebar-panel-item:focus {
+  background: rgb(234, 234, 234);
+}
+
+.overview-sidebar-panel-item.selected {
+  background: #1A73E8;
+  color: #FFFFFF;
+}
+
+.overview-toolbar {
+  border-bottom: 1px solid rgb(204, 204, 204);
+}
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewStartView.css b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewStartView.css
new file mode 100644
index 0000000..1c3d870
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/cssOverviewStartView.css
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2019 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.
+ */
+
+.overview-start-view {
+  overflow: hidden;
+  padding: 16px;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+}
+
+.overview-start-view h1 {
+  font-size: 16px;
+  text-align: center;
+  font-weight: normal;
+  margin: 0;
+  padding: 8px;
+}
+
+.overview-start-view div {
+  font-size: 12px;
+  text-align: center;
+  font-weight: normal;
+  margin: 0;
+  padding-bottom: 44px;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/css_overview_strings.grdp b/third_party/blink/renderer/devtools/front_end/css_overview/css_overview_strings.grdp
new file mode 100644
index 0000000..87042a3
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/css_overview_strings.grdp
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_01ac702a8b31775d1cdbc30ec453e611" desc="Label for the number of non-simple selectors in the CSS Overview report">
+    Non-simple selectors
+  </message>
+  <message name="IDS_DEVTOOLS_1049ae5279b7a1b50dddec905ed992df" desc="Label for the number ofstyle rules in CSS Overview report">
+    Style rules
+  </message>
+  <message name="IDS_DEVTOOLS_196c5897880c864a303527f5d8e7cb77" desc="Label for unique background colors in the CSS Overview report">
+    Unique background colors: <ph name="BACKGROUNDCOLORS_LENGTH">$1s<ex>17</ex></ph>
+  </message>
+  <message name="IDS_DEVTOOLS_1f1621cad66c8f661842bf9392d8559b" desc="Label for the summary in the CSS Overview report">
+    Overview summary
+  </message>
+  <message name="IDS_DEVTOOLS_23717568804f63f75e7596dcced3119e" desc="Label for the number of universal selectors in the CSS Overview report">
+    Universal selectors
+  </message>
+  <message name="IDS_DEVTOOLS_396ea220fc4d34d57a5a505de918bdde" desc="Singular of 'document element' in the processing update message">
+    document element
+  </message>
+  <message name="IDS_DEVTOOLS_4b3e6e4359cfb882a3b8b551f38a92c7" desc="Label for unique text colors in the CSS Overview Panel">
+    Unique text colors: <ph name="TEXTCOLORS_LENGTH">$1s<ex>32</ex></ph>
+  </message>
+  <message name="IDS_DEVTOOLS_52798275481d47b10f5b8e31310b155e" desc="Label for the update message in the processing stage of CSS Overview">
+    Processed <ph name="THIS__FORMATTER_FORMAT_HANDLED_">$1s<ex>23</ex></ph> of <ph name="THIS__FORMATTER_FORMAT_TOTAL_">$2s<ex>1,223</ex></ph> <ph name="ELEMENTSTOTAL">$3s<ex>document elements</ex></ph>.
+  </message>
+  <message name="IDS_DEVTOOLS_5d50889672f6f860d14f502de3de1957" desc="Title of colors subsection in the CSS Overview Panel">
+    Colors
+  </message>
+  <message name="IDS_DEVTOOLS_62d79b9db9edfb64bd8c538cb9ed0190" desc="Label for the number of media rules in the CSS Overview report">
+    Media rules
+  </message>
+  <message name="IDS_DEVTOOLS_6ad170a0581c9c116ca254758bd0c5b9" desc="Label for the number of ID selectors in the CSS Overview report">
+    ID selectors
+  </message>
+  <message name="IDS_DEVTOOLS_811b0dc0576a9743353448d08b400f45" desc="Label for the number of Attribute selectors in the CSS Overview report">
+    Attribute selectors
+  </message>
+  <message name="IDS_DEVTOOLS_8781fdba66e6cf7177ce03ea3db8a35a" desc="Label for the number of class selectors in the CSS Overview report">
+    Class selectors
+  </message>
+  <message name="IDS_DEVTOOLS_8a2d39bc733e82310760e2767889673a" desc="Title of the CSS Overview Panel">
+    CSS Overview
+  </message>
+  <message name="IDS_DEVTOOLS_95d366a44bd7bad09d9916e05b512cc6" desc="Label for the number of External stylesheets in the CSS Overview report">
+    External stylesheets
+  </message>
+  <message name="IDS_DEVTOOLS_a9427e421abd9fd7ba45212c25ba55a5" desc="Plural of 'document element' in the processing update message">
+    document elements
+  </message>
+  <message name="IDS_DEVTOOLS_aff1062318f6d07520a0feba60f3f253" desc="Label for the number of type selectors in the CSS Overview report">
+    Type selectors
+  </message>
+  <message name="IDS_DEVTOOLS_cd9c2b5232bcbda828c542a9f5306f8e" desc="Label for the 'Elements processed' button in the CSS Overview report">
+    Elements processed
+  </message>
+  <message name="IDS_DEVTOOLS_d4a993a4b0d13c2cc4ce9387af1604ce" desc="Label for the 'Clear overview' button in the CSS Overview report">
+    Clear overview
+  </message>
+  <message name="IDS_DEVTOOLS_e9c6934e961dad71d311f68c679dfd3b" desc="Label for the capture button in the CSS Overview Panel">
+    Capture overview
+  </message>
+  <message name="IDS_DEVTOOLS_f1637e0528f8c9d37be5de8b50fd2926" desc="Label for the number of inline style elements in the CSS Overview report">
+    Inline style elements
+  </message>
+</grit-part>
diff --git a/third_party/blink/renderer/devtools/front_end/css_overview/module.json b/third_party/blink/renderer/devtools/front_end/css_overview/module.json
new file mode 100644
index 0000000..4e447cd
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/css_overview/module.json
@@ -0,0 +1,34 @@
+{
+  "extensions": [
+      {
+          "type": "view",
+          "location": "panel",
+          "id": "cssoverview",
+          "title": "CSS Overview",
+          "className": "CssOverview.CSSOverviewPanel",
+          "order": 95,
+          "experiment": "cssOverview"
+      }
+  ],
+  "dependencies": [
+      "elements",
+      "ui",
+      "sdk"
+  ],
+  "scripts": [
+      "CSSOverviewController.js",
+      "CSSOverviewModel.js",
+      "CSSOverviewStartView.js",
+      "CSSOverviewProcessingView.js",
+      "CSSOverviewCompletedView.js",
+      "CSSOverviewSidebarPanel.js",
+      "CSSOverviewPanel.js"
+    ],
+  "resources": [
+      "cssOverview.css",
+      "cssOverviewStartView.css",
+      "cssOverviewProcessingView.css",
+      "cssOverviewCompletedView.css",
+      "cssOverviewSidebarPanel.css"
+  ]
+}
diff --git a/third_party/blink/renderer/devtools/front_end/devices/DevicesView.js b/third_party/blink/renderer/devtools/front_end/devices/DevicesView.js
index 3b8e4808..59f72dda 100644
--- a/third_party/blink/renderer/devtools/front_end/devices/DevicesView.js
+++ b/third_party/blink/renderer/devtools/front_end/devices/DevicesView.js
@@ -44,13 +44,12 @@
     this._selectSidebarListItem(this._discoveryListItem, this._discoveryView);
 
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.DevicesUpdated, this._devicesUpdated, this);
+        Host.InspectorFrontendHostAPI.Events.DevicesUpdated, this._devicesUpdated, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
+        Host.InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.DevicesPortForwardingStatusChanged, this._devicesPortForwardingStatusChanged,
-        this);
-
+        Host.InspectorFrontendHostAPI.Events.DevicesPortForwardingStatusChanged,
+        this._devicesPortForwardingStatusChanged, this);
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/devices/devices_strings.grdp b/third_party/blink/renderer/devtools/front_end/devices/devices_strings.grdp
index 8ca703dd..1a3c208 100644
--- a/third_party/blink/renderer/devtools/front_end/devices/devices_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/devices/devices_strings.grdp
@@ -4,7 +4,7 @@
     Port Forwarding:
   </message>
   <message name="IDS_DEVTOOLS_034fd013261994214d1e8cb42571d191" desc="Text in Devices View of the Remote Devices tab">
-    Need help? Read Chrome <ph name="DOCUMENTATIONLINK">$1s<ex>documentation link</ex></ph>.
+    Need help? Read <ph name="LOCKED_1">Chrome</ph> <ph name="DOCUMENTATIONLINK">$1s<ex>documentation link</ex></ph>.
   </message>
   <message name="IDS_DEVTOOLS_03fc5c0bd59679c442b86074c7a7cc3a" desc="Text in Devices View of the Remote Devices tab">
     Port forwarding
diff --git a/third_party/blink/renderer/devtools/front_end/devtools_app.json b/third_party/blink/renderer/devtools/front_end/devtools_app.json
index 79074d56..f50defe 100644
--- a/third_party/blink/renderer/devtools/front_end/devtools_app.json
+++ b/third_party/blink/renderer/devtools/front_end/devtools_app.json
@@ -8,6 +8,7 @@
     { "name": "animation" },
     { "name": "audits" },
     { "name": "browser_debugger" },
+    { "name": "css_overview" },
     { "name": "cookie_table" },
     { "name": "devices" },
     { "name": "elements" },
diff --git a/third_party/blink/renderer/devtools/front_end/emulated_devices/emulated_devices_strings.grdp b/third_party/blink/renderer/devtools/front_end/emulated_devices/emulated_devices_strings.grdp
index 053c7c38e..2a2c2260 100644
--- a/third_party/blink/renderer/devtools/front_end/emulated_devices/emulated_devices_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/emulated_devices/emulated_devices_strings.grdp
@@ -4,99 +4,99 @@
     Laptop with MDPI screen
   </message>
   <message name="IDS_DEVTOOLS_079a3d134d2387adc52b2b3895f85e55" desc="Title of the iPhone X device">
-    iPhone X
+    <ph name="LOCKED_1">iPhone X</ph>
   </message>
   <message name="IDS_DEVTOOLS_0d1160fe394d87ddbc2ca8e7cc25379d" desc="Title of the Laptop with touch device">
     Laptop with touch
   </message>
   <message name="IDS_DEVTOOLS_0e5aeadd53726e96d7d9faa671b423ae" desc="Title of the Kindle Fire HDX device">
-    Kindle Fire HDX
+    <ph name="LOCKED_1">Kindle Fire HDX</ph>
   </message>
   <message name="IDS_DEVTOOLS_1b9018182a49e16ba85bb095f224867c" desc="Title of the iPad device">
-    iPad
+    <ph name="LOCKED_1">iPad</ph>
   </message>
   <message name="IDS_DEVTOOLS_2235bd527cc1684230b1db146c570a9d" desc="Title of the Pixel 2 device">
-    Pixel 2
+    <ph name="LOCKED_1">Pixel 2</ph>
   </message>
   <message name="IDS_DEVTOOLS_2c17ed2506add59c15ea763801cfafb1" desc="Title of the Nexus 6 device">
-    Nexus 6
+    <ph name="LOCKED_1">Nexus 6</ph>
   </message>
   <message name="IDS_DEVTOOLS_2d1fbe61c136940101281ec1df805c01" desc="Title of the Nexus 5 device">
-    Nexus 5
+    <ph name="LOCKED_1">Nexus 5</ph>
   </message>
   <message name="IDS_DEVTOOLS_2d9a25096b47a022c95787ab90308d6b" desc="Title of the LG Optimus L70 device">
-    LG Optimus L70
+    <ph name="LOCKED_1">LG Optimus L70</ph>
   </message>
   <message name="IDS_DEVTOOLS_4a321c67ad4bac0f3386f5e257b1fba3" desc="Title of the Laptop with HiDPI screen device">
     Laptop with HiDPI screen
   </message>
   <message name="IDS_DEVTOOLS_6247fa438881677e5f910b89db1974a7" desc="Title of the Nokia N9 device">
-    Nokia N9
+    <ph name="LOCKED_1">Nokia N9</ph>
   </message>
   <message name="IDS_DEVTOOLS_659810deafc0279869114daf8446ef4a" desc="Title of the Nexus 10 device">
-    Nexus 10
+    <ph name="LOCKED_1">Nexus 10</ph>
   </message>
   <message name="IDS_DEVTOOLS_6d9131a4a569108e7d19038b27111c86" desc="Title of the Nexus 5X device">
-    Nexus 5X
+    <ph name="LOCKED_1">Nexus 5X</ph>
   </message>
   <message name="IDS_DEVTOOLS_71e3580e3c3a03f17798ebccaad9e6cb" desc="Title of the Nexus 7 device">
-    Nexus 7
+    <ph name="LOCKED_1">Nexus 7</ph>
   </message>
   <message name="IDS_DEVTOOLS_71f859515e31ad9bb417977247315f0a" desc="Title of the iPad Pro device">
-    iPad Pro
+    <ph name="LOCKED_1">iPad Pro</ph>
   </message>
   <message name="IDS_DEVTOOLS_76c4064f51952f873baa0f9a201b49a3" desc="Title of the Nokia Lumia 520 device">
-    Nokia Lumia 520
+    <ph name="LOCKED_1">Nokia Lumia 520</ph>
   </message>
   <message name="IDS_DEVTOOLS_8be08fbcf1f787159e451a97a7c1cc90" desc="Title of the iPhone 6/7/8 Plus device">
-    iPhone 6/7/8 Plus
+    <ph name="LOCKED_1">iPhone 6/7/8 Plus</ph>
   </message>
   <message name="IDS_DEVTOOLS_8d0c6a99bcfbb75724c123476d683d3f" desc="Title of the iPhone 4 device">
-    iPhone 4
+    <ph name="LOCKED_1">iPhone 4</ph>
   </message>
   <message name="IDS_DEVTOOLS_99d1a0f994cdce14eea8b6ae823d5a76" desc="Title of the Galaxy S III device">
-    Galaxy S III
+    <ph name="LOCKED_1">Galaxy S III</ph>
   </message>
   <message name="IDS_DEVTOOLS_a75624548994321c1cc7173b9933438b" desc="Title of the Microsoft Lumia 550 device">
-    Microsoft Lumia 550
+    <ph name="LOCKED_1">Microsoft Lumia 550</ph>
   </message>
   <message name="IDS_DEVTOOLS_aa5fa1bdadb078da054c34752031a5e9" desc="Title of the iPhone 6/7/8 device">
-    iPhone 6/7/8
+    <ph name="LOCKED_1">iPhone 6/7/8</ph>
   </message>
   <message name="IDS_DEVTOOLS_b9e5554cdd9d9f2d3df1f30914b617c2" desc="Title of the Galaxy Note 3 device">
-    Galaxy Note 3
+    <ph name="LOCKED_1">Galaxy Note 3</ph>
   </message>
   <message name="IDS_DEVTOOLS_be6a9d69eff86af39e5a73345c585da6" desc="Title of the iPad Mini device">
-    iPad Mini
+    <ph name="LOCKED_1">iPad Mini</ph>
   </message>
   <message name="IDS_DEVTOOLS_c99b8431589c0273278e87ebf44a7fd6" desc="Title of the iPhone 5/SE device">
-    iPhone 5/SE
+    <ph name="LOCKED_1">iPhone 5/SE</ph>
   </message>
   <message name="IDS_DEVTOOLS_cc810fb8b8bc7c7d6a8327f0e7b2aead" desc="Title of the Nexus 6P device">
-    Nexus 6P
+    <ph name="LOCKED_1">Nexus 6P</ph>
   </message>
   <message name="IDS_DEVTOOLS_d4f2f5453f28c5fd35c68f52e4da07cd" desc="Title of the BlackBerry Z30 device">
-    BlackBerry Z30
+    <ph name="LOCKED_1">BlackBerry Z30</ph>
   </message>
   <message name="IDS_DEVTOOLS_d6162eb3dd4dc5a9bc67222e24f19b25" desc="Title of the Nexus 4 device">
-    Nexus 4
+    <ph name="LOCKED_1">Nexus 4</ph>
   </message>
   <message name="IDS_DEVTOOLS_d8860bf736e83cfb5c13ee82a8f61a43" desc="Title of the Microsoft Lumia 950 device">
-    Microsoft Lumia 950
+    <ph name="LOCKED_1">Microsoft Lumia 950</ph>
   </message>
   <message name="IDS_DEVTOOLS_dfd6e576e036db4f857c8c03c977d975" desc="Title of the Galaxy Note II device">
-    Galaxy Note II
+    <ph name="LOCKED_1">Galaxy Note II</ph>
   </message>
   <message name="IDS_DEVTOOLS_e0261d2f1730ebff89bbde9a4bc74ac5" desc="Title of the JioPhone 2 device">
-    JioPhone 2
+    <ph name="LOCKED_1">JioPhone 2</ph>
   </message>
   <message name="IDS_DEVTOOLS_e770076f957c87605ff906d0ed2c4b52" desc="Title of the Blackberry PlayBook device">
-    Blackberry PlayBook
+    <ph name="LOCKED_1">Blackberry PlayBook</ph>
   </message>
   <message name="IDS_DEVTOOLS_eb850446088fbc75e974788cc2b39caa" desc="Title of the Pixel 2 XL device">
-    Pixel 2 XL
+    <ph name="LOCKED_1">Pixel 2 XL</ph>
   </message>
   <message name="IDS_DEVTOOLS_f8896f769d62b6102e48039154c4ca5e" desc="Title of the Galaxy S5 device">
-    Galaxy S5
+    <ph name="LOCKED_1">Galaxy S5</ph>
   </message>
 </grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/emulation/emulation_strings.grdp b/third_party/blink/renderer/devtools/front_end/emulation/emulation_strings.grdp
index 4eaa21f..ee369b04 100644
--- a/third_party/blink/renderer/devtools/front_end/emulation/emulation_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/emulation/emulation_strings.grdp
@@ -100,7 +100,7 @@
     Latitude
   </message>
   <message name="IDS_DEVTOOLS_414b730ab2cf9123d9230740864ffeec" desc="A context menu item in the Device Mode Toolbar of the Device Toolbar">
-    Close DevTools
+    Close <ph name="LOCKED_1">DevTools</ph>
   </message>
   <message name="IDS_DEVTOOLS_45f80006d304f294d6c1de50c244856e" desc="Text of add geolocations button in Geolocations Settings Tab of the Device Toolbar">
     Add location...
diff --git a/third_party/blink/renderer/devtools/front_end/extensions/ExtensionServer.js b/third_party/blink/renderer/devtools/front_end/extensions/ExtensionServer.js
index 5953633..c1a2a097 100644
--- a/third_party/blink/renderer/devtools/front_end/extensions/ExtensionServer.js
+++ b/third_party/blink/renderer/devtools/front_end/extensions/ExtensionServer.js
@@ -83,7 +83,7 @@
     window.addEventListener('message', this._onWindowMessage.bind(this), false);  // Only for main window.
 
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.SetInspectedTabId, this._setInspectedTabId, this);
+        Host.InspectorFrontendHostAPI.Events.SetInspectedTabId, this._setInspectedTabId, this);
 
     this._initExtensions();
   }
diff --git a/third_party/blink/renderer/devtools/front_end/externs.js b/third_party/blink/renderer/devtools/front_end/externs.js
index 48017b8..eaaf2a7 100644
--- a/third_party/blink/renderer/devtools/front_end/externs.js
+++ b/third_party/blink/renderer/devtools/front_end/externs.js
@@ -1164,3 +1164,315 @@
  * @return {string}
  */
 Lighthouse.ReportGenerator.replaceStrings;
+
+/** @interface */
+class InspectorFrontendHostAPI {
+  /**
+   * @param {string=} type
+   */
+  addFileSystem(type) {
+  }
+
+  loadCompleted() {
+  }
+
+  /**
+   * @param {number} requestId
+   * @param {string} fileSystemPath
+   * @param {string} excludedFolders
+   */
+  indexPath(requestId, fileSystemPath, excludedFolders) {
+  }
+
+  /**
+   * Requests inspected page to be placed atop of the inspector frontend with specified bounds.
+   * @param {{x: number, y: number, width: number, height: number}} bounds
+   */
+  setInspectedPageBounds(bounds) {
+  }
+
+  /**
+   * @param {!Array<string>} certChain
+   */
+  showCertificateViewer(certChain) {
+  }
+
+  /**
+   * @param {string} shortcuts
+   */
+  setWhitelistedShortcuts(shortcuts) {
+  }
+
+  /**
+   * @param {boolean} active
+   */
+  setEyeDropperActive(active) {
+  }
+
+  inspectElementCompleted() {
+  }
+
+  /**
+   * @param {string} url
+   */
+  openInNewTab(url) {
+  }
+
+  /**
+   * @param {string} fileSystemPath
+   */
+  showItemInFolder(fileSystemPath) {
+  }
+
+  /**
+   * @param {string} fileSystemPath
+   */
+  removeFileSystem(fileSystemPath) {
+  }
+
+  requestFileSystems() {
+  }
+
+  /**
+   * @param {string} url
+   * @param {string} content
+   * @param {boolean} forceSaveAs
+   */
+  save(url, content, forceSaveAs) {
+  }
+
+  /**
+   * @param {string} url
+   * @param {string} content
+   */
+  append(url, content) {
+  }
+
+  /**
+   * @param {string} url
+   */
+  close(url) {
+  }
+
+  /**
+   * @param {number} requestId
+   * @param {string} fileSystemPath
+   * @param {string} query
+   */
+  searchInPath(requestId, fileSystemPath, query) {
+  }
+
+  /**
+   * @param {number} requestId
+   */
+  stopIndexing(requestId) {
+  }
+
+  bringToFront() {
+  }
+
+  closeWindow() {
+  }
+
+  copyText(text) {
+  }
+
+  /**
+   * @param {string} url
+   */
+  inspectedURLChanged(url) {
+  }
+
+  /**
+   * @param {string} fileSystemId
+   * @param {string} registeredName
+   * @return {?DOMFileSystem}
+   */
+  isolatedFileSystem(fileSystemId, registeredName) {
+  }
+
+  /**
+   * @param {string} url
+   * @param {string} headers
+   * @param {number} streamId
+   * @param {function(!InspectorFrontendHostAPI.LoadNetworkResourceResult)} callback
+   */
+  loadNetworkResource(url, headers, streamId, callback) {
+  }
+
+  /**
+   * @param {function(!Object<string, string>)} callback
+   */
+  getPreferences(callback) {
+  }
+
+  /**
+   * @param {string} name
+   * @param {string} value
+   */
+  setPreference(name, value) {
+  }
+
+  /**
+   * @param {string} name
+   */
+  removePreference(name) {
+  }
+
+  clearPreferences() {
+  }
+
+  /**
+   * @param {!FileSystem} fileSystem
+   */
+  upgradeDraggedFileSystemPermissions(fileSystem) {
+  }
+
+  /**
+   * @return {string}
+   */
+  platform() {
+  }
+
+  /**
+   * @param {string} actionName
+   * @param {number} actionCode
+   * @param {number} bucketSize
+   */
+  recordEnumeratedHistogram(actionName, actionCode, bucketSize) {
+  }
+
+  /**
+   * @param {string} histogramName
+   * @param {number} duration
+   */
+  recordPerformanceHistogram(histogramName, duration) {
+  }
+
+  /**
+   * @param {string} umaName
+   */
+  recordUserMetricsAction(umaName) {
+  }
+
+  /**
+   * @param {string} message
+   */
+  sendMessageToBackend(message) {
+  }
+
+  /**
+   * @param {!Adb.Config} config
+   */
+  setDevicesDiscoveryConfig(config) {
+  }
+
+  /**
+   * @param {boolean} enabled
+   */
+  setDevicesUpdatesEnabled(enabled) {
+  }
+
+  /**
+   * @param {string} pageId
+   * @param {string} action
+   */
+  performActionOnRemotePage(pageId, action) {
+  }
+
+  /**
+   * @param {string} browserId
+   * @param {string} url
+   */
+  openRemotePage(browserId, url) {
+  }
+
+  openNodeFrontend() {
+  }
+
+  /**
+   * @param {string} origin
+   * @param {string} script
+   */
+  setInjectedScriptForOrigin(origin, script) {
+  }
+
+  /**
+   * @param {boolean} isDocked
+   * @param {function()} callback
+   */
+  setIsDocked(isDocked, callback) {
+  }
+
+  /**
+   * @return {number}
+   */
+  zoomFactor() {
+  }
+
+  zoomIn() {
+  }
+
+  zoomOut() {
+  }
+
+  resetZoom() {
+  }
+
+  /**
+   * @param {number} x
+   * @param {number} y
+   * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items
+   * @param {!Document} document
+   */
+  showContextMenuAtPoint(x, y, items, document) {
+  }
+
+  /**
+   * @param {function()} callback
+   */
+  reattach(callback) {
+  }
+
+  readyForTest() {
+  }
+
+  connectionReady() {
+  }
+
+  /**
+   * @param {boolean} value
+   */
+  setOpenNewWindowForPopups(value) {
+  }
+
+  /**
+   * @return {boolean}
+   */
+  isHostedMode() {
+  }
+
+  /**
+   * @param {function(!ExtensionDescriptor)} callback
+   */
+  setAddExtensionCallback(callback) {
+  }
+}
+
+/** @typedef
+{{
+    type: string,
+    id: (number|undefined),
+    label: (string|undefined),
+    enabled: (boolean|undefined),
+    checked: (boolean|undefined),
+    subItems: (!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>|undefined)
+}} */
+InspectorFrontendHostAPI.ContextMenuDescriptor;
+
+/** @typedef
+{{
+    statusCode: number,
+    headers: (!Object.<string, string>|undefined)
+}} */
+InspectorFrontendHostAPI.LoadNetworkResourceResult;
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/help/help_strings.grdp b/third_party/blink/renderer/devtools/front_end/help/help_strings.grdp
index e71d504..ad49d79 100644
--- a/third_party/blink/renderer/devtools/front_end/help/help_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/help/help_strings.grdp
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
   <message name="IDS_DEVTOOLS_8381815d48c2d49f31dce1807ae0507c" desc="Title of an action in the help tool to file an issue">
-    Report a DevTools issue
+    Report a <ph name="LOCKED_1">DevTools</ph> issue
   </message>
   <message name="IDS_DEVTOOLS_7d0ee6fed10d3d4e5c9ee496729ab519" desc="Title of an action in the help tool to release notes">
     Release notes
diff --git a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
index af2b1633..6249a30 100644
--- a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
+++ b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
@@ -50,6 +50,11 @@
      * @type {!Map<string, !Array<string>>}
      */
     this._urlsBeingSaved = new Map();
+
+    /**
+     * @type {!Common.EventTarget}
+     */
+    this.events;
   }
 
   /**
@@ -128,10 +133,12 @@
 
   /**
    * @override
-   * @param {string} text
+   * @param {?(string|undefined)} text
    * @suppressGlobalPropertiesCheck
    */
   copyText(text) {
+    if (text === undefined || text === null)
+      return;
     if (navigator.clipboard) {
       navigator.clipboard.writeText(text);
     } else if (document.queryCommandSupported('copy')) {
@@ -175,7 +182,7 @@
       this._urlsBeingSaved.set(url, buffer);
     }
     buffer.push(content);
-    this.events.dispatchEventToListeners(InspectorFrontendHostAPI.Events.SavedURL, {url, fileSystemPath: url});
+    this.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.SavedURL, {url, fileSystemPath: url});
   }
 
   /**
@@ -186,7 +193,7 @@
   append(url, content) {
     const buffer = this._urlsBeingSaved.get(url);
     buffer.push(content);
-    this.events.dispatchEventToListeners(InspectorFrontendHostAPI.Events.AppendedToURL, url);
+    this.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.AppendedToURL, url);
   }
 
   /**
@@ -239,7 +246,7 @@
    * @override
    */
   requestFileSystems() {
-    this.events.dispatchEventToListeners(InspectorFrontendHostAPI.Events.FileSystemsLoaded, []);
+    this.events.dispatchEventToListeners(Host.InspectorFrontendHostAPI.Events.FileSystemsLoaded, []);
   }
 
   /**
@@ -496,7 +503,7 @@
     this._debugFrontend =
         !!Runtime.queryParam('debugFrontend') || (window['InspectorTest'] && window['InspectorTest']['debugTest']);
 
-    const descriptors = InspectorFrontendHostAPI.EventDescriptors;
+    const descriptors = Host.InspectorFrontendAPIImpl.EventDescriptors;
     for (let i = 0; i < descriptors.length; ++i)
       this[descriptors[i][1]] = this._dispatch.bind(this, descriptors[i][0], descriptors[i][2], descriptors[i][3]);
   }
@@ -544,8 +551,47 @@
   }
 };
 
+Host.InspectorFrontendAPIImpl.EventDescriptors = [
+  [Host.InspectorFrontendHostAPI.Events.AppendedToURL, 'appendedToURL', ['url']],
+  [Host.InspectorFrontendHostAPI.Events.CanceledSaveURL, 'canceledSaveURL', ['url']],
+  [Host.InspectorFrontendHostAPI.Events.ContextMenuCleared, 'contextMenuCleared', []],
+  [Host.InspectorFrontendHostAPI.Events.ContextMenuItemSelected, 'contextMenuItemSelected', ['id']],
+  [Host.InspectorFrontendHostAPI.Events.DeviceCountUpdated, 'deviceCountUpdated', ['count']],
+  [Host.InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, 'devicesDiscoveryConfigChanged', ['config']],
+  [
+    Host.InspectorFrontendHostAPI.Events.DevicesPortForwardingStatusChanged, 'devicesPortForwardingStatusChanged',
+    ['status']
+  ],
+  [Host.InspectorFrontendHostAPI.Events.DevicesUpdated, 'devicesUpdated', ['devices']],
+  [Host.InspectorFrontendHostAPI.Events.DispatchMessage, 'dispatchMessage', ['messageObject']],
+  [Host.InspectorFrontendHostAPI.Events.DispatchMessageChunk, 'dispatchMessageChunk', ['messageChunk', 'messageSize']],
+  [Host.InspectorFrontendHostAPI.Events.EnterInspectElementMode, 'enterInspectElementMode', []],
+  [Host.InspectorFrontendHostAPI.Events.EyeDropperPickedColor, 'eyeDropperPickedColor', ['color']],
+  [Host.InspectorFrontendHostAPI.Events.FileSystemsLoaded, 'fileSystemsLoaded', ['fileSystems']],
+  [Host.InspectorFrontendHostAPI.Events.FileSystemRemoved, 'fileSystemRemoved', ['fileSystemPath']],
+  [Host.InspectorFrontendHostAPI.Events.FileSystemAdded, 'fileSystemAdded', ['errorMessage', 'fileSystem']],
+  [
+    Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved, 'fileSystemFilesChangedAddedRemoved',
+    ['changed', 'added', 'removed']
+  ],
+  [
+    Host.InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, 'indexingTotalWorkCalculated',
+    ['requestId', 'fileSystemPath', 'totalWork']
+  ],
+  [Host.InspectorFrontendHostAPI.Events.IndexingWorked, 'indexingWorked', ['requestId', 'fileSystemPath', 'worked']],
+  [Host.InspectorFrontendHostAPI.Events.IndexingDone, 'indexingDone', ['requestId', 'fileSystemPath']],
+  [Host.InspectorFrontendHostAPI.Events.KeyEventUnhandled, 'keyEventUnhandled', ['event']],
+  [Host.InspectorFrontendHostAPI.Events.ReloadInspectedPage, 'reloadInspectedPage', ['hard']],
+  [Host.InspectorFrontendHostAPI.Events.RevealSourceLine, 'revealSourceLine', ['url', 'lineNumber', 'columnNumber']],
+  [Host.InspectorFrontendHostAPI.Events.SavedURL, 'savedURL', ['url', 'fileSystemPath']],
+  [Host.InspectorFrontendHostAPI.Events.SearchCompleted, 'searchCompleted', ['requestId', 'fileSystemPath', 'files']],
+  [Host.InspectorFrontendHostAPI.Events.SetInspectedTabId, 'setInspectedTabId', ['tabId']],
+  [Host.InspectorFrontendHostAPI.Events.SetUseSoftMenu, 'setUseSoftMenu', ['useSoftMenu']],
+  [Host.InspectorFrontendHostAPI.Events.ShowPanel, 'showPanel', ['panelName']]
+];
+
 /**
- * @type {!InspectorFrontendHostAPI}
+ * @type {!Host.InspectorFrontendHostStub}
  */
 let InspectorFrontendHost = window.InspectorFrontendHost;
 (function() {
@@ -580,11 +626,6 @@
 })();
 
 /**
- * @type {!Common.EventTarget}
- */
-InspectorFrontendHost.events;
-
-/**
  * @param {!Object<string, string>=} prefs
  * @return {boolean}
  */
diff --git a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js
index f148630..e017314 100644
--- a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js
+++ b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHostAPI.js
@@ -1,30 +1,11 @@
 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-/** @interface */
-function InspectorFrontendHostAPI() {
-}
-window.InspectorFrontendHostAPI = InspectorFrontendHostAPI;
-/** @typedef
-{{
-    type: string,
-    id: (number|undefined),
-    label: (string|undefined),
-    enabled: (boolean|undefined),
-    checked: (boolean|undefined),
-    subItems: (!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>|undefined)
-}} */
-InspectorFrontendHostAPI.ContextMenuDescriptor;
 
-/** @typedef
-{{
-    statusCode: number,
-    headers: (!Object.<string, string>|undefined)
-}} */
-InspectorFrontendHostAPI.LoadNetworkResourceResult;
+Host.InspectorFrontendHostAPI = {};
 
 /** @enum {symbol} */
-InspectorFrontendHostAPI.Events = {
+Host.InspectorFrontendHostAPI.Events = {
   AppendedToURL: Symbol('appendedToURL'),
   CanceledSaveURL: Symbol('canceledSaveURL'),
   ContextMenuCleared: Symbol('contextMenuCleared'),
@@ -53,283 +34,3 @@
   SetUseSoftMenu: Symbol('setUseSoftMenu'),
   ShowPanel: Symbol('showPanel')
 };
-
-InspectorFrontendHostAPI.EventDescriptors = [
-  [InspectorFrontendHostAPI.Events.AppendedToURL, 'appendedToURL', ['url']],
-  [InspectorFrontendHostAPI.Events.CanceledSaveURL, 'canceledSaveURL', ['url']],
-  [InspectorFrontendHostAPI.Events.ContextMenuCleared, 'contextMenuCleared', []],
-  [InspectorFrontendHostAPI.Events.ContextMenuItemSelected, 'contextMenuItemSelected', ['id']],
-  [InspectorFrontendHostAPI.Events.DeviceCountUpdated, 'deviceCountUpdated', ['count']],
-  [InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, 'devicesDiscoveryConfigChanged', ['config']],
-  [
-    InspectorFrontendHostAPI.Events.DevicesPortForwardingStatusChanged, 'devicesPortForwardingStatusChanged', ['status']
-  ],
-  [InspectorFrontendHostAPI.Events.DevicesUpdated, 'devicesUpdated', ['devices']],
-  [InspectorFrontendHostAPI.Events.DispatchMessage, 'dispatchMessage', ['messageObject']],
-  [InspectorFrontendHostAPI.Events.DispatchMessageChunk, 'dispatchMessageChunk', ['messageChunk', 'messageSize']],
-  [InspectorFrontendHostAPI.Events.EnterInspectElementMode, 'enterInspectElementMode', []],
-  [InspectorFrontendHostAPI.Events.EyeDropperPickedColor, 'eyeDropperPickedColor', ['color']],
-  [InspectorFrontendHostAPI.Events.FileSystemsLoaded, 'fileSystemsLoaded', ['fileSystems']],
-  [InspectorFrontendHostAPI.Events.FileSystemRemoved, 'fileSystemRemoved', ['fileSystemPath']],
-  [InspectorFrontendHostAPI.Events.FileSystemAdded, 'fileSystemAdded', ['errorMessage', 'fileSystem']],
-  [
-    InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved, 'fileSystemFilesChangedAddedRemoved',
-    ['changed', 'added', 'removed']
-  ],
-  [
-    InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, 'indexingTotalWorkCalculated',
-    ['requestId', 'fileSystemPath', 'totalWork']
-  ],
-  [InspectorFrontendHostAPI.Events.IndexingWorked, 'indexingWorked', ['requestId', 'fileSystemPath', 'worked']],
-  [InspectorFrontendHostAPI.Events.IndexingDone, 'indexingDone', ['requestId', 'fileSystemPath']],
-  [InspectorFrontendHostAPI.Events.KeyEventUnhandled, 'keyEventUnhandled', ['event']],
-  [InspectorFrontendHostAPI.Events.ReloadInspectedPage, 'reloadInspectedPage', ['hard']],
-  [InspectorFrontendHostAPI.Events.RevealSourceLine, 'revealSourceLine', ['url', 'lineNumber', 'columnNumber']],
-  [InspectorFrontendHostAPI.Events.SavedURL, 'savedURL', ['url', 'fileSystemPath']],
-  [InspectorFrontendHostAPI.Events.SearchCompleted, 'searchCompleted', ['requestId', 'fileSystemPath', 'files']],
-  [InspectorFrontendHostAPI.Events.SetInspectedTabId, 'setInspectedTabId', ['tabId']],
-  [InspectorFrontendHostAPI.Events.SetUseSoftMenu, 'setUseSoftMenu', ['useSoftMenu']],
-  [InspectorFrontendHostAPI.Events.ShowPanel, 'showPanel', ['panelName']]
-];
-
-InspectorFrontendHostAPI.prototype = {
-  /**
-   * @param {string=} type
-   */
-  addFileSystem(type) {},
-
-  loadCompleted() {},
-
-  /**
-   * @param {number} requestId
-   * @param {string} fileSystemPath
-   * @param {string} excludedFolders
-   */
-  indexPath(requestId, fileSystemPath, excludedFolders) {},
-
-  /**
-   * Requests inspected page to be placed atop of the inspector frontend with specified bounds.
-   * @param {{x: number, y: number, width: number, height: number}} bounds
-   */
-  setInspectedPageBounds(bounds) {},
-
-  /**
-   * @param {!Array<string>} certChain
-   */
-  showCertificateViewer(certChain) {},
-
-  /**
-   * @param {string} shortcuts
-   */
-  setWhitelistedShortcuts(shortcuts) {},
-
-  /**
-   * @param {boolean} active
-   */
-  setEyeDropperActive(active) {},
-
-  inspectElementCompleted() {},
-
-  /**
-   * @param {string} url
-   */
-  openInNewTab(url) {},
-
-  /**
-   * @param {string} fileSystemPath
-   */
-  showItemInFolder(fileSystemPath) {},
-
-  /**
-   * @param {string} fileSystemPath
-   */
-  removeFileSystem(fileSystemPath) {},
-
-  requestFileSystems() {},
-
-  /**
-   * @param {string} url
-   * @param {string} content
-   * @param {boolean} forceSaveAs
-   */
-  save(url, content, forceSaveAs) {},
-
-  /**
-   * @param {string} url
-   * @param {string} content
-   */
-  append(url, content) {},
-
-  /**
-   * @param {string} url
-   */
-  close(url) {},
-
-  /**
-   * @param {number} requestId
-   * @param {string} fileSystemPath
-   * @param {string} query
-   */
-  searchInPath(requestId, fileSystemPath, query) {},
-
-  /**
-   * @param {number} requestId
-   */
-  stopIndexing(requestId) {},
-
-  bringToFront() {},
-
-  closeWindow() {},
-
-  copyText(text) {},
-
-  /**
-   * @param {string} url
-   */
-  inspectedURLChanged(url) {},
-
-  /**
-   * @param {string} fileSystemId
-   * @param {string} registeredName
-   * @return {?DOMFileSystem}
-   */
-  isolatedFileSystem(fileSystemId, registeredName) {},
-
-  /**
-   * @param {string} url
-   * @param {string} headers
-   * @param {number} streamId
-   * @param {function(!InspectorFrontendHostAPI.LoadNetworkResourceResult)} callback
-   */
-  loadNetworkResource(url, headers, streamId, callback) {},
-
-  /**
-   * @param {function(!Object<string, string>)} callback
-   */
-  getPreferences(callback) {},
-
-  /**
-   * @param {string} name
-   * @param {string} value
-   */
-  setPreference(name, value) {},
-
-  /**
-   * @param {string} name
-   */
-  removePreference(name) {},
-
-  clearPreferences() {},
-
-  /**
-   * @param {!FileSystem} fileSystem
-   */
-  upgradeDraggedFileSystemPermissions(fileSystem) {},
-
-  /**
-   * @return {string}
-   */
-  platform() {},
-
-  /**
-   * @param {string} actionName
-   * @param {number} actionCode
-   * @param {number} bucketSize
-   */
-  recordEnumeratedHistogram(actionName, actionCode, bucketSize) {},
-
-  /**
-   * @param {string} histogramName
-   * @param {number} duration
-   */
-  recordPerformanceHistogram(histogramName, duration) {},
-
-  /**
-   * @param {string} umaName
-   */
-  recordUserMetricsAction(umaName) {},
-
-  /**
-   * @param {string} message
-   */
-  sendMessageToBackend(message) {},
-
-  /**
-   * @param {!Adb.Config} config
-   */
-  setDevicesDiscoveryConfig(config) {},
-
-  /**
-   * @param {boolean} enabled
-   */
-  setDevicesUpdatesEnabled(enabled) {},
-
-  /**
-   * @param {string} pageId
-   * @param {string} action
-   */
-  performActionOnRemotePage(pageId, action) {},
-
-  /**
-   * @param {string} browserId
-   * @param {string} url
-   */
-  openRemotePage(browserId, url) {},
-
-  openNodeFrontend() {},
-
-  /**
-   * @param {string} origin
-   * @param {string} script
-   */
-  setInjectedScriptForOrigin(origin, script) {},
-
-  /**
-   * @param {boolean} isDocked
-   * @param {function()} callback
-   */
-  setIsDocked(isDocked, callback) {},
-
-  /**
-   * @return {number}
-   */
-  zoomFactor() {},
-
-  zoomIn() {},
-
-  zoomOut() {},
-
-  resetZoom() {},
-
-  /**
-   * @param {number} x
-   * @param {number} y
-   * @param {!Array.<!InspectorFrontendHostAPI.ContextMenuDescriptor>} items
-   * @param {!Document} document
-   */
-  showContextMenuAtPoint(x, y, items, document) {},
-
-  /**
-   * @param {function()} callback
-   */
-  reattach(callback) {},
-
-  readyForTest() {},
-
-  connectionReady() {},
-
-  /**
-   * @param {boolean} value
-   */
-  setOpenNewWindowForPopups(value) {},
-
-  /**
-   * @return {boolean}
-   */
-  isHostedMode() {},
-
-  /**
-   * @param {function(!ExtensionDescriptor)} callback
-   */
-  setAddExtensionCallback(callback) {}
-};
diff --git a/third_party/blink/renderer/devtools/front_end/host/host_strings.grdp b/third_party/blink/renderer/devtools/front_end/host/host_strings.grdp
index a88edbb..020a2ff 100644
--- a/third_party/blink/renderer/devtools/front_end/host/host_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/host/host_strings.grdp
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <grit-part>
   <message name="IDS_DEVTOOLS_92bb7e733a12d24684262f046afcc2fd" desc="Document title in Inspector Frontend Host of the DevTools window">
-    DevTools - <ph name="URL_REPLACE___HTTPS____________">$1s<ex>example.com</ex></ph>
+    <ph name="LOCKED_1">DevTools</ph> - <ph name="URL_REPLACE___HTTPS____________">$1s<ex>example.com</ex></ph>
   </message>
 </grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js b/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js
index 6808e0b..079b4e5 100644
--- a/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js
+++ b/third_party/blink/renderer/devtools/front_end/inspector_main/InspectorMain.js
@@ -38,7 +38,7 @@
     new InspectorMain.BackendSettingsSync();
     new MobileThrottling.NetworkPanelIndicator();
 
-    InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ReloadInspectedPage, event => {
+    InspectorFrontendHost.events.addEventListener(Host.InspectorFrontendHostAPI.Events.ReloadInspectedPage, event => {
       const hard = /** @type {boolean} */ (event.data);
       SDK.ResourceTreeModel.reloadAllPages(hard);
     });
diff --git a/third_party/blink/renderer/devtools/front_end/inspector_main/inspector_main_strings.grdp b/third_party/blink/renderer/devtools/front_end/inspector_main/inspector_main_strings.grdp
index b073284..4f9582c 100644
--- a/third_party/blink/renderer/devtools/front_end/inspector_main/inspector_main_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/inspector_main/inspector_main_strings.grdp
@@ -13,7 +13,7 @@
     Paint flashing
   </message>
   <message name="IDS_DEVTOOLS_0ef16a6645018dec4609e392c7e9bda1" desc="Title of a setting under the DevTools category that can be invoked through the Command Menu">
-    Do not auto-open DevTools for popups
+    Do not auto-open <ph name="LOCKED_1">DevTools</ph> for popups
   </message>
   <message name="IDS_DEVTOOLS_1808fb32cf4e8a03daa326b48d4246eb" desc="Title of a setting under the Appearance category in Settings">
     Disable paused state overlay
@@ -46,7 +46,7 @@
     Hit-test borders
   </message>
   <message name="IDS_DEVTOOLS_7e9495c56e55fa2c5236512bf80e5d2b" desc="Title of a setting under the Appearance category in Settings">
-    Don&apos;t show Chrome Data Saver warning
+    Don&apos;t show <ph name="LOCKED_1">Chrome Data Saver</ph> warning
   </message>
   <message name="IDS_DEVTOOLS_8339f28e0c74e2c821b05332280c754b" desc="Title of a setting under the Network category in Settings">
     Force ad blocking on this site
@@ -55,7 +55,7 @@
     Navigation
   </message>
   <message name="IDS_DEVTOOLS_8634af2a16e41305fc8dca2d67360810" desc="Title of button in inspector main">
-    Open dedicated DevTools for Node.js
+    Open dedicated <ph name="LOCKED_1">DevTools</ph> for Node.js
   </message>
   <message name="IDS_DEVTOOLS_86f849e1a655c2df19f28cb3dfe07bc9" desc="Title of a setting under the Network category that can be invoked through the Command Menu">
     Block ads on this site
@@ -82,6 +82,6 @@
     Hard reload page
   </message>
   <message name="IDS_DEVTOOLS_f23c9ba06e7123f0b4c906de90fbcc9f" desc="Title of a setting under the DevTools category that can be invoked through the Command Menu">
-    Auto-open DevTools for popups
+    Auto-open <ph name="LOCKED_1">DevTools</ph> for popups
   </message>
 </grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd b/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
index af6a9228..340e2c6 100644
--- a/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
+++ b/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
@@ -24,6 +24,7 @@
       <part file="../console_counters/console_counters_strings.grdp" />
       <part file="../cookie_table/cookie_table_strings.grdp" />
       <part file="../coverage/coverage_strings.grdp" />
+      <part file="../css_overview/css_overview_strings.grdp" />
       <part file="../data_grid/data_grid_strings.grdp" />
       <part file="../devices/devices_strings.grdp" />
       <part file="../elements/elements_strings.grdp" />
diff --git a/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp b/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
index 0fc5d67..887a1cd 100644
--- a/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
@@ -49,6 +49,9 @@
   <message name="IDS_DEVTOOLS_193cfc9be3b995831c6af2fea6650e60" desc="Text that refers to one or a group of webpages">
     Page
   </message>
+  <message name="IDS_DEVTOOLS_1c481aa99d081c32182011a758f73d33" desc="Text that only contain a placeholder">
+    <ph name="DURATIONTEXT">$1s<ex>100ms (at 200ms)</ex></ph>
+  </message>
   <message name="IDS_DEVTOOLS_1eaeeaeb638fdf7f6eeb047abbfd0f1a" desc="Time of a single activity, as opposed to the total time">
     Self Time
   </message>
@@ -152,7 +155,7 @@
     No breakpoints
   </message>
   <message name="IDS_DEVTOOLS_482a4dca0bbd8fcdda5acc6f95f3c279" desc="Title of the DevTools">
-    DevTools
+    <ph name="LOCKED_1">DevTools</ph>
   </message>
   <message name="IDS_DEVTOOLS_49ee3087348e8d44e1feda1917443987" desc="Text for the name of something">
     Name
@@ -215,7 +218,7 @@
     Errors
   </message>
   <message name="IDS_DEVTOOLS_5f0ea62e5bc9f795512ef292ff162a2a" desc="Text to disable cache while DevTools is open">
-    Disable cache (while DevTools is open)
+    Disable cache (while <ph name="LOCKED_1">DevTools</ph> is open)
   </message>
   <message name="IDS_DEVTOOLS_5f8442a46861496e9d2a3d11be13692b" desc="Text to indicate DevTools is writing to a file">
     Writing file…
@@ -284,7 +287,7 @@
     (index)
   </message>
   <message name="IDS_DEVTOOLS_850985cd851d0fe440f03f77762e2590" desc="Text for the network request Content-Length header">
-    Content-Length
+    <ph name="LOCKED_1">Content-Length</ph>
   </message>
   <message name="IDS_DEVTOOLS_86408593c34af77fdd90df932f8b5261" desc="Text for a programming function">
     Function
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index 787c31a..043cf09 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -116,6 +116,7 @@
     Runtime.experiments.register(
         'backgroundServicesPeriodicBackgroundSync', 'Background services section for Periodic Background Sync');
     Runtime.experiments.register('blackboxJSFramesOnTimeline', 'Blackbox JavaScript frames on Timeline', true);
+    Runtime.experiments.register('cssOverview', 'CSS Overview');
     Runtime.experiments.register('emptySourceMapAutoStepping', 'Empty sourcemap auto-stepping');
     Runtime.experiments.register('inputEventsOnTimelineOverview', 'Input events on Timeline overview', true);
     Runtime.experiments.register('liveHeapProfile', 'Live heap profile', true);
@@ -236,11 +237,11 @@
     // TODO: we should not access actions from other modules.
     if (toggleSearchNodeAction) {
       InspectorFrontendHost.events.addEventListener(
-          InspectorFrontendHostAPI.Events.EnterInspectElementMode,
+          Host.InspectorFrontendHostAPI.Events.EnterInspectElementMode,
           toggleSearchNodeAction.execute.bind(toggleSearchNodeAction), this);
     }
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.RevealSourceLine, this._revealSourceLine, this);
+        Host.InspectorFrontendHostAPI.Events.RevealSourceLine, this._revealSourceLine, this);
 
     UI.inspectorView.createToolbars();
     InspectorFrontendHost.loadCompleted();
diff --git a/third_party/blink/renderer/devtools/front_end/main/main_strings.grdp b/third_party/blink/renderer/devtools/front_end/main/main_strings.grdp
index 41c43d0..808c2cd 100644
--- a/third_party/blink/renderer/devtools/front_end/main/main_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/main/main_strings.grdp
@@ -49,7 +49,7 @@
     Go to the panel to the left/right
   </message>
   <message name="IDS_DEVTOOLS_586740a88e6502850bc64f9f2d04d380" desc="Title element title in Main">
-    Placement of DevTools relative to the page. (<ph name="TOGGLEDOCKSIDESHORCUTS____NAME">$1s<ex>Ctrl+Shift+D</ex></ph> to restore last position)
+    Placement of <ph name="LOCKED_1">DevTools</ph> relative to the page. (<ph name="TOGGLEDOCKSIDESHORCUTS____NAME">$1s<ex>Ctrl+Shift+D</ex></ph> to restore last position)
   </message>
   <message name="IDS_DEVTOOLS_5bf4a92da989a6943e3fed84f18c3f0f" desc="Text in the Shortcuts page in settings to explain a keyboard shortcut">
     Search across all sources
diff --git a/third_party/blink/renderer/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/blink/renderer/devtools/front_end/network/NetworkLogViewColumns.js
index 938940e4..daefa7c 100644
--- a/third_party/blink/renderer/devtools/front_end/network/NetworkLogViewColumns.js
+++ b/third_party/blink/renderer/devtools/front_end/network/NetworkLogViewColumns.js
@@ -749,7 +749,9 @@
   {
     id: 'connection',
     isResponseHeader: true,
-    title: Common.UIString('Connection'),
+    // until IDs are supported for strings, the placeholder is used to workaround the limitation that
+    // having multiple translations for a string is not supported
+    title: ls`${'Connection'}`,
     sortingFunction: Network.NetworkRequestNode.ResponseHeaderStringComparator.bind(null, 'connection')
   },
   {
diff --git a/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
index 4234efe..98b9eec 100644
--- a/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/network/network_strings.grdp
@@ -301,7 +301,7 @@
     Stop recording network log
   </message>
   <message name="IDS_DEVTOOLS_611a2b5dcde004cf68ffd56345584d40" desc="Text in Network Log View Columns of the Network panel">
-    ETag
+    <ph name="LOCKED_1">ETag</ph>
   </message>
   <message name="IDS_DEVTOOLS_619d32de7168c6e770464dfc43e374d1" desc="Text in Request Headers View of the Network panel">
     Request Headers
@@ -322,7 +322,7 @@
     Header Name
   </message>
   <message name="IDS_DEVTOOLS_6a98894cd6b4628f0b5117412aab083e" desc="Text in Network Log View Columns of the Network panel">
-    Cache-Control
+    <ph name="LOCKED_1">Cache-Control</ph>
   </message>
   <message name="IDS_DEVTOOLS_6b41d86d21d49c95f5b106fb2ed95762" desc="An option in the user agent dropdown menu in the Network conditions tool">
     <ph name="LOCKED_1">Internet Explorer 10</ph>
@@ -439,7 +439,7 @@
     Set Cookies
   </message>
   <message name="IDS_DEVTOOLS_8c074f405ad9737b1b59d35d6aab7cab" desc="Text in Network Log View Columns of the Network panel">
-    Last-Modified
+    <ph name="LOCKED_1">Last-Modified</ph>
   </message>
   <message name="IDS_DEVTOOLS_8c09001c99ecb6fdd8d6023fcf039054" desc="Text in Signed Exchange Info View of the Network panel">
     Signature
@@ -529,7 +529,7 @@
     Parser
   </message>
   <message name="IDS_DEVTOOLS_9aa1b03934893d7134a660af4204f2a9" desc="Text in Network Log View Columns of the Network panel">
-    Server
+    <ph name="LOCKED_1">Server</ph>
   </message>
   <message name="IDS_DEVTOOLS_9c1ab57e621c2bb257798752dbbe6f14" desc="Text in Request Headers View of the Network panel">
     view source
@@ -658,7 +658,7 @@
     mixed-content
   </message>
   <message name="IDS_DEVTOOLS_bd9176ee57c46268a853e038b133966a" desc="Text in Network Log View Columns of the Network panel">
-    Keep-Alive
+    <ph name="LOCKED_1">Keep-Alive</ph>
   </message>
   <message name="IDS_DEVTOOLS_bdaacef16991cfa4cf17a388579e7c06" desc="Text in Network Item View of the Network panel">
     EventStream
@@ -670,7 +670,7 @@
     Cookies that were sent to the server in the &apos;cookie&apos; header of the request
   </message>
   <message name="IDS_DEVTOOLS_c0635a52980f98eff8adf2279c8ad8e0" desc="A tag of Network Conditions tool that can be searched in the command menu">
-    <ph name="LOCKED_1">useragent</ph>
+    useragent
   </message>
   <message name="IDS_DEVTOOLS_c14108bc627e61f0e445dda0ff029f58" desc="An option in the user agent dropdown menu in the Network conditions tool">
     <ph name="LOCKED_1">Chrome — iPhone</ph>
@@ -742,7 +742,7 @@
     Record (<ph name="RECORDNODE">$1s<ex>Ctrl + E</ex></ph>) to display network activity.
   </message>
   <message name="IDS_DEVTOOLS_d82a834165b99c4ea0969316296a2bc2" desc="Text in Network Log View Columns of the Network panel">
-    Vary
+    <ph name="LOCKED_1">Vary</ph>
   </message>
   <message name="IDS_DEVTOOLS_d8e381f360a750d29368d3ec51346006" desc="Label for response cookies with invalid syntax">
     Malformed Response Cookies
@@ -760,7 +760,7 @@
     Request/Response
   </message>
   <message name="IDS_DEVTOOLS_dcf3e36ee8115282aad46485cab6a4be" desc="A tag of Group Network by frame setting that can be searched in the command menu">
-    <ph name="LOCKED_1">frame</ph>
+    frame
   </message>
   <message name="IDS_DEVTOOLS_dd47445f60115097d07d4cf2e61d933b" desc="Text of a DOM element in Request Timing View of the Network panel">
     CAUTION: request is not finished yet!
@@ -826,7 +826,7 @@
     Started at <ph name="CALCULATOR_FORMATVALUE_REQUEST_STARTTIME____">$1s<ex>120.39ms</ex></ph>
   </message>
   <message name="IDS_DEVTOOLS_edfffa5a1ffba7492b25869813e7e15e" desc="Text in Network Log View Columns of the Network panel">
-    Content-Encoding
+    <ph name="LOCKED_1">Content-Encoding</ph>
   </message>
   <message name="IDS_DEVTOOLS_ef110dd7355d7315c73995a47fe77dfd" desc="Text in Request Timing View of the Network panel">
     DNS Lookup
@@ -841,7 +841,7 @@
     Content
   </message>
   <message name="IDS_DEVTOOLS_f2a4bb0e0bae2587046fb2bd377f3922" desc="A tag of Network Conditions tool that can be searched in the command menu">
-    <ph name="LOCKED_1">user agent</ph>
+    user agent
   </message>
   <message name="IDS_DEVTOOLS_f312b3f268931a1367de22756237b197" desc="Text in Network Item View of the Network panel">
     Headers and request body
diff --git a/third_party/blink/renderer/devtools/front_end/node_main/NodeConnectionsPanel.js b/third_party/blink/renderer/devtools/front_end/node_main/NodeConnectionsPanel.js
index 7c49dbb..25e7259 100644
--- a/third_party/blink/renderer/devtools/front_end/node_main/NodeConnectionsPanel.js
+++ b/third_party/blink/renderer/devtools/front_end/node_main/NodeConnectionsPanel.js
@@ -14,7 +14,7 @@
     image.src = 'https://nodejs.org/static/images/logos/nodejs-new-pantone-black.png';
 
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
+        Host.InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
 
     /** @type {!Adb.Config} */
     this._config;
diff --git a/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js b/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js
index 1b515281..10afb889 100644
--- a/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js
+++ b/third_party/blink/renderer/devtools/front_end/node_main/NodeMain.js
@@ -39,7 +39,7 @@
     this._targetAgent.setDiscoverTargets(true);
 
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
+        Host.InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
     InspectorFrontendHost.setDevicesUpdatesEnabled(false);
     InspectorFrontendHost.setDevicesUpdatesEnabled(true);
   }
@@ -64,7 +64,7 @@
    */
   dispose() {
     InspectorFrontendHost.events.removeEventListener(
-        InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
+        Host.InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
 
     for (const sessionId of this._childTargets.keys())
       this.detachedFromTarget(sessionId, undefined);
diff --git a/third_party/blink/renderer/devtools/front_end/node_main/node_main_strings.grdp b/third_party/blink/renderer/devtools/front_end/node_main/node_main_strings.grdp
index 0323906d..d7b6a6a 100644
--- a/third_party/blink/renderer/devtools/front_end/node_main/node_main_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/node_main/node_main_strings.grdp
@@ -4,7 +4,7 @@
     Add connection
   </message>
   <message name="IDS_DEVTOOLS_3fd49f986cbd5dd3148c27c9aaaaa700" desc="Text in Node Connections Panel of the Sources panel when debugging a Node.js app">
-    Network address (e.g. localhost:9229)
+    Network address (e.g. <ph name="LOCKED_1">localhost:9229</ph>)
   </message>
   <message name="IDS_DEVTOOLS_43d755260901476a60e28982f36b2913" desc="Text in Node Connections Panel of the Sources panel when debugging a Node.js app">
     Node.js debugging guide
@@ -16,9 +16,9 @@
     Node.js: <ph name="TARGETINFO_URL">$1s<ex>example.com</ex></ph>
   </message>
   <message name="IDS_DEVTOOLS_ed6b1cccfa7455e9506ff7e5127e1df4" desc="Text in Node Connections Panel of the Sources panel when debugging a Node.js app">
-    Specify network endpoint and DevTools will connect to it automatically. Read <ph name="DOCUMENTATIONLINK">$1s<ex>Node.js debugging guide</ex></ph> to learn more.
+    Specify network endpoint and <ph name="LOCKED_1">DevTools</ph> will connect to it automatically. Read <ph name="DOCUMENTATIONLINK">$1s<ex>Node.js debugging guide</ex></ph> to learn more.
   </message>
   <message name="IDS_DEVTOOLS_36c4536996ca5615dcf9911f068786dc" desc="A tag of Node.js Connection Panel that can be searched in the command menu">
-    <ph name="LOCKED_1">node</ph>
+    node
   </message>
 </grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/object_ui/objectValue.css b/third_party/blink/renderer/devtools/front_end/object_ui/objectValue.css
index bfaf983..e2dae873 100644
--- a/third_party/blink/renderer/devtools/front_end/object_ui/objectValue.css
+++ b/third_party/blink/renderer/devtools/front_end/object_ui/objectValue.css
@@ -48,7 +48,7 @@
 .object-value-string,
 .object-value-regexp,
 .object-value-symbol {
-    white-space: pre;
+    white-space: pre !important;
     unicode-bidi: -webkit-isolate;
     color: rgb(196, 26, 22);
 }
diff --git a/third_party/blink/renderer/devtools/front_end/persistence/IsolatedFileSystemManager.js b/third_party/blink/renderer/devtools/front_end/persistence/IsolatedFileSystemManager.js
index 3ea1dc8..2c3604bd 100644
--- a/third_party/blink/renderer/devtools/front_end/persistence/IsolatedFileSystemManager.js
+++ b/third_party/blink/renderer/devtools/front_end/persistence/IsolatedFileSystemManager.js
@@ -43,19 +43,19 @@
     this._progresses = new Map();
 
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.FileSystemRemoved, this._onFileSystemRemoved, this);
+        Host.InspectorFrontendHostAPI.Events.FileSystemRemoved, this._onFileSystemRemoved, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.FileSystemAdded, this._onFileSystemAdded, this);
+        Host.InspectorFrontendHostAPI.Events.FileSystemAdded, this._onFileSystemAdded, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved, this._onFileSystemFilesChanged, this);
+        Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved, this._onFileSystemFilesChanged, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, this._onIndexingTotalWorkCalculated, this);
+        Host.InspectorFrontendHostAPI.Events.IndexingTotalWorkCalculated, this._onIndexingTotalWorkCalculated, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.IndexingWorked, this._onIndexingWorked, this);
+        Host.InspectorFrontendHostAPI.Events.IndexingWorked, this._onIndexingWorked, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.IndexingDone, this._onIndexingDone, this);
+        Host.InspectorFrontendHostAPI.Events.IndexingDone, this._onIndexingDone, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.SearchCompleted, this._onSearchCompleted, this);
+        Host.InspectorFrontendHostAPI.Events.SearchCompleted, this._onSearchCompleted, this);
 
     this._initExcludePatterSetting();
 
@@ -71,7 +71,7 @@
     let fulfill;
     const promise = new Promise(f => fulfill = f);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.FileSystemsLoaded, onFileSystemsLoaded, this);
+        Host.InspectorFrontendHostAPI.Events.FileSystemsLoaded, onFileSystemsLoaded, this);
     InspectorFrontendHost.requestFileSystems();
     return promise;
 
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
index bf670e1..bd0f64aa 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/profiler/profiler_strings.grdp
@@ -176,7 +176,7 @@
   </message>
   <message name="IDS_DEVTOOLS_6b59ba1fcfd868474cae169876f9cbfa" desc="Text in Heap Profile View of a profiler tool">
     Native memory snapshots show sampled native allocations in the renderer process since start up.
-              Chrome has to be started with --memlog=all flag. Check flags at chrome://flags
+              <ph name="LOCKED_1">Chrome</ph> has to be started with <ph name="LOCKED_2">--memlog=all</ph> flag. Check flags at <ph name="LOCKED_1">chrome://flags</ph>
   </message>
   <message name="IDS_DEVTOOLS_6d27e30b2f09c2a451a6d123c1a35805" desc="Text in Profiles Panel of a profiler tool">
     Run <ph name="_">$1d<ex>2</ex></ph>
diff --git a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
index 53ee664..4b8bb66a 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -495,8 +495,11 @@
     this._panel.showView(view);
   }
 
-  _updateDatabaseTables(event) {
-    const database = event.data;
+  /**
+   * @param {!Common.Event} event
+   */
+  async _updateDatabaseTables(event) {
+    const database = /** @type {!Resources.Database} */ (event.data);
 
     if (!database)
       return;
@@ -513,20 +516,21 @@
 
     const tableNamesHash = {};
     const panel = this._panel;
-    function tableNamesCallback(tableNames) {
-      const tableNamesLength = tableNames.length;
-      for (let i = 0; i < tableNamesLength; ++i)
-        tableNamesHash[tableNames[i]] = true;
+    const tableNames = await database.tableNames();
+    const tableNamesLength = tableNames.length;
 
-      for (const tableName in tableViews) {
-        if (!(tableName in tableNamesHash)) {
-          if (panel.visibleView === tableViews[tableName])
-            panel.showView(null);
-          delete tableViews[tableName];
-        }
+    for (let i = 0; i < tableNamesLength; ++i)
+      tableNamesHash[tableNames[i]] = true;
+
+    for (const tableName in tableViews) {
+      if (!(tableName in tableNamesHash)) {
+        if (panel.visibleView === tableViews[tableName])
+          panel.showView(null);
+        delete tableViews[tableName];
       }
     }
-    database.getTableNames(tableNamesCallback);
+
+    await databasesTreeElement.updateChildren();
   }
 
   /**
@@ -863,10 +867,11 @@
    * @override
    */
   onexpand() {
-    this._updateChildren();
+    this.updateChildren();
   }
 
-  async _updateChildren() {
+  async updateChildren() {
+    this.removeChildren();
     const tableNames = await this._database.tableNames();
     for (const tableName of tableNames)
       this.appendChild(new Resources.DatabaseTableTreeElement(this._sidebar, this._database, tableName));
diff --git a/third_party/blink/renderer/devtools/front_end/resources/CookieItemsView.js b/third_party/blink/renderer/devtools/front_end/resources/CookieItemsView.js
index b3f2338..87f39a4 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/CookieItemsView.js
+++ b/third_party/blink/renderer/devtools/front_end/resources/CookieItemsView.js
@@ -35,6 +35,7 @@
   constructor(model, cookieDomain) {
     super(Common.UIString('Cookies'), 'cookiesPanel');
 
+    this.registerRequiredCSS('resources/cookieItemsView.css');
     this.element.classList.add('storage-view');
 
     this._model = model;
@@ -42,10 +43,34 @@
 
     this._totalSize = 0;
     /** @type {?CookieTable.CookiesTable} */
-    this._cookiesTable = null;
+    this._cookiesTable = this._cookiesTable = new CookieTable.CookiesTable(
+        /* renderInline */ false, this._saveCookie.bind(this), this.refreshItems.bind(this),
+        this._handleCookieSelected.bind(this), this._deleteCookie.bind(this));
+
+    this._cookiesTable.setMinimumSize(0, 50);
+
+    this._splitWidget = new UI.SplitWidget(false, false);
+    this._splitWidget.show(this.element);
+    this._splitWidget.setSecondIsSidebar(true);
+
+    this._previewPanel = new UI.VBox();
+    const resizer = this._previewPanel.element.createChild('div', 'preview-panel-resizer');
+
+    this._splitWidget.setMainWidget(this._cookiesTable);
+    this._splitWidget.setSidebarWidget(this._previewPanel);
+    this._splitWidget.installResizer(resizer);
+
+
     this._refreshThrottler = new Common.Throttler(300);
     /** @type {!Array<!Common.EventTarget.EventDescriptor>} */
     this._eventDescriptors = [];
+
+
+    /** @type {?UI.Widget} */
+    this._preview = null;
+    /** @type {?SDK.Cookie} */
+    this._previewValue = null;
+
     this.setCookiesDomain(model, cookieDomain);
   }
 
@@ -61,6 +86,57 @@
     const networkManager = model.target().model(SDK.NetworkManager);
     this._eventDescriptors =
         [networkManager.addEventListener(SDK.NetworkManager.Events.ResponseReceived, this._onResponseReceived, this)];
+
+    this._showPreview(null, null);
+  }
+
+  /**
+   * @param {?UI.Widget} preview
+   * @param {?SDK.Cookie} value
+   */
+  _showPreview(preview, value) {
+    if (this._preview && this._previewValue === value)
+      return;
+
+    if (this._preview)
+      this._preview.detach();
+
+    if (!preview)
+      preview = new UI.EmptyWidget(ls`Select a cookie to preview it value`);
+
+    this._previewValue = value;
+    this._preview = preview;
+
+    preview.show(this._previewPanel.contentElement);
+  }
+
+  _handleCookieSelected() {
+    const cookie = this._cookiesTable.selectedCookie();
+    this.setCanDeleteSelected(!!cookie);
+
+    if (!cookie) {
+      this._showPreview(null, null);
+      return;
+    }
+
+    const value = createElementWithClass('div', 'cookie-value');
+    value.textContent = cookie.value();
+    value.addEventListener('dblclick', handleDblClickOnCookieValue);
+
+    const preview = new UI.VBox();
+    preview.contentElement.appendChild(value);
+
+    this._showPreview(preview, cookie);
+
+    /**
+     * @suppressGlobalPropertiesCheck
+     */
+    function handleDblClickOnCookieValue() {
+      const range = document.createRange();
+      range.selectNode(value);
+      window.getSelection().removeAllRanges();
+      window.getSelection().addRange(range);
+    }
   }
 
   /**
@@ -90,19 +166,12 @@
   _updateWithCookies(allCookies) {
     this._totalSize = allCookies.reduce((size, cookie) => size + cookie.size(), 0);
 
-    if (!this._cookiesTable) {
-      this._cookiesTable = new CookieTable.CookiesTable(
-          /* renderInline */ false, this._saveCookie.bind(this), this.refreshItems.bind(this),
-          () => this.setCanDeleteSelected(!!this._cookiesTable.selectedCookie()), this._deleteCookie.bind(this));
-    }
-
     const parsedURL = this._cookieDomain.asParsedURL();
     const host = parsedURL ? parsedURL.host : '';
     this._cookiesTable.setCookieDomain(host);
 
     const shownCookies = this.filter(allCookies, cookie => `${cookie.name()} ${cookie.value()} ${cookie.domain()}`);
     this._cookiesTable.setCookies(shownCookies);
-    this._cookiesTable.show(this.element);
     this.setCanFilter(true);
     this.setCanDeleteAll(true);
     this.setCanDeleteSelected(!!this._cookiesTable.selectedCookie());
diff --git a/third_party/blink/renderer/devtools/front_end/resources/cookieItemsView.css b/third_party/blink/renderer/devtools/front_end/resources/cookieItemsView.css
new file mode 100644
index 0000000..b2b9930
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/resources/cookieItemsView.css
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2019 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.
+ */
+
+.cookie-value {
+    padding: 2px 6px;
+    overflow: auto;
+    user-select: text;
+    min-height: 100%;
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/resources/module.json b/third_party/blink/renderer/devtools/front_end/resources/module.json
index 9dbc544..182c1a4 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/module.json
+++ b/third_party/blink/renderer/devtools/front_end/resources/module.json
@@ -100,6 +100,7 @@
         "resourcesPanel.css",
         "resourcesSidebar.css",
         "serviceWorkerCacheViews.css",
-        "serviceWorkersView.css"
+        "serviceWorkersView.css",
+        "cookieItemsView.css"
     ]
 }
diff --git a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
index c425de1a..8cff6e0 100644
--- a/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/resources/resources_strings.grdp
@@ -218,6 +218,9 @@
   <message name="IDS_DEVTOOLS_9dce9dd0f39a17c2e029174f5bc86ef9" desc="Text in Background Service View of the Application panel">
     Background Sync
   </message>
+  <message name="IDS_DEVTOOLS_9ea4dd36a0094fc33b5ea341a1f909ee" desc="Text in Cookie Items View of the Application panel">
+    Select a cookie to preview it value
+  </message>
   <message name="IDS_DEVTOOLS_9ff9f5649294f7200b671d6389d17d9a" desc="Text in Application Panel Sidebar of the Application panel">
     multiEntry
   </message>
@@ -234,7 +237,7 @@
     Cache Storage
   </message>
   <message name="IDS_DEVTOOLS_a9cd2046f50376846feaa11c68e56e2f" desc="Inform users that DevTools are recording/waiting for events in the Periodic Background Sync tool of the Application panel">
-    DevTools will record all <ph name="FEATURENAME">$1s<ex>Background Fetch</ex></ph> activity for up to 3 days, even when closed.
+    <ph name="LOCKED_1">DevTools</ph> will record all <ph name="FEATURENAME">$1s<ex>Background Fetch</ex></ph> activity for up to 3 days, even when closed.
   </message>
   <message name="IDS_DEVTOOLS_ab0cf104f39708eabd07b8cb67e149ba" desc="Text in Application Panel Sidebar of the Application panel">
     Cache
@@ -267,7 +270,7 @@
     Display
   </message>
   <message name="IDS_DEVTOOLS_ba8d2e1eca2bf52ab3cd0202d4d04c07" desc="Text in Service Workers View of the Application panel">
-    Test push message from DevTools.
+    Test push message from <ph name="LOCKED_1">DevTools</ph>.
   </message>
   <message name="IDS_DEVTOOLS_bb8839cf9d324a22591ff426c28c5345" desc="Tooltip text that appears when hovering over the largeicon play button in the Indexed DBViews of the Application panel">
     Show next page
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/Connections.js b/third_party/blink/renderer/devtools/front_end/sdk/Connections.js
index be2a939..a3ea04b 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/Connections.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/Connections.js
@@ -13,9 +13,9 @@
     this._messageSize = 0;
     this._eventListeners = [
       InspectorFrontendHost.events.addEventListener(
-          InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this),
+          Host.InspectorFrontendHostAPI.Events.DispatchMessage, this._dispatchMessage, this),
       InspectorFrontendHost.events.addEventListener(
-          InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this),
+          Host.InspectorFrontendHostAPI.Events.DispatchMessageChunk, this._dispatchMessageChunk, this),
     ];
   }
 
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js b/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js
index f26799a..acda0f17 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/RuntimeModel.js
@@ -366,7 +366,7 @@
    */
   _copyRequested(object) {
     if (!object.objectId) {
-      InspectorFrontendHost.copyText(object.unserializableValue() || object.value);
+      InspectorFrontendHost.copyText(object.unserializableValue() || /** @type {string} */ (object.value));
       return;
     }
     object.callFunctionJSON(toStringForClipboard, [{value: object.subtype}])
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
index 7c854d82..fb3fdf74 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
@@ -91,7 +91,7 @@
     Hide frames per second (FPS) meter
   </message>
   <message name="IDS_DEVTOOLS_3ff9f750075f426831f71818a8f4ff12" desc="Text in Network Manager">
-    Request was blocked by DevTools: &quot;<ph name="NETWORKREQUEST_URL__">$1s<ex>https://example.com</ex></ph>&quot;.
+    Request was blocked by <ph name="LOCKED_1">DevTools</ph>: &quot;<ph name="NETWORKREQUEST_URL__">$1s<ex>https://example.com</ex></ph>&quot;.
   </message>
   <message name="IDS_DEVTOOLS_40dba446b661ae69dea3a8e026f76dfd" desc="Title of a setting under the Rendering category that can be invoked through the Command Menu">
     Hide paint flashing rectangles
@@ -157,7 +157,7 @@
     Show hit-test borders
   </message>
   <message name="IDS_DEVTOOLS_6948a469c79f7dd5426e4f291cba3db1" desc="Text in Network Log">
-    Chrome Data Saver
+    <ph name="LOCKED_1">Chrome Data Saver</ph>
   </message>
   <message name="IDS_DEVTOOLS_6ce4d85a628a88bbdb3ac24a8e5a9c2e" desc="Text in DOMDebugger Model">
     Keyboard
@@ -346,7 +346,7 @@
     Mouse
   </message>
   <message name="IDS_DEVTOOLS_f6137609f4decf877ede5bd3a3125629" desc="Text in CPUProfile Data Model">
-    DevTools: CPU profile parser is fixing <ph name="COUNT">$1s<ex>2</ex></ph> missing samples.
+    <ph name="LOCKED_1">DevTools</ph>: CPU profile parser is fixing <ph name="COUNT">$1s<ex>2</ex></ph> missing samples.
   </message>
   <message name="IDS_DEVTOOLS_f7531e2d0ea27233ce00b5f01c5bf335" desc="A drop-down menu option to emulate css print media type">
     print
diff --git a/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp b/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
index 18a339b..8eb546c 100644
--- a/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/security/security_strings.grdp
@@ -97,7 +97,7 @@
     View <ph name="FILTERREQUESTCOUNT">$1d<ex>2</ex></ph> requests in Network Panel
   </message>
   <message name="IDS_DEVTOOLS_a10edd2a3893a9ba446fb26e7c27556a" desc="Text in Security Panel of the Security panel">
-    This request complies with Chrome&apos;s Certificate Transparency policy.
+    This request complies with <ph name="LOCKED_1">Chrome</ph>&apos;s Certificate Transparency policy.
   </message>
   <message name="IDS_DEVTOOLS_a2b16c6b3db371738d0e5c3f3ba2b54d" desc="Text in Security Panel of the Security panel">
     This response was loaded from cache. Some security details might be missing.
@@ -115,7 +115,7 @@
     Show more (<ph name="SANLIST_LENGTH">$1s<ex>2</ex></ph> total)
   </message>
   <message name="IDS_DEVTOOLS_b92f649b203ca680d444432324771186" desc="Text in Security Panel of the Security panel">
-    This request does not comply with Chrome&apos;s Certificate Transparency policy.
+    This request does not comply with <ph name="LOCKED_1">Chrome</ph>&apos;s Certificate Transparency policy.
   </message>
   <message name="IDS_DEVTOOLS_c3db8a95444ea6caa45cc7dcb6e78d64" desc="Summary div text content in Security Panel of the Security panel">
     Security overview
diff --git a/third_party/blink/renderer/devtools/front_end/sources/sources_strings.grdp b/third_party/blink/renderer/devtools/front_end/sources/sources_strings.grdp
index 5eb40451..461a9e51 100644
--- a/third_party/blink/renderer/devtools/front_end/sources/sources_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/sources/sources_strings.grdp
@@ -151,7 +151,7 @@
     Override page assets with files from a local folder
   </message>
   <message name="IDS_DEVTOOLS_49a4a1505587da9f4475db7ee9958853" desc="Text in Sources Navigator of the Sources panel">
-    Sync changes in DevTools with the local filesystem
+    Sync changes in <ph name="LOCKED_1">DevTools</ph> with the local filesystem
   </message>
   <message name="IDS_DEVTOOLS_4b3812a22a58f04fb5a0598658a3bd27" desc="Title of the Filtered List WidgetProvider of Quick Open">
     Go to symbol
@@ -385,7 +385,7 @@
     Remove other breakpoints
   </message>
   <message name="IDS_DEVTOOLS_b9947e230aa5ac451e3b0649bdb1c7fc" desc="Event return value in Sources View of the Sources panel">
-    DevTools have unsaved changes that will be permanently lost.
+    <ph name="LOCKED_1">DevTools</ph> have unsaved changes that will be permanently lost.
   </message>
   <message name="IDS_DEVTOOLS_bb9f288b958ce8c8d250cb392e2f1309" desc="Text in Debugger Plugin of the Sources panel">
     You can click the <ph name="TOOLBAR_ELEMENT">$1s<ex>{}</ex></ph> button on the bottom status bar, and continue debugging with the new formatted source.
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/TimelineEventOverview.js b/third_party/blink/renderer/devtools/front_end/timeline/TimelineEventOverview.js
index e9a5be5..f24d822e 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline/TimelineEventOverview.js
+++ b/third_party/blink/renderer/devtools/front_end/timeline/TimelineEventOverview.js
@@ -740,12 +740,21 @@
 
     let total = 0;
     let total_used = 0;
+    /** @type {!Map<!Coverage.CoverageInfo>} */
     const usedByTimestamp = new Map();
+    /** @type {!Map<!Coverage.CoverageInfo>} */
+    const totalByTimestamp = new Map();
     for (const urlInfo of this._coverageModel.entries()) {
       for (const info of urlInfo.entries()) {
         total += info.size();
         for (const [stamp, used] of info.usedByTimestamp()) {
           total_used += used;
+
+          if (!totalByTimestamp.has(stamp))
+            totalByTimestamp.set(stamp, new Set());
+
+          totalByTimestamp.get(stamp).add(info);
+
           if (!usedByTimestamp.has(stamp))
             usedByTimestamp.set(stamp, used);
           else
@@ -753,6 +762,26 @@
         }
       }
     }
+
+    /** @type {!Set<!Coverage.CoverageInfo>} */
+    const seen = new Set();
+    /** @type {!Map<number, number>} */
+    const coverageByTimestamp = new Map();
+    let sumTotal = 0, sumUsed = 0;
+
+    const sortedByTimestamp = Array.from(totalByTimestamp.entries()).sort((a, b) => a[0] - b[0]);
+    for (const [stamp, infos] of sortedByTimestamp) {
+      for (const info of infos.values()) {
+        if (seen.has(info))
+          continue;
+
+        seen.add(info);
+        sumTotal += info.size();
+      }
+      sumUsed += usedByTimestamp.get(stamp);
+      coverageByTimestamp.set(stamp, sumUsed / sumTotal);
+    }
+
     const percentUsed = total ? Math.round(100 * total_used / total) : 0;
     const lowerOffset = 3 * ratio;
 
@@ -764,48 +793,41 @@
     const width = this.width();
     const height = this.height() - lowerOffset;
     const xFactor = width / (maxTime - minTime);
-    const yFactor = (height - lineWidth) / Math.max(total, 1);
+    const yFactor = height - lineWidth;
 
-
-    let y = 0;
+    let yOffset = 0;
     const ctx = this.context();
     const heightBeyondView = height + lowerOffset + lineWidth;
     ctx.translate(0.5, 0.5);
     ctx.beginPath();
     ctx.moveTo(-lineWidth, heightBeyondView);
 
-    ctx.lineTo(-lineWidth, height - y);
+    ctx.lineTo(-lineWidth, height - yOffset);
 
-    const entries = Array.from(usedByTimestamp.entries()).sort((a, b) => a[0] - b[0]);
-    let cummulative_used = 0;
-    for (const [stamp, used] of entries) {
+    for (const [stamp, coverage] of coverageByTimestamp) {
       if (stamp > maxTime)
         break;
       const x = (stamp - minTime) * xFactor;
-      cummulative_used += used;
-      y = cummulative_used * yFactor;
-      ctx.lineTo(x, height - y);
+      yOffset = coverage * yFactor;
+      ctx.lineTo(x, height - yOffset);
     }
 
-
-    ctx.lineTo(width + lineWidth, height - y);
+    ctx.lineTo(width + lineWidth, height - yOffset);
     ctx.lineTo(width + lineWidth, heightBeyondView);
     ctx.closePath();
-
     ctx.fillStyle = 'hsla(220, 90%, 70%, 0.2)';
     ctx.fill();
     ctx.lineWidth = lineWidth;
     ctx.strokeStyle = 'hsl(220, 90%, 70%)';
     ctx.stroke();
 
-    cummulative_used = 0;
-    for (const [stamp, used] of entries) {
+    for (const [stamp, coverage] of coverageByTimestamp) {
       if (stamp > maxTime)
         break;
-      cummulative_used += used;
       ctx.beginPath();
       const x = (stamp - minTime) * xFactor;
-      ctx.arc(x, height - cummulative_used * yFactor, 2 * lineWidth, 0, 2 * Math.PI, false);
+      const y = height - coverage * yFactor;
+      ctx.arc(x, y, 2 * lineWidth, 0, 2 * Math.PI, false);
       ctx.closePath();
       ctx.stroke();
       ctx.fill();
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp b/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
index 0d615980..d1c3bd0d 100644
--- a/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/timeline/timeline_strings.grdp
@@ -89,7 +89,7 @@
 Click the reload button <ph name="RELOADBUTTON">$3s<ex>reload</ex></ph> or hit <ph name="RELOADKEY">$4s<ex>Ctrl + R</ex></ph> to record the page load.
   </message>
   <message name="IDS_DEVTOOLS_0e8dfe937d359c7df31b16ea7e9cca81" desc="Title of a setting under the Performance category in Settings">
-    Hide chrome frame in Layers view
+    Hide <ph name="LOCKED_1">chrome</ph> frame in Layers view
   </message>
   <message name="IDS_DEVTOOLS_10aab9f12eca6bca3a6215914bab2745" desc="Text in Timeline UIUtils of the Performance panel">
     Allotted Time
@@ -124,9 +124,6 @@
   <message name="IDS_DEVTOOLS_1b2c363066d5912ae8b27a1527f3d45e" desc="Text in Timeline Tree View of the Performance panel">
     Group by Domain
   </message>
-  <message name="IDS_DEVTOOLS_1c481aa99d081c32182011a758f73d33" desc="Duration text in the Performance panel">
-    <ph name="DURATIONTEXT">$1s<ex>100ms (at 200ms)</ex></ph>
-  </message>
   <message name="IDS_DEVTOOLS_1e3eaaac8d3c9cffea8ee7f03a8a6121" desc="Text in Timeline UIUtils of the Performance panel">
     DOMContentLoaded Event
   </message>
@@ -482,7 +479,7 @@
     Decrypt
   </message>
   <message name="IDS_DEVTOOLS_76e815d5323a5f3c80a3d186c416e92b" desc="Text in Timeline Tree View of the Performance panel">
-    [Chrome extensions overhead]
+    [<ph name="LOCKED_1">Chrome</ph> extensions overhead]
   </message>
   <message name="IDS_DEVTOOLS_76ecb02d1f339c2dd6f14c41ead368c1" desc="Span text content in Timeline UIUtils of the Performance panel">
     Handler took <ph name="NUMBER_MILLISTOSTRING_EVENT_DURATION__TRUE_">$1s<ex>10ms</ex></ph>
@@ -691,7 +688,7 @@
     Layout root
   </message>
   <message name="IDS_DEVTOOLS_b3bfefedfa27aa11e79f637b22b16d09" desc="Text in Timeline Tree View of the Performance panel">
-    [V8 Runtime]
+    [<ph name="LOCKED_1">V8</ph> Runtime]
   </message>
   <message name="IDS_DEVTOOLS_b49fe3f49a656887d446c2da3851a88a" desc="Text in Timeline UIUtils of the Performance panel">
     Network request
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js b/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
index 69b9b89..9e877c75 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ContextMenu.js
@@ -368,7 +368,7 @@
   }
 
   static initialize() {
-    InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.SetUseSoftMenu, setUseSoftMenu);
+    InspectorFrontendHost.events.addEventListener(Host.InspectorFrontendHostAPI.Events.SetUseSoftMenu, setUseSoftMenu);
     /**
      * @param {!Common.Event} event
      */
@@ -447,9 +447,9 @@
        */
       function listenToEvents() {
         InspectorFrontendHost.events.addEventListener(
-            InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
+            Host.InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
         InspectorFrontendHost.events.addEventListener(
-            InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
+            Host.InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
       }
 
       // showContextMenuAtPoint call above synchronously issues a clear event for previous context menu (if any),
@@ -492,9 +492,9 @@
 
   _menuCleared() {
     InspectorFrontendHost.events.removeEventListener(
-        InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
+        Host.InspectorFrontendHostAPI.Events.ContextMenuCleared, this._menuCleared, this);
     InspectorFrontendHost.events.removeEventListener(
-        InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
+        Host.InspectorFrontendHostAPI.Events.ContextMenuItemSelected, this._onItemSelected, this);
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ForwardedInputEventHandler.js b/third_party/blink/renderer/devtools/front_end/ui/ForwardedInputEventHandler.js
index 6baa49a8..fa85a40c 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ForwardedInputEventHandler.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/ForwardedInputEventHandler.js
@@ -7,7 +7,7 @@
 UI.ForwardedInputEventHandler = class {
   constructor() {
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.KeyEventUnhandled, this._onKeyEventUnhandled, this);
+        Host.InspectorFrontendHostAPI.Events.KeyEventUnhandled, this._onKeyEventUnhandled, this);
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js b/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js
index c19d2825..960c308d 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/InspectorView.js
@@ -93,7 +93,7 @@
     this._drawerSplitWidget.setMainWidget(this._tabbedPane);
 
     this._keyDownBound = this._keyDown.bind(this);
-    InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.ShowPanel, showPanel.bind(this));
+    InspectorFrontendHost.events.addEventListener(Host.InspectorFrontendHostAPI.Events.ShowPanel, showPanel.bind(this));
 
     /**
      * @this {UI.InspectorView}
diff --git a/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
index bfa706a..5a861bfe 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/ui/ui_strings.grdp
@@ -85,7 +85,7 @@
     Step out
   </message>
   <message name="IDS_DEVTOOLS_44ac014b674d7d86a04702e6fd1fa858" desc="Text in a dialog box showing how to reconnect to DevTools when remote debugging has been terminated">
-    Reconnect when ready by reopening DevTools.
+    Reconnect when ready by reopening <ph name="LOCKED_1">DevTools</ph>.
   </message>
   <message name="IDS_DEVTOOLS_45b471827af8ba9628e1914afbb13980" desc="Minutes format in UIUtils">
     <ph name="PH1">$1.1f<ex>2.2</ex></ph> min
@@ -136,13 +136,13 @@
     Save profile
   </message>
   <message name="IDS_DEVTOOLS_63e29f78b2e7788d9234a5790897fa71" desc="Text in dialog box when the target page crashed">
-    DevTools was disconnected from the page.
+    <ph name="LOCKED_1">DevTools</ph> was disconnected from the page.
   </message>
   <message name="IDS_DEVTOOLS_650654cb12c12b00e68b4384e0aede82" desc="Aria alert to read the suggestion for the suggestion box when typing in text editor">
     <ph name="THIS__ONLYCOMPLETION_TEXT">$1s<ex>name</ex></ph>, suggestion
   </message>
   <message name="IDS_DEVTOOLS_678b7bbae3b9b67cee8bce1fac3067d7" desc="Text for a hyperlink to the shortcut documentation in Shortcuts page">
-    Full list of DevTools keyboard shortcuts and gestures
+    Full list of <ph name="LOCKED_1">DevTools</ph> keyboard shortcuts and gestures
   </message>
   <message name="IDS_DEVTOOLS_6e0e121820384f135321cb766273f4da" desc="Text in the Shortcuts page to explain a keyboard shortcut (soft undo in text editor)">
     Soft undo
@@ -178,7 +178,7 @@
     Zoom in
   </message>
   <message name="IDS_DEVTOOLS_7cf206a681131cf4d86fc190f2d9ad27" desc="Text on a button to reconnect Devtools when remote debugging terminated">
-    Reconnect DevTools
+    Reconnect <ph name="LOCKED_1">DevTools</ph>
   </message>
   <message name="IDS_DEVTOOLS_7dce122004969d56ae2e0245cb754d35" desc="Text on a button to start editing text">
     Edit
@@ -193,7 +193,7 @@
     Layers Panel
   </message>
   <message name="IDS_DEVTOOLS_86e8f8f8327d28b74e5bd3ab581f77ee" desc="Text that appears when hover over the filter bar in the Network tool">
-    e.g. /small[\d]+/ url:a.com/b
+    e.g. <ph name="LOCKED_1">/small[\d]+/ url:a.com/b</ph>
   </message>
   <message name="IDS_DEVTOOLS_89345110b204ed302eddff2ddbd0f4d9" desc="Text in the Shortcuts page to explain a keyboard shortcut (enable toggle breakpoint shortcut in debugger)">
     Toggle breakpoint enabled
@@ -235,7 +235,7 @@
     Toggle all breakpoints
   </message>
   <message name="IDS_DEVTOOLS_a950da8e902568f9a6879abe4313efe7" desc="Text content of content element">
-    Once page is reloaded, DevTools will automatically reconnect.
+    Once page is reloaded, <ph name="LOCKED_1">DevTools</ph> will automatically reconnect.
   </message>
   <message name="IDS_DEVTOOLS_ad91a1da588ce256d30b1af38f78f84e" desc="Accessibility label for unchecked items in the SoftContextMenu">
     unchecked
diff --git a/third_party/blink/renderer/devtools/front_end/workspace/FileManager.js b/third_party/blink/renderer/devtools/front_end/workspace/FileManager.js
index 740a6ec..21b5cbe 100644
--- a/third_party/blink/renderer/devtools/front_end/workspace/FileManager.js
+++ b/third_party/blink/renderer/devtools/front_end/workspace/FileManager.js
@@ -36,11 +36,11 @@
     super();
     /** @type {!Map<string, function(?{fileSystemPath: (string|undefined)})>} */
     this._saveCallbacks = new Map();
-    InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.SavedURL, this._savedURL, this);
+    InspectorFrontendHost.events.addEventListener(Host.InspectorFrontendHostAPI.Events.SavedURL, this._savedURL, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.CanceledSaveURL, this._canceledSavedURL, this);
+        Host.InspectorFrontendHostAPI.Events.CanceledSaveURL, this._canceledSavedURL, this);
     InspectorFrontendHost.events.addEventListener(
-        InspectorFrontendHostAPI.Events.AppendedToURL, this._appendedToURL, this);
+        Host.InspectorFrontendHostAPI.Events.AppendedToURL, this._appendedToURL, this);
   }
 
   /**
diff --git a/third_party/blink/renderer/devtools/scripts/compile_frontend.py b/third_party/blink/renderer/devtools/scripts/compile_frontend.py
index 0f251ebe..a15c1bc3 100755
--- a/third_party/blink/renderer/devtools/scripts/compile_frontend.py
+++ b/third_party/blink/renderer/devtools/scripts/compile_frontend.py
@@ -358,9 +358,7 @@
 
     devtools_js_compile_command = closure_compiler_command + [
         '--externs',
-        to_platform_path(GLOBAL_EXTERNS_FILE), '--externs',
-        to_platform_path(path.join(DEVTOOLS_FRONTEND_PATH, 'host', 'InspectorFrontendHostAPI.js')),
-        '--jscomp_off=externsValidation', '--js',
+        to_platform_path(GLOBAL_EXTERNS_FILE), '--jscomp_off=externsValidation', '--js',
         to_platform_path(path.join(DEVTOOLS_FRONTEND_PATH, 'devtools_compatibility.js'))
     ]
     devtools_js_compile_proc = popen(devtools_js_compile_command)
diff --git a/third_party/blink/renderer/devtools/tests/front_end/common/Color.ts b/third_party/blink/renderer/devtools/tests/front_end/common/Color.ts
new file mode 100644
index 0000000..341d126e
--- /dev/null
+++ b/third_party/blink/renderer/devtools/tests/front_end/common/Color.ts
@@ -0,0 +1,62 @@
+// Copyright 2019 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.
+
+const { assert } = chai;
+
+import { default as Color } from '../../../front_end/common/Color.js';
+
+describe('Color', () => {
+  describe('parse', () => {
+    it('parses hex values', () => {
+      assert.deepEqual(Color.parse('#FF00FF').rgba(), [1, 0, 1, 1]);
+      assert.deepEqual(Color.parse('#F0F').rgba(), [1, 0, 1, 1]);
+      assert.deepEqual(Color.parse('#F0F0').rgba(), [1, 0, 1, 0]);
+      assert.deepEqual(Color.parse('#FF00FF00').rgba(), [1, 0, 1, 0]);
+    });
+
+    it('parses nickname values', () => {
+      assert.deepEqual(Color.parse('red').rgba(), [1, 0, 0, 1]);
+    });
+
+    it('parses rgb(a) values', () => {
+      const colorOne = Color.parse('rgb(255, 255, 0)');
+      assert.deepEqual(colorOne.rgba(), [1, 1, 0, 1]);
+
+      const colorTwo = Color.parse('rgba(0, 255, 255, 0.5)');
+      assert.deepEqual(colorTwo.rgba(), [0, 1, 1, 0.5]);
+
+      const colorThree = Color.parse('rgb(255 255 255)');
+      assert.deepEqual(colorThree.rgba(), [1, 1, 1, 1]);
+
+      const colorFour = Color.parse('rgb(10% 10% 10%)');
+      assert.deepEqual(colorFour.rgba(), [0.1, 0.1, 0.1, 1]);
+
+      const colorFive = Color.parse('rgb(10% 10% 10% / 0.4)');
+      assert.deepEqual(colorFive.rgba(), [0.1, 0.1, 0.1, 0.4]);
+    });
+
+    it('parses hsl(a) values', () => {
+      const colorOne = Color.parse('hsl(0, 100%, 50%)');
+      assert.deepEqual(colorOne.rgba(), [1, 0, 0, 1]);
+
+      const colorTwo = Color.parse('hsla(0, 100%, 50%, 0.5)');
+      assert.deepEqual(colorTwo.rgba(), [1, 0, 0, 0.5]);
+
+      const colorThree = Color.parse('hsla(50deg 100% 100% / 50%)');
+      assert.deepEqual(colorThree.rgba(), [1, 1, 1, 0.5]);
+    });
+
+    it('handles invalid values', () => {
+      assert.isNull(Color.parse('#FAFAFA       Trailing'));
+      assert.isNull(Color.parse('#FAFAFG'));
+      assert.isNull(Color.parse('gooseberry'));
+      assert.isNull(Color.parse('rgb(10% 10% 10% /)'));
+      assert.isNull(Color.parse('rgb(10% 10% 10% 0.4 40)'));
+      assert.isNull(Color.parse('hsl(0, carrot, 30%)'));
+      assert.isNull(Color.parse('hsl(0)'));
+      assert.isNull(Color.parse('rgb(255)'));
+      assert.isNull(Color.parse('rgba(1 golf 30)'));
+    });
+  });
+});
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index b35b45c..50fdf34 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -228,16 +228,13 @@
       base::nullopt /* response_address_space */,
       nullptr /* OriginTrialTokens */, worker_start_data->devtools_worker_token,
       std::move(worker_settings),
-      static_cast<V8CacheOptions>(worker_start_data->v8_cache_options),
+      // Generate the full code cache in the first execution of the script.
+      kV8CacheOptionsFullCodeWithoutHeatCheck,
       nullptr /* worklet_module_respones_map */,
       std::move(interface_provider_info), std::move(browser_interface_broker),
       BeginFrameProviderParams(), nullptr /* parent_feature_policy */,
       base::UnguessableToken() /* agent_cluster_id */);
 
-  // Generate the full code cache in the first execution of the script.
-  global_scope_creation_params->v8_cache_options =
-      kV8CacheOptionsFullCodeWithoutHeatCheck;
-
   worker_thread_ = std::make_unique<ServiceWorkerThread>(
       std::make_unique<ServiceWorkerGlobalScopeProxy>(
           *this, *worker_context_client_, initiator_thread_task_runner),
diff --git a/third_party/blink/renderer/modules/imagecapture/image_capture_frame_grabber.h b/third_party/blink/renderer/modules/imagecapture/image_capture_frame_grabber.h
index 85b46e1..f050a85 100644
--- a/third_party/blink/renderer/modules/imagecapture/image_capture_frame_grabber.h
+++ b/third_party/blink/renderer/modules/imagecapture/image_capture_frame_grabber.h
@@ -92,10 +92,10 @@
       std::move(destruction_callback_).Run(std::move(callbacks_));
   }
 
-  ScopedWebCallbacks(ScopedWebCallbacks&& other) noexcept = default;
+  ScopedWebCallbacks(ScopedWebCallbacks&& other) = default;
   ScopedWebCallbacks(const ScopedWebCallbacks& other) = delete;
 
-  ScopedWebCallbacks& operator=(ScopedWebCallbacks&& other) noexcept = default;
+  ScopedWebCallbacks& operator=(ScopedWebCallbacks&& other) = default;
   ScopedWebCallbacks& operator=(const ScopedWebCallbacks& other) = delete;
 
   std::unique_ptr<CallbacksType> PassCallbacks() {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request.h b/third_party/blink/renderer/modules/indexeddb/idb_request.h
index f85b864..d3f1132 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request.h
@@ -110,13 +110,13 @@
     ~AsyncTraceState();
 
     // Used to transfer the trace end event state to an IDBRequest.
-    AsyncTraceState(AsyncTraceState&& other) noexcept {
+    AsyncTraceState(AsyncTraceState&& other) {
       DCHECK(IsEmpty());
       this->trace_event_name_ = other.trace_event_name_;
       this->id_ = other.id_;
       other.trace_event_name_ = nullptr;
     }
-    AsyncTraceState& operator=(AsyncTraceState&& rhs) noexcept {
+    AsyncTraceState& operator=(AsyncTraceState&& rhs) {
       DCHECK(IsEmpty());
       this->trace_event_name_ = rhs.trace_event_name_;
       this->id_ = rhs.id_;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
index 9c8d28d..f5cfb941 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
@@ -435,10 +435,9 @@
 class AsyncTraceStateForTesting : public IDBRequest::AsyncTraceState {
  public:
   AsyncTraceStateForTesting() : IDBRequest::AsyncTraceState() {}
-  AsyncTraceStateForTesting(AsyncTraceStateForTesting&& other) noexcept
+  AsyncTraceStateForTesting(AsyncTraceStateForTesting&& other)
       : IDBRequest::AsyncTraceState(std::move(other)) {}
-  AsyncTraceStateForTesting& operator=(
-      AsyncTraceStateForTesting&& rhs) noexcept {
+  AsyncTraceStateForTesting& operator=(AsyncTraceStateForTesting&& rhs) {
     AsyncTraceState::operator=(std::move(rhs));
     return *this;
   }
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc
index 24499bd..3bf00e9 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util.cc
@@ -134,13 +134,13 @@
 
 VideoCaptureSettings::VideoCaptureSettings(const VideoCaptureSettings& other) =
     default;
-VideoCaptureSettings::VideoCaptureSettings(
-    VideoCaptureSettings&& other) noexcept = default;
+VideoCaptureSettings::VideoCaptureSettings(VideoCaptureSettings&& other) =
+    default;
 VideoCaptureSettings::~VideoCaptureSettings() = default;
 VideoCaptureSettings& VideoCaptureSettings::operator=(
     const VideoCaptureSettings& other) = default;
 VideoCaptureSettings& VideoCaptureSettings::operator=(
-    VideoCaptureSettings&& other) noexcept = default;
+    VideoCaptureSettings&& other) = default;
 
 AudioCaptureSettings::AudioCaptureSettings() : AudioCaptureSettings("") {}
 
@@ -166,10 +166,10 @@
     default;
 AudioCaptureSettings& AudioCaptureSettings::operator=(
     const AudioCaptureSettings& other) = default;
-AudioCaptureSettings::AudioCaptureSettings(
-    AudioCaptureSettings&& other) noexcept = default;
+AudioCaptureSettings::AudioCaptureSettings(AudioCaptureSettings&& other) =
+    default;
 AudioCaptureSettings& AudioCaptureSettings::operator=(
-    AudioCaptureSettings&& other) noexcept = default;
+    AudioCaptureSettings&& other) = default;
 
 bool GetConstraintValueAsBoolean(
     const WebMediaConstraints& constraints,
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc
index 96992217..67c8bab 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.cc
@@ -529,9 +529,9 @@
       facing_mode(facing_mode) {}
 
 VideoInputDeviceCapabilities::VideoInputDeviceCapabilities(
-    VideoInputDeviceCapabilities&& other) noexcept = default;
+    VideoInputDeviceCapabilities&& other) = default;
 VideoInputDeviceCapabilities& VideoInputDeviceCapabilities::operator=(
-    VideoInputDeviceCapabilities&& other) noexcept = default;
+    VideoInputDeviceCapabilities&& other) = default;
 
 VideoInputDeviceCapabilities::~VideoInputDeviceCapabilities() = default;
 
@@ -557,10 +557,10 @@
 
 VideoDeviceCaptureCapabilities::VideoDeviceCaptureCapabilities() = default;
 VideoDeviceCaptureCapabilities::VideoDeviceCaptureCapabilities(
-    VideoDeviceCaptureCapabilities&& other) noexcept = default;
+    VideoDeviceCaptureCapabilities&& other) = default;
 VideoDeviceCaptureCapabilities::~VideoDeviceCaptureCapabilities() = default;
 VideoDeviceCaptureCapabilities& VideoDeviceCaptureCapabilities::operator=(
-    VideoDeviceCaptureCapabilities&& other) noexcept = default;
+    VideoDeviceCaptureCapabilities&& other) = default;
 
 VideoCaptureSettings SelectSettingsVideoDeviceCapture(
     const VideoDeviceCaptureCapabilities& capabilities,
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.h b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.h
index 66ae103..94d8ed96 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_device.h
@@ -34,9 +34,8 @@
                                Vector<media::VideoCaptureFormat> formats,
                                media::VideoFacingMode facing_mode);
   VideoInputDeviceCapabilities();
-  VideoInputDeviceCapabilities(VideoInputDeviceCapabilities&& other) noexcept;
-  VideoInputDeviceCapabilities& operator=(
-      VideoInputDeviceCapabilities&& other) noexcept;
+  VideoInputDeviceCapabilities(VideoInputDeviceCapabilities&& other);
+  VideoInputDeviceCapabilities& operator=(VideoInputDeviceCapabilities&& other);
   ~VideoInputDeviceCapabilities();
 
   String device_id;
@@ -47,11 +46,10 @@
 
 struct MODULES_EXPORT VideoDeviceCaptureCapabilities {
   VideoDeviceCaptureCapabilities();
-  VideoDeviceCaptureCapabilities(
-      VideoDeviceCaptureCapabilities&& other) noexcept;
+  VideoDeviceCaptureCapabilities(VideoDeviceCaptureCapabilities&& other);
   ~VideoDeviceCaptureCapabilities();
   VideoDeviceCaptureCapabilities& operator=(
-      VideoDeviceCaptureCapabilities&& other) noexcept;
+      VideoDeviceCaptureCapabilities&& other);
 
   // Each capabilities field is independent of each other.
   // TODO(crbug.com/704136): Replace VideoInputDeviceCapabilities in the
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
index 7651e476..99759c10 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
@@ -461,10 +461,10 @@
       callback(callback) {}
 
 MediaStreamVideoSource::PendingTrackInfo::PendingTrackInfo(
-    PendingTrackInfo&& other) noexcept = default;
+    PendingTrackInfo&& other) = default;
 MediaStreamVideoSource::PendingTrackInfo&
 MediaStreamVideoSource::PendingTrackInfo::operator=(
-    MediaStreamVideoSource::PendingTrackInfo&& other) noexcept = default;
+    MediaStreamVideoSource::PendingTrackInfo&& other) = default;
 
 MediaStreamVideoSource::PendingTrackInfo::~PendingTrackInfo() {}
 
diff --git a/third_party/blink/renderer/modules/payments/basic_card_helper.cc b/third_party/blink/renderer/modules/payments/basic_card_helper.cc
index 6f3ca2d6..96d7c5f 100644
--- a/third_party/blink/renderer/modules/payments/basic_card_helper.cc
+++ b/third_party/blink/renderer/modules/payments/basic_card_helper.cc
@@ -41,6 +41,7 @@
     const ScriptValue& input,
     Vector<BasicCardNetwork>& supported_networks_output,
     Vector<BasicCardType>& supported_types_output,
+    bool* has_supported_card_types,
     ExceptionState& exception_state) {
   DCHECK(!input.IsEmpty());
 
@@ -68,6 +69,10 @@
   }
 
   if (basic_card->hasSupportedTypes()) {
+    if (has_supported_card_types) {
+      *has_supported_card_types = true;
+    }
+
     if (basic_card->supportedTypes().size() > PaymentRequest::kMaxListSize) {
       exception_state.ThrowTypeError(
           "basic-card supportedTypes cannot be longer than 1024 elements");
diff --git a/third_party/blink/renderer/modules/payments/basic_card_helper.h b/third_party/blink/renderer/modules/payments/basic_card_helper.h
index f764cbd..239e305 100644
--- a/third_party/blink/renderer/modules/payments/basic_card_helper.h
+++ b/third_party/blink/renderer/modules/payments/basic_card_helper.h
@@ -20,11 +20,15 @@
   // Parse 'basic-card' data in |input| and store result in
   // |supported_networks_output| and |supported_types_output| or throw
   // exception.
+  //
+  // If |has_supported_card_types| is not null, then it is set to true if the
+  // basic-card specific method data has the "supportedTypes" field.
   static void ParseBasiccardData(
       const ScriptValue& input,
       Vector<::payments::mojom::blink::BasicCardNetwork>&
           supported_networks_output,
       Vector<::payments::mojom::blink::BasicCardType>& supported_types_output,
+      bool* has_supported_card_types,
       ExceptionState&);
 
   // Check whether |input| is 'basic-card' network name.
diff --git a/third_party/blink/renderer/modules/payments/basic_card_request.idl b/third_party/blink/renderer/modules/payments/basic_card_request.idl
index 8269e80..9bd0aeb9 100644
--- a/third_party/blink/renderer/modules/payments/basic_card_request.idl
+++ b/third_party/blink/renderer/modules/payments/basic_card_request.idl
@@ -12,5 +12,5 @@
 
 dictionary BasicCardRequest {
     sequence<DOMString> supportedNetworks;
-    [MeasureAs=BasicCardType] sequence<BasicCardType> supportedTypes;
+    sequence<BasicCardType> supportedTypes;
 };
diff --git a/third_party/blink/renderer/modules/payments/payment_instruments.cc b/third_party/blink/renderer/modules/payments/payment_instruments.cc
index 2aa69c4a..fb39539a 100644
--- a/third_party/blink/renderer/modules/payments/payment_instruments.cc
+++ b/third_party/blink/renderer/modules/payments/payment_instruments.cc
@@ -365,7 +365,8 @@
                                      "PaymentInstruments", "set");
       BasicCardHelper::ParseBasiccardData(
           details->capabilities(), instrument->supported_networks,
-          instrument->supported_types, exception_state);
+          instrument->supported_types, /*has_supported_card_types=*/nullptr,
+          exception_state);
       if (exception_state.HadException()) {
         resolver->Reject(exception_state);
         return;
diff --git a/third_party/blink/renderer/modules/payments/payment_manager.cc b/third_party/blink/renderer/modules/payments/payment_manager.cc
index b41c340..029f5ce 100644
--- a/third_party/blink/renderer/modules/payments/payment_manager.cc
+++ b/third_party/blink/renderer/modules/payments/payment_manager.cc
@@ -4,7 +4,7 @@
 
 #include "third_party/blink/renderer/modules/payments/payment_manager.h"
 
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -92,10 +92,9 @@
   DCHECK(registration);
 
   if (ExecutionContext* context = registration->GetExecutionContext()) {
-    if (auto* interface_provider = context->GetInterfaceProvider()) {
-      interface_provider->GetInterface(manager_.BindNewPipeAndPassReceiver(
-          context->GetTaskRunner(TaskType::kUserInteraction)));
-    }
+    context->GetBrowserInterfaceBroker().GetInterface(
+        manager_.BindNewPipeAndPassReceiver(
+            context->GetTaskRunner(TaskType::kUserInteraction)));
   }
 
   manager_.set_disconnect_handler(WTF::Bind(
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 0fe17dfc..4795bd55 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -393,19 +393,13 @@
     output->api_version = android_pay->apiVersion();
 }
 
-// Parses basic-card data to avoid parsing JSON in the browser.
-void SetBasicCardMethodData(const ScriptValue& input,
-                            PaymentMethodDataPtr& output,
-                            ExceptionState& exception_state) {
-  BasicCardHelper::ParseBasiccardData(input, output->supported_networks,
-                                      output->supported_types, exception_state);
-}
-
-void StringifyAndParseMethodSpecificData(v8::Isolate* isolate,
-                                         const String& supported_method,
-                                         const ScriptValue& input,
-                                         PaymentMethodDataPtr& output,
-                                         ExceptionState& exception_state) {
+void StringifyAndParseMethodSpecificData(
+    v8::Isolate* isolate,
+    const String& supported_method,
+    const ScriptValue& input,
+    PaymentMethodDataPtr& output,
+    bool* basic_card_has_supported_card_types,
+    ExceptionState& exception_state) {
   PaymentsValidators::ValidateAndStringifyObject(
       isolate, "Payment method data", input, output->stringified_data,
       exception_state);
@@ -423,13 +417,17 @@
   }
 
   if (supported_method == "basic-card") {
-    SetBasicCardMethodData(input, output, exception_state);
+    // Parses basic-card data to avoid parsing JSON in the browser.
+    BasicCardHelper::ParseBasiccardData(
+        input, output->supported_networks, output->supported_types,
+        basic_card_has_supported_card_types, exception_state);
   }
 }
 
 void ValidateAndConvertPaymentDetailsModifiers(
     const HeapVector<Member<PaymentDetailsModifier>>& input,
     Vector<PaymentDetailsModifierPtr>& output,
+    bool* basic_card_has_supported_card_types,
     ExecutionContext& execution_context,
     ExceptionState& exception_state) {
   if (input.size() > PaymentRequest::kMaxListSize) {
@@ -469,19 +467,22 @@
     if (modifier->hasData() && !modifier->data().IsEmpty()) {
       StringifyAndParseMethodSpecificData(
           execution_context.GetIsolate(), modifier->supportedMethod(),
-          modifier->data(), output.back()->method_data, exception_state);
+          modifier->data(), output.back()->method_data,
+          basic_card_has_supported_card_types, exception_state);
     } else {
       output.back()->method_data->stringified_data = "";
     }
   }
 }
 
-void ValidateAndConvertPaymentDetailsBase(const PaymentDetailsBase* input,
-                                          const PaymentOptions* options,
-                                          PaymentDetailsPtr& output,
-                                          String& shipping_option_output,
-                                          ExecutionContext& execution_context,
-                                          ExceptionState& exception_state) {
+void ValidateAndConvertPaymentDetailsBase(
+    const PaymentDetailsBase* input,
+    const PaymentOptions* options,
+    PaymentDetailsPtr& output,
+    String& shipping_option_output,
+    bool* basic_card_has_supported_card_types,
+    ExecutionContext& execution_context,
+    ExceptionState& exception_state) {
   if (input->hasDisplayItems()) {
     output->display_items = Vector<PaymentItemPtr>();
     ValidateAndConvertDisplayItems(input->displayItems(), "display items",
@@ -507,26 +508,29 @@
   if (input->hasModifiers()) {
     output->modifiers = Vector<PaymentDetailsModifierPtr>();
     ValidateAndConvertPaymentDetailsModifiers(
-        input->modifiers(), *output->modifiers, execution_context,
+        input->modifiers(), *output->modifiers,
+        basic_card_has_supported_card_types, execution_context,
         exception_state);
   }
 }
 
-void ValidateAndConvertPaymentDetailsInit(const PaymentDetailsInit* input,
-                                          const PaymentOptions* options,
-                                          PaymentDetailsPtr& output,
-                                          String& shipping_option_output,
-                                          ExecutionContext& execution_context,
-                                          ExceptionState& exception_state) {
+void ValidateAndConvertPaymentDetailsInit(
+    const PaymentDetailsInit* input,
+    const PaymentOptions* options,
+    PaymentDetailsPtr& output,
+    String& shipping_option_output,
+    bool* basic_card_has_supported_card_types,
+    ExecutionContext& execution_context,
+    ExceptionState& exception_state) {
   DCHECK(input->hasTotal());
   ValidateAndConvertTotal(input->total(), "total", output->total,
                           execution_context, exception_state);
   if (exception_state.HadException())
     return;
 
-  ValidateAndConvertPaymentDetailsBase(input, options, output,
-                                       shipping_option_output,
-                                       execution_context, exception_state);
+  ValidateAndConvertPaymentDetailsBase(
+      input, options, output, shipping_option_output,
+      basic_card_has_supported_card_types, execution_context, exception_state);
 }
 
 void ValidateAndConvertPaymentDetailsUpdate(const PaymentDetailsUpdate* input,
@@ -535,9 +539,9 @@
                                             String& shipping_option_output,
                                             ExecutionContext& execution_context,
                                             ExceptionState& exception_state) {
-  ValidateAndConvertPaymentDetailsBase(input, options, output,
-                                       shipping_option_output,
-                                       execution_context, exception_state);
+  ValidateAndConvertPaymentDetailsBase(
+      input, options, output, shipping_option_output,
+      /*has_supported_card_types=*/nullptr, execution_context, exception_state);
   if (exception_state.HadException())
     return;
 
@@ -585,6 +589,7 @@
     bool& skip_to_gpay_ready,
     Vector<payments::mojom::blink::PaymentMethodDataPtr>& output,
     HashSet<String>& method_names,
+    bool* basic_card_has_supported_card_types,
     ExecutionContext& execution_context,
     ExceptionState& exception_state) {
   if (input.IsEmpty()) {
@@ -621,7 +626,7 @@
       StringifyAndParseMethodSpecificData(
           execution_context.GetIsolate(),
           payment_method_data->supportedMethod(), payment_method_data->data(),
-          output.back(), exception_state);
+          output.back(), basic_card_has_supported_card_types, exception_state);
       if (exception_state.HadException())
         continue;
 
@@ -719,6 +724,10 @@
                       WebFeature::kPaymentRequestShowWithoutGesture);
   }
 
+  if (basic_card_has_supported_card_types_) {
+    UseCounter::Count(GetExecutionContext(), WebFeature::kBasicCardType);
+  }
+
   // TODO(crbug.com/779126): add support for handling payment requests in
   // immersive mode.
   if (GetFrame()->GetDocument()->GetSettings()->GetImmersiveModeEnabled()) {
@@ -1088,7 +1097,8 @@
           execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI),
           this,
           &PaymentRequest::OnUpdatePaymentDetailsTimeout),
-      is_waiting_for_show_promise_to_resolve_(false) {
+      is_waiting_for_show_promise_to_resolve_(false),
+      basic_card_has_supported_card_types_(false) {
   DCHECK(GetExecutionContext()->IsSecureContext());
 
   if (!AllowedToUsePaymentRequest(execution_context)) {
@@ -1118,13 +1128,15 @@
   Vector<payments::mojom::blink::PaymentMethodDataPtr> validated_method_data;
   ValidateAndConvertPaymentMethodData(method_data, options_, skip_to_gpay_ready,
                                       validated_method_data, method_names_,
+                                      &basic_card_has_supported_card_types_,
                                       *GetExecutionContext(), exception_state);
   if (exception_state.HadException())
     return;
 
   ValidateAndConvertPaymentDetailsInit(details, options_, validated_details,
-                                       shipping_option_, *GetExecutionContext(),
-                                       exception_state);
+                                       shipping_option_,
+                                       &basic_card_has_supported_card_types_,
+                                       *GetExecutionContext(), exception_state);
   if (exception_state.HadException())
     return;
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request.h b/third_party/blink/renderer/modules/payments/payment_request.h
index 25442453..d8f2a2c1 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.h
+++ b/third_party/blink/renderer/modules/payments/payment_request.h
@@ -171,6 +171,7 @@
   TaskRunnerTimer<PaymentRequest> complete_timer_;
   TaskRunnerTimer<PaymentRequest> update_payment_details_timer_;
   bool is_waiting_for_show_promise_to_resolve_;
+  bool basic_card_has_supported_card_types_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentRequest);
 };
diff --git a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
index 9f871bf..34884c49 100644
--- a/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
+++ b/third_party/blink/renderer/modules/service_worker/web_embedded_worker_impl_test.cc
@@ -27,7 +27,6 @@
 #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_client.h"
 #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h"
 #include "third_party/blink/public/web/web_embedded_worker_start_data.h"
-#include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
@@ -209,7 +208,6 @@
     start_data->script_type = mojom::ScriptType::kClassic;
     start_data->wait_for_debugger_mode =
         WebEmbeddedWorkerStartData::kDontWaitForDebugger;
-    start_data->v8_cache_options = WebSettings::V8CacheOptions::kDefault;
     return start_data;
   }
 
diff --git a/third_party/blink/renderer/modules/sms/sms_receiver.cc b/third_party/blink/renderer/modules/sms/sms_receiver.cc
index be89fae..127220a1 100644
--- a/third_party/blink/renderer/modules/sms/sms_receiver.cc
+++ b/third_party/blink/renderer/modules/sms/sms_receiver.cc
@@ -6,7 +6,7 @@
 
 #include "third_party/blink/renderer/modules/sms/sms_receiver.h"
 
-#include "services/service_manager/public/cpp/interface_provider.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "third_party/blink/public/mojom/sms/sms_receiver.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -46,7 +46,7 @@
       GetExecutionContext()->GetTaskRunner(TaskType::kInternalIPC);
 
   if (!service_) {
-    GetExecutionContext()->GetInterfaceProvider()->GetInterface(
+    GetExecutionContext()->GetBrowserInterfaceBroker().GetInterface(
         service_.BindNewPipeAndPassReceiver(task_runner));
     service_.set_disconnect_handler(WTF::Bind(
         &SMSReceiver::OnSMSReceiverConnectionError, WrapWeakPersistent(this)));
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_origin_3d.idl b/third_party/blink/renderer/modules/webgpu/gpu_origin_3d.idl
index d1359ba..fbfb79b 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_origin_3d.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_origin_3d.idl
@@ -5,7 +5,7 @@
 // https://gpuweb.github.io/gpuweb/
 
 dictionary GPUOrigin3D {
-    required unsigned long x;
-    required unsigned long y;
-    required unsigned long z;
+    unsigned long x = 0;
+    unsigned long y = 0;
+    unsigned long z = 0;
 };
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_texture_copy_view.idl b/third_party/blink/renderer/modules/webgpu/gpu_texture_copy_view.idl
index c0630b9..b5ffa0a22 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_texture_copy_view.idl
+++ b/third_party/blink/renderer/modules/webgpu/gpu_texture_copy_view.idl
@@ -8,5 +8,5 @@
     required GPUTexture texture;
     unsigned long mipLevel = 0;
     unsigned long arrayLayer = 0;
-    required GPUOrigin3D origin;
+    GPUOrigin3D origin;
 };
diff --git a/third_party/blink/renderer/platform/bindings/dom_data_store.h b/third_party/blink/renderer/platform/bindings/dom_data_store.h
index d660b674..0660dd18 100644
--- a/third_party/blink/renderer/platform/bindings/dom_data_store.h
+++ b/third_party/blink/renderer/platform/bindings/dom_data_store.h
@@ -230,12 +230,11 @@
         : TraceWrapperV8Reference(isolate, handle) {}
 
     // Move support without write barrier.
-    DOMWorldWrapperReference(DOMWorldWrapperReference&& other) noexcept
+    DOMWorldWrapperReference(DOMWorldWrapperReference&& other)
         : TraceWrapperV8Reference() {
       handle_ = std::move(other.handle_);
     }
-    DOMWorldWrapperReference& operator=(
-        DOMWorldWrapperReference&& rhs) noexcept {
+    DOMWorldWrapperReference& operator=(DOMWorldWrapperReference&& rhs) {
       handle_ = std::move(rhs.handle_);
       return *this;
     }
diff --git a/third_party/blink/renderer/platform/exported/web_string.cc b/third_party/blink/renderer/platform/exported/web_string.cc
index 71d4c07..fbe4828 100644
--- a/third_party/blink/renderer/platform/exported/web_string.cc
+++ b/third_party/blink/renderer/platform/exported/web_string.cc
@@ -51,9 +51,9 @@
 WebString::~WebString() = default;
 WebString::WebString() = default;
 WebString::WebString(const WebString&) = default;
-WebString::WebString(WebString&&) noexcept = default;
+WebString::WebString(WebString&&) = default;
 WebString& WebString::operator=(const WebString&) = default;
-WebString& WebString::operator=(WebString&&) noexcept = default;
+WebString& WebString::operator=(WebString&&) = default;
 
 WebString::WebString(const WebUChar* data, size_t len)
     : impl_(StringImpl::Create8BitIfPossible(data, len)) {}
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index f15bd95..f6b8014 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -564,13 +564,10 @@
   scoped_refptr<StaticBitmapImage> image;
 
   // If its cross thread, then the sync token was already verified. If not, then
-  // it doesn't need to be verified.
-  if (!is_cross_thread())
-    owning_thread_data().mailbox_sync_mode = kUnverifiedSyncToken;
-
+  // we don't need one. The image lazily generates a token if needed.
+  gpu::SyncToken token = is_cross_thread() ? sync_token() : gpu::SyncToken();
   image = AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
-      mailbox(), is_cross_thread() ? sync_token() : GetSyncToken(),
-      texture_id_for_image, image_info, texture_target_,
+      mailbox(), token, texture_id_for_image, image_info, texture_target_,
       context_provider_wrapper_, owning_thread_id_, is_origin_top_left_,
       std::move(release_callback));
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.cc b/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
index 1245f2dc..4aa0e2d 100644
--- a/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.cc
@@ -110,8 +110,6 @@
      "tree"},
     {CompositingReason::kLayerForDescendantClip, "layerForDescendantClip",
      "Secondary layer, to clip descendants of the owning layer"},
-    {CompositingReason::kLayerForPerspective, "layerForPerspective",
-     "Secondary layer, to house the perspective transform for all descendants"},
     {CompositingReason::kLayerForHorizontalScrollbar,
      "layerForHorizontalScrollbar",
      "Secondary layer, the horizontal scrollbar layer"},
diff --git a/third_party/blink/renderer/platform/graphics/compositing_reasons.h b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
index 64298eb2..3de897d 100644
--- a/third_party/blink/renderer/platform/graphics/compositing_reasons.h
+++ b/third_party/blink/renderer/platform/graphics/compositing_reasons.h
@@ -70,7 +70,6 @@
   /* CompositedLayerMapping internal hierarchy reasons. */                    \
   V(LayerForAncestorClip)                                                     \
   V(LayerForDescendantClip)                                                   \
-  V(LayerForPerspective)                                                      \
   V(LayerForHorizontalScrollbar)                                              \
   V(LayerForVerticalScrollbar)                                                \
   V(LayerForOverflowControlsHost)                                             \
diff --git a/third_party/blink/renderer/platform/graphics/contiguous_container.cc b/third_party/blink/renderer/platform/graphics/contiguous_container.cc
index fd01c00..19cb6f2 100644
--- a/third_party/blink/renderer/platform/graphics/contiguous_container.cc
+++ b/third_party/blink/renderer/platform/graphics/contiguous_container.cc
@@ -69,7 +69,7 @@
     : end_index_(0), max_object_size_(max_object_size) {}
 
 ContiguousContainerBase::ContiguousContainerBase(
-    ContiguousContainerBase&& source) noexcept
+    ContiguousContainerBase&& source)
     : ContiguousContainerBase(source.max_object_size_) {
   Swap(source);
 }
@@ -77,7 +77,7 @@
 ContiguousContainerBase::~ContiguousContainerBase() = default;
 
 ContiguousContainerBase& ContiguousContainerBase::operator=(
-    ContiguousContainerBase&& source) noexcept {
+    ContiguousContainerBase&& source) {
   Swap(source);
   return *this;
 }
diff --git a/third_party/blink/renderer/platform/graphics/contiguous_container.h b/third_party/blink/renderer/platform/graphics/contiguous_container.h
index 867e59c..3eee558 100644
--- a/third_party/blink/renderer/platform/graphics/contiguous_container.h
+++ b/third_party/blink/renderer/platform/graphics/contiguous_container.h
@@ -42,10 +42,10 @@
 
  protected:
   explicit ContiguousContainerBase(size_t max_object_size);
-  ContiguousContainerBase(ContiguousContainerBase&&) noexcept;
+  ContiguousContainerBase(ContiguousContainerBase&&);
   ~ContiguousContainerBase();
 
-  ContiguousContainerBase& operator=(ContiguousContainerBase&&) noexcept;
+  ContiguousContainerBase& operator=(ContiguousContainerBase&&);
 
   size_t size() const { return elements_.size(); }
   bool IsEmpty() const { return !size(); }
@@ -147,7 +147,7 @@
                            WTF_HEAP_PROFILER_TYPE_NAME(BaseElementType));
   }
 
-  ContiguousContainer(ContiguousContainer&& source) noexcept
+  ContiguousContainer(ContiguousContainer&& source)
       : ContiguousContainerBase(std::move(source)) {}
 
   ~ContiguousContainer() {
@@ -157,7 +157,7 @@
     }
   }
 
-  ContiguousContainer& operator=(ContiguousContainer&& source) noexcept {
+  ContiguousContainer& operator=(ContiguousContainer&& source) {
     // Must clear in the derived class to ensure that element destructors
     // care called.
     Clear();
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
index 66bc765..ecad189 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.cc
@@ -246,9 +246,9 @@
 }
 
 ImageLayerBridge::RegisteredBitmap::RegisteredBitmap() = default;
-ImageLayerBridge::RegisteredBitmap::RegisteredBitmap(
-    RegisteredBitmap&& other) noexcept = default;
+ImageLayerBridge::RegisteredBitmap::RegisteredBitmap(RegisteredBitmap&& other) =
+    default;
 ImageLayerBridge::RegisteredBitmap& ImageLayerBridge::RegisteredBitmap::
-operator=(RegisteredBitmap&& other) noexcept = default;
+operator=(RegisteredBitmap&& other) = default;
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
index 4a08050..d550eb47 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
@@ -64,8 +64,8 @@
   // only with software compositing.
   struct RegisteredBitmap {
     RegisteredBitmap();
-    RegisteredBitmap(RegisteredBitmap&& other) noexcept;
-    RegisteredBitmap& operator=(RegisteredBitmap&& other) noexcept;
+    RegisteredBitmap(RegisteredBitmap&& other);
+    RegisteredBitmap& operator=(RegisteredBitmap&& other);
 
     scoped_refptr<cc::CrossThreadSharedBitmap> bitmap;
     cc::SharedBitmapIdRegistration registration;
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item_list.h b/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
index cd3ed2f..9ee2571 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item_list.h
@@ -28,10 +28,10 @@
  public:
   DisplayItemList(size_t initial_size_bytes)
       : ContiguousContainer(kMaximumDisplayItemSize, initial_size_bytes) {}
-  DisplayItemList(DisplayItemList&& source) noexcept
+  DisplayItemList(DisplayItemList&& source)
       : ContiguousContainer(std::move(source)) {}
 
-  DisplayItemList& operator=(DisplayItemList&& source) noexcept {
+  DisplayItemList& operator=(DisplayItemList&& source) {
     ContiguousContainer::operator=(std::move(source));
     return *this;
   }
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index a3b0d0a..54b950c 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -145,7 +145,7 @@
   }
   KeyWithCopyingMoveConstructor(const KeyWithCopyingMoveConstructor&) = default;
   // The move constructor delegates to the copy constructor intentionally.
-  KeyWithCopyingMoveConstructor(KeyWithCopyingMoveConstructor&& x) noexcept
+  KeyWithCopyingMoveConstructor(KeyWithCopyingMoveConstructor&& x)
       : KeyWithCopyingMoveConstructor(x) {}
   KeyWithCopyingMoveConstructor& operator=(
       const KeyWithCopyingMoveConstructor&) = default;
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_stream.cc b/third_party/blink/renderer/platform/image-decoders/segment_stream.cc
index 4024f9c..5a98701 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_stream.cc
+++ b/third_party/blink/renderer/platform/image-decoders/segment_stream.cc
@@ -10,10 +10,10 @@
 
 SegmentStream::SegmentStream() = default;
 
-SegmentStream::SegmentStream(SegmentStream&& rhs) noexcept
+SegmentStream::SegmentStream(SegmentStream&& rhs)
     : reader_(std::move(rhs.reader_)), position_(rhs.position_) {}
 
-SegmentStream& SegmentStream::operator=(SegmentStream&& rhs) noexcept {
+SegmentStream& SegmentStream::operator=(SegmentStream&& rhs) {
   reader_ = std::move(rhs.reader_);
   position_ = rhs.position_;
 
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_stream.h b/third_party/blink/renderer/platform/image-decoders/segment_stream.h
index f49f551..fa1ccf2 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_stream.h
+++ b/third_party/blink/renderer/platform/image-decoders/segment_stream.h
@@ -19,8 +19,8 @@
   SegmentStream();
   SegmentStream(const SegmentStream&) = delete;
   SegmentStream& operator=(const SegmentStream&) = delete;
-  SegmentStream(SegmentStream&&) noexcept;
-  SegmentStream& operator=(SegmentStream&&) noexcept;
+  SegmentStream(SegmentStream&&);
+  SegmentStream& operator=(SegmentStream&&);
 
   ~SegmentStream() override;
 
diff --git a/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h b/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h
index 555be28..34bad3f 100644
--- a/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h
+++ b/third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h
@@ -41,7 +41,7 @@
   RevocableInterfacePtr(std::nullptr_t) {}
 
   // Takes over the binding of another RevocableInterfacePtr.
-  RevocableInterfacePtr(RevocableInterfacePtr&& other) noexcept {
+  RevocableInterfacePtr(RevocableInterfacePtr&& other) {
     interface_ptr_ = std::move(other.interface_ptr_);
     SetInvalidator(other.invalidator_.get());
     // Reset the other interface ptr to remove it as an observer of the
@@ -58,7 +58,7 @@
 
   // Takes over the binding of another RevocableInterfacePtr, and closes any
   // message pipe already bound to this pointer.
-  RevocableInterfacePtr& operator=(RevocableInterfacePtr&& other) noexcept {
+  RevocableInterfacePtr& operator=(RevocableInterfacePtr&& other) {
     reset();
     interface_ptr_ = std::move(other.interface_ptr_);
     SetInvalidator(other.invalidator_.get());
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data.cc b/third_party/blink/renderer/platform/network/encoded_form_data.cc
index bc4ec14d7..1b660e6f 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data.cc
+++ b/third_party/blink/renderer/platform/network/encoded_form_data.cc
@@ -61,11 +61,10 @@
     : type_(kDataPipe), data_pipe_getter_(std::move(data_pipe_getter)) {}
 
 FormDataElement::FormDataElement(const FormDataElement&) = default;
-FormDataElement::FormDataElement(FormDataElement&&) noexcept = default;
+FormDataElement::FormDataElement(FormDataElement&&) = default;
 FormDataElement::~FormDataElement() = default;
 FormDataElement& FormDataElement::operator=(const FormDataElement&) = default;
-FormDataElement& FormDataElement::operator=(FormDataElement&&) noexcept =
-    default;
+FormDataElement& FormDataElement::operator=(FormDataElement&&) = default;
 
 bool operator==(const FormDataElement& a, const FormDataElement& b) {
   if (&a == &b)
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data.h b/third_party/blink/renderer/platform/network/encoded_form_data.h
index 48f84c8..865f664 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data.h
+++ b/third_party/blink/renderer/platform/network/encoded_form_data.h
@@ -61,12 +61,12 @@
   explicit FormDataElement(scoped_refptr<WrappedDataPipeGetter>);
 
   FormDataElement(const FormDataElement&);
-  FormDataElement(FormDataElement&&) noexcept;
+  FormDataElement(FormDataElement&&);
 
   ~FormDataElement();
 
   FormDataElement& operator=(const FormDataElement&);
-  FormDataElement& operator=(FormDataElement&&) noexcept;
+  FormDataElement& operator=(FormDataElement&&);
 
   bool IsSafeToSendToAnotherThread() const;
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 6fb001f6..ee86fde 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -203,7 +203,7 @@
     },
     {
       name: "Badging",
-      origin_trial_feature_name: "BadgingV2",
+      origin_trial_feature_name: "Badging",
       status: "experimental",
     },
     {
diff --git a/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc
index ebe99ff..e971856 100644
--- a/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/frame_or_worker_scheduler.cc
@@ -28,15 +28,14 @@
 }
 
 FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle::
-    SchedulingAffectingFeatureHandle(
-        SchedulingAffectingFeatureHandle&& other) noexcept
+    SchedulingAffectingFeatureHandle(SchedulingAffectingFeatureHandle&& other)
     : feature_(other.feature_), scheduler_(std::move(other.scheduler_)) {
   other.scheduler_ = nullptr;
 }
 
 FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle&
 FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle::operator=(
-    SchedulingAffectingFeatureHandle&& other) noexcept {
+    SchedulingAffectingFeatureHandle&& other) {
   feature_ = other.feature_;
   policy_ = std::move(other.policy_);
   scheduler_ = std::move(other.scheduler_);
diff --git a/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task.cc b/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task.cc
index 2c84d70..fad56ee1 100644
--- a/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/post_cancellable_task.cc
@@ -103,9 +103,9 @@
   Cancel();
 }
 
-TaskHandle::TaskHandle(TaskHandle&&) noexcept = default;
+TaskHandle::TaskHandle(TaskHandle&&) = default;
 
-TaskHandle& TaskHandle::operator=(TaskHandle&& other) noexcept {
+TaskHandle& TaskHandle::operator=(TaskHandle&& other) {
   TaskHandle tmp(std::move(other));
   runner_.swap(tmp.runner_);
   return *this;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/web_scoped_virtual_time_pauser.cc b/third_party/blink/renderer/platform/scheduler/main_thread/web_scoped_virtual_time_pauser.cc
index f55730a..b64ec9d 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/web_scoped_virtual_time_pauser.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/web_scoped_virtual_time_pauser.cc
@@ -28,7 +28,7 @@
 }
 
 WebScopedVirtualTimePauser::WebScopedVirtualTimePauser(
-    WebScopedVirtualTimePauser&& other) noexcept {
+    WebScopedVirtualTimePauser&& other) {
   virtual_time_when_paused_ = other.virtual_time_when_paused_;
   paused_ = other.paused_;
   duration_ = other.duration_;
@@ -39,7 +39,7 @@
 }
 
 WebScopedVirtualTimePauser& WebScopedVirtualTimePauser::operator=(
-    WebScopedVirtualTimePauser&& other) noexcept {
+    WebScopedVirtualTimePauser&& other) {
   if (scheduler_ && paused_)
     DecrementVirtualTimePauseCount();
   virtual_time_when_paused_ = other.virtual_time_when_paused_;
diff --git a/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h
index 0e91c6f..3ded554 100644
--- a/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h
@@ -57,12 +57,11 @@
 
    public:
     SchedulingAffectingFeatureHandle() = default;
-    SchedulingAffectingFeatureHandle(
-        SchedulingAffectingFeatureHandle&&) noexcept;
+    SchedulingAffectingFeatureHandle(SchedulingAffectingFeatureHandle&&);
     inline ~SchedulingAffectingFeatureHandle() { reset(); }
 
     SchedulingAffectingFeatureHandle& operator=(
-        SchedulingAffectingFeatureHandle&&) noexcept;
+        SchedulingAffectingFeatureHandle&&);
 
     inline void reset() {
       if (scheduler_)
diff --git a/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h b/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h
index 38cbf63..ae66c73c 100644
--- a/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h
+++ b/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h
@@ -28,8 +28,8 @@
   TaskHandle();
   ~TaskHandle();
 
-  TaskHandle(TaskHandle&&) noexcept;
-  TaskHandle& operator=(TaskHandle&&) noexcept;
+  TaskHandle(TaskHandle&&);
+  TaskHandle& operator=(TaskHandle&&);
 
   // Returns true if the task will run later. Returns false if the task is
   // cancelled or the task is run already.
diff --git a/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc b/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
index 2e9a9d1..c9d777e7 100644
--- a/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
+++ b/third_party/blink/renderer/platform/weborigin/origin_access_entry.cc
@@ -59,8 +59,7 @@
                network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort,
                priority) {}
 
-OriginAccessEntry::OriginAccessEntry(OriginAccessEntry&& from) noexcept =
-    default;
+OriginAccessEntry::OriginAccessEntry(OriginAccessEntry&& from) = default;
 
 network::cors::OriginAccessEntry::MatchResult OriginAccessEntry::MatchesOrigin(
     const SecurityOrigin& origin) const {
diff --git a/third_party/blink/renderer/platform/weborigin/origin_access_entry.h b/third_party/blink/renderer/platform/weborigin/origin_access_entry.h
index b3e5674..5113a406 100644
--- a/third_party/blink/renderer/platform/weborigin/origin_access_entry.h
+++ b/third_party/blink/renderer/platform/weborigin/origin_access_entry.h
@@ -58,7 +58,7 @@
       network::mojom::CorsDomainMatchMode,
       network::mojom::CorsOriginAccessMatchPriority priority =
           network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
-  OriginAccessEntry(OriginAccessEntry&& from) noexcept;
+  OriginAccessEntry(OriginAccessEntry&& from);
 
   network::cors::OriginAccessEntry::MatchResult MatchesOrigin(
       const SecurityOrigin&) const;
diff --git a/third_party/blink/renderer/platform/wtf/deque.h b/third_party/blink/renderer/platform/wtf/deque.h
index c6fe1b9..4a98ffb 100644
--- a/third_party/blink/renderer/platform/wtf/deque.h
+++ b/third_party/blink/renderer/platform/wtf/deque.h
@@ -66,8 +66,8 @@
   Deque();
   Deque(const Deque&);
   Deque& operator=(const Deque&);
-  Deque(Deque&&) noexcept;
-  Deque& operator=(Deque&&) noexcept;
+  Deque(Deque&&);
+  Deque& operator=(Deque&&);
 
   void Finalize();
 
@@ -343,14 +343,14 @@
 }
 
 template <typename T, wtf_size_t inlineCapacity, typename Allocator>
-inline Deque<T, inlineCapacity, Allocator>::Deque(Deque&& other) noexcept
+inline Deque<T, inlineCapacity, Allocator>::Deque(Deque&& other)
     : start_(0), end_(0) {
   Swap(other);
 }
 
 template <typename T, wtf_size_t inlineCapacity, typename Allocator>
 inline Deque<T, inlineCapacity, Allocator>&
-Deque<T, inlineCapacity, Allocator>::operator=(Deque&& other) noexcept {
+Deque<T, inlineCapacity, Allocator>::operator=(Deque&& other) {
   Swap(other);
   return *this;
 }
diff --git a/third_party/blink/renderer/platform/wtf/functional.h b/third_party/blink/renderer/platform/wtf/functional.h
index 2c5aeea5f..5e4b373 100644
--- a/third_party/blink/renderer/platform/wtf/functional.h
+++ b/third_party/blink/renderer/platform/wtf/functional.h
@@ -117,8 +117,7 @@
 class PassedWrapper final {
  public:
   explicit PassedWrapper(T&& scoper) : scoper_(std::move(scoper)) {}
-  PassedWrapper(PassedWrapper&& other) noexcept
-      : scoper_(std::move(other.scoper_)) {}
+  PassedWrapper(PassedWrapper&& other) : scoper_(std::move(other.scoper_)) {}
   T MoveOut() const { return std::move(scoper_); }
 
  private:
@@ -319,9 +318,8 @@
   CrossThreadFunction(const CrossThreadFunction&) = delete;
   CrossThreadFunction& operator=(const CrossThreadFunction&) = delete;
 
-  CrossThreadFunction(CrossThreadFunction&& other) noexcept = default;
-  CrossThreadFunction& operator=(CrossThreadFunction&& other) noexcept =
-      default;
+  CrossThreadFunction(CrossThreadFunction&& other) = default;
+  CrossThreadFunction& operator=(CrossThreadFunction&& other) = default;
 
   R Run(Args... args) const & {
     return callback_.Run(std::forward<Args>(args)...);
@@ -356,9 +354,8 @@
   CrossThreadOnceFunction(const CrossThreadOnceFunction&) = delete;
   CrossThreadOnceFunction& operator=(const CrossThreadOnceFunction&) = delete;
 
-  CrossThreadOnceFunction(CrossThreadOnceFunction&& other) noexcept = default;
-  CrossThreadOnceFunction& operator=(CrossThreadOnceFunction&& other) noexcept =
-      default;
+  CrossThreadOnceFunction(CrossThreadOnceFunction&& other) = default;
+  CrossThreadOnceFunction& operator=(CrossThreadOnceFunction&& other) = default;
 
   R Run(Args... args) && {
     return std::move(callback_).Run(std::forward<Args>(args)...);
diff --git a/third_party/blink/renderer/platform/wtf/hash_map.h b/third_party/blink/renderer/platform/wtf/hash_map.h
index 8a002a8..46b39e0 100644
--- a/third_party/blink/renderer/platform/wtf/hash_map.h
+++ b/third_party/blink/renderer/platform/wtf/hash_map.h
@@ -106,8 +106,8 @@
 #endif
   HashMap(const HashMap&) = default;
   HashMap& operator=(const HashMap&) = default;
-  HashMap(HashMap&&) noexcept = default;
-  HashMap& operator=(HashMap&&) noexcept = default;
+  HashMap(HashMap&&) = default;
+  HashMap& operator=(HashMap&&) = default;
 
   // For example, HashMap<int, int>({{1, 11}, {2, 22}, {3, 33}}) will give you
   // a HashMap containing a mapping {1 -> 11, 2 -> 22, 3 -> 33}.
diff --git a/third_party/blink/renderer/platform/wtf/hash_set.h b/third_party/blink/renderer/platform/wtf/hash_set.h
index c79e45f1..d7cdeed 100644
--- a/third_party/blink/renderer/platform/wtf/hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/hash_set.h
@@ -74,8 +74,8 @@
   }
   HashSet(const HashSet&) = default;
   HashSet& operator=(const HashSet&) = default;
-  HashSet(HashSet&&) noexcept = default;
-  HashSet& operator=(HashSet&&) noexcept = default;
+  HashSet(HashSet&&) = default;
+  HashSet& operator=(HashSet&&) = default;
 
   HashSet(std::initializer_list<ValueType> elements);
   HashSet& operator=(std::initializer_list<ValueType> elements);
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index 373280f..52483ec5 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -747,10 +747,10 @@
   }
 
   HashTable(const HashTable&);
-  HashTable(HashTable&&) noexcept;
+  HashTable(HashTable&&);
   void swap(HashTable&);
   HashTable& operator=(const HashTable&);
-  HashTable& operator=(HashTable&&) noexcept;
+  HashTable& operator=(HashTable&&);
 
   // When the hash table is empty, just return the same iterator for end as
   // for begin.  This is more efficient because we don't have to skip all the
@@ -1929,7 +1929,7 @@
           typename KeyTraits,
           typename Allocator>
 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
-    HashTable(HashTable&& other) noexcept
+    HashTable(HashTable&& other)
     : table_(nullptr),
       table_size_(0),
       key_count_(0),
@@ -2008,7 +2008,7 @@
           typename Allocator>
 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>&
 HashTable<Key, Value, Extractor, HashFunctions, Traits, KeyTraits, Allocator>::
-operator=(HashTable&& other) noexcept {
+operator=(HashTable&& other) {
   swap(other);
   return *this;
 }
diff --git a/third_party/blink/renderer/platform/wtf/linked_hash_set.h b/third_party/blink/renderer/platform/wtf/linked_hash_set.h
index 79c3d76d..71ef37a 100644
--- a/third_party/blink/renderer/platform/wtf/linked_hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/linked_hash_set.h
@@ -121,7 +121,7 @@
   LinkedHashSetNodeBase(const LinkedHashSetNodeBase& other)
       : prev_(nullptr), next_(nullptr) {}
 
-  LinkedHashSetNodeBase(LinkedHashSetNodeBase&& other) noexcept
+  LinkedHashSetNodeBase(LinkedHashSetNodeBase&& other)
       : prev_(other.prev_), next_(other.next_) {
     other.prev_ = nullptr;
     other.next_ = nullptr;
@@ -151,7 +151,7 @@
                     LinkedHashSetNodeBase* next)
       : LinkedHashSetNodeBase(prev, next), value_(std::move(value)) {}
 
-  LinkedHashSetNode(LinkedHashSetNode&& other) noexcept
+  LinkedHashSetNode(LinkedHashSetNode&& other)
       : LinkedHashSetNodeBase(std::move(other)),
         value_(std::move(other.value_)) {}
 
@@ -214,9 +214,9 @@
 
   LinkedHashSet();
   LinkedHashSet(const LinkedHashSet&);
-  LinkedHashSet(LinkedHashSet&&) noexcept;
+  LinkedHashSet(LinkedHashSet&&);
   LinkedHashSet& operator=(const LinkedHashSet&);
-  LinkedHashSet& operator=(LinkedHashSet&&) noexcept;
+  LinkedHashSet& operator=(LinkedHashSet&&);
 
   // Needs finalization. The anchor needs to unlink itself from the chain.
   ~LinkedHashSet();
@@ -748,7 +748,7 @@
 }
 
 template <typename T, typename U, typename V, typename W>
-inline LinkedHashSet<T, U, V, W>::LinkedHashSet(LinkedHashSet&& other) noexcept
+inline LinkedHashSet<T, U, V, W>::LinkedHashSet(LinkedHashSet&& other)
     : anchor_() {
   Swap(other);
 }
@@ -763,7 +763,7 @@
 
 template <typename T, typename U, typename V, typename W>
 inline LinkedHashSet<T, U, V, W>& LinkedHashSet<T, U, V, W>::operator=(
-    LinkedHashSet&& other) noexcept {
+    LinkedHashSet&& other) {
   Swap(other);
   return *this;
 }
diff --git a/third_party/blink/renderer/platform/wtf/list_hash_set.h b/third_party/blink/renderer/platform/wtf/list_hash_set.h
index ead6a87..78bd279 100644
--- a/third_party/blink/renderer/platform/wtf/list_hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/list_hash_set.h
@@ -149,9 +149,9 @@
 
   ListHashSet();
   ListHashSet(const ListHashSet&);
-  ListHashSet(ListHashSet&&) noexcept;
+  ListHashSet(ListHashSet&&);
   ListHashSet& operator=(const ListHashSet&);
-  ListHashSet& operator=(ListHashSet&&) noexcept;
+  ListHashSet& operator=(ListHashSet&&);
   void Finalize();
 
   void Swap(ListHashSet&);
@@ -780,8 +780,7 @@
 }
 
 template <typename T, size_t inlineCapacity, typename U, typename V>
-inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet(
-    ListHashSet&& other) noexcept
+inline ListHashSet<T, inlineCapacity, U, V>::ListHashSet(ListHashSet&& other)
     : head_(nullptr), tail_(nullptr) {
   Swap(other);
 }
@@ -796,7 +795,7 @@
 
 template <typename T, size_t inlineCapacity, typename U, typename V>
 inline ListHashSet<T, inlineCapacity, U, V>&
-ListHashSet<T, inlineCapacity, U, V>::operator=(ListHashSet&& other) noexcept {
+ListHashSet<T, inlineCapacity, U, V>::operator=(ListHashSet&& other) {
   Swap(other);
   return *this;
 }
diff --git a/third_party/blink/renderer/platform/wtf/type_traits_test.cc b/third_party/blink/renderer/platform/wtf/type_traits_test.cc
index 0930b73..1b45d55 100644
--- a/third_party/blink/renderer/platform/wtf/type_traits_test.cc
+++ b/third_party/blink/renderer/platform/wtf/type_traits_test.cc
@@ -158,7 +158,7 @@
   STACK_ALLOCATED();
 
  public:
-  CopyAssignmentDeleted& operator=(CopyAssignmentDeleted&&) noexcept;
+  CopyAssignmentDeleted& operator=(CopyAssignmentDeleted&&);
 
  private:
   CopyAssignmentDeleted& operator=(const CopyAssignmentDeleted&) = delete;
@@ -173,7 +173,7 @@
   STACK_ALLOCATED();
 
  public:
-  CopyAssignmentPrivate& operator=(CopyAssignmentPrivate&&) noexcept;
+  CopyAssignmentPrivate& operator=(CopyAssignmentPrivate&&);
 
  private:
   CopyAssignmentPrivate& operator=(const CopyAssignmentPrivate&);
@@ -188,7 +188,7 @@
   STACK_ALLOCATED();
 
  public:
-  CopyAssignmentUndeclared& operator=(CopyAssignmentUndeclared&&) noexcept;
+  CopyAssignmentUndeclared& operator=(CopyAssignmentUndeclared&&);
 };
 
 static_assert(!std::is_copy_assignable<CopyAssignmentUndeclared>::value,
diff --git a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
index 3f44cd2..ee7c89a 100644
--- a/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
+++ b/third_party/blink/renderer/platform/wtf/typed_arrays/array_buffer_contents.h
@@ -68,7 +68,7 @@
           deleter_(deleter),
           deleter_info_(deleter_info) {}
     // Move constructor
-    DataHandle(DataHandle&& other) noexcept { *this = std::move(other); }
+    DataHandle(DataHandle&& other) { *this = std::move(other); }
     ~DataHandle() {
       if (!data_)
         return;
@@ -76,7 +76,7 @@
     }
 
     // Move operator
-    DataHandle& operator=(DataHandle&& other) noexcept {
+    DataHandle& operator=(DataHandle&& other) {
       data_ = other.data_;
       data_length_ = other.data_length_;
       deleter_ = other.deleter_;
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index 2783c2b..43a27f7 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -1018,8 +1018,8 @@
   Vector& operator=(const Vector<T, otherCapacity, Allocator>&);
 
   // Moving.
-  Vector(Vector&&) noexcept;
-  Vector& operator=(Vector&&) noexcept;
+  Vector(Vector&&);
+  Vector& operator=(Vector&&);
 
   // Construct with an initializer list. You can do e.g.
   //     Vector<int> v({1, 2, 3});
@@ -1459,7 +1459,7 @@
 
 template <typename T, wtf_size_t inlineCapacity, typename Allocator>
 Vector<T, inlineCapacity, Allocator>::Vector(
-    Vector<T, inlineCapacity, Allocator>&& other) noexcept {
+    Vector<T, inlineCapacity, Allocator>&& other) {
   size_ = 0;
   // It's a little weird to implement a move constructor using swap but this
   // way we don't have to add a move constructor to VectorBuffer.
@@ -1468,7 +1468,7 @@
 
 template <typename T, wtf_size_t inlineCapacity, typename Allocator>
 Vector<T, inlineCapacity, Allocator>& Vector<T, inlineCapacity, Allocator>::
-operator=(Vector<T, inlineCapacity, Allocator>&& other) noexcept {
+operator=(Vector<T, inlineCapacity, Allocator>&& other) {
   swap(other);
   return *this;
 }
diff --git a/third_party/blink/renderer/platform/wtf/vector_test.cc b/third_party/blink/renderer/platform/wtf/vector_test.cc
index aa8a138c..788cc2bb 100644
--- a/third_party/blink/renderer/platform/wtf/vector_test.cc
+++ b/third_party/blink/renderer/platform/wtf/vector_test.cc
@@ -420,8 +420,8 @@
 class MojoMoveOnlyType final {
  public:
   MojoMoveOnlyType();
-  MojoMoveOnlyType(MojoMoveOnlyType&&) noexcept;
-  MojoMoveOnlyType& operator=(MojoMoveOnlyType&&) noexcept;
+  MojoMoveOnlyType(MojoMoveOnlyType&&);
+  MojoMoveOnlyType& operator=(MojoMoveOnlyType&&);
   ~MojoMoveOnlyType();
 
  private:
diff --git a/third_party/blink/renderer/platform/wtf/wtf_test_helper.h b/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
index e797445..8f31365 100644
--- a/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
+++ b/third_party/blink/renderer/platform/wtf/wtf_test_helper.h
@@ -36,9 +36,9 @@
  public:
   explicit MoveOnly(int i = 0) : i_(i) {}
 
-  MoveOnly(MoveOnly&& other) noexcept : i_(other.i_) { other.i_ = 0; }
+  MoveOnly(MoveOnly&& other) : i_(other.i_) { other.i_ = 0; }
 
-  MoveOnly& operator=(MoveOnly&& other) noexcept {
+  MoveOnly& operator=(MoveOnly&& other) {
     if (this != &other) {
       i_ = other.i_;
       other.i_ = 0;
@@ -62,12 +62,12 @@
 
   explicit MoveOnlyHashValue(int value = kEmpty, int id = 0)
       : value_(value), id_(id) {}
-  MoveOnlyHashValue(MoveOnlyHashValue&& other) noexcept
+  MoveOnlyHashValue(MoveOnlyHashValue&& other)
       : value_(other.value_), id_(other.id_) {
     other.value_ = kMovedOut;
     other.id_ = 0;
   }
-  MoveOnlyHashValue& operator=(MoveOnlyHashValue&& other) noexcept {
+  MoveOnlyHashValue& operator=(MoveOnlyHashValue&& other) {
     value_ = other.value_;
     id_ = other.id_;
     other.value_ = kMovedOut;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 07836b12..81f62043 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3226,6 +3226,21 @@
 crbug.com/618969 external/wpt/css/css-grid/subgrid/ [ Skip ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Win10 ] external/wpt/css/css-text/line-breaking/line-breaking-021.html [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-text/text-justify/text-justify-006.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-text/text-justify/text-justify-006.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-text/text-justify/text-justify-006.html [ Failure ]
+crbug.com/626703 [ Win7 ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html [ Timeout ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-text/white-space/seg-break-transformation-019.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-text/white-space/seg-break-transformation-019.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-text/white-space/seg-break-transformation-019.html [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-text/text-transform/text-transform-fullwidth-009.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-text/text-transform/text-transform-fullwidth-009.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-text/text-transform/text-transform-fullwidth-009.html [ Failure ]
+crbug.com/626703 [ Win7 ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html [ Timeout ]
+crbug.com/626703 [ Linux ] external/wpt/css/css-text/text-transform/text-transform-fullwidth-008.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-text/text-transform/text-transform-fullwidth-008.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-text/text-transform/text-transform-fullwidth-008.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-text/word-break/word-break-keep-all-008.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-text/word-break/word-break-keep-all-007.html [ Failure ]
 crbug.com/626703 [ Mac10.11 ] external/wpt/shape-detection/idlharness.https.any.sharedworker.html [ Failure Timeout ]
@@ -6207,3 +6222,6 @@
 # Sheriff 2019-09-26
 crbug.com/1008257 external/wpt/service-workers/service-worker/claim-shared-worker-fetch.https.html [ Pass Crash ]
 crbug.com/1008257 external/wpt/service-workers/service-worker/worker-interception-redirect.https.html [ Pass Crash ]
+
+# Sheriff 2019-09-27
+crbug.com/1008826 [ Mac10.13 ] http/tests/security/frameNavigation/xss-ALLOWED-parent-navigation-change.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt b/third_party/blink/web_tests/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt
index 0162eb6..dd344048 100644
--- a/third_party/blink/web_tests/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt
+++ b/third_party/blink/web_tests/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt
@@ -31,51 +31,46 @@
       "drawsContent": false
     },
     {
-      "name": "Child Transform Layer",
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutNGBlockFlow DIV id='camera' class='rotate-3d-start'",
       "contentsOpaque": true,
       "drawsContent": false,
-      "transform": 3
+      "transform": 2
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-1'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 4
+      "transform": 3
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-2'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 5
+      "transform": 4
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-3'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 6
+      "transform": 5
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-4'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 7
+      "transform": 6
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-5'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 8
+      "transform": 7
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-6'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 9
+      "transform": 8
     }
   ],
   "transforms": [
@@ -92,9 +87,9 @@
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.005],
+        [0.353553390593274, 0.25, -0.5, 0],
+        [0, 0.353553390593274, 0.707106781186548, 0],
+        [0.353553390593274, -0.25, 0.5, 0],
         [0, 0, 0, 1]
       ],
       "origin": [50, 50]
@@ -103,18 +98,6 @@
       "id": 3,
       "parent": 2,
       "transform": [
-        [0.353553390593274, 0.25, -0.5, 0],
-        [0, 0.353553390593274, 0.707106781186548, 0],
-        [0.353553390593274, -0.25, 0.5, 0],
-        [0, 0, 0, 1]
-      ],
-      "origin": [50, 50],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
@@ -123,8 +106,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 5,
-      "parent": 3,
+      "id": 4,
+      "parent": 2,
       "transform": [
         [0, 0, -1, 0],
         [0, 1, 0, 0],
@@ -135,8 +118,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 3,
+      "id": 5,
+      "parent": 2,
       "transform": [
         [-1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -147,8 +130,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 7,
-      "parent": 3,
+      "id": 6,
+      "parent": 2,
       "transform": [
         [0, 0, 1, 0],
         [0, 1, 0, 0],
@@ -159,8 +142,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 8,
-      "parent": 3,
+      "id": 7,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, 1, 0],
@@ -171,8 +154,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 9,
-      "parent": 3,
+      "id": 8,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, -1, 0],
@@ -212,51 +195,46 @@
       "drawsContent": false
     },
     {
-      "name": "Child Transform Layer",
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutNGBlockFlow DIV id='camera' class='rotate-3d-start rotate-3d-end'",
       "contentsOpaque": true,
       "drawsContent": false,
-      "transform": 3
+      "transform": 2
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-1'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 4
+      "transform": 3
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-2'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 5
+      "transform": 4
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-3'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 6
+      "transform": 5
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-4'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 7
+      "transform": 6
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-5'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 8
+      "transform": 7
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='side side-6'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 9
+      "transform": 8
     },
     {
       "name": "Squashing Containment Layer",
@@ -289,9 +267,9 @@
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.005],
+        [0.707106781186548, 0.5, -0.5, 0],
+        [0, 0.707106781186548, 0.707106781186548, 0],
+        [0.707106781186548, -0.5, 0.5, 0],
         [0, 0, 0, 1]
       ],
       "origin": [50, 50]
@@ -300,18 +278,6 @@
       "id": 3,
       "parent": 2,
       "transform": [
-        [0.707106781186548, 0.5, -0.5, 0],
-        [0, 0.707106781186548, 0.707106781186548, 0],
-        [0.707106781186548, -0.5, 0.5, 0],
-        [0, 0, 0, 1]
-      ],
-      "origin": [50, 50],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
@@ -320,8 +286,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 5,
-      "parent": 3,
+      "id": 4,
+      "parent": 2,
       "transform": [
         [0, 0, -1, 0],
         [0, 1, 0, 0],
@@ -332,8 +298,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 3,
+      "id": 5,
+      "parent": 2,
       "transform": [
         [-1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -344,8 +310,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 7,
-      "parent": 3,
+      "id": 6,
+      "parent": 2,
       "transform": [
         [0, 0, 1, 0],
         [0, 1, 0, 0],
@@ -356,8 +322,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 8,
-      "parent": 3,
+      "id": 7,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, 1, 0],
@@ -368,8 +334,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 9,
-      "parent": 3,
+      "id": 8,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, -1, 0],
diff --git a/third_party/blink/web_tests/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective-expected.txt b/third_party/blink/web_tests/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective-expected.txt
index 871971a..ec0fe1d 100644
--- a/third_party/blink/web_tests/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective-expected.txt
+++ b/third_party/blink/web_tests/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective-expected.txt
@@ -18,41 +18,35 @@
       "backgroundColor": "#FFFFFF"
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV class='container'",
+      "name": "LayoutNGBlockFlow (positioned) DIV class='container'",
       "position": [8, 8],
       "bounds": [200, 200]
     },
     {
-      "name": "Child Transform Layer",
-      "bounds": [200, 200],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "Scrolling Layer",
+      "position": [8, 8],
       "bounds": [185, 185],
-      "drawsContent": false,
-      "transform": 2
+      "drawsContent": false
     },
     {
       "name": "Scrolling Contents Layer",
+      "position": [8, 8],
       "bounds": [185, 290],
-      "drawsContent": false,
-      "transform": 2
+      "drawsContent": false
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV class='child first'",
+      "name": "LayoutNGBlockFlow (positioned) DIV class='child first'",
       "bounds": [60, 200],
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 4
+      "transform": 2
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV class='child second'",
+      "name": "LayoutNGBlockFlow (positioned) DIV class='child second'",
       "bounds": [60, 200],
       "contentsOpaque": true,
       "backgroundColor": "#0000FF",
-      "transform": 6
+      "transform": 4
     },
     {
       "name": "Overflow Controls Host Layer",
@@ -85,9 +79,8 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [8, 8, 0, 1]
-      ],
-      "flattenInheritedTransform": false
+        [8, 73, 0, 1]
+      ]
     },
     {
       "id": 2,
@@ -95,22 +88,18 @@
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
-        [0, 0, 1, -0.01],
-        [0, 0, 0, 1]
-      ],
-      "origin": [100, 100],
-      "flattenInheritedTransform": false
+        [0, 0, 1, 0],
+        [0, 0, 10, 1]
+      ]
     },
     {
       "id": 3,
-      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [0, 65, 0, 1]
-      ],
-      "flattenInheritedTransform": false
+        [73, 73, 0, 1]
+      ]
     },
     {
       "id": 4,
@@ -119,31 +108,8 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [0, 0, 10, 1]
-      ],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 5,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [65, 65, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 6,
-      "parent": 5,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
         [0, 0, 20, 1]
-      ],
-      "flattenInheritedTransform": false
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/compositing/squashing/squashing-inside-perspective-expected.txt b/third_party/blink/web_tests/compositing/squashing/squashing-inside-perspective-expected.txt
index e7ecd61..33ea495 100644
--- a/third_party/blink/web_tests/compositing/squashing/squashing-inside-perspective-expected.txt
+++ b/third_party/blink/web_tests/compositing/squashing/squashing-inside-perspective-expected.txt
@@ -18,22 +18,17 @@
       "backgroundColor": "#FFFFFF"
     },
     {
-      "name": "LayoutBlockFlow (positioned) DIV",
+      "name": "LayoutNGBlockFlow (positioned) DIV",
       "position": [8, 8],
       "contentsOpaque": true,
       "drawsContent": false
     },
     {
-      "name": "Child Transform Layer",
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
-      "name": "LayoutBlockFlow (positioned) DIV",
+      "name": "LayoutNGBlockFlow (positioned) DIV",
       "bounds": [200, 200],
       "contentsOpaque": true,
       "backgroundColor": "#00008B",
-      "transform": 3
+      "transform": 2
     }
   ],
   "transforms": [
@@ -52,21 +47,9 @@
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
-        [0, 0, 1, -0.001],
-        [0, 0, 0, 1]
-      ],
-      "origin": [0, 0]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
         [0, 0, 1, 0],
         [0, 74, 200, 1]
-      ],
-      "flattenInheritedTransform": false
+      ]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 7fa5856..564f6b12 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -69843,6 +69843,18 @@
      {}
     ]
    ],
+   "css/css-text/line-breaking/line-breaking-021.html": [
+    [
+     "css/css-text/line-breaking/line-breaking-021.html",
+     [
+      [
+       "/css/css-text/line-breaking/reference/line-breaking-021-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/line-breaking/line-breaking-atomic-001.html": [
     [
      "css/css-text/line-breaking/line-breaking-atomic-001.html",
@@ -71127,6 +71139,18 @@
      {}
     ]
    ],
+   "css/css-text/text-align/text-align-last-wins-001.html": [
+    [
+     "css/css-text/text-align/text-align-last-wins-001.html",
+     [
+      [
+       "/css/css-text/text-align/reference/text-align-last-wins-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/text-align/text-align-start-001.html": [
     [
      "css/css-text/text-align/text-align-start-001.html",
@@ -71475,6 +71499,18 @@
      {}
     ]
    ],
+   "css/css-text/text-justify/text-justify-006.html": [
+    [
+     "css/css-text/text-justify/text-justify-006.html",
+     [
+      [
+       "/css/css-text/text-justify/reference/text-justify-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/text-transform/math/text-transform-math-auto-001.tentative.html": [
     [
      "css/css-text/text-transform/math/text-transform-math-auto-001.tentative.html",
@@ -72039,6 +72075,30 @@
      {}
     ]
    ],
+   "css/css-text/text-transform/text-transform-fullwidth-008.html": [
+    [
+     "css/css-text/text-transform/text-transform-fullwidth-008.html",
+     [
+      [
+       "/css/css-text/text-transform/reference/text-transform-fullwidth-008-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/text-transform/text-transform-fullwidth-009.html": [
+    [
+     "css/css-text/text-transform/text-transform-fullwidth-009.html",
+     [
+      [
+       "/css/css-text/text-transform/reference/text-transform-fullwidth-009-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/text-transform/text-transform-lowercase-001.xht": [
     [
      "css/css-text/text-transform/text-transform-lowercase-001.xht",
@@ -74355,6 +74415,30 @@
      {}
     ]
    ],
+   "css/css-text/white-space/seg-break-transformation-018.html": [
+    [
+     "css/css-text/white-space/seg-break-transformation-018.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/white-space/seg-break-transformation-019.html": [
+    [
+     "css/css-text/white-space/seg-break-transformation-019.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/white-space/tab-stop-threshold-001.html": [
     [
      "css/css-text/white-space/tab-stop-threshold-001.html",
@@ -115885,6 +115969,18 @@
      {}
     ]
    ],
+   "mathml/presentation-markup/scripts/underover-legacy-align-attribute-001.html": [
+    [
+     "mathml/presentation-markup/scripts/underover-legacy-align-attribute-001.html",
+     [
+      [
+       "/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "mathml/presentation-markup/spaces/mspace-children.html": [
     [
      "mathml/presentation-markup/spaces/mspace-children.html",
@@ -122683,6 +122779,9 @@
    ]
   },
   "support": {
+   ".github/META.yml": [
+    []
+   ],
    ".github/workflows/pull_request.yml": [
     []
    ],
@@ -141994,6 +142093,9 @@
    "css/css-text/line-breaking/reference/line-breaking-019-ref.html": [
     []
    ],
+   "css/css-text/line-breaking/reference/line-breaking-021-ref.html": [
+    []
+   ],
    "css/css-text/line-breaking/reference/line-breaking-atomic-003-ref.html": [
     []
    ],
@@ -142402,6 +142504,9 @@
    "css/css-text/text-align/reference/text-align-justifyall-ref-006.html": [
     []
    ],
+   "css/css-text/text-align/reference/text-align-last-wins-001-ref.html": [
+    []
+   ],
    "css/css-text/text-align/reference/text-align-start-ref-001.html": [
     []
    ],
@@ -142477,6 +142582,9 @@
    "css/css-text/text-indent/reference/text-indent-tab-positions-001-ref.html": [
     []
    ],
+   "css/css-text/text-justify/reference/text-justify-006-ref.html": [
+    []
+   ],
    "css/css-text/text-justify/reference/text-justify-ref-001.html": [
     []
    ],
@@ -142621,6 +142729,12 @@
    "css/css-text/text-transform/reference/text-transform-fullwidth-007-ref.html": [
     []
    ],
+   "css/css-text/text-transform/reference/text-transform-fullwidth-008-ref.html": [
+    []
+   ],
+   "css/css-text/text-transform/reference/text-transform-fullwidth-009-ref.html": [
+    []
+   ],
    "css/css-text/text-transform/reference/text-transform-lowercase-001-ref.xht": [
     []
    ],
@@ -145369,9 +145483,18 @@
    "css/css-values/minmax-angle-serialize-expected.txt": [
     []
    ],
+   "css/css-values/minmax-length-percent-serialize-expected.txt": [
+    []
+   ],
    "css/css-values/minmax-length-serialize-expected.txt": [
     []
    ],
+   "css/css-values/minmax-number-serialize-expected.txt": [
+    []
+   ],
+   "css/css-values/minmax-percentage-serialize-expected.txt": [
+    []
+   ],
    "css/css-values/minmax-time-serialize-expected.txt": [
     []
    ],
@@ -158140,6 +158263,27 @@
    "html/interaction/focus/sequential-focus-navigation-and-the-tabindex-attribute/tabindex-getter-expected.txt": [
     []
    ],
+   "html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html": [
+    []
+   ],
+   "html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html": [
+    []
+   ],
+   "html/interaction/focus/the-autofocus-attribute/resources/erase-first.css": [
+    []
+   ],
+   "html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html": [
+    []
+   ],
+   "html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html": [
+    []
+   ],
+   "html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html": [
+    []
+   ],
+   "html/interaction/focus/the-autofocus-attribute/resources/utils.js": [
+    []
+   ],
    "html/obsolete/META.yml": [
     []
    ],
@@ -159511,27 +159655,6 @@
    "html/semantics/forms/attributes-common-to-form-controls/dirname-ltr-iframe.html": [
     []
    ],
-   "html/semantics/forms/autofocus/resources/child-autofocus.html": [
-    []
-   ],
-   "html/semantics/forms/autofocus/resources/child-iframe.html": [
-    []
-   ],
-   "html/semantics/forms/autofocus/resources/erase-first.css": [
-    []
-   ],
-   "html/semantics/forms/autofocus/resources/frame-with-autofocus-element.html": [
-    []
-   ],
-   "html/semantics/forms/autofocus/resources/grand-child-autofocus.html": [
-    []
-   ],
-   "html/semantics/forms/autofocus/resources/moving-autofocus-to-parent.html": [
-    []
-   ],
-   "html/semantics/forms/autofocus/resources/utils.js": [
-    []
-   ],
    "html/semantics/forms/constraints/form-validation-validity-valueMissing-expected.txt": [
     []
    ],
@@ -162439,6 +162562,9 @@
    "mathml/presentation-markup/radicals/radical-rendering-from-in-flow-ref.html": [
     []
    ],
+   "mathml/presentation-markup/scripts/underover-legacy-align-attribute-001-ref.html": [
+    []
+   ],
    "mathml/presentation-markup/spaces/mspace-children-ref.html": [
     []
    ],
@@ -216741,6 +216867,12 @@
      {}
     ]
    ],
+   "css/css-values/minmax-length-percent-serialize.html": [
+    [
+     "css/css-values/minmax-length-percent-serialize.html",
+     {}
+    ]
+   ],
    "css/css-values/minmax-length-serialize.html": [
     [
      "css/css-values/minmax-length-serialize.html",
@@ -216759,6 +216891,12 @@
      {}
     ]
    ],
+   "css/css-values/minmax-number-serialize.html": [
+    [
+     "css/css-values/minmax-number-serialize.html",
+     {}
+    ]
+   ],
    "css/css-values/minmax-percentage-computed.html": [
     [
      "css/css-values/minmax-percentage-computed.html",
@@ -216771,6 +216909,12 @@
      {}
     ]
    ],
+   "css/css-values/minmax-percentage-serialize.html": [
+    [
+     "css/css-values/minmax-percentage-serialize.html",
+     {}
+    ]
+   ],
    "css/css-values/minmax-time-computed.html": [
     [
      "css/css-values/minmax-time-computed.html",
@@ -241575,6 +241719,124 @@
      {}
     ]
    ],
+   "html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/first-reconnected.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/first-reconnected.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/first-when-later.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/first-when-later.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/first.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/first.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/not-on-first-task.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/not-on-first-task.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html",
+     {
+      "testdriver": true
+     }
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/skip-document-with-fragment.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/skip-document-with-fragment.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/supported-elements.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/supported-elements.html",
+     {}
+    ]
+   ],
+   "html/interaction/focus/the-autofocus-attribute/update-the-rendering.html": [
+    [
+     "html/interaction/focus/the-autofocus-attribute/update-the-rendering.html",
+     {}
+    ]
+   ],
    "html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html": [
     [
      "html/obsolete/requirements-for-implementations/other-elements-attributes-and-apis/document-all.html",
@@ -245455,124 +245717,6 @@
      {}
     ]
    ],
-   "html/semantics/forms/autofocus/autofocus-in-not-fully-active-document.html": [
-    [
-     "html/semantics/forms/autofocus/autofocus-in-not-fully-active-document.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/autofocus-on-stable-document.html": [
-    [
-     "html/semantics/forms/autofocus/autofocus-on-stable-document.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/first-reconnected.html": [
-    [
-     "html/semantics/forms/autofocus/first-reconnected.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/first-when-later-but-before.html": [
-    [
-     "html/semantics/forms/autofocus/first-when-later-but-before.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/first-when-later.html": [
-    [
-     "html/semantics/forms/autofocus/first-when-later.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/first.html": [
-    [
-     "html/semantics/forms/autofocus/first.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/focusable-area-in-top-document.html": [
-    [
-     "html/semantics/forms/autofocus/focusable-area-in-top-document.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/no-cross-origin-autofocus.html": [
-    [
-     "html/semantics/forms/autofocus/no-cross-origin-autofocus.html",
-     {
-      "testdriver": true
-     }
-    ]
-   ],
-   "html/semantics/forms/autofocus/no-sandboxed-automatic-features.html": [
-    [
-     "html/semantics/forms/autofocus/no-sandboxed-automatic-features.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/not-on-first-task.html": [
-    [
-     "html/semantics/forms/autofocus/not-on-first-task.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/queue-non-focusable.html": [
-    [
-     "html/semantics/forms/autofocus/queue-non-focusable.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/same-origin-autofocus.html": [
-    [
-     "html/semantics/forms/autofocus/same-origin-autofocus.html",
-     {
-      "testdriver": true
-     }
-    ]
-   ],
-   "html/semantics/forms/autofocus/skip-another-top-level-browsing-context.html": [
-    [
-     "html/semantics/forms/autofocus/skip-another-top-level-browsing-context.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/skip-document-with-fragment.html": [
-    [
-     "html/semantics/forms/autofocus/skip-document-with-fragment.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/skip-non-focusable.html": [
-    [
-     "html/semantics/forms/autofocus/skip-non-focusable.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/skip-not-fully-active.html": [
-    [
-     "html/semantics/forms/autofocus/skip-not-fully-active.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/spin-by-blocking-style-sheet.html": [
-    [
-     "html/semantics/forms/autofocus/spin-by-blocking-style-sheet.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/supported-elements.html": [
-    [
-     "html/semantics/forms/autofocus/supported-elements.html",
-     {}
-    ]
-   ],
-   "html/semantics/forms/autofocus/update-the-rendering.html": [
-    [
-     "html/semantics/forms/autofocus/update-the-rendering.html",
-     {}
-    ]
-   ],
    "html/semantics/forms/constraints/form-validation-checkValidity.html": [
     [
      "html/semantics/forms/constraints/form-validation-checkValidity.html",
@@ -320589,6 +320733,10 @@
   }
  },
  "paths": {
+  ".github/META.yml": [
+   "06083d1cd0ec0085ec9b4acbab4baad9aa133a33",
+   "support"
+  ],
   ".github/workflows/pull_request.yml": [
    "81a53c67f4b11ca3c7ddde916dcebb35cf83a021",
    "support"
@@ -380114,7 +380262,7 @@
    "testharness"
   ],
   "css/css-properties-values-api/idlharness-expected.txt": [
-   "36819f86bfb7c04e8440d389c5ecf8530b5538c2",
+   "20e8217b395a881e61296f15be372ad23b983e7b",
    "support"
   ],
   "css/css-properties-values-api/idlharness.html": [
@@ -388365,6 +388513,10 @@
    "7c8d0f119edb5ef768b37a5a41b8df9bb9e59600",
    "testharness"
   ],
+  "css/css-text/line-breaking/line-breaking-021.html": [
+   "b3b907410e82d87ac3912e0f27cfc1dbea17d3a9",
+   "reftest"
+  ],
   "css/css-text/line-breaking/line-breaking-atomic-001.html": [
    "e071378c95fd436484a3056ecb2ba6ff35ddbaae",
    "reftest"
@@ -388461,6 +388613,10 @@
    "4a7772aa290f5ba72b3f9c604e9935438269e6f9",
    "support"
   ],
+  "css/css-text/line-breaking/reference/line-breaking-021-ref.html": [
+   "b1cc22bd2049f66a86f61bd8bf8f6c8a6ec26258",
+   "support"
+  ],
   "css/css-text/line-breaking/reference/line-breaking-atomic-003-ref.html": [
    "a9fdd2591b3f3af3d2a9accfd8c65db028f87749",
    "support"
@@ -389549,6 +389705,10 @@
    "4496c3866ee10cee2da3b716203ec46d6730a6c0",
    "support"
   ],
+  "css/css-text/text-align/reference/text-align-last-wins-001-ref.html": [
+   "997d58a79884ac3086426da2a84b992019bfd100",
+   "support"
+  ],
   "css/css-text/text-align/reference/text-align-start-ref-001.html": [
    "72e11fae2dd1eccb5d9742970a401db54b72dfc0",
    "support"
@@ -389789,6 +389949,10 @@
    "fd3d706d1ee4acd3fda03cd46c12f20dfb69b9b9",
    "testharness"
   ],
+  "css/css-text/text-align/text-align-last-wins-001.html": [
+   "30f1a07864b073b1d11953be867f17c0e6ef129d",
+   "reftest"
+  ],
   "css/css-text/text-align/text-align-start-001.html": [
    "43219cc248fcca0a20cacae5993860b7e4cd292e",
    "reftest"
@@ -389953,6 +390117,10 @@
    "5bf766ce7607bd21892a558f67901a564fc64994",
    "reftest"
   ],
+  "css/css-text/text-justify/reference/text-justify-006-ref.html": [
+   "595485c5f4ea346b011f07128649da3f2c020902",
+   "support"
+  ],
   "css/css-text/text-justify/reference/text-justify-ref-001.html": [
    "976df7f4dcee2efbb4e232cbd3a86ec4d7ac8943",
    "support"
@@ -389977,6 +390145,10 @@
    "afbc036cce43268a4764e3dea75190a0ed44be3c",
    "visual"
   ],
+  "css/css-text/text-justify/text-justify-006.html": [
+   "7744f52afea63d9b1abe7c7edab4880d703be855",
+   "reftest"
+  ],
   "css/css-text/text-transform/math/text-transform-math-auto-001.tentative-ref.html": [
    "d41d618769e85a581ceada90e020d6fc979ad7d5",
    "support"
@@ -390241,6 +390413,14 @@
    "b8fc5662bd83d9f29aabadbd4eb973e91621f1ed",
    "support"
   ],
+  "css/css-text/text-transform/reference/text-transform-fullwidth-008-ref.html": [
+   "efe6508e2e61dddd9a132891e4fdd8a3e155067d",
+   "support"
+  ],
+  "css/css-text/text-transform/reference/text-transform-fullwidth-009-ref.html": [
+   "915e8444f1335e920073e2c8091281241370f830",
+   "support"
+  ],
   "css/css-text/text-transform/reference/text-transform-lowercase-001-ref.xht": [
    "3d6eb4af8ac5aeb7fd54e1b2e2aec325886ddca0",
    "support"
@@ -390597,6 +390777,14 @@
    "f1089f19ab67a4c34bea126a9dec4bc23e7de0fe",
    "reftest"
   ],
+  "css/css-text/text-transform/text-transform-fullwidth-008.html": [
+   "d6cd9c4c9833973d2fcfa2bfedc74a64b1872ec8",
+   "reftest"
+  ],
+  "css/css-text/text-transform/text-transform-fullwidth-009.html": [
+   "afcb89b0cfcc6db2ca3517b1f99e91abdca321fa",
+   "reftest"
+  ],
   "css/css-text/text-transform/text-transform-lowercase-001.xht": [
    "dc3fadf64caf5786123250da2cc50187ec693d2b",
    "reftest"
@@ -391693,6 +391881,14 @@
    "52b7ce1f6ab15ac1833067cdf957b1e4c6b4af21",
    "testharness"
   ],
+  "css/css-text/white-space/seg-break-transformation-018.html": [
+   "2faf185856baf75b4820b57cd9b4ffe4161f7a29",
+   "reftest"
+  ],
+  "css/css-text/white-space/seg-break-transformation-019.html": [
+   "afbba2642524209eda0378577305b5c0e48d1232",
+   "reftest"
+  ],
   "css/css-text/white-space/tab-stop-threshold-001.html": [
    "dae6012bf7f42bfa4154f2c88c439db6959e80cb",
    "reftest"
@@ -402437,6 +402633,14 @@
    "ee086ef269d07bf6b0db5d1306a0f24af0ad8fab",
    "testharness"
   ],
+  "css/css-values/minmax-length-percent-serialize-expected.txt": [
+   "4431554f9d0c19079e4297c888639adae6715cf5",
+   "support"
+  ],
+  "css/css-values/minmax-length-percent-serialize.html": [
+   "f0ffd4ea2ce2e3ae3246793cc3191ccb290d0697",
+   "testharness"
+  ],
   "css/css-values/minmax-length-serialize-expected.txt": [
    "7859bd08aa09515ccc30515a574bd9c3c35fe21a",
    "support"
@@ -402453,6 +402657,14 @@
    "3f34fde2f23732ed1b9bc540a098e5914ad674bf",
    "testharness"
   ],
+  "css/css-values/minmax-number-serialize-expected.txt": [
+   "d09f724749ab3809edf085d5865cc5e7954e055b",
+   "support"
+  ],
+  "css/css-values/minmax-number-serialize.html": [
+   "e05ccc339c90a1c8df22d6b9f46ff7c357dc36af",
+   "testharness"
+  ],
   "css/css-values/minmax-percentage-computed.html": [
    "9f9d0a59d12c2642bfea8ca5a3e2f8528067c80e",
    "testharness"
@@ -402461,6 +402673,14 @@
    "48d2cdabec1bee5cde0d5d6f4c3524f463916db1",
    "testharness"
   ],
+  "css/css-values/minmax-percentage-serialize-expected.txt": [
+   "1d7b870dbbc3b44fffceec1d66481852cd2870c3",
+   "support"
+  ],
+  "css/css-values/minmax-percentage-serialize.html": [
+   "79624be529e5ec91b847a995d497e515736f66cc",
+   "testharness"
+  ],
   "css/css-values/minmax-time-computed.html": [
    "36bcf601eb808311732562a6c1c6a7c801f33e9d",
    "testharness"
@@ -438937,6 +439157,110 @@
    "e40bc077594351019591de8cbfae8fb0d6af13fe",
    "testharness"
   ],
+  "html/interaction/focus/the-autofocus-attribute/autofocus-in-not-fully-active-document.html": [
+   "a26a44dbfb6e8c3eb81c7b216f9aa1c0da8bb9ed",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/autofocus-on-stable-document.html": [
+   "47e3e3fd0abdc93e8447c099314935f8cdc31c42",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/first-reconnected.html": [
+   "99ee9198d1b0a39605ee7115ba71b1178e815943",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/first-when-later-but-before.html": [
+   "f361463401b555f4c90cc25f195dd8b6c7e03b0b",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/first-when-later.html": [
+   "1d64b863a16e8a0745c4f207d5a92e80175ee34f",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/first.html": [
+   "02ebb79a3e9dff0f1b0211eb187b7a91f7de8c17",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/focusable-area-in-top-document.html": [
+   "327040eeeeb7da9bf134cb7120b60c7d1e76d5c7",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/no-cross-origin-autofocus.html": [
+   "2cf7428f36cb30b3831b58744ad4c322fee0bdec",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/no-sandboxed-automatic-features.html": [
+   "991373d3363a195bde7c7e4e9ad255026e173e47",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/not-on-first-task.html": [
+   "50efc176935c710f43a42c31ecc0b7676e96b833",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/queue-non-focusable.html": [
+   "e3b556035d35f6ea18d51cd3772651a22940d161",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/resources/child-autofocus.html": [
+   "afd5601a523ff0a1d60d37b171b2098a38600ace",
+   "support"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/resources/child-iframe.html": [
+   "f60acfc8710192f72813738f0f67e4f512f8163e",
+   "support"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/resources/erase-first.css": [
+   "bbbcf799393fc047dae6d47836c3696868df8fb7",
+   "support"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/resources/frame-with-autofocus-element.html": [
+   "985cba41494919525031081d236e4409aace453c",
+   "support"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/resources/grand-child-autofocus.html": [
+   "88be6e0b04a99b8477925107e1f534024f021b5e",
+   "support"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/resources/moving-autofocus-to-parent.html": [
+   "fc6c298a46e376cf45089f168cf841cab59ffd5c",
+   "support"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/resources/utils.js": [
+   "0eeb5a9f0adf1d09959227241cd71fe32ebb485c",
+   "support"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/same-origin-autofocus.html": [
+   "6e67aa6c0d39aa8e5ffe3fc513e04f9edd683f45",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html": [
+   "d392b903f075276a03189207e57d789608523de1",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/skip-document-with-fragment.html": [
+   "a4301e13516634e9fc09d1b8084091a2697b1f24",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/skip-non-focusable.html": [
+   "008371d8e163fbdec937e29435fe61dffd520cde",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/skip-not-fully-active.html": [
+   "fa5b608d050de2b59e4f55bf9490d503844b1bcc",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/spin-by-blocking-style-sheet.html": [
+   "22a4c3573cccf909d3a5675db2aab97a4f366bc0",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/supported-elements.html": [
+   "761936715a3060ba4c6cca1068612c21c465ea02",
+   "testharness"
+  ],
+  "html/interaction/focus/the-autofocus-attribute/update-the-rendering.html": [
+   "afaf0926f5b55e4f1925010c7262ed26a616d3be",
+   "testharness"
+  ],
   "html/obsolete/META.yml": [
    "c1dd8dddf9eec3ab3fb58df01c549c251f3a3fdf",
    "support"
@@ -444017,110 +444341,6 @@
    "82798eaa84f533cdc675c653ef22fcb12b52137e",
    "testharness"
   ],
-  "html/semantics/forms/autofocus/autofocus-in-not-fully-active-document.html": [
-   "a26a44dbfb6e8c3eb81c7b216f9aa1c0da8bb9ed",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/autofocus-on-stable-document.html": [
-   "47e3e3fd0abdc93e8447c099314935f8cdc31c42",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/first-reconnected.html": [
-   "99ee9198d1b0a39605ee7115ba71b1178e815943",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/first-when-later-but-before.html": [
-   "f361463401b555f4c90cc25f195dd8b6c7e03b0b",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/first-when-later.html": [
-   "1d64b863a16e8a0745c4f207d5a92e80175ee34f",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/first.html": [
-   "02ebb79a3e9dff0f1b0211eb187b7a91f7de8c17",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/focusable-area-in-top-document.html": [
-   "327040eeeeb7da9bf134cb7120b60c7d1e76d5c7",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/no-cross-origin-autofocus.html": [
-   "c3974bd02a3655cef5513258e81f65e2f86e07d3",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/no-sandboxed-automatic-features.html": [
-   "991373d3363a195bde7c7e4e9ad255026e173e47",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/not-on-first-task.html": [
-   "50efc176935c710f43a42c31ecc0b7676e96b833",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/queue-non-focusable.html": [
-   "e3b556035d35f6ea18d51cd3772651a22940d161",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/resources/child-autofocus.html": [
-   "afd5601a523ff0a1d60d37b171b2098a38600ace",
-   "support"
-  ],
-  "html/semantics/forms/autofocus/resources/child-iframe.html": [
-   "2645a180e4866ed9d1f0a107fc912d53b7437247",
-   "support"
-  ],
-  "html/semantics/forms/autofocus/resources/erase-first.css": [
-   "bbbcf799393fc047dae6d47836c3696868df8fb7",
-   "support"
-  ],
-  "html/semantics/forms/autofocus/resources/frame-with-autofocus-element.html": [
-   "985cba41494919525031081d236e4409aace453c",
-   "support"
-  ],
-  "html/semantics/forms/autofocus/resources/grand-child-autofocus.html": [
-   "88be6e0b04a99b8477925107e1f534024f021b5e",
-   "support"
-  ],
-  "html/semantics/forms/autofocus/resources/moving-autofocus-to-parent.html": [
-   "fc6c298a46e376cf45089f168cf841cab59ffd5c",
-   "support"
-  ],
-  "html/semantics/forms/autofocus/resources/utils.js": [
-   "0eeb5a9f0adf1d09959227241cd71fe32ebb485c",
-   "support"
-  ],
-  "html/semantics/forms/autofocus/same-origin-autofocus.html": [
-   "9cfcdb925cf16cc883ac37f126dc40673066a8b0",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/skip-another-top-level-browsing-context.html": [
-   "d392b903f075276a03189207e57d789608523de1",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/skip-document-with-fragment.html": [
-   "a4301e13516634e9fc09d1b8084091a2697b1f24",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/skip-non-focusable.html": [
-   "008371d8e163fbdec937e29435fe61dffd520cde",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/skip-not-fully-active.html": [
-   "fa5b608d050de2b59e4f55bf9490d503844b1bcc",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/spin-by-blocking-style-sheet.html": [
-   "22a4c3573cccf909d3a5675db2aab97a4f366bc0",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/supported-elements.html": [
-   "761936715a3060ba4c6cca1068612c21c465ea02",
-   "testharness"
-  ],
-  "html/semantics/forms/autofocus/update-the-rendering.html": [
-   "afaf0926f5b55e4f1925010c7262ed26a616d3be",
-   "testharness"
-  ],
   "html/semantics/forms/constraints/form-validation-checkValidity.html": [
    "2e790c75d82dec2f60f64199ea4e1cba1164bc63",
    "testharness"
@@ -451962,7 +452182,7 @@
    "support"
   ],
   "interfaces/css-properties-values-api.idl": [
-   "d8f54b1e15bc020ef101ab53626eee6985c63dcb",
+   "ee444ebb29d8b5b15c96d259bb8a1f2bdd280d5f",
    "support"
   ],
   "interfaces/css-pseudo.idl": [
@@ -453717,6 +453937,14 @@
    "5b718707de9ab316c6abb0ae7bf26667af191447",
    "testharness"
   ],
+  "mathml/presentation-markup/scripts/underover-legacy-align-attribute-001-ref.html": [
+   "a9545c60f400d73eae4513316d7cf731676c9b68",
+   "support"
+  ],
+  "mathml/presentation-markup/scripts/underover-legacy-align-attribute-001.html": [
+   "31920c61f4ebe7e341f1771dd7556bff0f8bbcb2",
+   "reftest"
+  ],
   "mathml/presentation-markup/scripts/underover-parameters-1.html": [
    "9217488a561735c75798e2c05197f5a84b233237",
    "testharness"
@@ -479082,7 +479310,7 @@
    "support"
   ],
   "resources/chromium/sms_mock.js": [
-   "a8cd81a5ceffc29ab1f4c6ea20d2771793f42d69",
+   "bdfbc2052fa102a0e502937a610a9c64d79d1bbe",
    "support"
   ],
   "resources/chromium/string16.mojom.js": [
@@ -487522,7 +487750,7 @@
    "support"
   ],
   "svg/struct/scripted/autofocus-attribute.svg": [
-   "d8f25741ee40cc488d504065548342c095cbc4c3",
+   "edf200c4c7be31f2e1fea39003b991695610e6c3",
    "testharness"
   ],
   "svg/struct/scripted/blank.svg": [
@@ -488226,7 +488454,7 @@
    "support"
   ],
   "tools/ci/run_tc.py": [
-   "ea4a1ac1a6ac4a9a2b2a0265e74c70dc5fdc957d",
+   "b2826bf075e9b1e82ee3d021003134930231f556",
    "support"
   ],
   "tools/ci/taskcluster-run.py": [
@@ -495142,7 +495370,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property-expected.txt": [
-   "30400b22f20c0343e93d064e90f44fe829946478",
+   "21f5bd82c5df91569054b3c3c9f8f182f8877f5f",
    "support"
   ],
   "web-animations/animation-model/animation-types/accumulation-per-property.html": [
@@ -495150,7 +495378,7 @@
    "testharness"
   ],
   "web-animations/animation-model/animation-types/addition-per-property-expected.txt": [
-   "b7d391086a37903b2bf59817c00eff3f20c20eed",
+   "d8f9003384fd8716a822d945161c17ebe86677a7",
    "support"
   ],
   "web-animations/animation-model/animation-types/addition-per-property.html": [
@@ -495174,7 +495402,7 @@
    "support"
   ],
   "web-animations/animation-model/animation-types/property-types.js": [
-   "9f549c9a199e8078612932a7b666b85c0d7439ab",
+   "75f7d61161a8569beef9b1104ce0e2c9b8e70a06",
    "support"
   ],
   "web-animations/animation-model/animation-types/visibility.html": [
diff --git a/third_party/blink/web_tests/external/wpt/.github/META.yml b/third_party/blink/web_tests/external/wpt/.github/META.yml
new file mode 100644
index 0000000..06083d1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/.github/META.yml
@@ -0,0 +1,3 @@
+suggested_reviewers:
+  - jgraham
+  - jugglinmike
diff --git a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/idlharness-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/idlharness-expected.txt
index 36819f8..20e8217b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/idlharness-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-properties-values-api/idlharness-expected.txt
@@ -3,6 +3,8 @@
 PASS idl_test validation
 PASS Partial namespace CSS: original namespace defined
 PASS Partial namespace CSS: member names are unique
+PASS Partial interface CSSRule: original interface defined
+PASS Partial interface CSSRule: member names are unique
 FAIL CSSPropertyRule interface: existence and properties of interface object assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
 FAIL CSSPropertyRule interface object length assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
 FAIL CSSPropertyRule interface object name assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
@@ -13,6 +15,8 @@
 FAIL CSSPropertyRule interface: attribute syntax assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
 FAIL CSSPropertyRule interface: attribute inherits assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
 FAIL CSSPropertyRule interface: attribute initialValue assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSRule interface: constant PROPERTY_RULE on interface object assert_own_property: expected property "PROPERTY_RULE" missing
+FAIL CSSRule interface: constant PROPERTY_RULE on interface prototype object assert_own_property: expected property "PROPERTY_RULE" missing
 PASS CSS namespace: operation escape(CSSOMString)
 PASS CSS namespace: operation registerProperty(PropertyDefinition)
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-021.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-021.html
new file mode 100644
index 0000000..b3b9074
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-021.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text Test: Simple line breaking test</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-details">
+<link rel="match" href="reference/line-breaking-021-ref.html">
+<meta name=assert content="Line breaking behavior defined for the ZWJ line-breaking classes in [UAX14] must be honored.">
+<style>
+div {
+  width: 0;
+}
+</style>
+
+<p>This test passes if there the text below is on a single line.
+
+<div>じ&#x200D;字&#x200D;자&#x200D;😂&#x200D;😭</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-021-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-021-ref.html
new file mode 100644
index 0000000..b1cc22b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-021-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>test reference</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net">
+</style>
+
+<p>This test passes if there the text below is on a single line.
+
+<div>じ&#x200D;字&#x200D;자&#x200D;😂&#x200D;😭</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-align/reference/text-align-last-wins-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/reference/text-align-last-wins-001-ref.html
new file mode 100644
index 0000000..997d58a7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/reference/text-align-last-wins-001-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html lang=en>
+<meta charset=utf-8>
+<title>test reference</title>
+<style>
+div {
+  text-align: right;
+  width: 300px;
+  border: solid;
+}
+</style>
+
+<p>Test passes if the words below are aligned to the right of the box.
+
+<div>right<br>right</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-wins-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-wins-001.html
new file mode 100644
index 0000000..30f1a07
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-align/text-align-last-wins-001.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html lang=en>
+<meta charset=utf-8>
+<title>CSS text test: text-align-last has precedence over text-align-all</title>
+<link rel=help href="https://drafts.csswg.org/css-text-3/#text-align-all-property">
+<link rel=help href="https://drafts.csswg.org/css-text-3/#text-align-last-property">
+<link rel="match" href="reference/text-align-last-wins-001-ref.html">
+<meta name=assert content="If a line is both the first and last line, text-align-last applies">
+<style>
+div {
+  text-align-last: right;
+  width: 300px;
+  border: solid;
+}
+</style>
+
+<p>Test passes if the words below are aligned to the right of the box.
+
+<div>right<br>right</div>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-justify/reference/text-justify-006-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-justify/reference/text-justify-006-ref.html
new file mode 100644
index 0000000..595485c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-justify/reference/text-justify-006-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en" >
+<head>
+<meta charset="utf-8">
+<title>test reference</title>
+<link rel='author' title='Florian Rivoal' href='https://florian.rivoal.net'>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 20px/1 Ahem;
+  white-space: pre;
+}
+#ref {
+  color: orange;
+}
+#test {
+  color: blue;
+}
+
+/* this is just filler content to have an invisible last line, as jutification does not affect the last line */
+a { color: white; }
+</style>
+
+<p>Test passes if the the blue and orange boxes are aligned.
+
+<div id=ref>X   X X   X</div>
+<div id=test>X   X X   X</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-justify/text-justify-006.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-justify/text-justify-006.html
new file mode 100644
index 0000000..7744f52
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-justify/text-justify-006.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html  lang="en" >
+<head>
+<meta charset="utf-8">
+<title>CSS text tests: text-justify applies inline</title>
+<link rel='author' title='Florian Rivoal' href='https://florian.rivoal.net'>
+<link rel='help' href='https://drafts.csswg.org/css-text-3/#text-justify-property'>
+<link rel='match' href='reference/text-justify-006-ref.html'>
+<meta name="assert" content="text-justify applies to inline elements">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 20px/1 Ahem;
+  width: 11ch;
+}
+#ref {
+  white-space: pre;
+  color: orange;
+}
+#test {
+  text-align: justify;
+  color: blue;
+}
+span {
+  text-justify: none;
+}
+
+/* this is just filler content to have an invisible last line, as jutification does not affect the last line */
+a { color: white; }
+</style>
+
+<p>Test passes if the the blue and orange boxes are aligned.
+
+<div id=ref>X   X X   X</div>
+<div id=test>X <span>X X</span> X <a>###########</a></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-fullwidth-008-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-fullwidth-008-ref.html
new file mode 100644
index 0000000..efe6508
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-fullwidth-008-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>test reference</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 20px/1 Ahem;
+  margin: 1em 0;
+  white-space: pre;
+}
+</style>
+
+<p>Test passes if all black boxes below have the same width and height and are aligned vertically.
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-fullwidth-009-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-fullwidth-009-ref.html
new file mode 100644
index 0000000..915e8444
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/reference/text-transform-fullwidth-009-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>test reference</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 10px/1 Ahem;
+  margin: 1em 0;
+  white-space: pre;
+}
+</style>
+
+<p>Test passes if all black boxes below have the same width and height and are aligned vertically.
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
+<div> x<br> x</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-008.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-008.html
new file mode 100644
index 0000000..d6cd9c4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-008.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text level 3 Test: text-transform:fullwidth and trailing spaces</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#text-transform-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="match" href="reference/text-transform-fullwidth-008-ref.html">
+<meta name="assert" content="full-width does transforms U+0020 spaces to U+3000 after phase 1, but before phase 2, so that end-of-line transformed spaces get the same treatment as natural ones: hang when white-space is normal.">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 20px/1 Ahem;
+  margin: 1em 0;
+}
+.pre {
+  white-space: pre;
+}
+#test, #ref {
+  width: 2em;
+  text-align: right;
+}
+span {
+  text-transform: full-width;
+}
+#test2, #ref2 {
+  width: min-content;
+  margin-left: 1em;
+  background: black;
+}
+</style>
+
+<p>Test passes if all black boxes below have the same width and height and are aligned vertically.
+<div class=pre> x<br> x</div>
+<div id=ref>x&#x3000;x</div>
+<div id=test>x<span> </span>x</div>
+<div id=ref2>x&#x3000;x</div>
+<div id=test2>x<span> </span>x</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-009.html b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-009.html
new file mode 100644
index 0000000..afcb89b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/text-transform/text-transform-fullwidth-009.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text level 3 Test: text-transform:fullwidth and trailing spaces, with pre-wrap</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#text-transform-property">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2">
+<link rel="match" href="reference/text-transform-fullwidth-009-ref.html">
+<meta name="assert" content="full-width does transforms U+0020 spaces to U+3000 after phase 1, but before phase 2, so that end-of-line transformed spaces get the same treatment as natural ones: hang at the end of soft-wrapped lines, and conditionally hang before forced breaks when white-space is pre-wrap.">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 10px/1 Ahem;
+  margin: 1em 0;
+}
+.pre {
+  white-space: pre;
+}
+#test, #ref,
+#test3, #ref3 {
+  width: 2em;
+  text-align: right;
+  white-space: pre-wrap;
+}
+#test3, #ref3 {
+  margin-left: 1em;
+}
+span {
+  text-transform: full-width;
+}
+#test2, #ref2,
+#test4, #ref4 {
+  width: min-content;
+  margin-left: 1em;
+  white-space: pre-wrap;
+  background: black;
+}
+</style>
+
+<p>Test passes if all black boxes below have the same width and height and are aligned vertically.
+<div class=pre> x<br> x</div>
+<div id=ref>x&#x3000;x</div>
+<div id=test>x<span> </span>x</div>
+<div id=ref2>x&#x3000;x</div>
+<div id=test2>x<span> </span>x</div>
+
+<div id=ref3>x&#x3000;<br>x&#x3000;</div>
+<div id=test3>x<span> </span><br>x<span> </span></div>
+<div id=ref4>x&#x3000;<br>x&#x3000;</div>
+<div id=test4>x<span> </span><br>x<span> </span></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/seg-break-transformation-018.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/seg-break-transformation-018.html
new file mode 100644
index 0000000..2faf185
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/seg-break-transformation-018.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text level 3 Test: inline element boundary and segment break transformations</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#text-encoding">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-transform">
+<link rel=match href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="intervening inline box boundaries must be ignored for segment break transformations">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 20px/1 Ahem;
+  color: green;
+}
+#b { border-right: solid 20px green; }
+#p { padding-right: 20px; background: green; }
+#m { margin-right: 20px; }
+#m2 { margin-right: -20px; }
+
+#red {
+  width: 100px;
+  height: 100px;
+  background: red;
+  position: absolute;
+  z-index: -1;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+<div id=red></div>
+
+<div>aa&#x200b;
+bbb</div>
+
+<div>aa<span>&#x200b;</span>
+bbb</div>
+
+<div>aa<span id=b>&#x200b;</span>
+bb</div>
+
+<div>aa<span id=p>&#x200b;</span>
+bb</div>
+
+<div>aa<span id=m>&#x200b;</span><span id=m2></span>
+bbb</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/seg-break-transformation-019.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/seg-break-transformation-019.html
new file mode 100644
index 0000000..afbba26
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/seg-break-transformation-019.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Text level 3 Test: out of flow elements and segment break transformations</title>
+<link rel="author" title="Florian Rivoal" href="https://florian.rivoal.net/">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#text-encoding">
+<link rel="help" href="https://drafts.csswg.org/css-text-3/#line-break-transform">
+<link rel=match href="../../reference/ref-filled-green-100px-square.xht">
+<meta name="assert" content="Out-of-flow elements must be ignored for segment break transformations">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+  font: 20px/1 Ahem;
+  color: green;
+}
+aside {
+  color: transparent;
+}
+#abs { position: absolute; }
+#fixed { position: fixed; }
+#float-r { float: right; }
+#float-l { float: left; margin-left: -3em; }
+#red {
+  width: 100px;
+  height: 100px;
+  background: red;
+  position: absolute;
+  z-index: -1;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+<div id=red></div>
+
+<div>aa&#x200b;
+bbb</div>
+
+<div>aa&#x200b;<aside id=abs>foo</aside>
+bbb</div>
+
+<div>aa&#x200b;<aside id=fixed>foo</aside>
+bbb</div>
+
+<div>aa&#x200b;<aside id=float-r>foo</aside>
+bbb</div>
+
+<div>aa&#x200b;<aside id=float-l>foo</aside>
+bbb</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-percent-serialize-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-percent-serialize-expected.txt
new file mode 100644
index 0000000..4431554f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-percent-serialize-expected.txt
@@ -0,0 +1,64 @@
+This is a testharness.js-based test.
+Found 60 tests; 52 PASS, 8 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS e.style['margin-left'] = "min(1px + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1cm + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1mm + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1Q + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1in + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1pc + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1pt + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1em + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1ex + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1ch + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1rem + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1vh + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1vw + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1vmin + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(1vmax + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1px + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1cm + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1mm + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1Q + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1in + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1pc + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1pt + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1em + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1ex + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1ch + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1rem + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1vh + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1vw + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1vmin + 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1vmax + 1%)" should set the property value
+PASS e.style['margin-left'] = "min(20px, 10%)" should set the property value
+PASS e.style['margin-left'] = "min(1em, 10%)" should set the property value
+PASS e.style['margin-left'] = "max(20px, 10%)" should set the property value
+PASS e.style['margin-left'] = "max(1em, 10%)" should set the property value
+PASS e.style['margin-left'] = "min(10%, 20px)" should set the property value
+PASS e.style['margin-left'] = "min(10%, 1em)" should set the property value
+PASS e.style['margin-left'] = "max(10%, 20px)" should set the property value
+PASS e.style['margin-left'] = "max(10%, 1em)" should set the property value
+PASS e.style['margin-left'] = "min(10% + 30px, 5% + 60px)" should set the property value
+PASS e.style['margin-left'] = "max(10% + 2em, 5% + 1em)" should set the property value
+PASS e.style['margin-left'] = "calc(min(10%) + max(1em) + min(20px))" should set the property value
+PASS e.style['margin-left'] = "calc(max(20px) + min(1em) + max(10%))" should set the property value
+PASS e.style['margin-left'] = "calc(max(10%) + min(1em) + max(20px))" should set the property value
+PASS e.style['margin-left'] = "calc(min(20px) + max(1em) + min(10%))" should set the property value
+PASS e.style['margin-left'] = "calc(20px + min(10%))" should set the property value
+PASS e.style['margin-left'] = "calc(10% + min(20px))" should set the property value
+PASS e.style['margin-left'] = "calc(1em + min(10%))" should set the property value
+PASS e.style['margin-left'] = "calc(10% + min(1em))" should set the property value
+FAIL e.style['margin-left'] = "calc(min(10%) + 20px)" should set the property value assert_equals: serialization should be canonical expected "calc(20px + min(10%))" but got "calc(min(10%) + 20px)"
+FAIL e.style['margin-left'] = "calc(min(20px) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(20px))" but got "calc(min(20px) + 10%)"
+FAIL e.style['margin-left'] = "calc(min(10%) + 1em)" should set the property value assert_equals: serialization should be canonical expected "calc(1em + min(10%))" but got "calc(min(10%) + 1em)"
+FAIL e.style['margin-left'] = "calc(min(1em) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + min(1em))" but got "calc(min(1em) + 10%)"
+PASS e.style['margin-left'] = "calc(20px + max(10%))" should set the property value
+PASS e.style['margin-left'] = "calc(10% + max(20px))" should set the property value
+PASS e.style['margin-left'] = "calc(1em + max(10%))" should set the property value
+PASS e.style['margin-left'] = "calc(10% + max(1em))" should set the property value
+FAIL e.style['margin-left'] = "calc(max(10%) + 20px)" should set the property value assert_equals: serialization should be canonical expected "calc(20px + max(10%))" but got "calc(max(10%) + 20px)"
+FAIL e.style['margin-left'] = "calc(max(20px) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(20px))" but got "calc(max(20px) + 10%)"
+FAIL e.style['margin-left'] = "calc(max(10%) + 1em)" should set the property value assert_equals: serialization should be canonical expected "calc(1em + max(10%))" but got "calc(max(10%) + 1em)"
+FAIL e.style['margin-left'] = "calc(max(1em) + 10%)" should set the property value assert_equals: serialization should be canonical expected "calc(10% + max(1em))" but got "calc(max(1em) + 10%)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-percent-serialize.html b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-percent-serialize.html
new file mode 100644
index 0000000..f0ffd4e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-length-percent-serialize.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#mixed-percentages">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-serialize">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+const property = 'margin-left';
+
+function test_valid_length_percent(value, expected) {
+  test_valid_value(property, value, expected);
+}
+
+test_valid_length_percent('min(1px + 1%)', 'min(1px + 1%)');
+test_valid_length_percent('min(1cm + 1%)', 'min(1cm + 1%)');
+test_valid_length_percent('min(1mm + 1%)', 'min(1mm + 1%)');
+test_valid_length_percent('min(1Q + 1%)', 'min(1q + 1%)');
+test_valid_length_percent('min(1in + 1%)', 'min(1in + 1%)');
+test_valid_length_percent('min(1pc + 1%)', 'min(1pc + 1%)');
+test_valid_length_percent('min(1pt + 1%)', 'min(1pt + 1%)');
+test_valid_length_percent('min(1em + 1%)', 'min(1em + 1%)');
+test_valid_length_percent('min(1ex + 1%)', 'min(1ex + 1%)');
+test_valid_length_percent('min(1ch + 1%)', 'min(1ch + 1%)');
+test_valid_length_percent('min(1rem + 1%)', 'min(1rem + 1%)');
+test_valid_length_percent('min(1vh + 1%)', 'min(1vh + 1%)');
+test_valid_length_percent('min(1vw + 1%)', 'min(1vw + 1%)');
+test_valid_length_percent('min(1vmin + 1%)', 'min(1vmin + 1%)');
+test_valid_length_percent('min(1vmax + 1%)', 'min(1vmax + 1%)');
+test_valid_length_percent('max(1px + 1%)', 'max(1px + 1%)');
+test_valid_length_percent('max(1cm + 1%)', 'max(1cm + 1%)');
+test_valid_length_percent('max(1mm + 1%)', 'max(1mm + 1%)');
+test_valid_length_percent('max(1Q + 1%)', 'max(1q + 1%)');
+test_valid_length_percent('max(1in + 1%)', 'max(1in + 1%)');
+test_valid_length_percent('max(1pc + 1%)', 'max(1pc + 1%)');
+test_valid_length_percent('max(1pt + 1%)', 'max(1pt + 1%)');
+test_valid_length_percent('max(1em + 1%)', 'max(1em + 1%)');
+test_valid_length_percent('max(1ex + 1%)', 'max(1ex + 1%)');
+test_valid_length_percent('max(1ch + 1%)', 'max(1ch + 1%)');
+test_valid_length_percent('max(1rem + 1%)', 'max(1rem + 1%)');
+test_valid_length_percent('max(1vh + 1%)', 'max(1vh + 1%)');
+test_valid_length_percent('max(1vw + 1%)', 'max(1vw + 1%)');
+test_valid_length_percent('max(1vmin + 1%)', 'max(1vmin + 1%)');
+test_valid_length_percent('max(1vmax + 1%)', 'max(1vmax + 1%)');
+
+test_valid_length_percent('min(20px, 10%)', 'min(20px, 10%)');
+test_valid_length_percent('min(1em, 10%)', 'min(1em, 10%)');
+test_valid_length_percent('max(20px, 10%)', 'max(20px, 10%)');
+test_valid_length_percent('max(1em, 10%)', 'max(1em, 10%)');
+test_valid_length_percent('min(10%, 20px)', 'min(10%, 20px)');
+test_valid_length_percent('min(10%, 1em)', 'min(10%, 1em)');
+test_valid_length_percent('max(10%, 20px)', 'max(10%, 20px)');
+test_valid_length_percent('max(10%, 1em)', 'max(10%, 1em)');
+
+test_valid_length_percent('min(10% + 30px, 5% + 60px)', 'min(10% + 30px, 5% + 60px)')
+test_valid_length_percent('max(10% + 2em, 5% + 1em)', 'max(10% + 2em, 5% + 1em)')
+
+test_valid_length_percent('calc(min(10%) + max(1em) + min(20px))', 'calc(min(10%) + max(1em) + min(20px))');
+test_valid_length_percent('calc(max(20px) + min(1em) + max(10%))', 'calc(max(20px) + min(1em) + max(10%))');
+test_valid_length_percent('calc(max(10%) + min(1em) + max(20px))', 'calc(max(10%) + min(1em) + max(20px))');
+test_valid_length_percent('calc(min(20px) + max(1em) + min(10%))', 'calc(min(20px) + max(1em) + min(10%))');
+
+test_valid_length_percent('calc(20px + min(10%))', 'calc(20px + min(10%))');
+test_valid_length_percent('calc(10% + min(20px))', 'calc(10% + min(20px))');
+test_valid_length_percent('calc(1em + min(10%))', 'calc(1em + min(10%))');
+test_valid_length_percent('calc(10% + min(1em))', 'calc(10% + min(1em))');
+test_valid_length_percent('calc(min(10%) + 20px)', 'calc(20px + min(10%))');
+test_valid_length_percent('calc(min(20px) + 10%)', 'calc(10% + min(20px))');
+test_valid_length_percent('calc(min(10%) + 1em)', 'calc(1em + min(10%))');
+test_valid_length_percent('calc(min(1em) + 10%)', 'calc(10% + min(1em))');
+test_valid_length_percent('calc(20px + max(10%))', 'calc(20px + max(10%))');
+test_valid_length_percent('calc(10% + max(20px))', 'calc(10% + max(20px))');
+test_valid_length_percent('calc(1em + max(10%))', 'calc(1em + max(10%))');
+test_valid_length_percent('calc(10% + max(1em))', 'calc(10% + max(1em))');
+test_valid_length_percent('calc(max(10%) + 20px)', 'calc(20px + max(10%))');
+test_valid_length_percent('calc(max(20px) + 10%)', 'calc(10% + max(20px))');
+test_valid_length_percent('calc(max(10%) + 1em)', 'calc(1em + max(10%))');
+test_valid_length_percent('calc(max(1em) + 10%)', 'calc(10% + max(1em))');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-number-serialize-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-number-serialize-expected.txt
new file mode 100644
index 0000000..d09f724
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-number-serialize-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+FAIL e.style['opacity'] = "min(1)" should set the property value assert_equals: serialization should be canonical expected "min(1)" but got "1"
+FAIL e.style['opacity'] = "max(1)" should set the property value assert_equals: serialization should be canonical expected "max(1)" but got "1"
+FAIL e.style['opacity'] = "min(1, 2, 3)" should set the property value assert_equals: serialization should be canonical expected "min(1, 2, 3)" but got "1"
+FAIL e.style['opacity'] = "min(3, 2, 1)" should set the property value assert_equals: serialization should be canonical expected "min(3, 2, 1)" but got "1"
+FAIL e.style['opacity'] = "max(1, 2, 3)" should set the property value assert_equals: serialization should be canonical expected "max(1, 2, 3)" but got "3"
+FAIL e.style['opacity'] = "max(3, 2, 1)" should set the property value assert_equals: serialization should be canonical expected "max(3, 2, 1)" but got "3"
+FAIL e.style['opacity'] = "calc(min(1) + min(2))" should set the property value assert_equals: serialization should be canonical expected "calc(min(1) + min(2))" but got "3"
+FAIL e.style['opacity'] = "calc(max(1) + max(2))" should set the property value assert_equals: serialization should be canonical expected "calc(max(1) + max(2))" but got "3"
+FAIL e.style['opacity'] = "calc(1 + min(1))" should set the property value assert_equals: serialization should be canonical expected "calc(1 + min(1))" but got "2"
+FAIL e.style['opacity'] = "calc(min(1) + 1)" should set the property value assert_equals: serialization should be canonical expected "calc(1 + min(1))" but got "2"
+FAIL e.style['opacity'] = "calc(1 + max(1))" should set the property value assert_equals: serialization should be canonical expected "calc(1 + max(1))" but got "2"
+FAIL e.style['opacity'] = "calc(max(1) + 1)" should set the property value assert_equals: serialization should be canonical expected "calc(1 + max(1))" but got "2"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-number-serialize.html b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-number-serialize.html
new file mode 100644
index 0000000..e05ccc33
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-number-serialize.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#numbers">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-serialize">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+function test_valid_number(value, expected) {
+  test_valid_value('opacity', value, expected);
+}
+
+test_valid_number('min(1)', 'min(1)');
+test_valid_number('max(1)', 'max(1)');
+
+test_valid_number('min(1, 2, 3)', 'min(1, 2, 3)');
+test_valid_number('min(3, 2, 1)', 'min(3, 2, 1)');
+test_valid_number('max(1, 2, 3)', 'max(1, 2, 3)');
+test_valid_number('max(3, 2, 1)', 'max(3, 2, 1)');
+
+test_valid_number('calc(min(1) + min(2))', 'calc(min(1) + min(2))');
+test_valid_number('calc(max(1) + max(2))', 'calc(max(1) + max(2))');
+test_valid_number('calc(1 + min(1))', 'calc(1 + min(1))');
+test_valid_number('calc(min(1) + 1)', 'calc(1 + min(1))');
+test_valid_number('calc(1 + max(1))', 'calc(1 + max(1))');
+test_valid_number('calc(max(1) + 1)', 'calc(1 + max(1))');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-percentage-serialize-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-percentage-serialize-expected.txt
new file mode 100644
index 0000000..1d7b870d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-percentage-serialize-expected.txt
@@ -0,0 +1,15 @@
+This is a testharness.js-based test.
+PASS e.style['margin-left'] = "min(1%)" should set the property value
+PASS e.style['margin-left'] = "max(1%)" should set the property value
+PASS e.style['margin-left'] = "min(1%, 2%, 3%)" should set the property value
+PASS e.style['margin-left'] = "min(3%, 2%, 1%)" should set the property value
+PASS e.style['margin-left'] = "max(1%, 2%, 3%)" should set the property value
+PASS e.style['margin-left'] = "max(3%, 2%, 1%)" should set the property value
+PASS e.style['margin-left'] = "calc(min(1%) + min(2%))" should set the property value
+PASS e.style['margin-left'] = "calc(max(1%) + max(2%))" should set the property value
+PASS e.style['margin-left'] = "calc(1% + min(1%))" should set the property value
+FAIL e.style['margin-left'] = "calc(min(1%) + 1%)" should set the property value assert_equals: serialization should be canonical expected "calc(1% + min(1%))" but got "calc(min(1%) + 1%)"
+PASS e.style['margin-left'] = "calc(1% + max(1%))" should set the property value
+FAIL e.style['margin-left'] = "calc(max(1%) + 1%)" should set the property value assert_equals: serialization should be canonical expected "calc(1% + max(1%))" but got "calc(max(1%) + 1%)"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/minmax-percentage-serialize.html b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-percentage-serialize.html
new file mode 100644
index 0000000..79624be
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/minmax-percentage-serialize.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#comp-func">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#percentages">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#calc-serialize">
+<link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+function test_valid_percentage(value, expected) {
+  test_valid_value('margin-left', value, expected);
+}
+
+test_valid_percentage('min(1%)', 'min(1%)');
+test_valid_percentage('max(1%)', 'max(1%)');
+
+test_valid_percentage('min(1%, 2%, 3%)', 'min(1%, 2%, 3%)');
+test_valid_percentage('min(3%, 2%, 1%)', 'min(3%, 2%, 1%)');
+test_valid_percentage('max(1%, 2%, 3%)', 'max(1%, 2%, 3%)');
+test_valid_percentage('max(3%, 2%, 1%)', 'max(3%, 2%, 1%)');
+
+test_valid_percentage('calc(min(1%) + min(2%))', 'calc(min(1%) + min(2%))');
+test_valid_percentage('calc(max(1%) + max(2%))', 'calc(max(1%) + max(2%))');
+test_valid_percentage('calc(1% + min(1%))', 'calc(1% + min(1%))');
+test_valid_percentage('calc(min(1%) + 1%)', 'calc(1% + min(1%))');
+test_valid_percentage('calc(1% + max(1%))', 'calc(1% + max(1%))');
+test_valid_percentage('calc(max(1%) + 1%)', 'calc(1% + max(1%))');
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/css-properties-values-api.idl b/third_party/blink/web_tests/external/wpt/interfaces/css-properties-values-api.idl
index d8f54b1e..ee444eb 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/css-properties-values-api.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/css-properties-values-api.idl
@@ -14,6 +14,10 @@
   void registerProperty(PropertyDefinition definition);
 };
 
+partial interface CSSRule {
+    const unsigned short PROPERTY_RULE = 18;
+};
+
 [Exposed=Window]
 interface CSSPropertyRule : CSSRule {
     readonly attribute CSSOMString name;
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html b/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html
index 8e5624e0..1c35fe2 100644
--- a/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html
+++ b/third_party/blink/web_tests/external/wpt/layout-instability/observe-layout-shift.html
@@ -4,6 +4,11 @@
 <body>
 <style>
 #myDiv { position: relative; width: 300px; height: 100px; }
+
+/* Disable the button's focus ring, which otherwise expands its visual rect by
+ * 1px on all sides, triggering a layout shift event.
+ */
+#button { outline: none; }
 </style>
 <div id='myDiv'></div>
 <button id='button'>Generate a 'click' event</button>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001-ref.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001-ref.html
new file mode 100644
index 0000000..a9545c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001-ref.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>munder/mover/munderover align (reference)</title>
+  </head>
+  <body>
+    <p>Test passes if the center of the following rectangles is aligned
+      on the same vertical axis.</p>
+    <p>
+      <math>
+        <munder>
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munder>
+      </math>
+    </p>
+    <p>
+      <math>
+        <munder>
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munder>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mover>
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </mover>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mover>
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </mover>
+      </math>
+    </p>
+    <p>
+      <math>
+        <munderover>
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munderover>
+      </math>
+    </p>
+    <p>
+      <math>
+        <munderover>
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munderover>
+      </math>
+    </p>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001.html
new file mode 100644
index 0000000..31920c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-legacy-align-attribute-001.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>munder/mover/munderover align</title>
+    <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#underscripts-and-overscripts-munder-mover-munderover">
+    <link rel="help" href="https://www.w3.org/TR/MathML3/chapter3.html#presm.munder">
+    <link rel="help" href="https://www.w3.org/TR/MathML3/chapter3.html#presm.mover">
+    <link rel="help" href="https://www.w3.org/TR/MathML3/chapter3.html#presm.munderover">
+    <meta name="assert" content="Check that the legacy align attribute is ignored.">
+    <link rel="match" href="underover-legacy-align-attribute-001-ref.html">
+  </head>
+  <body>
+    <p>Test passes if the center of the following rectangles is aligned
+      on the same vertical axis.</p>
+    <p>
+      <math>
+        <munder align="left">
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munder>
+      </math>
+    </p>
+    <p>
+      <math>
+        <munder align="right">
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munder>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mover align="left">
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </mover>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mover align="right">
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </mover>
+      </math>
+    </p>
+    <p>
+      <math>
+        <munderover align="left">
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munderover>
+      </math>
+    </p>
+    <p>
+      <math>
+        <munderover align="right">
+          <mspace width="30px" height="20px" style="background: cyan;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+          <mspace width="10px" height="20px" style="background: blue;"></mspace>
+        </munderover>
+      </math>
+    </p>
+    <script src="/mathml/support/feature-detection.js"></script>
+    <script>
+      MathMLFeatureDetection.ensure_for_match_reftest("has_mspace");
+      MathMLFeatureDetection.ensure_for_match_reftest("has_munderover");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/sms_mock.js b/third_party/blink/web_tests/external/wpt/resources/chromium/sms_mock.js
index a8cd81a..bdfbc20 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/sms_mock.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/sms_mock.js
@@ -8,7 +8,7 @@
       this.mojoReceiver_ = new blink.mojom.SmsReceiverReceiver(this);
 
       this.interceptor_ = new MojoInterfaceInterceptor(
-          blink.mojom.SmsReceiver.$interfaceName)
+          blink.mojom.SmsReceiver.$interfaceName, "context", true)
 
       this.interceptor_.oninterfacerequest = (e) => {
         this.mojoReceiver_.$.bindHandle(e.handle);
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py b/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py
index ea4a1ac..b2826bf0 100755
--- a/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/run_tc.py
@@ -124,8 +124,12 @@
 
 
 def install_chrome(channel):
+    deb_prefix = "https://dl.google.com/linux/direct/"
     if channel in ("experimental", "dev", "nightly"):
-        deb_archive = "google-chrome-unstable_current_amd64.deb"
+        # Pinned to 78 as 79 consistently fails reftests. TODO(foolip).
+        # See https://github.com/web-platform-tests/wpt/issues/19297.
+        deb_archive = "google-chrome-unstable_78.0.3904.17-1_amd64.deb"
+        deb_prefix = "https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-unstable/"
     elif channel == "beta":
         deb_archive = "google-chrome-beta_current_amd64.deb"
     elif channel == "stable":
@@ -134,7 +138,7 @@
         raise ValueError("Unrecognized release channel: %s" % channel)
 
     dest = os.path.join("/tmp", deb_archive)
-    resp = urlopen("https://dl.google.com/linux/direct/%s" % deb_archive)
+    resp = urlopen(deb_prefix + deb_archive)
     with open(dest, "w") as f:
         f.write(resp.read())
 
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
index 30400b2..21f5bd8 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/accumulation-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 563 tests; 530 PASS, 33 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 563 tests; 531 PASS, 32 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testAccumulation function
 PASS align-content: "flex-end" onto "flex-start"
@@ -543,7 +543,7 @@
 PASS vector-effect: "non-scaling-stroke" onto "none"
 PASS vector-effect: "none" onto "non-scaling-stroke"
 PASS visibility (type: visibility) has testAccumulation function
-FAIL visibility: onto "visible" assert_equals: The value should be visible at 1000ms expected "visible" but got "hidden"
+PASS visibility: onto "visible"
 PASS visibility: onto "hidden"
 PASS white-space (type: discrete) has testAccumulation function
 PASS white-space: "nowrap" onto "pre"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
index b7d3910..d8f90033 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/addition-per-property-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 559 tests; 537 PASS, 22 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 559 tests; 538 PASS, 21 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Setup
 PASS align-content (type: discrete) has testAddition function
 PASS align-content: "flex-end" onto "flex-start"
@@ -539,7 +539,7 @@
 PASS vector-effect: "non-scaling-stroke" onto "none"
 PASS vector-effect: "none" onto "non-scaling-stroke"
 PASS visibility (type: visibility) has testAddition function
-FAIL visibility: onto "visible" assert_equals: The value should be visible at 1000ms expected "visible" but got "hidden"
+PASS visibility: onto "visible"
 PASS visibility: onto "hidden"
 PASS white-space (type: discrete) has testAddition function
 PASS white-space: "nowrap" onto "pre"
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js
index 9f549c9..75f7d61 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js
+++ b/third_party/blink/web_tests/external/wpt/web-animations/animation-model/animation-types/property-types.js
@@ -617,7 +617,7 @@
                                          composite });
       testAnimationSamples(animation, idlName,
                            [{ time: 0,    expected: 'visible' },
-                            { time: 1000, expected: 'visible' }]);
+                            { time: 1000, expected: 'hidden' }]);
     }, `${property}: onto "visible"`);
 
     test(t => {
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..cbfb8bd23
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/fast/forms/controls-new-ui-high-contrast/details.html b/third_party/blink/web_tests/fast/forms/controls-new-ui-high-contrast/details.html
new file mode 100644
index 0000000..fae398c9
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/controls-new-ui-high-contrast/details.html
@@ -0,0 +1,4 @@
+<details open>
+  <summary>Summary</summary>
+  Description
+</details>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html b/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
index 67ad178..af57ac4 100644
--- a/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
+++ b/third_party/blink/web_tests/fast/forms/state-restore-dynamic-controls.html
@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <script src="../../resources/testharness.js"></script>
 <script src="../../resources/testharnessreport.js"></script>
-<script src="../../external/wpt/html/semantics/forms/autofocus/resources/utils.js"></script>
+<script src="../../external/wpt/html/interaction/focus/the-autofocus-attribute/resources/utils.js"></script>
 <body>
 <iframe src="resources/state-restore-dynamic-controls-frame.html"></iframe>
 <script>
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt
index 5ef1c8a2..bfb0722 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt
@@ -26,17 +26,11 @@
       "bounds": [304, 304]
     },
     {
-      "name": "Child Transform Layer",
-      "bounds": [304, 304],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutBlockFlow DIV id='parent'",
       "bounds": [280, 280],
       "contentsOpaque": true,
       "backgroundColor": "#FFFF00",
-      "transform": 4
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV",
@@ -44,7 +38,7 @@
       "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 6
+      "transform": 4
     }
   ],
   "transforms": [
@@ -54,46 +48,23 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [108, 73, 0, 1]
+        [120, 85, 0, 1]
       ]
     },
     {
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.002],
-        [0, 0, 0, 1]
-      ],
-      "origin": [152, 152]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [12, 12, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [0.766044443118978, -0.556670399226419, -0.32139380484327, 0],
         [0, 0.5, -0.866025403784439, 0],
         [0.642787609686539, 0.663413948168938, 0.383022221559489, 0],
         [0, 0, 0, 1]
       ],
-      "origin": [140, 140],
-      "flattenInheritedTransform": false
+      "origin": [140, 140]
     },
     {
-      "id": 5,
-      "parent": 4,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -103,8 +74,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 5,
+      "id": 4,
+      "parent": 3,
       "transform": [
         [0.766044443118978, 0, 0.642787609686539, 0],
         [0, 1, 0, 0],
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt
index 6cd565c..b0a9727 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/layer-creation/overlap-transformed-preserved-3d-expected.txt
@@ -31,51 +31,46 @@
       "drawsContent": false
     },
     {
-      "name": "Child Transform Layer",
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutBlockFlow DIV id='camera' class='rotate-3d-start'",
       "contentsOpaque": true,
       "drawsContent": false,
-      "transform": 3
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-1'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 4
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-2'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 5
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-3'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 6
+      "transform": 5
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-4'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 7
+      "transform": 6
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-5'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 8
+      "transform": 7
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-6'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 9
+      "transform": 8
     }
   ],
   "transforms": [
@@ -92,9 +87,9 @@
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.005],
+        [0.353553390593274, 0.25, -0.5, 0],
+        [0, 0.353553390593274, 0.707106781186548, 0],
+        [0.353553390593274, -0.25, 0.5, 0],
         [0, 0, 0, 1]
       ],
       "origin": [50, 50]
@@ -103,18 +98,6 @@
       "id": 3,
       "parent": 2,
       "transform": [
-        [0.353553390593274, 0.25, -0.5, 0],
-        [0, 0.353553390593274, 0.707106781186548, 0],
-        [0.353553390593274, -0.25, 0.5, 0],
-        [0, 0, 0, 1]
-      ],
-      "origin": [50, 50],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
@@ -123,8 +106,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 5,
-      "parent": 3,
+      "id": 4,
+      "parent": 2,
       "transform": [
         [0, 0, -1, 0],
         [0, 1, 0, 0],
@@ -135,8 +118,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 3,
+      "id": 5,
+      "parent": 2,
       "transform": [
         [-1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -147,8 +130,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 7,
-      "parent": 3,
+      "id": 6,
+      "parent": 2,
       "transform": [
         [0, 0, 1, 0],
         [0, 1, 0, 0],
@@ -159,8 +142,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 8,
-      "parent": 3,
+      "id": 7,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, 1, 0],
@@ -171,8 +154,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 9,
-      "parent": 3,
+      "id": 8,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, -1, 0],
@@ -212,51 +195,46 @@
       "drawsContent": false
     },
     {
-      "name": "Child Transform Layer",
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutBlockFlow DIV id='camera' class='rotate-3d-start rotate-3d-end'",
       "contentsOpaque": true,
       "drawsContent": false,
-      "transform": 3
+      "transform": 2
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-1'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 4
+      "transform": 3
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-2'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 5
+      "transform": 4
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-3'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 6
+      "transform": 5
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-4'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 7
+      "transform": 6
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-5'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 8
+      "transform": 7
     },
     {
       "name": "LayoutBlockFlow (positioned) DIV class='side side-6'",
       "bounds": [100, 100],
       "backgroundColor": "#00FF00CC",
-      "transform": 9
+      "transform": 8
     },
     {
       "name": "Squashing Containment Layer",
@@ -289,9 +267,9 @@
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.005],
+        [0.707106781186548, 0.5, -0.5, 0],
+        [0, 0.707106781186548, 0.707106781186548, 0],
+        [0.707106781186548, -0.5, 0.5, 0],
         [0, 0, 0, 1]
       ],
       "origin": [50, 50]
@@ -300,18 +278,6 @@
       "id": 3,
       "parent": 2,
       "transform": [
-        [0.707106781186548, 0.5, -0.5, 0],
-        [0, 0.707106781186548, 0.707106781186548, 0],
-        [0.707106781186548, -0.5, 0.5, 0],
-        [0, 0, 0, 1]
-      ],
-      "origin": [50, 50],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
@@ -320,8 +286,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 5,
-      "parent": 3,
+      "id": 4,
+      "parent": 2,
       "transform": [
         [0, 0, -1, 0],
         [0, 1, 0, 0],
@@ -332,8 +298,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 3,
+      "id": 5,
+      "parent": 2,
       "transform": [
         [-1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -344,8 +310,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 7,
-      "parent": 3,
+      "id": 6,
+      "parent": 2,
       "transform": [
         [0, 0, 1, 0],
         [0, 1, 0, 0],
@@ -356,8 +322,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 8,
-      "parent": 3,
+      "id": 7,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, 1, 0],
@@ -368,8 +334,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 9,
-      "parent": 3,
+      "id": 8,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 0, -1, 0],
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective-expected.txt
new file mode 100644
index 0000000..b245fa2
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/overflow/accelerated-overflow-scroll-should-not-affect-perspective-expected.txt
@@ -0,0 +1,116 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='container'",
+      "position": [8, 8],
+      "bounds": [200, 200]
+    },
+    {
+      "name": "Scrolling Layer",
+      "position": [8, 8],
+      "bounds": [185, 185],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "position": [8, 8],
+      "bounds": [185, 290],
+      "drawsContent": false
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='child first'",
+      "bounds": [60, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#008000",
+      "transform": 2
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV class='child second'",
+      "bounds": [60, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#0000FF",
+      "transform": 4
+    },
+    {
+      "name": "Overflow Controls Host Layer",
+      "position": [8, 8],
+      "bounds": [200, 200],
+      "drawsContent": false
+    },
+    {
+      "name": "Horizontal Scrollbar Layer",
+      "position": [8, 193],
+      "bounds": [185, 15],
+      "drawsContent": false
+    },
+    {
+      "name": "Vertical Scrollbar Layer",
+      "position": [193, 8],
+      "bounds": [15, 185],
+      "drawsContent": false
+    },
+    {
+      "name": "Scroll Corner Layer",
+      "position": [193, 193],
+      "bounds": [15, 15]
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 73, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 10, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [73, 73, 0, 1]
+      ]
+    },
+    {
+      "id": 4,
+      "parent": 3,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 0, 20, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/squashing/squashing-inside-perspective-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/squashing/squashing-inside-perspective-expected.txt
new file mode 100644
index 0000000..40fc40f
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/squashing/squashing-inside-perspective-expected.txt
@@ -0,0 +1,56 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV",
+      "position": [8, 8],
+      "contentsOpaque": true,
+      "drawsContent": false
+    },
+    {
+      "name": "LayoutBlockFlow (positioned) DIV",
+      "bounds": [200, 200],
+      "contentsOpaque": true,
+      "backgroundColor": "#00008B",
+      "transform": 2
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, 74, 200, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/file-system-project.js b/third_party/blink/web_tests/http/tests/devtools/file-system-project.js
index ac6a8d5..c6ec9f8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/file-system-project.js
+++ b/third_party/blink/web_tests/http/tests/devtools/file-system-project.js
@@ -174,7 +174,7 @@
 
         dir.addFile('bar.js', '');
         InspectorFrontendHost.events.dispatchEventToListeners(
-            InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
+            Host.InspectorFrontendHostAPI.Events.FileSystemFilesChangedAddedRemoved,
             {changed: [], added: ['/var/www4/html/bar.js'], removed: []});
 
         TestRunner.addResult('-- File added externally --');
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/badging-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/badging-origin-trial-interfaces.html
index e88af7eb..9459b0ca 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/badging-origin-trial-interfaces.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/badging-origin-trial-interfaces.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <!-- Generate token with the command:
-generate_token.py http://127.0.0.1:8000 BadgingV2 --expire-timestamp=2000000000
+generate_token.py http://127.0.0.1:8000 Badging --expire-timestamp=2000000000
 -->
 
-<meta http-equiv="origin-trial" content="AqzH1yAjqt/6grJkR3r1584FLOYa+kkfoenZBdnmBOShEN/eGrOF7OoxdPXg5e2b+KeB+ysH8qp/F9eyimHZygIAAABReyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQmFkZ2luZ1YyIiwgImV4cGlyeSI6IDIwMDAwMDAwMDB9" />
+<meta http-equiv="origin-trial" content="AjMyKQIep8aE/3H2rfLKB0UEt0aGtfdVGult8LN5tjpYZnkhbaGEm8KirzODRym/5KnVfv33DaXex0tUZoDQFQ0AAABPeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQmFkZ2luZyIsICJleHBpcnkiOiAyMDAwMDAwMDAwfQ==" />
 <title>Badging API - interfaces exposed by origin trial</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
@@ -16,4 +16,4 @@
   assert_own_property(window.ExperimentalBadge, 'set', 'Set is not defined on the ExperimentalBadge API');
   assert_own_property(window.ExperimentalBadge, 'clear', 'Clear is not defined on the ExperimentalBadge API');
 }, 'Badge API interfaces and properties in Origin-Trial enabled document.');
-</script>
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt
index 7cb51745..cad01fd9 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt
@@ -26,17 +26,11 @@
       "bounds": [304, 304]
     },
     {
-      "name": "Child Transform Layer",
-      "bounds": [304, 304],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutNGBlockFlow DIV id='parent'",
       "bounds": [280, 280],
       "contentsOpaque": true,
       "backgroundColor": "#FFFF00",
-      "transform": 4
+      "transform": 2
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
@@ -44,7 +38,7 @@
       "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 6
+      "transform": 4
     }
   ],
   "transforms": [
@@ -54,46 +48,23 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [108, 73, 0, 1]
+        [120, 85, 0, 1]
       ]
     },
     {
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.002],
-        [0, 0, 0, 1]
-      ],
-      "origin": [152, 152]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [12, 12, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [0.766044443118978, -0.556670399226419, -0.32139380484327, 0],
         [0, 0.5, -0.866025403784439, 0],
         [0.642787609686539, 0.663413948168938, 0.383022221559489, 0],
         [0, 0, 0, 1]
       ],
-      "origin": [140, 140],
-      "flattenInheritedTransform": false
+      "origin": [140, 140]
     },
     {
-      "id": 5,
-      "parent": 4,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -103,8 +74,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 5,
+      "id": 4,
+      "parent": 3,
       "transform": [
         [0.766044443118978, 0, 0.642787609686539, 0],
         [0, 1, 0, 0],
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..9cfa1a6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..9cfa1a6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..7de77fd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..7de77fd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..7de77fd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..7de77fd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt
index 7cb51745..cad01fd9 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt
@@ -26,17 +26,11 @@
       "bounds": [304, 304]
     },
     {
-      "name": "Child Transform Layer",
-      "bounds": [304, 304],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutNGBlockFlow DIV id='parent'",
       "bounds": [280, 280],
       "contentsOpaque": true,
       "backgroundColor": "#FFFF00",
-      "transform": 4
+      "transform": 2
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
@@ -44,7 +38,7 @@
       "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 6
+      "transform": 4
     }
   ],
   "transforms": [
@@ -54,46 +48,23 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [108, 73, 0, 1]
+        [120, 85, 0, 1]
       ]
     },
     {
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.002],
-        [0, 0, 0, 1]
-      ],
-      "origin": [152, 152]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [12, 12, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [0.766044443118978, -0.556670399226419, -0.32139380484327, 0],
         [0, 0.5, -0.866025403784439, 0],
         [0.642787609686539, 0.663413948168938, 0.383022221559489, 0],
         [0, 0, 0, 1]
       ],
-      "origin": [140, 140],
-      "flattenInheritedTransform": false
+      "origin": [140, 140]
     },
     {
-      "id": 5,
-      "parent": 4,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -103,8 +74,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 5,
+      "id": 4,
+      "parent": 3,
       "transform": [
         [0.766044443118978, 0, 0.642787609686539, 0],
         [0, 1, 0, 0],
diff --git a/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..7de77fd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..7de77fd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt
index 3eca5361..2fc12cd0 100644
--- a/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt
@@ -26,17 +26,11 @@
       "bounds": [304, 304]
     },
     {
-      "name": "Child Transform Layer",
-      "bounds": [304, 304],
-      "drawsContent": false,
-      "transform": 2
-    },
-    {
       "name": "LayoutNGBlockFlow DIV id='parent'",
       "bounds": [280, 280],
       "contentsOpaque": true,
       "backgroundColor": "#FFFF00",
-      "transform": 4
+      "transform": 2
     },
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
@@ -44,7 +38,7 @@
       "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
-      "transform": 6
+      "transform": 4
     }
   ],
   "transforms": [
@@ -54,46 +48,23 @@
         [1, 0, 0, 0],
         [0, 1, 0, 0],
         [0, 0, 1, 0],
-        [108, 72, 0, 1]
+        [120, 84, 0, 1]
       ]
     },
     {
       "id": 2,
       "parent": 1,
       "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, -0.002],
-        [0, 0, 0, 1]
-      ],
-      "origin": [152, 152]
-    },
-    {
-      "id": 3,
-      "parent": 2,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [12, 12, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    },
-    {
-      "id": 4,
-      "parent": 3,
-      "transform": [
         [0.766044443118978, -0.556670399226419, -0.32139380484327, 0],
         [0, 0.5, -0.866025403784439, 0],
         [0.642787609686539, 0.663413948168938, 0.383022221559489, 0],
         [0, 0, 0, 1]
       ],
-      "origin": [140, 140],
-      "flattenInheritedTransform": false
+      "origin": [140, 140]
     },
     {
-      "id": 5,
-      "parent": 4,
+      "id": 3,
+      "parent": 2,
       "transform": [
         [1, 0, 0, 0],
         [0, 1, 0, 0],
@@ -103,8 +74,8 @@
       "flattenInheritedTransform": false
     },
     {
-      "id": 6,
-      "parent": 5,
+      "id": 4,
+      "parent": 3,
       "transform": [
         [0.766044443118978, 0, 0.642787609686539, 0],
         [0, 1, 0, 0],
diff --git a/third_party/blink/web_tests/virtual/at-property/external/wpt/css/css-properties-values-api/idlharness-expected.txt b/third_party/blink/web_tests/virtual/at-property/external/wpt/css/css-properties-values-api/idlharness-expected.txt
index 7a11537..87fb499 100644
--- a/third_party/blink/web_tests/virtual/at-property/external/wpt/css/css-properties-values-api/idlharness-expected.txt
+++ b/third_party/blink/web_tests/virtual/at-property/external/wpt/css/css-properties-values-api/idlharness-expected.txt
@@ -3,6 +3,8 @@
 PASS idl_test validation
 PASS Partial namespace CSS: original namespace defined
 PASS Partial namespace CSS: member names are unique
+PASS Partial interface CSSRule: original interface defined
+PASS Partial interface CSSRule: member names are unique
 PASS CSSPropertyRule interface: existence and properties of interface object
 PASS CSSPropertyRule interface object length
 PASS CSSPropertyRule interface object name
@@ -13,6 +15,8 @@
 FAIL CSSPropertyRule interface: attribute syntax assert_true: The prototype object must have a property "syntax" expected true got false
 FAIL CSSPropertyRule interface: attribute inherits assert_true: The prototype object must have a property "inherits" expected true got false
 FAIL CSSPropertyRule interface: attribute initialValue assert_true: The prototype object must have a property "initialValue" expected true got false
+FAIL CSSRule interface: constant PROPERTY_RULE on interface object assert_equals: property has wrong value expected 18 but got 1001
+FAIL CSSRule interface: constant PROPERTY_RULE on interface prototype object assert_equals: property has wrong value expected 18 but got 1001
 PASS CSS namespace: operation escape(CSSOMString)
 PASS CSS namespace: operation registerProperty(PropertyDefinition)
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/cascade/external/wpt/css/css-properties-values-api/idlharness-expected.txt b/third_party/blink/web_tests/virtual/cascade/external/wpt/css/css-properties-values-api/idlharness-expected.txt
new file mode 100644
index 0000000..20e8217b
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/cascade/external/wpt/css/css-properties-values-api/idlharness-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+PASS idl_test setup
+PASS idl_test validation
+PASS Partial namespace CSS: original namespace defined
+PASS Partial namespace CSS: member names are unique
+PASS Partial interface CSSRule: original interface defined
+PASS Partial interface CSSRule: member names are unique
+FAIL CSSPropertyRule interface: existence and properties of interface object assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface object length assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface object name assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface: existence and properties of interface prototype object assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface: attribute name assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface: attribute syntax assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface: attribute inherits assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSPropertyRule interface: attribute initialValue assert_own_property: self does not have own property "CSSPropertyRule" expected property "CSSPropertyRule" missing
+FAIL CSSRule interface: constant PROPERTY_RULE on interface object assert_own_property: expected property "PROPERTY_RULE" missing
+FAIL CSSRule interface: constant PROPERTY_RULE on interface prototype object assert_own_property: expected property "PROPERTY_RULE" missing
+PASS CSS namespace: operation escape(CSSOMString)
+PASS CSS namespace: operation registerProperty(PropertyDefinition)
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
new file mode 100644
index 0000000..cbfb8bd23
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh-high-contrast/fast/forms/controls-new-ui-high-contrast/details-expected.png
Binary files differ
diff --git a/tools/android/elf_compression/README.md b/tools/android/elf_compression/README.md
index ed068a2..9d41fd94 100644
--- a/tools/android/elf_compression/README.md
+++ b/tools/android/elf_compression/README.md
@@ -16,11 +16,19 @@
 constructor's symbols to point at the cutted range and to the compressed
 version.
 ### Library constructor
-TODO(https://crbug.com/998082): describe this.
+Located at `constructor/` path and should be build together with the target
+library.
+
+It decompresses data from compressed section, provided by compression script
+and populates the target range.
 
 ## Usage
-Firstly, the library needs to be build with the tool's constructor.
-TODO(https://crbug.com/998082): describe this.
+Firstly, the library needs to be build with the tool's constructor. To do this
+add the following file to your build:
+
+    constructor/library_constructor.c
+
+Additionally the library must be linked with `-pthread` option.
 
 After the library build is complete, the compression script must be applied to
 it in the following way:
diff --git a/tools/android/elf_compression/compress_section.py b/tools/android/elf_compression/compress_section.py
index 7f145266..64d39e4 100755
--- a/tools/android/elf_compression/compress_section.py
+++ b/tools/android/elf_compression/compress_section.py
@@ -38,6 +38,15 @@
 COMPRESSED_SECTION_NAME = '.compressed_library_data'
 ADDRESS_ALIGN = 0x1000
 
+CUT_RANGE_BEGIN_MAGIC = bytearray(
+    [0x2e, 0x2a, 0xee, 0xf6, 0x45, 0x03, 0xd2, 0x50])
+CUT_RANGE_END_MAGIC = bytearray(
+    [0x52, 0x40, 0xeb, 0x9d, 0xdb, 0x11, 0xed, 0x1a])
+COMPRESSED_RANGE_BEGIN_MAGIC = bytearray(
+    [0x5e, 0x49, 0x4a, 0x4c, 0xae, 0x28, 0xc8, 0xbb])
+COMPRESSED_RANGE_END_MAGIC = bytearray(
+    [0xdd, 0x60, 0xed, 0xcf, 0xc3, 0x29, 0xa6, 0xd6])
+
 # src/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy
 OBJCOPY_PATH = pathlib.Path(__file__).resolve().parents[3].joinpath(
     'third_party/llvm-build/Release+Asserts/bin/llvm-objcopy')
@@ -253,7 +262,9 @@
 
 
 def _CreateLoadForCompressedSection(data):
-  """Creates a LOAD segment to previously created COMPRESSED_SECTION_NAME."""
+  """Creates a LOAD segment to previously created COMPRESSED_SECTION_NAME.
+
+  Returns the virtual address range corresponding to created segment."""
   elf_hdr = elf_headers.ElfHeader(data)
 
   section_offset = None
@@ -282,6 +293,7 @@
           p_align=ADDRESS_ALIGN,
       ))
   elf_hdr.PatchData(data)
+  return new_vaddr, new_vaddr + section_size
 
 
 def _SplitLoadSegmentAndNullifyRange(data, l, r):
@@ -293,6 +305,8 @@
 
   The resulting LOAD segment containing [l, r) is edited so it sets the
   corresponding virtual address range to zeroes, ignoring file content.
+
+  Returns virtual address range corresponding to [l, r).
   """
   elf_hdr = elf_headers.ElfHeader(data)
 
@@ -349,6 +363,7 @@
   range_phdr.p_memsz = r - l
 
   elf_hdr.PatchData(data)
+  return central_segment_address, central_segment_address + (r - l)
 
 
 def _CutRangeAndCorrectFile(data, l, r):
@@ -397,6 +412,30 @@
   elf.PatchData(data)
 
 
+def _PatchConstructorBytes(data, cut_range_virt_l, cut_range_virt_r,
+                           compressed_range_virt_l, compressed_range_virt_r):
+  """Sets magic bytes given by constructor to the given ranges."""
+  elf = elf_headers.ElfHeader(data)
+
+  to_patch = [
+      (CUT_RANGE_BEGIN_MAGIC, cut_range_virt_l, 'cut range begin'),
+      (CUT_RANGE_END_MAGIC, cut_range_virt_r, 'cut range end'),
+      (COMPRESSED_RANGE_BEGIN_MAGIC, compressed_range_virt_l,
+       'compressed range begin'),
+      (COMPRESSED_RANGE_END_MAGIC, compressed_range_virt_r,
+       'compressed range end'),
+  ]
+
+  for magic_bytes, new_value, name in to_patch:
+    magic_idx = data.find(magic_bytes)
+    if magic_idx == -1:
+      raise RuntimeError('failed to find %s magic bytes' % name)
+    if data.rfind(magic_bytes) != magic_idx:
+      raise RuntimeError('%s magic bytes occures more then once' % name)
+    new_value_bytes = new_value.to_bytes(length=8, byteorder=elf.byte_order)
+    data[magic_idx:magic_idx + 8] = new_value_bytes
+
+
 def _ShrinkRangeToAlignVirtualAddress(data, l, r):
   virtual_l, virtual_r = _FileRangeToVirtualAddressRange(data, l, r)
   # LOAD segments borders are being rounded to the page size so we have to
@@ -422,9 +461,12 @@
   _CopyRangeIntoCompressedSection(data, left_range, right_range)
   _MovePhdrToTheEnd(data)
 
-  _CreateLoadForCompressedSection(data)
-  _SplitLoadSegmentAndNullifyRange(data, left_range, right_range)
+  compressed_virt_l, compressed_virt_r = _CreateLoadForCompressedSection(data)
+  virt_l, virt_r = _SplitLoadSegmentAndNullifyRange(data, left_range,
+                                                    right_range)
   _CutRangeAndCorrectFile(data, left_range, right_range)
+  _PatchConstructorBytes(data, virt_l, virt_r, compressed_virt_l,
+                         compressed_virt_r)
 
   with open(args.output, 'wb') as f:
     f.write(data)
diff --git a/tools/android/elf_compression/constructor/library_constructor.c b/tools/android/elf_compression/constructor/library_constructor.c
new file mode 100644
index 0000000..c808131
--- /dev/null
+++ b/tools/android/elf_compression/constructor/library_constructor.c
@@ -0,0 +1,327 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains userfaultfd watcher constructor which decompress
+// parts of the library's code, compressed by compress_section.py script.
+#include <asm-generic/ioctls.h>
+#include <asm/unistd_64.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/syscall.h>
+#include <sys/ttydefaults.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Symbol with virtual address of the start of ELF header of the library. Set by
+// linker.
+extern char __ehdr_start;
+
+// This function can be used to prevent a value or expression from being
+// optimized away by the compiler.
+//
+// This method is clang specific, hence the #error
+#ifndef __clang__
+#error "DoNotOptimize is clang specific method"
+#endif
+void DoNotOptimize(void* value) {
+  __asm__ volatile("" : : "r,m"(value) : "memory");
+}
+
+// The following 4 arrays are here to be patched into by compress_section.py
+// script. They currently contain magic bytes that will be used to locate them
+// in the library file. DoNotOptimize method is applied to them at the
+// beginning of the constructor to ensure that the arrays are not optimized
+// away.
+unsigned char g_dummy_cut_range_begin[8] = {0x2e, 0x2a, 0xee, 0xf6,
+                                            0x45, 0x03, 0xd2, 0x50};
+unsigned char g_dummy_cut_range_end[8] = {0x52, 0x40, 0xeb, 0x9d,
+                                          0xdb, 0x11, 0xed, 0x1a};
+unsigned char g_dummy_compressed_range_begin[8] = {0x5e, 0x49, 0x4a, 0x4c,
+                                                   0xae, 0x28, 0xc8, 0xbb};
+unsigned char g_dummy_compressed_range_end[8] = {0xdd, 0x60, 0xed, 0xcf,
+                                                 0xc3, 0x29, 0xa6, 0xd6};
+
+static void DecompressPage(void* cut_start,
+                           void* compressed_start,
+                           void* page_start,
+                           size_t page_size,
+                           void* buffer) {
+  // TODO(https://crbug.com/998082): Update the method to work with arbitrary
+  // block sizes.
+
+  // This method is a stub to plug the decompression login into.
+  uint64_t delta = page_start - cut_start;
+  void* compressed_page_start = compressed_start + delta;
+  memcpy(buffer, compressed_page_start, page_size);
+}
+
+typedef struct PollArray {
+  struct pollfd* pollfd_array;
+  size_t size;
+  size_t capacity;
+} PollArray;
+
+PollArray* CreatePollArray() {
+  PollArray* array = calloc(1, sizeof(PollArray));
+  return array;
+}
+
+void DestroyPollArray(PollArray* arr) {
+  free(arr->pollfd_array);
+  free(arr);
+}
+
+void IncreasePollArrayCapacity(PollArray* arr) {
+  if (arr->capacity == 0) {
+    arr->capacity = 1;
+  } else {
+    arr->capacity *= 2;
+  }
+  arr->pollfd_array =
+      realloc(arr->pollfd_array, arr->capacity * sizeof(struct pollfd));
+}
+
+void PollArrayPush(PollArray* arr, struct pollfd el) {
+  if (arr->size == arr->capacity) {
+    IncreasePollArrayCapacity(arr);
+  }
+  arr->pollfd_array[arr->size] = el;
+  arr->size++;
+}
+
+void PollArrayPop(PollArray* arr) {
+  arr->size--;
+}
+
+typedef struct ThreadArguments {
+  int uffd;
+  void* cut_start;
+  void* compressed_start;
+  size_t cut_length;
+  size_t page_size;
+} ThreadArguments;
+
+static void HandlePageFault(int uffd,
+                            void* pagefault_address,
+                            void* cut_start,
+                            void* compressed_start,
+                            size_t page_size,
+                            void* buffer) {
+  DecompressPage(cut_start, compressed_start, pagefault_address, page_size,
+                 buffer);
+  struct uffdio_copy copy;
+  copy.dst = (uint64_t)pagefault_address;
+  copy.src = (uint64_t)buffer;
+  copy.len = page_size;
+  copy.mode = 0;
+  if (ioctl(uffd, UFFDIO_COPY, &copy)) {
+    switch (errno) {
+      case EINVAL:
+      case EAGAIN:
+        perror("UFFDIO_COPY failed");
+    }
+  }
+}
+
+static void* WatcherThreadFunc(void* thread_args) {
+  ThreadArguments* args = thread_args;
+
+  void* buffer = calloc(args->page_size, sizeof(char));
+  struct uffd_msg message;
+
+  PollArray* poll_array = CreatePollArray();
+  struct pollfd poll_fd = {.fd = args->uffd, .events = POLLIN};
+  PollArrayPush(poll_array, poll_fd);
+
+  // TODO(https://crbug.com/998082): Use epoll instead
+  while (poll_array->size &&
+         poll(poll_array->pollfd_array, poll_array->size, -1) >= 0) {
+    for (int i = 0; i < poll_array->size; i++) {
+      struct pollfd* current_fd = &poll_array->pollfd_array[i];
+      if (current_fd->revents & POLLIN) {
+        read(current_fd->fd, &message, sizeof(message));
+        if (message.event == UFFD_EVENT_FORK) {
+          struct pollfd new_fd = {.fd = message.arg.fork.ufd, .events = POLLIN};
+          PollArrayPush(poll_array, new_fd);
+        }
+        if (message.event == UFFD_EVENT_PAGEFAULT) {
+          HandlePageFault(current_fd->fd, (void*)message.arg.pagefault.address,
+                          args->cut_start, args->compressed_start,
+                          args->page_size, buffer);
+        }
+      } else if (current_fd->revents & POLLHUP) {
+        close(current_fd->fd);
+        poll_array->pollfd_array[i] =
+            poll_array->pollfd_array[poll_array->size - 1];
+        PollArrayPop(poll_array);
+      }
+    }
+  }
+  // Everything died, the thread is free.
+  free(buffer);
+  free(thread_args);
+  DestroyPollArray(poll_array);
+  return NULL;
+}
+
+static int StartWatcherThread(void* cut_addr,
+                              void* compressed_l,
+                              size_t cut_length,
+                              size_t page_size,
+                              int uffd) {
+  pthread_attr_t attr;
+  if (pthread_attr_init(&attr)) {
+    return -1;
+  }
+  if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+    pthread_attr_destroy(&attr);
+    return -1;
+  }
+  ThreadArguments* args = malloc(sizeof(ThreadArguments));
+  args->uffd = uffd;
+  args->cut_start = cut_addr;
+  args->compressed_start = compressed_l;
+  args->page_size = page_size;
+
+  pthread_t thread_id;
+  if (pthread_create(&thread_id, &attr, WatcherThreadFunc, (void*)args)) {
+    pthread_attr_destroy(&attr);
+    free(args);
+    return -1;
+  }
+  pthread_attr_destroy(&attr);
+  return 0;
+}
+
+static int SetupUserfaultFd(void* cut_start, size_t cut_length) {
+  int uffd = syscall(__NR_userfaultfd, O_NONBLOCK | O_CLOEXEC);
+  if (uffd == -1) {
+    perror("Userfaultfd syscall failed");
+    return -1;
+  }
+  // Enabling userfaultfd.
+  struct uffdio_api api = {.api = UFFD_API,
+                           .features = UFFD_FEATURE_EVENT_FORK};
+  if (ioctl(uffd, UFFDIO_API, &api)) {
+    perror("ioctl UFFDIO_API failed");
+    close(uffd);
+    return -1;
+  }
+  // Setting userfaultfd watch over cut region.
+  struct uffdio_register uffd_register;
+  uffd_register.range.start = (uint64_t)cut_start;
+  uffd_register.range.len = cut_length;
+  uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+  if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) {
+    perror("ioctl UFFDIO_REGISTER failed");
+    close(uffd);
+    return -1;
+  }
+  return uffd;
+}
+
+// Unregisters the userfaultfd watch on cut range. Used to revert to
+// DecompressWholeRange in case of error during the creation of the thread.
+static void UnregisterUserfaultFd(void* cut_start,
+                                  size_t cut_length,
+                                  int uffd) {
+  struct uffdio_register uffd_unregister;
+  uffd_unregister.range.start = (uint64_t)cut_start;
+  uffd_unregister.range.len = cut_length;
+  uffd_unregister.mode = UFFDIO_REGISTER_MODE_MISSING;
+  // No error handling here since we are already resorting to the fallback
+  // option.
+  ioctl(uffd, UFFDIO_UNREGISTER, &uffd_unregister);
+}
+
+// Backup slow solution for the constructor. Fully decompresses and populates
+// the cut range. This method is used if the userfaultfd setup failed to ensure
+// that the library will still function despite the failure.
+static void DecompressWholeRange(void* cut_start,
+                                 void* compressed_start,
+                                 size_t cut_length,
+                                 size_t page_size) {
+  if (mprotect(cut_start, cut_length, PROT_READ | PROT_EXEC | PROT_WRITE)) {
+    perror("Failed to enable PROT_WRITE on cut range");
+    exit(1);
+  }
+
+  void* buffer = calloc(page_size, sizeof(char));
+  for (uint64_t offset = 0; offset < cut_length; offset += page_size) {
+    DecompressPage(cut_start, compressed_start, cut_start + offset, page_size,
+                   buffer);
+    memcpy(cut_start + offset, buffer, page_size);
+  }
+  free(buffer);
+
+  if (mprotect(cut_start, cut_length, PROT_READ | PROT_EXEC)) {
+    perror("Failed to disable PROT_WRITE on cut range");
+    exit(1);
+  }
+}
+
+static unsigned char* MapCutRange(void* cut_start, size_t cut_length) {
+  void* addr = mmap(cut_start, cut_length, PROT_READ | PROT_EXEC,
+                    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+  if (addr == MAP_FAILED) {
+    perror("Constructor cut range mapping failed");
+    // If we fail at this point of time there is no way for us to recover since
+    // without valid mapping we can't change the cut region.
+    exit(1);
+  }
+  return addr;
+}
+
+void* ConvertDummyArrayToAddress(void* dummy_array) {
+  uintptr_t* value_ptr = (uintptr_t*)dummy_array;
+  uintptr_t value = *value_ptr;
+  value += (uintptr_t)&__ehdr_start;
+  return (void*)value;
+}
+
+void __attribute__((constructor(100))) InitLibraryDecompressor() {
+  // The constructor only works on 64 bit systems and as such expecting the
+  // pointer size to be 8 bytes.
+  _Static_assert(sizeof(uint64_t) == sizeof(uintptr_t),
+                 "Pointer size is not 8 bytes");
+
+  DoNotOptimize(g_dummy_cut_range_begin);
+  DoNotOptimize(g_dummy_cut_range_end);
+  DoNotOptimize(g_dummy_compressed_range_begin);
+  DoNotOptimize(g_dummy_compressed_range_end);
+
+  void* cut_l = ConvertDummyArrayToAddress(g_dummy_cut_range_begin);
+  void* cut_r = ConvertDummyArrayToAddress(g_dummy_cut_range_end);
+  void* compressed_l =
+      ConvertDummyArrayToAddress(g_dummy_compressed_range_begin);
+  void* compressed_r = ConvertDummyArrayToAddress(g_dummy_compressed_range_end);
+
+  uint64_t cut_range_length = (uintptr_t)cut_r - (uintptr_t)cut_l;
+  void* cut_addr = MapCutRange(cut_l, cut_range_length);
+
+  size_t page_size = sysconf(_SC_PAGESIZE);
+  int uffd = SetupUserfaultFd(cut_addr, cut_range_length);
+  if (uffd == -1) {
+    DecompressWholeRange(cut_addr, compressed_l, cut_range_length, page_size);
+    return;
+  }
+  if (StartWatcherThread(cut_addr, compressed_l, cut_range_length, page_size,
+                         uffd)) {
+    UnregisterUserfaultFd(cut_addr, cut_range_length, uffd);
+    close(uffd);
+    DecompressWholeRange(cut_addr, compressed_l, cut_range_length, page_size);
+    return;
+  }
+}
diff --git a/tools/android/elf_compression/elf_headers.py b/tools/android/elf_compression/elf_headers.py
index f929191b..c517b19 100644
--- a/tools/android/elf_compression/elf_headers.py
+++ b/tools/android/elf_compression/elf_headers.py
@@ -297,7 +297,6 @@
       string_table_offset = self.e_shoff + self.e_shstrndx * self.e_shentsize
       string_table = StringTableHeader.FromBytes(self.byte_order, data,
                                                  string_table_offset)
-
       for shdr in self.shdrs:
         shdr.SetStrName(string_table.GetName(data, shdr.sh_name))
 
@@ -426,7 +425,7 @@
     self.phdrs.sort(key=HeaderToKey)
 
   def _PatchProgramHeaders(self, data):
-    """Patch all program headers."""
+    """Patches all program headers."""
     current_offset = self.e_phoff
     self._OrderProgramHeaders()
     for phdr in self.GetProgramHeaders():
@@ -434,6 +433,14 @@
       data[current_offset:current_offset + len(phdr_bytes)] = phdr_bytes
       current_offset += self.e_phentsize
 
+  def _PatchSectionHeaders(self, data):
+    """Patches all section headers."""
+    current_offset = self.e_shoff
+    for shdr in self.GetSectionHeaders():
+      shdr_bytes = shdr.ToBytes()
+      data[current_offset:current_offset + len(shdr_bytes)] = shdr_bytes
+      current_offset += self.e_shentsize
+
   def PatchData(self, data):
     """Patches the given data array to reflect all changes made to the header.
 
@@ -453,3 +460,4 @@
     elf_bytes = self.ToBytes()
     data[:len(elf_bytes)] = elf_bytes
     self._PatchProgramHeaders(data)
+    self._PatchSectionHeaders(data)
diff --git a/tools/android/elf_compression/test/compression_script_test.py b/tools/android/elf_compression/test/compression_script_test.py
index 4ec8213..8627734a 100755
--- a/tools/android/elf_compression/test/compression_script_test.py
+++ b/tools/android/elf_compression/test/compression_script_test.py
@@ -17,11 +17,15 @@
 LIBRARY_CC_NAME = 'libtest.cc'
 OPENER_CC_NAME = 'library_opener.cc'
 
+CONSTRUCTOR_C_PATH = '../constructor/library_constructor.c'
 SCRIPT_PATH = '../compress_section.py'
 
 # src/third_party/llvm-build/Release+Asserts/bin/clang++
-LLVM_CLANG_PATH = pathlib.Path(__file__).resolve().parents[4].joinpath(
+LLVM_CLANG_CC_PATH = pathlib.Path(__file__).resolve().parents[4].joinpath(
     'third_party/llvm-build/Release+Asserts/bin/clang++')
+# src/third_party/llvm-build/Release+Asserts/bin/clang
+LLVM_CLANG_PATH = pathlib.Path(__file__).resolve().parents[4].joinpath(
+    'third_party/llvm-build/Release+Asserts/bin/clang')
 
 # The array that we are trying to cut out of the file have those bytes at
 # its start and end. This is done to simplify the test code to not perform
@@ -44,6 +48,7 @@
     self.library_cc_path = os.path.join(script_dir, LIBRARY_CC_NAME)
     self.opener_cc_path = os.path.join(script_dir, OPENER_CC_NAME)
     self.script_path = os.path.join(script_dir, SCRIPT_PATH)
+    self.constructor_c_path = os.path.join(script_dir, CONSTRUCTOR_C_PATH)
 
   def tearDown(self):
     self.tmpdir_object.cleanup()
@@ -58,9 +63,23 @@
 
   def _BuildLibrary(self):
     library_path = os.path.join(self.tmpdir, 'libtest.so')
+    library_object_path = os.path.join(self.tmpdir, 'libtest.o')
+    constructor_object_path = os.path.join(self.tmpdir, 'constructor.o')
+    library_object_build_result = subprocess.run([
+        LLVM_CLANG_CC_PATH, '-c', '-fPIC', '-O2', self.library_cc_path, '-o',
+        library_object_path
+    ])
+    self.assertEqual(library_object_build_result.returncode, 0)
+
+    constructor_object_build_result = subprocess.run([
+        LLVM_CLANG_PATH, '-c', '-fPIC', '-O2', self.constructor_c_path, '-o',
+        constructor_object_path
+    ])
+    self.assertEqual(constructor_object_build_result.returncode, 0)
+
     library_build_result = subprocess.run([
-        LLVM_CLANG_PATH, '-shared', '-fPIC', '-O2', self.library_cc_path, '-o',
-        library_path
+        LLVM_CLANG_PATH, '-shared', '-fPIC', '-O2', library_object_path,
+        constructor_object_path, '-o', library_path, '-pthread'
     ])
     self.assertEqual(library_build_result.returncode, 0)
     return library_path
@@ -68,7 +87,8 @@
   def _BuildOpener(self):
     opener_path = os.path.join(self.tmpdir, 'library_opener')
     opener_build_result = subprocess.run([
-        LLVM_CLANG_PATH, '-O2', self.opener_cc_path, '-o', opener_path, '-ldl'
+        LLVM_CLANG_CC_PATH, '-O2', self.opener_cc_path, '-o', opener_path,
+        '-ldl'
     ])
     self.assertEqual(opener_build_result.returncode, 0)
     return opener_path
@@ -105,7 +125,6 @@
                                        stdout=subprocess.PIPE,
                                        stderr=subprocess.PIPE,
                                        encoding='utf-8')
-
     self.assertEqual(opener_run_result.stderr, '')
     self.assertEqual(opener_run_result.returncode, 0)
     return opener_run_result.stdout
@@ -116,8 +135,9 @@
     opener_path = self._BuildOpener()
 
     patched_library_path = self._RunScript(library_path)
-    opener_output = self._RunOpener(opener_path, patched_library_path)
-    self.assertEqual(opener_output, '4096\n')
+    for _ in range(10):
+      opener_output = self._RunOpener(opener_path, patched_library_path)
+      self.assertEqual(opener_output, '1046543\n')
 
   def testAlignUp(self):
     """Tests for AlignUp method of the script."""
diff --git a/tools/android/elf_compression/test/library_opener.cc b/tools/android/elf_compression/test/library_opener.cc
index f4075f43..bfc78f84 100644
--- a/tools/android/elf_compression/test/library_opener.cc
+++ b/tools/android/elf_compression/test/library_opener.cc
@@ -24,13 +24,13 @@
     return 1;
   }
 
-  TestFunction get_zeroes =
-      reinterpret_cast<TestFunction>(dlsym(handle, "GetZeroes"));
-  if (get_zeroes == nullptr) {
+  TestFunction get_sum =
+      reinterpret_cast<TestFunction>(dlsym(handle, "GetSum"));
+  if (get_sum == nullptr) {
     std::cerr << "GetSum method not found" << std::endl;
     return 1;
   }
 
-  std::cout << get_zeroes() << std::endl;
+  std::cout << get_sum() << std::endl;
   return 0;
 }
diff --git a/tools/android/elf_compression/test/libtest.cc b/tools/android/elf_compression/test/libtest.cc
index aa1832bb..d31ede2 100644
--- a/tools/android/elf_compression/test/libtest.cc
+++ b/tools/android/elf_compression/test/libtest.cc
@@ -8,20 +8,20 @@
 // for the script. We expect library to not crash and return the 55 as a
 // result.
 
-#include <algorithm>
+#include <numeric>
 #include <vector>
 
 #include "libtest_array.h"  // NOLINT(build/include)
 
 extern "C" {
-int GetZeroes();
+int GetSum();
 }
 
-int GetZeroes() {
+int GetSum() {
   // We are using some c++ features here to better simulate a c++ library and
   // cause more code reach to catch potential memory errors.
   std::vector<int> sum_array(std::begin(array), std::end(array));
-  int count = std::count(sum_array.begin(), sum_array.end(), 0);
-  // count should be equal to 4096.
-  return count;
+  int sum = std::accumulate(sum_array.begin(), sum_array.end(), 0);
+  // sum should be equal to 1046543.
+  return sum;
 }
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 3a6262b..ccbb647 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -740,11 +740,7 @@
       'chromeos-kevin-compile-rel': 'cros_chrome_sdk_headless_ozone',
       'chromeos-kevin-experimental-rel': 'cros_chrome_sdk_headless_ozone',
       'chromeos-kevin-rel': 'cros_chrome_sdk_headless_ozone',
-      'linux-chromeos-rel': 'chromeos_with_codecs_release_trybot',
-      # Replicate linux-chromeos-rel for code coverage experiment.
-      # TODO(crbug.com/1000367): Remove once linux-chromeos-coverage-rel is
-      # folded into linux-chromeos-rel.
-      'linux-chromeos-coverage-rel': 'chromeos_with_codecs_release_trybot_code_coverage',
+      'linux-chromeos-rel': 'chromeos_with_codecs_release_trybot_code_coverage',
       'linux-chromeos-compile-dbg': 'chromeos_with_codecs_debug_bot',
       'linux-chromeos-dbg': 'chromeos_with_codecs_debug_bot',
     },
@@ -1301,6 +1297,7 @@
       'chromeos_with_codecs', 'release_trybot', 'no_symbols',
     ],
 
+    # Keep in sync with chromeos_with_codecs_release_trybot.
     'chromeos_with_codecs_release_trybot_code_coverage': [
       'chromeos_with_codecs', 'release_trybot', 'no_symbols',
       'use_clang_coverage', 'partial_code_coverage_instrumentation',
@@ -1654,6 +1651,8 @@
       'gpu_tests', 'release_trybot', 'no_symbols', 'use_dummy_lastchange',
     ],
 
+    # Keep in sync with
+    # gpu_tests_release_trybot_no_symbols_use_dummy_lastchange.
     'gpu_tests_release_trybot_no_symbols_use_dummy_lastchange_code_coverage': [
       'gpu_tests', 'release_trybot', 'no_symbols', 'use_dummy_lastchange',
       'use_clang_coverage', 'partial_code_coverage_instrumentation',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 00aa5730..81ecc50 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -18131,6 +18131,8 @@
   <int value="611" label="DeviceLoginScreenDictationEnabled"/>
   <int value="612" label="DeviceLoginScreenSelectToSpeakEnabled"/>
   <int value="613" label="DeviceLoginScreenCursorHighlightEnabled"/>
+  <int value="614" label="DeviceLoginScreenCaretHighlightEnabled"/>
+  <int value="615" label="DeviceLoginScreenMonoAudioEnabled"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index d28e464..27a0345 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -36480,12 +36480,12 @@
 </histogram>
 
 <histogram name="Enterprise.LockToSingleUserResult"
-    enum="LockToSingleUserResult" expires_after="M79">
+    enum="LockToSingleUserResult" expires_after="M82">
   <owner>emaxx@chromium.org</owner>
   <owner>igorcov@chromium.org</owner>
   <summary>
     Chrome OS only. The result of D-Bus call to lock device to single user.
-    Estimated that till version M79 we should get enough stats to understand if
+    Estimated that till version M82 we should get enough stats to understand if
     there are big problems with the D-Bus call.
   </summary>
 </histogram>
@@ -94576,54 +94576,63 @@
 
 <histogram name="OOBE.RecommendApps.Fetcher.AppCount" units="units"
     expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>play-bm-eng@google.com</owner>
   <summary>The number of recommended apps.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Fetcher.DownloadTime" units="ms"
     expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>play-bm-eng@google.com</owner>
   <summary>The time it takes to fetch the recommended apps.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Fetcher.ResponseCode"
     enum="HttpResponseCode" expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>chromesky-eng@google.com</owner>
   <summary>The response code of fetching the recommended apps.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Fetcher.ResponseParseResult"
     enum="RecommendAppsResponseParseResult" expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>chromesky-eng@google.com</owner>
   <summary>The result of parsing the recommend-apps response.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Fetcher.ResponseSize" units="KB"
     expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>chromesky-eng@google.com</owner>
   <summary>The size of the recommend-apps JSON response.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Screen.Action"
     enum="RecommendAppsScreenAction" expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>chromesky-eng@google.com</owner>
   <summary>The user action on the recommend apps screen.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Screen.SelectedAppCount" units="units"
     expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>chromesky-eng@google.com</owner>
   <summary>The number of apps user selected.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Screen.SelectedRecommendedPercentage"
     units="%" expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>chromesky-eng@google.com</owner>
   <summary>The ratio of selected and recommended apps.</summary>
 </histogram>
 
 <histogram name="OOBE.RecommendApps.Screen.State"
     enum="RecommendAppsScreenState" expires_after="2019-08-30">
+  <owner>rsorokin@chromium.org</owner>
   <owner>chromesky-eng@google.com</owner>
   <summary>Whether the reocmmend apps screen is shown.</summary>
 </histogram>
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 9d39be4..16e6b337 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -159,7 +159,6 @@
 
   # crbug.com/1008001
   'system_health.memory_desktop/browse:tools:sheets:2019',
-  'system_health.memory_desktop/browse:tools:maps:2019',
 
   # The following tests are disabled because they are disabled on the perf
   # waterfall (using tools/perf/expectations.config) on one platform or another.
diff --git a/tools/perf/core/results_processor/command_line.py b/tools/perf/core/results_processor/command_line.py
index faf113e..b3f305fc 100644
--- a/tools/perf/core/results_processor/command_line.py
+++ b/tools/perf/core/results_processor/command_line.py
@@ -15,14 +15,20 @@
 
 from py_utils import cloud_storage
 
+from core.results_processor import formatters
 
-SUPPORTED_FORMATS = ['none', 'json-test-results']
+
+# These formats are always handled natively, and never handed over to Telemetry.
+HANDLED_NATIVELY = ['none', 'json-test-results']
 
 
 def ArgumentParser(standalone=False, legacy_formats=None):
   """Create an ArgumentParser defining options required by the processor."""
-  all_output_formats = sorted(
-      set(SUPPORTED_FORMATS).union(legacy_formats or ()))
+  if standalone:
+    all_output_formats = formatters.FORMATTERS.keys()
+  else:
+    all_output_formats = sorted(
+        set(HANDLED_NATIVELY).union(legacy_formats or ()))
   parser, group = _CreateTopLevelParser(standalone)
   group.add_argument(
       '--output-format', action='append', dest='output_formats',
@@ -65,7 +71,7 @@
   return parser
 
 
-def ProcessOptions(options):
+def ProcessOptions(options, standalone=False):
   """Adjust result processing options as needed before running benchmarks.
 
   Note: The intended scope of this function is limited to only adjust options
@@ -78,6 +84,8 @@
 
   Args:
     options: An options object with values parsed from the command line.
+    standalone: Whether this is a standalone Results Processor run (as
+      opposed to the run with Telemetry).
   """
   # The output_dir option is None or missing if the selected Telemetry command
   # does not involve output generation, e.g. "run_benchmark list", and the
@@ -116,7 +124,7 @@
   for output_format in chosen_formats:
     if output_format == 'none':
       continue
-    elif output_format in SUPPORTED_FORMATS:
+    elif standalone or output_format in HANDLED_NATIVELY:
       options.output_formats.append(output_format)
     else:
       options.legacy_output_formats.append(output_format)
diff --git a/tools/perf/core/results_processor/command_line_unittest.py b/tools/perf/core/results_processor/command_line_unittest.py
index 2220cd01..f67154c 100644
--- a/tools/perf/core/results_processor/command_line_unittest.py
+++ b/tools/perf/core/results_processor/command_line_unittest.py
@@ -17,7 +17,7 @@
 import mock
 
 from core.results_processor import command_line
-from core.results_processor import processor
+from core.results_processor import formatters
 
 
 # To easily mock module level symbols within the command_line module.
@@ -133,7 +133,7 @@
     with self.assertRaises(SystemExit):
       self.ParseArgs(['--output-format', 'unknown'])
 
-  @mock.patch(module('SUPPORTED_FORMATS'), ['new-format'])
+  @mock.patch(module('HANDLED_NATIVELY'), ['new-format'])
   def testOutputFormatsSplit(self):
     self.legacy_formats = ['old-format']
     options = self.ParseArgs(
@@ -141,7 +141,7 @@
     self.assertEqual(options.output_formats, ['new-format'])
     self.assertEqual(options.legacy_output_formats, ['old-format'])
 
-  @mock.patch(module('SUPPORTED_FORMATS'), ['new-format'])
+  @mock.patch(module('HANDLED_NATIVELY'), ['new-format'])
   def testNoDuplicateOutputFormats(self):
     self.legacy_formats = ['old-format']
     options = self.ParseArgs(
@@ -160,23 +160,22 @@
     with self.assertRaises(SystemExit):
       self.ParseArgs([])
 
-  @mock.patch(module('SUPPORTED_FORMATS'), ['new-format'])
   def testIntermediateDirRequired(self):
     with self.assertRaises(SystemExit):
-      self.ParseArgs(['--output-format', 'new-format'])
+      self.ParseArgs(['--output-format', 'json-test-results'])
 
-  @mock.patch(module('SUPPORTED_FORMATS'), ['new-format'])
   def testSuccessful(self):
     options = self.ParseArgs(
-        ['--output-format', 'new-format', '--intermediate-dir', 'some_dir'])
-    self.assertEqual(options.output_formats, ['new-format'])
+        ['--output-format', 'json-test-results',
+         '--intermediate-dir', 'some_dir'])
+    self.assertEqual(options.output_formats, ['json-test-results'])
     self.assertEqual(options.intermediate_dir, '/path/to/curdir/some_dir')
     self.assertEqual(options.output_dir, '/path/to/output_dir')
 
 
-class TestSupportedFormats(unittest.TestCase):
-  def testAllSupportedFormatsHaveFormatters(self):
-    for output_format in command_line.SUPPORTED_FORMATS:
+class TestNativelyHandledFormats(unittest.TestCase):
+  def testNativelyHandledFormatsHaveFormatters(self):
+    for output_format in command_line.HANDLED_NATIVELY:
       if output_format == 'none':
         continue
-      self.assertIn(output_format, processor.FORMATTERS)
+      self.assertIn(output_format, formatters.FORMATTERS)
diff --git a/tools/perf/core/results_processor/formatters/__init__.py b/tools/perf/core/results_processor/formatters/__init__.py
new file mode 100644
index 0000000..f75ff697
--- /dev/null
+++ b/tools/perf/core/results_processor/formatters/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+from core.results_processor.formatters import histograms_output
+from core.results_processor.formatters import html_output
+from core.results_processor.formatters import json3_output
+
+
+FORMATTERS = {
+    'json-test-results': json3_output,
+    'histograms': histograms_output,
+    'html': html_output,
+}
diff --git a/tools/perf/core/results_processor/histograms_output.py b/tools/perf/core/results_processor/formatters/histograms_output.py
similarity index 100%
rename from tools/perf/core/results_processor/histograms_output.py
rename to tools/perf/core/results_processor/formatters/histograms_output.py
diff --git a/tools/perf/core/results_processor/histograms_output_unittest.py b/tools/perf/core/results_processor/formatters/histograms_output_unittest.py
similarity index 98%
rename from tools/perf/core/results_processor/histograms_output_unittest.py
rename to tools/perf/core/results_processor/formatters/histograms_output_unittest.py
index b3ea74d..7b4cd36 100644
--- a/tools/perf/core/results_processor/histograms_output_unittest.py
+++ b/tools/perf/core/results_processor/formatters/histograms_output_unittest.py
@@ -9,7 +9,7 @@
 import shutil
 import tempfile
 
-from core.results_processor import histograms_output
+from core.results_processor.formatters import histograms_output
 from core.results_processor import command_line
 from core.results_processor import testing
 
diff --git a/tools/perf/core/results_processor/html_output.py b/tools/perf/core/results_processor/formatters/html_output.py
similarity index 93%
rename from tools/perf/core/results_processor/html_output.py
rename to tools/perf/core/results_processor/formatters/html_output.py
index dff1de9..a8f3cab 100644
--- a/tools/perf/core/results_processor/html_output.py
+++ b/tools/perf/core/results_processor/formatters/html_output.py
@@ -7,7 +7,7 @@
 import codecs
 import os
 
-from core.results_processor import histograms_output
+from core.results_processor.formatters import histograms_output
 from tracing_build import vulcanize_histograms_viewer
 
 
diff --git a/tools/perf/core/results_processor/json3_output.py b/tools/perf/core/results_processor/formatters/json3_output.py
similarity index 100%
rename from tools/perf/core/results_processor/json3_output.py
rename to tools/perf/core/results_processor/formatters/json3_output.py
diff --git a/tools/perf/core/results_processor/json3_output_unittest.py b/tools/perf/core/results_processor/formatters/json3_output_unittest.py
similarity index 98%
rename from tools/perf/core/results_processor/json3_output_unittest.py
rename to tools/perf/core/results_processor/formatters/json3_output_unittest.py
index 3c9b118d..297ca30e 100644
--- a/tools/perf/core/results_processor/json3_output_unittest.py
+++ b/tools/perf/core/results_processor/formatters/json3_output_unittest.py
@@ -5,7 +5,7 @@
 import copy
 import unittest
 
-from core.results_processor import json3_output
+from core.results_processor.formatters import json3_output
 from core.results_processor import testing
 
 
diff --git a/tools/perf/core/results_processor/processor.py b/tools/perf/core/results_processor/processor.py
index 3cf53cb..c0874b7 100644
--- a/tools/perf/core/results_processor/processor.py
+++ b/tools/perf/core/results_processor/processor.py
@@ -12,18 +12,11 @@
 import os
 
 from core.results_processor import command_line
-from core.results_processor import json3_output
-from core.results_processor import histograms_output
-from core.results_processor import html_output
+from core.results_processor import formatters
 
 
 HTML_TRACE_NAME = 'trace.html'
 TELEMETRY_RESULTS = '_telemetry_results.jsonl'
-FORMATTERS = {
-    'json-test-results': json3_output,
-    'histograms': histograms_output,
-    'html': html_output,
-}
 
 
 def ProcessResults(options):
@@ -48,10 +41,10 @@
   _UploadArtifacts(intermediate_results, options.upload_bucket)
 
   for output_format in options.output_formats:
-    if output_format not in FORMATTERS:
+    if output_format not in formatters.FORMATTERS:
       raise NotImplementedError(output_format)
 
-    formatter = FORMATTERS[output_format]
+    formatter = formatters.FORMATTERS[output_format]
     formatter.Process(intermediate_results, options)
 
 
@@ -106,5 +99,5 @@
   """Entry point for the standalone version of the results_processor script."""
   parser = command_line.ArgumentParser(standalone=True)
   options = parser.parse_args(args)
-  command_line.ProcessOptions(options)
+  command_line.ProcessOptions(options, standalone=True)
   return ProcessResults(options)
diff --git a/tools/perf/core/results_processor/processor_test.py b/tools/perf/core/results_processor/processor_test.py
index 05adfd8b..f2a802c 100644
--- a/tools/perf/core/results_processor/processor_test.py
+++ b/tools/perf/core/results_processor/processor_test.py
@@ -15,11 +15,9 @@
 import tempfile
 import unittest
 
-import mock
-
-from core.results_processor import json3_output
-from core.results_processor import histograms_output
-from core.results_processor import html_output
+from core.results_processor.formatters import json3_output
+from core.results_processor.formatters import histograms_output
+from core.results_processor.formatters import html_output
 from core.results_processor import processor
 from core.results_processor import testing
 
@@ -105,10 +103,6 @@
     self.assertEqual(artifacts['logs'], ['gs://logs.txt'])
     self.assertEqual(artifacts['trace.html'], ['gs://trace.html'])
 
-  # TODO(crbug.com/981349): Remove this mock when histograms format
-  # is enabled in results_processor.
-  @mock.patch('core.results_processor.command_line.SUPPORTED_FORMATS',
-              ['histograms'])
   def testHistogramsOutput(self):
     hist_file = os.path.join(self.output_dir,
                              histograms_output.HISTOGRAM_DICTS_NAME)
@@ -154,10 +148,6 @@
     self.assertIn([['documentation', 'url']], diag_values)
     self.assertIn(['label'], diag_values)
 
-  # TODO(crbug.com/981349): Remove this mock when histograms format
-  # is enabled in results_processor.
-  @mock.patch('core.results_processor.command_line.SUPPORTED_FORMATS',
-              ['histograms'])
   def testHistogramsOutputResetResults(self):
     hist_file = os.path.join(self.output_dir,
                              histograms_output.HISTOGRAM_DICTS_NAME)
@@ -199,10 +189,6 @@
     self.assertNotIn(['label1'], diag_values)
     self.assertIn(['label2'], diag_values)
 
-  # TODO(crbug.com/981349): Remove this mock when histograms format
-  # is enabled in results_processor.
-  @mock.patch('core.results_processor.command_line.SUPPORTED_FORMATS',
-              ['histograms'])
   def testHistogramsOutputAppendResults(self):
     hist_file = os.path.join(self.output_dir,
                              histograms_output.HISTOGRAM_DICTS_NAME)
@@ -243,10 +229,6 @@
     self.assertIn(['label1'], diag_values)
     self.assertIn(['label2'], diag_values)
 
-  # TODO(crbug.com/981349): Remove this mock when html format
-  # is enabled in results_processor.
-  @mock.patch('core.results_processor.command_line.SUPPORTED_FORMATS',
-              ['html'])
   def testHtmlOutput(self):
     hist_file = os.path.join(self.output_dir,
                              histograms_output.HISTOGRAM_DICTS_NAME)
@@ -292,10 +274,6 @@
     self.assertIn([['documentation', 'url']], diag_values)
     self.assertIn(['label'], diag_values)
 
-  # TODO(crbug.com/981349): Remove this mock when html format
-  # is enabled in results_processor.
-  @mock.patch('core.results_processor.command_line.SUPPORTED_FORMATS',
-              ['html'])
   def testHtmlOutputResetResults(self):
     self.SerializeIntermediateResults([])
 
@@ -324,10 +302,6 @@
     self.assertNotIn(['label1'], diag_values)
     self.assertIn(['label2'], diag_values)
 
-  # TODO(crbug.com/981349): Remove this mock when html format
-  # is enabled in results_processor.
-  @mock.patch('core.results_processor.command_line.SUPPORTED_FORMATS',
-              ['html'])
   def testHtmlOutputAppendResults(self):
     self.SerializeIntermediateResults([])
 
diff --git a/tools/perf/results_processor b/tools/perf/results_processor
index ce11f3b..0cc14995 100755
--- a/tools/perf/results_processor
+++ b/tools/perf/results_processor
@@ -7,6 +7,7 @@
 
 from core import path_util
 path_util.AddPyUtilsToPath()
+path_util.AddTracingToPath()
 
 from core import results_processor
 
diff --git a/ui/android/java/src/org/chromium/ui/widget/Toast.java b/ui/android/java/src/org/chromium/ui/widget/Toast.java
index 7ae15c5..b0cb5202 100644
--- a/ui/android/java/src/org/chromium/ui/widget/Toast.java
+++ b/ui/android/java/src/org/chromium/ui/widget/Toast.java
@@ -12,6 +12,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.os.Build;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -168,4 +169,36 @@
     public static void setGlobalExtraYOffset(int yOffsetPx) {
         sExtraYOffset = yOffsetPx;
     }
+
+    /**
+     * Shows a toast anchored on a view.
+     * @param context The context to use for the toast.
+     * @param view The view to anchor the toast.
+     * @param description The string shown in the toast.
+     * @return Whether a toast has been shown successfully.
+     */
+    @SuppressLint("RtlHardcoded")
+    public static boolean showAnchoredToast(Context context, View view, CharSequence description) {
+        if (description == null) return false;
+
+        final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+        final int screenHeight = context.getResources().getDisplayMetrics().heightPixels;
+        final int[] screenPos = new int[2];
+        view.getLocationOnScreen(screenPos);
+        final int width = view.getWidth();
+        final int height = view.getHeight();
+
+        final int horizontalGravity =
+                (screenPos[0] < screenWidth / 2) ? Gravity.LEFT : Gravity.RIGHT;
+        final int xOffset = (screenPos[0] < screenWidth / 2)
+                ? screenPos[0] + width / 2
+                : screenWidth - screenPos[0] - width / 2;
+        final int yOffset = (screenPos[1] < screenHeight / 2) ? screenPos[1] + height / 2
+                                                              : screenPos[1] - height * 3 / 2;
+
+        Toast toast = Toast.makeText(context, description, Toast.LENGTH_SHORT);
+        toast.setGravity(Gravity.TOP | horizontalGravity, xOffset, yOffset);
+        toast.show();
+        return true;
+    }
 }
diff --git a/ui/android/view_android.h b/ui/android/view_android.h
index ccd504e..c8eefde 100644
--- a/ui/android/view_android.h
+++ b/ui/android/view_android.h
@@ -163,7 +163,7 @@
   void OnBottomControlsChanged(float bottom_controls_offset,
                                float bottom_content_offset);
 
-  // Gets the Visual Viewport inset to apply.
+  // Gets the Visual Viewport inset to apply in physical pixels.
   int GetViewportInsetBottom();
 
   ScopedAnchorView AcquireAnchorView();
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 0436f7fa..b11bad9 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -25,6 +25,7 @@
 #include "cc/base/switches.h"
 #include "cc/input/input_handler.h"
 #include "cc/layers/layer.h"
+#include "cc/metrics/begin_main_frame_metrics.h"
 #include "cc/trees/latency_info_swap_promise.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
@@ -626,6 +627,11 @@
     observer.OnCompositingDidCommit(this);
 }
 
+std::unique_ptr<cc::BeginMainFrameMetrics>
+Compositor::GetBeginMainFrameMetrics() {
+  return nullptr;
+}
+
 void Compositor::DidReceiveCompositorFrameAck() {
   ++activated_frame_count_;
   for (auto& observer : observer_list_)
@@ -661,7 +667,7 @@
   NOTREACHED();
 }
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 void Compositor::OnCompleteSwapWithNewSize(const gfx::Size& size) {
   for (auto& observer : observer_list_)
     observer.OnCompositingCompleteSwapWithNewSize(this, size);
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index d8d1fbb..e5b62b3 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -400,6 +400,8 @@
       const gfx::PresentationFeedback& feedback) override;
   void RecordStartOfFrameMetrics() override {}
   void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override {}
+  std::unique_ptr<cc::BeginMainFrameMetrics> GetBeginMainFrameMetrics()
+      override;
 
   // cc::LayerTreeHostSingleThreadClient implementation.
   void DidSubmitCompositorFrame() override;
@@ -410,7 +412,7 @@
   void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
   void OnFrameTokenChanged(uint32_t frame_token) override;
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   void OnCompleteSwapWithNewSize(const gfx::Size& size);
 #endif
 
diff --git a/ui/compositor/compositor_observer.h b/ui/compositor/compositor_observer.h
index 62d9da2d..501aa3d 100644
--- a/ui/compositor/compositor_observer.h
+++ b/ui/compositor/compositor_observer.h
@@ -6,6 +6,7 @@
 #define UI_COMPOSITOR_COMPOSITOR_OBSERVER_H_
 
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "ui/compositor/compositor_export.h"
 
 namespace gfx {
@@ -41,11 +42,11 @@
   // Called when a child of the compositor is resizing.
   virtual void OnCompositingChildResizing(Compositor* compositor) {}
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   // Called when a swap with new size is completed.
   virtual void OnCompositingCompleteSwapWithNewSize(ui::Compositor* compositor,
                                                     const gfx::Size& size) {}
-#endif
+#endif  // defined(OS_LINUX)
 
   // Called at the top of the compositor's destructor, to give observers a
   // chance to remove themselves.
diff --git a/ui/compositor/host/host_context_factory_private.cc b/ui/compositor/host/host_context_factory_private.cc
index bf8d8d2..820b70f 100644
--- a/ui/compositor/host/host_context_factory_private.cc
+++ b/ui/compositor/host/host_context_factory_private.cc
@@ -29,7 +29,7 @@
 
 static const char* kBrowser = "Browser";
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 class HostDisplayClient : public viz::HostDisplayClient {
  public:
   explicit HostDisplayClient(ui::Compositor* compositor)
diff --git a/ui/gfx/font.cc b/ui/gfx/font.cc
index a3f46a86..21367fd7 100644
--- a/ui/gfx/font.cc
+++ b/ui/gfx/font.cc
@@ -100,4 +100,21 @@
 }
 #endif
 
+Font::Weight FontWeightFromInt(int weight) {
+  static const Font::Weight weights[] = {
+      Font::Weight::INVALID,  Font::Weight::THIN,   Font::Weight::EXTRA_LIGHT,
+      Font::Weight::LIGHT,    Font::Weight::NORMAL, Font::Weight::MEDIUM,
+      Font::Weight::SEMIBOLD, Font::Weight::BOLD,   Font::Weight::EXTRA_BOLD,
+      Font::Weight::BLACK};
+
+  const Font::Weight* next_bigger_weight = std::lower_bound(
+      std::begin(weights), std::end(weights), weight,
+      [](const Font::Weight& a, const int& b) {
+        return static_cast<std::underlying_type<Font::Weight>::type>(a) < b;
+      });
+  if (next_bigger_weight != std::end(weights))
+    return *next_bigger_weight;
+  return Font::Weight::INVALID;
+}
+
 }  // namespace gfx
diff --git a/ui/gfx/font.h b/ui/gfx/font.h
index 0f71876..6d53975 100644
--- a/ui/gfx/font.h
+++ b/ui/gfx/font.h
@@ -140,6 +140,9 @@
                                     const Font::Weight weight);
 #endif
 
+// Returns the Font::Weight that matches |weight| or the next bigger one.
+GFX_EXPORT Font::Weight FontWeightFromInt(int weight);
+
 }  // namespace gfx
 
 #endif  // UI_GFX_FONT_H_
diff --git a/ui/gfx/font_fallback_skia.cc b/ui/gfx/font_fallback_skia.cc
index 2a87842..03bf25e 100644
--- a/ui/gfx/font_fallback_skia.cc
+++ b/ui/gfx/font_fallback_skia.cc
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_fallback_skia_impl.h"
+#include "ui/gfx/platform_font.h"
 
 namespace gfx {
 
@@ -28,14 +29,18 @@
   if (text.empty())
     return false;
 
-  std::string skia_family_name =
-      GetFallbackFontFamilyNameSkia(font, locale, text);
+  sk_sp<SkTypeface> fallback_typeface =
+      GetSkiaFallbackTypeface(font, locale, text);
 
-  if (skia_family_name.empty())
+  if (!fallback_typeface)
     return false;
 
-  *result = Font(std::string(skia_family_name.c_str(), skia_family_name.size()),
-                 font.GetFontSize());
+  // Fallback needs to keep the exact SkTypeface, as re-matching the font using
+  // family name and styling information loses access to the underlying platform
+  // font handles and is not guaranteed to result in the correct typeface, see
+  // https://crbug.com/1003829
+  *result = Font(PlatformFont::CreateFromSkTypeface(
+      std::move(fallback_typeface), font.GetFontSize()));
   return true;
 }
 
diff --git a/ui/gfx/font_fallback_skia_impl.cc b/ui/gfx/font_fallback_skia_impl.cc
index 11870a6..2000d3fa 100644
--- a/ui/gfx/font_fallback_skia_impl.cc
+++ b/ui/gfx/font_fallback_skia_impl.cc
@@ -14,11 +14,11 @@
 
 namespace gfx {
 
-std::string GetFallbackFontFamilyNameSkia(const Font& template_font,
+sk_sp<SkTypeface> GetSkiaFallbackTypeface(const Font& template_font,
                                           const std::string& locale,
                                           base::StringPiece16 text) {
   if (text.empty())
-    return std::string();
+    return nullptr;
 
   sk_sp<SkFontMgr> font_mgr(SkFontMgr::RefDefault());
 
@@ -35,7 +35,7 @@
       italic ? SkFontStyle::kItalic_Slant : SkFontStyle::kUpright_Slant);
 
   std::set<SkFontID> tested_typeface;
-  SkString skia_family_name;
+  sk_sp<SkTypeface> fallback_typeface;
   size_t fewest_missing_glyphs = text.length() + 1;
 
   size_t offset = 0;
@@ -62,7 +62,7 @@
 
     if (missing_glyphs < fewest_missing_glyphs) {
       fewest_missing_glyphs = missing_glyphs;
-      typeface->getFamilyName(&skia_family_name);
+      fallback_typeface = typeface;
     }
 
     // The font is a valid fallback font for the given text.
@@ -70,7 +70,7 @@
       break;
   }
 
-  return skia_family_name.c_str();
+  return fallback_typeface;
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/font_fallback_skia_impl.h b/ui/gfx/font_fallback_skia_impl.h
index 2284c780..1908dee 100644
--- a/ui/gfx/font_fallback_skia_impl.h
+++ b/ui/gfx/font_fallback_skia_impl.h
@@ -10,9 +10,12 @@
 #include "base/strings/string_piece.h"
 #include "ui/gfx/font.h"
 
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+
 namespace gfx {
 
-std::string GetFallbackFontFamilyNameSkia(const Font& template_font,
+sk_sp<SkTypeface> GetSkiaFallbackTypeface(const Font& template_font,
                                           const std::string& locale,
                                           base::StringPiece16 text);
 }
diff --git a/ui/gfx/font_fallback_win.cc b/ui/gfx/font_fallback_win.cc
index 65d96cb0..716badf 100644
--- a/ui/gfx/font_fallback_win.cc
+++ b/ui/gfx/font_fallback_win.cc
@@ -20,6 +20,7 @@
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_fallback.h"
 #include "ui/gfx/font_fallback_skia_impl.h"
+#include "ui/gfx/platform_font.h"
 
 namespace gfx {
 
@@ -250,13 +251,18 @@
   if (text.find(kNulCharacter) != base::StringPiece16::npos)
     return false;
 
-  std::string skia_fallback_family =
-      GetFallbackFontFamilyNameSkia(font, locale, text);
+  sk_sp<SkTypeface> fallback_typeface =
+      GetSkiaFallbackTypeface(font, locale, text);
 
-  if (skia_fallback_family.empty())
+  if (!fallback_typeface)
     return false;
 
-  *result = Font(skia_fallback_family, font.GetFontSize());
+  // Fallback needs to keep the exact SkTypeface, as re-matching the font using
+  // family name and styling information loses access to the underlying platform
+  // font handles and is not guaranteed to result in the correct typeface, see
+  // https://crbug.com/1003829
+  *result = Font(PlatformFont::CreateFromSkTypeface(
+      std::move(fallback_typeface), font.GetFontSize()));
   return true;
 }
 
diff --git a/ui/gfx/font_unittest.cc b/ui/gfx/font_unittest.cc
index 0aeae3c..399fea2 100644
--- a/ui/gfx/font_unittest.cc
+++ b/ui/gfx/font_unittest.cc
@@ -161,5 +161,20 @@
 }
 #endif  // defined(OS_WIN)
 
+TEST_F(FontTest, WeightConversion) {
+  struct WeightMatchExpectation {
+    int weight;
+    Font::Weight enum_value;
+  } expectations[] = {
+      {-10, Font::Weight::INVALID}, {-1, Font::Weight::INVALID},
+      {0, Font::Weight::THIN},      {1, Font::Weight::THIN},
+      {100, Font::Weight::THIN},    {350, Font::Weight::NORMAL},
+      {400, Font::Weight::NORMAL},  {899, Font::Weight::BLACK},
+      {900, Font::Weight::BLACK},   {901, Font::Weight::INVALID}};
+  for (const auto& expectation : expectations) {
+    EXPECT_EQ(FontWeightFromInt(expectation.weight), expectation.enum_value);
+  }
+}
+
 }  // namespace
 }  // namespace gfx
diff --git a/ui/gfx/platform_font.h b/ui/gfx/platform_font.h
index 7bdcf2505..8a2b375 100644
--- a/ui/gfx/platform_font.h
+++ b/ui/gfx/platform_font.h
@@ -39,6 +39,15 @@
   static PlatformFont* CreateFromNameAndSize(const std::string& font_name,
                                              int font_size);
 
+  // Creates a PlatformFont instance from the provided SkTypeface, ideally by
+  // just wrapping it without triggering a new font match. Implemented for
+  // PlatformFontWin and PlatformFontSkia, where only the latter provides true
+  // wrapping of the provided SkTypeface, while PlatformFontWin creates a
+  // PlatformFont object by extracting the family name and falls back to
+  // CreateFromNameAndSize().
+  static PlatformFont* CreateFromSkTypeface(sk_sp<SkTypeface> typeface,
+                                            int size);
+
   // Returns a new Font derived from the existing font.
   // |size_delta| is the size in pixels to add to the current font.
   // The style parameter specifies the new style for the font, and is a
diff --git a/ui/gfx/platform_font_skia.cc b/ui/gfx/platform_font_skia.cc
index bf25331c..64cb1ea1 100644
--- a/ui/gfx/platform_font_skia.cc
+++ b/ui/gfx/platform_font_skia.cc
@@ -292,6 +292,27 @@
                   render_params);
 }
 
+PlatformFontSkia::PlatformFontSkia(sk_sp<SkTypeface> typeface, int size) {
+  TRACE_EVENT0("fonts", "PlatformFontSkia::PlatformFontSkia (from typeface)");
+  DCHECK(typeface);
+
+  SkString family_name;
+  typeface->getFamilyName(&family_name);
+
+  SkFontStyle font_style = typeface->fontStyle();
+  Font::Weight font_weight = FontWeightFromInt(font_style.weight());
+
+  FontRenderParamsQuery query;
+  query.families.push_back(family_name.c_str());
+  query.pixel_size = size;
+  query.weight = font_weight;
+
+  int style = typeface->isItalic() ? Font::ITALIC : Font::NORMAL;
+
+  InitFromDetails(std::move(typeface), family_name.c_str(), size, style,
+                  font_weight, gfx::GetFontRenderParams(query, nullptr));
+}
+
 PlatformFontSkia::~PlatformFontSkia() {}
 
 void PlatformFontSkia::InitFromDetails(sk_sp<SkTypeface> typeface,
@@ -426,6 +447,13 @@
   TRACE_EVENT0("fonts", "PlatformFont::CreateFromNameAndSize");
   return new PlatformFontSkia(font_name, font_size);
 }
+
+// static
+PlatformFont* PlatformFont::CreateFromSkTypeface(sk_sp<SkTypeface> typeface,
+                                                 int font_size_pixels) {
+  TRACE_EVENT0("fonts", "PlatformFont::CreateFromSkTypeface");
+  return new PlatformFontSkia(typeface, font_size_pixels);
+}
 #endif  // !defined(OS_WIN)
 
 }  // namespace gfx
diff --git a/ui/gfx/platform_font_skia.h b/ui/gfx/platform_font_skia.h
index 4615a77..9456af32 100644
--- a/ui/gfx/platform_font_skia.h
+++ b/ui/gfx/platform_font_skia.h
@@ -25,6 +25,9 @@
   PlatformFontSkia();
   PlatformFontSkia(const std::string& font_name, int font_size_pixels);
 
+  // Wraps the provided SkTypeface without triggering a font rematch.
+  PlatformFontSkia(sk_sp<SkTypeface> typeface, int font_size_pixels);
+
   // Initials the default PlatformFont. Returns true if this is successful, or
   // false if fonts resources are not available. If this returns false, the
   // calling service should shut down.
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
index a9963ab..0acdf331 100644
--- a/ui/gfx/platform_font_win.cc
+++ b/ui/gfx/platform_font_win.cc
@@ -649,4 +649,22 @@
   return new PlatformFontWin(font_name, font_size);
 }
 
+// static
+PlatformFont* PlatformFont::CreateFromSkTypeface(sk_sp<SkTypeface> typeface,
+                                                 int size) {
+  TRACE_EVENT0("fonts", "PlatformFont::CreateFromSkTypeface");
+  if (base::FeatureList::IsEnabled(kPlatformFontSkiaOnWindows))
+    return new PlatformFontSkia(typeface, size);
+
+  // This is a transitional code path until we complete migrating to
+  // PlatformFontSkia on Windows. Being unable to wrap the SkTypeface into a
+  // PlatformFontSkia and performing a rematching by font family name instead
+  // loses platform font handles encapsulated in SkTypeface, and in turn leads
+  // to instantiating a different font than what was returned by font fallback,
+  // compare https://crbug.com/1003829.
+  SkString family_name;
+  typeface->getFamilyName(&family_name);
+  return new PlatformFontWin(family_name.c_str(), size);
+}
+
 }  // namespace gfx
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index f3a9602..d72f89a 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -304,7 +304,7 @@
      */
     setShelfHeight: function(height) {
       document.documentElement.style.setProperty(
-          '--shelf-area-height', height + 'px');
+          '--shelf-area-height-base', height + 'px');
     },
 
     /**
@@ -747,13 +747,36 @@
      * @param {!HTMLElement} screen Screen that is being shown.
      */
     updateScreenSize: function(screen) {
+      if (!cr.isChromeOS) {
+        // Have to reset any previously predefined screen size first
+        // so that screen contents would define it instead.
+        $('inner-container').style.height = '';
+        $('inner-container').style.width = '';
+        screen.style.width = '';
+        screen.style.height = '';
+      }
+
       $('outer-container').classList.toggle(
         'fullscreen', screen.classList.contains('fullscreen'));
 
+      var width = screen.getPreferredSize().width;
+      var height = screen.getPreferredSize().height;
+
+      if (!cr.isChromeOS) {
+        if (screen.classList.contains('fullscreen')) {
+          $('inner-container').style.height = '100%';
+          $('inner-container').style.width = '100%';
+        } else {
+          $('inner-container').style.height = height + 'px';
+          $('inner-container').style.width = width + 'px';
+        }
+        // This requires |screen| to have 'box-sizing: border-box'.
+        screen.style.width = width + 'px';
+        screen.style.height = height + 'px';
+        screen.style.margin = 'auto';
+      }
 
       if (this.showingViewsLogin) {
-        var width = screen.getPreferredSize().width;
-        var height = screen.getPreferredSize().height;
         chrome.send('updateOobeDialogSize', [width, height]);
         $('scroll-container').classList.toggle('disable-scroll', true);
         $('inner-container').classList.toggle('disable-scroll', true);
diff --git a/ui/login/oobe.css b/ui/login/oobe.css
index d7fa837..7e028d9 100644
--- a/ui/login/oobe.css
+++ b/ui/login/oobe.css
@@ -8,7 +8,7 @@
 
 :root {
   --google-grey-700: rgb(95, 99, 104);
-  --shelf-area-height: 57px;
+  --shelf-area-height-base: 57px;
 }
 
 html,
@@ -17,6 +17,14 @@
   width: 100%;
 }
 
+html {
+  --shelf-area-height: var(--shelf-area-height-base);
+}
+
+html[screen=gaia-signin] {
+  --shelf-area-height: 0;
+}
+
 body {
   background-color: transparent;
   cursor: default;
diff --git a/ui/ozone/demo/demo_window.h b/ui/ozone/demo/demo_window.h
index 9514366f..e0e7871 100644
--- a/ui/ozone/demo/demo_window.h
+++ b/ui/ozone/demo/demo_window.h
@@ -10,12 +10,12 @@
 #include "base/memory/weak_ptr.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
 
 namespace ui {
 
 class Event;
-class PlatformWindow;
 class Renderer;
 class RendererFactory;
 class WindowManager;
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index 33a9467..2f12f9d 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -14,6 +14,7 @@
 #include "base/message_loop/message_pump_type.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 #include "ui/gfx/buffer_types.h"
+#include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_delegate.h"
 
 namespace display {
@@ -31,7 +32,6 @@
 class GpuPlatformSupportHost;
 class OverlayManagerOzone;
 class PlatformScreen;
-class PlatformWindow;
 class SurfaceFactoryOzone;
 class SystemInputInjector;
 class PlatformClipboard;
diff --git a/ui/platform_window/BUILD.gn b/ui/platform_window/BUILD.gn
index 8330427..d826a8a 100644
--- a/ui/platform_window/BUILD.gn
+++ b/ui/platform_window/BUILD.gn
@@ -6,8 +6,9 @@
 
 source_set("platform_window") {
   sources = [
-    "platform_window.cc",
     "platform_window.h",
+    "platform_window_base.cc",
+    "platform_window_base.h",
     "platform_window_delegate.h",
     "platform_window_delegate_base.cc",
     "platform_window_delegate_base.h",
@@ -39,6 +40,8 @@
     sources += [
       "platform_window_delegate_linux.cc",
       "platform_window_delegate_linux.h",
+      "platform_window_linux.cc",
+      "platform_window_linux.h",
     ]
   }
 }
diff --git a/ui/platform_window/platform_window.cc b/ui/platform_window/platform_window.cc
deleted file mode 100644
index 3b52261..0000000
--- a/ui/platform_window/platform_window.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2019 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/platform_window/platform_window.h"
-
-namespace ui {
-
-PlatformWindow::PlatformWindow() = default;
-
-PlatformWindow::~PlatformWindow() = default;
-
-bool PlatformWindow::ShouldWindowContentsBeTransparent() const {
-  return false;
-}
-
-void PlatformWindow::SetZOrderLevel(ZOrderLevel order) {}
-
-ZOrderLevel PlatformWindow::GetZOrderLevel() const {
-  return ZOrderLevel::kNormal;
-}
-
-void PlatformWindow::StackAbove(gfx::AcceleratedWidget widget) {}
-
-void PlatformWindow::StackAtTop() {}
-
-}  // namespace ui
diff --git a/ui/platform_window/platform_window.h b/ui/platform_window/platform_window.h
index 113ddef..aa0b4df 100644
--- a/ui/platform_window/platform_window.h
+++ b/ui/platform_window/platform_window.h
@@ -1,102 +1,27 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2019 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_PLATFORM_WINDOW_PLATFORM_WINDOW_H_
 #define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_H_
 
-#include <memory>
+#include "build/build_config.h"
 
-#include "base/strings/string16.h"
-#include "ui/base/class_property.h"
-#include "ui/base/cursor/cursor.h"
-#include "ui/base/ui_base_types.h"
-#include "ui/gfx/native_widget_types.h"
-#include "ui/platform_window/platform_window_delegate.h"
-
-namespace gfx {
-class Point;
-class Rect;
-}  // namespace gfx
+// By default, PlatformWindowBase is used. However, different platforms
+// should specify what window they would like to use if needed.
+#if defined(OS_LINUX)
+#include "ui/platform_window/platform_window_linux.h"
+#else
+#include "ui/platform_window/platform_window_base.h"
+#endif
 
 namespace ui {
 
-// Platform window.
-//
-// Each instance of PlatformWindow represents a single window in the
-// underlying platform windowing system (i.e. X11/Win/OSX).
-class PlatformWindow : public PropertyHandler {
- public:
-  PlatformWindow();
-  ~PlatformWindow() override;
-
-  // PlatformWindow maybe called with the |inactive| set to true in some cases.
-  // That means that the Window Manager must not activate the window when it is
-  // shown. Most of PlatformWindow may ignore this value if not supported.
-  virtual void Show(bool inactive = false) = 0;
-  virtual void Hide() = 0;
-  virtual void Close() = 0;
-
-  virtual bool IsVisible() const = 0;
-
-  // Informs the window it is going to be destroyed sometime soon. This is only
-  // called for specific code paths, for example by Ash, so it shouldn't be
-  // assumed this will get called before destruction.
-  virtual void PrepareForShutdown() = 0;
-
-  // Sets and gets the bounds of the platform-window. Note that the bounds is in
-  // physical pixel coordinates.
-  virtual void SetBounds(const gfx::Rect& bounds) = 0;
-  virtual gfx::Rect GetBounds() = 0;
-
-  virtual void SetTitle(const base::string16& title) = 0;
-
-  virtual void SetCapture() = 0;
-  virtual void ReleaseCapture() = 0;
-  virtual bool HasCapture() const = 0;
-
-  virtual void ToggleFullscreen() = 0;
-  virtual void Maximize() = 0;
-  virtual void Minimize() = 0;
-  virtual void Restore() = 0;
-  virtual PlatformWindowState GetPlatformWindowState() const = 0;
-
-  virtual void Activate() = 0;
-  virtual void Deactivate() = 0;
-
-  // Sets whether the window should have the standard title bar provided by the
-  // underlying windowing system.  For the main browser window, this may be
-  // changed by the user at any time via 'Show system title bar' option in the
-  // tab strip menu.
-  virtual void SetUseNativeFrame(bool use_native_frame) = 0;
-
-  virtual void SetCursor(PlatformCursor cursor) = 0;
-
-  // Moves the cursor to |location|. Location is in platform window coordinates.
-  virtual void MoveCursorTo(const gfx::Point& location) = 0;
-
-  // Confines the cursor to |bounds| when it is in the platform window. |bounds|
-  // is in platform window coordinates.
-  virtual void ConfineCursorToBounds(const gfx::Rect& bounds) = 0;
-
-  // Sets and gets the restored bounds of the platform-window.
-  virtual void SetRestoredBoundsInPixels(const gfx::Rect& bounds) = 0;
-  virtual gfx::Rect GetRestoredBoundsInPixels() const = 0;
-
-  // Tells if the content of the platform window should be transparent. By
-  // default returns false.
-  virtual bool ShouldWindowContentsBeTransparent() const;
-
-  // Sets and gets ZOrderLevel of the PlatformWindow. Such platforms that do not
-  // support ordering, should not implement these methods as the default
-  // implementation always returns ZOrderLevel::kNormal value.
-  virtual void SetZOrderLevel(ZOrderLevel order);
-  virtual ZOrderLevel GetZOrderLevel() const;
-
-  // Asks the PlatformWindow to stack itself on top of |widget|.
-  virtual void StackAbove(gfx::AcceleratedWidget widget);
-  virtual void StackAtTop();
-};
+#if defined(OS_LINUX)
+using PlatformWindow = PlatformWindowLinux;
+#else
+using PlatformWindow = PlatformWindowBase;
+#endif
 
 }  // namespace ui
 
diff --git a/ui/platform_window/platform_window_base.cc b/ui/platform_window/platform_window_base.cc
new file mode 100644
index 0000000..23a4e70
--- /dev/null
+++ b/ui/platform_window/platform_window_base.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 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/platform_window/platform_window_base.h"
+
+namespace ui {
+
+PlatformWindowBase::PlatformWindowBase() = default;
+
+PlatformWindowBase::~PlatformWindowBase() = default;
+
+bool PlatformWindowBase::ShouldWindowContentsBeTransparent() const {
+  return false;
+}
+
+void PlatformWindowBase::SetZOrderLevel(ZOrderLevel order) {}
+
+ZOrderLevel PlatformWindowBase::GetZOrderLevel() const {
+  return ZOrderLevel::kNormal;
+}
+
+void PlatformWindowBase::StackAbove(gfx::AcceleratedWidget widget) {}
+
+void PlatformWindowBase::StackAtTop() {}
+
+}  // namespace ui
diff --git a/ui/platform_window/platform_window_base.h b/ui/platform_window/platform_window_base.h
new file mode 100644
index 0000000..a19254d1
--- /dev/null
+++ b/ui/platform_window/platform_window_base.h
@@ -0,0 +1,103 @@
+// 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.
+
+#ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_BASE_H_
+#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_BASE_H_
+
+#include <memory>
+
+#include "base/strings/string16.h"
+#include "ui/base/class_property.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace gfx {
+class Point;
+class Rect;
+}  // namespace gfx
+
+namespace ui {
+
+// Platform window.
+//
+// Each instance of PlatformWindowBase represents a single window in the
+// underlying platform windowing system (i.e. X11/Win/OSX).
+class PlatformWindowBase : public PropertyHandler {
+ public:
+  PlatformWindowBase();
+  ~PlatformWindowBase() override;
+
+  // PlatformWindow may be called with the |inactive| set to true in some cases.
+  // That means that the Window Manager must not activate the window when it is
+  // shown.  Most of PlatformWindow may ignore this value if not supported.
+  virtual void Show(bool inactive = false) = 0;
+  virtual void Hide() = 0;
+  virtual void Close() = 0;
+
+  virtual bool IsVisible() const = 0;
+
+  // Informs the window it is going to be destroyed sometime soon. This is only
+  // called for specific code paths, for example by Ash, so it shouldn't be
+  // assumed this will get called before destruction.
+  virtual void PrepareForShutdown() = 0;
+
+  // Sets and gets the bounds of the platform-window. Note that the bounds is in
+  // physical pixel coordinates.
+  virtual void SetBounds(const gfx::Rect& bounds) = 0;
+  virtual gfx::Rect GetBounds() = 0;
+
+  virtual void SetTitle(const base::string16& title) = 0;
+
+  virtual void SetCapture() = 0;
+  virtual void ReleaseCapture() = 0;
+  virtual bool HasCapture() const = 0;
+
+  virtual void ToggleFullscreen() = 0;
+  virtual void Maximize() = 0;
+  virtual void Minimize() = 0;
+  virtual void Restore() = 0;
+  virtual PlatformWindowState GetPlatformWindowState() const = 0;
+
+  virtual void Activate() = 0;
+  virtual void Deactivate() = 0;
+
+  // Sets whether the window should have the standard title bar provided by the
+  // underlying windowing system.  For the main browser window, this may be
+  // changed by the user at any time via 'Show system title bar' option in the
+  // tab strip menu.
+  virtual void SetUseNativeFrame(bool use_native_frame) = 0;
+
+  virtual void SetCursor(PlatformCursor cursor) = 0;
+
+  // Moves the cursor to |location|. Location is in platform window coordinates.
+  virtual void MoveCursorTo(const gfx::Point& location) = 0;
+
+  // Confines the cursor to |bounds| when it is in the platform window. |bounds|
+  // is in platform window coordinates.
+  virtual void ConfineCursorToBounds(const gfx::Rect& bounds) = 0;
+
+  // Sets and gets the restored bounds of the platform-window.
+  virtual void SetRestoredBoundsInPixels(const gfx::Rect& bounds) = 0;
+  virtual gfx::Rect GetRestoredBoundsInPixels() const = 0;
+
+  // Tells if the content of the platform window should be transparent. By
+  // default returns false.
+  virtual bool ShouldWindowContentsBeTransparent() const;
+
+  // Sets and gets ZOrderLevel of the PlatformWindow. Such platforms that do not
+  // support ordering, should not implement these methods as the default
+  // implementation always returns ZOrderLevel::kNormal value.
+  virtual void SetZOrderLevel(ZOrderLevel order);
+  virtual ZOrderLevel GetZOrderLevel() const;
+
+  // Asks the PlatformWindow to stack itself on top of |widget|.
+  virtual void StackAbove(gfx::AcceleratedWidget widget);
+  virtual void StackAtTop();
+};
+
+}  // namespace ui
+
+#endif  // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_BASE_H_
diff --git a/ui/platform_window/platform_window_handler/wm_drag_handler.h b/ui/platform_window/platform_window_handler/wm_drag_handler.h
index 1fc3af3..2789c3e 100644
--- a/ui/platform_window/platform_window_handler/wm_drag_handler.h
+++ b/ui/platform_window/platform_window_handler/wm_drag_handler.h
@@ -7,11 +7,11 @@
 
 #include "base/bind.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_handler/wm_platform_export.h"
 
 namespace ui {
 class OSExchangeData;
-class PlatformWindow;
 
 class WM_PLATFORM_EXPORT WmDragHandler {
  public:
diff --git a/ui/platform_window/platform_window_handler/wm_drop_handler.h b/ui/platform_window/platform_window_handler/wm_drop_handler.h
index 768d9e3..fc7a81f 100644
--- a/ui/platform_window/platform_window_handler/wm_drop_handler.h
+++ b/ui/platform_window/platform_window_handler/wm_drop_handler.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_handler/wm_platform_export.h"
 
 namespace gfx {
@@ -16,7 +17,6 @@
 
 namespace ui {
 class OSExchangeData;
-class PlatformWindow;
 
 class WM_PLATFORM_EXPORT WmDropHandler {
  public:
diff --git a/ui/platform_window/platform_window_handler/wm_move_resize_handler.h b/ui/platform_window/platform_window_handler/wm_move_resize_handler.h
index ac30160..3da718c 100644
--- a/ui/platform_window/platform_window_handler/wm_move_resize_handler.h
+++ b/ui/platform_window/platform_window_handler/wm_move_resize_handler.h
@@ -5,6 +5,7 @@
 #ifndef UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
 #define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_
 
+#include "ui/platform_window/platform_window.h"
 #include "ui/platform_window/platform_window_handler/wm_platform_export.h"
 
 namespace gfx {
@@ -13,8 +14,6 @@
 
 namespace ui {
 
-class PlatformWindow;
-
 class WmMoveResizeHandler {
  public:
   // A system window manager starts interactive drag or resize of a window based
diff --git a/ui/platform_window/platform_window_linux.cc b/ui/platform_window/platform_window_linux.cc
new file mode 100644
index 0000000..d3aabb5
--- /dev/null
+++ b/ui/platform_window/platform_window_linux.cc
@@ -0,0 +1,19 @@
+// Copyright 2019 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/platform_window/platform_window_linux.h"
+
+namespace ui {
+
+PlatformWindowLinux::PlatformWindowLinux() = default;
+
+PlatformWindowLinux::~PlatformWindowLinux() = default;
+
+bool PlatformWindowLinux::IsSyncExtensionAvailable() const {
+  return false;
+}
+
+void PlatformWindowLinux::OnCompleteSwapAfterResize() {}
+
+}  // namespace ui
diff --git a/ui/platform_window/platform_window_linux.h b/ui/platform_window/platform_window_linux.h
new file mode 100644
index 0000000..257a978
--- /dev/null
+++ b/ui/platform_window/platform_window_linux.h
@@ -0,0 +1,28 @@
+// Copyright 2019 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_PLATFORM_WINDOW_PLATFORM_WINDOW_LINUX_H_
+#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_LINUX_H_
+
+#include "ui/platform_window/platform_window_base.h"
+
+namespace ui {
+
+// Linux extensions to the PlatformWindowBase.
+class PlatformWindowLinux : public PlatformWindowBase {
+ public:
+  PlatformWindowLinux();
+  ~PlatformWindowLinux() override;
+
+  // X11-specific.  Returns whether an XSync extension is available at the
+  // current platform.
+  virtual bool IsSyncExtensionAvailable() const;
+  // X11-specific.  Handles CompleteSwapAfterResize event coming from the
+  // compositor observer.
+  virtual void OnCompleteSwapAfterResize();
+};
+
+}  // namespace ui
+
+#endif  // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_LINUX_H_
diff --git a/ui/platform_window/x11/x11_window.cc b/ui/platform_window/x11/x11_window.cc
index e8d926b..d3593f3 100644
--- a/ui/platform_window/x11/x11_window.cc
+++ b/ui/platform_window/x11/x11_window.cc
@@ -279,6 +279,14 @@
   XWindow::Deactivate();
 }
 
+bool X11Window::IsSyncExtensionAvailable() const {
+  return ui::IsSyncExtensionAvailable();
+}
+
+void X11Window::OnCompleteSwapAfterResize() {
+  XWindow::NotifySwapAfterResize();
+}
+
 void X11Window::SetUseNativeFrame(bool use_native_frame) {
   XWindow::SetUseNativeFrame(use_native_frame);
 }
diff --git a/ui/platform_window/x11/x11_window.h b/ui/platform_window/x11/x11_window.h
index 8effaaf..8f62267 100644
--- a/ui/platform_window/x11/x11_window.h
+++ b/ui/platform_window/x11/x11_window.h
@@ -64,6 +64,8 @@
   PlatformWindowState GetPlatformWindowState() const override;
   void Activate() override;
   void Deactivate() override;
+  bool IsSyncExtensionAvailable() const override;
+  void OnCompleteSwapAfterResize() override;
   void SetUseNativeFrame(bool use_native_frame) override;
   void SetCursor(PlatformCursor cursor) override;
   void MoveCursorTo(const gfx::Point& location) override;
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc
index f8e19ec9..3cbd1965 100644
--- a/ui/views/controls/table/table_view.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -298,17 +298,15 @@
 
   if (GetHasFocusIndicator()) {
     // Draw a focus indicator around the active column.
-    focus_ring_ = FocusRing::Install(this);
     const gfx::Rect cell_bounds(GetCellBounds(
         ModelToView(selection_model_.active()), active_visible_column_index_));
     auto path = std::make_unique<SkPath>();
     path->addRect(gfx::RectToSkRect(cell_bounds));
     SetProperty(views::kHighlightPathKey, path.release());
-    focus_ring_->SchedulePaint();
   } else {
     ClearProperty(views::kHighlightPathKey);
-    focus_ring_.reset();
   }
+  focus_ring_->SchedulePaint();
 }
 
 const TableView::VisibleColumn& TableView::GetVisibleColumn(int index) {
@@ -404,8 +402,7 @@
                   gfx::Size(width, header_->GetPreferredSize().height())));
   }
 
-  if (focus_ring_)
-    focus_ring_->Layout();
+  focus_ring_->Layout();
 }
 
 gfx::Size TableView::CalculatePreferredSize() const {
@@ -822,19 +819,12 @@
 }
 
 void TableView::OnFocus() {
-  ScrollView* scroll_view = ScrollView::GetScrollViewForContents(this);
-  if (scroll_view)
-    scroll_view->SetHasFocusIndicator(true);
-
   SchedulePaintForSelection();
   ResetFocusIndicator();
   UpdateAccessibilityFocus();
 }
 
 void TableView::OnBlur() {
-  ScrollView* scroll_view = ScrollView::GetScrollViewForContents(this);
-  if (scroll_view)
-    scroll_view->SetHasFocusIndicator(false);
   SchedulePaintForSelection();
   ResetFocusIndicator();
   UpdateAccessibilityFocus();
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h
index 835b6db..fa19a25 100644
--- a/ui/views/controls/table/table_view.h
+++ b/ui/views/controls/table/table_view.h
@@ -369,7 +369,7 @@
   int active_visible_column_index_ = -1;
 
   // Used to draw a focus indicator around the active cell.
-  std::unique_ptr<FocusRing> focus_ring_;
+  std::unique_ptr<FocusRing> focus_ring_ = FocusRing::Install(this);
 
   // The header. This is only created if more than one column is specified or
   // the first column has a non-empty title.
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
index cea50c2..d5799a1 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.cc
@@ -15,6 +15,42 @@
 
 namespace views {
 
+namespace {
+
+class SwapWithNewSizeObserverHelper : public ui::CompositorObserver {
+ public:
+  using HelperCallback = base::RepeatingCallback<void(const gfx::Size&)>;
+  SwapWithNewSizeObserverHelper(ui::Compositor* compositor,
+                                const HelperCallback& callback)
+      : compositor_(compositor), callback_(callback) {
+    compositor_->AddObserver(this);
+  }
+  ~SwapWithNewSizeObserverHelper() override {
+    if (compositor_)
+      compositor_->RemoveObserver(this);
+  }
+
+ private:
+  // ui::CompositorObserver:
+  void OnCompositingCompleteSwapWithNewSize(ui::Compositor* compositor,
+                                            const gfx::Size& size) override {
+    DCHECK_EQ(compositor, compositor_);
+    callback_.Run(size);
+  }
+  void OnCompositingShuttingDown(ui::Compositor* compositor) override {
+    DCHECK_EQ(compositor, compositor_);
+    compositor_->RemoveObserver(this);
+    compositor_ = nullptr;
+  }
+
+  ui::Compositor* compositor_;
+  const HelperCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(SwapWithNewSizeObserverHelper);
+};
+
+}  // namespace
+
 DesktopWindowTreeHostLinux::DesktopWindowTreeHostLinux(
     internal::NativeWidgetDelegate* native_widget_delegate,
     DesktopNativeWidgetAura* desktop_native_widget_aura)
@@ -33,6 +69,18 @@
   DesktopWindowTreeHostPlatform::OnNativeWidgetCreated(params);
 }
 
+void DesktopWindowTreeHostLinux::Init(const Widget::InitParams& params) {
+  DesktopWindowTreeHostPlatform::Init(params);
+
+  if (platform_window()->IsSyncExtensionAvailable()) {
+    compositor_observer_ = std::make_unique<SwapWithNewSizeObserverHelper>(
+        compositor(),
+        base::BindRepeating(
+            &DesktopWindowTreeHostLinux::OnCompleteSwapWithNewSize,
+            base::Unretained(this)));
+  }
+}
+
 void DesktopWindowTreeHostLinux::OnDisplayMetricsChanged(
     const display::Display& display,
     uint32_t changed_metrics) {
@@ -91,6 +139,11 @@
   properties->x_visual_id = pending_x_visual_id_;
 }
 
+void DesktopWindowTreeHostLinux::OnCompleteSwapWithNewSize(
+    const gfx::Size& size) {
+  platform_window()->OnCompleteSwapAfterResize();
+}
+
 void DesktopWindowTreeHostLinux::AddNonClientEventFilter() {
   DCHECK(!non_client_window_event_filter_);
   non_client_window_event_filter_ = std::make_unique<WindowEventFilterLinux>(
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
index daa7aa5..69e3d36 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h
@@ -33,6 +33,9 @@
   // PlatformWindowDelegateBase:
   void OnClosed() override;
 
+  // DesktopWindowTreeHostPlatform:
+  void Init(const Widget::InitParams& params) override;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostLinuxTest, HitTest);
 
@@ -45,6 +48,9 @@
       const Widget::InitParams& params,
       ui::PlatformWindowInitProperties* properties) override;
 
+  // Called back by compositor_observer_ if the latter is set.
+  virtual void OnCompleteSwapWithNewSize(const gfx::Size& size);
+
   void AddNonClientEventFilter();
   void RemoveNonClientEventFilter();
 
@@ -56,6 +62,8 @@
   // initialization of the host.
   base::Optional<int> pending_x_visual_id_;
 
+  std::unique_ptr<CompositorObserver> compositor_observer_;
+
   DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostLinux);
 };
 
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
index f1295c8..486e49e 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc
@@ -81,38 +81,6 @@
 
 namespace {
 
-class SwapWithNewSizeObserverHelper : public ui::CompositorObserver {
- public:
-  using HelperCallback = base::RepeatingCallback<void(const gfx::Size&)>;
-  SwapWithNewSizeObserverHelper(ui::Compositor* compositor,
-                                const HelperCallback& callback)
-      : compositor_(compositor), callback_(callback) {
-    compositor_->AddObserver(this);
-  }
-  ~SwapWithNewSizeObserverHelper() override {
-    if (compositor_)
-      compositor_->RemoveObserver(this);
-  }
-
- private:
-  // ui::CompositorObserver:
-  void OnCompositingCompleteSwapWithNewSize(ui::Compositor* compositor,
-                                            const gfx::Size& size) override {
-    DCHECK_EQ(compositor, compositor_);
-    callback_.Run(size);
-  }
-  void OnCompositingShuttingDown(ui::Compositor* compositor) override {
-    DCHECK_EQ(compositor, compositor_);
-    compositor_->RemoveObserver(this);
-    compositor_ = nullptr;
-  }
-
-  ui::Compositor* compositor_;
-  const HelperCallback callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(SwapWithNewSizeObserverHelper);
-};
-
 bool ShouldDiscardKeyEvent(XEvent* xev) {
 #if BUILDFLAG(USE_ATK)
   return ui::AtkUtilAuraLinux::HandleKeyEvent(xev) ==
@@ -203,7 +171,7 @@
 // DesktopWindowTreeHostX11, DesktopWindowTreeHost implementation:
 
 void DesktopWindowTreeHostX11::Init(const Widget::InitParams& params) {
-  DesktopWindowTreeHostPlatform::Init(params);
+  DesktopWindowTreeHostLinux::Init(params);
 
   // Set XEventDelegate to receive selection, drag&drop and raw key events.
   //
@@ -212,15 +180,6 @@
   // unified so that DragAndrDropClientOzone is used and XEvent are handled on
   // platform level.
   static_cast<ui::X11Window*>(platform_window())->SetXEventDelegate(this);
-
-  // Can it be unified and will Ozone benefit from this? Check comment above
-  // where this class is defined and declared.
-  if (ui::IsSyncExtensionAvailable()) {
-    compositor_observer_ = std::make_unique<SwapWithNewSizeObserverHelper>(
-        compositor(), base::BindRepeating(
-                          &DesktopWindowTreeHostX11::OnCompleteSwapWithNewSize,
-                          base::Unretained(this)));
-  }
 }
 
 void DesktopWindowTreeHostX11::OnNativeWidgetCreated(
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
index cc1dd17..bef3c948 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h
@@ -156,7 +156,7 @@
   void EnableEventListening();
 
   // Callback for a swapbuffer after resize.
-  void OnCompleteSwapWithNewSize(const gfx::Size& size);
+  void OnCompleteSwapWithNewSize(const gfx::Size& size) override;
 
   // PlatformWindowDelegate overrides:
   //
@@ -206,8 +206,6 @@
 
   uint32_t modal_dialog_counter_ = 0;
 
-  std::unique_ptr<CompositorObserver> compositor_observer_;
-
   // The display and the native X window hosting the root window.
   base::WeakPtrFactory<DesktopWindowTreeHostX11> weak_factory_{this};